summaryrefslogtreecommitdiff
path: root/tests/OffscreenContext.cc
diff options
context:
space:
mode:
Diffstat (limited to 'tests/OffscreenContext.cc')
-rw-r--r--tests/OffscreenContext.cc244
1 files changed, 244 insertions, 0 deletions
diff --git a/tests/OffscreenContext.cc b/tests/OffscreenContext.cc
new file mode 100644
index 0000000..e4045af
--- /dev/null
+++ b/tests/OffscreenContext.cc
@@ -0,0 +1,244 @@
+/*
+
+Create an OpenGL context without creating an OpenGL Window. for Linux.
+
+See Also
+
+ glxgears.c by Brian Paul from mesa-demos (mesa3d.org)
+ http://cgit.freedesktop.org/mesa/demos/tree/src/xdemos?id=mesa-demos-8.0.1
+ 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 "system-gl.h"
+#include "fbo.h"
+
+#include <GL/gl.h>
+#include <GL/glx.h>
+
+using namespace std;
+
+struct OffscreenContext
+{
+ GLXContext openGLContext;
+ Display *xdisplay;
+ Window xwindow;
+ int width;
+ int height;
+ fbo_t *fbo;
+};
+
+void offscreen_context_init(OffscreenContext &ctx, int width, int height)
+{
+ ctx.width = width;
+ ctx.height = height;
+ ctx.openGLContext = NULL;
+ ctx.xdisplay = NULL;
+ ctx.xwindow = (Window)NULL;
+ ctx.fbo = NULL;
+}
+
+static XErrorHandler original_xlib_handler = (XErrorHandler) NULL;
+static bool XCreateWindow_failed = false;
+static int XCreateWindow_error(Display *dpy, XErrorEvent *event)
+{
+ cerr << "XCreateWindow failed: XID: " << event->resourceid
+ << " request: " << (int)event->request_code
+ << " minor: " << (int)event->minor_code << "\n";
+ char description[1024];
+ XGetErrorText( dpy, event->error_code, description, 1023 );
+ cerr << " error message: " << description << "\n";
+ XCreateWindow_failed = true;
+ return 0;
+}
+
+bool create_glx_dummy_window(OffscreenContext &ctx)
+{
+ /*
+ create a dummy X window without showing it. (without 'mapping' it)
+ and save information to the ctx.
+
+ This purposely does not use glxCreateWindow, to avoid crashes,
+ "failed to create drawable" errors, and Mesa "WARNING: Application calling
+ GLX 1.3 function when GLX 1.3 is not supported! This is an application bug!"
+
+ This function will alter ctx.openGLContext and ctx.xwindow if successfull
+ */
+
+ int attributes[] = {
+ GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
+ GLX_RENDER_TYPE, GLX_RGBA_BIT,
+ GLX_RED_SIZE, 1,
+ GLX_GREEN_SIZE, 1,
+ GLX_BLUE_SIZE, 1,
+ None
+ };
+
+ Display *dpy = ctx.xdisplay;
+
+ int num_returned = 0;
+ GLXFBConfig *fbconfigs = glXChooseFBConfig( dpy, DefaultScreen(dpy), attributes, &num_returned );
+ if ( fbconfigs == NULL ) {
+ cerr << "glXChooseFBConfig failed\n";
+ return false;
+ }
+
+ XVisualInfo *visinfo = glXGetVisualFromFBConfig( dpy, fbconfigs[0] );
+ if ( visinfo == NULL ) {
+ cerr << "glXGetVisualFromFBConfig failed\n";
+ XFree( fbconfigs );
+ return false;
+ }
+
+ // can't depend on xWin==NULL at failure. use a custom Xlib error handler instead.
+ original_xlib_handler = XSetErrorHandler( XCreateWindow_error );
+ Window xWin = XCreateSimpleWindow( dpy, DefaultRootWindow(dpy), 0,0,10,10, 0,0,0 );
+ XSync( dpy, false );
+ if ( XCreateWindow_failed ) {
+ XFree( visinfo );
+ XFree( fbconfigs );
+ return false;
+ }
+ XSetErrorHandler( original_xlib_handler );
+
+ // Most programs would call XMapWindow here. But we don't, to keep the window hidden
+ // XMapWindow( dpy, xWin );
+
+ GLXContext context = glXCreateNewContext( dpy, fbconfigs[0], GLX_RGBA_TYPE, NULL, True );
+ if ( context == NULL ) {
+ cerr << "glXCreateNewContext failed\n";
+ XDestroyWindow( dpy, xWin );
+ XFree( visinfo );
+ XFree( fbconfigs );
+ return false;
+ }
+
+ //GLXWindow glxWin = glXCreateWindow( dpy, fbconfigs[0], xWin, NULL );
+
+ if (!glXMakeContextCurrent( dpy, xWin, xWin, context )) {
+ //if (!glXMakeContextCurrent( dpy, glxWin, glxWin, context )) {
+ cerr << "glXMakeContextCurrent failed\n";
+ glXDestroyContext( dpy, context );
+ XDestroyWindow( dpy, xWin );
+ XFree( visinfo );
+ XFree( fbconfigs );
+ return false;
+ }
+
+ ctx.openGLContext = context;
+ ctx.xwindow = xWin;
+
+ XFree( visinfo );
+ XFree( fbconfigs );
+
+ return true;
+}
+
+
+Bool create_glx_dummy_context(OffscreenContext &ctx)
+{
+ // This will alter ctx.openGLContext and ctx.xdisplay and ctx.xwindow if successfull
+ int major;
+ int minor;
+ Bool result = False;
+
+ ctx.xdisplay = XOpenDisplay( NULL );
+ if ( ctx.xdisplay == NULL ) {
+ cerr << "Unable to open a connection to the X server\n";
+ return False;
+ }
+
+ // glxQueryVersion is not always reliable. Use it, but then
+ // also check to see if GLX 1.3 functions exist
+
+ glXQueryVersion(ctx.xdisplay, &major, &minor);
+
+ if ( major==1 && minor<=2 && glXGetVisualFromFBConfig==NULL ) {
+ cerr << "Error: GLX version 1.3 functions missing. "
+ << "Your GLX version: " << major << "." << minor << endl;
+ } else {
+ result = create_glx_dummy_window(ctx);
+ }
+
+ if (!result) XCloseDisplay( ctx.xdisplay );
+ return result;
+}
+
+OffscreenContext *create_offscreen_context(int w, int h)
+{
+ OffscreenContext *ctx = new OffscreenContext;
+ offscreen_context_init( *ctx, w, h );
+
+ // before an FBO can be setup, a GLX context must be created
+ // this call alters ctx->xDisplay and ctx->openGLContext
+ // and ctx->xwindow if successfull
+ if (!create_glx_dummy_context( *ctx )) {
+ return NULL;
+ }
+
+ // glewInit must come after Context creation and before FBO calls.
+ GLenum err = glewInit();
+ if (GLEW_OK != err) {
+ cerr << "Unable to init GLEW: " << glewGetErrorString(err) << endl;
+ return NULL;
+ }
+ glew_dump();
+
+ ctx->fbo = fbo_new();
+ if (!fbo_init(ctx->fbo, w, h)) {
+ cerr << "GL Framebuffer Object init failed; dumping GLEW info" << endl;
+ return NULL;
+ }
+
+ return ctx;
+}
+
+bool teardown_offscreen_context(OffscreenContext *ctx)
+{
+ if (ctx) {
+ fbo_unbind(ctx->fbo);
+ fbo_delete(ctx->fbo);
+ XDestroyWindow( ctx->xdisplay, ctx->xwindow );
+ glXDestroyContext( ctx->xdisplay, ctx->openGLContext );
+ XCloseDisplay( ctx->xdisplay );
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Capture framebuffer from OpenGL and write it to the given filename as PNG.
+*/
+bool save_framebuffer(OffscreenContext *ctx, const char *filename)
+{
+ if (!ctx || !filename) return false;
+ 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);
+
+ // Flip it vertically - images read from OpenGL buffers are upside-down
+ int rowBytes = samplesPerPixel * ctx->width;
+ unsigned char *flippedBuffer = (unsigned char *)malloc(rowBytes * ctx->height);
+ if (!flippedBuffer) {
+ std::cerr << "Unable to allocate flipped buffer for corrected image.";
+ return 1;
+ }
+ flip_image(pixels, flippedBuffer, samplesPerPixel, ctx->width, ctx->height);
+
+ bool writeok = write_png(filename, flippedBuffer, ctx->width, ctx->height);
+
+ free(flippedBuffer);
+
+ return writeok;
+}
+
+void bind_offscreen_context(OffscreenContext *ctx)
+{
+ if (ctx) fbo_bind(ctx->fbo);
+}
contact: Jan Huwald // Impressum