summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesse Barnes <jbarnes@virtuousgeek.org>2009-09-02 13:59:56 -0700
committerJesse Barnes <jbarnes@virtuousgeek.org>2009-10-29 14:05:10 -0700
commitecf7439af402de218a3874a6ac9ebd83b50c970e (patch)
tree82da23fb0bc4e37db2a06cd804cc8d53df10ef32
parentae9d0ce9ba91b9ba1a4f7ec4dc3fccd35992ef66 (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.c19
-rw-r--r--glx/swap_interval.c5
-rw-r--r--hw/xfree86/dri2/dri2.c194
-rw-r--r--hw/xfree86/dri2/dri2.h4
-rw-r--r--hw/xfree86/dri2/dri2ext.c2
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, &region, &box, 0);
-
- return DRI2CopyRegion(pDraw, &region,
- DRI2BufferFrontLeft, DRI2BufferBackLeft);
+ box.x2 = pPriv->width;
+ box.y2 = pPriv->height;
+ REGION_INIT(pScreen, &region, &box, 0);
+
+ DRI2CopyRegion(pDraw, &region,
+ 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