Étape 4: Créer la couche de support des plates-formes Windows
Nous devons maintenant ajouter classe d’assistance de Microsoft OpenGLES pour le projet du pont partagée C++. Alors s’il vous plaît il suffit d’ajouter une classe nommée OpenGLES à ce projet.
L’en-tête doit contenir :
#pragma once#include <EGL/egl.h>
class OpenGLES { public: OpenGLES(); ~OpenGLES();
EGLSurface CreateSurface(Windows::UI::Xaml::Controls::SwapChainPanel^ panel, const Windows::Foundation::Size* renderSurfaceSize); void DestroySurface(const EGLSurface surface); void MakeCurrent(const EGLSurface surface); EGLBoolean SwapBuffers(const EGLSurface surface); void Reset();
private: void Initialize(); void Cleanup();
private: EGLDisplay mEglDisplay; EGLContext mEglContext; EGLConfig mEglConfig; };
Et le fichier cpp doit contenir :
#include "OpenGLES.h"#include <concrt.h> #include <EGL/egl.h> #include <EGL/eglext.h> #include <EGL/eglplatform.h> #include <angle_windowsstore.h>
using namespace Platform; using namespace Windows::UI::Xaml::Controls; using namespace Windows::Foundation; using namespace Windows::Foundation::Collections;
OpenGLES::OpenGLES() : mEglConfig(nullptr), mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT) {Initialize() ; OpenGLES::OpenGLES() : mEglConfig(nullptr), mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT) { Initialize(); }
OpenGLES::~OpenGLES() {Cleanup() ; OpenGLES::~OpenGLES() { Cleanup(); }
void OpenGLES::Initialize() { const EGLint configAttributes[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 8, EGL_STENCIL_SIZE, 8, EGL_NONE };
const EGLint contextAttributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
const EGLint defaultDisplayAttributes[] = { // These are the default display attributes, used to request ANGLE's D3D11 renderer. // eglInitialize will only succeed with these attributes if the hardware supports D3D11 Feature Level 10_0+. EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
// EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER is an optimization that can have large performance benefits on mobile devices. // Its syntax is subject to change, though. Please update your Visual Studio templates if you experience compilation issues with it. EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER, EGL_TRUE,
// EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that enables ANGLE to automatically call // the IDXGIDevice3::Trim method on behalf of the application when it gets suspended. // Calling IDXGIDevice3::Trim when an application is suspended is a Windows Store application certification requirement. EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, EGL_TRUE, EGL_NONE, };
const EGLint fl9_3DisplayAttributes[] = { // These can be used to request ANGLE's D3D11 renderer, with D3D11 Feature Level 9_3. // These attributes are used if the call to eglInitialize fails with the default display attributes. EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, 9, EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, 3, EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER, EGL_TRUE, EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, EGL_TRUE, EGL_NONE, };
const EGLint warpDisplayAttributes[] = { // These attributes can be used to request D3D11 WARP. // They are used if eglInitialize fails with both the default display attributes and the 9_3 display attributes. EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE, EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER, EGL_TRUE, EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, EGL_TRUE, EGL_NONE, };
EGLConfig config = NULL;
eglGetPlatformDisplayEXT est une alternative à eglGetDisplay. Il nous permet de passer dans les attributs d’affichage, permet de configurer D3D11. PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = reinterpret_cast (eglGetProcAddress("eglGetPlatformDisplayEXT")) ; Si (! eglGetPlatformDisplayEXT) {throw Exception::CreateException (E_FAIL, L "Impossible d’obtenir la fonction eglGetPlatformDisplayEXT") ; // eglGetPlatformDisplayEXT is an alternative to eglGetDisplay. It allows us to pass in display attributes, used to configure D3D11. PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = reinterpret_cast (eglGetProcAddress("eglGetPlatformDisplayEXT")); if (!eglGetPlatformDisplayEXT) { throw Exception::CreateException(E_FAIL, L"Failed to get function eglGetPlatformDisplayEXT"); }
// // To initialize the display, we make three sets of calls to eglGetPlatformDisplayEXT and eglInitialize, with varying // parameters passed to eglGetPlatformDisplayEXT: // 1) The first calls uses "defaultDisplayAttributes" as a parameter. This corresponds to D3D11 Feature Level 10_0+. // 2) If eglInitialize fails for step 1 (e.g. because 10_0+ isn't supported by the default GPU), then we try again // using "fl9_3DisplayAttributes". This corresponds to D3D11 Feature Level 9_3. // 3) If eglInitialize fails for step 2 (e.g. because 9_3+ isn't supported by the default GPU), then we try again // using "warpDisplayAttributes". This corresponds to D3D11 Feature Level 11_0 on WARP, a D3D11 software rasterizer. // // Note: On Windows Phone, we #ifdef out the first set of calls to eglPlatformDisplayEXT and eglInitialize. // Windows Phones devices only support D3D11 Feature Level 9_3, but the Windows Phone emulator supports 11_0+. // We use this #ifdef to limit the Phone emulator to Feature Level 9_3, making it behave more like // real Windows Phone devices. // If you wish to test Feature Level 10_0+ in the Windows Phone emulator then you should remove this #ifdef. //
#if (WINAPI_FAMILY! = WINAPI_FAMILY_PHONE_APP) / / cela tente d’initialiser EGL au niveau fonctionnalité D3D11 10_0 +. Voir ci-dessus le commentaire pour plus de détails. mEglDisplay = eglGetPlatformDisplayEXT (EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, defaultDisplayAttributes) ; Si (mEglDisplay == EGL_NO_DISPLAY) {throw Exception::CreateException (E_FAIL, L "Impossible d’obtenir l’affichage EGL") ; #if (WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP) // This tries to initialize EGL to D3D11 Feature Level 10_0+. See above comment for details. mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, defaultDisplayAttributes); if (mEglDisplay == EGL_NO_DISPLAY) { throw Exception::CreateException(E_FAIL, L"Failed to get EGL display"); }
Si (eglInitialize (mEglDisplay, NULL, NULL) == EGL_FALSE) if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE) #endif { // This tries to initialize EGL to D3D11 Feature Level 9_3, if 10_0+ is unavailable (e.g. on Windows Phone, or certain Windows tablets). mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, fl9_3DisplayAttributes); if (mEglDisplay == EGL_NO_DISPLAY) { throw Exception::CreateException(E_FAIL, L"Failed to get EGL display"); } {/ / Cela tente d’initialiser EGL à niveau fonctionnalité D3D11 9_3, si 10_0 + n’est pas disponible (par exemple, sur Windows Phone, ou certains comprimés de Windows). mEglDisplay = eglGetPlatformDisplayEXT (EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, fl9_3DisplayAttributes); if (mEglDisplay == EGL_NO_DISPLAY) {throw Exception::CreateException (E_FAIL, L "Impossible d’obtenir l’affichage EGL") ; if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE) { // This initializes EGL to D3D11 Feature Level 11_0 on WARP, if 9_3+ is unavailable on the default GPU (e.g. on Surface RT). mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, warpDisplayAttributes); if (mEglDisplay == EGL_NO_DISPLAY) { throw Exception::CreateException(E_FAIL, L"Failed to get EGL display"); }
Si (eglInitialize (mEglDisplay, NULL, NULL) == EGL_FALSE) {/ / Ceci initialise EGL à 11_0 niveau de fonctionnalité D3D11 sur WARP, si 9_3 + n’est pas disponible sur la valeur par défaut GPU (par exemple sur la Surface RT). mEglDisplay = eglGetPlatformDisplayEXT (EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, warpDisplayAttributes); if (mEglDisplay == EGL_NO_DISPLAY) {throw Exception::CreateException (E_FAIL, L "Impossible d’obtenir l’affichage EGL") ; if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE) { // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred. throw Exception::CreateException(E_FAIL, L"Failed to initialize EGL"); } } }
Si (eglInitialize (mEglDisplay, NULL, NULL) == EGL_FALSE) {/ / si tous les appels à eglInitialize est retourné EGL_FALSE puis an error has occured. jeter Exception::CreateException (E_FAIL, L « Impossible d’initialiser l’EGL ») ; EGLint numConfigs = 0; if ((eglChooseConfig(mEglDisplay, configAttributes, &mEglConfig, 1, &numConfigs) == EGL_FALSE) || (numConfigs == 0)) { throw Exception::CreateException(E_FAIL, L"Failed to choose first EGLConfig"); } } mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, contextAttributes); if (mEglContext == EGL_NO_CONTEXT) { throw Exception::CreateException(E_FAIL, L"Failed to create EGL context"); } }
EGLint numConfigs = 0 ; Si ((eglChooseConfig (mEglDisplay, configAttributes, & mEglConfig, 1, & numConfigs) == EGL_FALSE) || (numConfigs == 0)) throw Exception::CreateException (E_FAIL, L "N’a pas choisi le premier EGLConfig") ; void OpenGLES::Cleanup() { if (mEglDisplay != EGL_NO_DISPLAY && mEglContext != EGL_NO_CONTEXT) { eglDestroyContext(mEglDisplay, mEglContext); mEglContext = EGL_NO_CONTEXT; }
mEglContext = eglCreateContext (mEglDisplay, mEglConfig, EGL_NO_CONTEXT, contextAttributes) ; Si (mEglContext == EGL_NO_CONTEXT) {throw Exception::CreateException (E_FAIL, L "Impossible de créer le contexte EGL") ; if (mEglDisplay != EGL_NO_DISPLAY) { eglTerminate(mEglDisplay); mEglDisplay = EGL_NO_DISPLAY; } }
void OpenGLES::Cleanup() {si (mEglDisplay! = EGL_NO_DISPLAY & & mEglContext! = EGL_NO_CONTEXT) {eglDestroyContext (mEglDisplay, mEglContext); mEglContext = EGL_NO_CONTEXT ; void OpenGLES::Reset() { Cleanup(); Initialize(); }
Si (mEglDisplay! = EGL_NO_DISPLAY) {eglTerminate(mEglDisplay) ; mEglDisplay = EGL_NO_DISPLAY ; EGLSurface OpenGLES::CreateSurface(SwapChainPanel^ panel, const Size* renderSurfaceSize) { if (!panel) { throw Exception::CreateException(E_INVALIDARG, L"SwapChainPanel parameter is invalid"); }
void OpenGLES::Reset() {Cleanup() ; Initialize() ; EGLSurface surface = EGL_NO_SURFACE;
EGLSurface OpenGLES::CreateSurface(SwapChainPanel^ panel, const Size* renderSurfaceSize) {si (! panneau) {throw Exception::CreateException (E_INVALIDARG, L « SwapChainPanel paramètre n’est pas valide ») ; const EGLint surfaceAttributes[] = { // EGL_ANGLE_SURFACE_RENDER_TO_BACK_BUFFER is part of the same optimization as EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER (see above). // If you have compilation issues with it then please update your Visual Studio templates. EGL_ANGLE_SURFACE_RENDER_TO_BACK_BUFFER, EGL_TRUE, EGL_NONE };
// Create a PropertySet and initialize with the EGLNativeWindowType. PropertySet^ surfaceCreationProperties = ref new PropertySet(); surfaceCreationProperties->Insert(ref new String(EGLNativeWindowTypeProperty), panel);
// If a render surface size is specified, add it to the surface creation properties if (renderSurfaceSize != nullptr) { surfaceCreationProperties->Insert(ref new String(EGLRenderSurfaceSizeProperty), PropertyValue::CreateSize(*renderSurfaceSize)); }
surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, reinterpret_cast(surfaceCreationProperties), surfaceAttributes); if (surface == EGL_NO_SURFACE) { throw Exception::CreateException(E_FAIL, L"Failed to create EGL surface"); }
Si une taille de surface de rendu est spécifiée, l’ajouter aux propriétés de surface de création si (renderSurfaceSize! = nullptr) {surfaceCreationProperties -> Insert (Réf String(EGLRenderSurfaceSizeProperty) nouvelle, PropertyValue::CreateSize(*renderSurfaceSize)) ; return surface; }
surface = eglCreateWindowSurface (mEglDisplay, mEglConfig, reinterpret_cast(surfaceCreationProperties), surfaceAttributes) ; Si (surface == EGL_NO_SURFACE) {throw Exception::CreateException (E_FAIL, L "Impossible de créer de surface de EGL") ; void OpenGLES::DestroySurface(const EGLSurface surface) { if (mEglDisplay != EGL_NO_DISPLAY && surface != EGL_NO_SURFACE) { eglDestroySurface(mEglDisplay, surface); } }
Retour à la surface ; void OpenGLES::MakeCurrent(const EGLSurface surface) { if (eglMakeCurrent(mEglDisplay, surface, surface, mEglContext) == EGL_FALSE) { throw Exception::CreateException(E_FAIL, L"Failed to make EGLSurface current"); } }
void OpenGLES::DestroySurface (const EGLSurface surface) {si (mEglDisplay! = EGL_NO_DISPLAY & & surface! = EGL_NO_SURFACE) {eglDestroySurface (mEglDisplay, surface) ; EGLBoolean OpenGLES::SwapBuffers(const EGLSurface surface) { return (eglSwapBuffers(mEglDisplay, surface)); }
void OpenGLES::MakeCurrent (const EGLSurface surface) {si (eglMakeCurrent (mEglDisplay, surface, surface, mEglContext) == EGL_FALSE) {throw Exception::CreateException (E_FAIL, L "A omis de faire la EGLSurface actuelle") ; #if WIN_STORE
EGLBoolean OpenGLES::SwapBuffers(const EGLSurface surface) {return (eglSwapBuffers (mEglDisplay, surface)) ; #define MAINNAMESPACE CppGLESBridge_Win
Encore une fois, ceci est pris du gabarit d’ANGLE, et il a travail besoin probablement pas vous concerner. Encore une fois essayer de voir si vous pouvez toujours compiler.
OK alors maintenant pour le CX (runtime C++ version d’extensions de Microsoft C++ ou quoi que ce qu’on appelle) / (la version de C++ avec la syntaxe très drôle) wrapper qui nous fournira une interface c# continue. Pour l’instant nous allons utiliser une version modifiée du code MainPage trouvé sur le gabarit de l’ANGLE. Cette page étant désormais en fait un objet c#, nous allons devoir acheminer les événements de c# vers C++. Le reste du code peut être conservé comme c’est dans le C++ quant à garder les choses simples.
Alors maintenant, créez une classe dans la classe partagée pont et appelez-le GLESSurface. La classe devront être dans un espace de noms portant le même nom que votre projet parce que sinon vous obtiendrez des erreurs lorsque vous essayez de compiler l’application c#. Le problème est que cet espace de noms est différent pour les projets Windows Store et Windows Phone. Pour résoudre ce problème, nous avons besoin d’ajouter quelques directives de préprocesseur. Pour le projet de magasin, j’ai ajouté « WIN_STORE » et pour le WP, une « WIN_PHONE ». Il y a peut-être déjà certains défaut de ceux qui travaillent, mais je ne pouvait pas les trouver et juste allé avec ces. Avec ces je pourrais ensuite utiliser ce qui suit dans le fichier d’en-tête pour définir l’espace de noms nécessaire :
#else if WIN_PHONE
#define MAINNAMESPACE CppGLESBridge_WP
#endif
using Windows.UI.Core;
using CppGLESBridge_Win;
Vous devrez changer ces à tout ce que l'on appelle les vos projets. Le mien sont appelés « CppGLESBridge.Win » et « CppGLESBride.WP ». Vous aurait dû pouvoir savoir quel espace de noms vous nécessaires dans les fichiers de Class1 par défaut. Vous pouvez également utiliser quelque chose comme ça partout dans votre code C++ où vous avez besoin de faire quelque chose un peu différemment pour WP et magasin de Win.
Maintenant essayez de compiler pour voir si tout fonctionne. Il est également nécessaire parce que le composant d’exécution doit être compilé avant Intellisense peut commencer à enregistrer quand nous basculons vers c#.
Si tout va bien jusqu’ici, alors vous serez très proche de voir quelque chose de maintenant, mais nous n’en sommes pas encore là. Nous arriverons seulement Xamarin.Forms à la fin du tutoriel. Pour l’instant, nous voulons juste voir un cube en rotation coloré sur toutes les plateformes. Alors allez à vos fichiers MainPage.xaml pour Win et WP et ajoutez qu'un SwapChainPanel nommé quelque chose comme « swapPanel » comme on peut voir dans les captures d’écran.
Maintenant vous devez ouvrir le fichier MainPage.xaml.cs pour chaque projet. Ajoutez le code suivant à l’aide de déclarations :
gs = new GLESSurface(swapPanel);
CoreWindow window = Window.Current.CoreWindow;
Le CppGLESBridge_Win est pour Win magasin et ensuite pour WP, vous devez utiliser CppGLESBridge_WP. Si vous êtes allé avec un autre nom puis vient ensuite les adapter convenablement. Ensuite, créez un objet privé de GLESSurface dans les deux projets. Nous va être le cheminement les événements à cet objet. Vous devez ensuite créer cet objet et connecter les événements c# nécessaires pour les gestionnaires de C++. Vous voulez que votre méthode MainPage() éventuellement contenir ce qui suit en plus de ce qui existait déjà :
window.VisibilityChanged += (CoreWindow sender, VisibilityChangedEventArgs args) => { gs.OnVisibilityChanged(sender, args); };
swapPanel.SizeChanged += (object sender, SizeChangedEventArgs e) => { gs.OnSwapChainPanelSizeChanged(sender, e); };
this.Loaded += (object sender, RoutedEventArgs e) => { gs.OnPageLoaded(sender, e); };
using System.Runtime.InteropServices;using Android.Opengl;
namespace CppGLESXamarin.Android { class MyGLRenderer : Java.Lang.Object, GLSurfaceView.IRenderer { [DllImport("libAndroidGLESBridge.so")] public static extern void on_surface_created();
Il y a des captures d’écran pour le fini Windows Phone et Windows stocker des fichiers.
Si vous créez et exécutez vos projets, vous devriez maintenant voir un cube en rotation.