• 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 <stdbool.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 
28 #include <xcb/xcb.h>
29 #include <xcb/dri3.h>
30 #include <xcb/present.h>
31 
32 #include <xf86drm.h>
33 #include "util/macros.h"
34 
35 #include "egl_dri2.h"
36 #include "egl_dri2_fallbacks.h"
37 #include "platform_x11_dri3.h"
38 
39 #include "loader.h"
40 #include "loader_dri3_helper.h"
41 
42 static struct dri3_egl_surface *
loader_drawable_to_egl_surface(struct loader_dri3_drawable * draw)43 loader_drawable_to_egl_surface(struct loader_dri3_drawable *draw) {
44    size_t offset = offsetof(struct dri3_egl_surface, loader_drawable);
45    return (struct dri3_egl_surface *)(((void*) draw) - offset);
46 }
47 
48 static void
egl_dri3_set_drawable_size(struct loader_dri3_drawable * draw,int width,int height)49 egl_dri3_set_drawable_size(struct loader_dri3_drawable *draw,
50                            int width, int height)
51 {
52    struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
53 
54    dri3_surf->surf.base.Width = width;
55    dri3_surf->surf.base.Height = height;
56 }
57 
58 static bool
egl_dri3_in_current_context(struct loader_dri3_drawable * draw)59 egl_dri3_in_current_context(struct loader_dri3_drawable *draw)
60 {
61    struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
62    _EGLContext *ctx = _eglGetCurrentContext();
63 
64    return ctx->Resource.Display == dri3_surf->surf.base.Resource.Display;
65 }
66 
67 static __DRIcontext *
egl_dri3_get_dri_context(struct loader_dri3_drawable * draw)68 egl_dri3_get_dri_context(struct loader_dri3_drawable *draw)
69 {
70    _EGLContext *ctx = _eglGetCurrentContext();
71    struct dri2_egl_context *dri2_ctx;
72    if (!ctx)
73       return NULL;
74    dri2_ctx = dri2_egl_context(ctx);
75    return dri2_ctx->dri_context;
76 }
77 
78 static __DRIscreen *
egl_dri3_get_dri_screen(void)79 egl_dri3_get_dri_screen(void)
80 {
81    _EGLContext *ctx = _eglGetCurrentContext();
82    struct dri2_egl_context *dri2_ctx;
83    if (!ctx)
84       return NULL;
85    dri2_ctx = dri2_egl_context(ctx);
86    return dri2_egl_display(dri2_ctx->base.Resource.Display)->dri_screen;
87 }
88 
89 static void
egl_dri3_flush_drawable(struct loader_dri3_drawable * draw,unsigned flags)90 egl_dri3_flush_drawable(struct loader_dri3_drawable *draw, unsigned flags)
91 {
92    struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
93    _EGLDisplay *disp = dri3_surf->surf.base.Resource.Display;
94 
95    dri2_flush_drawable_for_swapbuffers(disp, &dri3_surf->surf.base);
96 }
97 
98 static const struct loader_dri3_vtable egl_dri3_vtable = {
99    .set_drawable_size = egl_dri3_set_drawable_size,
100    .in_current_context = egl_dri3_in_current_context,
101    .get_dri_context = egl_dri3_get_dri_context,
102    .get_dri_screen = egl_dri3_get_dri_screen,
103    .flush_drawable = egl_dri3_flush_drawable,
104    .show_fps = NULL,
105 };
106 
107 static EGLBoolean
dri3_destroy_surface(_EGLDriver * drv,_EGLDisplay * disp,_EGLSurface * surf)108 dri3_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf)
109 {
110    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
111 
112    (void) drv;
113 
114    loader_dri3_drawable_fini(&dri3_surf->loader_drawable);
115 
116    dri2_fini_surface(surf);
117    free(surf);
118 
119    return EGL_TRUE;
120 }
121 
122 static EGLBoolean
dri3_set_swap_interval(_EGLDriver * drv,_EGLDisplay * disp,_EGLSurface * surf,EGLint interval)123 dri3_set_swap_interval(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf,
124                        EGLint interval)
125 {
126    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
127 
128    dri3_surf->surf.base.SwapInterval = interval;
129    loader_dri3_set_swap_interval(&dri3_surf->loader_drawable, interval);
130 
131    return EGL_TRUE;
132 }
133 
134 static _EGLSurface *
dri3_create_surface(_EGLDriver * drv,_EGLDisplay * disp,EGLint type,_EGLConfig * conf,void * native_surface,const EGLint * attrib_list)135 dri3_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type,
136                     _EGLConfig *conf, void *native_surface,
137                     const EGLint *attrib_list)
138 {
139    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
140    struct dri2_egl_config *dri2_conf = dri2_egl_config(conf);
141    struct dri3_egl_surface *dri3_surf;
142    const __DRIconfig *dri_config;
143    xcb_drawable_t drawable;
144 
145    (void) drv;
146 
147    dri3_surf = calloc(1, sizeof *dri3_surf);
148    if (!dri3_surf) {
149       _eglError(EGL_BAD_ALLOC, "dri3_create_surface");
150       return NULL;
151    }
152 
153    if (!dri2_init_surface(&dri3_surf->surf.base, disp, type, conf, attrib_list, false))
154       goto cleanup_surf;
155 
156    if (type == EGL_PBUFFER_BIT) {
157       drawable = xcb_generate_id(dri2_dpy->conn);
158       xcb_create_pixmap(dri2_dpy->conn, conf->BufferSize,
159                         drawable, dri2_dpy->screen->root,
160                         dri3_surf->surf.base.Width, dri3_surf->surf.base.Height);
161    } else {
162       STATIC_ASSERT(sizeof(uintptr_t) == sizeof(native_surface));
163       drawable = (uintptr_t) native_surface;
164    }
165 
166    dri_config = dri2_get_dri_config(dri2_conf, type,
167                                     dri3_surf->surf.base.GLColorspace);
168 
169    if (loader_dri3_drawable_init(dri2_dpy->conn, drawable,
170                                  dri2_dpy->dri_screen,
171                                  dri2_dpy->is_different_gpu, dri_config,
172                                  &dri2_dpy->loader_dri3_ext,
173                                  &egl_dri3_vtable,
174                                  &dri3_surf->loader_drawable)) {
175       _eglError(EGL_BAD_ALLOC, "dri3_surface_create");
176       goto cleanup_pixmap;
177    }
178 
179    return &dri3_surf->surf.base;
180 
181  cleanup_pixmap:
182    if (type == EGL_PBUFFER_BIT)
183       xcb_free_pixmap(dri2_dpy->conn, drawable);
184  cleanup_surf:
185    free(dri3_surf);
186 
187    return NULL;
188 }
189 
190 static int
dri3_authenticate(_EGLDisplay * disp,uint32_t id)191 dri3_authenticate(_EGLDisplay *disp, uint32_t id)
192 {
193 #ifdef HAVE_WAYLAND_PLATFORM
194    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
195 
196    if (dri2_dpy->device_name) {
197       _eglLog(_EGL_WARNING,
198               "Wayland client render node authentication is unnecessary");
199       return 0;
200    }
201 
202    _eglLog(_EGL_WARNING,
203            "Wayland client primary node authentication isn't supported");
204 #endif
205 
206    return -1;
207 }
208 
209 /**
210  * Called via eglCreateWindowSurface(), drv->API.CreateWindowSurface().
211  */
212 static _EGLSurface *
dri3_create_window_surface(_EGLDriver * drv,_EGLDisplay * disp,_EGLConfig * conf,void * native_window,const EGLint * attrib_list)213 dri3_create_window_surface(_EGLDriver *drv, _EGLDisplay *disp,
214                            _EGLConfig *conf, void *native_window,
215                            const EGLint *attrib_list)
216 {
217    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
218    _EGLSurface *surf;
219 
220    surf = dri3_create_surface(drv, disp, EGL_WINDOW_BIT, conf,
221                               native_window, attrib_list);
222    if (surf != NULL)
223       dri3_set_swap_interval(drv, disp, surf, dri2_dpy->default_swap_interval);
224 
225    return surf;
226 }
227 
228 static _EGLSurface *
dri3_create_pixmap_surface(_EGLDriver * drv,_EGLDisplay * disp,_EGLConfig * conf,void * native_pixmap,const EGLint * attrib_list)229 dri3_create_pixmap_surface(_EGLDriver *drv, _EGLDisplay *disp,
230                            _EGLConfig *conf, void *native_pixmap,
231                            const EGLint *attrib_list)
232 {
233    return dri3_create_surface(drv, disp, EGL_PIXMAP_BIT, conf,
234                               native_pixmap, attrib_list);
235 }
236 
237 static _EGLSurface *
dri3_create_pbuffer_surface(_EGLDriver * drv,_EGLDisplay * disp,_EGLConfig * conf,const EGLint * attrib_list)238 dri3_create_pbuffer_surface(_EGLDriver *drv, _EGLDisplay *disp,
239                                 _EGLConfig *conf, const EGLint *attrib_list)
240 {
241    return dri3_create_surface(drv, disp, EGL_PBUFFER_BIT, conf,
242                               NULL, attrib_list);
243 }
244 
245 static EGLBoolean
dri3_get_sync_values(_EGLDisplay * display,_EGLSurface * surface,EGLuint64KHR * ust,EGLuint64KHR * msc,EGLuint64KHR * sbc)246 dri3_get_sync_values(_EGLDisplay *display, _EGLSurface *surface,
247                      EGLuint64KHR *ust, EGLuint64KHR *msc,
248                      EGLuint64KHR *sbc)
249 {
250    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surface);
251 
252    return loader_dri3_wait_for_msc(&dri3_surf->loader_drawable, 0, 0, 0,
253                                    (int64_t *) ust, (int64_t *) msc,
254                                    (int64_t *) sbc) ? EGL_TRUE : EGL_FALSE;
255 }
256 
257 static _EGLImage *
dri3_create_image_khr_pixmap(_EGLDisplay * disp,_EGLContext * ctx,EGLClientBuffer buffer,const EGLint * attr_list)258 dri3_create_image_khr_pixmap(_EGLDisplay *disp, _EGLContext *ctx,
259                              EGLClientBuffer buffer, const EGLint *attr_list)
260 {
261    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
262    struct dri2_egl_image *dri2_img;
263    xcb_drawable_t drawable;
264    xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie;
265    xcb_dri3_buffer_from_pixmap_reply_t  *bp_reply;
266    unsigned int format;
267 
268    drawable = (xcb_drawable_t) (uintptr_t) buffer;
269    bp_cookie = xcb_dri3_buffer_from_pixmap(dri2_dpy->conn, drawable);
270    bp_reply = xcb_dri3_buffer_from_pixmap_reply(dri2_dpy->conn,
271                                                 bp_cookie, NULL);
272    if (!bp_reply) {
273       _eglError(EGL_BAD_ALLOC, "xcb_dri3_buffer_from_pixmap");
274       return NULL;
275    }
276 
277    switch (bp_reply->depth) {
278    case 16:
279       format = __DRI_IMAGE_FORMAT_RGB565;
280       break;
281    case 24:
282       format = __DRI_IMAGE_FORMAT_XRGB8888;
283       break;
284    case 30:
285       format = __DRI_IMAGE_FORMAT_XRGB2101010;
286       break;
287    case 32:
288       format = __DRI_IMAGE_FORMAT_ARGB8888;
289       break;
290    default:
291       _eglError(EGL_BAD_PARAMETER,
292                 "dri3_create_image_khr: unsupported pixmap depth");
293       free(bp_reply);
294       return EGL_NO_IMAGE_KHR;
295    }
296 
297    dri2_img = malloc(sizeof *dri2_img);
298    if (!dri2_img) {
299       _eglError(EGL_BAD_ALLOC, "dri3_create_image_khr");
300       return EGL_NO_IMAGE_KHR;
301    }
302 
303    _eglInitImage(&dri2_img->base, disp);
304 
305    dri2_img->dri_image = loader_dri3_create_image(dri2_dpy->conn,
306                                                   bp_reply,
307                                                   format,
308                                                   dri2_dpy->dri_screen,
309                                                   dri2_dpy->image,
310                                                   dri2_img);
311 
312    free(bp_reply);
313 
314    return &dri2_img->base;
315 }
316 
317 static _EGLImage *
dri3_create_image_khr(_EGLDriver * drv,_EGLDisplay * disp,_EGLContext * ctx,EGLenum target,EGLClientBuffer buffer,const EGLint * attr_list)318 dri3_create_image_khr(_EGLDriver *drv, _EGLDisplay *disp,
319                       _EGLContext *ctx, EGLenum target,
320                       EGLClientBuffer buffer, const EGLint *attr_list)
321 {
322    switch (target) {
323    case EGL_NATIVE_PIXMAP_KHR:
324       return dri3_create_image_khr_pixmap(disp, ctx, buffer, attr_list);
325    default:
326       return dri2_create_image_khr(drv, disp, ctx, target, buffer, attr_list);
327    }
328 }
329 
330 /**
331  * Called by the driver when it needs to update the real front buffer with the
332  * contents of its fake front buffer.
333  */
334 static void
dri3_flush_front_buffer(__DRIdrawable * driDrawable,void * loaderPrivate)335 dri3_flush_front_buffer(__DRIdrawable *driDrawable, void *loaderPrivate)
336 {
337    /* There does not seem to be any kind of consensus on whether we should
338     * support front-buffer rendering or not:
339     * http://lists.freedesktop.org/archives/mesa-dev/2013-June/040129.html
340     */
341    _eglLog(_EGL_WARNING, "FIXME: egl/x11 doesn't support front buffer rendering.");
342    (void) driDrawable;
343    (void) loaderPrivate;
344 }
345 
346 const __DRIimageLoaderExtension dri3_image_loader_extension = {
347    .base = { __DRI_IMAGE_LOADER, 1 },
348 
349    .getBuffers          = loader_dri3_get_buffers,
350    .flushFrontBuffer    = dri3_flush_front_buffer,
351 };
352 
353 static EGLBoolean
dri3_swap_buffers(_EGLDriver * drv,_EGLDisplay * disp,_EGLSurface * draw)354 dri3_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw)
355 {
356    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(draw);
357 
358    /* No-op for a pixmap or pbuffer surface */
359    if (draw->Type == EGL_PIXMAP_BIT || draw->Type == EGL_PBUFFER_BIT)
360       return EGL_FALSE;
361 
362    return loader_dri3_swap_buffers_msc(&dri3_surf->loader_drawable,
363                                        0, 0, 0, 0,
364                                        draw->SwapBehavior == EGL_BUFFER_PRESERVED) != -1;
365 }
366 
367 static EGLBoolean
dri3_copy_buffers(_EGLDriver * drv,_EGLDisplay * disp,_EGLSurface * surf,void * native_pixmap_target)368 dri3_copy_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf,
369                   void *native_pixmap_target)
370 {
371    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
372    xcb_pixmap_t target;
373 
374    STATIC_ASSERT(sizeof(uintptr_t) == sizeof(native_pixmap_target));
375    target = (uintptr_t) native_pixmap_target;
376 
377    loader_dri3_copy_drawable(&dri3_surf->loader_drawable, target,
378                              dri3_surf->loader_drawable.drawable);
379 
380    return EGL_TRUE;
381 }
382 
383 static int
dri3_query_buffer_age(_EGLDriver * drv,_EGLDisplay * dpy,_EGLSurface * surf)384 dri3_query_buffer_age(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surf)
385 {
386    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
387 
388    return loader_dri3_query_buffer_age(&dri3_surf->loader_drawable);
389 }
390 
391 static EGLBoolean
dri3_query_surface(_EGLDriver * drv,_EGLDisplay * dpy,_EGLSurface * surf,EGLint attribute,EGLint * value)392 dri3_query_surface(_EGLDriver *drv, _EGLDisplay *dpy,
393                    _EGLSurface *surf, EGLint attribute,
394                    EGLint *value)
395 {
396    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
397 
398    switch (attribute) {
399    case EGL_WIDTH:
400    case EGL_HEIGHT:
401       loader_dri3_update_drawable_geometry(&dri3_surf->loader_drawable);
402       break;
403    default:
404       break;
405    }
406 
407    return _eglQuerySurface(drv, dpy, surf, attribute, value);
408 }
409 
410 static __DRIdrawable *
dri3_get_dri_drawable(_EGLSurface * surf)411 dri3_get_dri_drawable(_EGLSurface *surf)
412 {
413    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
414 
415    return dri3_surf->loader_drawable.dri_drawable;
416 }
417 
418 static void
dri3_close_screen_notify(_EGLDisplay * dpy)419 dri3_close_screen_notify(_EGLDisplay *dpy)
420 {
421    struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy);
422 
423    loader_dri3_close_screen(dri2_dpy->dri_screen);
424 }
425 
426 struct dri2_egl_display_vtbl dri3_x11_display_vtbl = {
427    .authenticate = dri3_authenticate,
428    .create_window_surface = dri3_create_window_surface,
429    .create_pixmap_surface = dri3_create_pixmap_surface,
430    .create_pbuffer_surface = dri3_create_pbuffer_surface,
431    .destroy_surface = dri3_destroy_surface,
432    .create_image = dri3_create_image_khr,
433    .swap_interval = dri3_set_swap_interval,
434    .swap_buffers = dri3_swap_buffers,
435    .swap_buffers_with_damage = dri2_fallback_swap_buffers_with_damage,
436    .swap_buffers_region = dri2_fallback_swap_buffers_region,
437    .set_damage_region = dri2_fallback_set_damage_region,
438    .post_sub_buffer = dri2_fallback_post_sub_buffer,
439    .copy_buffers = dri3_copy_buffers,
440    .query_buffer_age = dri3_query_buffer_age,
441    .query_surface = dri3_query_surface,
442    .create_wayland_buffer_from_image = dri2_fallback_create_wayland_buffer_from_image,
443    .get_sync_values = dri3_get_sync_values,
444    .get_dri_drawable = dri3_get_dri_drawable,
445    .close_screen_notify = dri3_close_screen_notify,
446 };
447 
448 EGLBoolean
dri3_x11_connect(struct dri2_egl_display * dri2_dpy)449 dri3_x11_connect(struct dri2_egl_display *dri2_dpy)
450 {
451    xcb_dri3_query_version_reply_t *dri3_query;
452    xcb_dri3_query_version_cookie_t dri3_query_cookie;
453    xcb_present_query_version_reply_t *present_query;
454    xcb_present_query_version_cookie_t present_query_cookie;
455    xcb_generic_error_t *error;
456    const xcb_query_extension_reply_t *extension;
457 
458    xcb_prefetch_extension_data (dri2_dpy->conn, &xcb_dri3_id);
459    xcb_prefetch_extension_data (dri2_dpy->conn, &xcb_present_id);
460 
461    extension = xcb_get_extension_data(dri2_dpy->conn, &xcb_dri3_id);
462    if (!(extension && extension->present))
463       return EGL_FALSE;
464 
465    extension = xcb_get_extension_data(dri2_dpy->conn, &xcb_present_id);
466    if (!(extension && extension->present))
467       return EGL_FALSE;
468 
469    dri3_query_cookie = xcb_dri3_query_version(dri2_dpy->conn,
470                                               XCB_DRI3_MAJOR_VERSION,
471                                               XCB_DRI3_MINOR_VERSION);
472 
473    present_query_cookie = xcb_present_query_version(dri2_dpy->conn,
474                                                     XCB_PRESENT_MAJOR_VERSION,
475                                                     XCB_PRESENT_MINOR_VERSION);
476 
477    dri3_query =
478       xcb_dri3_query_version_reply(dri2_dpy->conn, dri3_query_cookie, &error);
479    if (dri3_query == NULL || error != NULL) {
480       _eglLog(_EGL_WARNING, "DRI3: failed to query the version");
481       free(dri3_query);
482       free(error);
483       return EGL_FALSE;
484    }
485    free(dri3_query);
486 
487    present_query =
488       xcb_present_query_version_reply(dri2_dpy->conn,
489                                       present_query_cookie, &error);
490    if (present_query == NULL || error != NULL) {
491       _eglLog(_EGL_WARNING, "DRI3: failed to query Present version");
492       free(present_query);
493       free(error);
494       return EGL_FALSE;
495    }
496    free(present_query);
497 
498    dri2_dpy->fd = loader_dri3_open(dri2_dpy->conn, dri2_dpy->screen->root, 0);
499    if (dri2_dpy->fd < 0) {
500       int conn_error = xcb_connection_has_error(dri2_dpy->conn);
501       _eglLog(_EGL_WARNING, "DRI3: Screen seems not DRI3 capable");
502 
503       if (conn_error)
504          _eglLog(_EGL_WARNING, "DRI3: Failed to initialize");
505 
506       return EGL_FALSE;
507    }
508 
509    dri2_dpy->fd = loader_get_user_preferred_fd(dri2_dpy->fd, &dri2_dpy->is_different_gpu);
510 
511    dri2_dpy->driver_name = loader_get_driver_for_fd(dri2_dpy->fd);
512    if (!dri2_dpy->driver_name) {
513       _eglLog(_EGL_WARNING, "DRI3: No driver found");
514       close(dri2_dpy->fd);
515       return EGL_FALSE;
516    }
517 
518 #ifdef HAVE_WAYLAND_PLATFORM
519    /* Only try to get a render device name since dri3 doesn't provide a
520     * mechanism for authenticating client opened device node fds. If this
521     * fails then don't advertise the extension. */
522    dri2_dpy->device_name = drmGetRenderDeviceNameFromFd(dri2_dpy->fd);
523 #endif
524 
525    return EGL_TRUE;
526 }
527