mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-13 05:20:45 +00:00
Separate out function to make shared context for vendor checks
* This is only to be used to make a temporary context to share with. We make sure to use the same visual/config as the parent context to ensure it will successfully share.
This commit is contained in:
@@ -27,13 +27,13 @@
|
||||
class CGLPlatform : public GLPlatform
|
||||
{
|
||||
bool MakeContextCurrent(GLWindowingData data) { return false; }
|
||||
GLWindowingData MakeContext(GLWindowingData share)
|
||||
GLWindowingData CloneTemporaryContext(GLWindowingData share)
|
||||
{
|
||||
GLWindowingData ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void DeleteContext(GLWindowingData context) {}
|
||||
void DeleteClonedContext(GLWindowingData context) {}
|
||||
void DeleteReplayContext(GLWindowingData context) {}
|
||||
void SwapBuffers(GLWindowingData context) {}
|
||||
void GetOutputWindowDimensions(GLWindowingData context, int32_t &w, int32_t &h) { w = h = 0; }
|
||||
|
||||
@@ -51,6 +51,7 @@ public:
|
||||
void *handle = DEFAULT_HANDLE;
|
||||
WrappedOpenGL driver;
|
||||
std::set<EGLContext> contexts;
|
||||
std::map<EGLContext, EGLConfig> configs;
|
||||
} eglhook;
|
||||
|
||||
HOOK_EXPORT EGLDisplay eglGetDisplay(EGLNativeDisplayType display)
|
||||
@@ -165,6 +166,9 @@ HOOK_EXPORT EGLContext eglCreateContext(EGLDisplay display, EGLConfig config,
|
||||
data.egl_dpy = display;
|
||||
data.egl_wnd = (EGLSurface)NULL;
|
||||
data.egl_ctx = ret;
|
||||
data.egl_cfg = config;
|
||||
|
||||
eglhook.configs[ret] = config;
|
||||
|
||||
eglhook.driver.SetDriverType(RDCDriver::OpenGLES);
|
||||
{
|
||||
@@ -231,6 +235,9 @@ HOOK_EXPORT EGLBoolean eglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSu
|
||||
data.egl_wnd = draw;
|
||||
data.egl_ctx = ctx;
|
||||
|
||||
// we could query this out technically but it's easier to keep a map
|
||||
data.egl_cfg = eglhook.configs[ctx];
|
||||
|
||||
eglhook.driver.SetDriverType(RDCDriver::OpenGLES);
|
||||
|
||||
eglhook.driver.ActivateContext(data);
|
||||
|
||||
@@ -50,23 +50,25 @@ class EGLPlatform : public GLPlatform
|
||||
return false;
|
||||
}
|
||||
|
||||
GLWindowingData MakeContext(GLWindowingData share)
|
||||
GLWindowingData CloneTemporaryContext(GLWindowingData share)
|
||||
{
|
||||
GLWindowingData ret;
|
||||
GLWindowingData ret = share;
|
||||
|
||||
if(EGL.CreateContext && EGL.ChooseConfig && EGL.CreatePbufferSurface)
|
||||
ret.egl_ctx = NULL;
|
||||
|
||||
if(EGL.CreateContext)
|
||||
{
|
||||
ret = CreateWindowingData(share.egl_dpy, share.ctx, 0);
|
||||
EGLint baseAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_CONTEXT_FLAGS_KHR,
|
||||
EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL_NONE};
|
||||
|
||||
ret.egl_ctx = EGL.CreateContext(share.egl_dpy, share.egl_cfg, share.egl_ctx, baseAttribs);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void DeleteContext(GLWindowingData context)
|
||||
void DeleteClonedContext(GLWindowingData context)
|
||||
{
|
||||
if(context.wnd && EGL.DestroySurface)
|
||||
EGL.DestroySurface(context.egl_dpy, context.egl_wnd);
|
||||
|
||||
if(context.ctx && EGL.DestroyContext)
|
||||
EGL.DestroyContext(context.egl_dpy, context.egl_ctx);
|
||||
}
|
||||
@@ -159,8 +161,7 @@ class EGLPlatform : public GLPlatform
|
||||
EGL_NONE};
|
||||
|
||||
EGLint numConfigs;
|
||||
EGLConfig config;
|
||||
if(!EGL.ChooseConfig(eglDisplay, configAttribs, &config, 1, &numConfigs))
|
||||
if(!EGL.ChooseConfig(eglDisplay, configAttribs, &ret.egl_cfg, 1, &numConfigs))
|
||||
{
|
||||
RDCERR("Couldn't find a suitable EGL config");
|
||||
return ret;
|
||||
@@ -185,7 +186,7 @@ class EGLPlatform : public GLPlatform
|
||||
{
|
||||
verAttribs[1] = v.major;
|
||||
verAttribs[3] = v.minor;
|
||||
ctx = EGL.CreateContext(eglDisplay, config, share_ctx, verAttribs);
|
||||
ctx = EGL.CreateContext(eglDisplay, ret.egl_cfg, share_ctx, verAttribs);
|
||||
|
||||
if(ctx)
|
||||
break;
|
||||
@@ -197,7 +198,7 @@ class EGLPlatform : public GLPlatform
|
||||
static const EGLint baseAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_CONTEXT_FLAGS_KHR,
|
||||
EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL_NONE};
|
||||
|
||||
ctx = EGL.CreateContext(eglDisplay, config, share_ctx, baseAttribs);
|
||||
ctx = EGL.CreateContext(eglDisplay, ret.egl_cfg, share_ctx, baseAttribs);
|
||||
}
|
||||
|
||||
if(ctx == NULL)
|
||||
@@ -211,7 +212,7 @@ class EGLPlatform : public GLPlatform
|
||||
EGLSurface surface = 0;
|
||||
if(window != 0)
|
||||
{
|
||||
surface = EGL.CreateWindowSurface(eglDisplay, config, window, NULL);
|
||||
surface = EGL.CreateWindowSurface(eglDisplay, ret.egl_cfg, window, NULL);
|
||||
|
||||
if(surface == NULL)
|
||||
RDCERR("Couldn't create surface for window");
|
||||
@@ -219,7 +220,7 @@ class EGLPlatform : public GLPlatform
|
||||
else
|
||||
{
|
||||
static const EGLint pbAttribs[] = {EGL_WIDTH, 32, EGL_HEIGHT, 32, EGL_NONE};
|
||||
surface = EGL.CreatePbufferSurface(eglDisplay, config, pbAttribs);
|
||||
surface = EGL.CreatePbufferSurface(eglDisplay, ret.egl_cfg, pbAttribs);
|
||||
|
||||
if(surface == NULL)
|
||||
RDCERR("Couldn't create a suitable PBuffer");
|
||||
@@ -254,17 +255,12 @@ class EGLPlatform : public GLPlatform
|
||||
int major, minor;
|
||||
EGL.Initialize(eglDisplay, &major, &minor);
|
||||
|
||||
GLWindowingData base;
|
||||
base.egl_dpy = eglDisplay;
|
||||
base.egl_ctx = EGL_NO_CONTEXT;
|
||||
base.egl_wnd = 0;
|
||||
|
||||
replayContext = MakeContext(base);
|
||||
replayContext = CreateWindowingData(eglDisplay, EGL_NO_CONTEXT, 0);
|
||||
|
||||
if(!replayContext.ctx || !replayContext.wnd)
|
||||
{
|
||||
RDCERR("Couldn't create OpenGL ES 3.x replay context - required for replay");
|
||||
DeleteContext(replayContext);
|
||||
DeleteReplayContext(replayContext);
|
||||
RDCEraseEl(replayContext);
|
||||
return ReplayStatus::APIHardwareUnsupported;
|
||||
}
|
||||
|
||||
@@ -718,7 +718,7 @@ void DoVendorChecks(GLPlatform &platform, GLWindowingData context)
|
||||
GL.glBindVertexArray(vao);
|
||||
|
||||
// make a context that shares with the current one, and switch to it
|
||||
GLWindowingData child = platform.MakeContext(context);
|
||||
GLWindowingData child = platform.CloneTemporaryContext(context);
|
||||
|
||||
if(child.ctx)
|
||||
{
|
||||
@@ -737,7 +737,7 @@ void DoVendorChecks(GLPlatform &platform, GLWindowingData context)
|
||||
// switch back to context
|
||||
platform.MakeContextCurrent(context);
|
||||
|
||||
platform.DeleteContext(child);
|
||||
platform.DeleteClonedContext(child);
|
||||
}
|
||||
|
||||
GL.glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, prevFBO);
|
||||
|
||||
@@ -96,7 +96,6 @@ struct GLWindowingData
|
||||
wnd = NULL;
|
||||
}
|
||||
|
||||
void SetCtx(void *c) { ctx = (HGLRC)c; }
|
||||
union
|
||||
{
|
||||
HDC DC;
|
||||
@@ -112,6 +111,8 @@ struct GLWindowingData
|
||||
HWND wnd;
|
||||
EGLSurface egl_wnd;
|
||||
};
|
||||
|
||||
EGLConfig egl_cfg;
|
||||
};
|
||||
|
||||
#elif ENABLED(RDOC_LINUX)
|
||||
@@ -149,26 +150,28 @@ struct GLWindowingData
|
||||
wnd = 0;
|
||||
}
|
||||
|
||||
void SetCtx(void *c) { ctx = (GLContextPtr)c; }
|
||||
|
||||
#if defined(RENDERDOC_SUPPORT_GL)
|
||||
typedef Display *GLDisplayPtr;
|
||||
typedef GLXContext GLContextPtr;
|
||||
typedef GLXDrawable GLWindowPtr;
|
||||
typedef XVisualInfo *GLConfigPtr;
|
||||
#else
|
||||
typedef void *GLDisplayPtr;
|
||||
typedef void *GLContextPtr;
|
||||
typedef void *GLWindowPtr;
|
||||
typedef void *GLConfigPtr;
|
||||
#endif
|
||||
|
||||
#if defined(RENDERDOC_SUPPORT_GLES)
|
||||
typedef EGLDisplay GLESDisplayPtr;
|
||||
typedef EGLContext GLESContextPtr;
|
||||
typedef EGLSurface GLESWindowPtr;
|
||||
typedef EGLConfig GLESConfigPtr;
|
||||
#else
|
||||
typedef void *GLESDisplayPtr;
|
||||
typedef void *GLESContextPtr;
|
||||
typedef void *GLESWindowPtr;
|
||||
typedef vpod *GLESConfigPtr;
|
||||
#endif
|
||||
|
||||
union
|
||||
@@ -186,6 +189,11 @@ struct GLWindowingData
|
||||
GLWindowPtr wnd;
|
||||
GLESWindowPtr egl_wnd;
|
||||
};
|
||||
union
|
||||
{
|
||||
GLConfigPtr cfg;
|
||||
GLESConfigPtr egl_cfg;
|
||||
};
|
||||
};
|
||||
|
||||
#elif ENABLED(RDOC_APPLE)
|
||||
@@ -198,7 +206,6 @@ struct GLWindowingData
|
||||
wnd = 0;
|
||||
}
|
||||
|
||||
void SetCtx(void *c) { ctx = (void *)c; }
|
||||
void *ctx;
|
||||
void *wnd;
|
||||
};
|
||||
@@ -222,19 +229,19 @@ struct GLWindowingData
|
||||
egl_wnd = 0;
|
||||
}
|
||||
|
||||
void SetCtx(void *c) { egl_ctx = (void *)c; }
|
||||
union
|
||||
{
|
||||
// currently required to allow compatiblity with the driver parts
|
||||
void *ctx;
|
||||
EGLContext egl_ctx;
|
||||
};
|
||||
EGLDisplay egl_dpy;
|
||||
union
|
||||
{
|
||||
EGLSurface egl_wnd;
|
||||
void *wnd;
|
||||
};
|
||||
EGLDisplay egl_dpy;
|
||||
EGLConfig egl_cfg;
|
||||
};
|
||||
|
||||
#else
|
||||
@@ -246,8 +253,8 @@ struct GLWindowingData
|
||||
struct GLPlatform
|
||||
{
|
||||
// simple wrapper for OS functions to make/delete a context
|
||||
virtual GLWindowingData MakeContext(GLWindowingData share) = 0;
|
||||
virtual void DeleteContext(GLWindowingData context) = 0;
|
||||
virtual GLWindowingData CloneTemporaryContext(GLWindowingData share) = 0;
|
||||
virtual void DeleteClonedContext(GLWindowingData context) = 0;
|
||||
virtual void DeleteReplayContext(GLWindowingData context) = 0;
|
||||
virtual bool MakeContextCurrent(GLWindowingData data) = 0;
|
||||
virtual void SwapBuffers(GLWindowingData context) = 0;
|
||||
|
||||
@@ -3251,7 +3251,7 @@ ReplayStatus CreateReplayDevice(RDCFile *rdc, GLPlatform &platform, IReplayDrive
|
||||
if(!current)
|
||||
{
|
||||
RDCERR("Couldn't active the created GL ES context");
|
||||
platform.DeleteContext(data);
|
||||
platform.DeleteReplayContext(data);
|
||||
return ReplayStatus::APIInitFailed;
|
||||
}
|
||||
|
||||
@@ -3269,14 +3269,14 @@ ReplayStatus CreateReplayDevice(RDCFile *rdc, GLPlatform &platform, IReplayDrive
|
||||
|
||||
if(!extensionsValidated)
|
||||
{
|
||||
platform.DeleteContext(data);
|
||||
platform.DeleteReplayContext(data);
|
||||
return ReplayStatus::APIInitFailed;
|
||||
}
|
||||
|
||||
bool functionsValidated = ValidateFunctionPointers();
|
||||
if(!functionsValidated)
|
||||
{
|
||||
platform.DeleteContext(data);
|
||||
platform.DeleteReplayContext(data);
|
||||
return ReplayStatus::APIHardwareUnsupported;
|
||||
}
|
||||
|
||||
@@ -3299,8 +3299,8 @@ ReplayStatus CreateReplayDevice(RDCFile *rdc, GLPlatform &platform, IReplayDrive
|
||||
|
||||
class GLDummyPlatform : public GLPlatform
|
||||
{
|
||||
virtual GLWindowingData MakeContext(GLWindowingData share) { return GLWindowingData(); }
|
||||
virtual void DeleteContext(GLWindowingData context) {}
|
||||
virtual GLWindowingData CloneTemporaryContext(GLWindowingData share) { return GLWindowingData(); }
|
||||
virtual void DeleteClonedContext(GLWindowingData context) {}
|
||||
virtual void DeleteReplayContext(GLWindowingData context) {}
|
||||
virtual bool MakeContextCurrent(GLWindowingData data) { return true; }
|
||||
virtual void SwapBuffers(GLWindowingData context) {}
|
||||
|
||||
@@ -33,6 +33,7 @@ typedef void (*PFN_glXDestroyContext)(Display *dpy, GLXContext ctx);
|
||||
typedef Bool (*PFN_glXMakeCurrent)(Display *dpy, GLXDrawable drawable, GLXContext ctx);
|
||||
typedef void (*PFN_glXSwapBuffers)(Display *dpy, GLXDrawable drawable);
|
||||
typedef int (*PFN_glXGetConfig)(Display *dpy, XVisualInfo *vis, int attrib, int *value);
|
||||
typedef int (*PFN_glXQueryContext)(Display *dpy, GLXContext ctx, int attribute, int *value);
|
||||
typedef Bool (*PFN_glXIsDirect)(Display *dpy, GLXContext ctx);
|
||||
typedef __GLXextFuncPtr (*PFN_glXGetProcAddress)(const GLubyte *);
|
||||
typedef __GLXextFuncPtr (*PFN_glXGetProcAddressARB)(const GLubyte *);
|
||||
@@ -74,6 +75,7 @@ typedef void (*PFN_glEnd)();
|
||||
|
||||
#define GLX_NONHOOKED_SYMBOLS(FUNC) \
|
||||
FUNC(glXGetConfig); \
|
||||
FUNC(glXQueryContext); \
|
||||
FUNC(glXIsDirect); \
|
||||
FUNC(glXGetVisualFromFBConfig); \
|
||||
FUNC(glXChooseFBConfig); \
|
||||
|
||||
@@ -113,6 +113,7 @@ HOOK_EXPORT GLXContext glXCreateContext(Display *dpy, XVisualInfo *vis, GLXConte
|
||||
data.dpy = dpy;
|
||||
data.wnd = (GLXDrawable)NULL;
|
||||
data.ctx = ret;
|
||||
data.cfg = vis;
|
||||
|
||||
{
|
||||
SCOPED_LOCK(glLock);
|
||||
@@ -249,18 +250,19 @@ HOOK_EXPORT GLXContext glXCreateContextAttribsARB(Display *dpy, GLXFBConfig conf
|
||||
GLX.glXGetConfig(dpy, vis, GLX_SAMPLES_ARB, &value);
|
||||
init.isSRGB = RDCMAX(1, value);
|
||||
|
||||
XFree(vis);
|
||||
|
||||
GLWindowingData data;
|
||||
data.dpy = dpy;
|
||||
data.wnd = (GLXDrawable)NULL;
|
||||
data.ctx = ret;
|
||||
data.cfg = vis;
|
||||
|
||||
{
|
||||
SCOPED_LOCK(glLock);
|
||||
glxhook.driver.CreateContext(data, shareList, init, core, true);
|
||||
}
|
||||
|
||||
XFree(vis);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -299,7 +301,25 @@ HOOK_EXPORT Bool glXMakeCurrent(Display *dpy, GLXDrawable drawable, GLXContext c
|
||||
data.wnd = drawable;
|
||||
data.ctx = ctx;
|
||||
|
||||
int fbconfigid = -1;
|
||||
GLX.glXQueryContext(dpy, ctx, GLX_FBCONFIG_ID, &fbconfigid);
|
||||
|
||||
int attribs[] = {GLX_FBCONFIG_ID, fbconfigid, 0};
|
||||
|
||||
int numElems = 0;
|
||||
GLXFBConfig *config = GLX.glXChooseFBConfig(dpy, DefaultScreen(dpy), attribs, &numElems);
|
||||
|
||||
if(config)
|
||||
data.cfg = GLX.glXGetVisualFromFBConfig(dpy, *config);
|
||||
else
|
||||
data.cfg = NULL;
|
||||
|
||||
glxhook.driver.ActivateContext(data);
|
||||
|
||||
if(config)
|
||||
XFree(config);
|
||||
if(data.cfg)
|
||||
XFree(data.cfg);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -341,7 +361,25 @@ HOOK_EXPORT Bool glXMakeContextCurrent(Display *dpy, GLXDrawable draw, GLXDrawab
|
||||
data.wnd = draw;
|
||||
data.ctx = ctx;
|
||||
|
||||
int fbconfigid = -1;
|
||||
GLX.glXQueryContext(dpy, ctx, GLX_FBCONFIG_ID, &fbconfigid);
|
||||
|
||||
int attribs[] = {GLX_FBCONFIG_ID, fbconfigid, 0};
|
||||
|
||||
int numElems = 0;
|
||||
GLXFBConfig *config = GLX.glXChooseFBConfig(dpy, DefaultScreen(dpy), attribs, &numElems);
|
||||
|
||||
if(config)
|
||||
data.cfg = GLX.glXGetVisualFromFBConfig(dpy, *config);
|
||||
else
|
||||
data.cfg = NULL;
|
||||
|
||||
glxhook.driver.ActivateContext(data);
|
||||
|
||||
if(config)
|
||||
XFree(config);
|
||||
if(data.cfg)
|
||||
XFree(data.cfg);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -55,58 +55,44 @@ class GLXPlatform : public GLPlatform
|
||||
return false;
|
||||
}
|
||||
|
||||
GLWindowingData MakeContext(GLWindowingData share)
|
||||
GLWindowingData CloneTemporaryContext(GLWindowingData share)
|
||||
{
|
||||
GLWindowingData ret = {};
|
||||
GLWindowingData ret = share;
|
||||
|
||||
if(!GLX.glXCreateContextAttribsARB)
|
||||
ret.ctx = NULL;
|
||||
|
||||
if(!GLX.glXCreateContext)
|
||||
return ret;
|
||||
|
||||
const int attribs[] = {
|
||||
GLX_CONTEXT_MAJOR_VERSION_ARB,
|
||||
3,
|
||||
GLX_CONTEXT_MINOR_VERSION_ARB,
|
||||
2,
|
||||
GLX_CONTEXT_FLAGS_ARB,
|
||||
0,
|
||||
GLX_CONTEXT_PROFILE_MASK_ARB,
|
||||
GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
0,
|
||||
0,
|
||||
};
|
||||
bool is_direct = false;
|
||||
|
||||
if(GLX.glXIsDirect)
|
||||
is_direct = GLX.glXIsDirect(share.dpy, share.ctx);
|
||||
|
||||
if(GLX.glXChooseFBConfig && GLX.glXCreatePbuffer)
|
||||
XVisualInfo *cfg = share.cfg;
|
||||
|
||||
if(cfg == NULL)
|
||||
{
|
||||
// don't need to care about the fb config as we won't be using the default framebuffer
|
||||
// (backbuffer)
|
||||
int visAttribs[] = {0};
|
||||
static int visAttribs[] = {0};
|
||||
int numCfgs = 0;
|
||||
GLXFBConfig *fbcfg =
|
||||
GLX.glXChooseFBConfig(share.dpy, DefaultScreen(share.dpy), visAttribs, &numCfgs);
|
||||
|
||||
// don't care about pbuffer properties as we won't render directly to this
|
||||
int pbAttribs[] = {GLX_PBUFFER_WIDTH, 32, GLX_PBUFFER_HEIGHT, 32, 0};
|
||||
cfg = GLX.glXGetVisualFromFBConfig(share.dpy, fbcfg[0]);
|
||||
}
|
||||
|
||||
if(fbcfg)
|
||||
{
|
||||
ret.wnd = GLX.glXCreatePbuffer(share.dpy, fbcfg[0], pbAttribs);
|
||||
ret.dpy = share.dpy;
|
||||
ret.ctx = GLX.glXCreateContextAttribsARB(share.dpy, fbcfg[0], share.ctx, is_direct, attribs);
|
||||
}
|
||||
ret.ctx = GLX.glXCreateContext(share.dpy, cfg, share.ctx, is_direct);
|
||||
|
||||
if(cfg != share.cfg)
|
||||
{
|
||||
XFree(cfg);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void DeleteContext(GLWindowingData context)
|
||||
void DeleteClonedContext(GLWindowingData context)
|
||||
{
|
||||
if(context.wnd && GLX.glXDestroyPbuffer)
|
||||
GLX.glXDestroyPbuffer(context.dpy, context.wnd);
|
||||
|
||||
if(context.ctx && GLX.glXDestroyContext)
|
||||
GLX.glXDestroyContext(context.dpy, context.ctx);
|
||||
}
|
||||
|
||||
@@ -37,9 +37,11 @@ class WGLPlatform : public GLPlatform
|
||||
return false;
|
||||
}
|
||||
|
||||
GLWindowingData MakeContext(GLWindowingData share)
|
||||
GLWindowingData CloneTemporaryContext(GLWindowingData share)
|
||||
{
|
||||
GLWindowingData ret;
|
||||
GLWindowingData ret = share;
|
||||
ret.ctx = NULL;
|
||||
|
||||
if(!WGL.wglCreateContextAttribsARB)
|
||||
return ret;
|
||||
|
||||
@@ -55,13 +57,13 @@ class WGLPlatform : public GLPlatform
|
||||
0,
|
||||
0,
|
||||
};
|
||||
ret.DC = share.DC;
|
||||
|
||||
ret.ctx = WGL.wglCreateContextAttribsARB(share.DC, share.ctx, attribs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void DeleteContext(GLWindowingData context)
|
||||
void DeleteClonedContext(GLWindowingData context)
|
||||
{
|
||||
if(context.ctx && WGL.wglDeleteContext)
|
||||
WGL.wglDeleteContext(context.ctx);
|
||||
|
||||
Reference in New Issue
Block a user