Add support for OpenGL 3 contexts
authorRalf Jung <post@ralfj.de>
Fri, 27 Sep 2013 10:52:00 +0000 (12:52 +0200)
committerRalf Jung <post@ralfj.de>
Fri, 27 Sep 2013 10:52:00 +0000 (12:52 +0200)
This required some further changes:
* GLX backend: Choose FB configs instead of X visuals
* GL2 renderer: Set up a VAO if necessary

.gitignore
Makefile
eglbackend.cpp
glutil_gl1.cpp
glutil_gl2.cpp
glxbackend.cpp
glxbackend.h

index 73dd027a92274ff3dd62529763686809b08a3342..8c21785704c693d6aa7088330abcbc5283632598 100644 (file)
@@ -2,5 +2,6 @@ glxtest
 glx2test
 egltest
 egl2test
+glx3test
 gles2test
 eglinfo
index 39c67802ecaca55432cf6f65e0b1da927fb83ce7..c69fff735100438408997aa797725410ca9bfa49 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -4,13 +4,13 @@ COMMON_SRC = gltest.cpp glwindow.cpp
 COMMON_HDR = glwindow.h glutil.h
 COMMON_LD  = -lX11 -lboost_program_options
 
-BINARIES := glxtest egltest glx2test egl2test gles2test eglinfo
+BINARIES := glxtest egltest glx2test egl2test glx3test gles2test eglinfo
 
 all: $(BINARIES)
 
 # choices (not all combinations are valid)
 # windowing system: WIN_{GLX,EGL}
-# the kind of context: CON_{GL1,GLES2}
+# the kind of context: CON_{GL1,GL3,GLES2}
 # the API used to draw: compile in glutil_gl1.cpp or glutil_gl2.cpp
 
 glxtest: $(COMMON_SRC) $(COMMON_HDR) glutil_gl1.cpp glxbackend.cpp glxbackend.h
@@ -25,6 +25,9 @@ glx2test: $(COMMON_SRC) $(COMMON_HDR) glutil_gl2.cpp glxbackend.cpp glxbackend.h
 egl2test: $(COMMON_SRC) $(COMMON_HDR) glutil_gl2.cpp eglbackend.cpp eglbackend.h
        g++ $(CFLAGS) -DWIN_EGL -DCON_GL1 $^ -lEGL -lGL $(COMMON_LD) -o $@
 
+glx3test: $(COMMON_SRC) $(COMMON_HDR) glutil_gl2.cpp glxbackend.cpp glxbackend.h
+       g++ $(CFLAGS) -DWIN_GLX -DCON_GL3 $^ -lGL $(COMMON_LD) -o $@
+
 gles2test: $(COMMON_SRC) $(COMMON_HDR) glutil_gl2.cpp eglbackend.cpp eglbackend.h
        g++ $(CFLAGS) -DWIN_EGL -DCON_GLES2 $^ -lEGL -lGLESv2 $(COMMON_LD) -o $@
 
index ef27997391856b882859bb57b4259ba6440123fc..0d765e9cb64116d3a5455d0f8050e05548a8a38b 100644 (file)
@@ -54,13 +54,7 @@ static void exitEglError(const char *what)
        exit(1);
 }
 
