sync with tizen_2.2
[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 enum{
48     SURFACE_WINDOW,
49     SURFACE_PIXMAP,
50     SURFACE_PBUFFER,
51 };
52
53 void *g_malloc(size_t size);
54 void *g_realloc(void *ptr, size_t size);
55 void g_free(void *ptr);
56
57 struct GloMain {
58     Display *dpy;
59     int use_ximage;
60 };
61 struct GloMain glo;
62 int glo_inited = 0;
63
64 struct _GloContext {
65     GLuint                formatFlags;
66
67     GLXFBConfig           fbConfig;
68     GLXContext            context;
69 };
70
71 struct _GloSurface {
72     GLuint                width;
73     GLuint                height;
74
75     GloContext           *context;
76     Window                window;
77
78     // For use by the 'fast' copy code.
79     Pixmap              pixmap;
80     XImage               *image;
81     XShmSegmentInfo       shminfo;
82         GLXPixmap                       glxPixmap;
83 };
84
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);
88
89 /* ------------------------------------------------------------------------ */
90
91 int glo_initialised(void) {
92     return glo_inited;
93 }
94
95 /* 
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.
101  *
102  */
103 static int x_errhandler(Display *dpy, XErrorEvent *e)
104 {
105     //fprintf (stderr, "X Error Happened!\n");
106     return 0;
107 }
108
109 /* Sanity test of the host GL capabilities to see whether the gl offscreen
110  * could be well supported
111  */
112 int glo_sanity_test (void) {
113     return 0;
114 }
115
116 /* Initialise gloffscreen */
117 int glo_init(void) {
118     if (glo_inited) {
119         printf( "gloffscreen already inited\n" );
120         //exit( EXIT_FAILURE );
121                 return 1;
122     }
123
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 );
129                 return 1;
130     }
131
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();
137         return 0;
138 }
139
140 /* Uninitialise gloffscreen */
141 void glo_kill(void) {
142     XCloseDisplay(glo.dpy);
143     glo.dpy = NULL;
144 }
145
146
147 /* Like wglGetProcAddress/glxGetProcAddress */
148 void *glo_getprocaddress(const char *procName) {
149     if (!glo_inited)
150         glo_init();
151
152     return glXGetProcAddressARB((const GLubyte *) procName);
153 }
154
155 /* ------------------------------------------------------------------------ */
156
157 /* Create a light-weight context just for creating surface */
158 GloContext *__glo_context_create(int formatFlags) {
159
160     GLXFBConfig          *fbConfigs;
161     int                   numReturned;
162     GloContext           *context;
163     int                   rgbaBits[4];
164     int                   bufferAttributes[] = {
165         GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
166         GLX_RENDER_TYPE,   GLX_RGBA_BIT,
167         GLX_RED_SIZE,      8,
168         GLX_GREEN_SIZE,    8,
169         GLX_BLUE_SIZE,     8,
170         GLX_ALPHA_SIZE,    8,
171         GLX_DEPTH_SIZE,    0,
172         GLX_STENCIL_SIZE,  0,
173         None
174     };
175
176     if (!glo_inited)
177         glo_init();
178
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);
187
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 );
193                 return NULL;
194     }
195     context = (GloContext*)g_malloc(sizeof(GloContext));
196     memset(context, 0, sizeof(GloContext));
197     context->formatFlags = formatFlags;
198     context->fbConfig = fbConfigs[0];
199
200         return context;
201 }
202
203 /* Create an OpenGL context for a certain pixel format.
204       formatflags are from the GLO_ constants */
205
206 GloContext *glo_context_create(int formatFlags, GloContext *shareLists) {
207
208         GloContext *context = __glo_context_create(formatFlags);
209
210         if (!context) {
211                 return NULL;
212         }
213
214     /* Create a GLX context for OpenGL rendering */
215     context->context = glXCreateNewContext(glo.dpy, context->fbConfig,
216                                          GLX_RGBA_TYPE,
217                                          shareLists ? shareLists->context: NULL,
218                                          True );
219
220     if (!context->context) {
221         printf( "glXCreateNewContext failed\n" );
222         //exit( EXIT_FAILURE );
223                 return NULL;
224     }
225
226     return context;
227 }
228
229 /* Destroy a previously created OpenGL context */
230 void glo_context_destroy(GloContext *context) {
231     if (!context) return;
232
233     // TODO: check for GloSurfaces using this?
234     glXDestroyContext( glo.dpy, context->context);
235     g_free(context);
236 }
237
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);
244 }
245
246 //FIXMEIM - handle failure to allocate.
247 static void glo_surface_try_alloc_xshm_image(GloSurface *surface) {
248
249     if(surface->image)
250         glo_surface_free_xshm_image(surface);
251
252     surface->image =
253         XShmCreateImage(glo.dpy, DefaultVisual(glo.dpy, 0), 24, ZPixmap, NULL,
254                         &surface->shminfo, surface->width, surface->height);
255
256     surface->shminfo.shmid = shmget(IPC_PRIVATE,
257                                     surface->image->bytes_per_line *
258                                     surface->height,
259                                     IPC_CREAT | 0777);
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);
264 }
265
266 /* ------------------------------------------------------------------------ */
267
268 /* Update the context in surface and handle previous context */
269 void glo_surface_update_context(GloSurface *surface, GloContext *context, int free_flags)
270  {
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 */
274
275      if ( surface->context )
276     {
277                 if (free_flags) /* light-weight context */
278             g_free(surface->context);
279     }
280     surface->context = context;
281 }
282
283 /* Create a surface with given width and height, formatflags are from the
284  * GLO_ constants */
285 GloSurface *glo_surface_create(int width, int height, GloContext *context) {
286     GloSurface           *surface;
287     XSetWindowAttributes attr = { 0 };
288     unsigned long mask;
289     XVisualInfo *vis;
290
291     if (!context)
292       return 0;
293
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;
299
300     vis = glXGetVisualFromFBConfig(glo.dpy, ((struct _GloContext*)context)->fbConfig);
301
302     attr.background_pixel = 0xff000000;
303     attr.border_pixel = 0;
304     attr.colormap = XCreateColormap(glo.dpy, DefaultRootWindow(glo.dpy),
305                                     vis->visual, AllocNone);
306     attr.event_mask = 0;
307     attr.save_under = True;
308     attr.override_redirect = True;
309     attr.cursor = None;
310     mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask |
311            CWOverrideRedirect | CWSaveUnder;
312
313     surface->window = XCreateWindow(glo.dpy, DefaultRootWindow(glo.dpy), 0, 3000, width, height, 0, vis->depth, InputOutput, vis->visual, mask, &attr);
314
315     if (!surface->window) {
316         printf( "XCreateWindow failed\n" );
317         //exit( EXIT_FAILURE );
318                 return NULL;
319     }
320
321     XMapWindow(glo.dpy, surface->window);
322     XCompositeRedirectWindow (glo.dpy, surface->window, CompositeRedirectAutomatic);
323
324     if(glo.use_ximage) {
325         surface->pixmap = XCompositeNameWindowPixmap(glo.dpy, surface->window);
326     } else {
327         surface->pixmap = XCreatePixmap( glo.dpy, DefaultRootWindow(glo.dpy),
328                                          width, height,
329                                          glo_flags_get_bytes_per_pixel(context->formatFlags)*8);
330     }
331
332     if(surface->pixmap == 0) {
333         fprintf(stderr, "Failed to allocate pixmap!\n");
334         //exit(EXIT_FAILURE);
335                 return NULL;
336     }
337
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 );
345      return NULL;
346     }
347
348     XSync(glo.dpy, 0);
349
350     /* set hints and properties */
351     {
352         XSizeHints sizehints;
353
354         sizehints.x = 0;
355         sizehints.y = 0;
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);
362     }
363
364     XSync(glo.dpy, 0);
365
366     // If we're using XImages to pull the data from the graphics card...
367     glo_surface_try_alloc_xshm_image(surface);
368
369     return surface;
370 }
371
372 /* Destroy the given surface */
373 void glo_surface_destroy(GloSurface *surface) {
374
375     if(surface->pixmap)
376         XFreePixmap( glo.dpy, surface->pixmap);
377
378     if(surface->glxPixmap)
379         glXDestroyPixmap( glo.dpy, surface->glxPixmap);
380
381     XDestroyWindow( glo.dpy, surface->window);
382     if(surface->image)
383         glo_surface_free_xshm_image(surface);
384     g_free(surface);
385
386 }
387
388 /* Make the given surface current */
389 int glo_surface_makecurrent(GloSurface *surface) {
390     int ret;
391
392     if (!glo_inited)
393         glo_init();
394
395     if (surface)
396         ret = glXMakeCurrent(glo.dpy, surface->window,
397                              surface->context->context);
398     else
399         ret = glXMakeCurrent(glo.dpy, 0, NULL);
400
401     return ret;
402 }
403
404 void glo_surface_updatecontents(GloSurface *surface) {
405     if (!surface)
406         return;
407
408     if(glo.use_ximage) {
409         glXWaitGL();
410
411         if(surface->image) {
412             XShmGetImage (glo.dpy, surface->pixmap, surface->image, 0, 0, AllPlanes);
413         }
414         else {
415             XGetImage(glo.dpy, surface->pixmap, 0, 0, surface->width, surface->height, AllPlanes, ZPixmap);
416         }
417     }
418
419 }
420
421 /* Get the contents of the given surface */
422 void glo_surface_getcontents(GloSurface *surface, int stride, int bpp, void *data) {
423     static int once;
424     XImage *img;
425
426     if (!surface)
427         return;
428
429     if(glo.use_ximage) {
430         glXWaitGL();
431     
432         if(surface->image) {
433             XShmGetImage (glo.dpy, surface->pixmap, surface->image, 0, 0, AllPlanes);
434             img = surface->image;
435         }
436         else {
437             img = XGetImage(glo.dpy, surface->pixmap, 0, 0, surface->width, surface->height, AllPlanes, ZPixmap);
438         }
439
440         if (img) {
441             if(bpp != 32 && bpp != 24 && !once) {
442                 fprintf(stderr, "Warning: unsupported colourdepth\n");
443                 once = 1;
444             }
445
446             if(bpp == img->bits_per_pixel && stride == img->bytes_per_line)
447             {
448                  memcpy(data, img->data, stride * surface->height);
449             }
450             else
451             {
452                 int x, y;
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);
459                         dst[0] = src[0];
460                         dst[1] = src[1];
461                         dst[2] = src[2];
462                         if(bpp == 32)
463                               dst[3] = 0xff; // if guest is 32 bit and host is 24
464                     }
465                 }
466             }
467   
468             // If we're not using Shm
469             if(!surface->image)
470                 XDestroyImage(img);
471
472             return;  // We're done.
473         } 
474     // Uh oh... better fallback. Perhaps get glo.use_ximage to 0?
475     }
476
477     // Compatible / fallback method.
478     glo_surface_getcontents_readpixels(surface->context->formatFlags,
479                                        stride, bpp, surface->width,
480                                        surface->height, data, 0);
481 }
482
483 /* Return the width and height of the given surface */
484 void glo_surface_get_size(GloSurface *surface, int *width, int *height) {
485     if (width)
486         *width = surface->width;
487     if (height)
488         *height = surface->height;
489 }
490
491 /* Bind the surface as texture */
492 void glo_surface_as_texture(GloContext *ctxt, GloSurface *surface, int surface_type)
493 {
494 #if 0
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");
498
499     /*XXX: When to call the glXReleaseTexImageEXT?*/
500     if (!ptr_func_glXBindTexImageEXT)
501     {
502         fprintf (stderr, "glXBindTexImageEXT not supported! Can't emulate glEGLImageTargetTexture2DOES!\n");
503     }
504
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());
508 #else
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);*/
516
517    if(surface_type == SURFACE_PBUFFER)
518    {
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);
524         int irow;
525
526         for(irow = 0; irow < surface->height / 2; irow++) {
527             memcpy(tmp, b, stride);
528             memcpy(b, c, stride);
529             memcpy(c, tmp, stride);
530             b += stride;
531             c -= stride;
532         }
533         g_free(tmp);
534     }
535    }
536
537     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surface->width, surface->height, 0, glFormat, glType, surface->image->data);
538 #endif
539 }
540
541 void glo_surface_release_texture(GloSurface *surface)
542 {
543 }
544
545  /* Abstract glXQueryExtensionString() */
546 const char *glo_glXQueryExtensionsString(void) {
547     return glXQueryExtensionsString(glo.dpy, 0);
548 }
549
550
551 #define TX (17)
552 #define TY (16)
553
554 static int glo_can_readback(void) {
555     GloContext *context;
556     GloSurface *surface;
557
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;
561     unsigned char *p;
562     int x,y;
563
564     const int bufferAttributes[] = {
565             GLX_RED_SIZE,      8,
566             GLX_GREEN_SIZE,    8,
567             GLX_BLUE_SIZE,     8,
568             GLX_ALPHA_SIZE,    8,
569             GLX_DEPTH_SIZE,    0,
570             GLX_STENCIL_SIZE,  0,
571             0,
572         };
573
574     int bufferFlags = glo_flags_get_from_glx(bufferAttributes, 0);
575     int bpp = glo_flags_get_bytes_per_pixel(bufferFlags);
576     int glFormat, glType;
577
578     memset(datain_flip, 0, TX*TY*4);
579     memset(datain, 0, TX*TY*4);
580
581     p = datain;
582     for (y=0;y<TY;y++) {
583         for (x=0;x<TX;x++) {
584             p[0] = x;
585             p[1] = y;
586             if (bpp>2) p[2] = 0;
587             if (bpp>3) p[3] = 0xFF;
588             p+=bpp;
589         }
590         memcpy(&datain_flip[((TY-1)-y)*bpp*TX], &datain[y*bpp*TX], bpp*TX);
591     }
592
593     context = glo_context_create(bufferFlags, 0);
594         if (context == NULL) {
595                 g_free(datain);
596                 g_free(datain_flip);
597                 return 1;
598         }
599     surface = glo_surface_create(TX, TY, context);
600         if (surface == NULL) {
601                 g_free(datain);
602                 g_free(datain_flip);
603                 return 1;
604         }
605
606     glo_surface_makecurrent(surface);
607
608     glClear(GL_COLOR_BUFFER_BIT);
609     glMatrixMode(GL_PROJECTION);
610     glLoadIdentity();
611     glOrtho(0,TX, 0,TY, 0, 1);
612     glMatrixMode(GL_MODELVIEW);
613     glLoadIdentity();
614     glRasterPos2f(0,0);
615     glo_flags_get_readpixel_type(bufferFlags, &glFormat, &glType);
616     glDrawPixels(TX,TY,glFormat, glType, datain_flip);
617     glFlush();
618
619         dataout = (unsigned char *)g_malloc(4*TX*TY);
620     memset(dataout, 0, bpp*TX*TY);
621
622     glo_surface_getcontents(surface, TX*4, bpp*8, dataout);
623
624     glo_surface_destroy(surface);
625     glo_context_destroy(context);
626
627     if (memcmp(datain, dataout, bpp*TX*TY)==0) {
628                 g_free(datain);
629                 g_free(datain_flip);
630                 g_free(dataout);
631         return 1;
632         }
633
634         g_free(datain);
635         g_free(datain_flip);
636         g_free(dataout);
637
638     return 0;
639 }
640
641 static void glo_test_readback_methods(void) {
642     glo.use_ximage = 1;
643     if(!glo_can_readback())
644         glo.use_ximage = 0;
645
646     //fprintf(stderr, "VM GL: Using %s readback\n", glo.use_ximage?"XImage":"glReadPixels");
647 }
648
649 #endif