WinRT: Got OpenGL ES 2 working with the latest version of ANGLE/WinRT.
authorDavid Ludwig <dludwig@pobox.com>
Sat, 22 Mar 2014 20:48:18 -0400
changeset 8663308e74493106
parent 8662 f8e55cba0bca
child 8664 565698ab1c40
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.
VisualC-WinRT/SDL/SDL-WinRT_VS2012.vcxproj
VisualC-WinRT/SDL/SDL-WinRT_VS2012.vcxproj.filters
include/SDL_egl.h
src/video/SDL_egl.c
src/video/winrt/SDL_winrtopengles.cpp
src/video/winrt/SDL_winrtopengles.h
src/video/winrt/SDL_winrtvideo.cpp
src/video/winrt/SDL_winrtvideo_cpp.h
     1.1 --- a/VisualC-WinRT/SDL/SDL-WinRT_VS2012.vcxproj	Fri Mar 21 10:40:15 2014 -0400
     1.2 +++ b/VisualC-WinRT/SDL/SDL-WinRT_VS2012.vcxproj	Sat Mar 22 20:48:18 2014 -0400
     1.3 @@ -324,7 +324,7 @@
     1.4      <ClInclude Include="..\..\src\video\SDL_blit_auto.h" />
     1.5      <ClInclude Include="..\..\src\video\SDL_blit_copy.h" />
     1.6      <ClInclude Include="..\..\src\video\SDL_blit_slow.h" />
     1.7 -    <ClInclude Include="..\..\src\video\SDL_egl.h" />
     1.8 +    <ClInclude Include="..\..\src\video\SDL_egl_c.h" />
     1.9      <ClInclude Include="..\..\src\video\SDL_pixels_c.h" />
    1.10      <ClInclude Include="..\..\src\video\SDL_rect_c.h" />
    1.11      <ClInclude Include="..\..\src\video\SDL_RLEaccel_c.h" />
     2.1 --- a/VisualC-WinRT/SDL/SDL-WinRT_VS2012.vcxproj.filters	Fri Mar 21 10:40:15 2014 -0400
     2.2 +++ b/VisualC-WinRT/SDL/SDL-WinRT_VS2012.vcxproj.filters	Sat Mar 22 20:48:18 2014 -0400
     2.3 @@ -633,9 +633,6 @@
     2.4      <ClInclude Include="..\..\src\video\winrt\SDL_winrtopengles.h">
     2.5        <Filter>Source Files</Filter>
     2.6      </ClInclude>
     2.7 -    <ClInclude Include="..\..\src\video\SDL_egl.h">
     2.8 -      <Filter>Source Files</Filter>
     2.9 -    </ClInclude>
    2.10      <ClInclude Include="..\..\include\SDL_opengles2.h">
    2.11        <Filter>Header Files</Filter>
    2.12      </ClInclude>
    2.13 @@ -666,6 +663,9 @@
    2.14      <ClInclude Include="..\..\src\render\direct3d11\SDL_render_winrt.h">
    2.15        <Filter>Source Files</Filter>
    2.16      </ClInclude>
    2.17 +    <ClInclude Include="..\..\src\video\SDL_egl_c.h">
    2.18 +      <Filter>Source Files</Filter>
    2.19 +    </ClInclude>
    2.20    </ItemGroup>
    2.21    <ItemGroup>
    2.22      <Filter Include="Header Files">
     3.1 --- a/include/SDL_egl.h	Fri Mar 21 10:40:15 2014 -0400
     3.2 +++ b/include/SDL_egl.h	Sat Mar 22 20:48:18 2014 -0400
     3.3 @@ -394,8 +394,8 @@
     3.4  #if __WINRT__
     3.5  #include <Unknwn.h>
     3.6  typedef IUnknown * EGLNativeWindowType;
     3.7 -typedef int EGLNativeDisplayType;
     3.8 -typedef HBITMAP EGLNativePixmapType;
     3.9 +typedef IUnknown * EGLNativePixmapType;
    3.10 +typedef IUnknown * EGLNativeDisplayType;
    3.11  #else
    3.12  typedef HDC     EGLNativeDisplayType;
    3.13  typedef HBITMAP EGLNativePixmapType;
     4.1 --- a/src/video/SDL_egl.c	Fri Mar 21 10:40:15 2014 -0400
     4.2 +++ b/src/video/SDL_egl.c	Sat Mar 22 20:48:18 2014 -0400
     4.3 @@ -216,6 +216,7 @@
     4.4      LOAD_FUNC(eglWaitGL);
     4.5      LOAD_FUNC(eglBindAPI);
     4.6      
     4.7 +#if !defined(__WINRT__)
     4.8      _this->egl_data->egl_display = _this->egl_data->eglGetDisplay(native_display);
     4.9      if (!_this->egl_data->egl_display) {
    4.10          return SDL_SetError("Could not get EGL display");
    4.11 @@ -224,6 +225,7 @@
    4.12      if (_this->egl_data->eglInitialize(_this->egl_data->egl_display, NULL, NULL) != EGL_TRUE) {
    4.13          return SDL_SetError("Could not initialize EGL");
    4.14      }
    4.15 +#endif
    4.16  
    4.17      _this->egl_data->dll_handle = dll_handle;
    4.18      _this->egl_data->egl_dll_handle = egl_dll_handle;
     5.1 --- a/src/video/winrt/SDL_winrtopengles.cpp	Fri Mar 21 10:40:15 2014 -0400
     5.2 +++ b/src/video/winrt/SDL_winrtopengles.cpp	Sat Mar 22 20:48:18 2014 -0400
     5.3 @@ -20,8 +20,6 @@
     5.4  */
     5.5  #include "../../SDL_internal.h"
     5.6  
     5.7 -// TODO: WinRT, make this file compile via C code
     5.8 -
     5.9  #if SDL_VIDEO_DRIVER_WINRT && SDL_VIDEO_OPENGL_EGL
    5.10  
    5.11  /* EGL implementation of SDL OpenGL support */
    5.12 @@ -29,13 +27,77 @@
    5.13  #include "SDL_winrtvideo_cpp.h"
    5.14  extern "C" {
    5.15  #include "SDL_winrtopengles.h"
    5.16 +#include "SDL_loadso.h"
    5.17  }
    5.18  
    5.19 -#define EGL_D3D11_ONLY_DISPLAY_ANGLE ((NativeDisplayType) -3)
    5.20 +/* Windows includes */
    5.21 +#include <wrl/client.h>
    5.22 +using namespace Windows::UI::Core;
    5.23 +
    5.24 +/* ANGLE/WinRT constants */
    5.25 +static const int ANGLE_D3D_FEATURE_LEVEL_ANY = 0;
    5.26 +
    5.27 +
    5.28 +/*
    5.29 + * SDL/EGL top-level implementation
    5.30 + */
    5.31  
    5.32  extern "C" int
    5.33 -WINRT_GLES_LoadLibrary(_THIS, const char *path) {
    5.34 -    return SDL_EGL_LoadLibrary(_this, path, EGL_D3D11_ONLY_DISPLAY_ANGLE);
    5.35 +WINRT_GLES_LoadLibrary(_THIS, const char *path)
    5.36 +{
    5.37 +    SDL_VideoData *video_data = (SDL_VideoData *)_this->driverdata;
    5.38 +
    5.39 +    if (SDL_EGL_LoadLibrary(_this, path, EGL_DEFAULT_DISPLAY) != 0) {
    5.40 +        return -1;
    5.41 +    }
    5.42 +
    5.43 +    /* Load ANGLE/WinRT-specific functions */
    5.44 +    CreateWinrtEglWindow_Function CreateWinrtEglWindow = (CreateWinrtEglWindow_Function) SDL_LoadFunction(_this->egl_data->egl_dll_handle, "CreateWinrtEglWindow");
    5.45 +    if (!CreateWinrtEglWindow) {
    5.46 +        return SDL_SetError("Could not retrieve ANGLE/WinRT function CreateWinrtEglWindow");
    5.47 +    }
    5.48 +
    5.49 +    /* Create an ANGLE/WinRT EGL-window */
    5.50 +    /* TODO, WinRT: check for XAML usage before accessing the CoreWindow, as not doing so could lead to a crash */
    5.51 +    CoreWindow ^ native_win = CoreWindow::GetForCurrentThread();
    5.52 +    Microsoft::WRL::ComPtr<IUnknown> cpp_win = reinterpret_cast<IUnknown *>(native_win);
    5.53 +    HRESULT result = CreateWinrtEglWindow(cpp_win, ANGLE_D3D_FEATURE_LEVEL_ANY, &(video_data->winrtEglWindow));
    5.54 +    if (FAILED(result)) {
    5.55 +        return -1;
    5.56 +    }
    5.57 +
    5.58 +    /* Call eglGetDisplay and eglInitialize as appropriate.  On other
    5.59 +     * platforms, this would probably get done by SDL_EGL_LoadLibrary,
    5.60 +     * however ANGLE/WinRT's current implementation (as of Mar 22, 2014) of
    5.61 +     * eglGetDisplay requires that a C++ object be passed into it, so the
    5.62 +     * call will be made in this file, a C++ file, instead.
    5.63 +     */
    5.64 +    Microsoft::WRL::ComPtr<IUnknown> cpp_display = video_data->winrtEglWindow;
    5.65 +    _this->egl_data->egl_display = ((eglGetDisplay_Function)_this->egl_data->eglGetDisplay)(cpp_display);
    5.66 +    if (!_this->egl_data->egl_display) {
    5.67 +        return SDL_SetError("Could not get EGL display");
    5.68 +    }
    5.69 +    
    5.70 +    if (_this->egl_data->eglInitialize(_this->egl_data->egl_display, NULL, NULL) != EGL_TRUE) {
    5.71 +        return SDL_SetError("Could not initialize EGL");
    5.72 +    }
    5.73 +
    5.74 +    return 0;
    5.75 +}
    5.76 +
    5.77 +extern "C" void
    5.78 +WINRT_GLES_UnloadLibrary(_THIS)
    5.79 +{
    5.80 +    SDL_VideoData *video_data = (SDL_VideoData *)_this->driverdata;
    5.81 +
    5.82 +    /* Release SDL's own COM reference to the ANGLE/WinRT IWinrtEglWindow */
    5.83 +    if (video_data->winrtEglWindow) {
    5.84 +        video_data->winrtEglWindow->Release();
    5.85 +        video_data->winrtEglWindow = nullptr;
    5.86 +    }
    5.87 +
    5.88 +    /* Perform the bulk of the unloading */
    5.89 +    SDL_EGL_UnloadLibrary(_this);
    5.90  }
    5.91  
    5.92  extern "C" {
     6.1 --- a/src/video/winrt/SDL_winrtopengles.h	Fri Mar 21 10:40:15 2014 -0400
     6.2 +++ b/src/video/winrt/SDL_winrtopengles.h	Sat Mar 22 20:48:18 2014 -0400
     6.3 @@ -31,16 +31,34 @@
     6.4  /* OpenGLES functions */
     6.5  #define WINRT_GLES_GetAttribute SDL_EGL_GetAttribute
     6.6  #define WINRT_GLES_GetProcAddress SDL_EGL_GetProcAddress
     6.7 -#define WINRT_GLES_UnloadLibrary SDL_EGL_UnloadLibrary
     6.8  #define WINRT_GLES_SetSwapInterval SDL_EGL_SetSwapInterval
     6.9  #define WINRT_GLES_GetSwapInterval SDL_EGL_GetSwapInterval
    6.10  #define WINRT_GLES_DeleteContext SDL_EGL_DeleteContext
    6.11  
    6.12  extern int WINRT_GLES_LoadLibrary(_THIS, const char *path);
    6.13 +extern void WINRT_GLES_UnloadLibrary(_THIS);
    6.14  extern SDL_GLContext WINRT_GLES_CreateContext(_THIS, SDL_Window * window);
    6.15  extern void WINRT_GLES_SwapWindow(_THIS, SDL_Window * window);
    6.16  extern int WINRT_GLES_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context);
    6.17  
    6.18 +
    6.19 +#ifdef __cplusplus
    6.20 +
    6.21 +/* Typedefs for ANGLE/WinRT's C++-based native-display and native-window types,
    6.22 + * which are used when calling eglGetDisplay and eglCreateWindowSurface.
    6.23 + */
    6.24 +typedef Microsoft::WRL::ComPtr<IUnknown> WINRT_EGLNativeWindowType;
    6.25 +typedef WINRT_EGLNativeWindowType WINRT_EGLNativeDisplayType;
    6.26 +
    6.27 +/* Function pointer typedefs for ANGLE/WinRT's functions that require
    6.28 + * parameter customization [by passing in C++ objects].
    6.29 + */
    6.30 +typedef EGLDisplay (EGLAPIENTRY *eglGetDisplay_Function)(WINRT_EGLNativeWindowType);
    6.31 +typedef EGLSurface (EGLAPIENTRY *eglCreateWindowSurface_Function)(EGLDisplay, EGLConfig, WINRT_EGLNativeWindowType, const EGLint *);
    6.32 +typedef HRESULT (EGLAPIENTRY *CreateWinrtEglWindow_Function)(Microsoft::WRL::ComPtr<IUnknown>, int, IUnknown ** result);
    6.33 +
    6.34 +#endif /* __cplusplus */
    6.35 +
    6.36  #endif /* SDL_VIDEO_DRIVER_WINRT && SDL_VIDEO_OPENGL_EGL */
    6.37  
    6.38  #endif /* _SDL_winrtopengles_h */
     7.1 --- a/src/video/winrt/SDL_winrtvideo.cpp	Fri Mar 21 10:40:15 2014 -0400
     7.2 +++ b/src/video/winrt/SDL_winrtvideo.cpp	Sat Mar 22 20:48:18 2014 -0400
     7.3 @@ -30,6 +30,7 @@
     7.4  
     7.5  /* Windows includes */
     7.6  #include <agile.h>
     7.7 +#include <wrl/client.h>
     7.8  using namespace Windows::UI::Core;
     7.9  
    7.10  
    7.11 @@ -86,6 +87,15 @@
    7.12      if (device == WINRT_GlobalSDLVideoDevice) {
    7.13          WINRT_GlobalSDLVideoDevice = NULL;
    7.14      }
    7.15 +
    7.16 +    if (device->driverdata) {
    7.17 +        SDL_VideoData * video_data = (SDL_VideoData *)device->driverdata;
    7.18 +        if (video_data->winrtEglWindow) {
    7.19 +            video_data->winrtEglWindow->Release();
    7.20 +        }
    7.21 +        SDL_free(video_data);
    7.22 +    }
    7.23 +
    7.24      SDL_free(device);
    7.25  }
    7.26  
    7.27 @@ -93,6 +103,7 @@
    7.28  WINRT_CreateDevice(int devindex)
    7.29  {
    7.30      SDL_VideoDevice *device;
    7.31 +    SDL_VideoData *data;
    7.32  
    7.33      /* Initialize all variables that we clean on shutdown */
    7.34      device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
    7.35 @@ -104,6 +115,14 @@
    7.36          return (0);
    7.37      }
    7.38  
    7.39 +    data = (SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
    7.40 +    if (!data) {
    7.41 +        SDL_OutOfMemory();
    7.42 +        return (0);
    7.43 +    }
    7.44 +    SDL_zerop(data);
    7.45 +    device->driverdata = data;
    7.46 +
    7.47      /* Set the function pointers */
    7.48      device->VideoInit = WINRT_VideoInit;
    7.49      device->VideoQuit = WINRT_VideoQuit;
    7.50 @@ -301,29 +320,25 @@
    7.51          data->egl_surface = EGL_NO_SURFACE;
    7.52      } else {
    7.53          /* OpenGL ES 2 was reuqested.  Set up an EGL surface. */
    7.54 +        SDL_VideoData * video_data = (SDL_VideoData *)_this->driverdata;
    7.55  
    7.56 -        /* HACK: ANGLE/WinRT currently uses non-pointer, C++ objects to represent
    7.57 -           native windows.  The object only contains a single pointer to a COM
    7.58 -           interface pointer, which on x86 appears to be castable to the object
    7.59 -           without apparant problems.  On other platforms, notable ARM and x64,
    7.60 -           doing so will cause a crash.  To avoid this crash, we'll bypass
    7.61 -           SDL's normal call to eglCreateWindowSurface, which is invoked from C
    7.62 -           code, and call it here, where an appropriate C++ object may be
    7.63 -           passed in.
    7.64 +        /* Call SDL_EGL_ChooseConfig and eglCreateWindowSurface directly,
    7.65 +         * rather than via SDL_EGL_CreateSurface, as ANGLE/WinRT requires
    7.66 +         * a C++ object, ComPtr<IUnknown>, to be passed into
    7.67 +         * eglCreateWindowSurface.
    7.68           */
    7.69 -        typedef EGLSurface (*eglCreateWindowSurfaceFunction)(EGLDisplay dpy, EGLConfig config,
    7.70 -            Microsoft::WRL::ComPtr<IUnknown> win,
    7.71 -            const EGLint *attrib_list);
    7.72 -        eglCreateWindowSurfaceFunction WINRT_eglCreateWindowSurface =
    7.73 -            (eglCreateWindowSurfaceFunction) _this->egl_data->eglCreateWindowSurface;
    7.74 +        if (SDL_EGL_ChooseConfig(_this) != 0) {
    7.75 +            char buf[512];
    7.76 +            SDL_snprintf(buf, sizeof(buf), "SDL_EGL_ChooseConfig failed: %s", SDL_GetError());
    7.77 +            return SDL_SetError(buf);
    7.78 +        }
    7.79  
    7.80 -        Microsoft::WRL::ComPtr<IUnknown> nativeWindow = reinterpret_cast<IUnknown *>(data->coreWindow.Get());
    7.81 -        data->egl_surface = WINRT_eglCreateWindowSurface(
    7.82 +        Microsoft::WRL::ComPtr<IUnknown> cpp_winrtEglWindow = video_data->winrtEglWindow;
    7.83 +        data->egl_surface = ((eglCreateWindowSurface_Function)_this->egl_data->eglCreateWindowSurface)(
    7.84              _this->egl_data->egl_display,
    7.85              _this->egl_data->egl_config,
    7.86 -            nativeWindow, NULL);
    7.87 +            cpp_winrtEglWindow, NULL);
    7.88          if (data->egl_surface == NULL) {
    7.89 -            // TODO, WinRT: see if eglCreateWindowSurface, or its callee(s), sets an error message.  If so, attach it to the SDL error.
    7.90              return SDL_SetError("eglCreateWindowSurface failed");
    7.91          }
    7.92      }
     8.1 --- a/src/video/winrt/SDL_winrtvideo_cpp.h	Fri Mar 21 10:40:15 2014 -0400
     8.2 +++ b/src/video/winrt/SDL_winrtvideo_cpp.h	Sat Mar 22 20:48:18 2014 -0400
     8.3 @@ -34,6 +34,13 @@
     8.4  #include "../SDL_egl_c.h"
     8.5  }
     8.6  
     8.7 +/* Private display data */
     8.8 +typedef struct SDL_VideoData {
     8.9 +    /* An object created by ANGLE/WinRT (OpenGL ES 2 for WinRT) that gets
    8.10 +     * passed to eglGetDisplay and eglCreateWindowSurface:
    8.11 +     */
    8.12 +    IUnknown *winrtEglWindow;
    8.13 +} SDL_VideoData;
    8.14  
    8.15  /* The global, WinRT, SDL Window.
    8.16     For now, SDL/WinRT only supports one window (due to platform limitations of