2 * Offscreen OpenGL abstraction layer - GLX specific
4 * Copyright (c) 2010 Intel
6 * Gordon Williams <gordon.williams@collabora.co.uk>
7 * Ian Molton <ian.molton@collabora.co.uk>
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 #if (!defined _WIN32) && (!defined __APPLE__)
28 #include "gloffscreen.h"
35 #include <X11/Xutil.h>
40 #include <X11/extensions/XShm.h>
41 #include <X11/extensions/Xcomposite.h>
43 #ifdef MANGLE_OPENGL_SYMBOLS
44 #include "gl_mangled.h"
47 void *g_malloc(size_t size);
48 void *g_realloc(void *ptr, size_t size);
49 void g_free(void *ptr);
72 // For use by the 'fast' copy code.
75 XShmSegmentInfo shminfo;
79 extern void glo_surface_getcontents_readpixels(int formatFlags, int stride,
80 int bpp, int width, int height, void *data);
81 static void glo_test_readback_methods(void);
83 /* ------------------------------------------------------------------------ */
85 int glo_initialised(void) {
90 * The X error was disabled, otherwise QEMU will abort. Printing more error
91 * messages inside below function could help debug potential bugs. On the
92 * other hand, since the X errors could be caused by the GL calls forwarded
93 * from client OS side, which does not make sense to abort the whole QEMU,
94 * so it is reasonable to redirect error handler here.
97 static int x_errhandler(Display *dpy, XErrorEvent *e)
99 //fprintf (stderr, "X Error Happened!\n");
103 /* Sanity test of the host GL capabilities to see whether the gl offscreen
104 * could be well supported
106 int glo_sanity_test (void) {
110 /* Initialise gloffscreen */
113 printf( "gloffscreen already inited\n" );
114 //exit( EXIT_FAILURE );
118 /* Open a connection to the X server */
119 glo.dpy = XOpenDisplay( NULL );
120 if ( glo.dpy == NULL ) {
121 printf( "Unable to open a connection to the X server\n" );
122 //exit( EXIT_FAILURE );
126 glo_inited = 1; // safe because we are single threaded. Otherwise we cause
127 // recursion on the next call.
128 // set the X error handler.
129 XSetErrorHandler (x_errhandler);
130 glo_test_readback_methods();
134 /* Uninitialise gloffscreen */
135 void glo_kill(void) {
136 XCloseDisplay(glo.dpy);
141 /* Like wglGetProcAddress/glxGetProcAddress */
142 void *glo_getprocaddress(const char *procName) {
146 return glXGetProcAddressARB((const GLubyte *) procName);
149 /* ------------------------------------------------------------------------ */
151 /* Create a light-weight context just for creating surface */
152 GloContext *__glo_context_create(int formatFlags) {
154 GLXFBConfig *fbConfigs;
158 int bufferAttributes[] = {
159 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
160 GLX_RENDER_TYPE, GLX_RGBA_BIT,
173 // set up the surface format from the flags we were given
174 glo_flags_get_rgba_bits(formatFlags, rgbaBits);
175 bufferAttributes[5] = rgbaBits[0];
176 bufferAttributes[7] = rgbaBits[1];
177 bufferAttributes[9] = rgbaBits[2];
178 bufferAttributes[11] = rgbaBits[3];
179 bufferAttributes[13] = glo_flags_get_depth_bits(formatFlags);
180 bufferAttributes[15] = glo_flags_get_stencil_bits(formatFlags);
182 fbConfigs = glXChooseFBConfig( glo.dpy, DefaultScreen(glo.dpy),
183 bufferAttributes, &numReturned );
184 if (numReturned==0) {
185 printf( "No matching configs found.\n" );
186 //exit( EXIT_FAILURE );
189 context = (GloContext*)g_malloc(sizeof(GloContext));
190 memset(context, 0, sizeof(GloContext));
191 context->formatFlags = formatFlags;
192 context->fbConfig = fbConfigs[0];
197 /* Create an OpenGL context for a certain pixel format.
198 formatflags are from the GLO_ constants */
200 GloContext *glo_context_create(int formatFlags, GloContext *shareLists) {
202 GloContext *context = __glo_context_create(formatFlags);
208 /* Create a GLX context for OpenGL rendering */
209 context->context = glXCreateNewContext(glo.dpy, context->fbConfig,
211 shareLists ? shareLists->context: NULL,
214 if (!context->context) {
215 printf( "glXCreateNewContext failed\n" );
216 //exit( EXIT_FAILURE );
223 /* Destroy a previously created OpenGL context */
224 void glo_context_destroy(GloContext *context) {
225 if (!context) return;
227 // TODO: check for GloSurfaces using this?
228 glXDestroyContext( glo.dpy, context->context);
232 static void glo_surface_free_xshm_image(GloSurface *surface) {
233 XShmDetach(glo.dpy, &surface->shminfo);
234 surface->image->data = NULL;
235 XDestroyImage(surface->image);
236 shmdt(surface->shminfo.shmaddr);
237 shmctl(surface->shminfo.shmid, IPC_RMID, NULL);
240 //FIXMEIM - handle failure to allocate.
241 static void glo_surface_try_alloc_xshm_image(GloSurface *surface) {
244 glo_surface_free_xshm_image(surface);
247 XShmCreateImage(glo.dpy, DefaultVisual(glo.dpy, 0), 24, ZPixmap, NULL,
248 &surface->shminfo, surface->width, surface->height);
250 surface->shminfo.shmid = shmget(IPC_PRIVATE,
251 surface->image->bytes_per_line *
254 surface->shminfo.shmaddr = shmat(surface->shminfo.shmid, NULL, 0);
255 surface->image->data = surface->shminfo.shmaddr;
256 surface->shminfo.readOnly = False;
257 XShmAttach(glo.dpy, &surface->shminfo);
260 /* ------------------------------------------------------------------------ */
262 /* Update the context in surface and handle previous context */
263 void glo_surface_update_context(GloSurface *surface, GloContext *context, int free_flags)
265 /* If previous context is light-weight context, just free it. If previous
266 * context is valid one binded with surface via MakeCurrent, we need unbind
267 * from original glstate */
269 if ( surface->context )
271 if (free_flags) /* light-weight context */
272 g_free(surface->context);
274 surface->context = context;
277 /* Create a surface with given width and height, formatflags are from the
279 GloSurface *glo_surface_create(int width, int height, GloContext *context) {
281 XSetWindowAttributes attr = { 0 };
288 surface = (GloSurface*)g_malloc(sizeof(GloSurface));
289 memset(surface, 0, sizeof(GloSurface));
290 surface->width = width;
291 surface->height = height;
292 surface->context = context;
294 vis = glXGetVisualFromFBConfig(glo.dpy, ((struct _GloContext*)context)->fbConfig);
296 attr.background_pixel = 0xff000000;
297 attr.border_pixel = 0;
298 attr.colormap = XCreateColormap(glo.dpy, DefaultRootWindow(glo.dpy),
299 vis->visual, AllocNone);
301 attr.save_under = True;
302 attr.override_redirect = True;
304 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask |
305 CWOverrideRedirect | CWSaveUnder;
307 surface->window = XCreateWindow(glo.dpy, DefaultRootWindow(glo.dpy), 0, 3000, width, height, 0, vis->depth, InputOutput, vis->visual, mask, &attr);
309 if (!surface->window) {
310 printf( "XCreateWindow failed\n" );
311 //exit( EXIT_FAILURE );
315 XMapWindow(glo.dpy, surface->window);
316 XCompositeRedirectWindow (glo.dpy, surface->window, CompositeRedirectAutomatic);
319 surface->pixmap = XCompositeNameWindowPixmap(glo.dpy, surface->window);
321 surface->pixmap = XCreatePixmap( glo.dpy, DefaultRootWindow(glo.dpy),
323 glo_flags_get_bytes_per_pixel(context->formatFlags)*8);
326 if(surface->pixmap == 0) {
327 fprintf(stderr, "Failed to allocate pixmap!\n");
328 //exit(EXIT_FAILURE);
332 /* Create a GLX pixmap to associate the frame buffer configuration with the
333 * created X window */
334 /*XXX: need attribute_list? */
335 surface->glxPixmap = glXCreatePixmap( glo.dpy, context->fbConfig, surface->pixmap, NULL );
336 if (!surface->glxPixmap) {
337 printf( "glXCreatePixmap failed\n" );
338 //exit( EXIT_FAILURE );
344 /* set hints and properties */
346 XSizeHints sizehints;
350 sizehints.width = width;
351 sizehints.height = height;
352 sizehints.flags = USSize | USPosition;
353 XSetWMNormalHints(glo.dpy, surface->window, &sizehints);
354 XSetStandardProperties(glo.dpy, surface->window, "", "", None,
355 (char **) NULL, 0, &sizehints);
360 // If we're using XImages to pull the data from the graphics card...
361 glo_surface_try_alloc_xshm_image(surface);
366 /* Destroy the given surface */
367 void glo_surface_destroy(GloSurface *surface) {
370 XFreePixmap( glo.dpy, surface->pixmap);
372 if(surface->glxPixmap)
373 glXDestroyPixmap( glo.dpy, surface->glxPixmap);
375 XDestroyWindow( glo.dpy, surface->window);
377 glo_surface_free_xshm_image(surface);
382 /* Make the given surface current */
383 int glo_surface_makecurrent(GloSurface *surface) {
390 ret = glXMakeCurrent(glo.dpy, surface->window,
391 surface->context->context);
393 ret = glXMakeCurrent(glo.dpy, 0, NULL);
398 void glo_surface_updatecontents(GloSurface *surface) {
406 XShmGetImage (glo.dpy, surface->pixmap, surface->image, 0, 0, AllPlanes);
409 XGetImage(glo.dpy, surface->pixmap, 0, 0, surface->width, surface->height, AllPlanes, ZPixmap);
415 /* Get the contents of the given surface */
416 void glo_surface_getcontents(GloSurface *surface, int stride, int bpp, void *data) {
427 XShmGetImage (glo.dpy, surface->pixmap, surface->image, 0, 0, AllPlanes);
428 img = surface->image;
431 img = XGetImage(glo.dpy, surface->pixmap, 0, 0, surface->width, surface->height, AllPlanes, ZPixmap);
435 if(bpp != 32 && bpp != 24 && !once) {
436 fprintf(stderr, "Warning: unsupported colourdepth\n");
440 if(bpp == img->bits_per_pixel && stride == img->bytes_per_line)
442 memcpy(data, img->data, stride * surface->height);
447 for(y = 0 ; y < surface->height ; y++) {
448 for(x = 0 ; x < surface->width ; x++) {
449 char *src = ((char*)img->data) +
450 (x*(img->bits_per_pixel/8)) +
451 (y*img->bytes_per_line);
452 char *dst = ((char*)data) + x*(bpp/8) + (y*stride);
457 dst[3] = 0xff; // if guest is 32 bit and host is 24
462 // If we're not using Shm
466 return; // We're done.
468 // Uh oh... better fallback. Perhaps get glo.use_ximage to 0?
471 // Compatible / fallback method.
472 glo_surface_getcontents_readpixels(surface->context->formatFlags,
473 stride, bpp, surface->width,
474 surface->height, data);
477 /* Return the width and height of the given surface */
478 void glo_surface_get_size(GloSurface *surface, int *width, int *height) {
480 *width = surface->width;
482 *height = surface->height;
485 /* Bind the surface as texture */
486 void glo_surface_as_texture(GloContext *ctxt, GloSurface *surface)
489 void (*ptr_func_glXBindTexImageEXT) (Display *dpy, GLXDrawable draw, int buffer, int *attrib_list);
490 ptr_func_glXBindTexImageEXT =
491 (void(*)(Display*, GLXDrawable, int, int*))glo_getprocaddress((const char*)"glXBindTexImageEXT");
493 /*XXX: When to call the glXReleaseTexImageEXT?*/
494 if (!ptr_func_glXBindTexImageEXT)
496 fprintf (stderr, "glXBindTexImageEXT not supported! Can't emulate glEGLImageTargetTexture2DOES!\n");
499 fprintf(stderr, "surface_as_texture:error=%d.\n", glGetError());
500 ptr_func_glXBindTexImageEXT(glo.dpy, surface->glxPixmap, GLX_FRONT_LEFT_EXT, NULL);
501 fprintf(stderr, "surface_as_texture:2:error=%d.\n", glGetError());
503 int glFormat, glType;
504 glo_surface_updatecontents(surface);
505 /*XXX: changet the fixed target: GL_TEXTURE_2D*/
506 glo_flags_get_readpixel_type(surface->context->formatFlags, &glFormat, &glType);
507 fprintf(stderr, "surface_as_texture:teximage:width=%d,height=%d, glFormat=0x%x, glType=0x%x.\n", surface->width, surface->height, glFormat, glType);
508 /* glTexImage2D use different RGB order than the contexts in the pixmap surface */
509 /* glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surface->width, surface->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, surface->image->data);*/
511 if ((glFormat == GL_RGBA || glFormat == GL_BGRA) && glType == GL_UNSIGNED_BYTE) {
512 GLubyte *b = (GLubyte *)surface->image->data;
513 int stride = surface->width * 4;
514 GLubyte *c = &((GLubyte *)surface->image->data)[stride*(surface->height-1)];
515 GLubyte *tmp = (GLubyte*)g_malloc(stride);
518 for(irow = 0; irow < surface->height / 2; irow++) {
519 memcpy(tmp, b, stride);
520 memcpy(b, c, stride);
521 memcpy(c, tmp, stride);
527 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surface->width, surface->height, 0, glFormat, glType, surface->image->data);
531 void glo_surface_release_texture(GloSurface *surface)
535 /* Abstract glXQueryExtensionString() */
536 const char *glo_glXQueryExtensionsString(void) {
537 return glXQueryExtensionsString(glo.dpy, 0);
544 static int glo_can_readback(void) {
548 unsigned char *datain = (unsigned char *)g_malloc(4*TX*TY);
549 unsigned char *datain_flip = (unsigned char *)g_malloc(4*TX*TY); // flipped input data (for GL)
550 unsigned char *dataout;
554 const int bufferAttributes[] = {
564 int bufferFlags = glo_flags_get_from_glx(bufferAttributes, 0);
565 int bpp = glo_flags_get_bytes_per_pixel(bufferFlags);
566 int glFormat, glType;
568 memset(datain_flip, 0, TX*TY*4);
569 memset(datain, 0, TX*TY*4);
577 if (bpp>3) p[3] = 0xFF;
580 memcpy(&datain_flip[((TY-1)-y)*bpp*TX], &datain[y*bpp*TX], bpp*TX);
583 context = glo_context_create(bufferFlags, 0);
584 if (context == NULL) {
589 surface = glo_surface_create(TX, TY, context);
590 if (surface == NULL) {
596 glo_surface_makecurrent(surface);
598 glClear(GL_COLOR_BUFFER_BIT);
599 glMatrixMode(GL_PROJECTION);
601 glOrtho(0,TX, 0,TY, 0, 1);
602 glMatrixMode(GL_MODELVIEW);
605 glo_flags_get_readpixel_type(bufferFlags, &glFormat, &glType);
606 glDrawPixels(TX,TY,glFormat, glType, datain_flip);
609 dataout = (unsigned char *)g_malloc(4*TX*TY);
610 memset(dataout, 0, bpp*TX*TY);
612 glo_surface_getcontents(surface, TX*4, bpp*8, dataout);
614 glo_surface_destroy(surface);
615 glo_context_destroy(context);
617 if (memcmp(datain, dataout, bpp*TX*TY)==0) {
631 static void glo_test_readback_methods(void) {
633 if(!glo_can_readback())
636 //fprintf(stderr, "VM GL: Using %s readback\n", glo.use_ximage?"XImage":"glReadPixels");