From: Ralf Jung Date: Fri, 27 Sep 2013 10:52:00 +0000 (+0200) Subject: Add support for OpenGL 3 contexts X-Git-Url: https://git.ralfj.de/gltest.git/commitdiff_plain/ce1b46da645fdcac80fc9c71082bb5cb40166576 Add support for OpenGL 3 contexts This required some further changes: * GLX backend: Choose FB configs instead of X visuals * GL2 renderer: Set up a VAO if necessary --- diff --git a/.gitignore b/.gitignore index 73dd027..8c21785 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ glxtest glx2test egltest egl2test +glx3test gles2test eglinfo diff --git a/Makefile b/Makefile index 39c6780..c69fff7 100644 --- 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 $@ diff --git a/eglbackend.cpp b/eglbackend.cpp index ef27997..0d765e9 100644 --- a/eglbackend.cpp +++ b/eglbackend.cpp @@ -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); diff --git a/glutil_gl1.cpp b/glutil_gl1.cpp index 1cedfac..0fb04e2 100644 --- a/glutil_gl1.cpp +++ b/glutil_gl1.cpp @@ -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) diff --git a/glutil_gl2.cpp b/glutil_gl2.cpp index e8cc190..666ede9 100644 --- a/glutil_gl2.cpp +++ b/glutil_gl2.cpp @@ -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 ""; + } +#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"); } diff --git a/glxbackend.cpp b/glxbackend.cpp index 3ebc878..e40e90c 100644 --- a/glxbackend.cpp +++ b/glxbackend.cpp @@ -25,20 +25,32 @@ #include #include -#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(); } } diff --git a/glxbackend.h b/glxbackend.h index 1e75cc8..a16f970 100644 --- a/glxbackend.h +++ b/glxbackend.h @@ -17,8 +17,10 @@ */ #include "glwindow.h" +#include "glutil.h" #include +#include /** 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); };