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.
This commit is contained in:
David Ludwig 2014-03-22 20:48:18 -04:00
parent cc52939853
commit b51a3206c8
8 changed files with 133 additions and 29 deletions

View File

@ -324,7 +324,7 @@
<ClInclude Include="..\..\src\video\SDL_blit_auto.h" /> <ClInclude Include="..\..\src\video\SDL_blit_auto.h" />
<ClInclude Include="..\..\src\video\SDL_blit_copy.h" /> <ClInclude Include="..\..\src\video\SDL_blit_copy.h" />
<ClInclude Include="..\..\src\video\SDL_blit_slow.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_pixels_c.h" />
<ClInclude Include="..\..\src\video\SDL_rect_c.h" /> <ClInclude Include="..\..\src\video\SDL_rect_c.h" />
<ClInclude Include="..\..\src\video\SDL_RLEaccel_c.h" /> <ClInclude Include="..\..\src\video\SDL_RLEaccel_c.h" />

View File

@ -633,9 +633,6 @@
<ClInclude Include="..\..\src\video\winrt\SDL_winrtopengles.h"> <ClInclude Include="..\..\src\video\winrt\SDL_winrtopengles.h">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\video\SDL_egl.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\..\include\SDL_opengles2.h"> <ClInclude Include="..\..\include\SDL_opengles2.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
@ -666,6 +663,9 @@
<ClInclude Include="..\..\src\render\direct3d11\SDL_render_winrt.h"> <ClInclude Include="..\..\src\render\direct3d11\SDL_render_winrt.h">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\video\SDL_egl_c.h">
<Filter>Source Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Filter Include="Header Files"> <Filter Include="Header Files">

View File

