Commit b51a3206c84e770bbfbb3c79124535c4f77cc318

David Ludwig 2014-03-22T20:48:18

WinRT: Got OpenGL ES 2 working with the latest version of ANGLE/WinRT. SDL/WinRT did have support for OpenGL ES 2 via an older version of ANGLE/WinRT, however its API changed a few months ago, and SDL/WinRT would crash when trying to use it. It would also occasionally crash when using the older version. This changeset should make SDL/WinRT work with the latest version, as available via MS Open Tech's git repository of it at https://github.com/msopentech/angle Older versions of ANGLE/WinRT (from either https://github.com/stammen/angleproject or https://bitbucket.org/DavidLudwig/angleproject) will need to be updated to MS Open Tech's latest version.

diff --git a/VisualC-WinRT/SDL/SDL-WinRT_VS2012.vcxproj b/VisualC-WinRT/SDL/SDL-WinRT_VS2012.vcxproj
index 9649279..d19b9d0 100644
--- a/VisualC-WinRT/SDL/SDL-WinRT_VS2012.vcxproj
+++ b/VisualC-WinRT/SDL/SDL-WinRT_VS2012.vcxproj
@@ -324,7 +324,7 @@
     <ClInclude Include="..\..\src\video\SDL_blit_auto.h" />
     <ClInclude Include="..\..\src\video\SDL_blit_copy.h" />
     <ClInclude Include="..\..\src\video\SDL_blit_slow.h" />
-    <ClInclude Include="..\..\src\video\SDL_egl.h" />
+    <ClInclude Include="..\..\src\video\SDL_egl_c.h" />
     <ClInclude Include="..\..\src\video\SDL_pixels_c.h" />
     <ClInclude Include="..\..\src\video\SDL_rect_c.h" />
     <ClInclude Include="..\..\src\video\SDL_RLEaccel_c.h" />
diff --git a/VisualC-WinRT/SDL/SDL-WinRT_VS2012.vcxproj.filters b/VisualC-WinRT/SDL/SDL-WinRT_VS2012.vcxproj.filters
index 7cdc81d..b5050af 100644
--- a/VisualC-WinRT/SDL/SDL-WinRT_VS2012.vcxproj.filters
+++ b/VisualC-WinRT/SDL/SDL-WinRT_VS2012.vcxproj.filters
@@ -633,9 +633,6 @@
     <ClInclude Include="..\..\src\video\winrt\SDL_winrtopengles.h">
       <Filter>Source Files</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\src\video\SDL_egl.h">
-      <Filter>Source Files</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\include\SDL_opengles2.h">
       <Filter>Header Files</Filter>
     </ClInclude>
@@ -666,6 +663,9 @@
     <ClInclude Include="..\..\src\render\direct3d11\SDL_render_winrt.h">
       <Filter>Source Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\src\video\SDL_egl_c.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <Filter Include="Header Files">
