7c043318a768a739df207d44a90fcf2c201fa51d
[sdk/emulator/qemu.git] / tizen / src / hw / gloffscreen_xcomposite.c
1 /*
2  *  Offscreen OpenGL abstraction layer - GLX specific
3  *
4  *  Copyright (c) 2010 Intel
5  *  Written by: 
6  *    Gordon Williams <gordon.williams@collabora.co.uk>
7  *    Ian Molton <ian.molton@collabora.co.uk>
8  *
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:
15  *
16  * The above copyright notice and this permission notice shall be included in
17  * all copies or substantial portions of the Software.
18  *
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
25  * THE SOFTWARE.
26  */
27 #if (!defined _WIN32) && (!defined __APPLE__)
28 #include "gloffscreen.h"
29
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33
34 #include <X11/Xlib.h>
35 #include <X11/Xutil.h>
36 #include <GL/gl.h>
37 #include <GL/glx.h>
38
39 #include <sys/shm.h>
40 #include <X11/extensions/XShm.h>
41 #include <X11/extensions/Xcomposite.h>
42
43 #ifdef MANGLE_OPENGL_SYMBOLS
44 #include "gl_mangled.h"
45 #endif
46
47 void *g_malloc(size_t size);
48 void *g_realloc(void *ptr, size_t size);
49 void g_free(void *ptr);
50
51 struct GloMain {
52     Display *dpy;
53     int use_ximage;
54 };
55 struct GloMain glo;
56 int glo_inited = 0;
57
58 struct _GloContext {
59     GLuint                formatFlags;
60
61     GLXFBConfig           fbConfig;
62     GLXContext            context;
63 };
64
65 struct _GloSurface {
66     GLuint                width;
67     GLuint                height;
68
69     GloContext           *context;
70     Window                window;
71
72     // For use by the 'fast' copy code.
73     Pixmap              pixmap;
74     XImage               *image;
75     XShmSegmentInfo       shminfo;
76         GLXPixmap                       glxPixmap;
77 };
78
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);
82
83 /* ------------------------------------------------------------------------ */
84
85 int glo_initialised(void) {
86     return glo_inited;
87 }
88
89 /* 
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.
95  *
96  */
97 static int x_errhandler(Display *dpy, XErrorEvent *e)
98 {
99     //fprintf (stderr, "X Error Happened!\n");
100     return 0;
101 }
102
103 /* Sanity test of the host GL capabilities to see whether the gl offscreen
104  * could be well supported
105  */
106 int glo_sanity_test (void) {
107     return 0;
108 }
109
110 /* Initialise gloffscreen */
111 int glo_init(void) {
112     if (glo_inited) {
113         printf( "gloffscreen already inited\n" );
114         //exit( EXIT_FAILURE );
115                 return 1;
116     }
117
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 );
123                 return 1;
124     }
125
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();
131         return 0;
132 }
133
134 /* Uninitialise gloffscreen */
135 void glo_kill(void) {
136     XCloseDisplay(glo.dpy);
137     glo.dpy = NULL;
138 }
139
140
141 /* Like wglGetProcAddress/glxGetProcAddress */
142 void *glo_getprocaddress(const char *procName) {
143     if (!glo_inited)
144         glo_init();
145
146     return glXGetProcAddressARB((const GLubyte *) procName);
147 }
148
149 /* ------------------------------------------------------------------------ */
150
151 /* Create a light-weight context just for creating surface */
152 GloContext *__glo_context_create(int formatFlags) {
153
154     GLXFBConfig          *fbConfigs;
155     int                   numReturned;
156     GloContext           *context;
157     int                   rgbaBits[4];
158     int                   bufferAttributes[] = {
159         GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
160         GLX_RENDER_TYPE,   GLX_RGBA_BIT,
161         GLX_RED_SIZE,      8,
162         GLX_GREEN_SIZE,    8,
163         GLX_BLUE_SIZE,     8,
164         GLX_ALPHA_SIZE,    8,
165         GLX_DEPTH_SIZE,    0,
166         GLX_STENCIL_SIZE,  0,
167         None
168     };
169
170     if (!glo_inited)
171         glo_init();
172
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);
181
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 );
187                 return NULL;
188     }
189     context = (GloContext*)g_malloc(sizeof(GloContext));
190     memset(context, 0, sizeof(GloContext));
191     context->formatFlags = formatFlags;
192     context->fbConfig = fbConfigs[0];
193
194         return context;
195 }
196
197 /* Create an OpenGL context for a certain pixel format.
198       formatflags are from the GLO_ constants */
199
200 GloContext *glo_context_create(int formatFlags, GloContext *shareLists) {
201
202         GloContext *context = __glo_context_create(formatFlags);
203
204         if (!context) {
205                 return NULL;
206         }
207
208     /* Create a GLX context for OpenGL rendering */
209     context->context = glXCreateNewContext(glo.dpy, context->fbConfig,
210                                          GLX_RGBA_TYPE,
211                                          shareLists ? shareLists->context: NULL,
212                                          True );
213
214     if (!context->context) {
215         printf( "glXCreateNewContext failed\n" );
216         //exit( EXIT_FAILURE );
217                 return NULL;
218     }
219
220     return context;
221 }
222
223 /* Destroy a previously created OpenGL context */
224 void glo_context_destroy(GloContext *context) {
225     if (!context) return;
226
227     // TODO: check for GloSurfaces using this?
228     glXDestroyContext( glo.dpy, context->context);
229     g_free(context);
230 }
231
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);
238 }
239
240 //FIXMEIM - handle failure to allocate.
241 static void glo_surface_try_alloc_xshm_image(GloSurface *surface) {
242
243     if(surface->image)
244         glo_surface_free_xshm_image(surface);
245
246     surface->image =
247         XShmCreateImage(glo.dpy, DefaultVisual(glo.dpy, 0), 24, ZPixmap, NULL,
248                         &surface->shminfo, surface->width, surface->height);
249
250     surface->shminfo.shmid = shmget(IPC_PRIVATE,
251                                     surface->image->bytes_per_line *
252                                     surface->height,
253                                     IPC_CREAT | 0777);
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);
258 }
259
260 /* ------------------------------------------------------------------------ */
261
262 /* Update the context in surface and handle previous context */
263 void glo_surface_update_context(GloSurface *surface, GloContext *context, int free_flags)
264  {
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 */
268
269      if ( surface->context )
270     {
271                 if (free_flags) /* light-weight context */
272             g_free(surface->context);
273     }
274     surface->context = context;
275 }
276
277 /* Create a surface with given width and height, formatflags are from the
278  * GLO_ constants */
279 GloSurface *glo_surface_create(int width, int height, GloContext *context) {
280     GloSurface           *surface;
281     XSetWindowAttributes attr = { 0 };
282     unsigned long mask;
283     XVisualInfo *vis;
284
285     if (!context)
286       return 0;
287
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;
293
294     vis = glXGetVisualFromFBConfig(glo.dpy, ((struct _GloContext*)context)->fbConfig);
295
296     attr.background_pixel = 0xff000000;
297     attr.border_pixel = 0;
298     attr.colormap = XCreateColormap(glo.dpy, DefaultRootWindow(glo.dpy),
299                                     vis->visual, AllocNone);
300     attr.event_mask = 0;
301     attr.save_under = True;
302     attr.override_redirect = True;
303     attr.cursor = None;
304     mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask |
305            CWOverrideRedirect | CWSaveUnder;
306
307     surface->window = XCreateWindow(glo.dpy, DefaultRootWindow(glo.dpy), 0, 3000, width, height, 0, vis->depth, InputOutput, vis->visual, mask, &attr);
308
309     if (!surface->window) {
310         printf( "XCreateWindow failed\n" );
311         //exit( EXIT_FAILURE );
312                 return NULL;
313     }
314
315     XMapWindow(glo.dpy, surface->window);
316     XCompositeRedirectWindow (glo.dpy, surface->window, CompositeRedirectAutomatic);
317
318     if(glo.use_ximage) {
319         surface->pixmap = XCompositeNameWindowPixmap(glo.dpy, surface->window);
320     } else {
321         surface->pixmap = XCreatePixmap( glo.dpy, DefaultRootWindow(glo.dpy),
322                                          width, height,
323                                          glo_flags_get_bytes_per_pixel(context->formatFlags)*8);
324     }
325
326     if(surface->pixmap == 0) {
327         fprintf(stderr, "Failed to allocate pixmap!\n");
328         //exit(EXIT_FAILURE);
329                 return NULL;
330     }
331
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 );
339      return NULL;
340     }
341
342     XSync(glo.dpy, 0);
343
344     /* set hints and properties */
345     {
346         XSizeHints sizehints;
347
348         sizehints.x = 0;
349         sizehints.y = 0;
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);
356     }
357
358     XSync(glo.dpy, 0);
359
360     // If we're using XImages to pull the data from the graphics card...
361     glo_surface_try_alloc_xshm_image(surface);
362
363     return surface;
364 }
365
366 /* Destroy the given surface */
367 void glo_surface_destroy(GloSurface *surface) {
368
369     if(surface->pixmap)
370         XFreePixmap( glo.dpy, surface->pixmap);
371
372     if(surface->glxPixmap)
373         glXDestroyPixmap( glo.dpy, surface->glxPixmap);
374
375     XDestroyWindow( glo.dpy, surface->window);
376     if(surface->image)
377         glo_surface_free_xshm_image(surface);
378     g_free(surface);
379
380 }
381
382 /* Make the given surface current */
383 int glo_surface_makecurrent(GloSurface *surface) {
384     int ret;
385
386     if (!glo_inited)
387         glo_init();
388
389     if (surface)
390         ret = glXMakeCurrent(glo.dpy, surface->window,
391                              surface->context->context);
392     else
393         ret = glXMakeCurrent(glo.dpy, 0, NULL);
394
395     return ret;
396 }
397
398 void glo_surface_updatecontents(GloSurface *surface) {
399     if (!surface)
400         return;
401
402     if(glo.use_ximage) {
403         glXWaitGL();
404
405         if(surface->image) {
406             XShmGetImage (glo.dpy, surface->pixmap, surface->image, 0, 0, AllPlanes);
407         }
408         else {
409             XGetImage(glo.dpy, surface->pixmap, 0, 0, surface->width, surface->height, AllPlanes, ZPixmap);
410         }
411     }
412
413 }
414
415 /* Get the contents of the given surface */
416 void glo_surface_getcontents(GloSurface *surface, int stride, int bpp, void *data) {
417     static int once;
418     XImage *img;
419
420     if (!surface)
421         return;
422
423     if(glo.use_ximage) {
424         glXWaitGL();
425     
426         if(surface->image) {
427             XShmGetImage (glo.dpy, surface->pixmap, surface->image, 0, 0, AllPlanes);
428             img = surface->image;
429         }
430         else {
431             img = XGetImage(glo.dpy, surface->pixmap, 0, 0, surface->width, surface->height, AllPlanes, ZPixmap);
432         }
433
434         if (img) {
435             if(bpp != 32 && bpp != 24 && !once) {
436                 fprintf(stderr, "Warning: unsupported colourdepth\n");
437                 once = 1;
438             }
439
440             if(bpp == img->bits_per_pixel && stride == img->bytes_per_line)
441             {
442                  memcpy(data, img->data, stride * surface->height);
443             }
444             else
445             {
446                 int x, y;
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);
453                         dst[0] = src[0];
454                         dst[1] = src[1];
455                         dst[2] = src[2];
456                         if(bpp == 32)
457                               dst[3] = 0xff; // if guest is 32 bit and host is 24
458                     }
459                 }
460             }
461   
462             // If we're not using Shm
463             if(!surface->image)
464                 XDestroyImage(img);
465
466             return;  // We're done.
467         } 
468     // Uh oh... better fallback. Perhaps get glo.use_ximage to 0?
469     }
470
471     // Compatible / fallback method.
472     glo_surface_getcontents_readpixels(surface->context->formatFlags,
473                                        stride, bpp, surface->width,
474                                        surface->height, data);
475 }
476
477 /* Return the width and height of the given surface */
478 void glo_surface_get_size(GloSurface *surface, int *width, int *height) {
479     if (width)
480         *width = surface->width;
481     if (height)
482         *height = surface->height;
483 }
484
485 /* Bind the surface as texture */
486 void glo_surface_as_texture(GloContext *ctxt, GloSurface *surface)
487 {
488 #if 0
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");
492
493     /*XXX: When to call the glXReleaseTexImageEXT?*/
494     if (!ptr_func_glXBindTexImageEXT)
495     {
496         fprintf (stderr, "glXBindTexImageEXT not supported! Can't emulate glEGLImageTargetTexture2DOES!\n");
497     }
498
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());
502 #else
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);*/
510
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);
516         int irow;
517
518         for(irow = 0; irow < surface->height / 2; irow++) {
519             memcpy(tmp, b, stride);
520             memcpy(b, c, stride);
521             memcpy(c, tmp, stride);
522             b += stride;
523             c -= stride;
524         }
525         g_free(tmp);
526     }
527     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surface->width, surface->height, 0, glFormat, glType, surface->image->data);
528 #endif
529 }
530
531 void glo_surface_release_texture(GloSurface *surface)
532 {
533 }
534
535  /* Abstract glXQueryExtensionString() */
536 const char *glo_glXQueryExtensionsString(void) {
537     return glXQueryExtensionsString(glo.dpy, 0);
538 }
539
540
541 #define TX (17)
542 #define TY (16)
543
544 static int glo_can_readback(void) {
545     GloContext *context;
546     GloSurface *surface;
547
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;
551     unsigned char *p;
552     int x,y;
553
554     const int bufferAttributes[] = {
555             GLX_RED_SIZE,      8,
556             GLX_GREEN_SIZE,    8,
557             GLX_BLUE_SIZE,     8,
558             GLX_ALPHA_SIZE,    8,
559             GLX_DEPTH_SIZE,    0,
560             GLX_STENCIL_SIZE,  0,
561             0,
562         };
563
564     int bufferFlags = glo_flags_get_from_glx(bufferAttributes, 0);
565     int bpp = glo_flags_get_bytes_per_pixel(bufferFlags);
566     int glFormat, glType;
567
568     memset(datain_flip, 0, TX*TY*4);
569     memset(datain, 0, TX*TY*4);
570
571     p = datain;
572     for (y=0;y<TY;y++) {
573         for (x=0;x<TX;x++) {
574             p[0] = x;
575             p[1] = y;
576             if (bpp>2) p[2] = 0;
577             if (bpp>3) p[3] = 0xFF;
578             p+=bpp;
579         }
580         memcpy(&datain_flip[((TY-1)-y)*bpp*TX], &datain[y*bpp*TX], bpp*TX);
581     }
582
583     context = glo_context_create(bufferFlags, 0);
584         if (context == NULL) {
585                 g_free(datain);
586                 g_free(datain_flip);
587                 return 1;
588         }
589     surface = glo_surface_create(TX, TY, context);
590         if (surface == NULL) {
591                 g_free(datain);
592                 g_free(datain_flip);
593                 return 1;
594         }
595
596     glo_surface_makecurrent(surface);
597
598     glClear(GL_COLOR_BUFFER_BIT);
599     glMatrixMode(GL_PROJECTION);
600     glLoadIdentity();
601     glOrtho(0,TX, 0,TY, 0, 1);
602     glMatrixMode(GL_MODELVIEW);
603     glLoadIdentity();
604     glRasterPos2f(0,0);
605     glo_flags_get_readpixel_type(bufferFlags, &glFormat, &glType);
606     glDrawPixels(TX,TY,glFormat, glType, datain_flip);
607     glFlush();
608
609         dataout = (unsigned char *)g_malloc(4*TX*TY);
610     memset(dataout, 0, bpp*TX*TY);
611
612     glo_surface_getcontents(surface, TX*4, bpp*8, dataout);
613
614     glo_surface_destroy(surface);
615     glo_context_destroy(context);
616
617     if (memcmp(datain, dataout, bpp*TX*TY)==0) {
618                 g_free(datain);
619                 g_free(datain_flip);
620                 g_free(dataout);
621         return 1;
622         }
623
624         g_free(datain);
625         g_free(datain_flip);
626         g_free(dataout);
627
628     return 0;
629 }
630
631 static void glo_test_readback_methods(void) {
632     glo.use_ximage = 1;
633     if(!glo_can_readback())
634         glo.use_ximage = 0;
635
636     //fprintf(stderr, "VM GL: Using %s readback\n", glo.use_ximage?"XImage":"glReadPixels");
637 }
638
639 #endif