@ -394,8 +394,8 @@ typedef enum {
#if __WINRT__ #if __WINRT__
#include <Unknwn.h> #include <Unknwn.h>
typedef IUnknown * EGLNativeWindowType; typedef IUnknown * EGLNativeWindowType;
typedef int EGLNativeDisplayType; typedef IUnknown * EGLNativePixmapType;
typedef HBITMAP EGLNativePixmapType; typedef IUnknown * EGLNativeDisplayType;
#else #else
typedef HDC EGLNativeDisplayType; typedef HDC EGLNativeDisplayType;
typedef HBITMAP EGLNativePixmapType; typedef HBITMAP EGLNativePixmapType;

View File

@ -216,6 +216,7 @@ SDL_EGL_LoadLibrary(_THIS, const char *egl_path, NativeDisplayType native_displa
LOAD_FUNC(eglWaitGL); LOAD_FUNC(eglWaitGL);
LOAD_FUNC(eglBindAPI); LOAD_FUNC(eglBindAPI);
#if !defined(__WINRT__)
_this->egl_data->egl_display = _this->egl_data->eglGetDisplay(native_display); _this->egl_data->egl_display = _this->egl_data->eglGetDisplay(native_display);
if (!_this->egl_data->egl_display) { if (!_this->egl_data->egl_display) {
return SDL_SetError("Could not get 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) { if (_this->egl_data->eglInitialize(_this->egl_data->egl_display, NULL, NULL) != EGL_TRUE) {
return SDL_SetError("Could not initialize EGL"); return SDL_SetError("Could not initialize EGL");
} }
#endif
_this->egl_data->dll_handle = dll_handle; _this->egl_data->dll_handle = dll_handle;
_this->egl_data->egl_dll_handle = egl_dll_handle; _this->egl_data->egl_dll_handle = egl_dll_handle;

View File

@ -20,8 +20,6 @@
*/ */
#include "../../SDL_internal.h" #include "../../SDL_internal.h"
// TODO: WinRT, make this file compile via C code
#if SDL_VIDEO_DRIVER_WINRT && SDL_VIDEO_OPENGL_EGL #if SDL_VIDEO_DRIVER_WINRT && SDL_VIDEO_OPENGL_EGL
/* EGL implementation of SDL OpenGL support */ /* EGL implementation of SDL OpenGL support */
@ -29,13 +27,77 @@
#include "SDL_winrtvideo_cpp.h" #include "SDL_winrtvideo_cpp.h"
extern "C" { extern "C" {
#include "SDL_winrtopengles.h" #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 extern "C" int
WINRT_GLES_LoadLibrary(_THIS, const char *path) { WINRT_GLES_LoadLibrary(_THIS, const char *path)
return SDL_EGL_LoadLibrary(_this, path, EGL_D3D11_ONLY_DISPLAY_ANGLE); {
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" { extern "C" {

View File

@ -31,16 +31,34 @@
/* OpenGLES functions */ /* OpenGLES functions */
#define WINRT_GLES_GetAttribute SDL_EGL_GetAttribute #define WINRT_GLES_GetAttribute SDL_EGL_GetAttribute
#define WINRT_GLES_GetProcAddress SDL_EGL_GetProcAddress #define WINRT_GLES_GetProcAddress SDL_EGL_GetProcAddress
#define WINRT_GLES_UnloadLibrary SDL_EGL_UnloadLibrary
#define WINRT_GLES_SetSwapInterval SDL_EGL_SetSwapInterval #define WINRT_GLES_SetSwapInterval SDL_EGL_SetSwapInterval
#define WINRT_GLES_GetSwapInterval SDL_EGL_GetSwapInterval #define WINRT_GLES_GetSwapInterval SDL_EGL_GetSwapInterval
#define WINRT_GLES_DeleteContext SDL_EGL_DeleteContext #define WINRT_GLES_DeleteContext SDL_EGL_DeleteContext
extern int WINRT_GLES_LoadLibrary(_THIS, const char *path); 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 SDL_GLContext WINRT_GLES_CreateContext(_THIS, SDL_Window * window);
extern void WINRT_GLES_SwapWindow(_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); 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_VIDEO_DRIVER_WINRT && SDL_VIDEO_OPENGL_EGL */
#endif /* _SDL_winrtopengles_h */ #endif /* _SDL_winrtopengles_h */

View File

@ -30,6 +30,7 @@
/* Windows includes */ /* Windows includes */
#include <agile.h> #include <agile.h>
#include <wrl/client.h>
using namespace Windows::UI::Core; using namespace Windows::UI::Core;
@ -86,6 +87,15 @@ WINRT_DeleteDevice(SDL_VideoDevice * device)
if (device == WINRT_GlobalSDLVideoDevice) { if (device == WINRT_GlobalSDLVideoDevice) {
WINRT_GlobalSDLVideoDevice = NULL; 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); SDL_free(device);
} }
@ -93,6 +103,7 @@ static SDL_VideoDevice *
WINRT_CreateDevice(int devindex) WINRT_CreateDevice(int devindex)
{ {
SDL_VideoDevice *device; SDL_VideoDevice *device;
SDL_VideoData *data;
/* Initialize all variables that we clean on shutdown */ /* Initialize all variables that we clean on shutdown */
device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice)); device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
@ -104,6 +115,14 @@ WINRT_CreateDevice(int devindex)
return (0); 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 */ /* Set the function pointers */
device->VideoInit = WINRT_VideoInit; device->VideoInit = WINRT_VideoInit;
device->VideoQuit = WINRT_VideoQuit; device->VideoQuit = WINRT_VideoQuit;
@ -301,29 +320,25 @@ WINRT_CreateWindow(_THIS, SDL_Window * window)
data->egl_surface = EGL_NO_SURFACE; data->egl_surface = EGL_NO_SURFACE;
} else { } else {
/* OpenGL ES 2 was reuqested. Set up an EGL surface. */ /* 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 /* Call SDL_EGL_ChooseConfig and eglCreateWindowSurface directly,
native windows. The object only contains a single pointer to a COM * rather than via SDL_EGL_CreateSurface, as ANGLE/WinRT requires
interface pointer, which on x86 appears to be castable to the object * a C++ object, ComPtr<IUnknown>, to be passed into
without apparant problems. On other platforms, notable ARM and x64, * eglCreateWindowSurface.
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.
*/ */
typedef EGLSurface (*eglCreateWindowSurfaceFunction)(EGLDisplay dpy, EGLConfig config, if (SDL_EGL_ChooseConfig(_this) != 0) {
Microsoft::WRL::ComPtr<IUnknown> win, char buf[512];
const EGLint *attrib_list); SDL_snprintf(buf, sizeof(buf), "SDL_EGL_ChooseConfig failed: %s", SDL_GetError());
eglCreateWindowSurfaceFunction WINRT_eglCreateWindowSurface = return SDL_SetError(buf);
(eglCreateWindowSurfaceFunction) _this->egl_data->eglCreateWindowSurface; }
Microsoft::WRL::ComPtr<IUnknown> nativeWindow = reinterpret_cast<IUnknown *>(data->coreWindow.Get()); Microsoft::WRL::ComPtr<IUnknown> cpp_winrtEglWindow = video_data->winrtEglWindow;
data->egl_surface = WINRT_eglCreateWindowSurface( data->egl_surface = ((eglCreateWindowSurface_Function)_this->egl_data->eglCreateWindowSurface)(
_this->egl_data->egl_display, _this->egl_data->egl_display,
_this->egl_data->egl_config, _this->egl_data->egl_config,
nativeWindow, NULL); cpp_winrtEglWindow, NULL);
if (data->egl_surface == 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"); return SDL_SetError("eglCreateWindowSurface failed");
} }
} }

View File

@ -34,6 +34,13 @@ extern "C" {
#include "../SDL_egl_c.h" #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. /* The global, WinRT, SDL Window.
For now, SDL/WinRT only supports one window (due to platform limitations of For now, SDL/WinRT only supports one window (due to platform limitations of