diff --git a/include/SDL_egl.h b/include/SDL_egl.h
index d312f04..a345818 100644
--- a/include/SDL_egl.h
+++ b/include/SDL_egl.h
@@ -394,8 +394,8 @@ typedef enum {
 #if __WINRT__
 #include <Unknwn.h>
 typedef IUnknown * EGLNativeWindowType;
-typedef int EGLNativeDisplayType;
-typedef HBITMAP EGLNativePixmapType;
+typedef IUnknown * EGLNativePixmapType;
+typedef IUnknown * EGLNativeDisplayType;
 #else
 typedef HDC     EGLNativeDisplayType;
 typedef HBITMAP EGLNativePixmapType;
diff --git a/src/video/SDL_egl.c b/src/video/SDL_egl.c
index cfba200..e0efa64 100644
--- a/src/video/SDL_egl.c
+++ b/src/video/SDL_egl.c
@@ -216,6 +216,7 @@ SDL_EGL_LoadLibrary(_THIS, const char *egl_path, NativeDisplayType native_displa
     LOAD_FUNC(eglWaitGL);
     LOAD_FUNC(eglBindAPI);
     
+#if !defined(__WINRT__)
     _this->egl_data->egl_display = _this->egl_data->eglGetDisplay(native_display);
     if (!_this->egl_data->egl_display) {
         return SDL_SetError("Could not get EGL display");
@@ -224,6 +225,7 @@ SDL_EGL_LoadLibrary(_THIS, const char *egl_path, NativeDisplayType native_displa
     if (_this->egl_data->eglInitialize(_this->egl_data->egl_display, NULL, NULL) != EGL_TRUE) {
         return SDL_SetError("Could not initialize EGL");
     }
+#endif
 
     _this->egl_data->dll_handle = dll_handle;
     _this->egl_data->egl_dll_handle = egl_dll_handle;
diff --git a/src/video/winrt/SDL_winrtopengles.cpp b/src/video/winrt/SDL_winrtopengles.cpp
index 6f3ddce..4a84174 100644
--- a/src/video/winrt/SDL_winrtopengles.cpp
+++ b/src/video/winrt/SDL_winrtopengles.cpp
@@ -20,8 +20,6 @@
 */
 #include "../../SDL_internal.h"
 
-// TODO: WinRT, make this file compile via C code
-
 #if SDL_VIDEO_DRIVER_WINRT && SDL_VIDEO_OPENGL_EGL
 
 /* EGL implementation of SDL OpenGL support */
@@ -29,13 +27,77 @@
 #include "SDL_winrtvideo_cpp.h"
 extern "C" {
 #include "SDL_winrtopengles.h"
+#include "SDL_loadso.h"
 }
 
-#define EGL_D3D11_ONLY_DISPLAY_ANGLE ((NativeDisplayType) -3)
+/* Windows includes */
+#include <wrl/client.h>
+using namespace Windows::UI::Core;
+
+/* ANGLE/WinRT constants */
+static const int ANGLE_D3D_FEATURE_LEVEL_ANY = 0;
+
+
+/*
+ * SDL/EGL top-level implementation
+ */
 
 extern "C" int
-WINRT_GLES_LoadLibrary(_THIS, const char *path) {
-    return SDL_EGL_LoadLibrary(_this, path, EGL_D3D11_ONLY_DISPLAY_ANGLE);
+WINRT_GLES_LoadLibrary(_THIS, const char *path)
+{
+    SDL_VideoData *video_data = (SDL_VideoData *)_this->driverdata;
+
+    if (SDL_EGL_LoadLibrary(_this, path, EGL_DEFAULT_DISPLAY) != 0) {
+        return -1;
+    }
+
+    /* Load ANGLE/WinRT-specific functions */
+    CreateWinrtEglWindow_Function CreateWinrtEglWindow = (CreateWinrtEglWindow_Function) SDL_LoadFunction(_this->egl_data->egl_dll_handle, "CreateWinrtEglWindow");
+    if (!CreateWinrtEglWindow) {
+        return SDL_SetError("Could not retrieve ANGLE/WinRT function CreateWinrtEglWindow");
+    }
+
+    /* Create an ANGLE/WinRT EGL-window */
+    /* TODO, WinRT: check for XAML usage before accessing the CoreWindow, as not doing so could lead to a crash */
+    CoreWindow ^ native_win = CoreWindow::GetForCurrentThread();
+    Microsoft::WRL::ComPtr<IUnknown> cpp_win = reinterpret_cast<IUnknown *>(native_win);
+    HRESULT result = CreateWinrtEglWindow(cpp_win, ANGLE_D3D_FEATURE_LEVEL_ANY, &(video_data->winrtEglWindow));
+    if (FAILED(result)) {
+        return -1;
+    }
+
+    /* Call eglGetDisplay and eglInitialize as appropriate.  On other
+     * platforms, this would probably get done by SDL_EGL_LoadLibrary,
+     * however ANGLE/WinRT's current implementation (as of Mar 22, 2014) of
+     * eglGetDisplay requires that a C++ object be passed into it, so the
+     * call will be made in this file, a C++ file, instead.
+     */
+    Microsoft::WRL::ComPtr<IUnknown> cpp_display = video_data->winrtEglWindow;
+    _this->egl_data->egl_display = ((eglGetDisplay_Function)_this->egl_data->eglGetDisplay)(cpp_display);
+    if (!_this->egl_data->egl_display) {
+        return SDL_SetError("Could not get EGL display");
+    }
+    
+    if (_this->egl_data->eglInitialize(_this->egl_data->egl_display, NULL, NULL) != EGL_TRUE) {
+        return SDL_SetError("Could not initialize EGL");
+    }
+
+    return 0;
+}
+
+extern "C" void
+WINRT_GLES_UnloadLibrary(_THIS)
+{
+    SDL_VideoData *video_data = (SDL_VideoData *)_this->driverdata;
+
+    /* Release SDL's own COM reference to the ANGLE/WinRT IWinrtEglWindow */
+    if (video_data->winrtEglWindow) {
+        video_data->winrtEglWindow->Release();
+        video_data->winrtEglWindow = nullptr;
+    }
+
+    /* Perform the bulk of the unloading */
+    SDL_EGL_UnloadLibrary(_this);
 }
 
 extern "C" {
diff --git a/src/video/winrt/SDL_winrtopengles.h b/src/video/winrt/SDL_winrtopengles.h
index f051132..ae17391 100644
--- a/src/video/winrt/SDL_winrtopengles.h
+++ b/src/video/winrt/SDL_winrtopengles.h
@@ -31,16 +31,34 @@
 /* OpenGLES functions */
 #define WINRT_GLES_GetAttribute SDL_EGL_GetAttribute
 #define WINRT_GLES_GetProcAddress SDL_EGL_GetProcAddress
-#define WINRT_GLES_UnloadLibrary SDL_EGL_UnloadLibrary
 #define WINRT_GLES_SetSwapInterval SDL_EGL_SetSwapInterval
 #define WINRT_GLES_GetSwapInterval SDL_EGL_GetSwapInterval
 #define WINRT_GLES_DeleteContext SDL_EGL_DeleteContext
 
 extern int WINRT_GLES_LoadLibrary(_THIS, const char *path);
+extern void WINRT_GLES_UnloadLibrary(_THIS);
 extern SDL_GLContext WINRT_GLES_CreateContext(_THIS, SDL_Window * window);
 extern void WINRT_GLES_SwapWindow(_THIS, SDL_Window * window);
 extern int WINRT_GLES_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context);
 
+
+#ifdef __cplusplus
+
+/* Typedefs for ANGLE/WinRT's C++-based native-display and native-window types,
+ * which are used when calling eglGetDisplay and eglCreateWindowSurface.
+ */
+typedef Microsoft::WRL::ComPtr<IUnknown> WINRT_EGLNativeWindowType;
+typedef WINRT_EGLNativeWindowType WINRT_EGLNativeDisplayType;
+
+/* Function pointer typedefs for ANGLE/WinRT's functions that require
+ * parameter customization [by passing in C++ objects].
+ */
+typedef EGLDisplay (EGLAPIENTRY *eglGetDisplay_Function)(WINRT_EGLNativeWindowType);
+typedef EGLSurface (EGLAPIENTRY *eglCreateWindowSurface_Function)(EGLDisplay, EGLConfig, WINRT_EGLNativeWindowType, const EGLint *);
+typedef HRESULT (EGLAPIENTRY *CreateWinrtEglWindow_Function)(Microsoft::WRL::ComPtr<IUnknown>, int, IUnknown ** result);
+
+#endif /* __cplusplus */
+
 #endif /* SDL_VIDEO_DRIVER_WINRT && SDL_VIDEO_OPENGL_EGL */
 
 #endif /* _SDL_winrtopengles_h */
diff --git a/src/video/winrt/SDL_winrtvideo.cpp b/src/video/winrt/SDL_winrtvideo.cpp
index 4a5301f..f54e985 100644
--- a/src/video/winrt/SDL_winrtvideo.cpp
+++ b/src/video/winrt/SDL_winrtvideo.cpp
@@ -30,6 +30,7 @@
 
 /* Windows includes */
 #include <agile.h>
+#include <wrl/client.h>
 using namespace Windows::UI::Core;
 
 
@@ -86,6 +87,15 @@ WINRT_DeleteDevice(SDL_VideoDevice * device)
     if (device == WINRT_GlobalSDLVideoDevice) {
         WINRT_GlobalSDLVideoDevice = NULL;
     }
+
+    if (device->driverdata) {
+        SDL_VideoData * video_data = (SDL_VideoData *)device->driverdata;
+        if (video_data->winrtEglWindow) {
+            video_data->winrtEglWindow->Release();
+        }
+        SDL_free(video_data);
+    }
+
     SDL_free(device);
 }
 
@@ -93,6 +103,7 @@ static SDL_VideoDevice *
 WINRT_CreateDevice(int devindex)
 {
     SDL_VideoDevice *device;
+    SDL_VideoData *data;
 
     /* Initialize all variables that we clean on shutdown */
     device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
@@ -104,6 +115,14 @@ WINRT_CreateDevice(int devindex)
         return (0);
     }
 
+    data = (SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
+    if (!data) {
+        SDL_OutOfMemory();
+        return (0);
+    }
+    SDL_zerop(data);
+    device->driverdata = data;
+
     /* Set the function pointers */
     device->VideoInit = WINRT_VideoInit;
     device->VideoQuit = WINRT_VideoQuit;
@@ -301,29 +320,25 @@ WINRT_CreateWindow(_THIS, SDL_Window * window)
         data->egl_surface = EGL_NO_SURFACE;
     } else {
         /* OpenGL ES 2 was reuqested.  Set up an EGL surface. */
+        SDL_VideoData * video_data = (SDL_VideoData *)_this->driverdata;
 
-        /* HACK: ANGLE/WinRT currently uses non-pointer, C++ objects to represent
-           native windows.  The object only contains a single pointer to a COM
-           interface pointer, which on x86 appears to be castable to the object
-           without apparant problems.  On other platforms, notable ARM and x64,
-           doing so will cause a crash.  To avoid this crash, we'll bypass
-           SDL's normal call to eglCreateWindowSurface, which is invoked from C
-           code, and call it here, where an appropriate C++ object may be
-           passed in.
+        /* Call SDL_EGL_ChooseConfig and eglCreateWindowSurface directly,
+         * rather than via SDL_EGL_CreateSurface, as ANGLE/WinRT requires
+         * a C++ object, ComPtr<IUnknown>, to be passed into
+         * eglCreateWindowSurface.
          */
-        typedef EGLSurface (*eglCreateWindowSurfaceFunction)(EGLDisplay dpy, EGLConfig config,
-            Microsoft::WRL::ComPtr<IUnknown> win,
-            const EGLint *attrib_list);
-        eglCreateWindowSurfaceFunction WINRT_eglCreateWindowSurface =
-            (eglCreateWindowSurfaceFunction) _this->egl_data->eglCreateWindowSurface;
-
-        Microsoft::WRL::ComPtr<IUnknown> nativeWindow = reinterpret_cast<IUnknown *>(data->coreWindow.Get());
-        data->egl_surface = WINRT_eglCreateWindowSurface(
+        if (SDL_EGL_ChooseConfig(_this) != 0) {
+            char buf[512];
+            SDL_snprintf(buf, sizeof(buf), "SDL_EGL_ChooseConfig failed: %s", SDL_GetError());
+            return SDL_SetError(buf);
+        }
+
+        Microsoft::WRL::ComPtr<IUnknown> cpp_winrtEglWindow = video_data->winrtEglWindow;
+        data->egl_surface = ((eglCreateWindowSurface_Function)_this->egl_data->eglCreateWindowSurface)(
             _this->egl_data->egl_display,
             _this->egl_data->egl_config,
-            nativeWindow, NULL);
+            cpp_winrtEglWindow, NULL);
         if (data->egl_surface == NULL) {
-            // TODO, WinRT: see if eglCreateWindowSurface, or its callee(s), sets an error message.  If so, attach it to the SDL error.
             return SDL_SetError("eglCreateWindowSurface failed");
         }
     }
diff --git a/src/video/winrt/SDL_winrtvideo_cpp.h b/src/video/winrt/SDL_winrtvideo_cpp.h
index b5f4451..119f5b8 100644
--- a/src/video/winrt/SDL_winrtvideo_cpp.h
+++ b/src/video/winrt/SDL_winrtvideo_cpp.h
@@ -34,6 +34,13 @@ extern "C" {
 #include "../SDL_egl_c.h"
 }
 
+/* Private display data */
+typedef struct SDL_VideoData {
+    /* An object created by ANGLE/WinRT (OpenGL ES 2 for WinRT) that gets
+     * passed to eglGetDisplay and eglCreateWindowSurface:
+     */
+    IUnknown *winrtEglWindow;
+} SDL_VideoData;
 
 /* The global, WinRT, SDL Window.
    For now, SDL/WinRT only supports one window (due to platform limitations of