[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