2 * Copyright © 2015 Boyan Ding
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.
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
30 #include <xcb/present.h>
35 #include "egl_dri2_fallbacks.h"
36 #include "platform_x11_dri3.h"
39 #include "loader_dri3_helper.h"
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
);
48 egl_dri3_get_swap_interval(struct loader_dri3_drawable
*draw
)
50 struct dri3_egl_surface
*dri3_surf
= loader_drawable_to_egl_surface(draw
);
52 return dri3_surf
->base
.SwapInterval
;
56 egl_dri3_clamp_swap_interval(struct loader_dri3_drawable
*draw
, int interval
)
58 struct dri3_egl_surface
*dri3_surf
= loader_drawable_to_egl_surface(draw
);
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
;
69 egl_dri3_set_swap_interval(struct loader_dri3_drawable
*draw
, int interval
)
71 struct dri3_egl_surface
*dri3_surf
= loader_drawable_to_egl_surface(draw
);
73 dri3_surf
->base
.SwapInterval
= interval
;
77 egl_dri3_set_drawable_size(struct loader_dri3_drawable
*draw
,
78 int width
, int height
)
80 struct dri3_egl_surface
*dri3_surf
= loader_drawable_to_egl_surface(draw
);
82 dri3_surf
->base
.Width
= width
;
83 dri3_surf
->base
.Height
= height
;
87 egl_dri3_in_current_context(struct loader_dri3_drawable
*draw
)
89 struct dri3_egl_surface
*dri3_surf
= loader_drawable_to_egl_surface(draw
);
90 _EGLContext
*ctx
= _eglGetCurrentContext();
92 return ctx
->Resource
.Display
== dri3_surf
->base
.Resource
.Display
;
96 egl_dri3_get_dri_context(struct loader_dri3_drawable
*draw
)
98 _EGLContext
*ctx
= _eglGetCurrentContext();
99 struct dri2_egl_context
*dri2_ctx
= dri2_egl_context(ctx
);
101 return dri2_ctx
->dri_context
;
105 egl_dri3_flush_drawable(struct loader_dri3_drawable
*draw
, unsigned flags
)
107 struct dri3_egl_surface
*dri3_surf
= loader_drawable_to_egl_surface(draw
);
108 _EGLDisplay
*disp
= dri3_surf
->base
.Resource
.Display
;
110 dri2_flush_drawable_for_swapbuffers(disp
, &dri3_surf
->base
);
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
,
125 dri3_destroy_surface(_EGLDriver
*drv
, _EGLDisplay
*disp
, _EGLSurface
*surf
)
127 struct dri3_egl_surface
*dri3_surf
= dri3_egl_surface(surf
);
131 if (!_eglPutSurface(surf
))
134 loader_dri3_drawable_fini(&dri3_surf
->loader_drawable
);
142 dri3_set_swap_interval(_EGLDriver
*drv
, _EGLDisplay
*disp
, _EGLSurface
*surf
,
145 struct dri3_egl_surface
*dri3_surf
= dri3_egl_surface(surf
);
147 loader_dri3_set_swap_interval(&dri3_surf
->loader_drawable
, interval
);
152 static xcb_screen_t
*
153 get_xcb_screen(xcb_screen_iterator_t iter
, int screen
)
155 for (; iter
.rem
; --screen
, xcb_screen_next(&iter
))
163 dri3_create_surface(_EGLDriver
*drv
, _EGLDisplay
*disp
, EGLint type
,
164 _EGLConfig
*conf
, void *native_surface
,
165 const EGLint
*attrib_list
)
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
;
175 STATIC_ASSERT(sizeof(uintptr_t) == sizeof(native_surface
));
176 drawable
= (uintptr_t) native_surface
;
180 dri3_surf
= calloc(1, sizeof *dri3_surf
);
182 _eglError(EGL_BAD_ALLOC
, "dri3_create_surface");
186 if (!_eglInitSurface(&dri3_surf
->base
, disp
, type
, conf
, attrib_list
))
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
);
193 _eglError(EGL_BAD_NATIVE_WINDOW
, "dri3_create_surface");
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
);
203 dri_config
= dri2_get_dri_config(dri2_conf
, type
,
204 dri3_surf
->base
.GLColorspace
);
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
,
211 &dri3_surf
->loader_drawable
)) {
212 _eglError(EGL_BAD_ALLOC
, "dri3_surface_create");
216 return &dri3_surf
->base
;
219 if (type
== EGL_PBUFFER_BIT
)
220 xcb_free_pixmap(dri2_dpy
->conn
, drawable
);
228 * Called via eglCreateWindowSurface(), drv->API.CreateWindowSurface().
231 dri3_create_window_surface(_EGLDriver
*drv
, _EGLDisplay
*disp
,
232 _EGLConfig
*conf
, void *native_window
,
233 const EGLint
*attrib_list
)
235 struct dri2_egl_display
*dri2_dpy
= dri2_egl_display(disp
);
238 surf
= dri3_create_surface(drv
, disp
, EGL_WINDOW_BIT
, conf
,
239 native_window
, attrib_list
);
241 dri3_set_swap_interval(drv
, disp
, surf
, dri2_dpy
->default_swap_interval
);
247 dri3_create_pixmap_surface(_EGLDriver
*drv
, _EGLDisplay
*disp
,
248 _EGLConfig
*conf
, void *native_pixmap
,
249 const EGLint
*attrib_list
)
251 return dri3_create_surface(drv
, disp
, EGL_PIXMAP_BIT
, conf
,
252 native_pixmap
, attrib_list
);
256 dri3_create_pbuffer_surface(_EGLDriver
*drv
, _EGLDisplay
*disp
,
257 _EGLConfig
*conf
, const EGLint
*attrib_list
)
259 return dri3_create_surface(drv
, disp
, EGL_PBUFFER_BIT
, conf
,
260 XCB_WINDOW_NONE
, attrib_list
);
264 dri3_get_sync_values(_EGLDisplay
*display
, _EGLSurface
*surface
,
265 EGLuint64KHR
*ust
, EGLuint64KHR
*msc
,
268 struct dri3_egl_surface
*dri3_surf
= dri3_egl_surface(surface
);
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
;
276 * Called by the driver when it needs to update the real front buffer with the
277 * contents of its fake front buffer.
280 dri3_flush_front_buffer(__DRIdrawable
*driDrawable
, void *loaderPrivate
)
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
286 _eglLog(_EGL_WARNING
, "FIXME: egl/x11 doesn't support front buffer rendering.");
288 (void) loaderPrivate
;
291 const __DRIimageLoaderExtension dri3_image_loader_extension
= {
292 .base
= { __DRI_IMAGE_LOADER
, 1 },
294 .getBuffers
= loader_dri3_get_buffers
,
295 .flushFrontBuffer
= dri3_flush_front_buffer
,
299 dri3_swap_buffers(_EGLDriver
*drv
, _EGLDisplay
*disp
, _EGLSurface
*draw
)
301 struct dri3_egl_surface
*dri3_surf
= dri3_egl_surface(draw
);
303 /* No-op for a pixmap or pbuffer surface */
304 if (draw
->Type
== EGL_PIXMAP_BIT
|| draw
->Type
== EGL_PBUFFER_BIT
)
307 return loader_dri3_swap_buffers_msc(&dri3_surf
->loader_drawable
,
309 draw
->SwapBehavior
== EGL_BUFFER_PRESERVED
) != -1;
313 dri3_copy_buffers(_EGLDriver
*drv
, _EGLDisplay
*disp
, _EGLSurface
*surf
,
314 void *native_pixmap_target
)
316 struct dri3_egl_surface
*dri3_surf
= dri3_egl_surface(surf
);
319 STATIC_ASSERT(sizeof(uintptr_t) == sizeof(native_pixmap_target
));
320 target
= (uintptr_t) native_pixmap_target
;
322 loader_dri3_copy_drawable(&dri3_surf
->loader_drawable
, target
,
323 dri3_surf
->loader_drawable
.drawable
);
329 dri3_query_buffer_age(_EGLDriver
*drv
, _EGLDisplay
*dpy
, _EGLSurface
*surf
)
331 struct dri3_egl_surface
*dri3_surf
= dri3_egl_surface(surf
);
333 return loader_dri3_query_buffer_age(&dri3_surf
->loader_drawable
);
336 static __DRIdrawable
*
337 dri3_get_dri_drawable(_EGLSurface
*surf
)
339 struct dri3_egl_surface
*dri3_surf
= dri3_egl_surface(surf
);
341 return dri3_surf
->loader_drawable
.dri_drawable
;
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
,
364 dri3_get_device_name(int fd
)
368 ret
= drmGetRenderDeviceNameFromFd(fd
);
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.
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");
381 ret
= loader_get_device_name_for_fd(fd
);
387 dri3_x11_connect(struct dri2_egl_display
*dri2_dpy
)
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
;
398 xcb_prefetch_extension_data (dri2_dpy
->conn
, &xcb_dri3_id
);
399 xcb_prefetch_extension_data (dri2_dpy
->conn
, &xcb_present_id
);
401 extension
= xcb_get_extension_data(dri2_dpy
->conn
, &xcb_dri3_id
);
402 if (!(extension
&& extension
->present
))
405 extension
= xcb_get_extension_data(dri2_dpy
->conn
, &xcb_present_id
);
406 if (!(extension
&& extension
->present
))
409 dri3_query_cookie
= xcb_dri3_query_version(dri2_dpy
->conn
,
410 XCB_DRI3_MAJOR_VERSION
,
411 XCB_DRI3_MINOR_VERSION
);
413 present_query_cookie
= xcb_present_query_version(dri2_dpy
->conn
,
414 XCB_PRESENT_MAJOR_VERSION
,
415 XCB_PRESENT_MINOR_VERSION
);
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");
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");
438 s
= xcb_setup_roots_iterator(xcb_get_setup(dri2_dpy
->conn
));
439 screen
= get_xcb_screen(s
, dri2_dpy
->screen
);
441 _eglError(EGL_BAD_NATIVE_WINDOW
, "dri3_x11_connect");
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");
451 _eglLog(_EGL_WARNING
, "DRI2: Failed to initialize DRI3");
456 dri2_dpy
->fd
= loader_get_user_preferred_fd(dri2_dpy
->fd
, &dri2_dpy
->is_different_gpu
);
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");
465 dri2_dpy
->device_name
= dri3_get_device_name(dri2_dpy
->fd
);
466 if (!dri2_dpy
->device_name
) {