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