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.lahti...@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.w...@intel.com> Signed-off-by: Joonas Lahtinen <joonas.lahti...@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 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/mesa-dev