egl/x11: Implement dri3 support with loader's dri3 helper
[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 = dri2_egl_context(ctx);
100
101 return dri2_ctx->dri_context;
102 }
103
104 static void
105 egl_dri3_flush_drawable(struct loader_dri3_drawable *draw, unsigned flags)
106 {
107 struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
108 _EGLDisplay *disp = dri3_surf->base.Resource.Display;
109
110 dri2_flush_drawable_for_swapbuffers(disp, &dri3_surf->base);
111 }
112
113 static struct loader_dri3_vtable egl_dri3_vtable = {
114 .get_swap_interval = egl_dri3_get_swap_interval,
115 .clamp_swap_interval = egl_dri3_clamp_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 .flush_drawable = egl_dri3_flush_drawable,
121 .show_fps = NULL,
122 };
123
124 static EGLBoolean
125 dri3_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf)
126 {
127 struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
128
129 (void) drv;
130
131 if (!_eglPutSurface(surf))
132 return EGL_TRUE;
133
134 loader_dri3_drawable_fini(&dri3_surf->loader_drawable);
135
136 free(surf);
137
138 return EGL_TRUE;
139 }
140
141 static EGLBoolean
142 dri3_set_swap_interval(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf,
143 EGLint interval)
144 {
145 struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
146
147 loader_dri3_set_swap_interval(&dri3_surf->loader_drawable, interval);
148
149 return EGL_TRUE;
150 }
151
152 static xcb_screen_t *
153 get_xcb_screen(xcb_screen_iterator_t iter, int screen)
154 {
155 for (; iter.rem; --screen, xcb_screen_next(&iter))
156 if (screen == 0)
157 return iter.data;
158
159 return NULL;
160 }
161
162 static _EGLSurface *
163 dri3_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type,
164 _EGLConfig *conf, void *native_surface,
165 const EGLint *attrib_list)
166 {
167 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
168 struct dri2_egl_config *dri2_conf = dri2_egl_config(conf);
169 struct dri3_egl_surface *dri3_surf;
170 const __DRIconfig *dri_config;
171 xcb_drawable_t drawable;
172 xcb_screen_iterator_t s;
173 xcb_screen_t *screen;
174
175 STATIC_ASSERT(sizeof(uintptr_t) == sizeof(native_surface));
176 drawable = (uintptr_t) native_surface;
177
178 (void) drv;
179
180 dri3_surf = calloc(1, sizeof *dri3_surf);
181 if (!dri3_surf) {
182 _eglError(EGL_BAD_ALLOC, "dri3_create_surface");
183 return NULL;
184 }
185
186 if (!_eglInitSurface(&dri3_surf->base, disp, type, conf, attrib_list))
187 goto cleanup_surf;
188
189 if (type == EGL_PBUFFER_BIT) {
190 s = xcb_setup_roots_iterator(xcb_get_setup(dri2_dpy->conn));
191 screen = get_xcb_screen(s, dri2_dpy->screen);
192 if (!screen) {
193 _eglError(EGL_BAD_NATIVE_WINDOW, "dri3_create_surface");
194 goto cleanup_surf;
195 }
196
197 drawable = xcb_generate_id(dri2_dpy->conn);
198 xcb_create_pixmap(dri2_dpy->conn, conf->BufferSize,
199 drawable, screen->root,
200 dri3_surf->base.Width, dri3_surf->base.Height);
201 }
202
203 dri_config = dri2_get_dri_config(dri2_conf, type,
204 dri3_surf->base.GLColorspace);
205
206 if (loader_dri3_drawable_init(dri2_dpy->conn, drawable,
207 dri2_dpy->dri_screen,
208 dri2_dpy->is_different_gpu, dri_config,
209 &dri2_dpy->loader_dri3_ext,
210 &egl_dri3_vtable,
211 &dri3_surf->loader_drawable)) {
212 _eglError(EGL_BAD_ALLOC, "dri3_surface_create");
213 goto cleanup_pixmap;
214 }
215
216 return &dri3_surf->base;
217
218 cleanup_pixmap:
219 if (type == EGL_PBUFFER_BIT)
220 xcb_free_pixmap(dri2_dpy->conn, drawable);
221 cleanup_surf:
222 free(dri3_surf);
223
224 return NULL;
225 }
226
227 /**
228 * Called via eglCreateWindowSurface(), drv->API.CreateWindowSurface().
229 */
230 static _EGLSurface *
231 dri3_create_window_surface(_EGLDriver *drv, _EGLDisplay *disp,
232 _EGLConfig *conf, void *native_window,
233 const EGLint *attrib_list)
234 {
235 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
236 _EGLSurface *surf;
237
238 surf = dri3_create_surface(drv, disp, EGL_WINDOW_BIT, conf,
239 native_window, attrib_list);
240 if (surf != NULL)
241 dri3_set_swap_interval(drv, disp, surf, dri2_dpy->default_swap_interval);
242
243 return surf;
244 }
245
246 static _EGLSurface *
247 dri3_create_pixmap_surface(_EGLDriver *drv, _EGLDisplay *disp,
248 _EGLConfig *conf, void *native_pixmap,
249 const EGLint *attrib_list)
250 {
251 return dri3_create_surface(drv, disp, EGL_PIXMAP_BIT, conf,
252 native_pixmap, attrib_list);
253 }
254
255 static _EGLSurface *
256 dri3_create_pbuffer_surface(_EGLDriver *drv, _EGLDisplay *disp,
257 _EGLConfig *conf, const EGLint *attrib_list)
258 {
259 return dri3_create_surface(drv, disp, EGL_PBUFFER_BIT, conf,
260 XCB_WINDOW_NONE, attrib_list);
261 }
262
263 static EGLBoolean
264 dri3_get_sync_values(_EGLDisplay *display, _EGLSurface *surface,
265 EGLuint64KHR *ust, EGLuint64KHR *msc,
266 EGLuint64KHR *sbc)
267 {
268 struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surface);
269
270 return loader_dri3_wait_for_msc(&dri3_surf->loader_drawable, 0, 0, 0,
271 (int64_t *) ust, (int64_t *) msc,
272 (int64_t *) sbc) ? EGL_TRUE : EGL_FALSE;
273 }
274
275 /**
276 * Called by the driver when it needs to update the real front buffer with the
277 * contents of its fake front buffer.
278 */
279 static void
280 dri3_flush_front_buffer(__DRIdrawable *driDrawable, void *loaderPrivate)
281 {
282 /* There does not seem to be any kind of consensus on whether we should
283 * support front-buffer rendering or not:
284 * http://lists.freedesktop.org/archives/mesa-dev/2013-June/040129.html
285 */
286 _eglLog(_EGL_WARNING, "FIXME: egl/x11 doesn't support front buffer rendering.");
287 (void) driDrawable;
288 (void) loaderPrivate;
289 }
290
291 const __DRIimageLoaderExtension dri3_image_loader_extension = {
292 .base = { __DRI_IMAGE_LOADER, 1 },
293
294 .getBuffers = loader_dri3_get_buffers,
295 .flushFrontBuffer = dri3_flush_front_buffer,
296 };
297
298 static EGLBoolean
299 dri3_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw)
300 {
301 struct dri3_egl_surface *dri3_surf = dri3_egl_surface(draw);
302
303 /* No-op for a pixmap or pbuffer surface */
304 if (draw->Type == EGL_PIXMAP_BIT || draw->Type == EGL_PBUFFER_BIT)
305 return 0;
306
307 return loader_dri3_swap_buffers_msc(&dri3_surf->loader_drawable,
308 0, 0, 0, 0,
309 draw->SwapBehavior == EGL_BUFFER_PRESERVED) != -1;
310 }
311
312 static EGLBoolean
313 dri3_copy_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf,
314 void *native_pixmap_target)
315 {
316 struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
317 xcb_pixmap_t target;
318
319 STATIC_ASSERT(sizeof(uintptr_t) == sizeof(native_pixmap_target));
320 target = (uintptr_t) native_pixmap_target;
321
322 loader_dri3_copy_drawable(&dri3_surf->loader_drawable, target,
323 dri3_surf->loader_drawable.drawable);
324
325 return EGL_TRUE;
326 }
327
328 static int
329 dri3_query_buffer_age(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surf)
330 {
331 struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
332
333 return loader_dri3_query_buffer_age(&dri3_surf->loader_drawable);
334 }
335
336 static __DRIdrawable *
337 dri3_get_dri_drawable(_EGLSurface *surf)
338 {
339 struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
340
341 return dri3_surf->loader_drawable.dri_drawable;
342 }
343
344 struct dri2_egl_display_vtbl dri3_x11_display_vtbl = {
345 .authenticate = NULL,
346 .create_window_surface = dri3_create_window_surface,
347 .create_pixmap_surface = dri3_create_pixmap_surface,
348 .create_pbuffer_surface = dri3_create_pbuffer_surface,
349 .destroy_surface = dri3_destroy_surface,
350 .create_image = dri2_create_image_khr,
351 .swap_interval = dri3_set_swap_interval,
352 .swap_buffers = dri3_swap_buffers,
353 .swap_buffers_with_damage = dri2_fallback_swap_buffers_with_damage,
354 .swap_buffers_region = dri2_fallback_swap_buffers_region,
355 .post_sub_buffer = dri2_fallback_post_sub_buffer,
356 .copy_buffers = dri3_copy_buffers,
357 .query_buffer_age = dri3_query_buffer_age,
358 .create_wayland_buffer_from_image = dri2_fallback_create_wayland_buffer_from_image,
359 .get_sync_values = dri3_get_sync_values,
360 .get_dri_drawable = dri3_get_dri_drawable,
361 };
362
363 static char *
364 dri3_get_device_name(int fd)
365 {
366 char *ret = NULL;
367
368 ret = drmGetRenderDeviceNameFromFd(fd);
369 if (ret)
370 return ret;
371
372 /* For dri3, render node support is required for WL_bind_wayland_display.
373 * In order not to regress on older systems without kernel or libdrm
374 * support, fall back to dri2. User can override it with environment
375 * variable if they don't need to use that extension.
376 */
377 if (getenv("EGL_FORCE_DRI3") == NULL) {
378 _eglLog(_EGL_WARNING, "Render node support not available, falling back to dri2");
379 _eglLog(_EGL_WARNING, "If you want to force dri3, set EGL_FORCE_DRI3 environment variable");
380 } else
381 ret = loader_get_device_name_for_fd(fd);
382
383 return ret;
384 }
385
386 EGLBoolean
387 dri3_x11_connect(struct dri2_egl_display *dri2_dpy)
388 {
389 xcb_dri3_query_version_reply_t *dri3_query;
390 xcb_dri3_query_version_cookie_t dri3_query_cookie;
391 xcb_present_query_version_reply_t *present_query;
392 xcb_present_query_version_cookie_t present_query_cookie;
393 xcb_generic_error_t *error;
394 xcb_screen_iterator_t s;
395 xcb_screen_t *screen;
396 const xcb_query_extension_reply_t *extension;
397
398 xcb_prefetch_extension_data (dri2_dpy->conn, &xcb_dri3_id);
399 xcb_prefetch_extension_data (dri2_dpy->conn, &xcb_present_id);
400
401 extension = xcb_get_extension_data(dri2_dpy->conn, &xcb_dri3_id);
402 if (!(extension && extension->present))
403 return EGL_FALSE;
404
405 extension = xcb_get_extension_data(dri2_dpy->conn, &xcb_present_id);
406 if (!(extension && extension->present))
407 return EGL_FALSE;
408
409 dri3_query_cookie = xcb_dri3_query_version(dri2_dpy->conn,
410 XCB_DRI3_MAJOR_VERSION,
411 XCB_DRI3_MINOR_VERSION);
412
413 present_query_cookie = xcb_present_query_version(dri2_dpy->conn,
414 XCB_PRESENT_MAJOR_VERSION,
415 XCB_PRESENT_MINOR_VERSION);
416
417 dri3_query =
418 xcb_dri3_query_version_reply(dri2_dpy->conn, dri3_query_cookie, &error);
419 if (dri3_query == NULL || error != NULL) {
420 _eglLog(_EGL_WARNING, "DRI2: failed to query dri3 version");
421 free(dri3_query);
422 free(error);
423 return EGL_FALSE;
424 }
425 free(dri3_query);
426
427 present_query =
428 xcb_present_query_version_reply(dri2_dpy->conn,
429 present_query_cookie, &error);
430 if (present_query == NULL || error != NULL) {
431 _eglLog(_EGL_WARNING, "DRI2: failed to query Present version");
432 free(present_query);
433 free(error);
434 return EGL_FALSE;
435 }
436 free(present_query);
437
438 s = xcb_setup_roots_iterator(xcb_get_setup(dri2_dpy->conn));
439 screen = get_xcb_screen(s, dri2_dpy->screen);
440 if (!screen) {
441 _eglError(EGL_BAD_NATIVE_WINDOW, "dri3_x11_connect");
442 return EGL_FALSE;
443 }
444
445 dri2_dpy->fd = loader_dri3_open(dri2_dpy->conn, screen->root, 0);
446 if (dri2_dpy->fd < 0) {
447 int conn_error = xcb_connection_has_error(dri2_dpy->conn);
448 _eglLog(_EGL_WARNING, "DRI2: Screen seem not DRI3 capable");
449
450 if (conn_error)
451 _eglLog(_EGL_WARNING, "DRI2: Failed to initialize DRI3");
452
453 return EGL_FALSE;
454 }
455
456 dri2_dpy->fd = loader_get_user_preferred_fd(dri2_dpy->fd, &dri2_dpy->is_different_gpu);
457
458 dri2_dpy->driver_name = loader_get_driver_for_fd(dri2_dpy->fd, 0);
459 if (!dri2_dpy->driver_name) {
460 _eglLog(_EGL_WARNING, "DRI2: No driver found");
461 close(dri2_dpy->fd);
462 return EGL_FALSE;
463 }
464
465 dri2_dpy->device_name = dri3_get_device_name(dri2_dpy->fd);
466 if (!dri2_dpy->device_name) {
467 close(dri2_dpy->fd);
468 return EGL_FALSE;
469 }
470
471 return EGL_TRUE;
472 }