[Mesa-dev] [PATCH RFC] egl/dri2: Add dri3 support to x11 platform
Boyan Ding
boyan.j.ding at gmail.com
Wed Jul 1 08:31:22 PDT 2015
Signed-off-by: Boyan Ding <boyan.j.ding at gmail.com>
---
configure.ac | 3 +
src/egl/drivers/dri2/Makefile.am | 5 +
src/egl/drivers/dri2/egl_dri2.c | 65 +-
src/egl/drivers/dri2/egl_dri2.h | 12 +-
src/egl/drivers/dri2/platform_x11.c | 127 ++-
src/egl/drivers/dri2/platform_x11_dri3.c | 1591 ++++++++++++++++++++++++++++++
src/egl/drivers/dri2/platform_x11_dri3.h | 140 +++
7 files changed, 1926 insertions(+), 17 deletions(-)
create mode 100644 src/egl/drivers/dri2/platform_x11_dri3.c
create mode 100644 src/egl/drivers/dri2/platform_x11_dri3.h
diff --git a/configure.ac b/configure.ac
index af61aa2..090e6c9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1545,6 +1545,9 @@ 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
+ EGL_LIB_DEPS="$EGL_LIB_DEPS -lxcb-dri3 -lxcb-present -lXxf86vm -lxshmfence -lxcb-sync"
+ fi
else
# Avoid building an "empty" libEGL. Drop/update this
# when other backends (haiku?) come along.
diff --git a/src/egl/drivers/dri2/Makefile.am b/src/egl/drivers/dri2/Makefile.am
index 55be4a7..d5b511c 100644
--- a/src/egl/drivers/dri2/Makefile.am
+++ b/src/egl/drivers/dri2/Makefile.am
@@ -52,6 +52,11 @@ if HAVE_EGL_PLATFORM_X11
libegl_dri2_la_SOURCES += platform_x11.c
AM_CFLAGS += -DHAVE_X11_PLATFORM
AM_CFLAGS += $(XCB_DRI2_CFLAGS)
+if HAVE_DRI3
+libegl_dri2_la_SOURCES += \
+ platform_x11_dri3.c \
+ platform_x11_dri3.h
+endif
endif
if HAVE_EGL_PLATFORM_WAYLAND
diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
index b1b65f7..052c579 100644
--- a/src/egl/drivers/dri2/egl_dri2.c
+++ b/src/egl/drivers/dri2/egl_dri2.c
@@ -322,6 +322,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) },
@@ -464,6 +470,24 @@ 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);
+ }
+ dri2_dpy->driver_extensions = extensions;
+
+ return EGL_TRUE;
+}
+
+EGLBoolean
dri2_load_driver(_EGLDisplay *disp)
{
struct dri2_egl_display *dri2_dpy = disp->DriverData;
@@ -507,7 +531,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);
@@ -527,11 +553,12 @@ 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;
- 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;
@@ -591,7 +618,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,
@@ -627,7 +661,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 {
@@ -971,7 +1005,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 f0cc6da..d258753 100644
--- a/src/egl/drivers/dri2/egl_dri2.h
+++ b/src/egl/drivers/dri2/egl_dri2.h
@@ -153,11 +153,16 @@ struct dri2_egl_display
int dri2_major;
int dri2_minor;
+ int dri3_major;
+ int dri3_minor;
+ int present_major;
+ int present_minor;
__DRIscreen *dri_screen;
int own_dri_screen;
const __DRIconfig **driver_configs;
void *driver;
const __DRIcoreExtension *core;
+ const __DRIimageDriverExtension *image_driver;
const __DRIdri2Extension *dri2;
const __DRIswrastExtension *swrast;
const __DRI2flushExtension *flush;
@@ -189,6 +194,7 @@ struct dri2_egl_display
#ifdef HAVE_X11_PLATFORM
xcb_connection_t *conn;
int screen;
+ Display *dpy;
#endif
#ifdef HAVE_WAYLAND_PLATFORM
@@ -202,8 +208,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
@@ -324,6 +331,9 @@ EGLBoolean
dri2_load_driver_swrast(_EGLDisplay *disp);
EGLBoolean
+dri2_load_driver_dri3(_EGLDisplay *disp);
+
+EGLBoolean
dri2_create_screen(_EGLDisplay *disp);
__DRIimage *
diff --git a/src/egl/drivers/dri2/platform_x11.c b/src/egl/drivers/dri2/platform_x11.c
index ad40bd5..2ce260f 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);
@@ -1094,13 +1098,17 @@ dri2_initialize_x11_swrast(_EGLDriver *drv, _EGLDisplay *disp)
disp->DriverData = (void *) dri2_dpy;
if (disp->PlatformDisplay == NULL) {
- dri2_dpy->conn = xcb_connect(0, &dri2_dpy->screen);
+ dri2_dpy->dpy = XOpenDisplay(NULL);
+
+ dri2_dpy->conn = XGetXCBConnection(dri2_dpy->dpy);
+ dri2_dpy->screen = DefaultScreen(dri2_dpy->dpy);
+
dri2_dpy->own_device = true;
} else {
- Display *dpy = disp->PlatformDisplay;
+ dri2_dpy->dpy = disp->PlatformDisplay;
- dri2_dpy->conn = XGetXCBConnection(dpy);
- dri2_dpy->screen = DefaultScreen(dpy);
+ dri2_dpy->conn = XGetXCBConnection(dri2_dpy->dpy);
+ dri2_dpy->screen = DefaultScreen(dri2_dpy->dpy);
}
if (xcb_connection_has_error(dri2_dpy->conn)) {
@@ -1202,6 +1210,94 @@ 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->dpy = XOpenDisplay(NULL);
+
+ dri2_dpy->conn = XGetXCBConnection(dri2_dpy->dpy);
+ dri2_dpy->screen = DefaultScreen(dri2_dpy->dpy);
+
+ dri2_dpy->own_device = true;
+ } else {
+ dri2_dpy->dpy = disp->PlatformDisplay;
+
+ dri2_dpy->conn = XGetXCBConnection(dri2_dpy->dpy);
+ dri2_dpy->screen = DefaultScreen(dri2_dpy->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] = &dri3_system_time_extension.base;
+ dri2_dpy->extensions[2] = &use_invalidate.base;
+ dri2_dpy->extensions[3] = &image_lookup_extension.base;
+ dri2_dpy->extensions[4] = 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))
+ goto cleanup_configs;
+ }
+
+ /* 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)
{
@@ -1213,13 +1309,17 @@ dri2_initialize_x11_dri2(_EGLDriver *drv, _EGLDisplay *disp)
disp->DriverData = (void *) dri2_dpy;
if (disp->PlatformDisplay == NULL) {
- dri2_dpy->conn = xcb_connect(0, &dri2_dpy->screen);
+ dri2_dpy->dpy = XOpenDisplay(NULL);
+
+ dri2_dpy->conn = XGetXCBConnection(dri2_dpy->dpy);
+ dri2_dpy->screen = DefaultScreen(dri2_dpy->dpy);
+
dri2_dpy->own_device = true;
} else {
- Display *dpy = disp->PlatformDisplay;
+ dri2_dpy->dpy = disp->PlatformDisplay;
- dri2_dpy->conn = XGetXCBConnection(dpy);
- dri2_dpy->screen = DefaultScreen(dpy);
+ dri2_dpy->conn = XGetXCBConnection(dri2_dpy->dpy);
+ dri2_dpy->screen = DefaultScreen(dri2_dpy->dpy);
}
if (xcb_connection_has_error(dri2_dpy->conn)) {
@@ -1321,9 +1421,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_ENABLE") == 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 0000000..dcd7128
--- /dev/null
+++ b/src/egl/drivers/dri2/platform_x11_dri3.c
@@ -0,0 +1,1591 @@
+/*
+ * Copyright © 2013 Keith Packard
+ *
+ * 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 <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#ifdef XF86VIDMODE
+#include <X11/extensions/xf86vmode.h>
+#endif
+#include <X11/xshmfence.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"
+
+static inline void
+dri3_fence_reset(xcb_connection_t *c, struct dri3_buffer *buffer)
+{
+ xshmfence_reset(buffer->shm_fence);
+}
+
+static inline void
+dri3_fence_set(struct dri3_buffer *buffer)
+{
+ xshmfence_trigger(buffer->shm_fence);
+}
+
+static inline void
+dri3_fence_trigger(xcb_connection_t *c, struct dri3_buffer *buffer)
+{
+ xcb_sync_trigger_fence(c, buffer->sync_fence);
+}
+
+static inline void
+dri3_fence_await(xcb_connection_t *c, struct dri3_buffer *buffer)
+{
+ xcb_flush(c);
+ xshmfence_await(buffer->shm_fence);
+}
+
+static void
+dri3_update_num_back(struct dri3_egl_surface *dri3_surf)
+{
+ dri3_surf->num_back = 1;
+ if (dri3_surf->flipping) {
+ if (!dri3_surf->is_pixmap && !(dri3_surf->present_capabilities & XCB_PRESENT_CAPABILITY_ASYNC))
+ dri3_surf->num_back++;
+ dri3_surf->num_back++;
+ }
+ if (dri3_surf->base.SwapInterval == 0)
+ dri3_surf->num_back++;
+}
+
+/** dri3_free_render_buffer
+ *
+ * Free everything associated with one render buffer including pixmap, fence
+ * stuff and the driver image
+ */
+static void
+dri3_free_render_buffer(struct dri2_egl_display *dri2_dpy,
+ struct dri3_egl_surface *dri3_surf,
+ struct dri3_buffer *buffer)
+{
+ if (buffer->own_pixmap)
+ xcb_free_pixmap(dri2_dpy->conn, buffer->pixmap);
+ xcb_sync_destroy_fence(dri2_dpy->conn, buffer->sync_fence);
+ xshmfence_unmap_shm(buffer->shm_fence);
+ (*dri2_dpy->image->destroyImage)(buffer->image);
+ if (buffer->linear_buffer)
+ (*dri2_dpy->image->destroyImage)(buffer->linear_buffer);
+ free(buffer);
+}
+
+static EGLBoolean
+dri3_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf)
+{
+ struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+ struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
+ int i;
+
+ (void) drv;
+
+ if (!_eglPutSurface(surf))
+ return EGL_TRUE;
+
+ (*dri2_dpy->core->destroyDrawable) (dri3_surf->dri_drawable);
+
+ for (i = 0; i < DRI3_NUM_BUFFERS; i++) {
+ if (dri3_surf->buffers[i])
+ dri3_free_render_buffer(dri2_dpy, dri3_surf, dri3_surf->buffers[i]);
+ }
+
+ if (dri3_surf->special_event)
+ xcb_unregister_for_special_event(dri2_dpy->conn, dri3_surf->special_event);
+ 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);
+
+ if (interval > surf->Config->MaxSwapInterval)
+ interval = surf->Config->MaxSwapInterval;
+ else if (interval < surf->Config->MinSwapInterval)
+ interval = surf->Config->MinSwapInterval;
+
+ dri3_surf->base.SwapInterval = interval;
+ dri3_update_num_back(dri3_surf);
+
+ 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;
+}
+
+/* xDrawable in glx maps to drawable here */
+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;
+ xcb_drawable_t drawable;
+ xcb_get_geometry_cookie_t cookie;
+ xcb_get_geometry_reply_t *reply;
+ xcb_screen_iterator_t s;
+ xcb_generic_error_t *error;
+ xcb_screen_t *screen;
+ GLint vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1;
+
+ 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;
+ }
+
+ dri3_surf->drawable = xcb_generate_id(dri2_dpy->conn);
+ xcb_create_pixmap(dri2_dpy->conn, conf->BufferSize,
+ dri3_surf->drawable, screen->root,
+ dri3_surf->base.Width, dri3_surf->base.Height);
+ } else {
+ dri3_surf->drawable = drawable;
+ }
+
+ dri3_surf->base.SwapInterval = 1; /* default may be overridden below */
+ dri3_surf->have_back = 0;
+ dri3_surf->have_fake_front = 0;
+ dri3_surf->first_init = true;
+
+ if (dri2_dpy->config)
+ dri2_dpy->config->configQueryi(dri2_dpy->dri_screen,
+ "vblank_mode", &vblank_mode);
+
+ switch (vblank_mode) {
+ case DRI_CONF_VBLANK_NEVER:
+ case DRI_CONF_VBLANK_DEF_INTERVAL_0:
+ dri3_surf->base.SwapInterval = 0;
+ break;
+ case DRI_CONF_VBLANK_DEF_INTERVAL_1:
+ case DRI_CONF_VBLANK_ALWAYS_SYNC:
+ default:
+ dri3_surf->base.SwapInterval = 1;
+ break;
+ }
+
+ dri3_update_num_back(dri3_surf);
+
+ /* Create a new drawable */
+ dri3_surf->dri_drawable =
+ (*dri2_dpy->image_driver->createNewDrawable) (dri2_dpy->dri_screen,
+ type == EGL_WINDOW_BIT ?
+ dri2_conf->dri_double_config :
+ dri2_conf->dri_single_config,
+ dri3_surf);
+
+ if (!dri3_surf->dri_drawable) {
+ _eglError(EGL_BAD_ALLOC, "dri3->createNewDrawable");
+ goto cleanup_pixmap;
+ }
+
+ if (type != EGL_PBUFFER_BIT) {
+ cookie = xcb_get_geometry (dri2_dpy->conn, dri3_surf->drawable);
+ reply = xcb_get_geometry_reply (dri2_dpy->conn, cookie, &error);
+ if (reply == NULL || error != NULL) {
+ _eglError(EGL_BAD_ALLOC, "xcb_get_geometry");
+ free(error);
+ goto cleanup_dri_drawable;
+ }
+
+ dri3_surf->base.Width = reply->width;
+ dri3_surf->base.Height = reply->height;
+ dri3_surf->depth = reply->depth;
+ free(reply);
+ }
+
+ /*
+ * Make sure server has the same swap interval we do for the new
+ * drawable.
+ */
+ dri3_set_swap_interval(drv, disp, &dri3_surf->base,
+ dri3_surf->base.SwapInterval);
+
+ return &dri3_surf->base;
+
+ cleanup_dri_drawable:
+ dri2_dpy->core->destroyDrawable(dri3_surf->dri_drawable);
+ cleanup_pixmap:
+ if (type == EGL_PBUFFER_BIT)
+ xcb_free_pixmap(dri2_dpy->conn, dri3_surf->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);
+}
+
+/*
+ * Process one Present event
+ */
+static void
+dri3_handle_present_event(struct dri2_egl_display *dri2_dpy,
+ struct dri3_egl_surface *dri3_surf,
+ xcb_present_generic_event_t *ge)
+{
+ switch (ge->evtype) {
+ case XCB_PRESENT_CONFIGURE_NOTIFY: {
+ xcb_present_configure_notify_event_t *ce = (void *) ge;
+
+ dri3_surf->base.Width = ce->width;
+ dri3_surf->base.Height = ce->height;
+ break;
+ }
+ case XCB_PRESENT_COMPLETE_NOTIFY: {
+ xcb_present_complete_notify_event_t *ce = (void *) ge;
+
+ /* Compute the processed SBC number from the received 32-bit serial number merged
+ * with the upper 32-bits of the sent 64-bit serial number while checking for
+ * wrap
+ */
+ if (ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP) {
+ dri3_surf->recv_sbc = (dri3_surf->send_sbc & 0xffffffff00000000LL) | ce->serial;
+ if (dri3_surf->recv_sbc > dri3_surf->send_sbc)
+ dri3_surf->recv_sbc -= 0x100000000;
+ switch (ce->mode) {
+ case XCB_PRESENT_COMPLETE_MODE_FLIP:
+ dri3_surf->flipping = true;
+ break;
+ case XCB_PRESENT_COMPLETE_MODE_COPY:
+ dri3_surf->flipping = false;
+ break;
+ }
+ dri3_update_num_back(dri3_surf);
+
+ /*
+ if (psc->show_fps_interval)
+ show_fps(priv, ce->ust); */
+
+ dri3_surf->ust = ce->ust;
+ dri3_surf->msc = ce->msc;
+ } else {
+ dri3_surf->recv_msc_serial = ce->serial;
+ dri3_surf->notify_ust = ce->ust;
+ dri3_surf->notify_msc = ce->msc;
+ }
+ break;
+ }
+ case XCB_PRESENT_EVENT_IDLE_NOTIFY: {
+ xcb_present_idle_notify_event_t *ie = (void *) ge;
+ int b;
+
+ for (b = 0; b < sizeof (dri3_surf->buffers) / sizeof (dri3_surf->buffers[0]); b++) {
+ struct dri3_buffer *buf = dri3_surf->buffers[b];
+
+ if (buf && buf->pixmap == ie->pixmap) {
+ buf->busy = 0;
+ if (dri3_surf->num_back <= b && b < DRI3_MAX_BACK) {
+ dri3_free_render_buffer(dri2_dpy, dri3_surf, buf);
+ dri3_surf->buffers[b] = NULL;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+ free(ge);
+}
+
+static bool
+dri3_wait_for_event(struct dri2_egl_display *dri2_dpy,
+ struct dri3_egl_surface *dri3_surf)
+{
+ xcb_generic_event_t *ev;
+ xcb_present_generic_event_t *ge;
+
+ xcb_flush(dri2_dpy->conn);
+ ev = xcb_wait_for_special_event(dri2_dpy->conn, dri3_surf->special_event);
+ if (!ev)
+ return false;
+ ge = (void *) ev;
+ dri3_handle_present_event(dri2_dpy, dri3_surf, ge);
+ return true;
+}
+
+/** dri3_wait_for_msc
+ *
+ * Get the X server to send an event when the target msc/divisor/remainder is
+ * reached.
+ */
+static EGLBoolean
+dri3_wait_for_msc(struct dri2_egl_display *dri2_dpy,
+ struct dri3_egl_surface *dri3_surf,
+ int64_t target_msc, int64_t divisor, int64_t remainder,
+ int64_t *ust, int64_t *msc, int64_t *sbc)
+{
+ uint32_t msc_serial;
+
+ msc_serial = ++dri3_surf->send_msc_serial;
+ xcb_present_notify_msc(dri2_dpy->conn,
+ dri3_surf->drawable,
+ msc_serial,
+ target_msc,
+ divisor,
+ remainder);
+
+ xcb_flush(dri2_dpy->conn);
+
+ /* Wait for the event */
+ if (dri3_surf->special_event) {
+ while ((int32_t) (msc_serial - dri3_surf->recv_msc_serial) > 0) {
+ if (!dri3_wait_for_event(dri2_dpy, dri3_surf))
+ return EGL_FALSE;
+ }
+ }
+
+ *ust = dri3_surf->notify_ust;
+ *msc = dri3_surf->notify_msc;
+ *sbc = dri3_surf->recv_sbc;
+
+ return EGL_TRUE;
+}
+
+static EGLBoolean
+dri3_get_sync_values(_EGLDisplay *display, _EGLSurface *surface,
+ EGLuint64KHR *ust, EGLuint64KHR *msc,
+ EGLuint64KHR *sbc)
+{
+ struct dri2_egl_display *dri2_dpy = dri2_egl_display(display);
+ struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surface);
+
+ return dri3_wait_for_msc(dri2_dpy, dri3_surf, 0, 0, 0, ust, msc, sbc);
+}
+
+/**
+ * Asks the driver to flush any queued work necessary for serializing with the
+ * X command stream, and optionally the slightly more strict requirement of
+ * glFlush() equivalence (which would require flushing even if nothing had
+ * been drawn to a window system framebuffer, for example).
+ */
+static void
+dri3_flush(struct dri2_egl_display *dri2_dpy,
+ struct dri3_egl_surface *dri3_surf,
+ unsigned flags,
+ enum __DRI2throttleReason throttle_reason)
+{
+ _EGLContext *ctx = _eglGetCurrentContext();
+ struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx);
+
+ if (dri2_ctx)
+ (*dri2_dpy->flush->flush_with_flags)(dri2_ctx->dri_context,
+ dri3_surf->dri_drawable,
+ flags, throttle_reason);
+}
+
+static xcb_gcontext_t
+dri3_drawable_gc(struct dri2_egl_display *dri2_dpy,
+ struct dri3_egl_surface *dri3_surf)
+{
+ if (!dri3_surf->gc) {
+ uint32_t v = 0;
+ xcb_create_gc(dri2_dpy->conn,
+ (dri3_surf->gc = xcb_generate_id(dri2_dpy->conn)),
+ dri3_surf->drawable,
+ XCB_GC_GRAPHICS_EXPOSURES,
+ &v);
+ }
+ return dri3_surf->gc;
+}
+
+
+static struct dri3_buffer *
+dri3_back_buffer(struct dri3_egl_surface *dri3_surf)
+{
+ return dri3_surf->buffers[DRI3_BACK_ID(dri3_surf->cur_back)];
+}
+
+static struct dri3_buffer *
+dri3_fake_front_buffer(struct dri3_egl_surface *dri3_surf)
+{
+ return dri3_surf->buffers[DRI3_FRONT_ID];
+}
+
+static void
+dri3_copy_area (xcb_connection_t *c /**< */,
+ xcb_drawable_t src_drawable /**< */,
+ xcb_drawable_t dst_drawable /**< */,
+ xcb_gcontext_t gc /**< */,
+ int16_t src_x /**< */,
+ int16_t src_y /**< */,
+ int16_t dst_x /**< */,
+ int16_t dst_y /**< */,
+ uint16_t width /**< */,
+ uint16_t height /**< */)
+{
+ xcb_void_cookie_t cookie;
+
+ cookie = xcb_copy_area_checked(c,
+ src_drawable,
+ dst_drawable,
+ gc,
+ src_x,
+ src_y,
+ dst_x,
+ dst_y,
+ width,
+ height);
+ xcb_discard_reply(c, cookie.sequence);
+}
+
+/**
+ * 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)
+{
+#if 0
+ struct glx_context *gc;
+ struct dri3_drawable *pdraw = loaderPrivate;
+ struct dri3_screen *psc;
+
+ if (!pdraw)
+ return;
+
+ if (!pdraw->base.psc)
+ return;
+
+ psc = (struct dri3_screen *) pdraw->base.psc;
+
+ (void) __glXInitialize(psc->base.dpy);
+
+ gc = __glXGetCurrentContext();
+
+ dri3_flush(psc, pdraw, __DRI2_FLUSH_DRAWABLE, __DRI2_THROTTLE_FLUSHFRONT);
+
+ dri3_wait_gl(gc);
+#endif
+ /* FIXME */
+ (void) driDrawable;
+ (void) loaderPrivate;
+}
+
+static uint32_t
+dri3_cpp_for_format(uint32_t format) {
+ switch (format) {
+ case __DRI_IMAGE_FORMAT_R8:
+ return 1;
+ case __DRI_IMAGE_FORMAT_RGB565:
+ case __DRI_IMAGE_FORMAT_GR88:
+ return 2;
+ case __DRI_IMAGE_FORMAT_XRGB8888:
+ case __DRI_IMAGE_FORMAT_ARGB8888:
+ case __DRI_IMAGE_FORMAT_ABGR8888:
+ case __DRI_IMAGE_FORMAT_XBGR8888:
+ case __DRI_IMAGE_FORMAT_XRGB2101010:
+ case __DRI_IMAGE_FORMAT_ARGB2101010:
+ case __DRI_IMAGE_FORMAT_SARGB8:
+ return 4;
+ case __DRI_IMAGE_FORMAT_NONE:
+ default:
+ return 0;
+ }
+}
+
+/** dri3_alloc_render_buffer
+ *
+ * Use the driver createImage function to construct a __DRIimage, then
+ * get a file descriptor for that and create an X pixmap from that
+ *
+ * Allocate an xshmfence for synchronization
+ */
+static struct dri3_buffer *
+dri3_alloc_render_buffer(struct dri2_egl_display *dri2_dpy, xcb_drawable_t draw,
+ unsigned int format, int width, int height, int depth)
+{
+ struct dri3_buffer *buffer;
+ __DRIimage *pixmap_buffer;
+ xcb_pixmap_t pixmap;
+ xcb_sync_fence_t sync_fence;
+ struct xshmfence *shm_fence;
+ int buffer_fd, fence_fd;
+ int stride;
+
+ /* Create an xshmfence object and
+ * prepare to send that to the X server
+ */
+
+ fence_fd = xshmfence_alloc_shm();
+ if (fence_fd < 0) {
+ _eglLog(_EGL_WARNING, "DRI3 Fence object allocation failure %s\n", strerror(errno));
+ return NULL;
+ }
+ shm_fence = xshmfence_map_shm(fence_fd);
+ if (shm_fence == NULL) {
+ _eglLog(_EGL_WARNING, "DRI3 Fence object map failure %s\n", strerror(errno));
+ goto no_shm_fence;
+ }
+
+ /* Allocate the image from the driver
+ */
+ buffer = calloc(1, sizeof (struct dri3_buffer));
+ if (!buffer)
+ goto no_buffer;
+
+ buffer->cpp = dri3_cpp_for_format(format);
+ if (!buffer->cpp) {
+ _eglLog(_EGL_WARNING, "DRI3 buffer format %d invalid\n", format);
+ goto no_image;
+ }
+
+ if (!dri2_dpy->is_different_gpu) {
+ buffer->image = (*dri2_dpy->image->createImage) (dri2_dpy->dri_screen,
+ width, height,
+ format,
+ __DRI_IMAGE_USE_SHARE |
+ __DRI_IMAGE_USE_SCANOUT,
+ buffer);
+ pixmap_buffer = buffer->image;
+
+ if (!buffer->image) {
+ _eglLog(_EGL_WARNING, "DRI3 gpu image creation failure\n");
+ goto no_image;
+ }
+ } else {
+ buffer->image = (*dri2_dpy->image->createImage) (dri2_dpy->dri_screen,
+ width, height,
+ format,
+ 0,
+ buffer);
+
+ if (!buffer->image) {
+ _eglLog(_EGL_WARNING, "DRI3 other gpu image creation failure\n");
+ goto no_image;
+ }
+
+ buffer->linear_buffer = (*dri2_dpy->image->createImage) (dri2_dpy->dri_screen,
+ width, height,
+ format,
+ __DRI_IMAGE_USE_SHARE |
+ __DRI_IMAGE_USE_LINEAR,
+ buffer);
+ pixmap_buffer = buffer->linear_buffer;
+
+ if (!buffer->linear_buffer) {
+ _eglLog(_EGL_WARNING, "DRI3 gpu linear image creation failure\n");
+ goto no_linear_buffer;
+ }
+ }
+
+ /* X wants the stride, so ask the image for it
+ */
+ if (!(*dri2_dpy->image->queryImage)(pixmap_buffer, __DRI_IMAGE_ATTRIB_STRIDE, &stride)) {
+ _eglLog(_EGL_WARNING, "DRI3 get image stride failed\n");
+ goto no_buffer_attrib;
+ }
+
+ buffer->pitch = stride;
+
+ if (!(*dri2_dpy->image->queryImage)(pixmap_buffer, __DRI_IMAGE_ATTRIB_FD, &buffer_fd)) {
+ _eglLog(_EGL_WARNING, "DRI3 get image FD failed\n");
+ goto no_buffer_attrib;
+ }
+
+ xcb_dri3_pixmap_from_buffer(dri2_dpy->conn,
+ (pixmap = xcb_generate_id(dri2_dpy->conn)),
+ draw,
+ buffer->size,
+ width, height, buffer->pitch,
+ depth, buffer->cpp * 8,
+ buffer_fd);
+
+ xcb_dri3_fence_from_fd(dri2_dpy->conn,
+ pixmap,
+ (sync_fence = xcb_generate_id(dri2_dpy->conn)),
+ false,
+ fence_fd);
+
+ buffer->pixmap = pixmap;
+ buffer->own_pixmap = true;
+ buffer->sync_fence = sync_fence;
+ buffer->shm_fence = shm_fence;
+ buffer->width = width;
+ buffer->height = height;
+
+ /* Mark the buffer as idle
+ */
+ dri3_fence_set(buffer);
+
+ return buffer;
+
+no_buffer_attrib:
+ (*dri2_dpy->image->destroyImage)(pixmap_buffer);
+no_linear_buffer:
+ if (dri2_dpy->is_different_gpu)
+ (*dri2_dpy->image->destroyImage)(buffer->image);
+no_image:
+ free(buffer);
+no_buffer:
+ xshmfence_unmap_shm(shm_fence);
+no_shm_fence:
+ close(fence_fd);
+ _eglLog(_EGL_WARNING, "DRI3 alloc_render_buffer failed\n");
+ return NULL;
+}
+
+/** dri3_flush_present_events
+ *
+ * Process any present events that have been received from the X server
+ */
+static void
+dri3_flush_present_events(struct dri2_egl_display *dri2_dpy,
+ struct dri3_egl_surface *dri3_surf)
+{
+ /* Check to see if any configuration changes have occurred
+ * since we were last invoked
+ */
+ if (dri3_surf->special_event) {
+ xcb_generic_event_t *ev;
+
+ while ((ev = xcb_poll_for_special_event(dri2_dpy->conn,
+ dri3_surf->special_event)) != NULL) {
+ xcb_present_generic_event_t *ge = (void *) ev;
+ dri3_handle_present_event(dri2_dpy, dri3_surf, ge);
+ }
+ }
+}
+
+/** dri3_update_drawable
+ *
+ * Called the first time we use the drawable and then
+ * after we receive present configure notify events to
+ * track the geometry of the drawable
+ */
+static int
+dri3_update_drawable(__DRIdrawable *driDrawable,
+ struct dri3_egl_surface *dri3_surf,
+ struct dri2_egl_display *dri2_dpy)
+{
+ /* First time through, go get the current drawable geometry
+ */
+ if (dri3_surf->first_init) {
+ xcb_void_cookie_t cookie;
+ xcb_generic_error_t *error;
+ xcb_present_query_capabilities_cookie_t present_capabilities_cookie;
+ xcb_present_query_capabilities_reply_t *present_capabilities_reply;
+
+ dri3_surf->first_init = false;
+
+ /* Try to select for input on the window.
+ *
+ * If the drawable is a window, this will get our events
+ * delivered.
+ *
+ * Otherwise, we'll get a BadWindow error back from this request which
+ * will let us know that the drawable is a pixmap instead.
+ */
+
+
+ cookie = xcb_present_select_input_checked(dri2_dpy->conn,
+ (dri3_surf->eid = xcb_generate_id(dri2_dpy->conn)),
+ dri3_surf->drawable,
+ XCB_PRESENT_EVENT_MASK_CONFIGURE_NOTIFY|
+ XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY|
+ XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY);
+
+ present_capabilities_cookie = xcb_present_query_capabilities(dri2_dpy->conn,
+ dri3_surf->drawable);
+
+ /* Create an XCB event queue to hold present events outside of the usual
+ * application event queue
+ */
+ dri3_surf->special_event = xcb_register_for_special_xge(dri2_dpy->conn,
+ &xcb_present_id,
+ dri3_surf->eid,
+ dri3_surf->stamp);
+
+ dri3_surf->is_pixmap = false;
+
+ /* Check to see if our select input call failed. If it failed with a
+ * BadWindow error, then assume the drawable is a pixmap. Destroy the
+ * special event queue created above and mark the drawable as a pixmap
+ */
+
+ error = xcb_request_check(dri2_dpy->conn, cookie);
+
+ present_capabilities_reply = xcb_present_query_capabilities_reply(dri2_dpy->conn,
+ present_capabilities_cookie,
+ NULL);
+
+ if (present_capabilities_reply) {
+ dri3_surf->present_capabilities = present_capabilities_reply->capabilities;
+ free(present_capabilities_reply);
+ } else
+ dri3_surf->present_capabilities = 0;
+
+ if (error) {
+ if (error->error_code != BadWindow) {
+ free(error);
+ return false;
+ }
+ dri3_surf->is_pixmap = true;
+ xcb_unregister_for_special_event(dri2_dpy->conn, dri3_surf->special_event);
+ dri3_surf->special_event = NULL;
+ }
+ }
+ dri3_flush_present_events(dri2_dpy, dri3_surf);
+ return true;
+}
+
+/* the DRIimage createImage function takes __DRI_IMAGE_FORMAT codes, while
+ * the createImageFromFds call takes __DRI_IMAGE_FOURCC codes. To avoid
+ * complete confusion, just deal in __DRI_IMAGE_FORMAT codes for now and
+ * translate to __DRI_IMAGE_FOURCC codes in the call to createImageFromFds
+ */
+static int
+image_format_to_fourcc(int format)
+{
+
+ /* Convert from __DRI_IMAGE_FORMAT to __DRI_IMAGE_FOURCC (sigh) */
+ switch (format) {
+ case __DRI_IMAGE_FORMAT_SARGB8: return __DRI_IMAGE_FOURCC_SARGB8888;
+ case __DRI_IMAGE_FORMAT_RGB565: return __DRI_IMAGE_FOURCC_RGB565;
+ case __DRI_IMAGE_FORMAT_XRGB8888: return __DRI_IMAGE_FOURCC_XRGB8888;
+ case __DRI_IMAGE_FORMAT_ARGB8888: return __DRI_IMAGE_FOURCC_ARGB8888;
+ case __DRI_IMAGE_FORMAT_ABGR8888: return __DRI_IMAGE_FOURCC_ABGR8888;
+ case __DRI_IMAGE_FORMAT_XBGR8888: return __DRI_IMAGE_FOURCC_XBGR8888;
+ }
+ return 0;
+}
+
+/** dri3_get_pixmap_buffer
+ *
+ * Get the DRM object for a pixmap from the X server and
+ * wrap that with a __DRIimage structure using createImageFromFds
+ */
+static struct dri3_buffer *
+dri3_get_pixmap_buffer(__DRIdrawable *driDrawable,
+ unsigned int format,
+ enum dri3_buffer_type buffer_type,
+ struct dri3_egl_surface *dri3_surf,
+ struct dri2_egl_display *dri2_dpy)
+{
+ int buf_id = dri3_pixmap_buf_id(buffer_type);
+ struct dri3_buffer *buffer = dri3_surf->buffers[buf_id];
+ xcb_drawable_t pixmap;
+ xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie;
+ xcb_dri3_buffer_from_pixmap_reply_t *bp_reply;
+ int *fds;
+ xcb_sync_fence_t sync_fence;
+ struct xshmfence *shm_fence;
+ int fence_fd;
+ __DRIimage *image_planar;
+ int stride, offset;
+
+ if (buffer)
+ return buffer;
+
+ pixmap = dri3_surf->drawable;
+
+ buffer = calloc(1, sizeof (struct dri3_buffer));
+ if (!buffer)
+ goto no_buffer;
+
+ fence_fd = xshmfence_alloc_shm();
+ if (fence_fd < 0)
+ goto no_fence;
+ shm_fence = xshmfence_map_shm(fence_fd);
+ if (shm_fence == NULL) {
+ close (fence_fd);
+ goto no_fence;
+ }
+
+ xcb_dri3_fence_from_fd(dri2_dpy->conn,
+ pixmap,
+ (sync_fence = xcb_generate_id(dri2_dpy->conn)),
+ false,
+ fence_fd);
+
+ /* Get an FD for the pixmap object
+ */
+ bp_cookie = xcb_dri3_buffer_from_pixmap(dri2_dpy->conn, pixmap);
+ bp_reply = xcb_dri3_buffer_from_pixmap_reply(dri2_dpy->conn,
+ bp_cookie, NULL);
+ if (!bp_reply)
+ goto no_image;
+ fds = xcb_dri3_buffer_from_pixmap_reply_fds(dri2_dpy->conn, bp_reply);
+
+ stride = bp_reply->stride;
+ offset = 0;
+
+ /* createImageFromFds creates a wrapper __DRIimage structure which
+ * can deal with multiple planes for things like Yuv images. So, once
+ * we've gotten the planar wrapper, pull the single plane out of it and
+ * discard the wrapper.
+ */
+ image_planar = (*dri2_dpy->image->createImageFromFds) (dri2_dpy->dri_screen,
+ bp_reply->width,
+ bp_reply->height,
+ image_format_to_fourcc(format),
+ fds, 1,
+ &stride, &offset, buffer);
+ close(fds[0]);
+ if (!image_planar)
+ goto no_image;
+
+ buffer->image = (*dri2_dpy->image->fromPlanar)(image_planar, 0, buffer);
+
+ (*dri2_dpy->image->destroyImage)(image_planar);
+
+ if (!buffer->image)
+ goto no_image;
+
+ buffer->pixmap = pixmap;
+ buffer->own_pixmap = false;
+ buffer->width = bp_reply->width;
+ buffer->height = bp_reply->height;
+ buffer->buffer_type = buffer_type;
+ buffer->shm_fence = shm_fence;
+ buffer->sync_fence = sync_fence;
+
+ dri3_surf->buffers[buf_id] = buffer;
+ return buffer;
+
+no_image:
+ xcb_sync_destroy_fence(dri2_dpy->conn, sync_fence);
+ xshmfence_unmap_shm(shm_fence);
+no_fence:
+ free(buffer);
+no_buffer:
+ return NULL;
+}
+
+/** dri3_find_back
+ *
+ * Find an idle back buffer. If there isn't one, then
+ * wait for a present idle notify event from the X server
+ */
+static int
+dri3_find_back(struct dri2_egl_display *dri2_dpy,
+ struct dri3_egl_surface *dri3_surf)
+{
+ int b;
+ xcb_generic_event_t *ev;
+ xcb_present_generic_event_t *ge;
+
+ for (;;) {
+ for (b = 0; b < dri3_surf->num_back; b++) {
+ int id = DRI3_BACK_ID((b + dri3_surf->cur_back) % dri3_surf->num_back);
+ struct dri3_buffer *buffer = dri3_surf->buffers[id];
+
+ if (!buffer || !buffer->busy) {
+ dri3_surf->cur_back = id;
+ return id;
+ }
+ }
+ xcb_flush(dri2_dpy->conn);
+ ev = xcb_wait_for_special_event(dri2_dpy->conn, dri3_surf->special_event);
+ if (!ev)
+ return -1;
+ ge = (void *) ev;
+ dri3_handle_present_event(dri2_dpy, dri3_surf, ge);
+ }
+}
+
+/** dri3_get_buffer
+ *
+ * Find a front or back buffer, allocating new ones as necessary
+ */
+static struct dri3_buffer *
+dri3_get_buffer(__DRIdrawable *driDrawable,
+ unsigned int format,
+ enum dri3_buffer_type buffer_type,
+ struct dri3_egl_surface *dri3_surf,
+ struct dri2_egl_display *dri2_dpy)
+{
+ struct dri3_buffer *buffer;
+ _EGLContext *ctx = _eglGetCurrentContext();
+ struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx);
+ int buf_id;
+
+ if (buffer_type == dri3_buffer_back) {
+ buf_id = dri3_find_back(dri2_dpy, dri3_surf);
+
+ if (buf_id < 0)
+ return NULL;
+ } else {
+ buf_id = DRI3_FRONT_ID;
+ }
+
+ buffer = dri3_surf->buffers[buf_id];
+
+ /* Allocate a new buffer if there isn't an old one, or if that
+ * old one is the wrong size
+ */
+ if (!buffer || buffer->width != dri3_surf->base.Width ||
+ buffer->height != dri3_surf->base.Height) {
+ struct dri3_buffer *new_buffer;
+
+ /* Allocate the new buffers
+ */
+ new_buffer = dri3_alloc_render_buffer(dri2_dpy,
+ dri3_surf->drawable,
+ format,
+ dri3_surf->base.Width,
+ dri3_surf->base.Height,
+ dri3_surf->depth);
+ if (!new_buffer)
+ return NULL;
+
+ /* When resizing, copy the contents of the old buffer, waiting for that
+ * copy to complete using our fences before proceeding
+ */
+ switch (buffer_type) {
+ case dri3_buffer_back:
+ if (buffer) {
+ if (!buffer->linear_buffer) {
+ dri3_fence_reset(dri2_dpy->conn, new_buffer);
+ dri3_fence_await(dri2_dpy->conn, buffer);
+ dri3_copy_area(dri2_dpy->conn,
+ buffer->pixmap,
+ new_buffer->pixmap,
+ dri3_drawable_gc(dri2_dpy, dri3_surf),
+ 0, 0, 0, 0,
+ dri3_surf->base.Width,
+ dri3_surf->base.Height);
+ dri3_fence_trigger(dri2_dpy->conn, new_buffer);
+ } else if (dri2_ctx->base.Resource.Display == dri3_surf->base.Resource.Display) {
+ dri2_dpy->image->blitImage(dri2_ctx->dri_context,
+ new_buffer->image,
+ buffer->image,
+ 0, 0, dri3_surf->base.Width,
+ dri3_surf->base.Height,
+ 0, 0, dri3_surf->base.Width,
+ dri3_surf->base.Height, 0);
+ }
+ dri3_free_render_buffer(dri2_dpy, dri3_surf, buffer);
+ }
+ break;
+ case dri3_buffer_front:
+ dri3_fence_reset(dri2_dpy->conn, new_buffer);
+ dri3_copy_area(dri2_dpy->conn,
+ dri3_surf->drawable,
+ new_buffer->pixmap,
+ dri3_drawable_gc(dri2_dpy, dri3_surf),
+ 0, 0, 0, 0,
+ dri3_surf->base.Width,
+ dri3_surf->base.Height);
+ dri3_fence_trigger(dri2_dpy->conn, new_buffer);
+
+ if (new_buffer->linear_buffer &&
+ dri2_ctx->base.Resource.Display == dri3_surf->base.Resource.Display) {
+ dri3_fence_await(dri2_dpy->conn, new_buffer);
+ dri2_dpy->image->blitImage(dri2_ctx->dri_context,
+ new_buffer->image,
+ new_buffer->linear_buffer,
+ 0, 0, dri3_surf->base.Width,
+ dri3_surf->base.Height,
+ 0, 0, dri3_surf->base.Width,
+ dri3_surf->base.Height, 0);
+ }
+ break;
+ }
+ buffer = new_buffer;
+ buffer->buffer_type = buffer_type;
+ dri3_surf->buffers[buf_id] = buffer;
+ }
+ dri3_fence_await(dri2_dpy->conn, buffer);
+
+ /* Return the requested buffer */
+ return buffer;
+}
+
+/** dri3_free_buffers
+ *
+ * Free the front bufffer or all of the back buffers. Used
+ * when the application changes which buffers it needs
+ */
+static void
+dri3_free_buffers(__DRIdrawable *driDrawable,
+ enum dri3_buffer_type buffer_type,
+ struct dri3_egl_surface *dri3_surf,
+ struct dri2_egl_display *dri2_dpy)
+{
+ struct dri3_buffer *buffer;
+ int first_id;
+ int n_id;
+ int buf_id;
+
+ switch (buffer_type) {
+ case dri3_buffer_back:
+ first_id = DRI3_BACK_ID(0);
+ n_id = DRI3_MAX_BACK;
+ break;
+ case dri3_buffer_front:
+ first_id = DRI3_FRONT_ID;
+ n_id = 1;
+ }
+
+ for (buf_id = first_id; buf_id < first_id + n_id; buf_id++) {
+ buffer = dri3_surf->buffers[buf_id];
+ if (buffer) {
+ dri3_free_render_buffer(dri2_dpy, dri3_surf, buffer);
+ dri3_surf->buffers[buf_id] = NULL;
+ }
+ }
+}
+
+/** dri3_get_buffers
+ *
+ * The published buffer allocation API.
+ * Returns all of the necessary buffers, allocating
+ * as needed.
+ */
+static int
+dri3_get_buffers(__DRIdrawable *driDrawable,
+ unsigned int format,
+ uint32_t *stamp,
+ void *loaderPrivate,
+ uint32_t buffer_mask,
+ struct __DRIimageList *buffers)
+{
+ struct dri3_egl_surface *dri3_surf = loaderPrivate;
+ struct dri2_egl_display *dri2_dpy =
+ dri2_egl_display(dri3_surf->base.Resource.Display);
+ struct dri3_buffer *front, *back;
+
+ buffers->image_mask = 0;
+ buffers->front = NULL;
+ buffers->back = NULL;
+
+ front = NULL;
+ back = NULL;
+
+ if (!dri3_update_drawable(driDrawable, dri3_surf, dri2_dpy))
+ return false;
+
+ /* pixmaps always have front buffers */
+ if (dri3_surf->is_pixmap)
+ buffer_mask |= __DRI_IMAGE_BUFFER_FRONT;
+
+ if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT) {
+ /* All pixmaps are owned by the server gpu.
+ * When we use a different gpu, we can't use the pixmap
+ * as buffer since it is potentially tiled a way
+ * our device can't understand. In this case, use
+ * a fake front buffer. Hopefully the pixmap
+ * content will get synced with the fake front
+ * buffer.
+ */
+ if (dri3_surf->is_pixmap && !dri2_dpy->is_different_gpu)
+ front = dri3_get_pixmap_buffer(driDrawable,
+ format,
+ dri3_buffer_front,
+ dri3_surf,
+ dri2_dpy);
+ else
+ front = dri3_get_buffer(driDrawable,
+ format,
+ dri3_buffer_front,
+ dri3_surf,
+ dri2_dpy);
+
+ if (!front)
+ return false;
+ } else {
+ dri3_free_buffers(driDrawable, dri3_buffer_front, dri3_surf, dri2_dpy);
+ dri3_surf->have_fake_front = 0;
+ }
+
+ if (buffer_mask & __DRI_IMAGE_BUFFER_BACK) {
+ back = dri3_get_buffer(driDrawable,
+ format,
+ dri3_buffer_back,
+ dri3_surf,
+ dri2_dpy);
+ if (!back)
+ return false;
+ dri3_surf->have_back = 1;
+ } else {
+ dri3_free_buffers(driDrawable, dri3_buffer_back, dri3_surf, dri2_dpy);
+ dri3_surf->have_back = 0;
+ }
+
+ if (front) {
+ buffers->image_mask |= __DRI_IMAGE_BUFFER_FRONT;
+ buffers->front = front->image;
+ dri3_surf->have_fake_front = dri2_dpy->is_different_gpu || !dri3_surf->is_pixmap;
+ }
+
+ if (back) {
+ buffers->image_mask |= __DRI_IMAGE_BUFFER_BACK;
+ buffers->back = back->image;
+ }
+
+ dri3_surf->stamp = stamp;
+
+ return true;
+}
+
+const __DRIimageLoaderExtension dri3_image_loader_extension = {
+ .base = { __DRI_IMAGE_LOADER, 1 },
+
+ .getBuffers = dri3_get_buffers,
+ .flushFrontBuffer = dri3_flush_front_buffer,
+};
+
+static int
+dri3_get_ust(int64_t *ust)
+{
+ struct timeval tv;
+
+ if (ust == NULL)
+ return -EFAULT;
+
+ if (gettimeofday(&tv, NULL) == 0) {
+ ust[0] = (tv.tv_sec * 1000000) + tv.tv_usec;
+ return 0;
+ } else
+ return -errno;
+}
+
+static GLboolean
+dri3_get_msc_rate(__DRIdrawable *draw, int32_t *numerator,
+ int32_t *denominator, void *loaderPrivate)
+{
+#ifdef XF86VIDMODE
+ dri3_egl_surface *dri3_surf = loaderPrivate;
+ struct dri2_egl_display *dri2_dpy =
+ dri2_egl_display(dri3_surf->base.Resource.Display);
+ XF86VidModeModeLine mode_line;
+ int dot_clock;
+ int i;
+
+ if (XF86VidModeQueryVersion(dri2_dpy->dpy, &i, &i) &&
+ XF86VidModeGetModeLine(dri2_dpy->dpy, dri2_dpy->screen,
+ &dot_clock, &mode_line)) {
+ unsigned n = dot_clock * 1000;
+ unsigned d = mode_line.vtotal * mode_line.htotal;
+
+# define V_INTERLACE 0x010
+# define V_DBLSCAN 0x020
+
+ if (mode_line.flags & V_INTERLACE)
+ n *= 2;
+ else if (mode_line.flags & V_DBLSCAN)
+ d *= 2;
+
+ /* The OML_sync_control spec requires that if the refresh rate is a
+ * whole number, that the returned numerator be equal to the refresh
+ * rate and the denominator be 1.
+ */
+
+ if (n % d == 0) {
+ n /= d;
+ d = 1;
+ }
+ else {
+ static const unsigned f[] = { 13, 11, 7, 5, 3, 2, 0 };
+
+ /* This is a poor man's way to reduce a fraction. It's far from
+ * perfect, but it will work well enough for this situation.
+ */
+
+ for (i = 0; f[i] != 0; i++) {
+ while (n % f[i] == 0 && d % f[i] == 0) {
+ d /= f[i];
+ n /= f[i];
+ }
+ }
+ }
+
+ *numerator = n;
+ *denominator = d;
+
+ return True;
+
+#endif
+ return False;
+}
+
+const __DRIsystemTimeExtension dri3_system_time_extension = {
+ .base = { __DRI_SYSTEM_TIME, 1 },
+
+ .getUST = dri3_get_ust,
+ .getMSCRate = dri3_get_msc_rate,
+};
+
+/** dri3_swap_buffers_msc
+ *
+ * Make the current back buffer visible using the present extension
+ */
+static int64_t
+dri3_swap_buffers_msc(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw,
+ int64_t target_msc, int64_t divisor,
+ int64_t remainder)
+{
+ struct dri3_egl_surface *dri3_surf = dri3_egl_surface(draw);
+ struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+ struct dri3_buffer *back;
+ _EGLContext *ctx = _eglGetCurrentContext();
+ struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx);
+ int64_t ret = 0;
+ uint32_t options = XCB_PRESENT_OPTION_NONE;
+
+ dri2_flush_drawable_for_swapbuffers(disp, draw);
+
+ back = dri3_surf->buffers[DRI3_BACK_ID(dri3_surf->cur_back)];
+ if (dri2_dpy->is_different_gpu && back) {
+ /* Update the linear buffer before presenting the pixmap */
+ dri2_dpy->image->blitImage(dri2_ctx->dri_context,
+ back->linear_buffer,
+ back->image,
+ 0, 0, back->width,
+ back->height,
+ 0, 0, back->width,
+ back->height, __BLIT_FLAG_FLUSH);
+ /* Update the fake front */
+ if (dri3_surf->have_fake_front)
+ dri2_dpy->image->blitImage(dri2_ctx->dri_context,
+ dri3_surf->buffers[DRI3_FRONT_ID]->image,
+ back->image,
+ 0, 0, dri3_surf->base.Width,
+ dri3_surf->base.Height,
+ 0, 0, dri3_surf->base.Width,
+ dri3_surf->base.Height,
+ __BLIT_FLAG_FLUSH);
+ }
+
+ dri3_flush_present_events(dri2_dpy, dri3_surf);
+
+ if (back && !dri3_surf->is_pixmap) {
+ dri3_fence_reset(dri2_dpy->conn, back);
+
+ /* Compute when we want the frame shown by taking the last known successful
+ * MSC and adding in a swap interval for each outstanding swap request.
+ * target_msc=divisor=remainder=0 means "Use glXSwapBuffers() semantic"
+ */
+ ++dri3_surf->send_sbc;
+ if (target_msc == 0 && divisor == 0 && remainder == 0)
+ target_msc = dri3_surf->msc + dri3_surf->base.SwapInterval * (dri3_surf->send_sbc - dri3_surf->recv_sbc);
+ else if (divisor == 0 && remainder > 0) {
+ /*
+ * "If <divisor> = 0, the swap will occur when MSC becomes
+ * greater than or equal to <target_msc>."
+ *
+ * Note that there's no mention of the remainder. The Present extension
+ * throws BadValue for remainder != 0 with divisor == 0, so just drop
+ * the passed in value.
+ */
+ remainder = 0;
+ }
+
+ /* From the EGL 1.4 spec (page 53):
+ *
+ * "If <interval> is set to a value of 0, buffer swaps are not
+ * synchronized to a video frame."
+ *
+ * Implementation note: It is possible to enable triple buffering behaviour
+ * by not using XCB_PRESENT_OPTION_ASYNC, but this should not be the default.
+ */
+ if (dri3_surf->base.SwapInterval == 0)
+ options |= XCB_PRESENT_OPTION_ASYNC;
+
+ back->busy = 1;
+ back->last_swap = dri3_surf->send_sbc;
+ xcb_present_pixmap(dri2_dpy->conn,
+ dri3_surf->drawable,
+ back->pixmap,
+ (uint32_t) dri3_surf->send_sbc,
+ 0, /* valid */
+ 0, /* update */
+ 0, /* x_off */
+ 0, /* y_off */
+ None, /* target_crtc */
+ None,
+ back->sync_fence,
+ options,
+ target_msc,
+ divisor,
+ remainder, 0, NULL);
+ ret = (int64_t) dri3_surf->send_sbc;
+
+ /* If there's a fake front, then copy the source back buffer
+ * to the fake front to keep it up to date. This needs
+ * to reset the fence and make future users block until
+ * the X server is done copying the bits
+ */
+ if (dri3_surf->have_fake_front && !dri2_dpy->is_different_gpu) {
+ dri3_fence_reset(dri2_dpy->conn, dri3_surf->buffers[DRI3_FRONT_ID]);
+ dri3_copy_area(dri2_dpy->conn,
+ back->pixmap,
+ dri3_surf->buffers[DRI3_FRONT_ID]->pixmap,
+ dri3_drawable_gc(dri2_dpy, dri3_surf),
+ 0, 0, 0, 0,
+ dri3_surf->base.Width,
+ dri3_surf->base.Height);
+ dri3_fence_trigger(dri2_dpy->conn, dri3_surf->buffers[DRI3_FRONT_ID]);
+ }
+ xcb_flush(dri2_dpy->conn);
+ if (dri3_surf->stamp)
+ ++(*dri3_surf->stamp);
+ }
+
+ (*dri2_dpy->flush->invalidate)(dri3_surf->dri_drawable);
+
+ return ret;
+}
+
+static EGLBoolean
+dri3_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw)
+{
+ return dri3_swap_buffers_msc(drv, disp, draw, 0, 0, 0) != -1;
+}
+
+static int
+dri3_query_buffer_age(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surf)
+{
+ struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy);
+ struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
+ int back_id = DRI3_BACK_ID(dri3_find_back(dri2_dpy, dri3_surf));
+
+ if (back_id < 0 || !dri3_surf->buffers[back_id])
+ return 0;
+
+ if (dri3_surf->buffers[back_id]->last_swap != 0)
+ return dri3_surf->send_sbc - dri3_surf->buffers[back_id]->last_swap + 1;
+ else
+ return 0;
+}
+
+/* FIXME: Is this right? Seems problematic for WL_bind_wayland_display */
+static int
+dri3_authenticate(_EGLDisplay *disp, uint32_t id)
+{
+ return 0;
+}
+
+struct dri2_egl_display_vtbl dri3_x11_display_vtbl = {
+ .authenticate = dri3_authenticate,
+ .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 = dri2_fallback_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,
+};
+
+/** dri3_open
+ *
+ * Wrapper around xcb_dri3_open
+ */
+static int
+dri3_open(xcb_connection_t *conn,
+ xcb_window_t root,
+ uint32_t provider)
+{
+ xcb_dri3_open_cookie_t cookie;
+ xcb_dri3_open_reply_t *reply;
+ int fd;
+
+ cookie = xcb_dri3_open(conn,
+ root,
+ provider);
+
+ reply = xcb_dri3_open_reply(conn, cookie, NULL);
+ if (!reply)
+ return -1;
+
+ if (reply->nfd != 1) {
+ free(reply);
+ return -1;
+ }
+
+ fd = xcb_dri3_open_reply_fds(conn, reply)[0];
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+
+ return fd;
+}
+
+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);
+
+ /* FIXME: a little bit memory leak here */
+ 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(error);
+ return EGL_FALSE;
+ }
+ dri2_dpy->dri3_major = dri3_query->major_version;
+ dri2_dpy->dri3_minor = dri3_query->minor_version;
+ 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(error);
+ return EGL_FALSE;
+ }
+ dri2_dpy->present_major = present_query->major_version;
+ dri2_dpy->present_minor = present_query->minor_version;
+ 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 = 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 = loader_get_device_name_for_fd(dri2_dpy->fd);
+ if (!dri2_dpy->device_name) {
+ _eglLog(_EGL_WARNING, "DRI2: Cannot find 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 0000000..efdfcdb
--- /dev/null
+++ b/src/egl/drivers/dri2/platform_x11_dri3.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright © 2013 Keith Packard
+ *
+ * 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 <stdbool.h>
+#include <xcb/xcb.h>
+#include <xcb/present.h>
+
+#include "egl_dri2.h"
+
+_EGL_DRIVER_TYPECAST(dri3_egl_surface, _EGLSurface, obj)
+enum dri3_buffer_type {
+ dri3_buffer_back = 0,
+ dri3_buffer_front = 1
+};
+
+struct dri3_buffer {
+ __DRIimage *image;
+ __DRIimage *linear_buffer;
+ uint32_t pixmap;
+
+ /* Synchronization between the client and X server is done using an
+ * xshmfence that is mapped into an X server SyncFence. This lets the
+ * client check whether the X server is done using a buffer with a simple
+ * xshmfence call, rather than going to read X events from the wire.
+ *
+ * However, we can only wait for one xshmfence to be triggered at a time,
+ * so we need to know *which* buffer is going to be idle next. We do that
+ * by waiting for a PresentIdleNotify event. When that event arrives, the
+ * 'busy' flag gets cleared and the client knows that the fence has been
+ * triggered, and that the wait call will not block.
+ */
+
+ uint32_t sync_fence; /* XID of X SyncFence object */
+ struct xshmfence *shm_fence; /* pointer to xshmfence object */
+ bool busy; /* Set on swap, cleared on IdleNotify */
+ bool own_pixmap; /* We allocated the pixmap ID, free on destroy */
+
+ uint32_t size;
+ uint32_t pitch;
+ uint32_t cpp;
+ uint32_t flags;
+ uint32_t width, height;
+ uint64_t last_swap;
+
+ enum dri3_buffer_type buffer_type;
+};
+
+
+#define DRI3_MAX_BACK 4
+#define DRI3_BACK_ID(i) (i)
+#define DRI3_FRONT_ID (DRI3_MAX_BACK)
+
+static inline int
+dri3_pixmap_buf_id(enum dri3_buffer_type buffer_type)
+{
+ if (buffer_type == dri3_buffer_back)
+ return DRI3_BACK_ID(0);
+ else
+ return DRI3_FRONT_ID;
+}
+
+#define DRI3_NUM_BUFFERS (1 + DRI3_MAX_BACK)
+
+struct dri3_egl_surface {
+ _EGLSurface base;
+ __DRIdrawable *dri_drawable;
+ xcb_drawable_t drawable;
+ int depth;
+ uint8_t have_back;
+ uint8_t have_fake_front;
+ uint8_t is_pixmap;
+ uint8_t flipping;
+
+ /* Present extension capabilities
+ */
+ uint32_t present_capabilities;
+
+ /* SBC numbers are tracked by using the serial numbers
+ * in the present request and complete events
+ */
+ uint64_t send_sbc;
+ uint64_t recv_sbc;
+
+ /* Last received UST/MSC values for pixmap present complete */
+ uint64_t ust, msc;
+
+ /* Last received UST/MSC values from present notify msc event */
+ uint64_t notify_ust, notify_msc;
+
+ /* Serial numbers for tracking wait_for_msc events */
+ uint32_t send_msc_serial;
+ uint32_t recv_msc_serial;
+
+ struct dri3_buffer *buffers[DRI3_NUM_BUFFERS];
+ int cur_back;
+ int num_back;
+
+ uint32_t *stamp;
+
+ xcb_present_event_t eid;
+ xcb_gcontext_t gc;
+ xcb_special_event_t *special_event;
+
+ /* LIBGL_SHOW_FPS support */
+ uint64_t previous_ust;
+ unsigned frames;
+
+ bool first_init;
+};
+
+extern const __DRIimageLoaderExtension dri3_image_loader_extension;
+extern const __DRIsystemTimeExtension dri3_system_time_extension;
+extern struct dri2_egl_display_vtbl dri3_x11_display_vtbl;
+
+EGLBoolean
+dri3_x11_connect(struct dri2_egl_display *dri2_dpy);
+
+#endif
--
2.4.4
More information about the mesa-dev
mailing list