• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2015 Boyan Ding
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that copyright
7  * notice and this permission notice appear in supporting documentation, and
8  * that the name of the copyright holders not be used in advertising or
9  * publicity pertaining to distribution of the software without specific,
10  * written prior permission.  The copyright holders make no representations
11  * about the suitability of this software for any purpose.  It is provided "as
12  * is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20  * OF THIS SOFTWARE.
21  */
22 
23 #include <fcntl.h>
24 #include <stdbool.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 
29 #include <xcb/dri3.h>
30 #include <xcb/present.h>
31 #include <xcb/xcb.h>
32 #include <xcb/xfixes.h>
33 
34 #include <xf86drm.h>
35 #include "util/macros.h"
36 
37 #include "egl_dri2.h"
38 #include "platform_x11_dri3.h"
39 
40 #include "loader.h"
41 #include "loader_dri3_helper.h"
42 
43 static struct dri3_egl_surface *
loader_drawable_to_egl_surface(struct loader_dri3_drawable * draw)44 loader_drawable_to_egl_surface(struct loader_dri3_drawable *draw)
45 {
46    size_t offset = offsetof(struct dri3_egl_surface, loader_drawable);
47    return (struct dri3_egl_surface *)(((void *)draw) - offset);
48 }
49 
50 static void
egl_dri3_set_drawable_size(struct loader_dri3_drawable * draw,int width,int height)51 egl_dri3_set_drawable_size(struct loader_dri3_drawable *draw, int width,
52                            int height)
53 {
54    struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
55 
56    dri3_surf->surf.base.Width = width;
57    dri3_surf->surf.base.Height = height;
58 }
59 
60 static bool
egl_dri3_in_current_context(struct loader_dri3_drawable * draw)61 egl_dri3_in_current_context(struct loader_dri3_drawable *draw)
62 {
63    struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
64    _EGLContext *ctx = _eglGetCurrentContext();
65 
66    return ctx->Resource.Display == dri3_surf->surf.base.Resource.Display;
67 }
68 
69 static __DRIcontext *
egl_dri3_get_dri_context(struct loader_dri3_drawable * draw)70 egl_dri3_get_dri_context(struct loader_dri3_drawable *draw)
71 {
72    _EGLContext *ctx = _eglGetCurrentContext();
73    struct dri2_egl_context *dri2_ctx;
74    if (!ctx)
75       return NULL;
76    dri2_ctx = dri2_egl_context(ctx);
77    return dri2_ctx->dri_context;
78 }
79 
80 static __DRIscreen *
egl_dri3_get_dri_screen(void)81 egl_dri3_get_dri_screen(void)
82 {
83    _EGLContext *ctx = _eglGetCurrentContext();
84    struct dri2_egl_context *dri2_ctx;
85    if (!ctx)
86       return NULL;
87    dri2_ctx = dri2_egl_context(ctx);
88    return dri2_egl_display(dri2_ctx->base.Resource.Display)
89       ->dri_screen_render_gpu;
90 }
91 
92 static void
egl_dri3_flush_drawable(struct loader_dri3_drawable * draw,unsigned flags)93 egl_dri3_flush_drawable(struct loader_dri3_drawable *draw, unsigned flags)
94 {
95    struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
96    _EGLDisplay *disp = dri3_surf->surf.base.Resource.Display;
97 
98    dri2_flush_drawable_for_swapbuffers(disp, &dri3_surf->surf.base);
99 }
100 
101 static const struct loader_dri3_vtable egl_dri3_vtable = {
102    .set_drawable_size = egl_dri3_set_drawable_size,
103    .in_current_context = egl_dri3_in_current_context,
104    .get_dri_context = egl_dri3_get_dri_context,
105    .get_dri_screen = egl_dri3_get_dri_screen,
106    .flush_drawable = egl_dri3_flush_drawable,
107 };
108 
109 static EGLBoolean
dri3_destroy_surface(_EGLDisplay * disp,_EGLSurface * surf)110 dri3_destroy_surface(_EGLDisplay *disp, _EGLSurface *surf)
111 {
112    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
113    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
114    xcb_drawable_t drawable = dri3_surf->loader_drawable.drawable;
115 
116    loader_dri3_drawable_fini(&dri3_surf->loader_drawable);
117 
118    if (surf->Type == EGL_PBUFFER_BIT)
119       xcb_free_pixmap(dri2_dpy->conn, drawable);
120 
121    dri2_fini_surface(surf);
122    free(surf);
123 
124    return EGL_TRUE;
125 }
126 
127 static EGLBoolean
dri3_set_swap_interval(_EGLDisplay * disp,_EGLSurface * surf,EGLint interval)128 dri3_set_swap_interval(_EGLDisplay *disp, _EGLSurface *surf, EGLint interval)
129 {
130    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
131 
132    dri3_surf->surf.base.SwapInterval = interval;
133    loader_dri3_set_swap_interval(&dri3_surf->loader_drawable, interval);
134 
135    return EGL_TRUE;
136 }
137 
138 static enum loader_dri3_drawable_type
egl_to_loader_dri3_drawable_type(EGLint type)139 egl_to_loader_dri3_drawable_type(EGLint type)
140 {
141    switch (type) {
142    case EGL_WINDOW_BIT:
143       return LOADER_DRI3_DRAWABLE_WINDOW;
144    case EGL_PIXMAP_BIT:
145       return LOADER_DRI3_DRAWABLE_PIXMAP;
146    case EGL_PBUFFER_BIT:
147       return LOADER_DRI3_DRAWABLE_PBUFFER;
148    default:
149       return LOADER_DRI3_DRAWABLE_UNKNOWN;
150    }
151 }
152 
153 static _EGLSurface *
dri3_create_surface(_EGLDisplay * disp,EGLint type,_EGLConfig * conf,void * native_surface,const EGLint * attrib_list)154 dri3_create_surface(_EGLDisplay *disp, EGLint type, _EGLConfig *conf,
155                     void *native_surface, const EGLint *attrib_list)
156 {
157    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
158    struct dri2_egl_config *dri2_conf = dri2_egl_config(conf);
159    struct dri3_egl_surface *dri3_surf;
160    const __DRIconfig *dri_config;
161    xcb_drawable_t drawable;
162 
163    dri3_surf = calloc(1, sizeof *dri3_surf);
164    if (!dri3_surf) {
165       _eglError(EGL_BAD_ALLOC, "dri3_create_surface");
166       return NULL;
167    }
168 
169    if (!dri2_init_surface(&dri3_surf->surf.base, disp, type, conf, attrib_list,
170                           false, native_surface))
171       goto cleanup_surf;
172 
173    if (type == EGL_PBUFFER_BIT) {
174       drawable = xcb_generate_id(dri2_dpy->conn);
175       xcb_create_pixmap(dri2_dpy->conn, conf->BufferSize, drawable,
176                         dri2_dpy->screen->root, dri3_surf->surf.base.Width,
177                         dri3_surf->surf.base.Height);
178    } else {
179       STATIC_ASSERT(sizeof(uintptr_t) == sizeof(native_surface));
180       drawable = (uintptr_t)native_surface;
181    }
182 
183    dri_config =
184       dri2_get_dri_config(dri2_conf, type, dri3_surf->surf.base.GLColorspace);
185 
186    if (!dri_config) {
187       _eglError(EGL_BAD_MATCH,
188                 "Unsupported surfacetype/colorspace configuration");
189       goto cleanup_pixmap;
190    }
191 
192    if (loader_dri3_drawable_init(
193           dri2_dpy->conn, drawable, egl_to_loader_dri3_drawable_type(type),
194           dri2_dpy->dri_screen_render_gpu, dri2_dpy->dri_screen_display_gpu,
195           dri2_dpy->multibuffers_available, true, dri_config,
196           &dri2_dpy->loader_dri3_ext, &egl_dri3_vtable,
197           &dri3_surf->loader_drawable)) {
198       _eglError(EGL_BAD_ALLOC, "dri3_surface_create");
199       goto cleanup_pixmap;
200    }
201 
202    if (dri3_surf->surf.base.ProtectedContent &&
203        dri2_dpy->fd_render_gpu != dri2_dpy->fd_display_gpu) {
204       _eglError(EGL_BAD_ALLOC, "dri3_surface_create");
205       goto cleanup_pixmap;
206    }
207 
208    dri3_surf->loader_drawable.is_protected_content =
209       dri3_surf->surf.base.ProtectedContent;
210 
211    return &dri3_surf->surf.base;
212 
213 cleanup_pixmap:
214    if (type == EGL_PBUFFER_BIT)
215       xcb_free_pixmap(dri2_dpy->conn, drawable);
216 cleanup_surf:
217    free(dri3_surf);
218 
219    return NULL;
220 }
221 
222 static int
dri3_authenticate(_EGLDisplay * disp,uint32_t id)223 dri3_authenticate(_EGLDisplay *disp, uint32_t id)
224 {
225 #ifdef HAVE_WAYLAND_PLATFORM
226    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
227 
228    if (dri2_dpy->device_name) {
229       _eglLog(_EGL_WARNING,
230               "Wayland client render node authentication is unnecessary");
231       return 0;
232    }
233 
234    _eglLog(_EGL_WARNING,
235            "Wayland client primary node authentication isn't supported");
236 #endif
237 
238    return -1;
239 }
240 
241 /**
242  * Called via eglCreateWindowSurface(), drv->CreateWindowSurface().
243  */
244 static _EGLSurface *
dri3_create_window_surface(_EGLDisplay * disp,_EGLConfig * conf,void * native_window,const EGLint * attrib_list)245 dri3_create_window_surface(_EGLDisplay *disp, _EGLConfig *conf,
246                            void *native_window, const EGLint *attrib_list)
247 {
248    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
249    _EGLSurface *surf;
250 
251    surf = dri3_create_surface(disp, EGL_WINDOW_BIT, conf, native_window,
252                               attrib_list);
253    if (surf != NULL)
254       dri3_set_swap_interval(disp, surf, dri2_dpy->default_swap_interval);
255 
256    return surf;
257 }
258 
259 static _EGLSurface *
dri3_create_pixmap_surface(_EGLDisplay * disp,_EGLConfig * conf,void * native_pixmap,const EGLint * attrib_list)260 dri3_create_pixmap_surface(_EGLDisplay *disp, _EGLConfig *conf,
261                            void *native_pixmap, const EGLint *attrib_list)
262 {
263    return dri3_create_surface(disp, EGL_PIXMAP_BIT, conf, native_pixmap,
264                               attrib_list);
265 }
266 
267 static _EGLSurface *
dri3_create_pbuffer_surface(_EGLDisplay * disp,_EGLConfig * conf,const EGLint * attrib_list)268 dri3_create_pbuffer_surface(_EGLDisplay *disp, _EGLConfig *conf,
269                             const EGLint *attrib_list)
270 {
271    return dri3_create_surface(disp, EGL_PBUFFER_BIT, conf, NULL, attrib_list);
272 }
273 
274 static EGLBoolean
dri3_get_sync_values(_EGLDisplay * display,_EGLSurface * surface,EGLuint64KHR * ust,EGLuint64KHR * msc,EGLuint64KHR * sbc)275 dri3_get_sync_values(_EGLDisplay *display, _EGLSurface *surface,
276                      EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc)
277 {
278    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surface);
279 
280    return loader_dri3_wait_for_msc(&dri3_surf->loader_drawable, 0, 0, 0,
281                                    (int64_t *)ust, (int64_t *)msc,
282                                    (int64_t *)sbc)
283              ? EGL_TRUE
284              : EGL_FALSE;
285 }
286 
287 static _EGLImage *
dri3_create_image_khr_pixmap(_EGLDisplay * disp,_EGLContext * ctx,EGLClientBuffer buffer,const EGLint * attr_list)288 dri3_create_image_khr_pixmap(_EGLDisplay *disp, _EGLContext *ctx,
289                              EGLClientBuffer buffer, const EGLint *attr_list)
290 {
291    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
292    struct dri2_egl_image *dri2_img;
293    xcb_drawable_t drawable;
294    xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie;
295    xcb_dri3_buffer_from_pixmap_reply_t *bp_reply;
296    unsigned int format;
297 
298    drawable = (xcb_drawable_t)(uintptr_t)buffer;
299    bp_cookie = xcb_dri3_buffer_from_pixmap(dri2_dpy->conn, drawable);
300    bp_reply =
301       xcb_dri3_buffer_from_pixmap_reply(dri2_dpy->conn, bp_cookie, NULL);
302    if (!bp_reply) {
303       _eglError(EGL_BAD_ALLOC, "xcb_dri3_buffer_from_pixmap");
304       return NULL;
305    }
306 
307    format = dri2_format_for_depth(dri2_dpy, bp_reply->depth);
308    if (format == __DRI_IMAGE_FORMAT_NONE) {
309       _eglError(EGL_BAD_PARAMETER,
310                 "dri3_create_image_khr: unsupported pixmap depth");
311       free(bp_reply);
312       return EGL_NO_IMAGE_KHR;
313    }
314 
315    dri2_img = malloc(sizeof *dri2_img);
316    if (!dri2_img) {
317       _eglError(EGL_BAD_ALLOC, "dri3_create_image_khr");
318       free(bp_reply);
319       return EGL_NO_IMAGE_KHR;
320    }
321 
322    _eglInitImage(&dri2_img->base, disp);
323 
324    dri2_img->dri_image = loader_dri3_create_image(
325       dri2_dpy->conn, bp_reply, format, dri2_dpy->dri_screen_render_gpu,
326       dri2_dpy->image, dri2_img);
327 
328    free(bp_reply);
329 
330    return &dri2_img->base;
331 }
332 
333 #ifdef HAVE_DRI3_MODIFIERS
334 static _EGLImage *
dri3_create_image_khr_pixmap_from_buffers(_EGLDisplay * disp,_EGLContext * ctx,EGLClientBuffer buffer,const EGLint * attr_list)335 dri3_create_image_khr_pixmap_from_buffers(_EGLDisplay *disp, _EGLContext *ctx,
336                                           EGLClientBuffer buffer,
337                                           const EGLint *attr_list)
338 {
339    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
340    struct dri2_egl_image *dri2_img;
341    xcb_dri3_buffers_from_pixmap_cookie_t bp_cookie;
342    xcb_dri3_buffers_from_pixmap_reply_t *bp_reply;
343    xcb_drawable_t drawable;
344    unsigned int format;
345 
346    drawable = (xcb_drawable_t)(uintptr_t)buffer;
347    bp_cookie = xcb_dri3_buffers_from_pixmap(dri2_dpy->conn, drawable);
348    bp_reply =
349       xcb_dri3_buffers_from_pixmap_reply(dri2_dpy->conn, bp_cookie, NULL);
350 
351    if (!bp_reply) {
352       _eglError(EGL_BAD_ATTRIBUTE, "dri3_create_image_khr");
353       return EGL_NO_IMAGE_KHR;
354    }
355 
356    format = dri2_format_for_depth(dri2_dpy, bp_reply->depth);
357    if (format == __DRI_IMAGE_FORMAT_NONE) {
358       _eglError(EGL_BAD_PARAMETER,
359                 "dri3_create_image_khr: unsupported pixmap depth");
360       free(bp_reply);
361       return EGL_NO_IMAGE_KHR;
362    }
363 
364    dri2_img = malloc(sizeof *dri2_img);
365    if (!dri2_img) {
366       _eglError(EGL_BAD_ALLOC, "dri3_create_image_khr");
367       free(bp_reply);
368       return EGL_NO_IMAGE_KHR;
369    }
370 
371    _eglInitImage(&dri2_img->base, disp);
372 
373    dri2_img->dri_image = loader_dri3_create_image_from_buffers(
374       dri2_dpy->conn, bp_reply, format, dri2_dpy->dri_screen_render_gpu,
375       dri2_dpy->image, dri2_img);
376    free(bp_reply);
377 
378    if (!dri2_img->dri_image) {
379       _eglError(EGL_BAD_ATTRIBUTE, "dri3_create_image_khr");
380       free(dri2_img);
381       return EGL_NO_IMAGE_KHR;
382    }
383 
384    return &dri2_img->base;
385 }
386 #endif
387 
388 static _EGLImage *
dri3_create_image_khr(_EGLDisplay * disp,_EGLContext * ctx,EGLenum target,EGLClientBuffer buffer,const EGLint * attr_list)389 dri3_create_image_khr(_EGLDisplay *disp, _EGLContext *ctx, EGLenum target,
390                       EGLClientBuffer buffer, const EGLint *attr_list)
391 {
392 #ifdef HAVE_DRI3_MODIFIERS
393    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
394 #endif
395 
396    switch (target) {
397    case EGL_NATIVE_PIXMAP_KHR:
398 #ifdef HAVE_DRI3_MODIFIERS
399       if (dri2_dpy->multibuffers_available)
400          return dri3_create_image_khr_pixmap_from_buffers(disp, ctx, buffer,
401                                                           attr_list);
402 #endif
403       return dri3_create_image_khr_pixmap(disp, ctx, buffer, attr_list);
404    default:
405       return dri2_create_image_khr(disp, ctx, target, buffer, attr_list);
406    }
407 }
408 
409 /**
410  * Called by the driver when it needs to update the real front buffer with the
411  * contents of its fake front buffer.
412  */
413 static void
dri3_flush_front_buffer(__DRIdrawable * driDrawable,void * loaderPrivate)414 dri3_flush_front_buffer(__DRIdrawable *driDrawable, void *loaderPrivate)
415 {
416    struct loader_dri3_drawable *draw = loaderPrivate;
417    (void)driDrawable;
418 
419    /* There does not seem to be any kind of consensus on whether we should
420     * support front-buffer rendering or not:
421     * http://lists.freedesktop.org/archives/mesa-dev/2013-June/040129.html
422     */
423    if (draw->type == LOADER_DRI3_DRAWABLE_WINDOW)
424       _eglLog(_EGL_WARNING,
425               "FIXME: egl/x11 doesn't support front buffer rendering.");
426 }
427 
428 const __DRIimageLoaderExtension dri3_image_loader_extension = {
429    .base = {__DRI_IMAGE_LOADER, 1},
430 
431    .getBuffers = loader_dri3_get_buffers,
432    .flushFrontBuffer = dri3_flush_front_buffer,
433 };
434 
435 static EGLBoolean
dri3_swap_buffers_with_damage(_EGLDisplay * disp,_EGLSurface * draw,const EGLint * rects,EGLint n_rects)436 dri3_swap_buffers_with_damage(_EGLDisplay *disp, _EGLSurface *draw,
437                               const EGLint *rects, EGLint n_rects)
438 {
439    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(draw);
440 
441    return loader_dri3_swap_buffers_msc(
442              &dri3_surf->loader_drawable, 0, 0, 0, 0, rects, n_rects,
443              draw->SwapBehavior == EGL_BUFFER_PRESERVED) != -1;
444 }
445 
446 static EGLBoolean
dri3_swap_buffers(_EGLDisplay * disp,_EGLSurface * draw)447 dri3_swap_buffers(_EGLDisplay *disp, _EGLSurface *draw)
448 {
449    return dri3_swap_buffers_with_damage(disp, draw, NULL, 0);
450 }
451 
452 static EGLBoolean
dri3_copy_buffers(_EGLDisplay * disp,_EGLSurface * surf,void * native_pixmap_target)453 dri3_copy_buffers(_EGLDisplay *disp, _EGLSurface *surf,
454                   void *native_pixmap_target)
455 {
456    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
457    xcb_pixmap_t target;
458 
459    STATIC_ASSERT(sizeof(uintptr_t) == sizeof(native_pixmap_target));
460    target = (uintptr_t)native_pixmap_target;
461 
462    loader_dri3_copy_drawable(&dri3_surf->loader_drawable, target,
463                              dri3_surf->loader_drawable.drawable);
464 
465    return EGL_TRUE;
466 }
467 
468 static int
dri3_query_buffer_age(_EGLDisplay * disp,_EGLSurface * surf)469 dri3_query_buffer_age(_EGLDisplay *disp, _EGLSurface *surf)
470 {
471    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
472 
473    return loader_dri3_query_buffer_age(&dri3_surf->loader_drawable);
474 }
475 
476 static EGLBoolean
dri3_query_surface(_EGLDisplay * disp,_EGLSurface * surf,EGLint attribute,EGLint * value)477 dri3_query_surface(_EGLDisplay *disp, _EGLSurface *surf, EGLint attribute,
478                    EGLint *value)
479 {
480    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
481 
482    switch (attribute) {
483    case EGL_WIDTH:
484    case EGL_HEIGHT:
485       loader_dri3_update_drawable_geometry(&dri3_surf->loader_drawable);
486       break;
487    default:
488       break;
489    }
490 
491    return _eglQuerySurface(disp, surf, attribute, value);
492 }
493 
494 static __DRIdrawable *
dri3_get_dri_drawable(_EGLSurface * surf)495 dri3_get_dri_drawable(_EGLSurface *surf)
496 {
497    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
498 
499    return dri3_surf->loader_drawable.dri_drawable;
500 }
501 
502 static void
dri3_close_screen_notify(_EGLDisplay * disp)503 dri3_close_screen_notify(_EGLDisplay *disp)
504 {
505    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
506 
507    loader_dri3_close_screen(dri2_dpy->dri_screen_render_gpu);
508 }
509 
510 struct dri2_egl_display_vtbl dri3_x11_display_vtbl = {
511    .authenticate = dri3_authenticate,
512    .create_window_surface = dri3_create_window_surface,
513    .create_pixmap_surface = dri3_create_pixmap_surface,
514    .create_pbuffer_surface = dri3_create_pbuffer_surface,
515    .destroy_surface = dri3_destroy_surface,
516    .create_image = dri3_create_image_khr,
517    .swap_interval = dri3_set_swap_interval,
518    .swap_buffers = dri3_swap_buffers,
519    .swap_buffers_with_damage = dri3_swap_buffers_with_damage,
520    .copy_buffers = dri3_copy_buffers,
521    .query_buffer_age = dri3_query_buffer_age,
522    .query_surface = dri3_query_surface,
523    .get_sync_values = dri3_get_sync_values,
524    .get_msc_rate = dri2_x11_get_msc_rate,
525    .get_dri_drawable = dri3_get_dri_drawable,
526    .close_screen_notify = dri3_close_screen_notify,
527 };
528 
529 /* Only request versions of these protocols which we actually support. */
530 #define DRI3_SUPPORTED_MAJOR    1
531 #define PRESENT_SUPPORTED_MAJOR 1
532 
533 #ifdef HAVE_DRI3_MODIFIERS
534 #define DRI3_SUPPORTED_MINOR    2
535 #define PRESENT_SUPPORTED_MINOR 2
536 #else
537 #define PRESENT_SUPPORTED_MINOR 0
538 #define DRI3_SUPPORTED_MINOR    0
539 #endif
540 
541 enum dri2_egl_driver_fail
dri3_x11_connect(struct dri2_egl_display * dri2_dpy)542 dri3_x11_connect(struct dri2_egl_display *dri2_dpy)
543 {
544    xcb_dri3_query_version_reply_t *dri3_query;
545    xcb_dri3_query_version_cookie_t dri3_query_cookie;
546    xcb_present_query_version_reply_t *present_query;
547    xcb_present_query_version_cookie_t present_query_cookie;
548    xcb_xfixes_query_version_reply_t *xfixes_query;
549    xcb_xfixes_query_version_cookie_t xfixes_query_cookie;
550    xcb_generic_error_t *error;
551    const xcb_query_extension_reply_t *extension;
552 
553    dri2_dpy->dri3_major_version = 0;
554    dri2_dpy->dri3_minor_version = 0;
555    dri2_dpy->present_major_version = 0;
556    dri2_dpy->present_minor_version = 0;
557 
558    xcb_prefetch_extension_data(dri2_dpy->conn, &xcb_dri3_id);
559    xcb_prefetch_extension_data(dri2_dpy->conn, &xcb_present_id);
560    xcb_prefetch_extension_data(dri2_dpy->conn, &xcb_xfixes_id);
561 
562    extension = xcb_get_extension_data(dri2_dpy->conn, &xcb_dri3_id);
563    if (!(extension && extension->present))
564       return DRI2_EGL_DRIVER_FAILED;
565 
566    extension = xcb_get_extension_data(dri2_dpy->conn, &xcb_present_id);
567    if (!(extension && extension->present))
568       return DRI2_EGL_DRIVER_FAILED;
569 
570    extension = xcb_get_extension_data(dri2_dpy->conn, &xcb_xfixes_id);
571    if (!(extension && extension->present))
572       return DRI2_EGL_DRIVER_FAILED;
573 
574    dri3_query_cookie = xcb_dri3_query_version(
575       dri2_dpy->conn, DRI3_SUPPORTED_MAJOR, DRI3_SUPPORTED_MINOR);
576 
577    present_query_cookie = xcb_present_query_version(
578       dri2_dpy->conn, PRESENT_SUPPORTED_MAJOR, PRESENT_SUPPORTED_MINOR);
579 
580    xfixes_query_cookie = xcb_xfixes_query_version(
581       dri2_dpy->conn, XCB_XFIXES_MAJOR_VERSION, XCB_XFIXES_MINOR_VERSION);
582 
583    dri3_query =
584       xcb_dri3_query_version_reply(dri2_dpy->conn, dri3_query_cookie, &error);
585    if (dri3_query == NULL || error != NULL) {
586       _eglLog(_EGL_WARNING, "DRI3: failed to query the version");
587       free(dri3_query);
588       free(error);
589       return DRI2_EGL_DRIVER_FAILED;
590    }
591 
592    dri2_dpy->dri3_major_version = dri3_query->major_version;
593    dri2_dpy->dri3_minor_version = dri3_query->minor_version;
594    free(dri3_query);
595 
596    present_query = xcb_present_query_version_reply(
597       dri2_dpy->conn, present_query_cookie, &error);
598    if (present_query == NULL || error != NULL) {
599       _eglLog(_EGL_WARNING, "DRI3: failed to query Present version");
600       free(present_query);
601       free(error);
602       return DRI2_EGL_DRIVER_FAILED;
603    }
604 
605    dri2_dpy->present_major_version = present_query->major_version;
606    dri2_dpy->present_minor_version = present_query->minor_version;
607    free(present_query);
608 
609    xfixes_query = xcb_xfixes_query_version_reply(dri2_dpy->conn,
610                                                  xfixes_query_cookie, &error);
611    if (xfixes_query == NULL || error != NULL ||
612        xfixes_query->major_version < 2) {
613       _eglLog(_EGL_WARNING, "DRI3: failed to query xfixes version");
614       free(error);
615       free(xfixes_query);
616       return DRI2_EGL_DRIVER_FAILED;
617    }
618    free(xfixes_query);
619 
620    dri2_dpy->fd_render_gpu =
621       loader_dri3_open(dri2_dpy->conn, dri2_dpy->screen->root, 0);
622    if (dri2_dpy->fd_render_gpu < 0) {
623       int conn_error = xcb_connection_has_error(dri2_dpy->conn);
624       _eglLog(_EGL_WARNING, "DRI3: Screen seems not DRI3 capable");
625 
626       if (conn_error)
627          _eglLog(_EGL_WARNING, "DRI3: Failed to initialize");
628 
629       return DRI2_EGL_DRIVER_FAILED;
630    }
631 
632    loader_get_user_preferred_fd(&dri2_dpy->fd_render_gpu,
633                                 &dri2_dpy->fd_display_gpu);
634 
635    if (!dri2_dpy->driver_name)
636       dri2_dpy->driver_name = loader_get_driver_for_fd(dri2_dpy->fd_render_gpu);
637 
638    if (!strcmp(dri2_dpy->driver_name, "zink")) {
639       close(dri2_dpy->fd_render_gpu);
640       return DRI2_EGL_DRIVER_PREFER_ZINK;
641    }
642 
643    if (!dri2_dpy->driver_name) {
644       _eglLog(_EGL_WARNING, "DRI3: No driver found");
645       close(dri2_dpy->fd_render_gpu);
646       return DRI2_EGL_DRIVER_FAILED;
647    }
648 
649 #ifdef HAVE_WAYLAND_PLATFORM
650    /* Only try to get a render device name since dri3 doesn't provide a
651     * mechanism for authenticating client opened device node fds. If this
652     * fails then don't advertise the extension. */
653    dri2_dpy->device_name =
654       drmGetRenderDeviceNameFromFd(dri2_dpy->fd_render_gpu);
655 #endif
656 
657    return DRI2_EGL_DRIVER_LOADED;
658 }
659