1 | /* GDK - The GIMP Drawing Kit |
2 | * |
3 | * gdkglcontext-glx.c: GLX specific wrappers |
4 | * |
5 | * SPDX-FileCopyrightText: 2014 Emmanuele Bassi |
6 | * SPDX-FileCopyrightText: 2021 GNOME Foundation |
7 | * |
8 | * SPDX-License-Identifier: LGPL-2.1-or-later |
9 | */ |
10 | |
11 | #include "config.h" |
12 | |
13 | #include "gdkglcontext-x11.h" |
14 | #include "gdkdisplay-x11.h" |
15 | #include "gdkprivate-x11.h" |
16 | #include "gdkscreen-x11.h" |
17 | |
18 | #include "gdkx11display.h" |
19 | #include "gdkx11glcontext.h" |
20 | #include "gdkx11screen.h" |
21 | #include "gdkx11surface.h" |
22 | #include "gdkx11property.h" |
23 | #include <X11/Xatom.h> |
24 | |
25 | #include "gdkprofilerprivate.h" |
26 | #include "gdkintl.h" |
27 | |
28 | #include <cairo-xlib.h> |
29 | |
30 | #include <epoxy/glx.h> |
31 | |
32 | struct _GdkX11GLContextGLX |
33 | { |
34 | GdkX11GLContext parent_instance; |
35 | |
36 | GLXContext glx_context; |
37 | |
38 | #ifdef HAVE_XDAMAGE |
39 | GLsync frame_fence; |
40 | Damage xdamage; |
41 | #endif |
42 | |
43 | guint do_frame_sync : 1; |
44 | }; |
45 | |
46 | typedef struct _GdkX11GLContextClass GdkX11GLContextGLXClass; |
47 | |
48 | G_DEFINE_TYPE (GdkX11GLContextGLX, gdk_x11_gl_context_glx, GDK_TYPE_X11_GL_CONTEXT) |
49 | |
50 | static GLXDrawable |
51 | gdk_x11_surface_get_glx_drawable (GdkSurface *surface) |
52 | { |
53 | GdkX11Surface *self = GDK_X11_SURFACE (surface); |
54 | GdkDisplay *display = gdk_surface_get_display (GDK_SURFACE (self)); |
55 | GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); |
56 | |
57 | if (self->glx_drawable) |
58 | return self->glx_drawable; |
59 | |
60 | self->glx_drawable = glXCreateWindow (gdk_x11_display_get_xdisplay (display), |
61 | display_x11->glx_config, |
62 | gdk_x11_surface_get_xid (surface), |
63 | NULL); |
64 | |
65 | return self->glx_drawable; |
66 | } |
67 | |
68 | void |
69 | gdk_x11_surface_destroy_glx_drawable (GdkX11Surface *self) |
70 | { |
71 | if (self->glx_drawable == None) |
72 | return; |
73 | |
74 | gdk_gl_context_clear_current_if_surface (GDK_SURFACE (self)); |
75 | |
76 | glXDestroyWindow (gdk_x11_display_get_xdisplay (display: gdk_surface_get_display (GDK_SURFACE (self))), |
77 | self->glx_drawable); |
78 | |
79 | self->glx_drawable = None; |
80 | } |
81 | |
82 | static void |
83 | maybe_wait_for_vblank (GdkDisplay *display, |
84 | GLXDrawable drawable) |
85 | { |
86 | GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); |
87 | Display *dpy = gdk_x11_display_get_xdisplay (display); |
88 | |
89 | if (display_x11->has_glx_sync_control) |
90 | { |
91 | gint64 ust, msc, sbc; |
92 | |
93 | glXGetSyncValuesOML (dpy, drawable, &ust, &msc, &sbc); |
94 | glXWaitForMscOML (dpy, drawable, |
95 | 0, 2, (msc + 1) % 2, |
96 | &ust, &msc, &sbc); |
97 | } |
98 | else if (display_x11->has_glx_video_sync) |
99 | { |
100 | guint32 current_count; |
101 | |
102 | glXGetVideoSyncSGI (¤t_count); |
103 | glXWaitVideoSyncSGI (2, (current_count + 1) % 2, ¤t_count); |
104 | } |
105 | } |
106 | |
107 | static GLXDrawable |
108 | gdk_x11_gl_context_glx_get_drawable (GdkX11GLContextGLX *self) |
109 | { |
110 | GdkDrawContext *draw_context = GDK_DRAW_CONTEXT (self); |
111 | GdkSurface *surface; |
112 | |
113 | if (gdk_draw_context_is_in_frame (context: draw_context)) |
114 | surface = gdk_draw_context_get_surface (context: draw_context); |
115 | else |
116 | surface = GDK_X11_DISPLAY (gdk_draw_context_get_display (draw_context))->leader_gdk_surface; |
117 | |
118 | return gdk_x11_surface_get_glx_drawable (surface); |
119 | } |
120 | |
121 | static void |
122 | gdk_x11_gl_context_glx_end_frame (GdkDrawContext *draw_context, |
123 | cairo_region_t *painted) |
124 | { |
125 | GdkX11GLContextGLX *self = GDK_X11_GL_CONTEXT_GLX (draw_context); |
126 | GdkGLContext *context = GDK_GL_CONTEXT (draw_context); |
127 | GdkSurface *surface = gdk_gl_context_get_surface (context); |
128 | GdkX11Surface *x11_surface = GDK_X11_SURFACE (surface); |
129 | GdkDisplay *display = gdk_gl_context_get_display (context); |
130 | Display *dpy = gdk_x11_display_get_xdisplay (display); |
131 | GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); |
132 | GLXDrawable drawable; |
133 | |
134 | GDK_DRAW_CONTEXT_CLASS (gdk_x11_gl_context_glx_parent_class)->end_frame (draw_context, painted); |
135 | |
136 | gdk_gl_context_make_current (context); |
137 | |
138 | drawable = gdk_x11_surface_get_glx_drawable (surface); |
139 | |
140 | GDK_DISPLAY_NOTE (display, OPENGL, |
141 | g_message ("Flushing GLX buffers for drawable %lu (window: %lu), frame sync: %s" , |
142 | (unsigned long) drawable, |
143 | (unsigned long) gdk_x11_surface_get_xid (surface), |
144 | self->do_frame_sync ? "yes" : "no" )); |
145 | |
146 | gdk_profiler_add_mark (GDK_PROFILER_CURRENT_TIME, 0, "x11" , "swap buffers" ); |
147 | |
148 | /* if we are going to wait for the vertical refresh manually |
149 | * we need to flush pending redraws, and we also need to wait |
150 | * for that to finish, otherwise we are going to tear. |
151 | * |
152 | * obviously, this condition should not be hit if we have |
153 | * GLX_SGI_swap_control, and we ask the driver to do the right |
154 | * thing. |
155 | */ |
156 | if (self->do_frame_sync) |
157 | { |
158 | guint32 end_frame_counter = 0; |
159 | gboolean has_counter = display_x11->has_glx_video_sync; |
160 | gboolean can_wait = display_x11->has_glx_video_sync || display_x11->has_glx_sync_control; |
161 | |
162 | if (display_x11->has_glx_video_sync) |
163 | glXGetVideoSyncSGI (&end_frame_counter); |
164 | |
165 | if (self->do_frame_sync && !display_x11->has_glx_swap_interval) |
166 | { |
167 | glFinish (); |
168 | |
169 | if (has_counter && can_wait) |
170 | { |
171 | if (x11_surface->glx_frame_counter == end_frame_counter) |
172 | maybe_wait_for_vblank (display, drawable); |
173 | } |
174 | else if (can_wait) |
175 | maybe_wait_for_vblank (display, drawable); |
176 | } |
177 | } |
178 | |
179 | gdk_x11_surface_pre_damage (surface); |
180 | |
181 | #ifdef HAVE_XDAMAGE |
182 | if (self->xdamage != 0 && _gdk_x11_surface_syncs_frames (surface)) |
183 | { |
184 | g_assert (self->frame_fence == 0); |
185 | |
186 | self->frame_fence = glFenceSync (GL_SYNC_GPU_COMMANDS_COMPLETE, 0); |
187 | |
188 | /* We consider the frame still getting painted until the GL operation is |
189 | * finished, and the window gets damage reported from the X server. |
190 | * It's only at this point the compositor can be sure it has full |
191 | * access to the new updates. |
192 | */ |
193 | _gdk_x11_surface_set_frame_still_painting (surface, TRUE); |
194 | } |
195 | #endif |
196 | |
197 | glXSwapBuffers (dpy, drawable); |
198 | |
199 | if (self->do_frame_sync && display_x11->has_glx_video_sync) |
200 | glXGetVideoSyncSGI (&x11_surface->glx_frame_counter); |
201 | } |
202 | |
203 | static gboolean |
204 | gdk_x11_gl_context_glx_clear_current (GdkGLContext *context) |
205 | { |
206 | GdkDisplay *display = gdk_gl_context_get_display (context); |
207 | Display *dpy = gdk_x11_display_get_xdisplay (display); |
208 | |
209 | glXMakeContextCurrent (dpy, None, None, NULL); |
210 | return TRUE; |
211 | } |
212 | |
213 | static gboolean |
214 | gdk_x11_gl_context_glx_make_current (GdkGLContext *context, |
215 | gboolean surfaceless) |
216 | |
217 | { |
218 | GdkX11GLContextGLX *self = GDK_X11_GL_CONTEXT_GLX (context); |
219 | GdkDisplay *display = gdk_gl_context_get_display (context); |
220 | Display *dpy = gdk_x11_display_get_xdisplay (display); |
221 | gboolean do_frame_sync = FALSE; |
222 | GdkSurface *surface; |
223 | GLXWindow drawable; |
224 | |
225 | if (!surfaceless) |
226 | surface = gdk_gl_context_get_surface (context); |
227 | else |
228 | surface = GDK_X11_DISPLAY (display)->leader_gdk_surface; |
229 | drawable = gdk_x11_surface_get_glx_drawable (surface); |
230 | |
231 | GDK_DISPLAY_NOTE (display, OPENGL, |
232 | g_message ("Making GLX context %p current to drawable %lu" , |
233 | context, (unsigned long) drawable)); |
234 | |
235 | /* Work around a glitch, see |
236 | * https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/5281 |
237 | */ |
238 | if (glXGetCurrentContext () != self->glx_context) |
239 | glXMakeContextCurrent (dpy, None, None, NULL); |
240 | |
241 | if (!glXMakeContextCurrent (dpy, drawable, drawable, self->glx_context)) |
242 | return FALSE; |
243 | |
244 | if (!surfaceless && GDK_X11_DISPLAY (display)->has_glx_swap_interval) |
245 | { |
246 | /* If the WM is compositing there is no particular need to delay |
247 | * the swap when drawing on the offscreen, rendering to the screen |
248 | * happens later anyway, and its up to the compositor to sync that |
249 | * to the vblank. */ |
250 | do_frame_sync = ! gdk_display_is_composited (display); |
251 | |
252 | if (do_frame_sync != self->do_frame_sync) |
253 | { |
254 | self->do_frame_sync = do_frame_sync; |
255 | |
256 | if (do_frame_sync) |
257 | glXSwapIntervalSGI (1); |
258 | else |
259 | glXSwapIntervalSGI (0); |
260 | } |
261 | } |
262 | |
263 | return TRUE; |
264 | } |
265 | |
266 | static cairo_region_t * |
267 | gdk_x11_gl_context_glx_get_damage (GdkGLContext *context) |
268 | { |
269 | GdkDisplay *display = gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context)); |
270 | GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); |
271 | Display *dpy = gdk_x11_display_get_xdisplay (display); |
272 | unsigned int buffer_age = 0; |
273 | |
274 | if (display_x11->has_glx_buffer_age) |
275 | { |
276 | GdkX11GLContextGLX *self = GDK_X11_GL_CONTEXT_GLX (context); |
277 | |
278 | gdk_gl_context_make_current (context); |
279 | glXQueryDrawable (dpy, gdk_x11_gl_context_glx_get_drawable (self), |
280 | GLX_BACK_BUFFER_AGE_EXT, &buffer_age); |
281 | |
282 | switch (buffer_age) |
283 | { |
284 | case 1: |
285 | return cairo_region_create (); |
286 | break; |
287 | |
288 | case 2: |
289 | if (context->old_updated_area[0]) |
290 | return cairo_region_copy (original: context->old_updated_area[0]); |
291 | break; |
292 | |
293 | case 3: |
294 | if (context->old_updated_area[0] && |
295 | context->old_updated_area[1]) |
296 | { |
297 | cairo_region_t *damage = cairo_region_copy (original: context->old_updated_area[0]); |
298 | cairo_region_union (dst: damage, other: context->old_updated_area[1]); |
299 | return damage; |
300 | } |
301 | break; |
302 | |
303 | default: |
304 | ; |
305 | } |
306 | } |
307 | |
308 | return GDK_GL_CONTEXT_CLASS (gdk_x11_gl_context_glx_parent_class)->get_damage (context); |
309 | } |
310 | |
311 | static GLXContext |
312 | create_gl3_context (GdkDisplay *display, |
313 | GLXFBConfig config, |
314 | GdkGLContext *share, |
315 | int profile, |
316 | int flags, |
317 | int major, |
318 | int minor) |
319 | { |
320 | int attrib_list[] = { |
321 | GLX_CONTEXT_PROFILE_MASK_ARB, profile, |
322 | GLX_CONTEXT_MAJOR_VERSION_ARB, major, |
323 | GLX_CONTEXT_MINOR_VERSION_ARB, minor, |
324 | GLX_CONTEXT_FLAGS_ARB, flags, |
325 | None, |
326 | }; |
327 | GLXContext res; |
328 | |
329 | GdkX11GLContextGLX *share_glx = NULL; |
330 | |
331 | if (share != NULL) |
332 | share_glx = GDK_X11_GL_CONTEXT_GLX (share); |
333 | |
334 | gdk_x11_display_error_trap_push (display); |
335 | |
336 | res = glXCreateContextAttribsARB (gdk_x11_display_get_xdisplay (display), |
337 | config, |
338 | share_glx != NULL ? share_glx->glx_context : NULL, |
339 | True, |
340 | attrib_list); |
341 | |
342 | if (gdk_x11_display_error_trap_pop (display)) |
343 | return NULL; |
344 | |
345 | return res; |
346 | } |
347 | |
348 | static GLXContext |
349 | create_legacy_context (GdkDisplay *display, |
350 | GLXFBConfig config, |
351 | GdkGLContext *share) |
352 | { |
353 | GdkX11GLContextGLX *share_glx = NULL; |
354 | GLXContext res; |
355 | |
356 | if (share != NULL) |
357 | share_glx = GDK_X11_GL_CONTEXT_GLX (share); |
358 | |
359 | gdk_x11_display_error_trap_push (display); |
360 | |
361 | res = glXCreateNewContext (gdk_x11_display_get_xdisplay (display), |
362 | config, |
363 | GLX_RGBA_TYPE, |
364 | share_glx != NULL ? share_glx->glx_context : NULL, |
365 | TRUE); |
366 | |
367 | if (gdk_x11_display_error_trap_pop (display)) |
368 | return NULL; |
369 | |
370 | return res; |
371 | } |
372 | |
373 | #ifdef HAVE_XDAMAGE |
374 | static void |
375 | bind_context_for_frame_fence (GdkX11GLContextGLX *self) |
376 | { |
377 | GdkX11GLContextGLX *current_context_glx; |
378 | GLXContext current_glx_context = NULL; |
379 | GdkGLContext *current_context; |
380 | gboolean needs_binding = TRUE; |
381 | |
382 | /* We don't care if the passed context is the current context, |
383 | * necessarily, but we do care that *some* context that can |
384 | * see the sync object is bound. |
385 | * |
386 | * If no context is bound at all, the GL dispatch layer will |
387 | * make glClientWaitSync() silently return 0. |
388 | */ |
389 | current_glx_context = glXGetCurrentContext (); |
390 | |
391 | if (current_glx_context == NULL) |
392 | goto out; |
393 | |
394 | current_context = gdk_gl_context_get_current (); |
395 | |
396 | if (current_context == NULL) |
397 | goto out; |
398 | |
399 | current_context_glx = GDK_X11_GL_CONTEXT_GLX (current_context); |
400 | |
401 | /* If the GLX context was changed out from under GDK, then |
402 | * that context may not be one that is able to see the |
403 | * created fence object. |
404 | */ |
405 | if (current_context_glx->glx_context != current_glx_context) |
406 | goto out; |
407 | |
408 | needs_binding = FALSE; |
409 | |
410 | out: |
411 | if (needs_binding) |
412 | gdk_gl_context_make_current (GDK_GL_CONTEXT (self)); |
413 | } |
414 | |
415 | static void |
416 | finish_frame (GdkGLContext *context) |
417 | { |
418 | GdkX11GLContextGLX *context_glx = GDK_X11_GL_CONTEXT_GLX (context); |
419 | GdkSurface *surface = gdk_gl_context_get_surface (context); |
420 | |
421 | if (context_glx->xdamage == 0) |
422 | return; |
423 | |
424 | if (context_glx->frame_fence == 0) |
425 | return; |
426 | |
427 | glDeleteSync (context_glx->frame_fence); |
428 | context_glx->frame_fence = 0; |
429 | |
430 | _gdk_x11_surface_set_frame_still_painting (surface, FALSE); |
431 | } |
432 | |
433 | static gboolean |
434 | on_gl_surface_xevent (GdkGLContext *context, |
435 | XEvent *xevent, |
436 | GdkX11Display *display_x11) |
437 | { |
438 | GdkX11GLContextGLX *context_glx = GDK_X11_GL_CONTEXT_GLX (context); |
439 | XDamageNotifyEvent *damage_xevent; |
440 | |
441 | if (xevent->type != (display_x11->damage_event_base + XDamageNotify)) |
442 | return FALSE; |
443 | |
444 | damage_xevent = (XDamageNotifyEvent *) xevent; |
445 | |
446 | if (damage_xevent->damage != context_glx->xdamage) |
447 | return FALSE; |
448 | |
449 | if (context_glx->frame_fence) |
450 | { |
451 | GLenum wait_result; |
452 | |
453 | bind_context_for_frame_fence (self: context_glx); |
454 | |
455 | wait_result = glClientWaitSync (context_glx->frame_fence, 0, 0); |
456 | |
457 | switch (wait_result) |
458 | { |
459 | /* We assume that if the fence has been signaled, that this damage |
460 | * event is the damage event that was triggered by the GL drawing |
461 | * associated with the fence. That's, technically, not necessarly |
462 | * always true. The X server could have generated damage for |
463 | * an unrelated event (say the size of the window changing), at |
464 | * just the right moment such that we're picking it up instead. |
465 | * |
466 | * We're choosing not to handle this edge case, but if it does ever |
467 | * happen in the wild, it could lead to slight underdrawing by |
468 | * the compositor for one frame. In the future, if we find out |
469 | * this edge case is noticeable, we can compensate by copying the |
470 | * painted region from gdk_x11_gl_context_end_frame and subtracting |
471 | * damaged areas from the copy as they come in. Once the copied |
472 | * region goes empty, we know that there won't be any underdraw, |
473 | * and can mark painting has finished. It's not worth the added |
474 | * complexity and resource usage to do this bookkeeping, however, |
475 | * unless the problem is practically visible. |
476 | */ |
477 | case GL_ALREADY_SIGNALED: |
478 | case GL_CONDITION_SATISFIED: |
479 | case GL_WAIT_FAILED: |
480 | if (wait_result == GL_WAIT_FAILED) |
481 | g_warning ("failed to wait on GL fence associated with last swap buffers call" ); |
482 | finish_frame (context); |
483 | break; |
484 | |
485 | /* We assume that if the fence hasn't been signaled, that this |
486 | * damage event is not the damage event that was triggered by the |
487 | * GL drawing associated with the fence. That's only true for |
488 | * the Nvidia vendor driver. When using open source drivers, damage |
489 | * is emitted immediately on swap buffers, before the fence ever |
490 | * has a chance to signal. |
491 | */ |
492 | case GL_TIMEOUT_EXPIRED: |
493 | break; |
494 | default: |
495 | g_error ("glClientWaitSync returned unexpected result: %x" , (guint) wait_result); |
496 | } |
497 | } |
498 | |
499 | return FALSE; |
500 | } |
501 | |
502 | static void |
503 | on_surface_state_changed (GdkGLContext *context) |
504 | { |
505 | GdkSurface *surface = gdk_gl_context_get_surface (context); |
506 | |
507 | if (GDK_SURFACE_IS_MAPPED (surface)) |
508 | return; |
509 | |
510 | /* If we're about to withdraw the surface, then we don't care if the frame is |
511 | * still getting rendered by the GPU. The compositor is going to remove the surface |
512 | * from the scene anyway, so wrap up the frame. |
513 | */ |
514 | finish_frame (context); |
515 | } |
516 | #endif |
517 | |
518 | static GdkGLAPI |
519 | gdk_x11_gl_context_glx_realize (GdkGLContext *context, |
520 | GError **error) |
521 | { |
522 | GdkX11Display *display_x11; |
523 | GdkDisplay *display; |
524 | GdkX11GLContextGLX *context_glx; |
525 | Display *dpy; |
526 | GdkSurface *surface; |
527 | GdkGLContext *share; |
528 | gboolean debug_bit, compat_bit, legacy_bit; |
529 | int major, minor, flags; |
530 | GdkGLAPI api = 0; |
531 | |
532 | display = gdk_gl_context_get_display (context); |
533 | dpy = gdk_x11_display_get_xdisplay (display); |
534 | context_glx = GDK_X11_GL_CONTEXT_GLX (context); |
535 | display_x11 = GDK_X11_DISPLAY (display); |
536 | share = gdk_display_get_gl_context (display); |
537 | surface = gdk_gl_context_get_surface (context); |
538 | |
539 | gdk_gl_context_get_required_version (context, major: &major, minor: &minor); |
540 | debug_bit = gdk_gl_context_get_debug_enabled (context); |
541 | compat_bit = gdk_gl_context_get_forward_compatible (context); |
542 | |
543 | /* If there is no glXCreateContextAttribsARB() then we default to legacy */ |
544 | legacy_bit = !display_x11->has_glx_create_context || GDK_DISPLAY_DEBUG_CHECK (display, GL_LEGACY); |
545 | |
546 | /* We cannot share legacy contexts with core profile ones, so the |
547 | * shared context is the one that decides if we're going to create |
548 | * a legacy context or not. |
549 | */ |
550 | if (share != NULL && gdk_gl_context_is_legacy (context: share)) |
551 | legacy_bit = TRUE; |
552 | |
553 | flags = 0; |
554 | if (debug_bit) |
555 | flags |= GLX_CONTEXT_DEBUG_BIT_ARB; |
556 | if (compat_bit) |
557 | flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; |
558 | |
559 | GDK_DISPLAY_NOTE (display, OPENGL, |
560 | g_message ("Creating GLX context (GL version:%d.%d, debug:%s, forward:%s, legacy:%s, GL:%s, GLES:%s)" , |
561 | major, minor, |
562 | debug_bit ? "yes" : "no" , |
563 | compat_bit ? "yes" : "no" , |
564 | legacy_bit ? "yes" : "no" , |
565 | gdk_gl_context_is_api_allowed (context, GDK_GL_API_GL, NULL) ? "yes" : "no" , |
566 | gdk_gl_context_is_api_allowed (context, GDK_GL_API_GLES, NULL) ? "yes" : "no" )); |
567 | |
568 | /* If we have access to GLX_ARB_create_context_profile then we can ask for |
569 | * a compatibility profile; if we don't, then we have to fall back to the |
570 | * old GLX 1.3 API. |
571 | */ |
572 | if (legacy_bit && !GDK_X11_DISPLAY (display)->has_glx_create_context) |
573 | { |
574 | GDK_DISPLAY_NOTE (display, OPENGL, g_message ("Creating legacy GL context on request" )); |
575 | /* do it below */ |
576 | } |
577 | else |
578 | { |
579 | if (gdk_gl_context_is_api_allowed (self: context, api: GDK_GL_API_GL, NULL)) |
580 | { |
581 | int profile = legacy_bit ? GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB |
582 | : GLX_CONTEXT_CORE_PROFILE_BIT_ARB; |
583 | |
584 | /* We need to tweak the version, otherwise we may end up requesting |
585 | * a compatibility context with a minimum version of 3.2, which is |
586 | * an error |
587 | */ |
588 | if (legacy_bit) |
589 | { |
590 | major = 3; |
591 | minor = 0; |
592 | } |
593 | |
594 | GDK_DISPLAY_NOTE (display, OPENGL, g_message ("Creating GL3 context" )); |
595 | context_glx->glx_context = create_gl3_context (display, |
596 | config: display_x11->glx_config, |
597 | share, |
598 | profile, |
599 | flags, major, minor); |
600 | api = GDK_GL_API_GL; |
601 | } |
602 | |
603 | if (context_glx->glx_context == NULL && !legacy_bit && |
604 | gdk_gl_context_is_api_allowed (self: context, api: GDK_GL_API_GLES, NULL)) |
605 | { |
606 | GDK_DISPLAY_NOTE (display, OPENGL, g_message ("Creating GL3 GLES context" )); |
607 | context_glx->glx_context = create_gl3_context (display, |
608 | config: display_x11->glx_config, |
609 | share, |
610 | GLX_CONTEXT_ES2_PROFILE_BIT_EXT, |
611 | flags, major, minor); |
612 | api = GDK_GL_API_GLES; |
613 | } |
614 | } |
615 | |
616 | /* Fall back to legacy in case the GL3 context creation failed */ |
617 | if (context_glx->glx_context == NULL && |
618 | gdk_gl_context_is_api_allowed (self: context, api: GDK_GL_API_GL, NULL)) |
619 | { |
620 | GDK_DISPLAY_NOTE (display, OPENGL, g_message ("Creating fallback legacy context" )); |
621 | context_glx->glx_context = create_legacy_context (display, config: display_x11->glx_config, share); |
622 | legacy_bit = TRUE; |
623 | api = GDK_GL_API_GL; |
624 | } |
625 | |
626 | if (context_glx->glx_context == NULL) |
627 | { |
628 | g_set_error_literal (err: error, GDK_GL_ERROR, |
629 | code: GDK_GL_ERROR_NOT_AVAILABLE, |
630 | _("Unable to create a GL context" )); |
631 | return 0; |
632 | } |
633 | |
634 | /* Ensure that any other context is created with a legacy bit set */ |
635 | gdk_gl_context_set_is_legacy (context, is_legacy: legacy_bit); |
636 | |
637 | GDK_DISPLAY_NOTE (display, OPENGL, |
638 | g_message ("Realized GLX context[%p], %s, version: %d.%d" , |
639 | context_glx->glx_context, |
640 | glXIsDirect (dpy, context_glx->glx_context) ? "direct" : "indirect" , |
641 | display_x11->glx_version / 10, |
642 | display_x11->glx_version % 10)); |
643 | |
644 | #ifdef HAVE_XDAMAGE |
645 | if (display_x11->have_damage && |
646 | display_x11->has_async_glx_swap_buffers) |
647 | { |
648 | gdk_x11_display_error_trap_push (display); |
649 | context_glx->xdamage = XDamageCreate (dpy, |
650 | drawable: gdk_x11_surface_get_xid (surface), |
651 | XDamageReportRawRectangles); |
652 | if (gdk_x11_display_error_trap_pop (display)) |
653 | { |
654 | context_glx->xdamage = 0; |
655 | } |
656 | else |
657 | { |
658 | g_signal_connect_object (G_OBJECT (display), |
659 | detailed_signal: "xevent" , |
660 | G_CALLBACK (on_gl_surface_xevent), |
661 | gobject: context, |
662 | connect_flags: G_CONNECT_SWAPPED); |
663 | g_signal_connect_object (G_OBJECT (surface), |
664 | detailed_signal: "notify::state" , |
665 | G_CALLBACK (on_surface_state_changed), |
666 | gobject: context, |
667 | connect_flags: G_CONNECT_SWAPPED); |
668 | |
669 | } |
670 | } |
671 | #endif |
672 | |
673 | return api; |
674 | } |
675 | |
676 | static void |
677 | gdk_x11_gl_context_glx_dispose (GObject *gobject) |
678 | { |
679 | GdkX11GLContextGLX *context_glx = GDK_X11_GL_CONTEXT_GLX (gobject); |
680 | |
681 | #ifdef HAVE_XDAMAGE |
682 | context_glx->xdamage = 0; |
683 | #endif |
684 | |
685 | if (context_glx->glx_context != NULL) |
686 | { |
687 | GdkGLContext *context = GDK_GL_CONTEXT (gobject); |
688 | GdkDisplay *display = gdk_gl_context_get_display (context); |
689 | Display *dpy = gdk_x11_display_get_xdisplay (display); |
690 | |
691 | if (glXGetCurrentContext () == context_glx->glx_context) |
692 | glXMakeContextCurrent (dpy, None, None, NULL); |
693 | |
694 | GDK_DISPLAY_NOTE (display, OPENGL, g_message ("Destroying GLX context" )); |
695 | glXDestroyContext (dpy, context_glx->glx_context); |
696 | context_glx->glx_context = NULL; |
697 | } |
698 | |
699 | G_OBJECT_CLASS (gdk_x11_gl_context_glx_parent_class)->dispose (gobject); |
700 | } |
701 | |
702 | static void |
703 | gdk_x11_gl_context_glx_class_init (GdkX11GLContextGLXClass *klass) |
704 | { |
705 | GdkGLContextClass *context_class = GDK_GL_CONTEXT_CLASS (klass); |
706 | GdkDrawContextClass *draw_context_class = GDK_DRAW_CONTEXT_CLASS (klass); |
707 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
708 | |
709 | context_class->backend_type = GDK_GL_GLX; |
710 | |
711 | context_class->realize = gdk_x11_gl_context_glx_realize; |
712 | context_class->make_current = gdk_x11_gl_context_glx_make_current; |
713 | context_class->clear_current = gdk_x11_gl_context_glx_clear_current; |
714 | context_class->get_damage = gdk_x11_gl_context_glx_get_damage; |
715 | |
716 | draw_context_class->end_frame = gdk_x11_gl_context_glx_end_frame; |
717 | |
718 | gobject_class->dispose = gdk_x11_gl_context_glx_dispose; |
719 | } |
720 | |
721 | static void |
722 | gdk_x11_gl_context_glx_init (GdkX11GLContextGLX *self) |
723 | { |
724 | self->do_frame_sync = TRUE; |
725 | } |
726 | |
727 | static gboolean |
728 | visual_is_rgba (XVisualInfo *visinfo) |
729 | { |
730 | return |
731 | visinfo->depth == 32 && |
732 | visinfo->visual->red_mask == 0xff0000 && |
733 | visinfo->visual->green_mask == 0x00ff00 && |
734 | visinfo->visual->blue_mask == 0x0000ff; |
735 | } |
736 | |
737 | #define MAX_GLX_ATTRS 30 |
738 | |
739 | static gboolean |
740 | gdk_x11_display_create_glx_config (GdkX11Display *self, |
741 | Visual **out_visual, |
742 | int *out_depth, |
743 | GError **error) |
744 | { |
745 | GdkDisplay *display = GDK_DISPLAY (self); |
746 | Display *dpy = gdk_x11_display_get_xdisplay (display); |
747 | int attrs[MAX_GLX_ATTRS]; |
748 | GLXFBConfig *configs; |
749 | int count; |
750 | enum { |
751 | NO_VISUAL_FOUND, |
752 | WITH_MULTISAMPLING, |
753 | WITH_STENCIL_AND_DEPTH_BUFFER, |
754 | NO_ALPHA, |
755 | NO_ALPHA_VISUAL, |
756 | PERFECT |
757 | } best_features; |
758 | int i = 0; |
759 | |
760 | attrs[i++] = GLX_DRAWABLE_TYPE; |
761 | attrs[i++] = GLX_WINDOW_BIT; |
762 | |
763 | attrs[i++] = GLX_RENDER_TYPE; |
764 | attrs[i++] = GLX_RGBA_BIT; |
765 | |
766 | attrs[i++] = GLX_DOUBLEBUFFER; |
767 | attrs[i++] = GL_TRUE; |
768 | |
769 | attrs[i++] = GLX_RED_SIZE; |
770 | attrs[i++] = 1; |
771 | attrs[i++] = GLX_GREEN_SIZE; |
772 | attrs[i++] = 1; |
773 | attrs[i++] = GLX_BLUE_SIZE; |
774 | attrs[i++] = 1; |
775 | attrs[i++] = GLX_ALPHA_SIZE; |
776 | attrs[i++] = 1; |
777 | |
778 | attrs[i++] = None; |
779 | g_assert (i < MAX_GLX_ATTRS); |
780 | |
781 | configs = glXChooseFBConfig (dpy, DefaultScreen (dpy), attrs, &count); |
782 | if (configs == NULL || count == 0) |
783 | { |
784 | g_set_error_literal (err: error, GDK_GL_ERROR, |
785 | code: GDK_GL_ERROR_NOT_AVAILABLE, |
786 | _("No GLX configurations available" )); |
787 | return FALSE; |
788 | } |
789 | |
790 | best_features = NO_VISUAL_FOUND; |
791 | |
792 | for (i = 0; i < count; i++) |
793 | { |
794 | XVisualInfo *visinfo; |
795 | int tmp; |
796 | |
797 | visinfo = glXGetVisualFromFBConfig (dpy, configs[i]); |
798 | if (visinfo == NULL) |
799 | continue; |
800 | |
801 | if (glXGetFBConfigAttrib (dpy, configs[i], GLX_SAMPLE_BUFFERS_ARB, &tmp) != Success || tmp != 0) |
802 | { |
803 | if (best_features < WITH_MULTISAMPLING) |
804 | { |
805 | GDK_NOTE (OPENGL, g_message ("Best GLX config is %u for visual 0x%lX with multisampling" , i, visinfo->visualid)); |
806 | best_features = WITH_MULTISAMPLING; |
807 | *out_visual = visinfo->visual; |
808 | *out_depth = visinfo->depth; |
809 | self->glx_config = configs[i]; |
810 | } |
811 | XFree (visinfo); |
812 | continue; |
813 | } |
814 | |
815 | if (glXGetFBConfigAttrib (dpy, configs[i], GLX_DEPTH_SIZE, &tmp) != Success || tmp != 0 || |
816 | glXGetFBConfigAttrib (dpy, configs[i], GLX_STENCIL_SIZE, &tmp) != Success || tmp != 0) |
817 | { |
818 | if (best_features < WITH_STENCIL_AND_DEPTH_BUFFER) |
819 | { |
820 | GDK_NOTE (OPENGL, g_message ("Best GLX config is %u for visual 0x%lX with a stencil or depth buffer" , i, visinfo->visualid)); |
821 | best_features = WITH_STENCIL_AND_DEPTH_BUFFER; |
822 | *out_visual = visinfo->visual; |
823 | *out_depth = visinfo->depth; |
824 | self->glx_config = configs[i]; |
825 | } |
826 | XFree (visinfo); |
827 | continue; |
828 | } |
829 | |
830 | if (!visual_is_rgba (visinfo)) |
831 | { |
832 | if (best_features < NO_ALPHA_VISUAL) |
833 | { |
834 | GDK_NOTE (OPENGL, g_message ("Best GLX config is %u for visual 0x%lX with no RGBA Visual" , i, visinfo->visualid)); |
835 | best_features = NO_ALPHA_VISUAL; |
836 | *out_visual = visinfo->visual; |
837 | *out_depth = visinfo->depth; |
838 | self->glx_config = configs[i]; |
839 | } |
840 | XFree (visinfo); |
841 | continue; |
842 | } |
843 | |
844 | GDK_NOTE (OPENGL, g_message ("GLX config %u for visual 0x%lX is the perfect choice" , i, visinfo->visualid)); |
845 | best_features = PERFECT; |
846 | *out_visual = visinfo->visual; |
847 | *out_depth = visinfo->depth; |
848 | self->glx_config = configs[i]; |
849 | XFree (visinfo); |
850 | break; |
851 | } |
852 | |
853 | XFree (configs); |
854 | |
855 | if (best_features == NO_VISUAL_FOUND) |
856 | { |
857 | g_set_error_literal (err: error, GDK_GL_ERROR, |
858 | code: GDK_GL_ERROR_NOT_AVAILABLE, |
859 | _("No GLX configuration with required features found" )); |
860 | return FALSE; |
861 | } |
862 | |
863 | return TRUE; |
864 | } |
865 | |
866 | #undef MAX_GLX_ATTRS |
867 | |
868 | /** |
869 | * gdk_x11_display_get_glx_version: |
870 | * @display: (type GdkX11Display): a `GdkDisplay` |
871 | * @major: (out): return location for the GLX major version |
872 | * @minor: (out): return location for the GLX minor version |
873 | * |
874 | * Retrieves the version of the GLX implementation. |
875 | * |
876 | * Returns: %TRUE if GLX is available |
877 | */ |
878 | gboolean |
879 | gdk_x11_display_get_glx_version (GdkDisplay *display, |
880 | int *major, |
881 | int *minor) |
882 | { |
883 | g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE); |
884 | |
885 | if (!GDK_IS_X11_DISPLAY (display)) |
886 | return FALSE; |
887 | |
888 | GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); |
889 | |
890 | if (display_x11->glx_config == NULL) |
891 | return FALSE; |
892 | |
893 | if (major != NULL) |
894 | *major = display_x11->glx_version / 10; |
895 | if (minor != NULL) |
896 | *minor = display_x11->glx_version % 10; |
897 | |
898 | return TRUE; |
899 | } |
900 | |
901 | /*< private > |
902 | * gdk_x11_display_init_glx: |
903 | * @display_x11: an X11 display that has not been inited yet. |
904 | * @out_visual: set to the Visual to be used with the returned config |
905 | * @out_depth: set to the depth to be used with the returned config |
906 | * @error: Return location for error |
907 | * |
908 | * Initializes the cached GLX state for the given @screen. |
909 | * |
910 | * This function must be called exactly once during initialization. |
911 | * |
912 | * Returns: %TRUE if GLX was initialized |
913 | */ |
914 | gboolean |
915 | gdk_x11_display_init_glx (GdkX11Display *display_x11, |
916 | Visual **out_visual, |
917 | int *out_depth, |
918 | GError **error) |
919 | { |
920 | GdkDisplay *display = GDK_DISPLAY (display_x11); |
921 | Display *dpy; |
922 | int screen_num; |
923 | |
924 | if (!gdk_gl_backend_can_be_used (backend_type: GDK_GL_GLX, error)) |
925 | return FALSE; |
926 | |
927 | dpy = gdk_x11_display_get_xdisplay (display); |
928 | |
929 | if (!epoxy_has_glx (dpy)) |
930 | { |
931 | g_set_error_literal (err: error, GDK_GL_ERROR, |
932 | code: GDK_GL_ERROR_NOT_AVAILABLE, |
933 | _("GLX is not supported" )); |
934 | return FALSE; |
935 | } |
936 | |
937 | screen_num = display_x11->screen->screen_num; |
938 | |
939 | display_x11->glx_version = epoxy_glx_version (dpy, screen: screen_num); |
940 | |
941 | display_x11->has_glx_create_context = |
942 | epoxy_has_glx_extension (dpy, screen: screen_num, extension: "GLX_ARB_create_context_profile" ); |
943 | display_x11->has_glx_create_es2_context = |
944 | epoxy_has_glx_extension (dpy, screen: screen_num, extension: "GLX_EXT_create_context_es2_profile" ); |
945 | display_x11->has_glx_swap_interval = |
946 | epoxy_has_glx_extension (dpy, screen: screen_num, extension: "GLX_SGI_swap_control" ); |
947 | display_x11->has_glx_texture_from_pixmap = |
948 | epoxy_has_glx_extension (dpy, screen: screen_num, extension: "GLX_EXT_texture_from_pixmap" ); |
949 | display_x11->has_glx_video_sync = |
950 | epoxy_has_glx_extension (dpy, screen: screen_num, extension: "GLX_SGI_video_sync" ); |
951 | display_x11->has_glx_buffer_age = |
952 | epoxy_has_glx_extension (dpy, screen: screen_num, extension: "GLX_EXT_buffer_age" ); |
953 | display_x11->has_glx_sync_control = |
954 | epoxy_has_glx_extension (dpy, screen: screen_num, extension: "GLX_OML_sync_control" ); |
955 | display_x11->has_glx_multisample = |
956 | epoxy_has_glx_extension (dpy, screen: screen_num, extension: "GLX_ARB_multisample" ); |
957 | display_x11->has_glx_visual_rating = |
958 | epoxy_has_glx_extension (dpy, screen: screen_num, extension: "GLX_EXT_visual_rating" ); |
959 | |
960 | if (g_strcmp0 (glXGetClientString (dpy, GLX_VENDOR), str2: "NVIDIA Corporation" ) == 0) |
961 | { |
962 | Atom type; |
963 | int format; |
964 | gulong nitems; |
965 | gulong bytes_after; |
966 | guchar *data = NULL; |
967 | |
968 | /* With the mesa based drivers, we can safely assume the compositor can |
969 | * access the updated surface texture immediately after glXSwapBuffers is |
970 | * run, because the kernel ensures there is an implicit synchronization |
971 | * operation upon texture access. This is not true with the Nvidia vendor |
972 | * driver. There is a window of time after glXSwapBuffers before other |
973 | * processes can see the updated drawing. We need to take special care, |
974 | * in that case, to defer telling the compositor our latest frame is |
975 | * ready until after the GPU has completed all issued commands related |
976 | * to the frame, and that the X server says the frame has been drawn. |
977 | * |
978 | * As this can cause deadlocks, we want to make sure to only enable it for Xorg, |
979 | * but not for XWayland, Xnest or whatever other X servers exist. |
980 | */ |
981 | |
982 | gdk_x11_display_error_trap_push (display); |
983 | if (XGetWindowProperty (dpy, DefaultRootWindow (dpy), |
984 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "XFree86_VT" ), |
985 | 0, 1, False, AnyPropertyType, |
986 | &type, &format, &nitems, &bytes_after, &data) == Success) |
987 | { |
988 | if (type != None) |
989 | display_x11->has_async_glx_swap_buffers = TRUE; |
990 | } |
991 | gdk_x11_display_error_trap_pop_ignored (display); |
992 | |
993 | if (data) |
994 | XFree (data); |
995 | } |
996 | |
997 | if (!gdk_x11_display_create_glx_config (self: display_x11, out_visual, out_depth, error)) |
998 | return FALSE; |
999 | |
1000 | GDK_DISPLAY_NOTE (display, OPENGL, |
1001 | g_message ("GLX version %d.%d found\n" |
1002 | " - Vendor: %s\n" |
1003 | " - Checked extensions:\n" |
1004 | "\t* GLX_ARB_create_context_profile: %s\n" |
1005 | "\t* GLX_EXT_create_context_es2_profile: %s\n" |
1006 | "\t* GLX_SGI_swap_control: %s\n" |
1007 | "\t* GLX_EXT_texture_from_pixmap: %s\n" |
1008 | "\t* GLX_SGI_video_sync: %s\n" |
1009 | "\t* GLX_EXT_buffer_age: %s\n" |
1010 | "\t* GLX_OML_sync_control: %s" |
1011 | "\t* GLX_ARB_multisample: %s" |
1012 | "\t* GLX_EXT_visual_rating: %s" , |
1013 | display_x11->glx_version / 10, |
1014 | display_x11->glx_version % 10, |
1015 | glXGetClientString (dpy, GLX_VENDOR), |
1016 | display_x11->has_glx_create_context ? "yes" : "no" , |
1017 | display_x11->has_glx_create_es2_context ? "yes" : "no" , |
1018 | display_x11->has_glx_swap_interval ? "yes" : "no" , |
1019 | display_x11->has_glx_texture_from_pixmap ? "yes" : "no" , |
1020 | display_x11->has_glx_video_sync ? "yes" : "no" , |
1021 | display_x11->has_glx_buffer_age ? "yes" : "no" , |
1022 | display_x11->has_glx_sync_control ? "yes" : "no" , |
1023 | display_x11->has_glx_multisample ? "yes" : "no" , |
1024 | display_x11->has_glx_visual_rating ? "yes" : "no" )); |
1025 | |
1026 | return TRUE; |
1027 | } |
1028 | |