-static const EGLint context_attribs[] = {
-#ifdef CON_GLES2
-       EGL_CONTEXT_CLIENT_VERSION, 2,
-#endif
-       EGL_NONE
-};
-static const EGLint config_attribs[] = {
+static const EGLint configAttribs[] = {
        EGL_RED_SIZE,             4,
        EGL_GREEN_SIZE,           4,
        EGL_BLUE_SIZE,            4,
@@ -71,6 +65,12 @@ static const EGLint config_attribs[] = {
 #endif
        EGL_NONE,
 };
+static const EGLint contextAttribs[] = {
+#ifdef CON_GLES2
+       EGL_CONTEXT_CLIENT_VERSION, 2,
+#endif
+       EGL_NONE
+};
 
 VisualID EGLBackend::initialize(Display *xDisplay)
 {
@@ -84,6 +84,7 @@ VisualID EGLBackend::initialize(Display *xDisplay)
                        exitEglError("Failed to initialize EGL");
                printf("Using EGL version: %d.%d\n", eglMajor, eglMinor);
                if (eglMajor == 1 && eglMinor < 3) {
+                       // Choosing the GL context version requires EGL 1.3
                        fprintf(stderr, "Need at least EGL 1.3 to function properly\n");
                        exit(1);
                }
@@ -96,11 +97,11 @@ VisualID EGLBackend::initialize(Display *xDisplay)
                // get an appropriate config
                EGLConfig configs[1];
                EGLint count;
-               if (eglChooseConfig(display, config_attribs, configs, 1, &count) == EGL_FALSE){
-                       exitEglError("Failed to choose config");
+               if (eglChooseConfig(display, configAttribs, configs, 1, &count) == EGL_FALSE){
+                       exitEglError("Failed to choose framebuffer configuration");
                }
                if (count == 0) {
-                       fprintf(stderr, "Found no matching EGL configuration\n");
+                       fprintf(stderr, "Found no matching framebuffer configuration\n");
                        exit(1);
                }
                config = configs[0];
@@ -116,7 +117,7 @@ void EGLBackend::createContext(Window window)
        assert(display != EGL_NO_DISPLAY && context == EGL_NO_CONTEXT);
        surface = eglCreateWindowSurface(display, config, window, NULL);
        // create an EGL context and use it with the surface
-       context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attribs);
+       context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
        if (context == EGL_NO_CONTEXT)
                exitEglError("Failed to create context");
        if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
@@ -150,12 +151,11 @@ void EGLBackend::setSwapInterval(int i) const
 {
        assert(context != EGL_NO_CONTEXT);
        // check if swap interval value is supported
-       EGLint val;
-       eglGetConfigAttrib(display, config, EGL_MIN_SWAP_INTERVAL, &val);
-       if (i < val) {
-               fprintf(stderr, "Cannot set swap interval to %d, minimum supported value is %d\n", i, val);
+       if (i < 0) {
+               fprintf(stderr, "Cannot set swap interval to %d, must not be negative\n", i);
                exit(1);
        }
+       EGLint val;
        eglGetConfigAttrib(display, config, EGL_MAX_SWAP_INTERVAL, &val);
        if (i > val) {
                fprintf(stderr, "Cannot set swap interval to %d, maximum supported value is %d\n", i, val);
index 1cedfac8e81348d6a7740f4e9487f097087b6f03..0fb04e21a9d2dd7978500e1690c4bccd7d3a8b7f 100644 (file)
@@ -18,8 +18,8 @@
 
 #include "glutil.h"
 
-#if defined(CON_GLES2)
-#error "GLES2 contexts do not support GL1 functionality"
+#if !defined(CON_GL1)
+#error "Only GL1 contexts support GL1 functionality"
 #endif
 
 void resolveFunctionPointers(T_glGetProcAddress)
index e8cc1904787cab795391912a89e26f276344e329..666ede9b5fd074c2dfe089c86b9a7321c3b438bb 100644 (file)
@@ -34,6 +34,7 @@ typedef void (*GLATTACHSHADERPROC) (GLuint program, GLuint shader);
 typedef void (*GLLINKPROGRAMPROC) (GLuint program);
 typedef GLint (*GLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name);
 typedef void (*GLUSEPROGRAMPROC) (GLuint program);
+
 typedef void (*GLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer);
 typedef void (*GLENABLEVERTEXATTRIBARRAYPROC) (GLuint index);
 typedef void (*GLGENBUFFERSPROC) (GLsizei n, GLuint *buffers);
@@ -41,6 +42,13 @@ typedef void (*GLBINDBUFFERPROC) (GLenum target, GLuint buffer);
 typedef void (*GLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage);
 typedef void (*GLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers);
 
+#ifdef CON_GL3
+// VAOs are only supported in GL3+, and not needed in earler contexts
+typedef void (*GLBINDVERTEXARRAYPROC) (GLuint array);
+typedef void (*GLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays);
+typedef void (*GLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays);
+#endif
+
 GLGETSHADERIVPROC p_glGetShaderiv = NULL;
 GLGETSHADERINFOLOGPROC p_glGetShaderInfoLog = NULL;
 GLCREATESHADERPROC p_glCreateShader = NULL;
@@ -51,6 +59,7 @@ GLATTACHSHADERPROC p_glAttachShader = NULL;
 GLLINKPROGRAMPROC p_glLinkProgram = NULL;
 GLGETATTRIBLOCATIONPROC p_glGetAttribLocation = NULL;
 GLUSEPROGRAMPROC p_glUseProgram = NULL;
+
 GLVERTEXATTRIBPOINTERPROC p_glVertexAttribPointer = NULL;
 GLENABLEVERTEXATTRIBARRAYPROC p_glEnableVertexAttribArray = NULL;
 GLGENBUFFERSPROC p_glGenBuffers = NULL;
@@ -58,6 +67,12 @@ GLBINDBUFFERPROC p_glBindBuffer = NULL;
 GLBUFFERDATAPROC p_glBufferData = NULL;
 GLDELETEBUFFERSPROC p_glDeleteBuffers = NULL;
 
+#ifdef CON_GL3
+GLBINDVERTEXARRAYPROC p_glBindVertexArray = NULL;
+GLDELETEVERTEXARRAYSPROC p_glDeleteVertexArrays = NULL;
+GLGENVERTEXARRAYSPROC p_glGenVertexArrays = NULL;
+#endif
+
 // resolve function pointers
 static T_proc resolveFunctionPointer(T_glGetProcAddress p_glGetProcAddress, const char *name)
 {
@@ -96,6 +111,13 @@ void resolveFunctionPointers(T_glGetProcAddress p_glGetProcAddress)
        p_glAttachShader = (GLATTACHSHADERPROC)resolveFunctionPointer(p_glGetProcAddress, "glAttachShader");
        p_glLinkProgram = (GLLINKPROGRAMPROC)resolveFunctionPointer(p_glGetProcAddress, "glLinkProgram");
        p_glGetAttribLocation = (GLGETATTRIBLOCATIONPROC)resolveFunctionPointer(p_glGetProcAddress, "glGetAttribLocation");
+
+#ifdef CON_GL3
+       p_glBindVertexArray = (GLBINDVERTEXARRAYPROC)resolveFunctionPointer(p_glGetProcAddress, "glBindVertexArray");
+       p_glDeleteVertexArrays = (GLDELETEVERTEXARRAYSPROC)resolveFunctionPointer(p_glGetProcAddress, "glDeleteVertexArrays");
+       p_glGenVertexArrays = (GLGENVERTEXARRAYSPROC)resolveFunctionPointer(p_glGetProcAddress, "glGenVertexArrays");
+#endif
+
        p_glUseProgram = (GLUSEPROGRAMPROC)resolveFunctionPointer(p_glGetProcAddress, "glUseProgram");
        p_glVertexAttribPointer = (GLVERTEXATTRIBPOINTERPROC)resolveFunctionPointer(p_glGetProcAddress, "glVertexAttribPointer");
        p_glEnableVertexAttribArray = (GLENABLEVERTEXATTRIBARRAYPROC)resolveFunctionPointer(p_glGetProcAddress, "glEnableVertexAttribArray");
@@ -105,6 +127,32 @@ void resolveFunctionPointers(T_glGetProcAddress p_glGetProcAddress)
        p_glDeleteBuffers = (GLDELETEBUFFERSPROC)resolveFunctionPointer(p_glGetProcAddress, "glDeleteBuffers");
 }
 
+static const char *glErrorToString(GLenum e)
+{
+#define CASE(name) case name: return #name
+       switch (e) {
+               CASE(GL_NO_ERROR);
+               CASE(GL_INVALID_ENUM);
+               CASE(GL_INVALID_VALUE);
+               CASE(GL_INVALID_OPERATION);
+#ifndef CON_GLES2
+               CASE(GL_STACK_OVERFLOW);
+               CASE(GL_STACK_UNDERFLOW);
+#endif
+               CASE(GL_OUT_OF_MEMORY);
+               default: return "<unknown>";
+       }
+#undef CASE
+}
+
+static void checkGlError(const char *what)
+{
+       GLenum e = glGetError();
+       if (e == GL_NO_ERROR) return;
+       fprintf(stderr, "GL error %d (%s): %s\n", e, glErrorToString(e), what);
+       exit(1);
+}
+
 // shaders
 static const char *vertex_shader_source =
 "#version 100 \n\
@@ -155,6 +203,7 @@ static GLuint compile_shader(GLenum shader_type, const char *source)
                show_info_log(shader);
                exit(1);
        }
+       checkGlError("Compiling shader");
        return shader;
 }
 
@@ -164,6 +213,7 @@ static GLuint createArrayBuffer(GLsizeiptr size, GLfloat *data)
        p_glGenBuffers(1, &buffer);
        p_glBindBuffer(GL_ARRAY_BUFFER, buffer);
        p_glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
+       checkGlError("Creating array buffer");
        return buffer;
 }
 
@@ -184,6 +234,15 @@ void initialise2dProjection()
 void drawQuad(GLfloat red, GLfloat green, GLfloat blue, GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2)
 {
        p_glUseProgram(program);
+       checkGlError("Enabling the shaders");
+
+#ifdef CON_GL3
+       // create a vertex array obect and use it
+       GLuint vao;
+       p_glGenVertexArrays(1, &vao);
+       p_glBindVertexArray(vao);
+       checkGlError("Creating a VAO");
+#endif
        
        // send vertex data to card with given attribute
        GLfloat vertex_buffer_data[] = {
@@ -201,7 +260,9 @@ void drawQuad(GLfloat red, GLfloat green, GLfloat blue, GLfloat x1, GLfloat y1,
                0,                                /* stride */
                (void*)0                          /* array buffer offset */
        );
+       checkGlError("Preparing vertex data");
        p_glEnableVertexAttribArray(attributes.position);
+       checkGlError("Sending vertex data");
        
        // same with color data
        GLfloat color_buffer_data[] = {
@@ -219,12 +280,19 @@ void drawQuad(GLfloat red, GLfloat green, GLfloat blue, GLfloat x1, GLfloat y1,
                0,                             /* stride */
                (void*)0                       /* array buffer offset */
        );
+       checkGlError("Preparing color data");
        p_glEnableVertexAttribArray(attributes.color);
+       checkGlError("Sending color data");
        
        // draw
        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+       checkGlError("Drawing");
        
        // cleanup
        p_glDeleteBuffers(1, &vertex_buffer);
        p_glDeleteBuffers(1, &color_buffer);
+#ifdef CON_GL3
+       p_glDeleteVertexArrays(1, &vao);
+#endif
+       checkGlError("Doing cleanup");
 }
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();
        }
 }
index 1e75cc8fd02fc6f5167de20f81b16b27880514d3..a16f970c1fb72398ff26589f954e5d700706b4c4 100644 (file)
  */
 
 #include "glwindow.h"
+#include "glutil.h"
 
 #include <GL/glx.h>
+#include <string>
 
 
 /** Make an X window fit for GL. You have to manage the X window yourself! */
@@ -42,10 +44,14 @@ public:
        virtual void setSwapInterval(int i) const;
 private:
        Display *display;
-       XVisualInfo *vi;
+       GLXFBConfig config;
        GLXContext context;
        Window window;
        
        PFNGLXSWAPINTERVALMESAPROC funSwapIntervalMesa;
        PFNGLXSWAPINTERVALEXTPROC funSwapIntervalExt;
+       PFNGLXCREATECONTEXTATTRIBSARBPROC funCreateContextAttribsARB;
+       
+       bool haveGLXExtension(const std::string &name);
+       T_proc resolveGLXFunction(const char *extension, const char *function);
 };