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"
53 void *g_malloc(size_t size);
54 void *g_realloc(void *ptr, size_t size);
55 void g_free(void *ptr);
78 // For use by the 'fast' copy code.
81 XShmSegmentInfo shminfo;
85 extern void glo_surface_getcontents_readpixels(int formatFlags, int stride,
86 int bpp, int width, int height, void *data, int noflip);
87 static void glo_test_readback_methods(void);
89 /* ------------------------------------------------------------------------ */
91 int glo_initialised(void) {
96 * The X error was disabled, otherwise QEMU will abort. Printing more error
97 * messages inside below function could help debug potential bugs. On the
98 * other hand, since the X errors could be caused by the GL calls forwarded
99 * from client OS side, which does not make sense to abort the whole QEMU,
100 * so it is reasonable to redirect error handler here.
103 static int x_errhandler(Display *dpy, XErrorEvent *e)
105 //fprintf (stderr, "X Error Happened!\n");
109 /* Sanity test of the host GL capabilities to see whether the gl offscreen
110 * could be well supported
112 int glo_sanity_test (void) {
116 /* Initialise gloffscreen */
119 printf( "gloffscreen already inited\n" );
120 //exit( EXIT_FAILURE );
124 /* Open a connection to the X server */
125 glo.dpy = XOpenDisplay( NULL );
126 if ( glo.dpy == NULL ) {
127 printf( "Unable to open a connection to the X server\n" );
128 //exit( EXIT_FAILURE );
132 glo_inited = 1; // safe because we are single threaded. Otherwise we cause
133 // recursion on the next call.
134 // set the X error handler.
135 XSetErrorHandler (x_errhandler);
136 glo_test_readback_methods();
140 /* Uninitialise gloffscreen */
141 void glo_kill(void) {
142 XCloseDisplay(glo.dpy);
147 /* Like wglGetProcAddress/glxGetProcAddress */
148 void *glo_getprocaddress(const char *procName) {
152 return glXGetProcAddressARB((const GLubyte *) procName);
155 /* ------------------------------------------------------------------------ */
157 /* Create a light-weight context just for creating surface */
158 GloContext *__glo_context_create(int formatFlags) {
160 GLXFBConfig *fbConfigs;
164 int bufferAttributes[] = {
165 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
166 GLX_RENDER_TYPE, GLX_RGBA_BIT,
179 // set up the surface format from the flags we were given
180 glo_flags_get_rgba_bits(formatFlags, rgbaBits);
181 bufferAttributes[5] = rgbaBits[0];
182 bufferAttributes[7] = rgbaBits[1];
183 bufferAttributes[9] = rgbaBits[2];
184 bufferAttributes[11] = rgbaBits[3];
185 bufferAttributes[13] = glo_flags_get_depth_bits(formatFlags);
186 bufferAttributes[15] = glo_flags_get_stencil_bits(formatFlags);
188 fbConfigs = glXChooseFBConfig( glo.dpy, DefaultScreen(glo.dpy),
189 bufferAttributes, &numReturned );
190 if (numReturned==0) {
191 printf( "No matching configs found.\n" );
192 //exit( EXIT_FAILURE );
195 context = (GloContext*)g_malloc(sizeof(GloContext));
196 memset(context, 0, sizeof(GloContext));
197 context->formatFlags = formatFlags;
198 context->fbConfig = fbConfigs[0];
203 /* Create an OpenGL context for a certain pixel format.
204 formatflags are from the GLO_ constants */
206 GloContext *glo_context_create(int formatFlags, GloContext *shareLists) {
208 GloContext *context = __glo_context_create(formatFlags);
214 /* Create a GLX context for OpenGL rendering */
215 context->context = glXCreateNewContext(glo.dpy, context->fbConfig,
217 shareLists ? shareLists->context: NULL,
220 if (!context->context) {
221 printf( "glXCreateNewContext failed\n" );
222 //exit( EXIT_FAILURE );
229 /* Destroy a previously created OpenGL context */
230 void glo_context_destroy(GloContext *context) {
231 if (!context) return;
233 // TODO: check for GloSurfaces using this?
234 glXDestroyContext( glo.dpy, context->context);
238 static void glo_surface_free_xshm_image(GloSurface *surface) {
239 XShmDetach(glo.dpy, &surface->shminfo);
240 surface->image->data = NULL;
241 XDestroyImage(surface->image);
242 shmdt(surface->shminfo.shmaddr);
243 shmctl(surface->shminfo.shmid, IPC_RMID, NULL);
246 //FIXMEIM - handle failure to allocate.
247 static void glo_surface_try_alloc_xshm_image(GloSurface *surface) {
250 glo_surface_free_xshm_image(surface);
253 XShmCreateImage(glo.dpy, DefaultVisual(glo.dpy, 0), 24, ZPixmap, NULL,
254 &surface->shminfo, surface->width, surface->height);
256 surface->shminfo.shmid = shmget(IPC_PRIVATE,
257 surface->image->bytes_per_line *
260 surface->shminfo.shmaddr = shmat(surface->shminfo.shmid, NULL, 0);
261 surface->image->data = surface->shminfo.shmaddr;
262 surface->shminfo.readOnly = False;
263 XShmAttach(glo.dpy, &surface->shminfo);
266 /* ------------------------------------------------------------------------ */
268 /* Update the context in surface and handle previous context */
269 void glo_surface_update_context(GloSurface *surface, GloContext *context, int free_flags)
271 /* If previous context is light-weight context, just free it. If previous
272 * context is valid one binded with surface via MakeCurrent, we need unbind
273 * from original glstate */
275 if ( surface->context )
277 if (free_flags) /* light-weight context */
278 g_free(surface->context);
280 surface->context = context;
283 /* Create a surface with given width and height, formatflags are from the
285 GloSurface *glo_surface_create(int width, int height, GloContext *context) {
287 XSetWindowAttributes attr = { 0 };
294 surface = (GloSurface*)g_malloc(sizeof(GloSurface));
295 memset(surface, 0, sizeof(GloSurface));
296 surface->width = width;
297 surface->height = height;
298 surface->context = context;
300 vis = glXGetVisualFromFBConfig(glo.dpy, ((struct _GloContext*)context)->fbConfig);
302 attr.background_pixel = 0xff000000;
303 attr.border_pixel = 0;
304 attr.colormap = XCreateColormap(glo.dpy, DefaultRootWindow(glo.dpy),
305 vis->visual, AllocNone);
307 attr.save_under = True;
308 attr.override_redirect = True;
310 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask |
311 CWOverrideRedirect | CWSaveUnder;
313 surface->window = XCreateWindow(glo.dpy, DefaultRootWindow(glo.dpy), 0, 3000, width, height, 0, vis->depth, InputOutput, vis->visual, mask, &attr);
315 if (!surface->window) {
316 printf( "XCreateWindow failed\n" );
317 //exit( EXIT_FAILURE );
321 XMapWindow(glo.dpy, surface->window);
322 XCompositeRedirectWindow (glo.dpy, surface->window, CompositeRedirectAutomatic);
325 surface->pixmap = XCompositeNameWindowPixmap(glo.dpy, surface->window);
327 surface->pixmap = XCreatePixmap( glo.dpy, DefaultRootWindow(glo.dpy),
329 glo_flags_get_bytes_per_pixel(context->formatFlags)*8);
332 if(surface->pixmap == 0) {
333 fprintf(stderr, "Failed to allocate pixmap!\n");
334 //exit(EXIT_FAILURE);
338 /* Create a GLX pixmap to associate the frame buffer configuration with the
339 * created X window */
340 /*XXX: need attribute_list? */
341 surface->glxPixmap = glXCreatePixmap( glo.dpy, context->fbConfig, surface->pixmap, NULL );
342 if (!surface->glxPixmap) {
343 printf( "glXCreatePixmap failed\n" );
344 //exit( EXIT_FAILURE );
350 /* set hints and properties */
352 XSizeHints sizehints;
356 sizehints.width = width;
357 sizehints.height = height;
358 sizehints.flags = USSize | USPosition;
359 XSetWMNormalHints(glo.dpy, surface->window, &sizehints);
360 XSetStandardProperties(glo.dpy, surface->window, "", "", None,
361 (char **) NULL, 0, &sizehints);
366 // If we're using XImages to pull the data from the graphics card...
367 glo_surface_try_alloc_xshm_image(surface);
372 /* Destroy the given surface */
373 void glo_surface_destroy(GloSurface *surface) {
376 XFreePixmap( glo.dpy, surface->pixmap);
378 if(surface->glxPixmap)
379 glXDestroyPixmap( glo.dpy, surface->glxPixmap);
381 XDestroyWindow( glo.dpy, surface->window);
383 glo_surface_free_xshm_image(surface);
388 /* Make the given surface current */
389 int glo_surface_makecurrent(GloSurface *surface) {
396 ret = glXMakeCurrent(glo.dpy, surface->window,
397 surface->context->context);
399 ret = glXMakeCurrent(glo.dpy, 0, NULL);
404 void glo_surface_updatecontents(GloSurface *surface) {
412 XShmGetImage (glo.dpy, surface->pixmap, surface->image, 0, 0, AllPlanes);
415 XGetImage(glo.dpy, surface->pixmap, 0, 0, surface->width, surface->height, AllPlanes, ZPixmap);
421 /* Get the contents of the given surface */
422 void glo_surface_getcontents(GloSurface *surface, int stride, int bpp, void *data) {
433 XShmGetImage (glo.dpy, surface->pixmap, surface->image, 0, 0, AllPlanes);
434 img = surface->image;
437 img = XGetImage(glo.dpy, surface->pixmap, 0, 0, surface->width, surface->height, AllPlanes, ZPixmap);
441 if(bpp != 32 && bpp != 24 && !once) {
442 fprintf(stderr, "Warning: unsupported colourdepth\n");
446 if(bpp == img->bits_per_pixel && stride == img->bytes_per_line)
448 memcpy(data, img->data, stride * surface->height);
453 for(y = 0 ; y < surface->height ; y++) {
454 for(x = 0 ; x < surface->width ; x++) {
455 char *src = ((char*)img->data) +
456 (x*(img->bits_per_pixel/8)) +
457 (y*img->bytes_per_line);
458 char *dst = ((char*)data) + x*(bpp/8) + (y*stride);
463 dst[3] = 0xff; // if guest is 32 bit and host is 24
468 // If we're not using Shm
472 return; // We're done.
474 // Uh oh... better fallback. Perhaps get glo.use_ximage to 0?
477 // Compatible / fallback method.
478 glo_surface_getcontents_readpixels(surface->context->formatFlags,
479 stride, bpp, surface->width,
480 surface->height, data, 0);
483 /* Return the width and height of the given surface */
484 void glo_surface_get_size(GloSurface *surface, int *width, int *height) {
486 *width = surface->width;
488 *height = surface->height;
491 /* Bind the surface as texture */
492 void glo_surface_as_texture(GloContext *ctxt, GloSurface *surface, int surface_type)
495 void (*ptr_func_glXBindTexImageEXT) (Display *dpy, GLXDrawable draw, int buffer, int *attrib_list);
496 ptr_func_glXBindTexImageEXT =
497 (void(*)(Display*, GLXDrawable, int, int*))glo_getprocaddress((const char*)"glXBindTexImageEXT");
499 /*XXX: When to call the glXReleaseTexImageEXT?*/
500 if (!ptr_func_glXBindTexImageEXT)
502 fprintf (stderr, "glXBindTexImageEXT not supported! Can't emulate glEGLImageTargetTexture2DOES!\n");
505 fprintf(stderr, "surface_as_texture:error=%d.\n", glGetError());
506 ptr_func_glXBindTexImageEXT(glo.dpy, surface->glxPixmap, GLX_FRONT_LEFT_EXT, NULL);
507 fprintf(stderr, "surface_as_texture:2:error=%d.\n", glGetError());
509 int glFormat, glType;
510 glo_surface_updatecontents(surface);
511 /*XXX: changet the fixed target: GL_TEXTURE_2D*/
512 glo_flags_get_readpixel_type(surface->context->formatFlags, &glFormat, &glType);
513 /* fprintf(stderr, "surface_as_texture:teximage:width=%d,height=%d, glFormat=0x%x, glType=0x%x.\n", surface->width, surface->height, glFormat, glType);*/
514 /* glTexImage2D use different RGB order than the contexts in the pixmap surface */
515 /* glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surface->width, surface->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, surface->image->data);*/
517 if(surface_type == SURFACE_PBUFFER)
519 if ((glFormat == GL_RGBA || glFormat == GL_BGRA) && glType == GL_UNSIGNED_BYTE) {
520 GLubyte *b = (GLubyte *)surface->image->data;
521 int stride = surface->width * 4;
522 GLubyte *c = &((GLubyte *)surface->image->data)[stride*(surface->height-1)];
523 GLubyte *tmp = (GLubyte*)g_malloc(stride);
526 for(irow = 0; irow < surface->height / 2; irow++) {
527 memcpy(tmp, b, stride);
528 memcpy(b, c, stride);
529 memcpy(c, tmp, stride);
537 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surface->width, surface->height, 0, glFormat, glType, surface->image->data);
541 void glo_surface_release_texture(GloSurface *surface)
545 /* Abstract glXQueryExtensionString() */
546 const char *glo_glXQueryExtensionsString(void) {
547 return glXQueryExtensionsString(glo.dpy, 0);
554 static int glo_can_readback(void) {
558 unsigned char *datain = (unsigned char *)g_malloc(4*TX*TY);
559 unsigned char *datain_flip = (unsigned char *)g_malloc(4*TX*TY); // flipped input data (for GL)
560 unsigned char *dataout;
564 const int bufferAttributes[] = {
574 int bufferFlags = glo_flags_get_from_glx(bufferAttributes, 0);
575 int bpp = glo_flags_get_bytes_per_pixel(bufferFlags);
576 int glFormat, glType;
578 memset(datain_flip, 0, TX*TY*4);
579 memset(datain, 0, TX*TY*4);
587 if (bpp>3) p[3] = 0xFF;
590 memcpy(&datain_flip[((TY-1)-y)*bpp*TX], &datain[y*bpp*TX], bpp*TX);
593 context = glo_context_create(bufferFlags, 0);
594 if (context == NULL) {
599 surface = glo_surface_create(TX, TY, context);
600 if (surface == NULL) {
606 glo_surface_makecurrent(surface);
608 glClear(GL_COLOR_BUFFER_BIT);
609 glMatrixMode(GL_PROJECTION);
611 glOrtho(0,TX, 0,TY, 0, 1);
612 glMatrixMode(GL_MODELVIEW);
615 glo_flags_get_readpixel_type(bufferFlags, &glFormat, &glType);
616 glDrawPixels(TX,TY,glFormat, glType, datain_flip);
619 dataout = (unsigned char *)g_malloc(4*TX*TY);
620 memset(dataout, 0, bpp*TX*TY);
622 glo_surface_getcontents(surface, TX*4, bpp*8, dataout);
624 glo_surface_destroy(surface);
625 glo_context_destroy(context);
627 if (memcmp(datain, dataout, bpp*TX*TY)==0) {
641 static void glo_test_readback_methods(void) {
643 if(!glo_can_readback())
646 //fprintf(stderr, "VM GL: Using %s readback\n", glo.use_ximage?"XImage":"glReadPixels");