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