From b51a3206c84e770bbfbb3c79124535c4f77cc318 Mon Sep 17 00:00:00 2001 From: David Ludwig Date: Sat, 22 Mar 2014 20:48:18 -0400 Subject: [PATCH] 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 | 2 +- .../SDL/SDL-WinRT_VS2012.vcxproj.filters | 6 +- include/SDL_egl.h | 4 +- src/video/SDL_egl.c | 2 + src/video/winrt/SDL_winrtopengles.cpp | 72 +++++++++++++++++-- src/video/winrt/SDL_winrtopengles.h | 20 +++++- src/video/winrt/SDL_winrtvideo.cpp | 49 ++++++++----- src/video/winrt/SDL_winrtvideo_cpp.h | 7 ++ 8 files changed, 133 insertions(+), 29 deletions(-) diff --git a/VisualC-WinRT/SDL/SDL-WinRT_VS2012.vcxproj b/VisualC-WinRT/SDL/SDL-WinRT_VS2012.vcxproj index 9649279ad..d19b9d0ec 100644 --- a/VisualC-WinRT/SDL/SDL-WinRT_VS2012.vcxproj +++ b/VisualC-WinRT/SDL/SDL-WinRT_VS2012.vcxproj @@ -324,7 +324,7 @@ - + diff --git a/VisualC-WinRT/SDL/SDL-WinRT_VS2012.vcxproj.filters b/VisualC-WinRT/SDL/SDL-WinRT_VS2012.vcxproj.filters index 7cdc81daf..b5050af5f 100644 --- a/VisualC-WinRT/SDL/SDL-WinRT_VS2012.vcxproj.filters +++ b/VisualC-WinRT/SDL/SDL-WinRT_VS2012.vcxproj.filters @@ -633,9 +633,6 @@ Source Files - - Source Files - Header Files @@ -666,6 +663,9 @@ Source Files + + Source Files + diff --git a/include/SDL_egl.h b/include/SDL_egl.h index d312f0425..a345818ae 100644 --- a/include/SDL_egl.h +++ b/include/SDL_egl.h @@ -394,8 +394,8 @@ typedef enum { #if __WINRT__ #include 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 cfba20071..e0efa64ef 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 6f3ddcedf..4a84174d0 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 +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 cpp_win = reinterpret_cast(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 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 f051132eb..ae1739171 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 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, 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 4a5301f22..f54e9855b 100644 --- a/src/video/winrt/SDL_winrtvideo.cpp +++ b/src/video/winrt/SDL_winrtvideo.cpp @@ -30,6 +30,7 @@ /* Windows includes */ #include +#include 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, to be passed into + * eglCreateWindowSurface. */ - typedef EGLSurface (*eglCreateWindowSurfaceFunction)(EGLDisplay dpy, EGLConfig config, - Microsoft::WRL::ComPtr win, - const EGLint *attrib_list); - eglCreateWindowSurfaceFunction WINRT_eglCreateWindowSurface = - (eglCreateWindowSurfaceFunction) _this->egl_data->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 nativeWindow = reinterpret_cast(data->coreWindow.Get()); - data->egl_surface = WINRT_eglCreateWindowSurface( + Microsoft::WRL::ComPtr 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 b5f44510d..119f5b8f2 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