mirror of
https://gitlab.freedesktop.org/xorg/xserver.git
synced 2026-06-16 10:58:35 +02:00
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.
This commit is contained in:
parent
ae9d0ce9ba
commit
ecf7439af4
5 changed files with 195 additions and 35 deletions
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
DRI2BufferPtr pDestBuffer, pSrcBuffer;
|
||||
int i;
|
||||
BoxRec box;
|
||||
RegionRec region;
|
||||
DRI2SwapDataPtr new;
|
||||
|
||||
pPriv = DRI2GetDrawable(pDraw);
|
||||
if (pPriv == NULL)
|
||||
return BadDrawable;
|
||||
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;
|
||||
BoxRec box;
|
||||
RegionRec region;
|
||||
int ret, i;
|
||||
|
||||
pPriv = DRI2GetDrawable(pDraw);
|
||||
if (pPriv == NULL)
|
||||
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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue