From d0ca83bf6ca67807ed41dc7fba6208ab7e5f2857 Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 4 Jul 2018 18:54:46 +0100 Subject: [PATCH] Support creating OpenGL ES contexts via WGL/GLX. Closes #1009 --- renderdoc/driver/gl/cgl_platform.cpp | 3 +- renderdoc/driver/gl/egl_platform.cpp | 27 +-- renderdoc/driver/gl/gl_common.h | 12 +- renderdoc/driver/gl/gl_replay.cpp | 66 +++++- renderdoc/driver/gl/glx_dispatch_table.h | 2 + renderdoc/driver/gl/glx_hooks.cpp | 13 +- renderdoc/driver/gl/glx_platform.cpp | 48 +++-- renderdoc/driver/gl/wgl_dispatch_table.h | 6 +- renderdoc/driver/gl/wgl_hooks.cpp | 21 +- renderdoc/driver/gl/wgl_platform.cpp | 259 +++++++++++++++-------- 10 files changed, 323 insertions(+), 134 deletions(-) diff --git a/renderdoc/driver/gl/cgl_platform.cpp b/renderdoc/driver/gl/cgl_platform.cpp index b1eade423..f45e15595 100644 --- a/renderdoc/driver/gl/cgl_platform.cpp +++ b/renderdoc/driver/gl/cgl_platform.cpp @@ -45,8 +45,9 @@ class CGLPlatform : public GLPlatform } void *GetReplayFunction(const char *funcname) { return NULL; } + bool CanCreateGLESContext() { return false; } bool PopulateForReplay() { return false; } - ReplayStatus InitialiseAPI(GLWindowingData &replayContext) + ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api) { return ReplayStatus::APIUnsupported; } diff --git a/renderdoc/driver/gl/egl_platform.cpp b/renderdoc/driver/gl/egl_platform.cpp index d471474fb..755cb9e5d 100644 --- a/renderdoc/driver/gl/egl_platform.cpp +++ b/renderdoc/driver/gl/egl_platform.cpp @@ -179,18 +179,12 @@ class EGLPlatform : public GLPlatform EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL_NONE}; - struct - { - int major; - int minor; - } versions[] = { - {3, 2}, {3, 1}, {3, 0}, - }; + std::vector versions = GetReplayVersions(RDCDriver::OpenGLES); - for(size_t v = 0; v < ARRAY_COUNT(versions); v++) + for(GLVersion v : versions) { - verAttribs[1] = versions[v].major; - verAttribs[3] = versions[v].minor; + verAttribs[1] = v.major; + verAttribs[3] = v.minor; ctx = EGL.CreateContext(eglDisplay, config, share_ctx, verAttribs); if(ctx) @@ -236,9 +230,18 @@ class EGLPlatform : public GLPlatform return ret; } - bool PopulateForReplay() { return EGL.PopulateForReplay(); } - ReplayStatus InitialiseAPI(GLWindowingData &replayContext) + bool CanCreateGLESContext() { + // as long as we can get libEGL we're fine + return GetEGLHandle() != NULL; + } + + bool PopulateForReplay() { return EGL.PopulateForReplay(); } + ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api) + { + // we only support replaying GLES through EGL + RDCASSERT(api == RDCDriver::OpenGLES); + EGL.BindAPI(EGL_OPENGL_ES_API); EGLDisplay eglDisplay = EGL.GetDisplay(EGL_DEFAULT_DISPLAY); diff --git a/renderdoc/driver/gl/gl_common.h b/renderdoc/driver/gl/gl_common.h index 71bcf78b4..2edec60ea 100644 --- a/renderdoc/driver/gl/gl_common.h +++ b/renderdoc/driver/gl/gl_common.h @@ -26,6 +26,7 @@ #pragma once #include "common/common.h" +#include "core/core.h" #include "maths/vec.h" // typed enum so that templates will pick up specialisations @@ -259,11 +260,20 @@ struct GLPlatform virtual void DrawQuads(float width, float height, const std::vector &vertices) = 0; // for initialisation at replay time + virtual bool CanCreateGLESContext() = 0; virtual bool PopulateForReplay() = 0; - virtual ReplayStatus InitialiseAPI(GLWindowingData &replayContext) = 0; + virtual ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api) = 0; virtual void *GetReplayFunction(const char *funcname) = 0; }; +struct GLVersion +{ + int major; + int minor; +}; + +std::vector GetReplayVersions(RDCDriver api); + #if defined(RENDERDOC_SUPPORT_GL) // platform specific (WGL, GLX, etc) diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index ab9befd82..28606504e 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -3241,7 +3241,7 @@ ReplayStatus CreateReplayDevice(RDCFile *rdc, GLPlatform &platform, IReplayDrive GLWindowingData data = {}; - ReplayStatus status = platform.InitialiseAPI(data); + ReplayStatus status = platform.InitialiseAPI(data, rdc->GetDriver()); // any errors will be already printed, just pass the error up if(status != ReplayStatus::Succeeded) @@ -3312,8 +3312,9 @@ class GLDummyPlatform : public GLPlatform virtual void DrawQuads(float width, float height, const std::vector &vertices) {} virtual void *GetReplayFunction(const char *funcname) { return NULL; } // for initialisation at replay time + virtual bool CanCreateGLESContext() { return true; } virtual bool PopulateForReplay() { return true; } - virtual ReplayStatus InitialiseAPI(GLWindowingData &replayContext) + virtual ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api) { return ReplayStatus::Succeeded; } @@ -3340,6 +3341,24 @@ static StructuredProcessRegistration GLProcessRegistration(RDCDriver::OpenGL, &G static StructuredProcessRegistration GLESProcessRegistration(RDCDriver::OpenGLES, &GL_ProcessStructured); +std::vector GetReplayVersions(RDCDriver api) +{ + // try to create all versions from highest down to lowest in order to get the highest versioned + // context we can + if(api == RDCDriver::OpenGLES) + { + return { + {3, 2}, {3, 1}, {3, 0}, + }; + } + else + { + return { + {4, 6}, {4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}, {3, 3}, {3, 2}, + }; + } +} + #if defined(RENDERDOC_SUPPORT_GL) ReplayStatus GL_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) @@ -3367,15 +3386,46 @@ ReplayStatus GLES_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) { RDCDEBUG("Creating an OpenGL ES replay device"); - bool load_ok = GetEGLPlatform().PopulateForReplay(); - - if(!load_ok) + // for GLES replay, we try to use EGL if it's available. If it's not available, we look to see if + // we can create an OpenGL ES context via the platform GL functions + if(GetEGLPlatform().CanCreateGLESContext()) { - RDCERR("Couldn't find required EGL function addresses"); - return ReplayStatus::APIInitFailed; + bool load_ok = GetEGLPlatform().PopulateForReplay(); + + if(!load_ok) + { + RDCERR("Couldn't find required EGL function addresses"); + return ReplayStatus::APIInitFailed; + } + + return CreateReplayDevice(rdc, GetEGLPlatform(), driver); + } +#if defined(RENDERDOC_SUPPORT_GL) + else if(GetGLPlatform().CanCreateGLESContext()) + { + RDCDEBUG("libEGL is not available, falling back to EXT_create_context_es2_profile"); + + bool load_ok = GetGLPlatform().PopulateForReplay(); + + if(!load_ok) + { + RDCERR("Couldn't find required GLX function addresses"); + return ReplayStatus::APIInitFailed; + } + + return CreateReplayDevice(rdc, GetGLPlatform(), driver); } - return CreateReplayDevice(rdc, GetEGLPlatform(), driver); + RDCERR( + "libEGL not available, and GL cannot initialise or doesn't support " + "EXT_create_context_es2_profile"); + return ReplayStatus::APIInitFailed; +#else + // no GL support, no fallback apart from EGL + + RDCERR("libEGL is not available"); + return ReplayStatus::APIInitFailed; +#endif } static DriverRegistration GLESDriverRegistration(RDCDriver::OpenGLES, &GLES_CreateReplayDevice); diff --git a/renderdoc/driver/gl/glx_dispatch_table.h b/renderdoc/driver/gl/glx_dispatch_table.h index 5ec720797..cd503cf3e 100644 --- a/renderdoc/driver/gl/glx_dispatch_table.h +++ b/renderdoc/driver/gl/glx_dispatch_table.h @@ -36,6 +36,7 @@ typedef int (*PFN_glXGetConfig)(Display *dpy, XVisualInfo *vis, int attrib, int typedef Bool (*PFN_glXIsDirect)(Display *dpy, GLXContext ctx); typedef __GLXextFuncPtr (*PFN_glXGetProcAddress)(const GLubyte *); typedef __GLXextFuncPtr (*PFN_glXGetProcAddressARB)(const GLubyte *); +typedef const char *(*PFN_glXQueryExtensionsString)(Display *dpy, int screen); typedef PFNGLXGETVISUALFROMFBCONFIGPROC PFN_glXGetVisualFromFBConfig; typedef PFNGLXMAKECONTEXTCURRENTPROC PFN_glXMakeContextCurrent; typedef PFNGLXCREATEWINDOWPROC PFN_glXCreateWindow; @@ -78,6 +79,7 @@ typedef void (*PFN_glEnd)(); FUNC(glXChooseFBConfig); \ FUNC(glXGetFBConfigAttrib); \ FUNC(glXQueryDrawable); \ + FUNC(glXQueryExtensionsString); \ FUNC(glXCreatePbuffer); \ FUNC(glXDestroyPbuffer); \ FUNC(glGetIntegerv); \ diff --git a/renderdoc/driver/gl/glx_hooks.cpp b/renderdoc/driver/gl/glx_hooks.cpp index 1d4283ec2..e5c8dee86 100644 --- a/renderdoc/driver/gl/glx_hooks.cpp +++ b/renderdoc/driver/gl/glx_hooks.cpp @@ -197,7 +197,7 @@ HOOK_EXPORT GLXContext glXCreateContextAttribsARB(Display *dpy, GLXFBConfig conf RDCDEBUG("glXCreateContextAttribsARB:"); - bool core = false; + bool core = false, es = false; int *a = (int *)attribs; while(*a) @@ -205,11 +205,20 @@ HOOK_EXPORT GLXContext glXCreateContextAttribsARB(Display *dpy, GLXFBConfig conf RDCDEBUG("%x: %d", a[0], a[1]); if(a[0] == GLX_CONTEXT_PROFILE_MASK_ARB) - core = (a[1] & GLX_CONTEXT_CORE_PROFILE_BIT_ARB); + { + core = (a[1] & GLX_CONTEXT_CORE_PROFILE_BIT_ARB) != 0; + es = (a[1] & (GLX_CONTEXT_ES_PROFILE_BIT_EXT | GLX_CONTEXT_ES2_PROFILE_BIT_EXT)) != 0; + } a += 2; } + if(es) + { + glxhook.driver.SetDriverType(RDCDriver::OpenGLES); + core = true; + } + GLXContext ret = GLX.glXCreateContextAttribsARB(dpy, config, shareList, direct, attribs); // don't continue if context creation failed diff --git a/renderdoc/driver/gl/glx_platform.cpp b/renderdoc/driver/gl/glx_platform.cpp index b898c41d4..10aa22063 100644 --- a/renderdoc/driver/gl/glx_platform.cpp +++ b/renderdoc/driver/gl/glx_platform.cpp @@ -296,9 +296,32 @@ class GLXPlatform : public GLPlatform return ret; } - bool PopulateForReplay() { return GLX.PopulateForReplay(); } - ReplayStatus InitialiseAPI(GLWindowingData &replayContext) + bool CanCreateGLESContext() { + bool success = GLX.PopulateForReplay(); + + // if we can't populate our functions we bail now. + if(!success) + return false; + + // we need to check for the presence of EXT_create_context_es2_profile + Display *dpy = RenderDoc::Inst().GetGlobalEnvironment().xlibDisplay; + + const char *exts = GLX.glXQueryExtensionsString(dpy, DefaultScreen(dpy)); + + bool ret = (strstr(exts, "EXT_create_context_es2_profile") != NULL); + + RDCDEBUG("%s find EXT_create_context_es2_profile to create GLES context", + ret ? "Could" : "Couldn't"); + + return ret; + } + + bool PopulateForReplay() { return GLX.PopulateForReplay(); } + ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api) + { + RDCASSERT(api == RDCDriver::OpenGL || api == RDCDriver::OpenGLES); + int attribs[64] = {0}; int i = 0; @@ -315,7 +338,8 @@ class GLXPlatform : public GLPlatform attribs[i++] = 0; #endif attribs[i++] = GLX_CONTEXT_PROFILE_MASK_ARB; - attribs[i++] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB; + attribs[i++] = api == RDCDriver::OpenGLES ? GLX_CONTEXT_ES2_PROFILE_BIT_EXT + : GLX_CONTEXT_CORE_PROFILE_BIT_ARB; Display *dpy = RenderDoc::Inst().GetGlobalEnvironment().xlibDisplay; @@ -339,25 +363,17 @@ class GLXPlatform : public GLPlatform GLXContext ctx = NULL; - // try to create all versions from 4.6 down to 3.2 in order to get the - // highest versioned context we can - struct - { - int major; - int minor; - } versions[] = { - {4, 6}, {4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}, {3, 3}, {3, 2}, - }; - { X11ErrorHandler prev = XSetErrorHandler(&NonFatalX11ErrorHandler); - for(size_t v = 0; v < ARRAY_COUNT(versions); v++) + std::vector versions = GetReplayVersions(api); + + for(GLVersion v : versions) { X11ErrorSeen = false; - major = versions[v].major; - minor = versions[v].minor; + major = v.major; + minor = v.minor; ctx = GLX.glXCreateContextAttribsARB(dpy, fbcfg[0], 0, true, attribs); if(ctx && !X11ErrorSeen) diff --git a/renderdoc/driver/gl/wgl_dispatch_table.h b/renderdoc/driver/gl/wgl_dispatch_table.h index 365c3920e..95f100aa1 100644 --- a/renderdoc/driver/gl/wgl_dispatch_table.h +++ b/renderdoc/driver/gl/wgl_dispatch_table.h @@ -40,9 +40,9 @@ typedef HDC(WINAPI *PFN_wglGetCurrentDC)(); // wgl extensions typedef PFNWGLCREATECONTEXTATTRIBSARBPROC PFN_wglCreateContextAttribsARB; -typedef PFNWGLCHOOSEPIXELFORMATARBPROC PFN_wglChoosePixelFormatARB; typedef PFNWGLGETPIXELFORMATATTRIBIVARBPROC PFN_wglGetPixelFormatAttribivARB; -typedef PFNWGLGETPIXELFORMATATTRIBFVARBPROC PFN_wglGetPixelFormatAttribfvARB; +typedef PFNWGLGETEXTENSIONSSTRINGEXTPROC PFN_wglGetExtensionsStringEXT; +typedef PFNWGLGETEXTENSIONSSTRINGARBPROC PFN_wglGetExtensionsStringARB; // gl functions (used for quad rendering on legacy contexts) typedef PFNGLGETINTEGERVPROC PFN_glGetIntegerv; @@ -83,6 +83,8 @@ typedef LONG(WINAPI *PFN_ChangeDisplaySettingsExW)(LPCWSTR, DEVMODEW *, HWND, DW FUNC("opengl32.dll", wglGetCurrentContext); \ FUNC("opengl32.dll", wglGetCurrentDC); \ FUNC("", wglGetPixelFormatAttribivARB); \ + FUNC("", wglGetExtensionsStringEXT); \ + FUNC("", wglGetExtensionsStringARB); \ FUNC("opengl32.dll", glGetIntegerv); \ FUNC("opengl32.dll", glPushMatrix); \ FUNC("opengl32.dll", glLoadIdentity); \ diff --git a/renderdoc/driver/gl/wgl_hooks.cpp b/renderdoc/driver/gl/wgl_hooks.cpp index 6cdec66ef..c787ca839 100644 --- a/renderdoc/driver/gl/wgl_hooks.cpp +++ b/renderdoc/driver/gl/wgl_hooks.cpp @@ -73,6 +73,14 @@ void WGLHook::PopulateFromContext(HDC dc, HGLRC rc) WGL.wglGetPixelFormatAttribivARB = (PFN_wglGetPixelFormatAttribivARB)WGL.wglGetProcAddress("wglGetPixelFormatAttribivARB"); + if(!WGL.wglGetExtensionsStringEXT) + WGL.wglGetExtensionsStringEXT = + (PFN_wglGetExtensionsStringEXT)WGL.wglGetProcAddress("wglGetExtensionsStringEXT"); + + if(!WGL.wglGetExtensionsStringARB) + WGL.wglGetExtensionsStringARB = + (PFN_wglGetExtensionsStringARB)WGL.wglGetProcAddress("wglGetExtensionsStringARB"); + GL.PopulateWithCallback([](const char *funcName) { ScopedSuppressHooking suppress; return (void *)WGL.wglGetProcAddress(funcName); @@ -279,7 +287,7 @@ static HGLRC WINAPI wglCreateContextAttribsARB_hooked(HDC dc, HGLRC hShareContex RDCDEBUG("wglCreateContextAttribsARB:"); - bool core = false; + bool core = false, es = false; int *a = (int *)attribs; while(*a) @@ -287,11 +295,20 @@ static HGLRC WINAPI wglCreateContextAttribsARB_hooked(HDC dc, HGLRC hShareContex RDCDEBUG("%x: %d", a[0], a[1]); if(a[0] == WGL_CONTEXT_PROFILE_MASK_ARB) - core = (a[1] & WGL_CONTEXT_CORE_PROFILE_BIT_ARB); + { + core = (a[1] & WGL_CONTEXT_CORE_PROFILE_BIT_ARB) != 0; + es = (a[1] & (WGL_CONTEXT_ES_PROFILE_BIT_EXT | WGL_CONTEXT_ES2_PROFILE_BIT_EXT)) != 0; + } a += 2; } + if(es) + { + wglhook.driver.SetDriverType(RDCDriver::OpenGLES); + core = true; + } + SetLastError(0); HGLRC ret = WGL.wglCreateContextAttribsARB(dc, hShareContext, attribs); diff --git a/renderdoc/driver/gl/wgl_platform.cpp b/renderdoc/driver/gl/wgl_platform.cpp index 4374537d5..7cda07365 100644 --- a/renderdoc/driver/gl/wgl_platform.cpp +++ b/renderdoc/driver/gl/wgl_platform.cpp @@ -25,6 +25,8 @@ #include "driver/gl/gl_common.h" #include "driver/gl/wgl_dispatch_table.h" +#define WINDOW_CLASS_NAME L"renderdocGLclass" + class WGLPlatform : public GLPlatform { bool MakeContextCurrent(GLWindowingData data) @@ -108,7 +110,7 @@ class WGLPlatform : public GLPlatform HWND w = window.win32.window; if(w == NULL) - w = CreateWindowEx(WS_EX_CLIENTEDGE, L"renderdocGLclass", L"", WS_OVERLAPPEDWINDOW, + w = CreateWindowEx(WS_EX_CLIENTEDGE, WINDOW_CLASS_NAME, L"", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, GetModuleHandle(NULL), NULL); @@ -237,88 +239,69 @@ class WGLPlatform : public GLPlatform return Process::GetFunctionAddress(Process::LoadModule("opengl32.dll"), funcname); } - bool PopulateForReplay() { return WGL.PopulateForReplay(); } - ReplayStatus InitialiseAPI(GLWindowingData &replayContext) + bool CanCreateGLESContext() { - WNDCLASSEX wc = {}; - wc.style = CS_OWNDC; - wc.cbSize = sizeof(WNDCLASSEX); - wc.lpfnWndProc = DefWindowProc; - wc.hInstance = GetModuleHandle(NULL); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.lpszClassName = L"renderdocGLclass"; + bool success = WGL.PopulateForReplay(); - WNDCLASSEX dummyCheck = {}; + // if we can't populate our functions we bail now. + if(!success) + return false; - // if the class isn't already registered, then register it. - if(!GetClassInfoExW(wc.hInstance, wc.lpszClassName, &dummyCheck)) - { - if(!RegisterClassEx(&wc)) - { - RDCERR("Couldn't register GL window class"); - return ReplayStatus::APIInitFailed; - } - } + // we need to check for the presence of EXT_create_context_es2_profile. + // Unfortunately on windows this means creating a trampoline context + success = RegisterClass(); - PIXELFORMATDESCRIPTOR pfd = {0}; - pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); - pfd.nVersion = 1; - pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; - pfd.iLayerType = PFD_MAIN_PLANE; - pfd.iPixelType = PFD_TYPE_RGBA; - pfd.cColorBits = 32; - pfd.cDepthBits = 24; - pfd.cStencilBits = 0; + if(!success) + return false; - HWND w = CreateWindowEx(WS_EX_CLIENTEDGE, L"renderdocGLclass", L"", WS_OVERLAPPEDWINDOW, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, - GetModuleHandle(NULL), NULL); + HWND w = NULL; + HDC dc = NULL; + HGLRC rc = NULL; - HDC dc = GetDC(w); + success = CreateTrampolineContext(w, dc, rc); - int pf = ::ChoosePixelFormat(dc, &pfd); - if(pf == 0) - { - ReleaseDC(w, dc); - DestroyWindow(w); - RDCERR("Couldn't choose pixel format"); + if(!success) + return false; + + const char *exts = NULL; + + if(WGL.wglGetExtensionsStringARB) + exts = WGL.wglGetExtensionsStringARB(dc); + + if(!exts && WGL.wglGetExtensionsStringEXT) + exts = WGL.wglGetExtensionsStringEXT(); + + if(!exts) + RDCERR("Couldn't get WGL extension string"); + + bool ret = (exts && strstr(exts, "EXT_create_context_es2_profile") != NULL); + + WGL.wglMakeCurrent(NULL, NULL); + WGL.wglDeleteContext(rc); + ReleaseDC(w, dc); + DestroyWindow(w); + + return ret; + } + + bool PopulateForReplay() { return WGL.PopulateForReplay(); } + ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api) + { + RDCASSERT(api == RDCDriver::OpenGL || api == RDCDriver::OpenGLES); + + bool success = RegisterClass(); + + if(!success) return ReplayStatus::APIInitFailed; - } - BOOL res = ::SetPixelFormat(dc, pf, &pfd); - if(res == FALSE) - { - ReleaseDC(w, dc); - DestroyWindow(w); - RDCERR("Couldn't set pixel format"); + HWND w = NULL; + HDC dc = NULL; + HGLRC rc = NULL; + + success = CreateTrampolineContext(w, dc, rc); + + if(!success) return ReplayStatus::APIInitFailed; - } - - HGLRC rc = WGL.wglCreateContext(dc); - if(rc == NULL) - { - ReleaseDC(w, dc); - DestroyWindow(w); - RDCERR("Couldn't create simple RC"); - return ReplayStatus::APIInitFailed; - } - - res = WGL.wglMakeCurrent(dc, rc); - if(res == FALSE) - { - WGL.wglMakeCurrent(NULL, NULL); - WGL.wglDeleteContext(rc); - ReleaseDC(w, dc); - DestroyWindow(w); - RDCERR("Couldn't make simple RC current"); - return ReplayStatus::APIInitFailed; - } - - // now we can fetch the extension functions we need and fill out WGL - WGL.wglCreateContextAttribsARB = - (PFN_wglCreateContextAttribsARB)WGL.wglGetProcAddress("wglCreateContextAttribsARB"); - WGL.wglGetPixelFormatAttribivARB = - (PFN_wglGetPixelFormatAttribivARB)WGL.wglGetProcAddress("wglGetPixelFormatAttribivARB"); if(!WGL.wglCreateContextAttribsARB || !WGL.wglGetPixelFormatAttribivARB) { @@ -337,18 +320,23 @@ class WGLPlatform : public GLPlatform // we don't use the default framebuffer (backbuffer) for anything, so we make it // tiny and with no depth/stencil bits + PIXELFORMATDESCRIPTOR pfd = {0}; + pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iLayerType = PFD_MAIN_PLANE; pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 24; pfd.cDepthBits = 0; pfd.cStencilBits = 0; - w = CreateWindowEx(WS_EX_CLIENTEDGE, L"renderdocGLclass", L"RenderDoc replay window", + w = CreateWindowEx(WS_EX_CLIENTEDGE, WINDOW_CLASS_NAME, L"RenderDoc replay window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 32, 32, NULL, NULL, GetModuleHandle(NULL), NULL); dc = GetDC(w); - pf = ChoosePixelFormat(dc, &pfd); + int pf = ChoosePixelFormat(dc, &pfd); if(pf == 0) { RDCERR("Couldn't choose pixel format"); @@ -357,7 +345,7 @@ class WGLPlatform : public GLPlatform return ReplayStatus::APIInitFailed; } - res = SetPixelFormat(dc, pf, &pfd); + BOOL res = SetPixelFormat(dc, pf, &pfd); if(res == FALSE) { RDCERR("Couldn't set pixel format"); @@ -382,24 +370,17 @@ class WGLPlatform : public GLPlatform attribs[i++] = 0; #endif attribs[i++] = WGL_CONTEXT_PROFILE_MASK_ARB; - attribs[i++] = WGL_CONTEXT_CORE_PROFILE_BIT_ARB; - - // try to create all versions from 4.6 down to 3.2 in order to get the - // highest versioned context we can - struct - { - int major; - int minor; - } versions[] = { - {4, 6}, {4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}, {3, 3}, {3, 2}, - }; + attribs[i++] = api == RDCDriver::OpenGLES ? WGL_CONTEXT_ES2_PROFILE_BIT_EXT + : WGL_CONTEXT_CORE_PROFILE_BIT_ARB; rc = NULL; - for(size_t v = 0; v < ARRAY_COUNT(versions); v++) + std::vector versions = GetReplayVersions(api); + + for(GLVersion v : versions) { - major = versions[v].major; - minor = versions[v].minor; + major = v.major; + minor = v.minor; rc = WGL.wglCreateContextAttribsARB(dc, NULL, attribs); if(rc) @@ -434,6 +415,104 @@ class WGLPlatform : public GLPlatform return ReplayStatus::Succeeded; } + bool CreateTrampolineContext(HWND &w, HDC &dc, HGLRC &rc) + { + PIXELFORMATDESCRIPTOR pfd = {0}; + pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iLayerType = PFD_MAIN_PLANE; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 24; + pfd.cDepthBits = 0; + pfd.cStencilBits = 0; + + w = CreateWindowEx(WS_EX_CLIENTEDGE, WINDOW_CLASS_NAME, L"", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, + GetModuleHandle(NULL), NULL); + + dc = GetDC(w); + + int pf = ::ChoosePixelFormat(dc, &pfd); + if(pf == 0) + { + ReleaseDC(w, dc); + DestroyWindow(w); + RDCERR("Couldn't choose pixel format"); + return false; + } + + BOOL res = ::SetPixelFormat(dc, pf, &pfd); + if(res == FALSE) + { + ReleaseDC(w, dc); + DestroyWindow(w); + RDCERR("Couldn't set pixel format"); + return false; + } + + rc = WGL.wglCreateContext(dc); + if(rc == NULL) + { + ReleaseDC(w, dc); + DestroyWindow(w); + RDCERR("Couldn't create trampoline context"); + return false; + } + + res = WGL.wglMakeCurrent(dc, rc); + if(res == FALSE) + { + WGL.wglMakeCurrent(NULL, NULL); + WGL.wglDeleteContext(rc); + ReleaseDC(w, dc); + DestroyWindow(w); + RDCERR("Couldn't make trampoline context current"); + return false; + } + + // now we can fetch the extension functions we need and fill out WGL + if(!WGL.wglCreateContextAttribsARB) + WGL.wglCreateContextAttribsARB = + (PFN_wglCreateContextAttribsARB)WGL.wglGetProcAddress("wglCreateContextAttribsARB"); + if(!WGL.wglGetPixelFormatAttribivARB) + WGL.wglGetPixelFormatAttribivARB = + (PFN_wglGetPixelFormatAttribivARB)WGL.wglGetProcAddress("wglGetPixelFormatAttribivARB"); + if(!WGL.wglGetExtensionsStringEXT) + WGL.wglGetExtensionsStringEXT = + (PFN_wglGetExtensionsStringEXT)WGL.wglGetProcAddress("wglGetExtensionsStringEXT"); + if(!WGL.wglGetExtensionsStringARB) + WGL.wglGetExtensionsStringARB = + (PFN_wglGetExtensionsStringARB)WGL.wglGetProcAddress("wglGetExtensionsStringARB"); + + return true; + } + + bool RegisterClass() + { + WNDCLASSEX wc = {}; + wc.style = CS_OWNDC; + wc.cbSize = sizeof(WNDCLASSEX); + wc.lpfnWndProc = DefWindowProc; + wc.hInstance = GetModuleHandle(NULL); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.lpszClassName = WINDOW_CLASS_NAME; + + WNDCLASSEX dummyCheck = {}; + + // if the class isn't already registered, then register it. + if(!GetClassInfoExW(wc.hInstance, wc.lpszClassName, &dummyCheck)) + { + if(!RegisterClassEx(&wc)) + { + RDCERR("Couldn't register GL window class"); + return false; + } + } + + return true; + } + void DrawQuads(float width, float height, const std::vector &vertices) { ::DrawQuads(WGL, width, height, vertices);