]> git.tdb.fi Git - libs/gui.git/commitdiff
Support forward-compatible OpenGL contexts
authorMikko Rasa <tdb@tdb.fi>
Wed, 2 Nov 2016 21:23:35 +0000 (23:23 +0200)
committerMikko Rasa <tdb@tdb.fi>
Wed, 2 Nov 2016 21:44:02 +0000 (23:44 +0200)
source/graphics/glcontext.cpp
source/graphics/glcontext.h
source/graphics/glx/glcontext.cpp
source/graphics/wgl/glcontext.cpp

index 47e1e59456cf50478cd3df8798b925407c643ec9..df6ad604ef7d2b9a4eb563abb7f1c4a458787e9f 100644 (file)
@@ -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))
 { }
 
 
index 8cf75c82948fc657869e1d3685dbfcfeb4133233..8c8b49968b91090809b2e746cabe2716707468d4 100644 (file)
@@ -15,6 +15,9 @@ struct GLOptions
        bool stencil;
        bool doublebuffer;
        unsigned multisample;
+       bool forward_compatible;
+       unsigned gl_version_major;
+       unsigned gl_version_minor;
 
        GLOptions();
 };
index 46da6d1a231091f6aed597e17e7f0f6eeb65911a..5f04e3b2fd51e21afbc1c03eac252623df16e1d7 100644 (file)
@@ -1,6 +1,8 @@
+#include <set>
 #include <vector>
-#include <GL/gl.h>
 #include <GL/glx.h>
+#include <GL/glxext.h>
+#include <msp/strings/utils.h>
 #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<int> 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<string> 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<string> 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<int> 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<int> 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<const GLubyte *>("glXCreateContextAttribsARB");
+                       PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribs = reinterpret_cast<PFNGLXCREATECONTEXTATTRIBSARBPROC>(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<int> 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<PFNGLXSWAPINTERVALEXTPROC>(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)
index 3df83f3119a294fa8472729d95e84451957881a6..6c378a6f195979a659b5a075bf3a5909b3008b12 100644 (file)
@@ -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<PFNWGLCREATECONTEXTATTRIBSARBPROC>(wglGetProcAddress("wglCreateContextAttribs"));
+               if(!wglCreateContextAttribs)
+                       throw unsupported_gl_mode(opts);
+
+               vector<int> 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);