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