#include "Precomp.h" #include "OpenGLContext.h" #include #include #ifdef WIN32 #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 #define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093 #define WGL_CONTEXT_FLAGS_ARB 0x2094 #define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001 #define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002 #define ERROR_INVALID_VERSION_ARB 0x2095 #define WGL_NUMBER_CL_PIXEL_FORMATS 0x2000 #define WGL_DRAW_TO_WINDOW 0x2001 #define WGL_DRAW_TO_BITMAP 0x2002 #define WGL_ACCELERATION 0x2003 #define WGL_NEED_PALETTE 0x2004 #define WGL_NEED_SYSTEM_PALETTE 0x2005 #define WGL_SWAP_LAYER_BUFFERS 0x2006 #define WGL_SWAP_METHOD 0x2007 #define WGL_NUMBER_OVERLAYS 0x2008 #define WGL_NUMBER_UNDERLAYS 0x2009 #define WGL_TRANSPARENT 0x200A #define WGL_TRANSPARENT_VALUE 0x200B #define WGL_SHARE_DEPTH 0x200C #define WGL_SHARE_STENCIL 0x200D #define WGL_SHARE_ACCUM 0x200E #define WGL_SUPPORT_GDI 0x200F #define WGL_SUPPORT_OPENGL 0x2010 #define WGL_DOUBLE_BUFFER 0x2011 #define WGL_STEREO 0x2012 #define WGL_PIXEL_TYPE 0x2013 #define WGL_COLOR_BITS 0x2014 #define WGL_RED_BITS 0x2015 #define WGL_RED_SHIFT 0x2016 #define WGL_GREEN_BITS 0x2017 #define WGL_GREEN_SHIFT 0x2018 #define WGL_BLUE_BITS 0x2019 #define WGL_BLUE_SHIFT 0x201A #define WGL_ALPHA_BITS 0x201B #define WGL_ALPHA_SHIFT 0x201C #define WGL_ACCUM_BITS 0x201D #define WGL_ACCUM_RED_BITS 0x201E #define WGL_ACCUM_GREEN_BITS 0x201F #define WGL_ACCUM_BLUE_BITS 0x2020 #define WGL_ACCUM_ALPHA_BITS 0x2021 #define WGL_DEPTH_BITS 0x2022 #define WGL_STENCIL_BITS 0x2023 #define WGL_AUX_BUFFERS 0x2024 #define WGL_NO_ACCELERATION 0x2025 #define WGL_GENERIC_ACCELERATION 0x2026 #define WGL_FULL_ACCELERATION 0x2027 #define WGL_SWAP_EXCHANGE 0x2028 #define WGL_SWAP_COPY 0x2029 #define WGL_SWAP_UNDEFINED 0x202A #define WGL_TYPE_RGBA 0x202B #define WGL_TYPE_COLORINDEX 0x202C #define WGL_SAMPLE_BUFFERS 0x2041 #define WGL_SAMPLES 0x2042 ///////////////////////////////////////////////////////////////////////////// class OpenGLLoadFunctions { public: OpenGLLoadFunctions() { ogl_LoadFunctions(); } }; ///////////////////////////////////////////////////////////////////////////// OpenGLContext::OpenGLContext(void* windowptr) : window((HWND)windowptr) { dc = GetDC(window); OpenGLCreationHelper helper(window); context = helper.CreateContext(dc, 3, 2); if (context) { Begin(); static OpenGLLoadFunctions loadFunctions; End(); } } OpenGLContext::~OpenGLContext() { if (context) wglDeleteContext(context); if (dc) ReleaseDC(window, dc); } void OpenGLContext::Begin() { refcount++; if (refcount == 1) wglMakeCurrent(dc, context); } void OpenGLContext::End() { refcount--; if (refcount == 0) wglMakeCurrent(0, 0); } void OpenGLContext::SwapBuffers() { Begin(); ::SwapBuffers(dc); End(); } int OpenGLContext::GetWidth() const { RECT box = { 0 }; GetClientRect(window, &box); return box.right; } int OpenGLContext::GetHeight() const { RECT box = { 0 }; GetClientRect(window, &box); return box.bottom; } ///////////////////////////////////////////////////////////////////////////// OpenGLCreationHelper::OpenGLCreationHelper(HWND window) : window(window) { WINDOWINFO window_info; memset(&window_info, 0, sizeof(WINDOWINFO)); window_info.cbSize = sizeof(WINDOWINFO); GetWindowInfo(window, &window_info); query_window = CreateWindowEx( 0, WC_STATIC, TEXT(""), WS_CHILD, window_info.rcWindow.left, window_info.rcWindow.top, window_info.rcWindow.right - window_info.rcWindow.left, window_info.rcWindow.bottom - window_info.rcWindow.top, window, 0, GetModuleHandle(0), 0); if (query_window == 0) return; query_dc = GetDC(query_window); if (query_dc == 0) { DestroyWindow(query_window); query_window = 0; return; } PIXELFORMATDESCRIPTOR pfd; memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 24; int pixelformat = ChoosePixelFormat(query_dc, &pfd); SetPixelFormat(query_dc, pixelformat, &pfd); query_context = wglCreateContext(query_dc); if (query_context == 0) { DeleteDC(query_dc); DestroyWindow(query_window); query_dc = 0; query_window = 0; } } OpenGLCreationHelper::~OpenGLCreationHelper() { wglDeleteContext(query_context); DeleteDC(query_dc); DestroyWindow(query_window); } HGLRC OpenGLCreationHelper::CreateContext(HDC hdc, int major_version, int minor_version, HGLRC share_context) { if (query_context == 0) return 0; PIXELFORMATDESCRIPTOR pfd; memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 24; pfd.cRedBits = 8; pfd.cGreenBits = 8; pfd.cBlueBits = 8; pfd.cAlphaBits = 8; pfd.cDepthBits = 24; pfd.cStencilBits = 8; int pixelformat = ChoosePixelFormat(hdc, &pfd); SetPixelFormat(hdc, pixelformat, &pfd); wglMakeCurrent(query_dc, query_context); ptr_wglCreateContextAttribsARB wglCreateContextAttribsARB = (ptr_wglCreateContextAttribsARB)wglGetProcAddress("wglCreateContextAttribsARB"); HGLRC opengl3_context = 0; if (wglCreateContextAttribsARB) { std::vector int_attributes; int_attributes.push_back(WGL_CONTEXT_MAJOR_VERSION_ARB); int_attributes.push_back(major_version); int_attributes.push_back(WGL_CONTEXT_MINOR_VERSION_ARB); int_attributes.push_back(minor_version); int_attributes.push_back(0x2094); // WGL_CONTEXT_FLAGS_ARB int_attributes.push_back(0x2); // WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB int_attributes.push_back(0x9126); // WGL_CONTEXT_PROFILE_MASK_ARB int_attributes.push_back(0x1); // WGL_CONTEXT_CORE_PROFILE_BIT_ARB int_attributes.push_back(0); opengl3_context = wglCreateContextAttribsARB(hdc, share_context, int_attributes.data()); } wglMakeCurrent(0, 0); return opengl3_context; } #else #include #define GL_USE_DLOPEN // Using dlopen for linux by default #ifdef GL_USE_DLOPEN #define GL_OPENGL_LIBRARY "libGL.so.1" #include #endif #ifdef GL_USE_DLOPEN #define GL_LOAD_GLFUNC(x) dlsym(opengl_lib_handle, # x) #else #define GL_LOAD_GLFUNC(x) &x #endif OpenGLContext::OpenGLContext(void* display, void* window) : disp((::Display*)display), window((::Window)window) { try { CreateContext(); } catch (const std::exception& e) { // to do: maybe provide a way to query what the creation error was } } OpenGLContext::~OpenGLContext() { if (opengl_visual_info) { XFree(opengl_visual_info); opengl_visual_info = nullptr; } if (opengl_context) { if (glx.glXGetCurrentContext() == opengl_context) OpenGL::set_active(nullptr); if (disp) glx.glXDestroyContext(disp, opengl_context); opengl_context = nullptr; } } void OpenGLContext::Begin() { refcount++; if (refcount == 1) glx.glXMakeCurrent(disp, window, opengl_context); } void OpenGLContext::End() { refcount--; if (refcount == 0) glx.glXMakeCurrent(0, 0, opengl_context); } void OpenGLContext::SwapBuffers() { Begin(); glx.glXSwapBuffers(disp, window); End(); } int OpenGLContext::GetWidth() const { ::Window root_window; int x, y; unsigned int width, height, border_width, border_height, depth; Status result = XGetGeometry(disp, window, &root_window, &x, &y, &width, &height, &border_width, &border_height, &depth); return (result != 0) ? width: 0; } int OpenGLContext::GetHeight() const { ::Window root_window; int x, y; unsigned int width, height, border_width, border_height, depth; Status result = XGetGeometry(disp, window, &root_window, &x, &y, &width, &height, &border_width, &border_height, &depth); return (result != 0) ? height : 0; } ProcAddress* OpenGLContext::get_proc_address(const char *function_name) const { if (glx.glXGetProcAddressARB) return glx.glXGetProcAddressARB((GLubyte*)function_name); else if (glx.glXGetProcAddress) return glx.glXGetProcAddress((GLubyte*)function_name); else return nullptr; } void OpenGLContext::CreateContext() { #ifdef GL_USE_DLOPEN opengl_lib_handle = dlopen(GL_OPENGL_LIBRARY, RTLD_NOW | RTLD_GLOBAL); if (!opengl_lib_handle) throw std::runtime_error(std::string("Cannot open opengl library: ") + GL_OPENGL_LIBRARY); #endif glx.glXChooseVisual = (GL_GLXFunctions::ptr_glXChooseVisual) GL_LOAD_GLFUNC(glXChooseVisual); glx.glXCopyContext = (GL_GLXFunctions::ptr_glXCopyContext) GL_LOAD_GLFUNC(glXCopyContext); glx.glXCreateContext = (GL_GLXFunctions::ptr_glXCreateContext) GL_LOAD_GLFUNC(glXCreateContext); glx.glXCreateGLXPixmap = (GL_GLXFunctions::ptr_glXCreateGLXPixmap) GL_LOAD_GLFUNC(glXCreateGLXPixmap); glx.glXDestroyContext = (GL_GLXFunctions::ptr_glXDestroyContext) GL_LOAD_GLFUNC(glXDestroyContext); glx.glXDestroyGLXPixmap = (GL_GLXFunctions::ptr_glXDestroyGLXPixmap) GL_LOAD_GLFUNC(glXDestroyGLXPixmap); glx.glXGetConfig = (GL_GLXFunctions::ptr_glXGetConfig) GL_LOAD_GLFUNC(glXGetConfig); glx.glXGetCurrentContext = (GL_GLXFunctions::ptr_glXGetCurrentContext) GL_LOAD_GLFUNC(glXGetCurrentContext); glx.glXGetCurrentDrawable = (GL_GLXFunctions::ptr_glXGetCurrentDrawable) GL_LOAD_GLFUNC(glXGetCurrentDrawable); glx.glXIsDirect = (GL_GLXFunctions::ptr_glXIsDirect) GL_LOAD_GLFUNC(glXIsDirect); glx.glXMakeCurrent = (GL_GLXFunctions::ptr_glXMakeCurrent) GL_LOAD_GLFUNC(glXMakeCurrent); glx.glXQueryExtension = (GL_GLXFunctions::ptr_glXQueryExtension) GL_LOAD_GLFUNC(glXQueryExtension); glx.glXQueryVersion = (GL_GLXFunctions::ptr_glXQueryVersion) GL_LOAD_GLFUNC(glXQueryVersion); glx.glXSwapBuffers = (GL_GLXFunctions::ptr_glXSwapBuffers) GL_LOAD_GLFUNC(glXSwapBuffers); glx.glXUseXFont = (GL_GLXFunctions::ptr_glXUseXFont) GL_LOAD_GLFUNC(glXUseXFont); glx.glXWaitGL = (GL_GLXFunctions::ptr_glXWaitGL) GL_LOAD_GLFUNC(glXWaitGL); glx.glXWaitX = (GL_GLXFunctions::ptr_glXWaitX) GL_LOAD_GLFUNC(glXWaitX); glx.glXGetClientString = (GL_GLXFunctions::ptr_glXGetClientString) GL_LOAD_GLFUNC(glXGetClientString); glx.glXQueryServerString = (GL_GLXFunctions::ptr_glXQueryServerString) GL_LOAD_GLFUNC(glXQueryServerString); glx.glXQueryExtensionsString = (GL_GLXFunctions::ptr_glXQueryExtensionsString) GL_LOAD_GLFUNC(glXQueryExtensionsString); glx.glXGetCurrentDisplay = (GL_GLXFunctions::ptr_glXGetCurrentDisplay) GL_LOAD_GLFUNC(glXGetCurrentDisplay); glx.glXChooseFBConfig = (GL_GLXFunctions::ptr_glXChooseFBConfig) GL_LOAD_GLFUNC(glXChooseFBConfig); glx.glXCreateNewContext = (GL_GLXFunctions::ptr_glXCreateNewContext) GL_LOAD_GLFUNC(glXCreateNewContext); glx.glXCreatePbuffer = (GL_GLXFunctions::ptr_glXCreatePbuffer) GL_LOAD_GLFUNC(glXCreatePbuffer); glx.glXCreatePixmap = (GL_GLXFunctions::ptr_glXCreatePixmap) GL_LOAD_GLFUNC(glXCreatePixmap); glx.glXCreateWindow = (GL_GLXFunctions::ptr_glXCreateWindow) GL_LOAD_GLFUNC(glXCreateWindow); glx.glXDestroyPbuffer = (GL_GLXFunctions::ptr_glXDestroyPbuffer) GL_LOAD_GLFUNC(glXDestroyPbuffer); glx.glXDestroyPixmap = (GL_GLXFunctions::ptr_glXDestroyPixmap) GL_LOAD_GLFUNC(glXDestroyPixmap); glx.glXDestroyWindow = (GL_GLXFunctions::ptr_glXDestroyWindow) GL_LOAD_GLFUNC(glXDestroyWindow); glx.glXGetCurrentReadDrawable = (GL_GLXFunctions::ptr_glXGetCurrentReadDrawable) GL_LOAD_GLFUNC(glXGetCurrentReadDrawable); glx.glXGetFBConfigAttrib = (GL_GLXFunctions::ptr_glXGetFBConfigAttrib) GL_LOAD_GLFUNC(glXGetFBConfigAttrib); glx.glXGetFBConfigs = (GL_GLXFunctions::ptr_glXGetFBConfigs) GL_LOAD_GLFUNC(glXGetFBConfigs); glx.glXGetSelectedEvent = (GL_GLXFunctions::ptr_glXGetSelectedEvent) GL_LOAD_GLFUNC(glXGetSelectedEvent); glx.glXGetVisualFromFBConfig = (GL_GLXFunctions::ptr_glXGetVisualFromFBConfig) GL_LOAD_GLFUNC(glXGetVisualFromFBConfig); glx.glXMakeContextCurrent = (GL_GLXFunctions::ptr_glXMakeContextCurrent) GL_LOAD_GLFUNC(glXMakeContextCurrent); glx.glXQueryContext = (GL_GLXFunctions::ptr_glXQueryContext) GL_LOAD_GLFUNC(glXQueryContext); glx.glXQueryDrawable = (GL_GLXFunctions::ptr_glXQueryDrawable) GL_LOAD_GLFUNC(glXQueryDrawable); glx.glXSelectEvent = (GL_GLXFunctions::ptr_glXSelectEvent) GL_LOAD_GLFUNC(glXSelectEvent); glx.glXGetProcAddressARB = (GL_GLXFunctions::ptr_glXGetProcAddressARB) GL_LOAD_GLFUNC(glXGetProcAddressARB); glx.glXGetProcAddress = (GL_GLXFunctions::ptr_glXGetProcAddress) GL_LOAD_GLFUNC(glXGetProcAddress); if ((glx.glXDestroyContext == nullptr) || (glx.glXMakeCurrent == nullptr) || (glx.glXGetCurrentContext == nullptr) || (glx.glXChooseVisual == nullptr) || (glx.glXIsDirect == nullptr) || (glx.glXGetConfig == nullptr) || (glx.glXQueryExtensionsString == nullptr) || (glx.glXQueryVersion == nullptr) || (glx.glXGetVisualFromFBConfig == nullptr) || (glx.glXCreateNewContext == nullptr) || (glx.glXCreateContext == nullptr)) { throw std::runtime_error("Cannot obtain required OpenGL GLX functions"); } if ((glx.glXGetProcAddressARB == nullptr) && (glx.glXGetProcAddress == nullptr)) { throw std::runtime_error("Cannot obtain required OpenGL GLX functions"); } // FBConfigs were added in GLX version 1.3. int glx_major, glx_minor; if (!glx.glXQueryVersion(disp, &glx_major, &glx_minor) || ((glx_major == 1) && (glx_minor < 3)) || (glx_major < 1)) throw std::runtime_error("GLX 1.3 or better is required"); create_glx_1_3(disp); //if (!glx.glXIsDirect(disp, opengl_context)) // printf("No hardware acceleration available. I hope you got a really fast machine.\n"); } void OpenGLContext::create_glx_1_3(::Display* disp) { if (glx.glXChooseFBConfig == nullptr) throw std::runtime_error("Cannot find the glXChooseFBConfig function"); // Setup OpenGL: int gl_attribs_single[] = { GLX_X_RENDERABLE, True, GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_DEPTH_SIZE, 16, GLX_STENCIL_SIZE, 8, GLX_BUFFER_SIZE, 24, None }; std::vector gl_attribs; gl_attribs.reserve(64); gl_attribs.push_back(GLX_X_RENDERABLE); gl_attribs.push_back(True); gl_attribs.push_back(GLX_DRAWABLE_TYPE); gl_attribs.push_back(GLX_WINDOW_BIT); gl_attribs.push_back(GLX_RENDER_TYPE); gl_attribs.push_back(GLX_RGBA_BIT); gl_attribs.push_back(GLX_X_VISUAL_TYPE); gl_attribs.push_back(GLX_TRUE_COLOR); gl_attribs.push_back(GLX_RED_SIZE); gl_attribs.push_back(8); gl_attribs.push_back(GLX_GREEN_SIZE); gl_attribs.push_back(8); gl_attribs.push_back(GLX_BLUE_SIZE); gl_attribs.push_back(8); gl_attribs.push_back(GLX_ALPHA_SIZE); gl_attribs.push_back(8); gl_attribs.push_back(GLX_DEPTH_SIZE); gl_attribs.push_back(24); gl_attribs.push_back(GLX_STENCIL_SIZE); gl_attribs.push_back(8); gl_attribs.push_back(GLX_DOUBLEBUFFER); gl_attribs.push_back(True); gl_attribs.push_back(GLX_STEREO); gl_attribs.push_back(False); gl_attribs.push_back(None); // get an appropriate visual int fb_count; GLXFBConfig* fbc = glx.glXChooseFBConfig(disp, DefaultScreen(disp), &gl_attribs[0], &fb_count); if (!fbc) { printf("Requested visual not supported by your OpenGL implementation. Falling back on singlebuffered Visual!\n"); fbc = glx.glXChooseFBConfig(disp, DefaultScreen(disp), gl_attribs_single, &fb_count); if (!fbc) throw std::runtime_error(" glxChooseFBConfig failed"); fbconfig = fbc[0]; } else { if (!glx.glXGetFBConfigAttrib) throw std::runtime_error("Cannot find function glXGetFBConfigAttrib"); int required_samples = 1; // desc.multisampling(); int desired_config = 0; int max_sample_buffers = 0; int max_samples = 0; // Find the best fitting multisampling option for (int i = 0; i < fb_count; i++) { int samp_buf, samples; glx.glXGetFBConfigAttrib(disp, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf); glx.glXGetFBConfigAttrib(disp, fbc[i], GLX_SAMPLES, &samples); // Samples are most important, because they are variable if (max_samples < required_samples) { if (samples > max_samples) { max_samples = samples; desired_config = i; } } // Use the maximum sample buffer if (max_samples == samples) // Only check if the sample is valid { if (max_sample_buffers < samp_buf) { max_sample_buffers = samp_buf; desired_config = i; } } } fbconfig = fbc[desired_config]; } XFree(fbc); if (opengl_visual_info) XFree(opengl_visual_info); opengl_visual_info = glx.glXGetVisualFromFBConfig(disp, fbconfig); if (opengl_visual_info == nullptr) { throw std::runtime_error("glXGetVisualFromFBConfig failed"); } // create a GLX context opengl_context = create_context_glx_1_3(nullptr); } bool OpenGLContext::is_glx_extension_supported(const char* ext_name) { const char* ext_string = glx.glXQueryExtensionsString(disp, opengl_visual_info->screen); if (ext_string) { const char* start; const char* where, * terminator; // Extension names should not have spaces. where = strchr(ext_name, ' '); if (where || *ext_name == '\0') return false; int ext_len = strlen(ext_name); // It takes a bit of care to be fool-proof about parsing the OpenGL extensions string. Don't be fooled by sub-strings, etc. for (start = ext_string; ; ) { where = strstr(start, ext_name); if (!where) break; terminator = where + ext_len; if (where == start || *(where - 1) == ' ') if (*terminator == ' ' || *terminator == '\0') return true; start = terminator; } } return false; } static bool cl_ctxErrorOccurred = false; static int cl_ctxErrorHandler(::Display* dpy, XErrorEvent* ev) { cl_ctxErrorOccurred = true; return 0; } GLXContext OpenGLContext::create_context_glx_1_3(GLXContext shared_context) { GLXContext context; context = glx.glXCreateNewContext(disp, fbconfig, GLX_RGBA_TYPE, shared_context, True); if (context == nullptr) throw std::runtime_error("glXCreateContext failed"); ptr_glXCreateContextAttribs glXCreateContextAttribs = nullptr; if (is_glx_extension_supported("GLX_ARB_create_context")) { if (glx.glXGetProcAddressARB) glXCreateContextAttribs = (ptr_glXCreateContextAttribs)glx.glXGetProcAddressARB((GLubyte*) "glXCreateContextAttribsARB"); if (glx.glXGetProcAddress) glXCreateContextAttribs = (ptr_glXCreateContextAttribs)glx.glXGetProcAddress((GLubyte*) "glXCreateContextAttribsARB"); } if (glXCreateContextAttribs) { // Install an X error handler so the application won't exit if GL 3.0 context allocation fails. // // Note this error handler is global. All display connections in all threads // of a process use the same error handler, so be sure to guard against other // threads issuing X commands while this code is running. int (*oldHandler)(::Display*, XErrorEvent*) = XSetErrorHandler(&cl_ctxErrorHandler); std::vector int_attributes; int_attributes.push_back(0x2091); // GLX_CONTEXT_MAJOR_VERSION_ARB int_attributes.push_back(major_version); int_attributes.push_back(0x2092); // GLX_CONTEXT_MINOR_VERSION_ARB int_attributes.push_back(minor_version); int_attributes.push_back(0x2094); // GLX_CONTEXT_FLAGS_ARB int_attributes.push_back(0x2); // GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB int_attributes.push_back(0x9126); // GLX_CONTEXT_PROFILE_MASK_ARB int_attributes.push_back(0x1); // GLX_CONTEXT_CORE_PROFILE_BIT_ARB int_attributes.push_back(None); cl_ctxErrorOccurred = false; GLXContext context_gl3 = glXCreateContextAttribs(disp, fbconfig, shared_context, True, &int_attributes[0]); if (cl_ctxErrorOccurred) { if (context_gl3) { glx.glXDestroyContext(disp, context_gl3); context_gl3 = nullptr; } } // Restore the original error handler XSetErrorHandler(oldHandler); if (context_gl3) { glx.glXDestroyContext(disp, context); context = context_gl3; } } return context; } #endif