From: Mikko Rasa Date: Wed, 2 Nov 2016 21:23:35 +0000 (+0200) Subject: Support forward-compatible OpenGL contexts X-Git-Url: http://git.tdb.fi/?p=libs%2Fgui.git;a=commitdiff_plain;h=018da17591533b034d6bf018d2a9ac640007575e Support forward-compatible OpenGL contexts --- diff --git a/source/graphics/glcontext.cpp b/source/graphics/glcontext.cpp index 47e1e59..df6ad60 100644 --- a/source/graphics/glcontext.cpp +++ b/source/graphics/glcontext.cpp @@ -12,13 +12,16 @@ GLOptions::GLOptions(): alpha(false), stencil(false), doublebuffer(true), - multisample(0) + multisample(0), + forward_compatible(false), + gl_version_major(0), + gl_version_minor(0) { } unsupported_gl_mode::unsupported_gl_mode(const GLOptions &opts): - runtime_error(format("{ .alpha=%s, .stencil=%s, .doublebuffer=%s, .multisample=%d }", - opts.alpha, opts.stencil, opts.doublebuffer, opts.multisample)) + runtime_error(format("{ .alpha=%s, .stencil=%s, .doublebuffer=%s, .multisample=%d, .forward_compatible=%s, .gl_version=%d.%d }", + opts.alpha, opts.stencil, opts.doublebuffer, opts.multisample, opts.forward_compatible, opts.gl_version_major, opts.gl_version_minor)) { } diff --git a/source/graphics/glcontext.h b/source/graphics/glcontext.h index 8cf75c8..8c8b499 100644 --- a/source/graphics/glcontext.h +++ b/source/graphics/glcontext.h @@ -15,6 +15,9 @@ struct GLOptions bool stencil; bool doublebuffer; unsigned multisample; + bool forward_compatible; + unsigned gl_version_major; + unsigned gl_version_minor; GLOptions(); }; diff --git a/source/graphics/glx/glcontext.cpp b/source/graphics/glx/glcontext.cpp index 46da6d1..5f04e3b 100644 --- a/source/graphics/glx/glcontext.cpp +++ b/source/graphics/glx/glcontext.cpp @@ -1,6 +1,8 @@ +#include #include -#include #include +#include +#include #include "glcontext.h" #include "display_private.h" #include "window_private.h" @@ -17,67 +19,189 @@ struct GLContext::Private ContextHandle context; // We need to create a window with the chosen visual WindowHandle subwnd; + GLXWindow glxwnd; }; void GLContext::platform_init(const GLOptions &opts) { - vector attribs; + DisplayHandle dpy = display.get_private().display; + int event_base; + int error_base; + if(!glXQueryExtension(dpy, &event_base, &error_base)) + throw runtime_error("glX not found"); - attribs.push_back(GLX_RGBA); - attribs.push_back(GLX_DEPTH_SIZE); - attribs.push_back(1); + int major, minor; + glXQueryVersion(dpy, &major, &minor); - if(opts.alpha) + set extensions; + if(major>1 || (major==1 && minor>=1)) { - attribs.push_back(GLX_ALPHA_SIZE); - attribs.push_back(1); + if(const char *ext_str = glXQueryExtensionsString(dpy, DefaultScreen(dpy))) + { + vector exts = split(ext_str); + extensions.insert(exts.begin(), exts.end()); + } } - if(opts.stencil) + if(major>1 || (major==1 && minor>=3)) { - attribs.push_back(GLX_STENCIL_SIZE); - attribs.push_back(1); - } + vector fb_attribs; + + fb_attribs.push_back(GLX_DRAWABLE_TYPE); + fb_attribs.push_back(GLX_WINDOW_BIT); + + fb_attribs.push_back(GLX_RENDER_TYPE); + fb_attribs.push_back(GLX_RGBA_BIT); + + fb_attribs.push_back(GLX_DEPTH_SIZE); + fb_attribs.push_back(1); + + if(opts.alpha) + { + fb_attribs.push_back(GLX_ALPHA_SIZE); + fb_attribs.push_back(1); + } + + if(opts.stencil) + { + fb_attribs.push_back(GLX_STENCIL_SIZE); + fb_attribs.push_back(1); + } + + fb_attribs.push_back(GLX_DOUBLEBUFFER); + fb_attribs.push_back(opts.doublebuffer); + + if(opts.multisample>0) + { + fb_attribs.push_back(GLX_SAMPLE_BUFFERS_ARB); + fb_attribs.push_back(1); + fb_attribs.push_back(GLX_SAMPLES_ARB); + fb_attribs.push_back(opts.multisample); + } + + fb_attribs.push_back(0); + + int n_configs = 0; + GLXFBConfig *fb_configs = glXChooseFBConfig(dpy, DefaultScreen(dpy), &fb_attribs[0], &n_configs); + if(!fb_configs) + throw unsupported_gl_mode(opts); + + XVisualInfo *vi = glXGetVisualFromFBConfig(dpy, fb_configs[0]); + + XSetWindowAttributes attr; + attr.colormap = XCreateColormap(dpy, DefaultRootWindow(dpy), vi->visual, AllocNone); + + priv = new Private; + priv->subwnd = XCreateWindow(dpy, window.get_private().window, 0, 0, window.get_width(), window.get_height(), 0, vi->depth, InputOutput, vi->visual, CWColormap, &attr); + XMapWindow(dpy, priv->subwnd); + + priv->glxwnd = glXCreateWindow(dpy, fb_configs[0], priv->subwnd, 0); + + if(opts.forward_compatible || opts.gl_version_major) + { + if(!extensions.count("GLX_ARB_create_context") || !extensions.count("GLX_ARB_get_proc_address")) + throw unsupported_gl_mode(opts); - if(opts.doublebuffer) - attribs.push_back(GLX_DOUBLEBUFFER); + vector ctx_attribs; - if(opts.multisample>0) + ctx_attribs.push_back(GLX_RENDER_TYPE); + ctx_attribs.push_back(GLX_RGBA_TYPE); + + if(opts.forward_compatible) + { + ctx_attribs.push_back(GLX_CONTEXT_FLAGS_ARB); + ctx_attribs.push_back(GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB); + } + + if(opts.gl_version_major) + { + ctx_attribs.push_back(GLX_CONTEXT_MAJOR_VERSION_ARB); + ctx_attribs.push_back(opts.gl_version_major); + ctx_attribs.push_back(GLX_CONTEXT_MINOR_VERSION_ARB); + ctx_attribs.push_back(opts.gl_version_minor); + } + + ctx_attribs.push_back(0); + + const GLubyte *name = reinterpret_cast("glXCreateContextAttribsARB"); + PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribs = reinterpret_cast(glXGetProcAddressARB(name)); + priv->context = glXCreateContextAttribs(dpy, fb_configs[0], 0, true, &ctx_attribs[0]); + } + else + priv->context = glXCreateNewContext(dpy, fb_configs[0], GLX_RGBA_TYPE, 0, true); + + XFree(vi); + XFree(fb_configs); + + glXMakeContextCurrent(dpy, priv->glxwnd, priv->glxwnd, priv->context); + } + else if(opts.forward_compatible || opts.gl_version_major) + throw unsupported_gl_mode(opts); + else { - attribs.push_back(GLX_SAMPLE_BUFFERS_ARB); + vector attribs; + + attribs.push_back(GLX_RGBA); + attribs.push_back(GLX_DEPTH_SIZE); attribs.push_back(1); - attribs.push_back(GLX_SAMPLES_ARB); - attribs.push_back(opts.multisample); - } - attribs.push_back(0); + if(opts.alpha) + { + attribs.push_back(GLX_ALPHA_SIZE); + attribs.push_back(1); + } - DisplayHandle dpy = display.get_private().display; + if(opts.stencil) + { + attribs.push_back(GLX_STENCIL_SIZE); + attribs.push_back(1); + } - XVisualInfo *vi = glXChooseVisual(dpy, DefaultScreen(dpy), &attribs.front()); - if(!vi) - throw unsupported_gl_mode(opts); + if(opts.doublebuffer) + attribs.push_back(GLX_DOUBLEBUFFER); + + if(opts.multisample>0) + { + attribs.push_back(GLX_SAMPLE_BUFFERS_ARB); + attribs.push_back(1); + attribs.push_back(GLX_SAMPLES_ARB); + attribs.push_back(opts.multisample); + } + + attribs.push_back(0); - priv = new Private; - priv->context = glXCreateContext(dpy, vi, 0, true); + XVisualInfo *vi = glXChooseVisual(dpy, DefaultScreen(dpy), &attribs.front()); + if(!vi) + throw unsupported_gl_mode(opts); - XSetWindowAttributes attr; - attr.colormap = XCreateColormap(dpy, DefaultRootWindow(dpy), vi->visual, AllocNone); + priv = new Private; + priv->glxwnd = 0; + priv->context = glXCreateContext(dpy, vi, 0, true); - priv->subwnd = XCreateWindow(dpy, window.get_private().window, 0, 0, window.get_width(), window.get_height(), 0, vi->depth, InputOutput, vi->visual, CWColormap, &attr); - XMapWindow(dpy, priv->subwnd); + XSetWindowAttributes attr; + attr.colormap = XCreateColormap(dpy, DefaultRootWindow(dpy), vi->visual, AllocNone); - XFree(vi); + priv->subwnd = XCreateWindow(dpy, window.get_private().window, 0, 0, window.get_width(), window.get_height(), 0, vi->depth, InputOutput, vi->visual, CWColormap, &attr); + XMapWindow(dpy, priv->subwnd); - glXMakeCurrent(dpy, priv->subwnd, priv->context); + XFree(vi); + + glXMakeCurrent(dpy, priv->subwnd, priv->context); + } } GLContext::~GLContext() { DisplayHandle dpy = display.get_private().display; - glXMakeCurrent(dpy, 0, 0); + if(priv->glxwnd) + { + glXMakeContextCurrent(dpy, 0, 0, 0); + glXDestroyWindow(dpy, priv->glxwnd); + } + else + glXMakeCurrent(dpy, 0, 0); glXDestroyContext(dpy, priv->context); XDestroyWindow(dpy, priv->subwnd); @@ -90,12 +214,12 @@ void GLContext::set_swap_interval(unsigned i) PFNGLXSWAPINTERVALEXTPROC func = reinterpret_cast(glXGetProcAddress(name)); if(!func) throw runtime_error("glXSwapIntervalEXT not found"); - func(display.get_private().display, priv->subwnd, i); + func(display.get_private().display, (priv->glxwnd ? priv->glxwnd : priv->subwnd), i); } void GLContext::swap_buffers() { - glXSwapBuffers(display.get_private().display, priv->subwnd); + glXSwapBuffers(display.get_private().display, (priv->glxwnd ? priv->glxwnd : priv->subwnd)); } void GLContext::window_resized(unsigned w, unsigned h) diff --git a/source/graphics/wgl/glcontext.cpp b/source/graphics/wgl/glcontext.cpp index 3df83f3..6c378a6 100644 --- a/source/graphics/wgl/glcontext.cpp +++ b/source/graphics/wgl/glcontext.cpp @@ -42,7 +42,34 @@ void GLContext::platform_init(const GLOptions &opts) SetPixelFormat(dc, pf_index, &pfd); priv = new Private; - priv->context = wglCreateContext(dc); + if(opts.forward_compatible || opts.gl_version_major) + { + PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs = reinterpret_cast(wglGetProcAddress("wglCreateContextAttribs")); + if(!wglCreateContextAttribs) + throw unsupported_gl_mode(opts); + + vector ctx_attribs; + + if(opts.forward_compatible) + { + ctx_attribs.push_back(WGL_CONTEXT_FLAGS_ARB); + ctx_attribs.push_back(WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB); + } + + if(opts.gl_version_major) + { + ctx_attribs.push_back(WGL_CONTEXT_MAJOR_VERSION_ARB); + ctx_attribs.push_back(opts.gl_version_major); + ctx_attribs.push_back(WGL_CONTEXT_MINOR_VERSION_ARB); + ctx_attribs.push_back(opts.gl_version_minor); + } + + ctx_attribs.push_back(0); + + priv->context = wglCreateContextAttribs(dc, 0, &ctx_attribs[0]); + } + else + priv->context = wglCreateContext(dc); wglMakeCurrent(dc, priv->context); ReleaseDC(window.get_private().window, dc);