glx2test
egltest
egl2test
+glx3test
gles2test
eglinfo
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
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 $@
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,
#endif
EGL_NONE,
};
+static const EGLint contextAttribs[] = {
+#ifdef CON_GLES2
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+#endif
+ EGL_NONE
+};
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);
}
// 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];
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)
{
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);
#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)
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);
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;
GLLINKPROGRAMPROC p_glLinkProgram = NULL;
GLGETATTRIBLOCATIONPROC p_glGetAttribLocation = NULL;
GLUSEPROGRAMPROC p_glUseProgram = NULL;
+
GLVERTEXATTRIBPOINTERPROC p_glVertexAttribPointer = NULL;
GLENABLEVERTEXATTRIBARRAYPROC p_glEnableVertexAttribArray = NULL;
GLGENBUFFERSPROC p_glGenBuffers = 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)
{
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");
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\
show_info_log(shader);
exit(1);
}
+ checkGlError("Compiling shader");
return shader;
}
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;
}
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[] = {
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[] = {
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");
}
#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
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)
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));
glXMakeCurrent(display, None, NULL);
glXDestroyContext(display, context);
}
- XFree(vi);
}
}
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();
}
}
*/
#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! */
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);
};