Add support for OpenGL 3 contexts
[gltest.git] / glxbackend.cpp
index 3ebc8789116df308498d936260124b3ab8e275b3..e40e90c5ba99dd23ad193a3e715f1e5e19c08d7b 100644 (file)
 #include <GL/glxext.h>
 #include <string>
 
-#if !defined(CON_GL1)
-#error "Valid GL contexts for GLX are: GL1"
+#if !defined(CON_GL1) && !defined(CON_GL3)
+#error "Valid GL contexts for GLX are: GL1, GL3"
 #endif
 
-// attributes for a double buffered visual in RGBA format with at least 4 bits per color
-static int attrList[] =                                             
+// attributes for a double buffered framebuffer in RGBA format with at least 4 bits per color
+static int configAttribs[] =                                             
 {
-       GLX_RGBA, GLX_DOUBLEBUFFER,
+       GLX_RENDER_TYPE, GLX_RGBA_BIT,
+       GLX_DOUBLEBUFFER, True,
        GLX_RED_SIZE, 4,
        GLX_GREEN_SIZE, 4,
        GLX_BLUE_SIZE, 4,
        None
 };
 
+#ifdef CON_GL3
+// attributes for a GL3 context
+static int contextAttribs[] =
+{
+       GLX_CONTEXT_MAJOR_VERSION_ARB,               3,
+       GLX_CONTEXT_MINOR_VERSION_ARB,               0,
+       GLX_CONTEXT_FLAGS_ARB,                       GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
+       None
+};
+#endif
+
 VisualID GLXBackend::initialize(Display *display)
 {
        if (this->display == NULL) { // this implies that the context is also unitialized
@@ -47,26 +59,46 @@ VisualID GLXBackend::initialize(Display *display)
                int glxMajor, glxMinor;
                glXQueryVersion(display, &glxMajor, &glxMinor);
                printf("Using GLX version: %d.%d\n", glxMajor, glxMinor);
-               // check for swap control functions
-               const char *extensions = glXQueryExtensionsString(display, DefaultScreen(display));
-               if (std::string(extensions).find("GLX_MESA_swap_control") != std::string::npos) {
-                       funSwapIntervalMesa = (PFNGLXSWAPINTERVALMESAPROC)glXGetProcAddress((const GLubyte*)"glXSwapIntervalMESA");
-                       if (funSwapIntervalMesa) printf("glXSwapIntervalMESA is present\n");
+               if (glxMajor < 1 || (glxMajor == 1 && glxMinor < 3)) {
+                       // glXChooseFBConfig and glXCreateNewContext require GLX 1.3
+                       fprintf(stderr, "Need at least GLX 1.3 to function properly\n");
+                       exit(1);
                }
-               else {
-                       funSwapIntervalMesa = 0;
+               // check for extension-based functions
+               funSwapIntervalMesa = (PFNGLXSWAPINTERVALMESAPROC)resolveGLXFunction("GLX_MESA_swap_control", "glXSwapIntervalMESA");
+               funSwapIntervalExt = (PFNGLXSWAPINTERVALEXTPROC)resolveGLXFunction("GLX_EXT_swap_control", "glXSwapIntervalEXT");
+               funCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)resolveGLXFunction("GLX_ARB_create_context", "glXCreateContextAttribsARB");
+               // get the first usable framebuffer configuration
+               int count = 0;
+               GLXFBConfig *configs = glXChooseFBConfig(display, DefaultScreen(display), configAttribs, &count);
+               if (count < 1) {
+                       fprintf(stderr, "Failed to choose framebuffer configuration\n");
+                       exit(1);
                }
-               if (std::string(extensions).find("GLX_EXT_swap_control") != std::string::npos) {
-                       funSwapIntervalExt = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddress((const GLubyte*)"glXSwapIntervalEXT");
-                       if (funSwapIntervalExt) printf("glXSwapIntervalEXT is present\n");
-               }
-               else {
-                       funSwapIntervalExt = 0;
-               }
-               // get the visual
-               vi = glXChooseVisual(display, DefaultScreen(display), attrList);
+               config = configs[0];
+               XFree(configs);
        }
-       return vi->visualid;
+       // return visual ID
+       XVisualInfo *vi = glXGetVisualFromFBConfig(display, config);
+       VisualID visualid = vi->visualid;
+       XFree(vi);
+       return visualid;
+}
+
+bool GLXBackend::haveGLXExtension(const std::string &name)
+{
+       assert(display != NULL);
+       std::string extensions = glXQueryExtensionsString(display, DefaultScreen(display));
+       return (std::string(" "+extensions+" ").find(" "+name+" ") != std::string::npos);
+}
+
+T_proc GLXBackend::resolveGLXFunction(const char *extension, const char *function)
+{
+       if (!haveGLXExtension(extension)) return NULL;
+       T_proc f = glXGetProcAddress((const GLubyte*)function);
+       if (f)
+               printf("%s is supported, using it for %s\n", extension, function);
+       return f;
 }
 
 void GLXBackend::createContext(Window window)
@@ -74,7 +106,19 @@ void GLXBackend::createContext(Window window)
        assert(display != NULL && context == None);
        this->window = window;
        // create context with that window
-       context = glXCreateContext(display, vi, 0, GL_TRUE);                             
+#ifdef CON_GL1
+       context = glXCreateNewContext(display, config, GLX_RGBA_TYPE, NULL, GL_TRUE);
+#else
+       if (!funCreateContextAttribsARB) {
+               fprintf(stderr, "Cannot create GL3 context: GLX_ARB_create_context not supported\n");
+               exit(1);
+       }
+       context = funCreateContextAttribsARB(display, config, NULL, GL_TRUE, contextAttribs);
+#endif
+       if (!context) {
+               fprintf(stderr, "Error creating context\n");
+               exit(1);
+       }
        glXMakeCurrent(display, window, context);                                      
        assert(glXIsDirect(display, context));
        printf("Using GL version: %s\n", glGetString(GL_VERSION));
@@ -89,7 +133,6 @@ GLXBackend::~GLXBackend()
                        glXMakeCurrent(display, None, NULL);
                        glXDestroyContext(display, context);
                }
-               XFree(vi);
        }
 }
 
@@ -102,12 +145,18 @@ void GLXBackend::swapBuffers() const
 void GLXBackend::setSwapInterval(int i) const
 {
        assert(context != None);
+       // check if swap interval value is supported
+       if (i < 0) {
+               fprintf(stderr, "Cannot set swap interval to %d, must not be negative\n", i);
+               exit(1);
+       }
+       // set it
        if (funSwapIntervalExt)
                funSwapIntervalExt(display, window, i);
        else if (funSwapIntervalMesa)
                funSwapIntervalMesa(i);
        else {
-               fprintf(stderr, "At least one of glXSwapIntervalMESA, glXSwapIntervalEXT must be provided by the system\n");
+               fprintf(stderr, "At least one of GLX_EXT_swap_control, GLX_MESA_swap_control must be supported by the system\n");
                abort();
        }
 }