diff options
author | Boyan Ding <boyan.j.ding@gmail.com> | 2015-07-21 23:44:00 +0800 |
---|---|---|
committer | Martin Peres <martin.peres@linux.intel.com> | 2015-11-17 17:26:20 +0200 |
commit | f35198badeb956a8f435727d805a47c7e42610d0 (patch) | |
tree | 055cc089ff20ad8101c632f4c2d9612f1c7684bf | |
parent | a25df5457121d40fef86929d4c10d8058a4d5c72 (diff) |
egl/x11: Implement dri3 support with loader's dri3 helper
v2: From Martin Peres
- Tell we are compiling the dri3 backend in configure.ac
- Update the Makefile.am
- get rid of the LIBDRM_HAS_RENDERNODE_SUPPORT macro
- fix some warnings related to EGLuint64KHR to int64_t conversions
- use dri2_get_dri_config to get the __DRIconfig instead of open-coding it
- replace the occasional tabs with spaces
v3: From Martin Peres
- fix and indent problem (Matt Turner)
- drop the authenticate function, use NULL in the vtable instead (Emil)
- drop some useless includes (Emil Velikov)
- mandate libdrm (Emil Velikov)
- link to xcb-dri3 (Kristian Høgsberg)
- convert to the new loader interface for drwable (Kristian)
- remove some dead code after the dropping of some vfuncs (Kristian)
- add a comment on the topic of rendering to the frontbuffer
v4: From Martin Peres
- do not expose the preserved swap behavior (Acked by Eric Anholt)
Signed-off-by: Boyan Ding <boyan.j.ding@gmail.com>
Signed-off-by: Martin Peres <martin.peres@linux.intel.com>
Reviewed-by: Kristian Høgsberg <krh@bitplanet.net>
Reviewed-by: Emil Velikov <emil.velikov@collabora.co.uk>
-rw-r--r-- | configure.ac | 9 | ||||
-rw-r--r-- | src/egl/Makefile.am | 10 | ||||
-rw-r--r-- | src/egl/drivers/dri2/egl_dri2.c | 66 | ||||
-rw-r--r-- | src/egl/drivers/dri2/egl_dri2.h | 14 | ||||
-rw-r--r-- | src/egl/drivers/dri2/platform_x11.c | 117 | ||||
-rw-r--r-- | src/egl/drivers/dri2/platform_x11_dri3.c | 472 | ||||
-rw-r--r-- | src/egl/drivers/dri2/platform_x11_dri3.h | 41 |
7 files changed, 714 insertions, 15 deletions
diff --git a/configure.ac b/configure.ac index 9ea9ab22346a..32fb989a898c 100644 --- a/configure.ac +++ b/configure.ac @@ -1568,6 +1568,12 @@ if test "x$enable_egl" = xyes; then if test "x$enable_shared_glapi" = xno; then AC_MSG_ERROR([egl_dri2 requires --enable-shared-glapi]) fi + if test "x$enable_dri3" = xyes; then + HAVE_EGL_DRIVER_DRI3=1 + if test "x$enable_shared_glapi" = xno; then + AC_MSG_ERROR([egl_dri3 requires --enable-shared-glapi]) + fi + fi else # Avoid building an "empty" libEGL. Drop/update this # when other backends (haiku?) come along. @@ -2520,6 +2526,9 @@ if test "$enable_egl" = yes; then if test "x$HAVE_EGL_DRIVER_DRI2" != "x"; then egl_drivers="$egl_drivers builtin:egl_dri2" fi + if test "x$HAVE_EGL_DRIVER_DRI3" != "x"; then + egl_drivers="$egl_drivers builtin:egl_dri3" + fi echo " EGL drivers: $egl_drivers" fi diff --git a/src/egl/Makefile.am b/src/egl/Makefile.am index 5c2ba301ffb3..88fe13acbd41 100644 --- a/src/egl/Makefile.am +++ b/src/egl/Makefile.am @@ -47,12 +47,19 @@ libEGL_la_LDFLAGS = \ $(LD_NO_UNDEFINED) dri2_backend_FILES = +dri3_backend_FILES = if HAVE_EGL_PLATFORM_X11 AM_CFLAGS += -DHAVE_X11_PLATFORM AM_CFLAGS += $(XCB_DRI2_CFLAGS) libEGL_la_LIBADD += $(XCB_DRI2_LIBS) dri2_backend_FILES += drivers/dri2/platform_x11.c + +if HAVE_DRI3 +dri3_backend_FILES += \ + drivers/dri2/platform_x11_dri3.c +libEGL_la_LIBADD += $(top_builddir)/src/loader/libloader_dri3_helper.la +endif endif if HAVE_EGL_PLATFORM_WAYLAND @@ -88,7 +95,8 @@ AM_CFLAGS += \ libEGL_la_SOURCES += \ $(dri2_backend_core_FILES) \ - $(dri2_backend_FILES) + $(dri2_backend_FILES) \ + $(dri3_backend_FILES) libEGL_la_LIBADD += $(top_builddir)/src/loader/libloader.la libEGL_la_LIBADD += $(DLOPEN_LIBS) $(LIBDRM_LIBS) diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c index ceff01ff523d..ba16b94e651e 100644 --- a/src/egl/drivers/dri2/egl_dri2.c +++ b/src/egl/drivers/dri2/egl_dri2.c @@ -352,6 +352,12 @@ struct dri2_extension_match { int offset; }; +static struct dri2_extension_match dri3_driver_extensions[] = { + { __DRI_CORE, 1, offsetof(struct dri2_egl_display, core) }, + { __DRI_IMAGE_DRIVER, 1, offsetof(struct dri2_egl_display, image_driver) }, + { NULL, 0, 0 } +}; + static struct dri2_extension_match dri2_driver_extensions[] = { { __DRI_CORE, 1, offsetof(struct dri2_egl_display, core) }, { __DRI_DRI2, 2, offsetof(struct dri2_egl_display, dri2) }, @@ -494,6 +500,25 @@ dri2_open_driver(_EGLDisplay *disp) } EGLBoolean +dri2_load_driver_dri3(_EGLDisplay *disp) +{ + struct dri2_egl_display *dri2_dpy = disp->DriverData; + const __DRIextension **extensions; + + extensions = dri2_open_driver(disp); + if (!extensions) + return EGL_FALSE; + + if (!dri2_bind_extensions(dri2_dpy, dri3_driver_extensions, extensions)) { + dlclose(dri2_dpy->driver); + return EGL_FALSE; + } + dri2_dpy->driver_extensions = extensions; + + return EGL_TRUE; +} + +EGLBoolean dri2_load_driver(_EGLDisplay *disp) { struct dri2_egl_display *dri2_dpy = disp->DriverData; @@ -550,7 +575,9 @@ dri2_setup_screen(_EGLDisplay *disp) struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); unsigned int api_mask; - if (dri2_dpy->dri2) { + if (dri2_dpy->image_driver) { + api_mask = dri2_dpy->image_driver->getAPIMask(dri2_dpy->dri_screen); + } else if (dri2_dpy->dri2) { api_mask = dri2_dpy->dri2->getAPIMask(dri2_dpy->dri_screen); } else { assert(dri2_dpy->swrast); @@ -570,7 +597,7 @@ dri2_setup_screen(_EGLDisplay *disp) if (api_mask & (1 << __DRI_API_GLES3)) disp->ClientAPIs |= EGL_OPENGL_ES3_BIT_KHR; - assert(dri2_dpy->dri2 || dri2_dpy->swrast); + assert(dri2_dpy->image_driver || dri2_dpy->dri2 || dri2_dpy->swrast); disp->Extensions.KHR_surfaceless_context = EGL_TRUE; disp->Extensions.MESA_configless_context = EGL_TRUE; @@ -578,7 +605,8 @@ dri2_setup_screen(_EGLDisplay *disp) __DRI2_RENDERER_HAS_FRAMEBUFFER_SRGB)) disp->Extensions.KHR_gl_colorspace = EGL_TRUE; - if ((dri2_dpy->dri2 && dri2_dpy->dri2->base.version >= 3) || + if (dri2_dpy->image_driver || + (dri2_dpy->dri2 && dri2_dpy->dri2->base.version >= 3) || (dri2_dpy->swrast && dri2_dpy->swrast->base.version >= 3)) { disp->Extensions.KHR_create_context = EGL_TRUE; @@ -641,7 +669,14 @@ dri2_create_screen(_EGLDisplay *disp) dri2_dpy = disp->DriverData; - if (dri2_dpy->dri2) { + if (dri2_dpy->image_driver) { + dri2_dpy->dri_screen = + dri2_dpy->image_driver->createNewScreen2(0, dri2_dpy->fd, + dri2_dpy->extensions, + dri2_dpy->driver_extensions, + &dri2_dpy->driver_configs, + disp); + } else if (dri2_dpy->dri2) { if (dri2_dpy->dri2->base.version >= 4) { dri2_dpy->dri_screen = dri2_dpy->dri2->createNewScreen2(0, dri2_dpy->fd, @@ -677,7 +712,7 @@ dri2_create_screen(_EGLDisplay *disp) extensions = dri2_dpy->core->getExtensions(dri2_dpy->dri_screen); - if (dri2_dpy->dri2) { + if (dri2_dpy->image_driver || dri2_dpy->dri2) { if (!dri2_bind_extensions(dri2_dpy, dri2_core_extensions, extensions)) goto cleanup_dri_screen; } else { @@ -1024,7 +1059,26 @@ dri2_create_context(_EGLDriver *drv, _EGLDisplay *disp, _EGLConfig *conf, else dri_config = NULL; - if (dri2_dpy->dri2) { + if (dri2_dpy->image_driver) { + unsigned error; + unsigned num_attribs = 8; + uint32_t ctx_attribs[8]; + + if (!dri2_fill_context_attribs(dri2_ctx, dri2_dpy, ctx_attribs, + &num_attribs)) + goto cleanup; + + dri2_ctx->dri_context = + dri2_dpy->image_driver->createContextAttribs(dri2_dpy->dri_screen, + api, + dri_config, + shared, + num_attribs / 2, + ctx_attribs, + & error, + dri2_ctx); + dri2_create_context_attribs_error(error); + } else if (dri2_dpy->dri2) { if (dri2_dpy->dri2->base.version >= 3) { unsigned error; unsigned num_attribs = 8; diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h index c3c9fc0166c6..52ad92b182d3 100644 --- a/src/egl/drivers/dri2/egl_dri2.h +++ b/src/egl/drivers/dri2/egl_dri2.h @@ -35,6 +35,10 @@ #include <xcb/dri2.h> #include <xcb/xfixes.h> #include <X11/Xlib-xcb.h> + +#ifdef HAVE_DRI3 +#include "loader_dri3_helper.h" +#endif #endif #ifdef HAVE_WAYLAND_PLATFORM @@ -160,6 +164,7 @@ struct dri2_egl_display const __DRIconfig **driver_configs; void *driver; const __DRIcoreExtension *core; + const __DRIimageDriverExtension *image_driver; const __DRIdri2Extension *dri2; const __DRIswrastExtension *swrast; const __DRI2flushExtension *flush; @@ -192,6 +197,9 @@ struct dri2_egl_display #ifdef HAVE_X11_PLATFORM xcb_connection_t *conn; int screen; +#ifdef HAVE_DRI3 + struct loader_dri3_extensions loader_dri3_ext; +#endif #endif #ifdef HAVE_WAYLAND_PLATFORM @@ -205,8 +213,9 @@ struct dri2_egl_display int formats; uint32_t capabilities; int is_render_node; - int is_different_gpu; #endif + + int is_different_gpu; }; struct dri2_egl_context @@ -327,6 +336,9 @@ EGLBoolean dri2_load_driver_swrast(_EGLDisplay *disp); EGLBoolean +dri2_load_driver_dri3(_EGLDisplay *disp); + +EGLBoolean dri2_create_screen(_EGLDisplay *disp); __DRIdrawable * diff --git a/src/egl/drivers/dri2/platform_x11.c b/src/egl/drivers/dri2/platform_x11.c index e75dcb90699f..d291b478a25d 100644 --- a/src/egl/drivers/dri2/platform_x11.c +++ b/src/egl/drivers/dri2/platform_x11.c @@ -45,6 +45,10 @@ #include "egl_dri2_fallbacks.h" #include "loader.h" +#ifdef HAVE_DRI3 +#include "platform_x11_dri3.h" +#endif + static EGLBoolean dri2_x11_swap_interval(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf, EGLint interval); @@ -703,7 +707,7 @@ dri2_x11_local_authenticate(_EGLDisplay *disp) static EGLBoolean dri2_x11_add_configs_for_visuals(struct dri2_egl_display *dri2_dpy, - _EGLDisplay *disp) + _EGLDisplay *disp, bool supports_preserved) { xcb_screen_iterator_t s; xcb_depth_iterator_t d; @@ -724,8 +728,10 @@ dri2_x11_add_configs_for_visuals(struct dri2_egl_display *dri2_dpy, surface_type = EGL_WINDOW_BIT | EGL_PIXMAP_BIT | - EGL_PBUFFER_BIT | - EGL_SWAP_BEHAVIOR_PRESERVED_BIT; + EGL_PBUFFER_BIT; + + if (supports_preserved) + surface_type |= EGL_SWAP_BEHAVIOR_PRESERVED_BIT; while (d.rem > 0) { EGLBoolean class_added[6] = { 0, }; @@ -1181,7 +1187,7 @@ dri2_initialize_x11_swrast(_EGLDriver *drv, _EGLDisplay *disp) if (!dri2_create_screen(disp)) goto cleanup_driver; - if (!dri2_x11_add_configs_for_visuals(dri2_dpy, disp)) + if (!dri2_x11_add_configs_for_visuals(dri2_dpy, disp, true)) goto cleanup_configs; /* Fill vtbl last to prevent accidentally calling virtual function during @@ -1252,6 +1258,96 @@ dri2_x11_setup_swap_interval(struct dri2_egl_display *dri2_dpy) } } +#ifdef HAVE_DRI3 +static EGLBoolean +dri2_initialize_x11_dri3(_EGLDriver *drv, _EGLDisplay *disp) +{ + struct dri2_egl_display *dri2_dpy; + + dri2_dpy = calloc(1, sizeof *dri2_dpy); + if (!dri2_dpy) + return _eglError(EGL_BAD_ALLOC, "eglInitialize"); + + disp->DriverData = (void *) dri2_dpy; + if (disp->PlatformDisplay == NULL) { + dri2_dpy->conn = xcb_connect(0, &dri2_dpy->screen); + dri2_dpy->own_device = true; + } else { + Display *dpy = disp->PlatformDisplay; + + dri2_dpy->conn = XGetXCBConnection(dpy); + dri2_dpy->screen = DefaultScreen(dpy); + } + + if (xcb_connection_has_error(dri2_dpy->conn)) { + _eglLog(_EGL_WARNING, "DRI2: xcb_connect failed"); + goto cleanup_dpy; + } + + if (dri2_dpy->conn) { + if (!dri3_x11_connect(dri2_dpy)) + goto cleanup_conn; + } + + if (!dri2_load_driver_dri3(disp)) + goto cleanup_conn; + + dri2_dpy->extensions[0] = &dri3_image_loader_extension.base; + dri2_dpy->extensions[1] = &use_invalidate.base; + dri2_dpy->extensions[2] = &image_lookup_extension.base; + dri2_dpy->extensions[3] = NULL; + + dri2_dpy->swap_available = true; + dri2_dpy->invalidate_available = true; + + if (!dri2_create_screen(disp)) + goto cleanup_fd; + + dri2_x11_setup_swap_interval(dri2_dpy); + + disp->Extensions.NOK_texture_from_pixmap = EGL_TRUE; + disp->Extensions.CHROMIUM_sync_control = EGL_TRUE; + disp->Extensions.EXT_buffer_age = EGL_TRUE; + +#ifdef HAVE_WAYLAND_PLATFORM + disp->Extensions.WL_bind_wayland_display = EGL_TRUE; +#endif + + if (dri2_dpy->conn) { + if (!dri2_x11_add_configs_for_visuals(dri2_dpy, disp, false)) + goto cleanup_configs; + } + + dri2_dpy->loader_dri3_ext.core = dri2_dpy->core; + dri2_dpy->loader_dri3_ext.image_driver = dri2_dpy->image_driver; + dri2_dpy->loader_dri3_ext.flush = dri2_dpy->flush; + dri2_dpy->loader_dri3_ext.tex_buffer = dri2_dpy->tex_buffer; + dri2_dpy->loader_dri3_ext.image = dri2_dpy->image; + dri2_dpy->loader_dri3_ext.config = dri2_dpy->config; + + /* Fill vtbl last to prevent accidentally calling virtual function during + * initialization. + */ + dri2_dpy->vtbl = &dri3_x11_display_vtbl; + + return EGL_TRUE; + + cleanup_configs: + _eglCleanupDisplay(disp); + dri2_dpy->core->destroyScreen(dri2_dpy->dri_screen); + dlclose(dri2_dpy->driver); + cleanup_fd: + close(dri2_dpy->fd); + cleanup_conn: + if (disp->PlatformDisplay == NULL) + xcb_disconnect(dri2_dpy->conn); + cleanup_dpy: + free(dri2_dpy); + + return EGL_FALSE; +} +#endif + static EGLBoolean dri2_initialize_x11_dri2(_EGLDriver *drv, _EGLDisplay *disp) { @@ -1323,7 +1419,7 @@ dri2_initialize_x11_dri2(_EGLDriver *drv, _EGLDisplay *disp) disp->Extensions.WL_bind_wayland_display = EGL_TRUE; #endif - if (!dri2_x11_add_configs_for_visuals(dri2_dpy, disp)) + if (!dri2_x11_add_configs_for_visuals(dri2_dpy, disp, true)) goto cleanup_configs; /* Fill vtbl last to prevent accidentally calling virtual function during @@ -1357,9 +1453,16 @@ dri2_initialize_x11(_EGLDriver *drv, _EGLDisplay *disp) int x11_dri2_accel = (getenv("LIBGL_ALWAYS_SOFTWARE") == NULL); if (x11_dri2_accel) { - if (!dri2_initialize_x11_dri2(drv, disp)) { - initialized = dri2_initialize_x11_swrast(drv, disp); +#ifdef HAVE_DRI3 + if (getenv("LIBGL_DRI3_DISABLE") != NULL || + !dri2_initialize_x11_dri3(drv, disp)) { +#endif + if (!dri2_initialize_x11_dri2(drv, disp)) { + initialized = dri2_initialize_x11_swrast(drv, disp); + } +#ifdef HAVE_DRI3 } +#endif } else { initialized = dri2_initialize_x11_swrast(drv, disp); } diff --git a/src/egl/drivers/dri2/platform_x11_dri3.c b/src/egl/drivers/dri2/platform_x11_dri3.c new file mode 100644 index 000000000000..0b95e4d42de4 --- /dev/null +++ b/src/egl/drivers/dri2/platform_x11_dri3.c @@ -0,0 +1,472 @@ +/* + * Copyright © 2015 Boyan Ding + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <xcb/xcb.h> +#include <xcb/dri3.h> +#include <xcb/present.h> + +#include <xf86drm.h> + +#include "egl_dri2.h" +#include "egl_dri2_fallbacks.h" +#include "platform_x11_dri3.h" + +#include "loader.h" +#include "loader_dri3_helper.h" + +static struct dri3_egl_surface * +loader_drawable_to_egl_surface(struct loader_dri3_drawable *draw) { + size_t offset = offsetof(struct dri3_egl_surface, loader_drawable); + return (struct dri3_egl_surface *)(((void*) draw) - offset); +} + +static int +egl_dri3_get_swap_interval(struct loader_dri3_drawable *draw) +{ + struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw); + + return dri3_surf->base.SwapInterval; +} + +static int +egl_dri3_clamp_swap_interval(struct loader_dri3_drawable *draw, int interval) +{ + struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw); + + if (interval > dri3_surf->base.Config->MaxSwapInterval) + interval = dri3_surf->base.Config->MaxSwapInterval; + else if (interval < dri3_surf->base.Config->MinSwapInterval) + interval = dri3_surf->base.Config->MinSwapInterval; + + return interval; +} + +static void +egl_dri3_set_swap_interval(struct loader_dri3_drawable *draw, int interval) +{ + struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw); + + dri3_surf->base.SwapInterval = interval; +} + +static void +egl_dri3_set_drawable_size(struct loader_dri3_drawable *draw, + int width, int height) +{ + struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw); + + dri3_surf->base.Width = width; + dri3_surf->base.Height = height; +} + +static bool +egl_dri3_in_current_context(struct loader_dri3_drawable *draw) +{ + struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw); + _EGLContext *ctx = _eglGetCurrentContext(); + + return ctx->Resource.Display == dri3_surf->base.Resource.Display; +} + +static __DRIcontext * +egl_dri3_get_dri_context(struct loader_dri3_drawable *draw) +{ + _EGLContext *ctx = _eglGetCurrentContext(); + struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); + + return dri2_ctx->dri_context; +} + +static void +egl_dri3_flush_drawable(struct loader_dri3_drawable *draw, unsigned flags) +{ + struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw); + _EGLDisplay *disp = dri3_surf->base.Resource.Display; + + dri2_flush_drawable_for_swapbuffers(disp, &dri3_surf->base); +} + +static struct loader_dri3_vtable egl_dri3_vtable = { + .get_swap_interval = egl_dri3_get_swap_interval, + .clamp_swap_interval = egl_dri3_clamp_swap_interval, + .set_swap_interval = egl_dri3_set_swap_interval, + .set_drawable_size = egl_dri3_set_drawable_size, + .in_current_context = egl_dri3_in_current_context, + .get_dri_context = egl_dri3_get_dri_context, + .flush_drawable = egl_dri3_flush_drawable, + .show_fps = NULL, +}; + +static EGLBoolean +dri3_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf) +{ + struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf); + + (void) drv; + + if (!_eglPutSurface(surf)) + return EGL_TRUE; + + loader_dri3_drawable_fini(&dri3_surf->loader_drawable); + + free(surf); + + return EGL_TRUE; +} + +static EGLBoolean +dri3_set_swap_interval(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf, + EGLint interval) +{ + struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf); + + loader_dri3_set_swap_interval(&dri3_surf->loader_drawable, interval); + + return EGL_TRUE; +} + +static xcb_screen_t * +get_xcb_screen(xcb_screen_iterator_t iter, int screen) +{ + for (; iter.rem; --screen, xcb_screen_next(&iter)) + if (screen == 0) + return iter.data; + + return NULL; +} + +static _EGLSurface * +dri3_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type, + _EGLConfig *conf, void *native_surface, + const EGLint *attrib_list) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct dri2_egl_config *dri2_conf = dri2_egl_config(conf); + struct dri3_egl_surface *dri3_surf; + const __DRIconfig *dri_config; + xcb_drawable_t drawable; + xcb_screen_iterator_t s; + xcb_screen_t *screen; + + STATIC_ASSERT(sizeof(uintptr_t) == sizeof(native_surface)); + drawable = (uintptr_t) native_surface; + + (void) drv; + + dri3_surf = calloc(1, sizeof *dri3_surf); + if (!dri3_surf) { + _eglError(EGL_BAD_ALLOC, "dri3_create_surface"); + return NULL; + } + + if (!_eglInitSurface(&dri3_surf->base, disp, type, conf, attrib_list)) + goto cleanup_surf; + + if (type == EGL_PBUFFER_BIT) { + s = xcb_setup_roots_iterator(xcb_get_setup(dri2_dpy->conn)); + screen = get_xcb_screen(s, dri2_dpy->screen); + if (!screen) { + _eglError(EGL_BAD_NATIVE_WINDOW, "dri3_create_surface"); + goto cleanup_surf; + } + + drawable = xcb_generate_id(dri2_dpy->conn); + xcb_create_pixmap(dri2_dpy->conn, conf->BufferSize, + drawable, screen->root, + dri3_surf->base.Width, dri3_surf->base.Height); + } + + dri_config = dri2_get_dri_config(dri2_conf, type, + dri3_surf->base.GLColorspace); + + if (loader_dri3_drawable_init(dri2_dpy->conn, drawable, + dri2_dpy->dri_screen, + dri2_dpy->is_different_gpu, dri_config, + &dri2_dpy->loader_dri3_ext, + &egl_dri3_vtable, + &dri3_surf->loader_drawable)) { + _eglError(EGL_BAD_ALLOC, "dri3_surface_create"); + goto cleanup_pixmap; + } + + return &dri3_surf->base; + + cleanup_pixmap: + if (type == EGL_PBUFFER_BIT) + xcb_free_pixmap(dri2_dpy->conn, drawable); + cleanup_surf: + free(dri3_surf); + + return NULL; +} + +/** + * Called via eglCreateWindowSurface(), drv->API.CreateWindowSurface(). + */ +static _EGLSurface * +dri3_create_window_surface(_EGLDriver *drv, _EGLDisplay *disp, + _EGLConfig *conf, void *native_window, + const EGLint *attrib_list) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + _EGLSurface *surf; + + surf = dri3_create_surface(drv, disp, EGL_WINDOW_BIT, conf, + native_window, attrib_list); + if (surf != NULL) + dri3_set_swap_interval(drv, disp, surf, dri2_dpy->default_swap_interval); + + return surf; +} + +static _EGLSurface * +dri3_create_pixmap_surface(_EGLDriver *drv, _EGLDisplay *disp, + _EGLConfig *conf, void *native_pixmap, + const EGLint *attrib_list) +{ + return dri3_create_surface(drv, disp, EGL_PIXMAP_BIT, conf, + native_pixmap, attrib_list); +} + +static _EGLSurface * +dri3_create_pbuffer_surface(_EGLDriver *drv, _EGLDisplay *disp, + _EGLConfig *conf, const EGLint *attrib_list) +{ + return dri3_create_surface(drv, disp, EGL_PBUFFER_BIT, conf, + XCB_WINDOW_NONE, attrib_list); +} + +static EGLBoolean +dri3_get_sync_values(_EGLDisplay *display, _EGLSurface *surface, + EGLuint64KHR *ust, EGLuint64KHR *msc, + EGLuint64KHR *sbc) +{ + struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surface); + + return loader_dri3_wait_for_msc(&dri3_surf->loader_drawable, 0, 0, 0, + (int64_t *) ust, (int64_t *) msc, + (int64_t *) sbc) ? EGL_TRUE : EGL_FALSE; +} + +/** + * Called by the driver when it needs to update the real front buffer with the + * contents of its fake front buffer. + */ +static void +dri3_flush_front_buffer(__DRIdrawable *driDrawable, void *loaderPrivate) +{ + /* There does not seem to be any kind of consensus on whether we should + * support front-buffer rendering or not: + * http://lists.freedesktop.org/archives/mesa-dev/2013-June/040129.html + */ + _eglLog(_EGL_WARNING, "FIXME: egl/x11 doesn't support front buffer rendering."); + (void) driDrawable; + (void) loaderPrivate; +} + +const __DRIimageLoaderExtension dri3_image_loader_extension = { + .base = { __DRI_IMAGE_LOADER, 1 }, + + .getBuffers = loader_dri3_get_buffers, + .flushFrontBuffer = dri3_flush_front_buffer, +}; + +static EGLBoolean +dri3_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw) +{ + struct dri3_egl_surface *dri3_surf = dri3_egl_surface(draw); + + /* No-op for a pixmap or pbuffer surface */ + if (draw->Type == EGL_PIXMAP_BIT || draw->Type == EGL_PBUFFER_BIT) + return 0; + + return loader_dri3_swap_buffers_msc(&dri3_surf->loader_drawable, + 0, 0, 0, 0, + draw->SwapBehavior == EGL_BUFFER_PRESERVED) != -1; +} + +static EGLBoolean +dri3_copy_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf, + void *native_pixmap_target) +{ + struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf); + xcb_pixmap_t target; + + STATIC_ASSERT(sizeof(uintptr_t) == sizeof(native_pixmap_target)); + target = (uintptr_t) native_pixmap_target; + + loader_dri3_copy_drawable(&dri3_surf->loader_drawable, target, + dri3_surf->loader_drawable.drawable); + + return EGL_TRUE; +} + +static int +dri3_query_buffer_age(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surf) +{ + struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf); + + return loader_dri3_query_buffer_age(&dri3_surf->loader_drawable); +} + +static __DRIdrawable * +dri3_get_dri_drawable(_EGLSurface *surf) +{ + struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf); + + return dri3_surf->loader_drawable.dri_drawable; +} + +struct dri2_egl_display_vtbl dri3_x11_display_vtbl = { + .authenticate = NULL, + .create_window_surface = dri3_create_window_surface, + .create_pixmap_surface = dri3_create_pixmap_surface, + .create_pbuffer_surface = dri3_create_pbuffer_surface, + .destroy_surface = dri3_destroy_surface, + .create_image = dri2_create_image_khr, + .swap_interval = dri3_set_swap_interval, + .swap_buffers = dri3_swap_buffers, + .swap_buffers_with_damage = dri2_fallback_swap_buffers_with_damage, + .swap_buffers_region = dri2_fallback_swap_buffers_region, + .post_sub_buffer = dri2_fallback_post_sub_buffer, + .copy_buffers = dri3_copy_buffers, + .query_buffer_age = dri3_query_buffer_age, + .create_wayland_buffer_from_image = dri2_fallback_create_wayland_buffer_from_image, + .get_sync_values = dri3_get_sync_values, + .get_dri_drawable = dri3_get_dri_drawable, +}; + +static char * +dri3_get_device_name(int fd) +{ + char *ret = NULL; + + ret = drmGetRenderDeviceNameFromFd(fd); + if (ret) + return ret; + + /* For dri3, render node support is required for WL_bind_wayland_display. + * In order not to regress on older systems without kernel or libdrm + * support, fall back to dri2. User can override it with environment + * variable if they don't need to use that extension. + */ + if (getenv("EGL_FORCE_DRI3") == NULL) { + _eglLog(_EGL_WARNING, "Render node support not available, falling back to dri2"); + _eglLog(_EGL_WARNING, "If you want to force dri3, set EGL_FORCE_DRI3 environment variable"); + } else + ret = loader_get_device_name_for_fd(fd); + + return ret; +} + +EGLBoolean +dri3_x11_connect(struct dri2_egl_display *dri2_dpy) +{ + xcb_dri3_query_version_reply_t *dri3_query; + xcb_dri3_query_version_cookie_t dri3_query_cookie; + xcb_present_query_version_reply_t *present_query; + xcb_present_query_version_cookie_t present_query_cookie; + xcb_generic_error_t *error; + xcb_screen_iterator_t s; + xcb_screen_t *screen; + const xcb_query_extension_reply_t *extension; + + xcb_prefetch_extension_data (dri2_dpy->conn, &xcb_dri3_id); + xcb_prefetch_extension_data (dri2_dpy->conn, &xcb_present_id); + + extension = xcb_get_extension_data(dri2_dpy->conn, &xcb_dri3_id); + if (!(extension && extension->present)) + return EGL_FALSE; + + extension = xcb_get_extension_data(dri2_dpy->conn, &xcb_present_id); + if (!(extension && extension->present)) + return EGL_FALSE; + + dri3_query_cookie = xcb_dri3_query_version(dri2_dpy->conn, + XCB_DRI3_MAJOR_VERSION, + XCB_DRI3_MINOR_VERSION); + + present_query_cookie = xcb_present_query_version(dri2_dpy->conn, + XCB_PRESENT_MAJOR_VERSION, + XCB_PRESENT_MINOR_VERSION); + + dri3_query = + xcb_dri3_query_version_reply(dri2_dpy->conn, dri3_query_cookie, &error); + if (dri3_query == NULL || error != NULL) { + _eglLog(_EGL_WARNING, "DRI2: failed to query dri3 version"); + free(dri3_query); + free(error); + return EGL_FALSE; + } + free(dri3_query); + + present_query = + xcb_present_query_version_reply(dri2_dpy->conn, + present_query_cookie, &error); + if (present_query == NULL || error != NULL) { + _eglLog(_EGL_WARNING, "DRI2: failed to query Present version"); + free(present_query); + free(error); + return EGL_FALSE; + } + free(present_query); + + s = xcb_setup_roots_iterator(xcb_get_setup(dri2_dpy->conn)); + screen = get_xcb_screen(s, dri2_dpy->screen); + if (!screen) { + _eglError(EGL_BAD_NATIVE_WINDOW, "dri3_x11_connect"); + return EGL_FALSE; + } + + dri2_dpy->fd = loader_dri3_open(dri2_dpy->conn, screen->root, 0); + if (dri2_dpy->fd < 0) { + int conn_error = xcb_connection_has_error(dri2_dpy->conn); + _eglLog(_EGL_WARNING, "DRI2: Screen seem not DRI3 capable"); + + if (conn_error) + _eglLog(_EGL_WARNING, "DRI2: Failed to initialize DRI3"); + + return EGL_FALSE; + } + + dri2_dpy->fd = loader_get_user_preferred_fd(dri2_dpy->fd, &dri2_dpy->is_different_gpu); + + dri2_dpy->driver_name = loader_get_driver_for_fd(dri2_dpy->fd, 0); + if (!dri2_dpy->driver_name) { + _eglLog(_EGL_WARNING, "DRI2: No driver found"); + close(dri2_dpy->fd); + return EGL_FALSE; + } + + dri2_dpy->device_name = dri3_get_device_name(dri2_dpy->fd); + if (!dri2_dpy->device_name) { + close(dri2_dpy->fd); + return EGL_FALSE; + } + + return EGL_TRUE; +} diff --git a/src/egl/drivers/dri2/platform_x11_dri3.h b/src/egl/drivers/dri2/platform_x11_dri3.h new file mode 100644 index 000000000000..13d857242886 --- /dev/null +++ b/src/egl/drivers/dri2/platform_x11_dri3.h @@ -0,0 +1,41 @@ +/* + * Copyright © 2015 Boyan Ding + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef EGL_X11_DRI3_INCLUDED +#define EGL_X11_DRI3_INCLUDED + +#include "egl_dri2.h" + +_EGL_DRIVER_TYPECAST(dri3_egl_surface, _EGLSurface, obj) + +struct dri3_egl_surface { + _EGLSurface base; + struct loader_dri3_drawable loader_drawable; +}; + +extern const __DRIimageLoaderExtension dri3_image_loader_extension; +extern struct dri2_egl_display_vtbl dri3_x11_display_vtbl; + +EGLBoolean +dri3_x11_connect(struct dri2_egl_display *dri2_dpy); + +#endif |