diff options
author | Jesse Barnes <jbarnes@virtuousgeek.org> | 2009-09-02 13:59:56 -0700 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2009-10-29 14:05:10 -0700 |
commit | ecf7439af402de218a3874a6ac9ebd83b50c970e (patch) | |
tree | 82da23fb0bc4e37db2a06cd804cc8d53df10ef32 | |
parent | ae9d0ce9ba91b9ba1a4f7ec4dc3fccd35992ef66 (diff) |
DRI2: add support for scheduled swaps & flips for swap interval support
Add support for scheduling swaps and flips in the future and tie that into a
swap interval handling infrastructure.
At swapbuffers time, queue a blit or flip and request a kernel event when the
specified frame number is reached. Once that event is received, perform the
blit or flip and unblock the client if necessary.
-rw-r--r-- | glx/glxdri2.c | 19 | ||||
-rw-r--r-- | glx/swap_interval.c | 5 | ||||
-rw-r--r-- | hw/xfree86/dri2/dri2.c | 194 | ||||
-rw-r--r-- | hw/xfree86/dri2/dri2.h | 4 | ||||
-rw-r--r-- | hw/xfree86/dri2/dri2ext.c | 2 |
5 files changed, 192 insertions, 32 deletions
diff --git a/glx/glxdri2.c b/glx/glxdri2.c index 42335926b..0a3720e45 100644 --- a/glx/glxdri2.c +++ b/glx/glxdri2.c @@ -167,15 +167,25 @@ __glXDRIdrawableWaitGL(__GLXdrawable *drawable) DRI2BufferFrontLeft, DRI2BufferFakeFrontLeft); } +/* + * Copy or flip back to front, honoring the swap interval if possible. + * + * If the kernel supports it, we request an event for the frame when the + * swap should happen, then perform the copy when we receive it. + */ static GLboolean __glXDRIdrawableSwapBuffers(__GLXdrawable *drawable) { __GLXDRIdrawable *priv = (__GLXDRIdrawable *) drawable; __GLXDRIscreen *screen = priv->screen; + int interval = 1; + + if (screen->swapControl) + interval = screen->swapControl->getSwapInterval(priv->driDrawable); (*screen->flush->flushInvalidate)(priv->driDrawable); - if (DRI2SwapBuffers(drawable->pDraw) != Success) + if (DRI2SwapBuffers(drawable->pDraw, interval) != Success) return FALSE; return TRUE; @@ -184,6 +194,13 @@ __glXDRIdrawableSwapBuffers(__GLXdrawable *drawable) static int __glXDRIdrawableSwapInterval(__GLXdrawable *drawable, int interval) { + __GLXDRIdrawable *draw = (__GLXDRIdrawable *) drawable; + __GLXDRIscreen *screen = + (__GLXDRIscreen *) glxGetScreen(drawable->pDraw->pScreen); + + if (screen->swapControl) + screen->swapControl->setSwapInterval(draw->driDrawable, interval); + return 0; } diff --git a/glx/swap_interval.c b/glx/swap_interval.c index 3a5242022..728944bf9 100644 --- a/glx/swap_interval.c +++ b/glx/swap_interval.c @@ -68,7 +68,7 @@ int DoSwapInterval(__GLXclientState *cl, GLbyte *pc, int do_swap) if (cx->drawPriv == NULL) { client->errorValue = tag; - return __glXError(GLXBadDrawable); + return BadValue; } pc += __GLX_VENDPRIV_HDR_SIZE; @@ -76,6 +76,9 @@ int DoSwapInterval(__GLXclientState *cl, GLbyte *pc, int do_swap) ? bswap_32(*(int *)(pc + 0)) : *(int *)(pc + 0); + if (interval <= 0) + return BadValue; + (void) (*cx->pGlxScreen->swapInterval)(cx->drawPriv, interval); return Success; } diff --git a/hw/xfree86/dri2/dri2.c b/hw/xfree86/dri2/dri2.c index 7b9fb23aa..fe3888558 100644 --- a/hw/xfree86/dri2/dri2.c +++ b/hw/xfree86/dri2/dri2.c @@ -57,15 +57,28 @@ typedef struct _DRI2Drawable { int height; DRI2BufferPtr *buffers; int bufferCount; - unsigned int swapPending; + unsigned int swapsPending; + unsigned int flipsPending; ClientPtr blockedClient; } DRI2DrawableRec, *DRI2DrawablePtr; +typedef struct _DRI2Screen *DRI2ScreenPtr; +typedef struct _DRI2SwapData *DRI2SwapDataPtr; + +typedef struct _DRI2SwapData { + DrawablePtr pDraw; + ScreenPtr pScreen; + int frame; + DRI2SwapDataPtr next; +} DRI2SwapDataRec; + typedef struct _DRI2Screen { const char *driverName; const char *deviceName; int fd; unsigned int lastSequence; + drmEventContext event_context; + DRI2SwapDataPtr swaps; /* Pending swap list */ DRI2CreateBufferProcPtr CreateBuffer; DRI2DestroyBufferProcPtr DestroyBuffer; @@ -73,7 +86,7 @@ typedef struct _DRI2Screen { DRI2SwapBuffersProcPtr SwapBuffers; HandleExposuresProcPtr HandleExposures; -} DRI2ScreenRec, *DRI2ScreenPtr; +} DRI2ScreenRec; static DRI2ScreenPtr DRI2GetScreen(ScreenPtr pScreen) @@ -87,6 +100,9 @@ DRI2GetDrawable(DrawablePtr pDraw) WindowPtr pWin; PixmapPtr pPixmap; + if (!pDraw) + return NULL; + if (pDraw->type == DRAWABLE_WINDOW) { pWin = (WindowPtr) pDraw; @@ -122,7 +138,8 @@ DRI2CreateDrawable(DrawablePtr pDraw) pPriv->height = pDraw->height; pPriv->buffers = NULL; pPriv->bufferCount = 0; - pPriv->swapPending = FALSE; + pPriv->swapsPending = 0; + pPriv->flipsPending = 0; pPriv->blockedClient = NULL; if (pDraw->type == DRAWABLE_WINDOW) @@ -366,19 +383,64 @@ DRI2FlipCheck(DrawablePtr pDraw) return TRUE; } -int -DRI2SwapBuffers(DrawablePtr pDraw) +static Bool DRI2AddSwap(DrawablePtr pDraw, int frame) { DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen); DRI2DrawablePtr pPriv; + DRI2SwapDataPtr new; + + pPriv = DRI2GetDrawable(pDraw); + if (pPriv == NULL) + return FALSE; + + new = xcalloc(1, sizeof(DRI2SwapDataRec)); + if (!new) + return FALSE; + + new->pScreen = pDraw->pScreen; + new->pDraw = pDraw; + new->frame = frame; + new->next = ds->swaps; + ds->swaps = new; + + return TRUE; +} + +static void DRI2RemoveSwap(DRI2SwapDataPtr swap) +{ + ScreenPtr pScreen = swap->pScreen; + DRI2ScreenPtr ds = DRI2GetScreen(pScreen); + DRI2SwapDataPtr cur = ds->swaps; + + while (cur) { + if (cur == swap) { + cur->next = swap->next; + xfree(swap); + } + cur = cur->next; + } +} + +static void +DRI2SwapSubmit(DRI2SwapDataPtr swap) +{ + DrawablePtr pDraw = swap->pDraw; + ScreenPtr pScreen = swap->pScreen; + DRI2ScreenPtr ds = DRI2GetScreen(pScreen); + DRI2DrawablePtr pPriv; DRI2BufferPtr pDestBuffer, pSrcBuffer; - int i; BoxRec box; RegionRec region; + int ret, i; pPriv = DRI2GetDrawable(pDraw); if (pPriv == NULL) - return BadDrawable; + return; + + if (pPriv->refCount == 0) { + xfree(pPriv); + return; + } pDestBuffer = NULL; pSrcBuffer = NULL; @@ -390,23 +452,97 @@ DRI2SwapBuffers(DrawablePtr pDraw) pSrcBuffer = pPriv->buffers[i]; } if (pSrcBuffer == NULL || pDestBuffer == NULL) - return BadValue; + return; - if (DRI2FlipCheck(pDraw) && - (*ds->SwapBuffers)(pDraw, pDestBuffer, pSrcBuffer, pPriv)) - { - pPriv->swapPending = TRUE; - return Success; + /* Ask the driver for a flip */ + if (pPriv->flipsPending) { + pPriv->flipsPending--; + ret = (*ds->SwapBuffers)(pScreen, pDestBuffer, pSrcBuffer, pPriv); + if (ret == TRUE) + return; + + pPriv->swapsPending++; + /* Schedule a copy for the next frame here? */ } + /* Swaps need a copy when we get the vblank event */ box.x1 = 0; box.y1 = 0; - box.x2 = pDraw->width; - box.y2 = pDraw->height; - REGION_INIT(drawable->pDraw->pScreen, ®ion, &box, 0); - - return DRI2CopyRegion(pDraw, ®ion, - DRI2BufferFrontLeft, DRI2BufferBackLeft); + box.x2 = pPriv->width; + box.y2 = pPriv->height; + REGION_INIT(pScreen, ®ion, &box, 0); + + DRI2CopyRegion(pDraw, ®ion, + DRI2BufferFrontLeft, DRI2BufferBackLeft); + pPriv->swapsPending--; + + DRI2SwapComplete(pPriv); +} + +static void drm_vblank_handler(int fd, unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *user_data) +{ + DRI2ScreenPtr ds = user_data; + DRI2SwapDataPtr cur = ds->swaps; + + while (cur) { + if (cur->frame == frame) { + DRI2SwapSubmit(cur); + DRI2RemoveSwap(cur); + } + cur = cur->next; + } +} + +static void +drm_wakeup_handler(pointer data, int err, pointer p) +{ + DRI2ScreenPtr ds = data; + fd_set *read_mask = p; + + if (err >= 0 && FD_ISSET(ds->fd, read_mask)) + drmHandleEvent(ds->fd, &ds->event_context); +} + +int +DRI2SwapBuffers(DrawablePtr pDraw, int interval) +{ + DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen); + DRI2DrawablePtr pPriv; + drmVBlank vbl; + + pPriv = DRI2GetDrawable(pDraw); + if (pPriv == NULL) + return BadDrawable; + + vbl.request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT; + + if (DRI2FlipCheck(pDraw)) { + /* + * Flips schedule for the next vblank, so we need to schedule them at + * frame - 1 to honor the swap interval. + */ + pPriv->flipsPending++; + if (interval > 1) { + vbl.request.sequence = interval - 1; + /* fixme: prevent cliprect changes between now and the flip */ + } else { + DRI2SwapComplete(pPriv); + return Success; + } + } else { + pPriv->swapsPending++; + vbl.request.sequence = interval; + } + + /* fixme: get correct crtc for this drawable */ + drmWaitVBlank(ds->fd, &vbl); + + /* Request an event for the requested frame */ + if (!DRI2AddSwap(pDraw, vbl.reply.sequence)) + return BadValue; + + return Success; } Bool @@ -417,7 +553,8 @@ DRI2WaitSwap(ClientPtr client, DrawablePtr pDrawable) /* If we're currently waiting for a swap on this drawable, reset * the request and suspend the client. We only support one * blocked client per drawable. */ - if (pPriv->swapPending && pPriv->blockedClient == NULL) { + if ((pPriv->swapsPending || pPriv->flipsPending) && + pPriv->blockedClient == NULL) { ResetCurrentRequest(client); client->sequence--; IgnoreClient(client); @@ -428,19 +565,15 @@ DRI2WaitSwap(ClientPtr client, DrawablePtr pDrawable) return FALSE; } -void -DRI2SwapComplete(void *data) +/* Wake up clients waiting for flip/swap completion */ +void DRI2SwapComplete(void *data) { DRI2DrawablePtr pPriv = data; if (pPriv->blockedClient) AttendClient(pPriv->blockedClient); - pPriv->swapPending = FALSE; pPriv->blockedClient = NULL; - - if (pPriv->refCount == 0) - xfree(pPriv); } void @@ -471,7 +604,7 @@ DRI2DestroyDrawable(DrawablePtr pDraw) /* If the window is destroyed while we have a swap pending, don't * actually free the priv yet. We'll need it in the DRI2SwapComplete() * callback and we'll free it there once we're done. */ - if (!pPriv->swapPending) + if (!pPriv->swapsPending && !pPriv->flipsPending) xfree(pPriv); if (pDraw->type == DRAWABLE_WINDOW) @@ -545,6 +678,13 @@ DRI2ScreenInit(ScreenPtr pScreen, DRI2InfoPtr info) if (info->version >= 4) ds->SwapBuffers = info->SwapBuffers; + ds->event_context.version = DRM_EVENT_CONTEXT_VERSION; + ds->event_context.vblank_handler = drm_vblank_handler; + + AddGeneralSocket(ds->fd); + RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA, + drm_wakeup_handler, ds); + dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, ds); xf86DrvMsg(pScreen->myNum, X_INFO, "[DRI2] Setup complete\n"); diff --git a/hw/xfree86/dri2/dri2.h b/hw/xfree86/dri2/dri2.h index 42bdb0978..e8f12d1d9 100644 --- a/hw/xfree86/dri2/dri2.h +++ b/hw/xfree86/dri2/dri2.h @@ -58,7 +58,7 @@ typedef void (*DRI2CopyRegionProcPtr)(DrawablePtr pDraw, RegionPtr pRegion, DRI2BufferPtr pDestBuffer, DRI2BufferPtr pSrcBuffer); -typedef Bool (*DRI2SwapBuffersProcPtr)(DrawablePtr pDraw, +typedef Bool (*DRI2SwapBuffersProcPtr)(ScreenPtr pScreen, DRI2BufferPtr pFrontBuffer, DRI2BufferPtr pBackBuffer, void *data); @@ -141,7 +141,7 @@ extern _X_EXPORT DRI2BufferPtr *DRI2GetBuffersWithFormat(DrawablePtr pDraw, int *width, int *height, unsigned int *attachments, int count, int *out_count); -extern _X_EXPORT int DRI2SwapBuffers(DrawablePtr pDrawable); +extern _X_EXPORT int DRI2SwapBuffers(DrawablePtr pDrawable, int interval); extern _X_EXPORT Bool DRI2WaitSwap(ClientPtr client, DrawablePtr pDrawable); extern _X_EXPORT void DRI2SwapComplete(void *data); diff --git a/hw/xfree86/dri2/dri2ext.c b/hw/xfree86/dri2/dri2ext.c index 666fe729c..9fc033ab7 100644 --- a/hw/xfree86/dri2/dri2ext.c +++ b/hw/xfree86/dri2/dri2ext.c @@ -347,7 +347,7 @@ ProcDRI2SwapBuffers(ClientPtr client) if (!validDrawable(client, stuff->drawable, &pDrawable, &status)) return status; - return DRI2SwapBuffers(pDrawable); + return DRI2SwapBuffers(pDrawable, 0); /* get swap interval... */ } static int |