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