1 /*
2  * Cogl
3  *
4  * A Low Level GPU Graphics and Utilities API
5  *
6  * Copyright (C) 2007,2008,2009,2010,2011,2013 Intel Corporation.
7  *
8  * Permission is hereby granted, free of charge, to any person
9  * obtaining a copy of this software and associated documentation
10  * files (the "Software"), to deal in the Software without
11  * restriction, including without limitation the rights to use, copy,
12  * modify, merge, publish, distribute, sublicense, and/or sell copies
13  * 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
17  * included in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
23  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
24  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26  * SOFTWARE.
27  *
28  *
29  * Authors:
30  *   Robert Bragg <robert@linux.intel.com>
31  */
32 
33 #include "cogl-config.h"
34 
35 #include "cogl-i18n-private.h"
36 #include "cogl-util.h"
37 #include "cogl-feature-private.h"
38 #include "cogl-context-private.h"
39 #include "cogl-framebuffer.h"
40 #include "cogl-swap-chain-private.h"
41 #include "cogl-renderer-private.h"
42 #include "cogl-glx-renderer-private.h"
43 #include "cogl-onscreen-template-private.h"
44 #include "cogl-glx-display-private.h"
45 #include "cogl-private.h"
46 #include "cogl-texture-2d-private.h"
47 #include "cogl-frame-info-private.h"
48 #include "cogl-framebuffer-private.h"
49 #include "cogl-onscreen-private.h"
50 #include "cogl-swap-chain-private.h"
51 #include "cogl-xlib-renderer.h"
52 #include "cogl-util.h"
53 #include "cogl-poll-private.h"
54 #include "cogl-version.h"
55 #include "cogl-glx.h"
56 #include "driver/gl/cogl-pipeline-opengl-private.h"
57 #include "winsys/cogl-onscreen-glx.h"
58 #include "winsys/cogl-winsys-private.h"
59 #include "winsys/cogl-winsys-glx-private.h"
60 
61 #include <stdlib.h>
62 #include <sys/types.h>
63 #include <sys/stat.h>
64 #include <sys/time.h>
65 #include <errno.h>
66 #include <fcntl.h>
67 #include <time.h>
68 #include <unistd.h>
69 
70 #include <GL/glx.h>
71 #include <X11/Xlib.h>
72 
73 #include <glib.h>
74 
75 /* This is a relatively new extension */
76 #ifndef GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV
77 #define GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x20F7
78 #endif
79 
80 #define MAX_GLX_CONFIG_ATTRIBS 30
81 
82 typedef struct _CoglOnscreenGlx CoglOnscreenGlx;
83 
84 typedef struct _CoglContextGLX
85 {
86   GLXDrawable current_drawable;
87 } CoglContextGLX;
88 
89 typedef struct _CoglPixmapTextureEyeGLX
90 {
91   CoglTexture *glx_tex;
92   gboolean bind_tex_image_queued;
93   gboolean pixmap_bound;
94 } CoglPixmapTextureEyeGLX;
95 
96 typedef struct _CoglTexturePixmapGLX
97 {
98   GLXPixmap glx_pixmap;
99   gboolean has_mipmap_space;
100   gboolean can_mipmap;
101 
102   CoglPixmapTextureEyeGLX left;
103   CoglPixmapTextureEyeGLX right;
104 } CoglTexturePixmapGLX;
105 
106 /* Define a set of arrays containing the functions required from GL
107    for each winsys feature */
108 #define COGL_WINSYS_FEATURE_BEGIN(major_version, minor_version,         \
109                                   name, namespaces, extension_names,    \
110                                   feature_flags,                        \
111                                   winsys_feature)                       \
112   static const CoglFeatureFunction                                      \
113   cogl_glx_feature_ ## name ## _funcs[] = {
114 #define COGL_WINSYS_FEATURE_FUNCTION(ret, name, args)                   \
115   { G_STRINGIFY (name), G_STRUCT_OFFSET (CoglGLXRenderer, name) },
116 #define COGL_WINSYS_FEATURE_END()               \
117   { NULL, 0 },                                  \
118     };
119 #include "winsys/cogl-winsys-glx-feature-functions.h"
120 
121 /* Define an array of features */
122 #undef COGL_WINSYS_FEATURE_BEGIN
123 #define COGL_WINSYS_FEATURE_BEGIN(major_version, minor_version,         \
124                                   name, namespaces, extension_names,    \
125                                   feature_flags,                        \
126                                   winsys_feature)                       \
127   { major_version, minor_version,                                       \
128       0, namespaces, extension_names,                                   \
129       0,                                                                \
130       winsys_feature, \
131       cogl_glx_feature_ ## name ## _funcs },
132 #undef COGL_WINSYS_FEATURE_FUNCTION
133 #define COGL_WINSYS_FEATURE_FUNCTION(ret, name, args)
134 #undef COGL_WINSYS_FEATURE_END
135 #define COGL_WINSYS_FEATURE_END()
136 
137 static const CoglFeatureData winsys_feature_data[] =
138   {
139 #include "winsys/cogl-winsys-glx-feature-functions.h"
140   };
141 
142 static GCallback
_cogl_winsys_renderer_get_proc_address(CoglRenderer * renderer,const char * name,gboolean in_core)143 _cogl_winsys_renderer_get_proc_address (CoglRenderer *renderer,
144                                         const char *name,
145                                         gboolean in_core)
146 {
147   CoglGLXRenderer *glx_renderer = renderer->winsys;
148 
149   /* The GLX_ARB_get_proc_address extension documents that this should
150    * work for core functions too so we don't need to do anything
151    * special with in_core */
152 
153   return glx_renderer->glXGetProcAddress ((const GLubyte *) name);
154 }
155 
156 static CoglOnscreen *
find_onscreen_for_xid(CoglContext * context,uint32_t xid)157 find_onscreen_for_xid (CoglContext *context, uint32_t xid)
158 {
159   GList *l;
160 
161   for (l = context->framebuffers; l; l = l->next)
162     {
163       CoglFramebuffer *framebuffer = l->data;
164       CoglOnscreen *onscreen;
165 
166       if (!COGL_IS_ONSCREEN (framebuffer))
167         continue;
168 
169       onscreen = COGL_ONSCREEN (framebuffer);
170       if (cogl_onscreen_glx_is_for_window (onscreen, (Window) xid))
171         return onscreen;
172     }
173 
174   return NULL;
175 }
176 
177 static void
notify_swap_buffers(CoglContext * context,GLXBufferSwapComplete * swap_event)178 notify_swap_buffers (CoglContext *context, GLXBufferSwapComplete *swap_event)
179 {
180   CoglOnscreen *onscreen = find_onscreen_for_xid (context, (uint32_t)swap_event->drawable);
181 
182   if (!onscreen)
183     return;
184 
185   cogl_onscreen_glx_notify_swap_buffers (onscreen, swap_event);
186 }
187 
188 static void
notify_resize(CoglContext * context,XConfigureEvent * configure_event)189 notify_resize (CoglContext *context,
190                XConfigureEvent *configure_event)
191 {
192   CoglOnscreen *onscreen;
193 
194   onscreen = find_onscreen_for_xid (context, configure_event->window);
195   if (!onscreen)
196     return;
197 
198   cogl_onscreen_glx_resize (onscreen, configure_event);
199 }
200 
201 static CoglFilterReturn
glx_event_filter_cb(XEvent * xevent,void * data)202 glx_event_filter_cb (XEvent *xevent, void *data)
203 {
204   CoglContext *context = data;
205 #ifdef GLX_INTEL_swap_event
206   CoglGLXRenderer *glx_renderer;
207 #endif
208 
209   if (xevent->type == ConfigureNotify)
210     {
211       notify_resize (context,
212                      &xevent->xconfigure);
213 
214       /* we let ConfigureNotify pass through */
215       return COGL_FILTER_CONTINUE;
216     }
217 
218 #ifdef GLX_INTEL_swap_event
219   glx_renderer = context->display->renderer->winsys;
220 
221   if (xevent->type == (glx_renderer->glx_event_base + GLX_BufferSwapComplete))
222     {
223       GLXBufferSwapComplete *swap_event = (GLXBufferSwapComplete *) xevent;
224 
225       notify_swap_buffers (context, swap_event);
226 
227       /* remove SwapComplete events from the queue */
228       return COGL_FILTER_REMOVE;
229     }
230 #endif /* GLX_INTEL_swap_event */
231 
232   if (xevent->type == Expose)
233     {
234       CoglOnscreen *onscreen =
235         find_onscreen_for_xid (context, xevent->xexpose.window);
236 
237       if (onscreen)
238         {
239           CoglOnscreenDirtyInfo info;
240 
241           info.x = xevent->xexpose.x;
242           info.y = xevent->xexpose.y;
243           info.width = xevent->xexpose.width;
244           info.height = xevent->xexpose.height;
245 
246           _cogl_onscreen_queue_dirty (onscreen, &info);
247         }
248 
249       return COGL_FILTER_CONTINUE;
250     }
251 
252   return COGL_FILTER_CONTINUE;
253 }
254 
255 static void
_cogl_winsys_renderer_disconnect(CoglRenderer * renderer)256 _cogl_winsys_renderer_disconnect (CoglRenderer *renderer)
257 {
258   CoglGLXRenderer *glx_renderer = renderer->winsys;
259 
260   _cogl_xlib_renderer_disconnect (renderer);
261 
262   if (glx_renderer->libgl_module)
263     g_module_close (glx_renderer->libgl_module);
264 
265   g_free (renderer->winsys);
266 }
267 
268 static gboolean
update_all_outputs(CoglRenderer * renderer)269 update_all_outputs (CoglRenderer *renderer)
270 {
271   GList *l;
272 
273   _COGL_GET_CONTEXT (context, FALSE);
274 
275   if (context->display == NULL) /* during connection */
276     return FALSE;
277 
278   if (context->display->renderer != renderer)
279     return FALSE;
280 
281   for (l = context->framebuffers; l; l = l->next)
282     {
283       CoglFramebuffer *framebuffer = l->data;
284 
285       if (!COGL_IS_ONSCREEN (framebuffer))
286         continue;
287 
288       cogl_onscreen_glx_update_output (COGL_ONSCREEN (framebuffer));
289     }
290 
291   return TRUE;
292 }
293 
294 static void
_cogl_winsys_renderer_outputs_changed(CoglRenderer * renderer)295 _cogl_winsys_renderer_outputs_changed (CoglRenderer *renderer)
296 {
297   update_all_outputs (renderer);
298 }
299 
300 static void
_cogl_winsys_renderer_bind_api(CoglRenderer * renderer)301 _cogl_winsys_renderer_bind_api (CoglRenderer *renderer)
302 {
303 }
304 
305 static gboolean
resolve_core_glx_functions(CoglRenderer * renderer,GError ** error)306 resolve_core_glx_functions (CoglRenderer *renderer,
307                             GError **error)
308 {
309   CoglGLXRenderer *glx_renderer;
310 
311   glx_renderer = renderer->winsys;
312 
313   if (!g_module_symbol (glx_renderer->libgl_module, "glXQueryExtension",
314                         (void **) &glx_renderer->glXQueryExtension) ||
315       !g_module_symbol (glx_renderer->libgl_module, "glXQueryVersion",
316                         (void **) &glx_renderer->glXQueryVersion) ||
317       !g_module_symbol (glx_renderer->libgl_module, "glXQueryExtensionsString",
318                         (void **) &glx_renderer->glXQueryExtensionsString) ||
319       (!g_module_symbol (glx_renderer->libgl_module, "glXGetProcAddress",
320                          (void **) &glx_renderer->glXGetProcAddress) &&
321        !g_module_symbol (glx_renderer->libgl_module, "glXGetProcAddressARB",
322                          (void **) &glx_renderer->glXGetProcAddress)) ||
323        !g_module_symbol (glx_renderer->libgl_module, "glXQueryDrawable",
324                          (void **) &glx_renderer->glXQueryDrawable))
325     {
326       g_set_error_literal (error, COGL_WINSYS_ERROR,
327                            COGL_WINSYS_ERROR_INIT,
328                            "Failed to resolve required GLX symbol");
329       return FALSE;
330     }
331 
332   return TRUE;
333 }
334 
335 static void
update_base_winsys_features(CoglRenderer * renderer)336 update_base_winsys_features (CoglRenderer *renderer)
337 {
338   CoglGLXRenderer *glx_renderer = renderer->winsys;
339   CoglXlibRenderer *xlib_renderer =
340     _cogl_xlib_renderer_get_data (renderer);
341   const char *glx_extensions;
342   int default_screen;
343   char **split_extensions;
344   int i;
345 
346   default_screen = DefaultScreen (xlib_renderer->xdpy);
347   glx_extensions =
348     glx_renderer->glXQueryExtensionsString (xlib_renderer->xdpy,
349                                             default_screen);
350 
351   COGL_NOTE (WINSYS, "  GLX Extensions: %s", glx_extensions);
352 
353   split_extensions = g_strsplit (glx_extensions, " ", 0 /* max_tokens */);
354 
355   for (i = 0; i < G_N_ELEMENTS (winsys_feature_data); i++)
356     if (_cogl_feature_check (renderer,
357                              "GLX", winsys_feature_data + i,
358                              glx_renderer->glx_major,
359                              glx_renderer->glx_minor,
360                              COGL_DRIVER_GL, /* the driver isn't used */
361                              split_extensions,
362                              glx_renderer))
363       {
364         if (winsys_feature_data[i].winsys_feature)
365           COGL_FLAGS_SET (glx_renderer->base_winsys_features,
366                           winsys_feature_data[i].winsys_feature,
367                           TRUE);
368       }
369 
370   g_strfreev (split_extensions);
371 
372   /* The GLX_SGI_video_sync spec explicitly states this extension
373    * only works for direct contexts; we don't know per-renderer
374    * if the context is direct or not, so we turn off the feature
375    * flag; we still use the extension within this file looking
376    * instead at glx_display->have_vblank_counter.
377    */
378   COGL_FLAGS_SET (glx_renderer->base_winsys_features,
379                   COGL_WINSYS_FEATURE_VBLANK_COUNTER,
380                   FALSE);
381 
382 
383   COGL_FLAGS_SET (glx_renderer->base_winsys_features,
384                   COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN,
385                   TRUE);
386 
387   /* Because of the direct-context dependency, the VBLANK_WAIT feature
388    * doesn't reflect the presence of GLX_SGI_video_sync.
389    */
390   if (glx_renderer->glXWaitForMsc)
391     COGL_FLAGS_SET (glx_renderer->base_winsys_features,
392                     COGL_WINSYS_FEATURE_VBLANK_WAIT,
393                     TRUE);
394 }
395 
396 static gboolean
_cogl_winsys_renderer_connect(CoglRenderer * renderer,GError ** error)397 _cogl_winsys_renderer_connect (CoglRenderer *renderer,
398                                GError **error)
399 {
400   CoglGLXRenderer *glx_renderer;
401   CoglXlibRenderer *xlib_renderer;
402 
403   renderer->winsys = g_new0 (CoglGLXRenderer, 1);
404 
405   glx_renderer = renderer->winsys;
406   xlib_renderer = _cogl_xlib_renderer_get_data (renderer);
407 
408   if (!_cogl_xlib_renderer_connect (renderer, error))
409     goto error;
410 
411   if (renderer->driver != COGL_DRIVER_GL &&
412       renderer->driver != COGL_DRIVER_GL3)
413     {
414       g_set_error_literal (error, COGL_WINSYS_ERROR,
415                            COGL_WINSYS_ERROR_INIT,
416                            "GLX Backend can only be used in conjunction with OpenGL");
417       goto error;
418     }
419 
420   glx_renderer->libgl_module = g_module_open (COGL_GL_LIBNAME,
421                                               G_MODULE_BIND_LAZY);
422 
423   if (glx_renderer->libgl_module == NULL)
424     {
425       g_set_error_literal (error, COGL_WINSYS_ERROR,
426                            COGL_WINSYS_ERROR_INIT,
427                            "Failed to dynamically open the OpenGL library");
428       goto error;
429     }
430 
431   if (!resolve_core_glx_functions (renderer, error))
432     goto error;
433 
434   if (!glx_renderer->glXQueryExtension (xlib_renderer->xdpy,
435                                         &glx_renderer->glx_error_base,
436                                         &glx_renderer->glx_event_base))
437     {
438       g_set_error_literal (error, COGL_WINSYS_ERROR,
439                            COGL_WINSYS_ERROR_INIT,
440                            "XServer appears to lack required GLX support");
441       goto error;
442     }
443 
444   /* XXX: Note: For a long time Mesa exported a hybrid GLX, exporting
445    * extensions specified to require GLX 1.3, but still reporting 1.2
446    * via glXQueryVersion. */
447   if (!glx_renderer->glXQueryVersion (xlib_renderer->xdpy,
448                                       &glx_renderer->glx_major,
449                                       &glx_renderer->glx_minor)
450       || !(glx_renderer->glx_major == 1 && glx_renderer->glx_minor >= 2))
451     {
452       g_set_error_literal (error, COGL_WINSYS_ERROR,
453                            COGL_WINSYS_ERROR_INIT,
454                            "XServer appears to lack required GLX 1.2 support");
455       goto error;
456     }
457 
458   update_base_winsys_features (renderer);
459 
460   glx_renderer->dri_fd = -1;
461 
462   return TRUE;
463 
464 error:
465   _cogl_winsys_renderer_disconnect (renderer);
466   return FALSE;
467 }
468 
469 static gboolean
update_winsys_features(CoglContext * context,GError ** error)470 update_winsys_features (CoglContext *context, GError **error)
471 {
472   CoglGLXDisplay *glx_display = context->display->winsys;
473   CoglGLXRenderer *glx_renderer = context->display->renderer->winsys;
474 
475   g_return_val_if_fail (glx_display->glx_context, FALSE);
476 
477   if (!_cogl_context_update_features (context, error))
478     return FALSE;
479 
480   memcpy (context->winsys_features,
481           glx_renderer->base_winsys_features,
482           sizeof (context->winsys_features));
483 
484   if (glx_renderer->glXCopySubBuffer || context->glBlitFramebuffer)
485     COGL_FLAGS_SET (context->winsys_features, COGL_WINSYS_FEATURE_SWAP_REGION, TRUE);
486 
487   /* Note: glXCopySubBuffer and glBlitFramebuffer won't be throttled
488    * by the SwapInterval so we have to throttle swap_region requests
489    * manually... */
490   if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION) &&
491       (glx_display->have_vblank_counter || glx_display->can_vblank_wait))
492     COGL_FLAGS_SET (context->winsys_features,
493                     COGL_WINSYS_FEATURE_SWAP_REGION_THROTTLE, TRUE);
494 
495   if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT))
496     {
497       COGL_FLAGS_SET (context->winsys_features,
498                       COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT, TRUE);
499     }
500 
501   /* We'll manually handle queueing dirty events in response to
502    * Expose events from X */
503   COGL_FLAGS_SET (context->private_features,
504                   COGL_PRIVATE_FEATURE_DIRTY_EVENTS,
505                   TRUE);
506 
507   if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE))
508     COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_BUFFER_AGE, TRUE);
509 
510   return TRUE;
511 }
512 
513 static void
glx_attributes_from_framebuffer_config(CoglDisplay * display,const CoglFramebufferConfig * config,int * attributes)514 glx_attributes_from_framebuffer_config (CoglDisplay                 *display,
515                                         const CoglFramebufferConfig *config,
516                                         int                         *attributes)
517 {
518   CoglGLXRenderer *glx_renderer = display->renderer->winsys;
519   int i = 0;
520 
521   attributes[i++] = GLX_DRAWABLE_TYPE;
522   attributes[i++] = GLX_WINDOW_BIT;
523 
524   attributes[i++] = GLX_RENDER_TYPE;
525   attributes[i++] = GLX_RGBA_BIT;
526 
527   attributes[i++] = GLX_DOUBLEBUFFER;
528   attributes[i++] = GL_TRUE;
529 
530   attributes[i++] = GLX_RED_SIZE;
531   attributes[i++] = 1;
532   attributes[i++] = GLX_GREEN_SIZE;
533   attributes[i++] = 1;
534   attributes[i++] = GLX_BLUE_SIZE;
535   attributes[i++] = 1;
536   attributes[i++] = GLX_ALPHA_SIZE;
537   attributes[i++] = GLX_DONT_CARE;
538   attributes[i++] = GLX_DEPTH_SIZE;
539   attributes[i++] = 1;
540   attributes[i++] = GLX_STENCIL_SIZE;
541   attributes[i++] = config->need_stencil ? 2 : 0;
542   if (config->stereo_enabled)
543     {
544       attributes[i++] = GLX_STEREO;
545       attributes[i++] = TRUE;
546     }
547 
548   if (glx_renderer->glx_major == 1 && glx_renderer->glx_minor >= 4 &&
549       config->samples_per_pixel)
550     {
551       attributes[i++] = GLX_SAMPLE_BUFFERS;
552       attributes[i++] = 1;
553       attributes[i++] = GLX_SAMPLES;
554       attributes[i++] = config->samples_per_pixel;
555     }
556 
557   attributes[i++] = None;
558 
559   g_assert (i < MAX_GLX_CONFIG_ATTRIBS);
560 }
561 
562 /* It seems the GLX spec never defined an invalid GLXFBConfig that
563  * we could overload as an indication of error, so we have to return
564  * an explicit boolean status. */
565 gboolean
cogl_display_glx_find_fbconfig(CoglDisplay * display,const CoglFramebufferConfig * config,GLXFBConfig * config_ret,GError ** error)566 cogl_display_glx_find_fbconfig (CoglDisplay                  *display,
567                                 const CoglFramebufferConfig  *config,
568                                 GLXFBConfig                  *config_ret,
569                                 GError                      **error)
570 {
571   CoglXlibRenderer *xlib_renderer =
572     _cogl_xlib_renderer_get_data (display->renderer);
573   CoglGLXRenderer *glx_renderer = display->renderer->winsys;
574   GLXFBConfig *configs = NULL;
575   int n_configs;
576   static int attributes[MAX_GLX_CONFIG_ATTRIBS];
577   gboolean ret = TRUE;
578   int xscreen_num = DefaultScreen (xlib_renderer->xdpy);
579 
580   glx_attributes_from_framebuffer_config (display, config, attributes);
581 
582   configs = glx_renderer->glXChooseFBConfig (xlib_renderer->xdpy,
583                                              xscreen_num,
584                                              attributes,
585                                              &n_configs);
586 
587   if (!configs || n_configs == 0)
588     {
589       g_set_error_literal (error, COGL_WINSYS_ERROR,
590                            COGL_WINSYS_ERROR_CREATE_CONTEXT,
591                            "Failed to find any compatible fbconfigs");
592       ret = FALSE;
593       goto done;
594     }
595 
596   COGL_NOTE (WINSYS, "Using the first available FBConfig");
597   *config_ret = configs[0];
598 
599 done:
600   XFree (configs);
601   return ret;
602 }
603 
604 static GLXContext
create_gl3_context(CoglDisplay * display,GLXFBConfig fb_config)605 create_gl3_context (CoglDisplay *display,
606                     GLXFBConfig fb_config)
607 {
608   CoglXlibRenderer *xlib_renderer =
609     _cogl_xlib_renderer_get_data (display->renderer);
610   CoglGLXRenderer *glx_renderer = display->renderer->winsys;
611 
612   /* We want a core profile 3.1 context with no deprecated features */
613   static const int attrib_list[] =
614     {
615       GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
616       GLX_CONTEXT_MINOR_VERSION_ARB, 1,
617       GLX_CONTEXT_PROFILE_MASK_ARB,  GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
618       GLX_CONTEXT_FLAGS_ARB,         GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
619       None
620     };
621   /* NV_robustness_video_memory_purge relies on GLX_ARB_create_context
622      and in part on ARB_robustness. Namely, it needs the notification
623      strategy to be set to GLX_LOSE_CONTEXT_ON_RESET_ARB and that the
624      driver exposes the GetGraphicsResetStatusARB function. This means
625      we don't actually enable robust buffer access. */
626   static const int attrib_list_reset_on_purge[] =
627     {
628       GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
629       GLX_CONTEXT_MINOR_VERSION_ARB, 1,
630       GLX_CONTEXT_PROFILE_MASK_ARB,  GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
631       GLX_CONTEXT_FLAGS_ARB,         GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
632       GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV,
633                                      GL_TRUE,
634       GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
635                                      GLX_LOSE_CONTEXT_ON_RESET_ARB,
636       None
637     };
638 
639   /* Make sure that the display supports the GLX_ARB_create_context
640      extension */
641   if (glx_renderer->glXCreateContextAttribs == NULL)
642     return NULL;
643 
644   /* We can't check the presence of this extension with the usual
645      COGL_WINSYS_FEATURE machinery because that only gets initialized
646      later when the CoglContext is created. */
647   if (display->renderer->xlib_want_reset_on_video_memory_purge &&
648       strstr (glx_renderer->glXQueryExtensionsString (xlib_renderer->xdpy,
649                                                       DefaultScreen (xlib_renderer->xdpy)),
650               "GLX_NV_robustness_video_memory_purge"))
651     {
652       CoglXlibTrapState old_state;
653       GLXContext ctx;
654 
655       _cogl_xlib_renderer_trap_errors (display->renderer, &old_state);
656       ctx = glx_renderer->glXCreateContextAttribs (xlib_renderer->xdpy,
657                                                    fb_config,
658                                                    NULL /* share_context */,
659                                                    True, /* direct */
660                                                    attrib_list_reset_on_purge);
661       if (!_cogl_xlib_renderer_untrap_errors (display->renderer, &old_state) && ctx)
662         return ctx;
663     }
664 
665   return glx_renderer->glXCreateContextAttribs (xlib_renderer->xdpy,
666                                                 fb_config,
667                                                 NULL /* share_context */,
668                                                 True, /* direct */
669                                                 attrib_list);
670 }
671 
672 static gboolean
create_context(CoglDisplay * display,GError ** error)673 create_context (CoglDisplay *display, GError **error)
674 {
675   CoglGLXDisplay *glx_display = display->winsys;
676   CoglXlibRenderer *xlib_renderer =
677     _cogl_xlib_renderer_get_data (display->renderer);
678   CoglGLXRenderer *glx_renderer = display->renderer->winsys;
679   GLXFBConfig config;
680   GError *fbconfig_error = NULL;
681   XSetWindowAttributes attrs;
682   XVisualInfo *xvisinfo;
683   GLXDrawable dummy_drawable;
684   CoglXlibTrapState old_state;
685 
686   g_return_val_if_fail (glx_display->glx_context == NULL, TRUE);
687 
688   glx_display->found_fbconfig =
689     cogl_display_glx_find_fbconfig (display,
690                                     &display->onscreen_template->config,
691                                     &config,
692                                     &fbconfig_error);
693   if (!glx_display->found_fbconfig)
694     {
695       g_set_error (error, COGL_WINSYS_ERROR,
696                    COGL_WINSYS_ERROR_CREATE_CONTEXT,
697                    "Unable to find suitable fbconfig for the GLX context: %s",
698                    fbconfig_error->message);
699       g_error_free (fbconfig_error);
700       return FALSE;
701     }
702 
703   glx_display->fbconfig = config;
704 
705   COGL_NOTE (WINSYS, "Creating GLX Context (display: %p)",
706              xlib_renderer->xdpy);
707 
708   _cogl_xlib_renderer_trap_errors (display->renderer, &old_state);
709 
710   if (display->renderer->driver == COGL_DRIVER_GL3)
711     glx_display->glx_context = create_gl3_context (display, config);
712   else
713     glx_display->glx_context =
714       glx_renderer->glXCreateNewContext (xlib_renderer->xdpy,
715                                          config,
716                                          GLX_RGBA_TYPE,
717                                          NULL,
718                                          True);
719 
720   if (_cogl_xlib_renderer_untrap_errors (display->renderer, &old_state) ||
721       glx_display->glx_context == NULL)
722     {
723       g_set_error_literal (error, COGL_WINSYS_ERROR,
724                            COGL_WINSYS_ERROR_CREATE_CONTEXT,
725                            "Unable to create suitable GL context");
726       return FALSE;
727     }
728 
729   glx_display->is_direct =
730     glx_renderer->glXIsDirect (xlib_renderer->xdpy, glx_display->glx_context);
731   glx_display->have_vblank_counter = glx_display->is_direct && glx_renderer->glXWaitVideoSync;
732   glx_display->can_vblank_wait = glx_renderer->glXWaitForMsc || glx_display->have_vblank_counter;
733 
734   COGL_NOTE (WINSYS, "Setting %s context",
735              glx_display->is_direct ? "direct" : "indirect");
736 
737   /* XXX: GLX doesn't let us make a context current without a window
738    * so we create a dummy window that we can use while no CoglOnscreen
739    * framebuffer is in use.
740    */
741 
742   xvisinfo = glx_renderer->glXGetVisualFromFBConfig (xlib_renderer->xdpy,
743                                                      config);
744   if (xvisinfo == NULL)
745     {
746       g_set_error_literal (error, COGL_WINSYS_ERROR,
747                            COGL_WINSYS_ERROR_CREATE_CONTEXT,
748                            "Unable to retrieve the X11 visual");
749       return FALSE;
750     }
751 
752   _cogl_xlib_renderer_trap_errors (display->renderer, &old_state);
753 
754   attrs.override_redirect = True;
755   attrs.colormap = XCreateColormap (xlib_renderer->xdpy,
756                                     DefaultRootWindow (xlib_renderer->xdpy),
757                                     xvisinfo->visual,
758                                     AllocNone);
759   attrs.border_pixel = 0;
760 
761   glx_display->dummy_xwin =
762     XCreateWindow (xlib_renderer->xdpy,
763                    DefaultRootWindow (xlib_renderer->xdpy),
764                    -100, -100, 1, 1,
765                    0,
766                    xvisinfo->depth,
767                    CopyFromParent,
768                    xvisinfo->visual,
769                    CWOverrideRedirect | CWColormap | CWBorderPixel,
770                    &attrs);
771 
772   /* Try and create a GLXWindow to use with extensions dependent on
773    * GLX versions >= 1.3 that don't accept regular X Windows as GLX
774    * drawables. */
775   if (glx_renderer->glx_major == 1 && glx_renderer->glx_minor >= 3)
776     {
777       glx_display->dummy_glxwin =
778         glx_renderer->glXCreateWindow (xlib_renderer->xdpy,
779                                        config,
780                                        glx_display->dummy_xwin,
781                                        NULL);
782     }
783 
784   if (glx_display->dummy_glxwin)
785     dummy_drawable = glx_display->dummy_glxwin;
786   else
787     dummy_drawable = glx_display->dummy_xwin;
788 
789   COGL_NOTE (WINSYS, "Selecting dummy 0x%x for the GLX context",
790              (unsigned int) dummy_drawable);
791 
792   glx_renderer->glXMakeContextCurrent (xlib_renderer->xdpy,
793                                        dummy_drawable,
794                                        dummy_drawable,
795                                        glx_display->glx_context);
796 
797   xlib_renderer->xvisinfo = xvisinfo;
798 
799   if (_cogl_xlib_renderer_untrap_errors (display->renderer, &old_state))
800     {
801       g_set_error_literal (error, COGL_WINSYS_ERROR,
802                            COGL_WINSYS_ERROR_CREATE_CONTEXT,
803                            "Unable to select the newly created GLX context");
804       return FALSE;
805     }
806 
807   return TRUE;
808 }
809 
810 static void
_cogl_winsys_display_destroy(CoglDisplay * display)811 _cogl_winsys_display_destroy (CoglDisplay *display)
812 {
813   CoglGLXDisplay *glx_display = display->winsys;
814   CoglXlibRenderer *xlib_renderer =
815     _cogl_xlib_renderer_get_data (display->renderer);
816   CoglGLXRenderer *glx_renderer = display->renderer->winsys;
817 
818   g_return_if_fail (glx_display != NULL);
819 
820   if (glx_display->glx_context)
821     {
822       glx_renderer->glXMakeContextCurrent (xlib_renderer->xdpy,
823                                            None, None, NULL);
824       glx_renderer->glXDestroyContext (xlib_renderer->xdpy,
825                                        glx_display->glx_context);
826       glx_display->glx_context = NULL;
827     }
828 
829   if (glx_display->dummy_glxwin)
830     {
831       glx_renderer->glXDestroyWindow (xlib_renderer->xdpy,
832                                       glx_display->dummy_glxwin);
833       glx_display->dummy_glxwin = None;
834     }
835 
836   if (glx_display->dummy_xwin)
837     {
838       XDestroyWindow (xlib_renderer->xdpy, glx_display->dummy_xwin);
839       glx_display->dummy_xwin = None;
840     }
841 
842   g_free (display->winsys);
843   display->winsys = NULL;
844 }
845 
846 static gboolean
_cogl_winsys_display_setup(CoglDisplay * display,GError ** error)847 _cogl_winsys_display_setup (CoglDisplay *display,
848                             GError **error)
849 {
850   CoglGLXDisplay *glx_display;
851   int i;
852 
853   g_return_val_if_fail (display->winsys == NULL, FALSE);
854 
855   glx_display = g_new0 (CoglGLXDisplay, 1);
856   display->winsys = glx_display;
857 
858   if (!create_context (display, error))
859     goto error;
860 
861   for (i = 0; i < COGL_GLX_N_CACHED_CONFIGS; i++)
862     glx_display->glx_cached_configs[i].depth = -1;
863 
864   return TRUE;
865 
866 error:
867   _cogl_winsys_display_destroy (display);
868   return FALSE;
869 }
870 
871 static gboolean
_cogl_winsys_context_init(CoglContext * context,GError ** error)872 _cogl_winsys_context_init (CoglContext *context, GError **error)
873 {
874   context->winsys = g_new0 (CoglContextGLX, 1);
875 
876   cogl_xlib_renderer_add_filter (context->display->renderer,
877                                  glx_event_filter_cb,
878                                  context);
879   return update_winsys_features (context, error);
880 }
881 
882 static void
_cogl_winsys_context_deinit(CoglContext * context)883 _cogl_winsys_context_deinit (CoglContext *context)
884 {
885   cogl_xlib_renderer_remove_filter (context->display->renderer,
886                                     glx_event_filter_cb,
887                                     context);
888   g_free (context->winsys);
889 }
890 
891 static gboolean
get_fbconfig_for_depth(CoglContext * context,unsigned int depth,gboolean stereo,GLXFBConfig * fbconfig_ret,gboolean * can_mipmap_ret)892 get_fbconfig_for_depth (CoglContext *context,
893                         unsigned int depth,
894                         gboolean stereo,
895                         GLXFBConfig *fbconfig_ret,
896                         gboolean *can_mipmap_ret)
897 {
898   CoglXlibRenderer *xlib_renderer;
899   CoglGLXRenderer *glx_renderer;
900   CoglGLXDisplay *glx_display;
901   Display *dpy;
902   GLXFBConfig *fbconfigs;
903   int n_elements, i;
904   int db, stencil, alpha, mipmap, rgba, value;
905   int spare_cache_slot = 0;
906   gboolean found = FALSE;
907 
908   xlib_renderer = _cogl_xlib_renderer_get_data (context->display->renderer);
909   glx_renderer = context->display->renderer->winsys;
910   glx_display = context->display->winsys;
911 
912   /* Check if we've already got a cached config for this depth and stereo */
913   for (i = 0; i < COGL_GLX_N_CACHED_CONFIGS; i++)
914     if (glx_display->glx_cached_configs[i].depth == -1)
915       spare_cache_slot = i;
916     else if (glx_display->glx_cached_configs[i].depth == depth &&
917              glx_display->glx_cached_configs[i].stereo == stereo)
918       {
919         *fbconfig_ret = glx_display->glx_cached_configs[i].fb_config;
920         *can_mipmap_ret = glx_display->glx_cached_configs[i].can_mipmap;
921         return glx_display->glx_cached_configs[i].found;
922       }
923 
924   dpy = xlib_renderer->xdpy;
925 
926   fbconfigs = glx_renderer->glXGetFBConfigs (dpy, DefaultScreen (dpy),
927                                              &n_elements);
928 
929   db = G_MAXSHORT;
930   stencil = G_MAXSHORT;
931   mipmap = 0;
932   rgba = 0;
933 
934   for (i = 0; i < n_elements; i++)
935     {
936       XVisualInfo *vi;
937       int visual_depth;
938 
939       vi = glx_renderer->glXGetVisualFromFBConfig (dpy, fbconfigs[i]);
940       if (vi == NULL)
941         continue;
942 
943       visual_depth = vi->depth;
944 
945       XFree (vi);
946 
947       if (visual_depth != depth)
948         continue;
949 
950       glx_renderer->glXGetFBConfigAttrib (dpy,
951                                           fbconfigs[i],
952                                           GLX_ALPHA_SIZE,
953                                           &alpha);
954       glx_renderer->glXGetFBConfigAttrib (dpy,
955                                           fbconfigs[i],
956                                           GLX_BUFFER_SIZE,
957                                           &value);
958       if (value != depth && (value - alpha) != depth)
959         continue;
960 
961       glx_renderer->glXGetFBConfigAttrib (dpy,
962                                           fbconfigs[i],
963                                           GLX_STEREO,
964                                           &value);
965       if (!!value != !!stereo)
966         continue;
967 
968       if (glx_renderer->glx_major == 1 && glx_renderer->glx_minor >= 4)
969         {
970           glx_renderer->glXGetFBConfigAttrib (dpy,
971                                               fbconfigs[i],
972                                               GLX_SAMPLES,
973                                               &value);
974           if (value > 1)
975             continue;
976         }
977 
978       value = 0;
979       if (depth == 32)
980         {
981           glx_renderer->glXGetFBConfigAttrib (dpy,
982                                               fbconfigs[i],
983                                               GLX_BIND_TO_TEXTURE_RGBA_EXT,
984                                               &value);
985           if (value)
986             rgba = 1;
987         }
988 
989       if (!value)
990         {
991           if (rgba)
992             continue;
993 
994           glx_renderer->glXGetFBConfigAttrib (dpy,
995                                               fbconfigs[i],
996                                               GLX_BIND_TO_TEXTURE_RGB_EXT,
997                                               &value);
998           if (!value)
999             continue;
1000         }
1001 
1002       glx_renderer->glXGetFBConfigAttrib (dpy,
1003                                           fbconfigs[i],
1004                                           GLX_DOUBLEBUFFER,
1005                                           &value);
1006       if (value > db)
1007         continue;
1008 
1009       db = value;
1010 
1011       glx_renderer->glXGetFBConfigAttrib (dpy,
1012                                           fbconfigs[i],
1013                                           GLX_STENCIL_SIZE,
1014                                           &value);
1015       if (value > stencil)
1016         continue;
1017 
1018       stencil = value;
1019 
1020       glx_renderer->glXGetFBConfigAttrib (dpy,
1021                                           fbconfigs[i],
1022                                           GLX_BIND_TO_MIPMAP_TEXTURE_EXT,
1023                                           &value);
1024 
1025       if (value < mipmap)
1026         continue;
1027 
1028       mipmap = value;
1029 
1030       *fbconfig_ret = fbconfigs[i];
1031       *can_mipmap_ret = mipmap;
1032       found = TRUE;
1033     }
1034 
1035   if (n_elements)
1036     XFree (fbconfigs);
1037 
1038   glx_display->glx_cached_configs[spare_cache_slot].depth = depth;
1039   glx_display->glx_cached_configs[spare_cache_slot].found = found;
1040   glx_display->glx_cached_configs[spare_cache_slot].fb_config = *fbconfig_ret;
1041   glx_display->glx_cached_configs[spare_cache_slot].can_mipmap = mipmap;
1042 
1043   return found;
1044 }
1045 
1046 static gboolean
try_create_glx_pixmap(CoglContext * context,CoglTexturePixmapX11 * tex_pixmap,gboolean mipmap)1047 try_create_glx_pixmap (CoglContext *context,
1048                        CoglTexturePixmapX11 *tex_pixmap,
1049                        gboolean mipmap)
1050 {
1051   CoglTexturePixmapGLX *glx_tex_pixmap = tex_pixmap->winsys;
1052   CoglRenderer *renderer;
1053   CoglXlibRenderer *xlib_renderer;
1054   CoglGLXRenderer *glx_renderer;
1055   Display *dpy;
1056   /* We have to initialize this *opaque* variable because gcc tries to
1057    * be too smart for its own good and warns that the variable may be
1058    * used uninitialized otherwise. */
1059   GLXFBConfig fb_config = (GLXFBConfig)0;
1060   int attribs[7];
1061   int i = 0;
1062   CoglXlibTrapState trap_state;
1063 
1064   unsigned int depth = tex_pixmap->depth;
1065   Visual* visual = tex_pixmap->visual;
1066 
1067   renderer = context->display->renderer;
1068   xlib_renderer = _cogl_xlib_renderer_get_data (renderer);
1069   glx_renderer = renderer->winsys;
1070   dpy = xlib_renderer->xdpy;
1071 
1072   if (!get_fbconfig_for_depth (context, depth,
1073                                tex_pixmap->stereo_mode != COGL_TEXTURE_PIXMAP_MONO,
1074                                &fb_config,
1075                                &glx_tex_pixmap->can_mipmap))
1076     {
1077       COGL_NOTE (TEXTURE_PIXMAP, "No suitable FBConfig found for depth %i",
1078                  depth);
1079       return FALSE;
1080     }
1081 
1082   if (!glx_tex_pixmap->can_mipmap)
1083     mipmap = FALSE;
1084 
1085   attribs[i++] = GLX_TEXTURE_FORMAT_EXT;
1086 
1087   /* Check whether an alpha channel is used by comparing the total
1088    * number of 1-bits in color masks against the color depth requested
1089    * by the client.
1090    */
1091   if (_cogl_util_popcountl (visual->red_mask |
1092                             visual->green_mask |
1093                             visual->blue_mask) == depth)
1094     attribs[i++] = GLX_TEXTURE_FORMAT_RGB_EXT;
1095   else
1096     attribs[i++] = GLX_TEXTURE_FORMAT_RGBA_EXT;
1097 
1098   attribs[i++] = GLX_MIPMAP_TEXTURE_EXT;
1099   attribs[i++] = mipmap;
1100 
1101   attribs[i++] = GLX_TEXTURE_TARGET_EXT;
1102   attribs[i++] = GLX_TEXTURE_2D_EXT;
1103 
1104   attribs[i++] = None;
1105 
1106   /* We need to trap errors from glXCreatePixmap because it can
1107    * sometimes fail during normal usage. For example on NVidia it gets
1108    * upset if you try to create two GLXPixmaps for the same drawable.
1109    */
1110 
1111   _cogl_xlib_renderer_trap_errors (renderer, &trap_state);
1112 
1113   glx_tex_pixmap->glx_pixmap =
1114     glx_renderer->glXCreatePixmap (dpy,
1115                                    fb_config,
1116                                    tex_pixmap->pixmap,
1117                                    attribs);
1118   glx_tex_pixmap->has_mipmap_space = mipmap;
1119 
1120   XSync (dpy, False);
1121 
1122   if (_cogl_xlib_renderer_untrap_errors (renderer, &trap_state))
1123     {
1124       COGL_NOTE (TEXTURE_PIXMAP, "Failed to create pixmap for %p", tex_pixmap);
1125       _cogl_xlib_renderer_trap_errors (renderer, &trap_state);
1126       glx_renderer->glXDestroyPixmap (dpy, glx_tex_pixmap->glx_pixmap);
1127       XSync (dpy, False);
1128       _cogl_xlib_renderer_untrap_errors (renderer, &trap_state);
1129 
1130       glx_tex_pixmap->glx_pixmap = None;
1131       return FALSE;
1132     }
1133 
1134   return TRUE;
1135 }
1136 
1137 static gboolean
_cogl_winsys_texture_pixmap_x11_create(CoglTexturePixmapX11 * tex_pixmap)1138 _cogl_winsys_texture_pixmap_x11_create (CoglTexturePixmapX11 *tex_pixmap)
1139 {
1140   CoglTexturePixmapGLX *glx_tex_pixmap;
1141   CoglContext *ctx = COGL_TEXTURE (tex_pixmap)->context;
1142 
1143   if (!_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_TEXTURE_FROM_PIXMAP))
1144     {
1145       tex_pixmap->winsys = NULL;
1146       return FALSE;
1147     }
1148 
1149   glx_tex_pixmap = g_new0 (CoglTexturePixmapGLX, 1);
1150 
1151   glx_tex_pixmap->glx_pixmap = None;
1152   glx_tex_pixmap->can_mipmap = FALSE;
1153   glx_tex_pixmap->has_mipmap_space = FALSE;
1154 
1155   glx_tex_pixmap->left.glx_tex = NULL;
1156   glx_tex_pixmap->right.glx_tex = NULL;
1157 
1158   glx_tex_pixmap->left.bind_tex_image_queued = TRUE;
1159   glx_tex_pixmap->right.bind_tex_image_queued = TRUE;
1160   glx_tex_pixmap->left.pixmap_bound = FALSE;
1161   glx_tex_pixmap->right.pixmap_bound = FALSE;
1162 
1163   tex_pixmap->winsys = glx_tex_pixmap;
1164 
1165   if (!try_create_glx_pixmap (ctx, tex_pixmap, FALSE))
1166     {
1167       tex_pixmap->winsys = NULL;
1168       g_free (glx_tex_pixmap);
1169       return FALSE;
1170     }
1171 
1172   return TRUE;
1173 }
1174 
1175 static void
free_glx_pixmap(CoglContext * context,CoglTexturePixmapGLX * glx_tex_pixmap)1176 free_glx_pixmap (CoglContext *context,
1177                  CoglTexturePixmapGLX *glx_tex_pixmap)
1178 {
1179   CoglXlibTrapState trap_state;
1180   CoglRenderer *renderer;
1181   CoglXlibRenderer *xlib_renderer;
1182   CoglGLXRenderer *glx_renderer;
1183 
1184   renderer = context->display->renderer;
1185   xlib_renderer = _cogl_xlib_renderer_get_data (renderer);
1186   glx_renderer = renderer->winsys;
1187 
1188   if (glx_tex_pixmap->left.pixmap_bound)
1189     glx_renderer->glXReleaseTexImage (xlib_renderer->xdpy,
1190                                       glx_tex_pixmap->glx_pixmap,
1191                                       GLX_FRONT_LEFT_EXT);
1192   if (glx_tex_pixmap->right.pixmap_bound)
1193     glx_renderer->glXReleaseTexImage (xlib_renderer->xdpy,
1194                                       glx_tex_pixmap->glx_pixmap,
1195                                       GLX_FRONT_RIGHT_EXT);
1196 
1197   /* FIXME - we need to trap errors and synchronize here because
1198    * of ordering issues between the XPixmap destruction and the
1199    * GLXPixmap destruction.
1200    *
1201    * If the X pixmap is destroyed, the GLX pixmap is destroyed as
1202    * well immediately, and thus, when Cogl calls glXDestroyPixmap()
1203    * it'll cause a BadDrawable error.
1204    *
1205    * this is technically a bug in the X server, which should not
1206    * destroy either pixmaps until the call to glXDestroyPixmap(); so
1207    * at some point we should revisit this code and remove the
1208    * trap+sync after verifying that the destruction is indeed safe.
1209    *
1210    * for reference, see:
1211    *   http://bugzilla.clutter-project.org/show_bug.cgi?id=2324
1212    */
1213   _cogl_xlib_renderer_trap_errors (renderer, &trap_state);
1214   glx_renderer->glXDestroyPixmap (xlib_renderer->xdpy,
1215                                   glx_tex_pixmap->glx_pixmap);
1216   XSync (xlib_renderer->xdpy, False);
1217   _cogl_xlib_renderer_untrap_errors (renderer, &trap_state);
1218 
1219   glx_tex_pixmap->glx_pixmap = None;
1220   glx_tex_pixmap->left.pixmap_bound = FALSE;
1221   glx_tex_pixmap->right.pixmap_bound = FALSE;
1222 }
1223 
1224 static void
_cogl_winsys_texture_pixmap_x11_free(CoglTexturePixmapX11 * tex_pixmap)1225 _cogl_winsys_texture_pixmap_x11_free (CoglTexturePixmapX11 *tex_pixmap)
1226 {
1227   CoglTexturePixmapGLX *glx_tex_pixmap;
1228 
1229   if (!tex_pixmap->winsys)
1230     return;
1231 
1232   glx_tex_pixmap = tex_pixmap->winsys;
1233 
1234   free_glx_pixmap (COGL_TEXTURE (tex_pixmap)->context, glx_tex_pixmap);
1235 
1236   if (glx_tex_pixmap->left.glx_tex)
1237     cogl_object_unref (glx_tex_pixmap->left.glx_tex);
1238 
1239   if (glx_tex_pixmap->right.glx_tex)
1240     cogl_object_unref (glx_tex_pixmap->right.glx_tex);
1241 
1242   tex_pixmap->winsys = NULL;
1243   g_free (glx_tex_pixmap);
1244 }
1245 
1246 static gboolean
_cogl_winsys_texture_pixmap_x11_update(CoglTexturePixmapX11 * tex_pixmap,CoglTexturePixmapStereoMode stereo_mode,gboolean needs_mipmap)1247 _cogl_winsys_texture_pixmap_x11_update (CoglTexturePixmapX11 *tex_pixmap,
1248                                         CoglTexturePixmapStereoMode stereo_mode,
1249                                         gboolean needs_mipmap)
1250 {
1251   CoglTexture *tex = COGL_TEXTURE (tex_pixmap);
1252   CoglContext *ctx = COGL_TEXTURE (tex_pixmap)->context;
1253   CoglTexturePixmapGLX *glx_tex_pixmap = tex_pixmap->winsys;
1254   CoglPixmapTextureEyeGLX *texture_info;
1255   int buffer;
1256   CoglGLXRenderer *glx_renderer;
1257 
1258   if (stereo_mode == COGL_TEXTURE_PIXMAP_RIGHT)
1259     {
1260       texture_info = &glx_tex_pixmap->right;
1261       buffer = GLX_FRONT_RIGHT_EXT;
1262     }
1263   else
1264     {
1265       texture_info = &glx_tex_pixmap->left;
1266       buffer = GLX_FRONT_LEFT_EXT;
1267     }
1268 
1269   /* If we don't have a GLX pixmap then fallback */
1270   if (glx_tex_pixmap->glx_pixmap == None)
1271     return FALSE;
1272 
1273   glx_renderer = ctx->display->renderer->winsys;
1274 
1275   /* Lazily create a texture to hold the pixmap */
1276   if (texture_info->glx_tex == NULL)
1277     {
1278       CoglPixelFormat texture_format;
1279       GError *error = NULL;
1280 
1281       texture_format = (tex_pixmap->depth >= 32 ?
1282                         COGL_PIXEL_FORMAT_RGBA_8888_PRE :
1283                         COGL_PIXEL_FORMAT_RGB_888);
1284 
1285       texture_info->glx_tex = COGL_TEXTURE (
1286         cogl_texture_2d_new_with_size (ctx, tex->width, tex->height));
1287 
1288       _cogl_texture_set_internal_format (tex, texture_format);
1289 
1290       if (cogl_texture_allocate (texture_info->glx_tex, &error))
1291         COGL_NOTE (TEXTURE_PIXMAP, "Created a texture 2d for %p", tex_pixmap);
1292       else
1293         {
1294           COGL_NOTE (TEXTURE_PIXMAP, "Falling back for %p because a "
1295                      "texture 2d could not be created: %s",
1296                      tex_pixmap, error->message);
1297           g_error_free (error);
1298           free_glx_pixmap (ctx, glx_tex_pixmap);
1299           return FALSE;
1300         }
1301     }
1302 
1303   if (needs_mipmap)
1304     {
1305       /* If we can't support mipmapping then temporarily fallback */
1306       if (!glx_tex_pixmap->can_mipmap)
1307         return FALSE;
1308 
1309       /* Recreate the GLXPixmap if it wasn't previously created with a
1310        * mipmap tree */
1311       if (!glx_tex_pixmap->has_mipmap_space)
1312         {
1313           free_glx_pixmap (ctx, glx_tex_pixmap);
1314 
1315           COGL_NOTE (TEXTURE_PIXMAP, "Recreating GLXPixmap with mipmap "
1316                      "support for %p", tex_pixmap);
1317           if (!try_create_glx_pixmap (ctx, tex_pixmap, TRUE))
1318 
1319             {
1320               /* If the pixmap failed then we'll permanently fallback
1321                * to using XImage. This shouldn't happen. */
1322               COGL_NOTE (TEXTURE_PIXMAP, "Falling back to XGetImage "
1323                          "updates for %p because creating the GLXPixmap "
1324                          "with mipmap support failed", tex_pixmap);
1325 
1326               if (texture_info->glx_tex)
1327                 cogl_object_unref (texture_info->glx_tex);
1328               return FALSE;
1329             }
1330 
1331           glx_tex_pixmap->left.bind_tex_image_queued = TRUE;
1332           glx_tex_pixmap->right.bind_tex_image_queued = TRUE;
1333         }
1334     }
1335 
1336   if (texture_info->bind_tex_image_queued)
1337     {
1338       GLuint gl_handle, gl_target;
1339       CoglXlibRenderer *xlib_renderer =
1340         _cogl_xlib_renderer_get_data (ctx->display->renderer);
1341 
1342       cogl_texture_get_gl_texture (texture_info->glx_tex,
1343                                    &gl_handle, &gl_target);
1344 
1345       COGL_NOTE (TEXTURE_PIXMAP, "Rebinding GLXPixmap for %p", tex_pixmap);
1346 
1347       _cogl_bind_gl_texture_transient (gl_target, gl_handle);
1348 
1349       if (texture_info->pixmap_bound)
1350         glx_renderer->glXReleaseTexImage (xlib_renderer->xdpy,
1351                                           glx_tex_pixmap->glx_pixmap,
1352                                           buffer);
1353 
1354       glx_renderer->glXBindTexImage (xlib_renderer->xdpy,
1355                                      glx_tex_pixmap->glx_pixmap,
1356                                      buffer,
1357                                      NULL);
1358 
1359       /* According to the recommended usage in the spec for
1360        * GLX_EXT_texture_pixmap we should release the texture after
1361        * we've finished drawing with it and it is undefined what
1362        * happens if you render to a pixmap that is bound to a texture.
1363        * However that would require the texture backend to know when
1364        * Cogl has finished painting and it may be more expensive to
1365        * keep unbinding the texture. Leaving it bound appears to work
1366        * on Mesa and NVidia drivers and it is also what Compiz does so
1367        * it is probably ok */
1368 
1369       texture_info->bind_tex_image_queued = FALSE;
1370       texture_info->pixmap_bound = TRUE;
1371 
1372       _cogl_texture_2d_externally_modified (texture_info->glx_tex);
1373     }
1374 
1375   return TRUE;
1376 }
1377 
1378 static void
_cogl_winsys_texture_pixmap_x11_damage_notify(CoglTexturePixmapX11 * tex_pixmap)1379 _cogl_winsys_texture_pixmap_x11_damage_notify (CoglTexturePixmapX11 *tex_pixmap)
1380 {
1381   CoglTexturePixmapGLX *glx_tex_pixmap = tex_pixmap->winsys;
1382 
1383   glx_tex_pixmap->left.bind_tex_image_queued = TRUE;
1384   glx_tex_pixmap->right.bind_tex_image_queued = TRUE;
1385 }
1386 
1387 static CoglTexture *
_cogl_winsys_texture_pixmap_x11_get_texture(CoglTexturePixmapX11 * tex_pixmap,CoglTexturePixmapStereoMode stereo_mode)1388 _cogl_winsys_texture_pixmap_x11_get_texture (CoglTexturePixmapX11 *tex_pixmap,
1389                                              CoglTexturePixmapStereoMode stereo_mode)
1390 {
1391   CoglTexturePixmapGLX *glx_tex_pixmap = tex_pixmap->winsys;
1392 
1393   if (stereo_mode == COGL_TEXTURE_PIXMAP_RIGHT)
1394     return glx_tex_pixmap->right.glx_tex;
1395   else
1396     return glx_tex_pixmap->left.glx_tex;
1397 }
1398 
1399 void
cogl_context_glx_set_current_drawable(CoglContext * context,GLXDrawable drawable)1400 cogl_context_glx_set_current_drawable (CoglContext *context,
1401                                        GLXDrawable  drawable)
1402 {
1403   CoglContextGLX *glx_context = context->winsys;
1404 
1405   glx_context->current_drawable = drawable;
1406 }
1407 
1408 GLXDrawable
cogl_context_glx_get_current_drawable(CoglContext * context)1409 cogl_context_glx_get_current_drawable (CoglContext *context)
1410 {
1411   CoglContextGLX *glx_context = context->winsys;
1412 
1413   return glx_context->current_drawable;
1414 }
1415 
1416 static CoglWinsysVtable _cogl_winsys_vtable =
1417   {
1418     .id = COGL_WINSYS_ID_GLX,
1419     .name = "GLX",
1420     .constraints = (COGL_RENDERER_CONSTRAINT_USES_X11 |
1421                     COGL_RENDERER_CONSTRAINT_USES_XLIB),
1422 
1423     .renderer_get_proc_address = _cogl_winsys_renderer_get_proc_address,
1424     .renderer_connect = _cogl_winsys_renderer_connect,
1425     .renderer_disconnect = _cogl_winsys_renderer_disconnect,
1426     .renderer_outputs_changed = _cogl_winsys_renderer_outputs_changed,
1427     .renderer_bind_api = _cogl_winsys_renderer_bind_api,
1428     .display_setup = _cogl_winsys_display_setup,
1429     .display_destroy = _cogl_winsys_display_destroy,
1430     .context_init = _cogl_winsys_context_init,
1431     .context_deinit = _cogl_winsys_context_deinit,
1432 
1433     /* X11 tfp support... */
1434     /* XXX: instead of having a rather monolithic winsys vtable we could
1435      * perhaps look for a way to separate these... */
1436     .texture_pixmap_x11_create =
1437       _cogl_winsys_texture_pixmap_x11_create,
1438     .texture_pixmap_x11_free =
1439       _cogl_winsys_texture_pixmap_x11_free,
1440     .texture_pixmap_x11_update =
1441       _cogl_winsys_texture_pixmap_x11_update,
1442     .texture_pixmap_x11_damage_notify =
1443       _cogl_winsys_texture_pixmap_x11_damage_notify,
1444     .texture_pixmap_x11_get_texture =
1445       _cogl_winsys_texture_pixmap_x11_get_texture,
1446   };
1447 
1448 /* XXX: we use a function because no doubt someone will complain
1449  * about using c99 member initializers because they aren't portable
1450  * to windows. We want to avoid having to rigidly follow the real
1451  * order of members since some members are #ifdefd and we'd have
1452  * to mirror the #ifdefing to add padding etc. For any winsys that
1453  * can assume the platform has a sane compiler then we can just use
1454  * c99 initializers for insane platforms they can initialize
1455  * the members by name in a function.
1456  */
1457 COGL_EXPORT const CoglWinsysVtable *
_cogl_winsys_glx_get_vtable(void)1458 _cogl_winsys_glx_get_vtable (void)
1459 {
1460   return &_cogl_winsys_vtable;
1461 }
1462