diff options
author | Don Bright <hugh.m.bright@gmail.com> | 2011-10-09 01:02:40 (GMT) |
---|---|---|
committer | Don Bright <hugh.m.bright@gmail.com> | 2011-10-09 01:02:40 (GMT) |
commit | 589991c41e5420e68a8504670c25b0dba9e6628e (patch) | |
tree | 792553691ad1d6a1c5258d532003e1eea900ffec | |
parent | 872fc643218ef642544612954272e89629e145c5 (diff) |
Linux offscreen OpenGL using glxpixmaps. also fix FBO ARB/EXT issues
-rw-r--r-- | doc/testing.txt | 11 | ||||
-rw-r--r-- | tests/CMakeLists.txt | 15 | ||||
-rw-r--r-- | tests/OffscreenContext.cc | 204 | ||||
-rw-r--r-- | tests/OffscreenView.cc | 5 | ||||
-rw-r--r-- | tests/cgalpngtest.cc | 7 | ||||
-rw-r--r-- | tests/csgtestcore.cc | 7 | ||||
-rw-r--r-- | tests/fbo.cc | 144 |
7 files changed, 304 insertions, 89 deletions
diff --git a/doc/testing.txt b/doc/testing.txt index 26000c6..e6645fc 100644 --- a/doc/testing.txt +++ b/doc/testing.txt @@ -31,14 +31,17 @@ be ignored for the test apps in question. Troubleshooting a failed test: ------------------------------ -You can run a single test by running - $ ctest -R testname +You can run a single test by passing the test name to ctest: + $ ctest -R throwntogethertest_sphere -You can run a series by running +You can run a series of tests by passing part of a name to ctest: $ ctest -R cgalpng # runs all cgalpng tests $ ctest -R sphere # runs all sphere tests - + Logs of test runs are found in tests/build/Testing/Temporary Expected results are found in tests/regression/* Actual results are found in tests/build/testname-output/* +You can also compile a single test program: + + $ make cgalpngtest diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index eb3e911..9e6640e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -57,21 +57,6 @@ if (NOT OPENCSG_INCLUDE_DIR) endif() include_directories(${OPENCSG_INCLUDE_DIR}) -# SDL (for OpenCSG on Linux & other platforms) -if (NOT $ENV{MACOSX_DEPLOY_DIR} STREQUAL "") - message(STATU "SDL not needed for Mac OSX") -else() - find_package(SDL REQUIRED) - if (NOT SDL_FOUND) - message(FATAL_ERROR "SDL not found.") - else() - message(STATUS "SDL library found in " ${SDL_LIBRARY}) - message(STATUS "SDL header found in " ${SDL_INCLUDE_DIR}) - set(OPENGL_LIBRARY ${OPENGL_LIBRARY} ${SDL_LIBRARY}) - endif() -endif() -include_directories(${SDL_INCLUDE_DIR}) - # GLEW if (NOT $ENV{MACOSX_DEPLOY_DIR} STREQUAL "") diff --git a/tests/OffscreenContext.cc b/tests/OffscreenContext.cc index fa1afde..775491e 100644 --- a/tests/OffscreenContext.cc +++ b/tests/OffscreenContext.cc @@ -1,85 +1,182 @@ +/* + +Create an OpenGL context without creating an OpenGL Window. for Linux. + +based on + + http://www.opengl.org/sdk/docs/man/xhtml/glXIntro.xml + http://www.mesa3d.org/brianp/sig97/offscrn.htm + http://glprogramming.com/blue/ch07.html + OffscreenContext.mm (Mac OSX version) + +*/ + #include "OffscreenContext.h" #include "printutils.h" #include "imageutils.h" +#include "fbo.h" -// see http://www.gamedev.net/topic/552607-conflict-between-glew-and-sdl/ -#define NO_SDL_GLEXT -#include <GL/glew.h> - -#define GL_GLEXT_PROTOTYPES #include <GL/gl.h> -#include <GL/glext.h> +#include <GL/glx.h> -//#include <GL/gl.h> -//#include <GL/glu.h> // for gluCheckExtension -#include <SDL.h> +#define REPORTGLERROR(task) { GLenum tGLErr = glGetError(); if (tGLErr != GL_NO_ERROR) { std::cout << "OpenGL error " << tGLErr << " while " << task << "\n"; } } struct OffscreenContext { + GLXContext openGLContext; + Display *xDisplay; + GLXPixmap glx_pixmap; + Pixmap x11_pixmap; int width; int height; - GLuint fbo; - GLuint colorbo; - GLuint depthbo; + fbo_t *fbo; }; -void write_targa(const char *filename, GLubyte *pixels, int width, int height) +Bool glx_1_3_pixmap_dummy_context(OffscreenContext *ctx, Bool hybrid) +{ + XVisualInfo *vInfo; + GLXFBConfig *fbConfigs; + + int numReturned; + int result; + int dummyAttributes_1_3[] = { + GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, + GLX_RENDER_TYPE, GLX_RGBA_BIT, + None + }; + + fbConfigs = glXChooseFBConfig( ctx->xDisplay, + DefaultScreen(ctx->xDisplay), + dummyAttributes_1_3, &numReturned ); + if ( fbConfigs == NULL ) { + REPORTGLERROR("glXChooseFBConfig failed") ; + return False; + } + + vInfo = glXGetVisualFromFBConfig( ctx->xDisplay, fbConfigs[0] ); + if ( vInfo == NULL ) { + REPORTGLERROR("glXGetVisualFromFBConfig failed") ; + return False; + } + + ctx->x11_pixmap = XCreatePixmap( ctx->xDisplay, DefaultRootWindow(ctx->xDisplay) , 10, 10, 8 ); + + if (hybrid) { + // GLX 1.2 - prevent Mesa warning + ctx->glx_pixmap = glXCreateGLXPixmap( ctx->xDisplay, vInfo, ctx->x11_pixmap ); + } else { + // GLX 1.3 + ctx->glx_pixmap = glXCreatePixmap( ctx->xDisplay, fbConfigs[0], ctx->x11_pixmap, NULL ); // GLX 1.3 + } + + ctx->openGLContext = glXCreateNewContext( ctx->xDisplay, fbConfigs[0], GLX_RGBA_TYPE, NULL, True ); + if ( ctx->openGLContext == NULL ) { + REPORTGLERROR("glXCreateNewContext failed" ); + return False; + } + + result = glXMakeContextCurrent( ctx->xDisplay, ctx->glx_pixmap, ctx->glx_pixmap, ctx->openGLContext ); + if ( result == False ) { + REPORTGLERROR("glXMakeContextCurrent failed" ); + return False; + } + + return True; +} + +Bool make_glx_dummy_context(OffscreenContext *ctx) { - FILE *f = fopen( filename, "w" ); - int y; - if (f) { - GLubyte header[] = { - 00,00,02, 00,00,00, 00,00,00, 00,00,00, - 0xff & width, 0xff & width >> 8, - 0xff & height, 0xff & height >> 8, - 32, 0x20 }; // next-to-last = bit depth - fwrite( header, sizeof(header), 1, f); - for (y=height-1; y>=0; y--) - fwrite( pixels + y*width*4, 4, width, f); - fclose(f); + /* + Before opening a framebuffer, an OpenGL context must be created. + For GLX, you can do this by creating a 'Pixmap' drawable then + creating the Context off of that. The Pixmap is then never used. + */ + int major; + int minor; + + ctx->xDisplay = XOpenDisplay( NULL ); + if ( ctx->xDisplay == NULL ) { + fprintf(stderr, "Unable to open a connection to the X server\n" ); + return False; + } + + /* + On some systems, the GLX library will report that it is version + 1.2, but some 1.3 functions will be implemented, and, furthermore, + some 1.2 functions won't work right, while the 1.3 functions will, + but glXCreatePixmp will still generate a MESA GLX 1.3 runtime warning. + + To workaround this, detect the situation and use 'hybrid' mix of + 1.2 and 1.3 as needed. + */ + glXQueryVersion(ctx->xDisplay, &major, &minor); + + if (major==1 && minor<=2) { + if (glXCreatePixmap!=NULL) { // 1.3 function exists, even though its 1.2 + return glx_1_3_pixmap_dummy_context(ctx,True); + } else { + fprintf(stderr,"OpenGL error: GLX version 1.3 functions missing. Your GLX: %i.%i\n",major,minor); + return False; + } + } else if (major>=1 && minor>=3) { + return glx_1_3_pixmap_dummy_context(ctx,False); } } +void glewCheck() { +#ifdef DEBUG + cout << "GLEW version " << glewGetString(GLEW_VERSION) << "\n"; + cout << (const char *)glGetString(GL_RENDERER) << "(" << (const char *)glGetString(GL_VENDOR) << ")\n" + << "OpenGL version " << (const char *)glGetString(GL_VERSION) << "\n"; + cout << "Extensions: " << (const char *)glGetString(GL_EXTENSIONS) << "\n"; + + if (GLEW_ARB_framebuffer_object) { + cout << "ARB_FBO supported\n"; + } + if (GLEW_EXT_framebuffer_object) { + cout << "EXT_FBO supported\n"; + } + if (GLEW_EXT_packed_depth_stencil) { + cout << "EXT_packed_depth_stencil\n"; + } +#endif +} + OffscreenContext *create_offscreen_context(int w, int h) { OffscreenContext *ctx = new OffscreenContext; ctx->width = w; ctx->height = h; + ctx->openGLContext = NULL; + ctx->xDisplay = NULL; + ctx->glx_pixmap = NULL; + ctx->x11_pixmap = NULL; + ctx->fbo = NULL; + + // fill ctx->xDisplay, ctx->openGLContext, x11_pixmap, glx_pixmap + if (!make_glx_dummy_context(ctx)) { + return NULL; + } - // dummy window - SDL_Init(SDL_INIT_VIDEO); - SDL_SetVideoMode(ctx->width,ctx->height,32,SDL_OPENGL); - - // must come after openGL context init (done by dummy window) - // but must also come before various EXT calls - //glewInit(); + glewInit(); //must come after Context creation and before FBO calls. + glewCheck(); -/* - glClearColor(1, 1, 1, 1); - glClear(GL_COLOR_BUFFER_BIT); - glBegin(GL_TRIANGLES); - glColor3f( 1, 0, 0); - glVertex3f( 0, 0, 0); - glVertex3f( 1, 0, 0); - glVertex3f( 0, 1, 0); - glEnd(); - SDL_GL_SwapBuffers(); -// sleep(2); -*/ - - int samplesPerPixel = 4; // R, G, B and A + ctx->fbo = fbo_new(); + if (!fbo_init(ctx->fbo, w, h)) { + return NULL; + } -/* char * filename = "blah.tga"; - GLubyte pixels[ ctx->width * ctx->height * samplesPerPixel ]; - glReadPixels(0, 0, ctx->width, ctx->height, GL_BGRA, GL_UNSIGNED_BYTE, pixels); - printf("writing %s\n",filename); - write_targa(filename,pixels,ctx->width, ctx->height);*/ return ctx; } bool teardown_offscreen_context(OffscreenContext *ctx) { - return true; + fbo_unbind(ctx->fbo); + fbo_delete(ctx->fbo); + glXDestroyPixmap(ctx->xDisplay, ctx->glx_pixmap ); + XFreePixmap(ctx->xDisplay, ctx->x11_pixmap ); + glXDestroyContext( ctx->xDisplay, ctx->openGLContext ); + return true; } /*! @@ -87,8 +184,6 @@ bool teardown_offscreen_context(OffscreenContext *ctx) */ bool save_framebuffer(OffscreenContext *ctx, const char *filename) { - SDL_GL_SwapBuffers(); // show image - int samplesPerPixel = 4; // R, G, B and A GLubyte pixels[ctx->width * ctx->height * samplesPerPixel]; glReadPixels(0, 0, ctx->width, ctx->height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); @@ -111,4 +206,5 @@ bool save_framebuffer(OffscreenContext *ctx, const char *filename) void bind_offscreen_context(OffscreenContext *ctx) { + fbo_bind(ctx->fbo); } diff --git a/tests/OffscreenView.cc b/tests/OffscreenView.cc index 8a4b57d..d188d0d 100644 --- a/tests/OffscreenView.cc +++ b/tests/OffscreenView.cc @@ -14,7 +14,8 @@ OffscreenView::OffscreenView(size_t width, size_t height) object_rot(35, 0, 25), camera_eye(0, 0, 0), camera_center(0, 0, 0) { for (int i = 0; i < 10; i++) this->shaderinfo[i] = 0; - this->ctx = create_offscreen_context(width, height); + this->ctx = create_offscreen_context(width, height); + if ( this->ctx == NULL ) throw -1; initializeGL(); resizeGL(width, height); } @@ -185,7 +186,7 @@ void OffscreenView::paintGL() glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - glClearColor(1.0, 1.0, 0.92, 0.0); + glClearColor(1.0, 1.0, 0.92, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); diff --git a/tests/cgalpngtest.cc b/tests/cgalpngtest.cc index 638c088..447df45 100644 --- a/tests/cgalpngtest.cc +++ b/tests/cgalpngtest.cc @@ -179,7 +179,12 @@ int main(int argc, char **argv) QDir::setCurrent(original_path.absolutePath()); // match with csgtest ends - csgInfo.glview = new OffscreenView(512,512); + try { + csgInfo.glview = new OffscreenView(512,512); + } catch (int error) { + fprintf(stderr,"Can't create OpenGL OffscreenView. exiting.\n"); + exit(1); + } GLenum err = glewInit(); if (GLEW_OK != err) { diff --git a/tests/csgtestcore.cc b/tests/csgtestcore.cc index 6ef05f3..2e28bf6 100644 --- a/tests/csgtestcore.cc +++ b/tests/csgtestcore.cc @@ -218,7 +218,12 @@ int csgtestcore(int argc, char *argv[], test_type_e test_type) QDir::setCurrent(original_path.absolutePath()); - csgInfo.glview = new OffscreenView(512,512); + try { + csgInfo.glview = new OffscreenView(512,512); + } catch (int error) { + fprintf(stderr,"Can't create OpenGL OffscreenView. exiting.\n"); + exit(1); + } BoundingBox bbox = csgInfo.root_chain->getBoundingBox(); Vector3d center = (bbox.min() + bbox.max()) / 2; diff --git a/tests/fbo.cc b/tests/fbo.cc index 403a32e..b7bf1c0 100644 --- a/tests/fbo.cc +++ b/tests/fbo.cc @@ -15,13 +15,94 @@ fbo_t *fbo_new() #define REPORTGLERROR(task) { GLenum tGLErr = glGetError(); if (tGLErr != GL_NO_ERROR) { std::cout << "OpenGL error " << tGLErr << " while " << task << "\n"; } } -bool fbo_init(fbo_t *fbo, size_t width, size_t height) +bool use_ext() +{ + // do we need to use the EXT or ARB version? + if (!glewIsSupported("GL_ARB_framebuffer_object") && + glewIsSupported("GL_EXT_framebuffer_object")) { + return true; + } else { + return false; + } +} + +bool check_fbo_status() { - if (!glewIsSupported("GL_ARB_framebuffer_object")) { - fprintf(stderr, "Framebuffer extension not found\n"); + /* This code is based on user V-man code from + http://www.opengl.org/wiki/GL_EXT_framebuffer_multisample + See also: http://www.songho.ca/opengl/gl_fbo.html */ + GLenum status; + bool result = false; + if (use_ext()) + status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + else + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + if (glGetError() != GL_NO_ERROR) + fprintf(stderr, "OpenGL Error %i\n",glGetError()); + + if (status == GL_FRAMEBUFFER_COMPLETE) + result = true; + else if (status == GL_FRAMEBUFFER_UNSUPPORTED) + fprintf(stderr, "GL_FRAMEBUFFER_UNSUPPORTED\n"); + else if (status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) + fprintf(stderr, "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT\n"); + else if (status == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) + fprintf(stderr, "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT\n"); + else if (status == GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT) + fprintf(stderr, "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT\n"); + else if (status == GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT) + fprintf(stderr, "GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT\n"); + else if (status == GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT) + fprintf(stderr, "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT\n"); + else if (status == GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT) + fprintf(stderr, "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT\n"); + else if (status == GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT) + fprintf(stderr, "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT\n"); + else + fprintf(stderr, "Unknown Code: glCheckFramebufferStatusEXT returned %i\n",status); + return result; +} + +bool fbo_ext_init(fbo_t *fbo, size_t width, size_t height) +{ + // Generate and bind FBO + glGenFramebuffersEXT(1, &fbo->fbo_id); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->fbo_id); + REPORTGLERROR("binding framebuffer"); + + // Generate depth and render buffers + glGenRenderbuffersEXT(1, &fbo->depthbuf_id); + glGenRenderbuffersEXT(1, &fbo->renderbuf_id); + + // Create buffers with correct size + if (!fbo_resize(fbo, width, height)) return false; + + // Attach render and depth buffers + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_RENDERBUFFER_EXT, fbo->renderbuf_id); + REPORTGLERROR("specifying color render buffer"); + + + if (!check_fbo_status()) { + fprintf(stderr, "Problem with OpenGL framebuffer after specifying color render buffer.\n"); + return false; + } + + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, fbo->depthbuf_id); + REPORTGLERROR("specifying depth render buffer"); + + if (!check_fbo_status()) { + fprintf(stderr, "Problem with OpenGL framebuffer after specifying depth render buffer.\n"); return false; } + return true; +} + +bool fbo_arb_init(fbo_t *fbo, size_t width, size_t height) +{ // Generate and bind FBO glGenFramebuffers(1, &fbo->fbo_id); glBindFramebuffer(GL_FRAMEBUFFER, fbo->fbo_id); @@ -56,15 +137,48 @@ bool fbo_init(fbo_t *fbo, size_t width, size_t height) return true; } -bool fbo_resize(fbo_t *fbo, size_t width, size_t height) + +bool fbo_init(fbo_t *fbo, size_t width, size_t height) { - glBindRenderbuffer(GL_RENDERBUFFER, fbo->depthbuf_id); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height); - REPORTGLERROR("creating depth render buffer"); + /* + Some OpenGL drivers include the framebuffer functions but not with + core or ARB names, only with the EXT name. This has been worked-around + by deciding at runtime, using GLEW, which version needs to be used. See also: + + http://www.opengl.org/wiki/Framebuffer_Object + http://stackoverflow.com/questions/6912988/glgenframebuffers-or-glgenframebuffersex + http://www.devmaster.net/forums/showthread.php?t=10967 + */ + + bool result = false; + if (glewIsSupported("GL_ARB_framebuffer_object")) + result = fbo_arb_init(fbo, width, height); + else if (use_ext()) + result = fbo_ext_init(fbo, width, height); + else + fprintf(stderr, "Framebuffer Object extension not found by GLEW\n"); + return result; +} - glBindRenderbuffer(GL_RENDERBUFFER, fbo->renderbuf_id); - glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height); - REPORTGLERROR("creating color render buffer"); +bool fbo_resize(fbo_t *fbo, size_t width, size_t height) +{ + if (use_ext()) { + glBindRenderbufferEXT(GL_RENDERBUFFER, fbo->depthbuf_id); + glRenderbufferStorageEXT(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height); + REPORTGLERROR("creating depth render buffer"); + + glBindRenderbufferEXT(GL_RENDERBUFFER, fbo->renderbuf_id); + glRenderbufferStorageEXT(GL_RENDERBUFFER, GL_RGBA8, width, height); + REPORTGLERROR("creating color render buffer"); + } else { + glBindRenderbuffer(GL_RENDERBUFFER, fbo->depthbuf_id); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height); + REPORTGLERROR("creating depth render buffer"); + + glBindRenderbuffer(GL_RENDERBUFFER, fbo->renderbuf_id); + glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height); + REPORTGLERROR("creating color render buffer"); + } return true; } @@ -77,11 +191,17 @@ void fbo_delete(fbo_t *fbo) GLuint fbo_bind(fbo_t *fbo) { glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)&fbo->old_fbo_id); - glBindFramebuffer(GL_FRAMEBUFFER, fbo->fbo_id); + if (use_ext()) + glBindFramebufferEXT(GL_FRAMEBUFFER, fbo->fbo_id); + else + glBindFramebuffer(GL_FRAMEBUFFER, fbo->fbo_id); return fbo->old_fbo_id; } void fbo_unbind(fbo_t *fbo) { - glBindFramebuffer(GL_FRAMEBUFFER, fbo->old_fbo_id); + if (use_ext()) + glBindFramebufferEXT(GL_FRAMEBUFFER, fbo->old_fbo_id); + else + glBindFramebuffer(GL_FRAMEBUFFER, fbo->old_fbo_id); } |