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