[Mesa-dev] [PATCH] egl: dri2: Use present extension. (Was: Re: [RFC] egl: Add DRI3 support to the EGL backend.)
Joonas Lahtinen
joonas.lahtinen at linux.intel.com
Wed Nov 5 03:14:35 PST 2014
Hi,
Modified not refer to DRI3, just uses the present extension to get rid
of the excess buffer invalidations.
Regards, Joonas
>From 257e2a8c750f9dcf868cce9da8632df3cae0fcec Mon Sep 17 00:00:00 2001
From: Joonas Lahtinen <joonas.lahtinen at linux.intel.com>
Date: Wed, 5 Nov 2014 12:25:32 +0200
Subject: [PATCH] egl: dri2: Use present extension.
Present extension is used to avoid excess buffer invalidations, because
the XCB interface doesn't supply that information.
Signed-off-by: Daniel van der Wath <danielx.j.van.der.wath at intel.com>
Signed-off-by: Joonas Lahtinen <joonas.lahtinen at linux.intel.com>
---
configure.ac | 5 +-
src/egl/drivers/dri2/egl_dri2.c | 2 +-
src/egl/drivers/dri2/egl_dri2.h | 24 ++-
src/egl/drivers/dri2/platform_x11.c | 247 ++++++++++++++++++++++++++++---
src/mesa/drivers/dri/i965/brw_context.c | 9 +-
5 files changed, 262 insertions(+), 25 deletions(-)
diff --git a/configure.ac b/configure.ac
index fc7d372..75d90c0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -952,7 +952,8 @@ xyesno)
fi
if test x"$enable_dri" = xyes; then
- dri_modules="$dri_modules xcb-dri2 >= $XCBDRI2_REQUIRED"
+ PKG_CHECK_MODULES([PRESENTPROTO], [presentproto >= $PRESENTPROTO_REQUIRED])
+ dri_modules="$dri_modules xcb-dri2 >= $XCBDRI2_REQUIRED xcb-present"
fi
if test x"$enable_dri3" = xyes; then
@@ -1564,7 +1565,7 @@ for plat in $egl_platforms; do
;;
x11)
- PKG_CHECK_MODULES([XCB_DRI2], [x11-xcb xcb xcb-dri2 >= $XCBDRI2_REQUIRED xcb-xfixes])
+ PKG_CHECK_MODULES([XCB_DRI2], [x11-xcb xcb xcb-dri2 >= $XCBDRI2_REQUIRED xcb-xfixes xcb-present])
;;
drm)
diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
index 20a7243..c1571d6 100644
--- a/src/egl/drivers/dri2/egl_dri2.c
+++ b/src/egl/drivers/dri2/egl_dri2.c
@@ -586,7 +586,7 @@ dri2_create_screen(_EGLDisplay *disp)
dri2_dpy->own_dri_screen = 1;
extensions = dri2_dpy->core->getExtensions(dri2_dpy->dri_screen);
-
+
if (dri2_dpy->dri2) {
unsigned i;
diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h
index 52f05fb..33f75f8 100644
--- a/src/egl/drivers/dri2/egl_dri2.h
+++ b/src/egl/drivers/dri2/egl_dri2.h
@@ -32,6 +32,7 @@
#include <xcb/xcb.h>
#include <xcb/dri2.h>
#include <xcb/xfixes.h>
+#include <xcb/present.h>
#include <X11/Xlib-xcb.h>
#endif
@@ -74,6 +75,7 @@
#include "eglimage.h"
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#define DRI2_EGL_SURFACE_NUM_BUFFERS 5
struct wl_buffer;
@@ -218,7 +220,7 @@ struct dri2_egl_surface
{
_EGLSurface base;
__DRIdrawable *dri_drawable;
- __DRIbuffer buffers[5];
+ __DRIbuffer buffers[DRI2_EGL_SURFACE_NUM_BUFFERS];
int buffer_count;
int have_fake_front;
@@ -229,6 +231,26 @@ struct dri2_egl_surface
int bytes_per_pixel;
xcb_gcontext_t gc;
xcb_gcontext_t swapgc;
+ xcb_special_event_t *special_event;
+ xcb_present_event_t eid;
+ uint32_t *stamp;
+ uint8_t is_pixmap;
+ uint8_t flipping;
+
+ /* 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 */
+ uint64_t ust, msc;
+
+ int num_back;
+
+ /* Serial numbers for tracking wait_for_msc events */
+ uint32_t send_msc_serial;
+ uint32_t recv_msc_serial;
#endif
#ifdef HAVE_WAYLAND_PLATFORM
diff --git a/src/egl/drivers/dri2/platform_x11.c b/src/egl/drivers/dri2/platform_x11.c
index f8c4b70..a1445b2 100644
--- a/src/egl/drivers/dri2/platform_x11.c
+++ b/src/egl/drivers/dri2/platform_x11.c
@@ -188,6 +188,205 @@ get_xcb_screen(xcb_screen_iterator_t iter, int screen)
return NULL;
}
+/*
+ * Called by the XCB_PRESENT_COMPLETE_NOTIFY case.
+ */
+static void
+dri2_update_num_back(struct dri2_egl_surface *priv)
+{
+ priv->num_back = 1;
+ if (priv->flipping)
+ priv->num_back++;
+ if (priv->base.SwapInterval == 0)
+ priv->num_back++;
+}
+
+/*
+ * In the glx version there's some more clean up, but here we can just call releaseBuffer().
+ */
+static void
+dri2_free_render_buffer(struct dri2_egl_surface *pdraw, __DRIbuffer *buffer)
+{
+ struct dri2_egl_display *dri2_dpy = dri2_egl_display(pdraw->base.Resource.Display);
+
+ (*dri2_dpy->dri2->releaseBuffer)(dri2_dpy->dri_screen, buffer);
+}
+
+/*
+ * Process one Present event
+ */
+static void
+dri2_handle_present_event(struct dri2_egl_surface *priv, xcb_present_generic_event_t *ge)
+{
+ switch (ge->evtype) {
+ case XCB_PRESENT_CONFIGURE_NOTIFY: {
+ xcb_present_configure_notify_event_t *ce = (void *) ge;
+
+ priv->base.Width = ce->width;
+ priv->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) {
+ priv->recv_sbc = (priv->send_sbc & 0xffffffff00000000LL) | ce->serial;
+ if (priv->recv_sbc > priv->send_sbc)
+ priv->recv_sbc -= 0x100000000;
+ switch (ce->mode) {
+ case XCB_PRESENT_COMPLETE_MODE_FLIP:
+ priv->flipping = true;
+ break;
+ case XCB_PRESENT_COMPLETE_MODE_COPY:
+ priv->flipping = false;
+ break;
+ }
+ dri2_update_num_back(priv);
+ } else {
+ priv->recv_msc_serial = ce->serial;
+ }
+ priv->ust = ce->ust;
+ priv->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 (priv->buffers) / sizeof (priv->buffers[0]); b++) {
+ __DRIbuffer *buf = &priv->buffers[b];
+
+ if (buf && buf->name == ie->pixmap) {
+ /* busy is used in the glx version (in dri3_find_back()) but isn't required here */
+ //buf->busy = 0;
+
+ /* slight change from glx, these buffers are not dynamically allocated so don't
+ * need to be NULL'd.
+ */
+ if (priv->num_back <= b && b < DRI2_EGL_SURFACE_NUM_BUFFERS) {
+ dri2_free_render_buffer(priv, buf);
+ }
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ free(ge);
+}
+
+/**
+ *
+ * Process any present events that have been received from the X server
+ *
+ * From glx, we additionally invalidate the drawable here if there has a been a special event.
+ */
+static void
+dri2_flush_present_events(struct dri2_egl_display *dri2_dpy, struct dri2_egl_surface *priv)
+{
+ xcb_connection_t *c = dri2_dpy->conn;
+
+ /* Check to see if any configuration changes have occurred
+ * since we were last invoked
+ */
+ if (priv->special_event) {
+ xcb_generic_event_t *ev;
+
+ while ((ev = xcb_poll_for_special_event(c, priv->special_event)) != NULL) {
+ xcb_present_generic_event_t *ge = (void *) ev;
+ dri2_handle_present_event(priv, ge);
+ _eglLog(_EGL_INFO, "DRI: Invalidating buffer 0x%x\n", priv->dri_drawable);
+ (*dri2_dpy->flush->invalidate)(priv->dri_drawable);
+ }
+ }
+}
+
+/**
+ *
+ * from glx
+ * 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
+dri2_update_drawable(struct dri2_egl_display *dri2_dpy, __DRIdrawable *driDrawable, void *loaderPrivate)
+{
+ struct dri2_egl_surface *priv = loaderPrivate;
+ xcb_connection_t *c = dri2_dpy->conn;
+
+ /* First time through, go get the current drawable geometry
+ */
+ if (priv->base.Width == 0 || priv->base.Height == 0 || priv->depth == 0) {
+ xcb_get_geometry_cookie_t geom_cookie;
+ xcb_get_geometry_reply_t *geom_reply;
+ xcb_void_cookie_t cookie;
+ xcb_generic_error_t *error;
+
+ /* 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(c,
+ (priv->eid = xcb_generate_id(c)),
+ priv->drawable,
+ XCB_PRESENT_EVENT_MASK_CONFIGURE_NOTIFY|
+ XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY|
+ XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY);
+
+ /* Create an XCB event queue to hold present events outside of the usual
+ * application event queue
+ */
+ priv->special_event = xcb_register_for_special_xge(c,
+ &xcb_present_id,
+ priv->eid,
+ priv->stamp);
+
+ geom_cookie = xcb_get_geometry(c, priv->drawable);
+
+ geom_reply = xcb_get_geometry_reply(c, geom_cookie, NULL);
+
+ if (!geom_reply)
+ return false;
+
+ priv->base.Width = geom_reply->width;
+ priv->base.Height = geom_reply->height;
+ priv->depth = geom_reply->depth;
+ priv->is_pixmap = false;
+
+ free(geom_reply);
+
+ /* 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(c, cookie);
+
+ if (error) {
+ if (error->error_code != BadWindow) {
+ free(error);
+ return false;
+ }
+ priv->is_pixmap = true;
+ xcb_unregister_for_special_event(c, priv->special_event);
+ priv->special_event = NULL;
+ }
+ }
+ dri2_flush_present_events(dri2_dpy, priv);
+ return true;
+}
/**
* Called via eglCreateWindowSurface(), drv->API.CreateWindowSurface().
@@ -212,7 +411,10 @@ dri2_x11_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type,
(void) drv;
- dri2_surf = malloc(sizeof *dri2_surf);
+ /* dri2_surf->special_event needs to be initialised as NULL
+ * so calloc makes sense here
+ */
+ dri2_surf = calloc(1, sizeof *dri2_surf);
if (!dri2_surf) {
_eglError(EGL_BAD_ALLOC, "dri2_create_surface");
return NULL;
@@ -360,6 +562,15 @@ dri2_x11_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf)
if (surf->Type == EGL_PBUFFER_BIT)
xcb_free_pixmap (dri2_dpy->conn, dri2_surf->drawable);
+ /* from glx: dri3_destroy_drawable(). dri2_surf is not necessarily being
+ * destroyed so we need to NULL special_event after unregistering it
+ */
+ if (dri2_surf->special_event)
+ {
+ xcb_unregister_for_special_event(dri2_dpy->conn, dri2_surf->special_event);
+ dri2_surf->special_event = NULL;
+ }
+
free(surf);
return EGL_TRUE;
@@ -427,6 +638,9 @@ dri2_x11_get_buffers(__DRIdrawable * driDrawable,
(void) driDrawable;
+ if (!dri2_update_drawable(dri2_dpy, driDrawable, loaderPrivate))
+ return false;
+
cookie = xcb_dri2_get_buffers_unchecked (dri2_dpy->conn,
dri2_surf->drawable,
count, count, attachments);
@@ -461,6 +675,9 @@ dri2_x11_get_buffers_with_format(__DRIdrawable * driDrawable,
(void) driDrawable;
+ if (!dri2_update_drawable(dri2_dpy, driDrawable, loaderPrivate))
+ return false;
+
format_attachments = (xcb_dri2_attach_format_t *) attachments;
cookie = xcb_dri2_get_buffers_with_format_unchecked (dri2_dpy->conn,
dri2_surf->drawable,
@@ -774,6 +991,10 @@ dri2_x11_swap_buffers_msc(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw,
if (dri2_dpy->flush)
(*dri2_dpy->flush->flush)(dri2_surf->dri_drawable);
+ /* Poll for special events and invalidate the surface if needed.
+ */
+ dri2_flush_present_events(dri2_dpy, dri2_surf);
+
cookie = xcb_dri2_swap_buffers_unchecked(dri2_dpy->conn, dri2_surf->drawable,
msc_hi, msc_lo, divisor_hi, divisor_lo, remainder_hi, remainder_lo);
@@ -784,20 +1005,6 @@ dri2_x11_swap_buffers_msc(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw,
free(reply);
}
- /* Since we aren't watching for the server's invalidate events like we're
- * supposed to (due to XCB providing no mechanism for filtering the events
- * the way xlib does), and SwapBuffers is a common cause of invalidate
- * events, just shove one down to the driver, even though we haven't told
- * the driver that we're the kind of loader that provides reliable
- * invalidate events. This causes the driver to request buffers again at
- * its next draw, so that we get the correct buffers if a pageflip
- * happened. The driver should still be using the viewport hack to catch
- * window resizes.
- */
- if (dri2_dpy->flush &&
- dri2_dpy->flush->base.version >= 3 && dri2_dpy->flush->invalidate)
- (*dri2_dpy->flush->invalidate)(dri2_surf->dri_drawable);
-
return swap_count;
}
@@ -807,6 +1014,11 @@ dri2_x11_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw)
struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw);
+ /* from glx: dri3_swap_buffers(). Polls for special events and invalidates
+ * the surface if needed.
+ */
+ dri2_flush_present_events(dri2_dpy, dri2_surf);
+
if (dri2_dpy->dri2) {
return dri2_x11_swap_buffers_msc(drv, disp, draw, 0, 0, 0) != -1;
} else {
@@ -1277,10 +1489,11 @@ dri2_initialize_x11_dri2(_EGLDriver *drv, _EGLDisplay *disp)
dri2_dpy->dri2_loader_extension.flushFrontBuffer = dri2_x11_flush_front_buffer;
dri2_dpy->dri2_loader_extension.getBuffersWithFormat = NULL;
}
-
+
dri2_dpy->extensions[0] = &dri2_dpy->dri2_loader_extension.base;
dri2_dpy->extensions[1] = &image_lookup_extension.base;
- dri2_dpy->extensions[2] = NULL;
+ dri2_dpy->extensions[2] = &use_invalidate.base;
+ dri2_dpy->extensions[3] = NULL;
dri2_dpy->swap_available = (dri2_dpy->dri2_minor >= 2);
dri2_dpy->invalidate_available = (dri2_dpy->dri2_minor >= 3);
diff --git a/src/mesa/drivers/dri/i965/brw_context.c b/src/mesa/drivers/dri/i965/brw_context.c
index e1a994a..dbadd10 100644
--- a/src/mesa/drivers/dri/i965/brw_context.c
+++ b/src/mesa/drivers/dri/i965/brw_context.c
@@ -148,6 +148,9 @@ intel_viewport(struct gl_context *ctx)
__DRIcontext *driContext = brw->driContext;
if (_mesa_is_winsys_fbo(ctx->DrawBuffer)) {
+ if (unlikely(INTEL_DEBUG & DEBUG_DRI))
+ fprintf(stderr, "invalidating drawables\n");
+
dri2InvalidateDrawable(driContext->driDrawablePriv);
dri2InvalidateDrawable(driContext->driReadablePriv);
}
@@ -252,11 +255,9 @@ brw_init_driver_functions(struct brw_context *brw,
_mesa_init_driver_functions(functions);
/* GLX uses DRI2 invalidate events to handle window resizing.
- * Unfortunately, EGL does not - libEGL is written in XCB (not Xlib),
- * which doesn't provide a mechanism for snooping the event queues.
+ * EGL uses present invalidate events to do the same.
*
- * So EGL still relies on viewport hacks to handle window resizing.
- * This should go away with DRI3000.
+ * Others have to rely on viewport hacks to handle window resizing.
*/
if (!brw->driContext->driScreenPriv->dri2.useInvalidate)
functions->Viewport = intel_viewport;
--
1.7.9.5
More information about the mesa-dev
mailing list