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