summaryrefslogtreecommitdiffstats
path: root/src/glx/dri3_glx.c
diff options
context:
space:
mode:
authorKeith Packard <[email protected]>2014-07-02 13:26:22 -0700
committerKeith Packard <[email protected]>2014-09-30 20:08:28 -0700
commitf7a355556ef5fe23056299a77414f9ad8b5e5a1d (patch)
tree382681eae215736152d104c08d5c69843d740a97 /src/glx/dri3_glx.c
parenteedbce9c63a3f385908bdc8a69e8be98dd3522ff (diff)
glx/dri3: Use four buffers until X driver supports async flips
A driver which doesn't have async flip support will queue up flips without any way to replace them afterwards. This means we've got a scanout buffer pinned as soon as we schedule a flip and so we need another buffer to keep from stalling. When vblank_mode=0, if there are only three buffers we do: current scanout buffer = 0 at MSC 0 Render frame 1 to buffer 1 PresentPixmap for buffer 1 at MSC 1 This is sitting down in the kernel waiting for vblank to become the next scanout buffer Render frame 2 to buffer 2 PresentPixmap for buffer 2 at MSC 1 This cannot be displayed at MSC 1 because the kernel doesn't have any way to replace buffer 1 as the pending scanout buffer. So, best case this will get displayed at MSC 2. Now we block after this, waiting for one of the three buffers to become idle. We can't use buffer 0 because it is the scanout buffer. We can't use buffer 1 because it's sitting in the kernel waiting to become the next scanout buffer and we can't use buffer 2 because that's the most recent frame which will become the next scanout buffer if the application doesn't manage to generate another complete frame by MSC 2. With four buffers, we get: current scanout buffer = 0 at MSC 0 Render frame 1 to buffer 1 PresentPixmap for buffer 1 at MSC 1 This is sitting down in the kernel waiting for vblank to become the next scanout buffer Render frame 2 to buffer 2 PresentPixmap for buffer 2 at MSC 1 This cannot be displayed at MSC 1 because the kernel doesn't have any way to replace buffer 1 as the pending scanout buffer. So, best case this will get displayed at MSC 2. The X server will queue this swap until buffer 1 becomes the scanout buffer. Render frame 3 to buffer 3 PresentPixmap for buffer 3 at MSC 1 As soon as the X server sees this, it will replace the pending buffer 2 swap with this swap and release buffer 2 back to the application Render frame 4 to buffer 2 PresentPixmap for buffer 2 at MSC 1 Now we're in a steady state, flipping between buffer 2 and 3 waiting for one of them to be queued to the kernel. ... current scanout buffer = 1 at MSC 1 Now buffer 0 is free and (e.g.) buffer 2 is queued in the kernel to be the scanout buffer at MSC 2 Render frames, flipping between buffer 0 and 3 When the system can replace a queued buffer, and we update Present to take advantage of that, we can use three buffers and get: current scanout buffer = 0 at MSC 0 Render frame 1 to buffer 1 PresentPixmap for buffer 1 at MSC 1 This is sitting waiting for vblank to become the next scanout buffer Render frame 2 to buffer 2 PresentPixmap for buffer 2 at MSC 1 Queue this for display at MSC 1 1. There are three possible results: 1) We're still before MSC 1. Buffer 1 is released, buffer 2 is queued waiting for MSC 1. 2) We're now after MSC 1. Buffer 0 was released at MSC 1. Buffer 1 is the current scanout buffer. a) If the user asked for a tearing update, we swap scanout from buffer 1 to buffer 2 and release buffer 1. b) If the user asked for non-tearing update, we queue buffer 2 for the MSC 2. In all three cases, we have a buffer released (call it 'n'), ready to receive the next frame. Render frame 3 to buffer n PresentPixmap for buffer n If we're still before MSC 1, then we'll ask to present at MSC 1. Otherwise, we'll ask to present at MSC 2. Present already does this if the driver offers async flips, however it does this by waiting for the right vblank event and sending an async flip right at that point. I've hacked the intel driver to offer this, but I get tearing at the top of the screen. I think this is because flips are always done from within the ring, and so the latency between the vblank event and the async flip happening can cause tearing at the top of the screen. That's why I'm keying the need for the extra buffer on the lack of 2D driver support for async flips. Signed-off-by: Keith Packard <[email protected]> Acked-by: Jason Ekstrand <[email protected]> Tested-by: Dylan Baker <[email protected]>
Diffstat (limited to 'src/glx/dri3_glx.c')
-rw-r--r--src/glx/dri3_glx.c20
1 files changed, 19 insertions, 1 deletions
diff --git a/src/glx/dri3_glx.c b/src/glx/dri3_glx.c
index e3fc4def86e..753b8d88de4 100644
--- a/src/glx/dri3_glx.c
+++ b/src/glx/dri3_glx.c
@@ -271,8 +271,11 @@ static void
dri3_update_num_back(struct dri3_drawable *priv)
{
priv->num_back = 1;
- if (priv->flipping)
+ if (priv->flipping) {
+ if (!priv->is_pixmap && !(priv->present_capabilities & XCB_PRESENT_CAPABILITY_ASYNC))
+ priv->num_back++;
priv->num_back++;
+ }
if (priv->swap_interval == 0)
priv->num_back++;
}
@@ -976,6 +979,9 @@ dri3_update_drawable(__DRIdrawable *driDrawable, void *loaderPrivate)
xcb_get_geometry_reply_t *geom_reply;
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;
+
/* Try to select for input on the window.
*
@@ -994,6 +1000,8 @@ dri3_update_drawable(__DRIdrawable *driDrawable, void *loaderPrivate)
XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY|
XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY);
+ present_capabilities_cookie = xcb_present_query_capabilities(c, priv->base.xDrawable);
+
/* Create an XCB event queue to hold present events outside of the usual
* application event queue
*/
@@ -1023,6 +1031,16 @@ dri3_update_drawable(__DRIdrawable *driDrawable, void *loaderPrivate)
error = xcb_request_check(c, cookie);
+ present_capabilities_reply = xcb_present_query_capabilities_reply(c,
+ present_capabilities_cookie,
+ NULL);
+
+ if (present_capabilities_reply) {
+ priv->present_capabilities = present_capabilities_reply->capabilities;
+ free(present_capabilities_reply);
+ } else
+ priv->present_capabilities = 0;
+
if (error) {
if (error->error_code != BadWindow) {
free(error);