xserver: Branch 'dri2-swapbuffers' - 2 commits
Jesse Barnes
jbarnes at kemper.freedesktop.org
Fri Oct 2 16:51:57 PDT 2009
glx/glxcmds.c | 2
glx/glxdrawable.h | 2
glx/glxdri.c | 2
glx/glxdri2.c | 16 +
glx/swap_interval.c | 5
hw/xfree86/dri2/dri2.c | 398 ++++++++++++++++++++++++++++++++++++++++++----
hw/xfree86/dri2/dri2.h | 39 ++++
hw/xfree86/dri2/dri2ext.c | 154 ++++++++++++++++-
8 files changed, 571 insertions(+), 47 deletions(-)
New commits:
commit 0290b68d3ea9d8f50f850c67d41074429aa6dd7e
Author: Jesse Barnes <jbarnes at virtuousgeek.org>
Date: Thu Sep 3 14:23:52 2009 -0700
DRI2: initial MSC support
Add support for MSC queries and waits along the same lines as asynchronous
swapbuffers.
diff --git a/glx/glxcmds.c b/glx/glxcmds.c
index f5632d1..d5e33e1 100644
--- a/glx/glxcmds.c
+++ b/glx/glxcmds.c
@@ -1481,7 +1481,7 @@ int __glXDisp_SwapBuffers(__GLXclientState *cl, GLbyte *pc)
return error;
if (pGlxDraw->type == DRAWABLE_WINDOW &&
- (*pGlxDraw->swapBuffers)(pGlxDraw) == GL_FALSE)
+ (*pGlxDraw->swapBuffers)(cl->client, pGlxDraw) == GL_FALSE)
return __glXError(GLXBadDrawable);
return Success;
diff --git a/glx/glxdrawable.h b/glx/glxdrawable.h
index 3f165ed..2a365c5 100644
--- a/glx/glxdrawable.h
+++ b/glx/glxdrawable.h
@@ -45,7 +45,7 @@ enum {
struct __GLXdrawable {
void (*destroy)(__GLXdrawable *private);
- GLboolean (*swapBuffers)(__GLXdrawable *);
+ GLboolean (*swapBuffers)(ClientPtr client, __GLXdrawable *);
void (*copySubBuffer)(__GLXdrawable *drawable,
int x, int y, int w, int h);
void (*waitX)(__GLXdrawable *);
diff --git a/glx/glxdri.c b/glx/glxdri.c
index c9d226b..8537609 100644
--- a/glx/glxdri.c
+++ b/glx/glxdri.c
@@ -245,7 +245,7 @@ __glXDRIdrawableDestroy(__GLXdrawable *drawable)
}
static GLboolean
-__glXDRIdrawableSwapBuffers(__GLXdrawable *basePrivate)
+__glXDRIdrawableSwapBuffers(ClientPtr client, __GLXdrawable *basePrivate)
{
__GLXDRIdrawable *private = (__GLXDRIdrawable *) basePrivate;
__GLXDRIscreen *screen =
diff --git a/glx/glxdri2.c b/glx/glxdri2.c
index cdf6794..c7a797e 100644
--- a/glx/glxdri2.c
+++ b/glx/glxdri2.c
@@ -174,18 +174,15 @@ __glXDRIdrawableWaitGL(__GLXdrawable *drawable)
* swap should happen, then perform the copy when we receive it.
*/
static GLboolean
-__glXDRIdrawableSwapBuffers(__GLXdrawable *drawable)
+__glXDRIdrawableSwapBuffers(ClientPtr client, __GLXdrawable *drawable)
{
__GLXDRIdrawable *priv = (__GLXDRIdrawable *) drawable;
__GLXDRIscreen *screen = priv->screen;
- int interval = 1;
-
- if (screen->swapControl)
- interval = screen->swapControl->getSwapInterval(priv->driDrawable);
+ CARD64 unused;
(*screen->flush->flushInvalidate)(priv->driDrawable);
- if (DRI2SwapBuffers(drawable->pDraw, interval) != Success)
+ if (DRI2SwapBuffers(client, drawable->pDraw, 0, 0, 0, &unused) != Success)
return FALSE;
return TRUE;
@@ -194,12 +191,10 @@ __glXDRIdrawableSwapBuffers(__GLXdrawable *drawable)
static int
__glXDRIdrawableSwapInterval(__GLXdrawable *drawable, int interval)
{
- __GLXDRIdrawable *draw = (__GLXDRIdrawable *) drawable;
- __GLXDRIscreen *screen =
- (__GLXDRIscreen *) glxGetScreen(drawable->pDraw->pScreen);
+ if (interval <= 0) /* || interval > BIGNUM? */
+ return GLX_BAD_VALUE;
- if (screen->swapControl)
- screen->swapControl->setSwapInterval(draw->driDrawable, interval);
+ DRI2SwapInterval(drawable->pDraw, interval);
return 0;
}
diff --git a/hw/xfree86/dri2/dri2.c b/hw/xfree86/dri2/dri2.c
index e462db5..0cbcc7e 100644
--- a/hw/xfree86/dri2/dri2.c
+++ b/hw/xfree86/dri2/dri2.c
@@ -34,6 +34,7 @@
#include <xorg-config.h>
#endif
+#include <errno.h>
#include <xf86drm.h>
#include "xf86Module.h"
#include "scrnintstr.h"
@@ -59,17 +60,75 @@ typedef struct _DRI2Drawable {
unsigned int swapsPending;
unsigned int flipsPending;
ClientPtr blockedClient;
+ int swap_interval;
+ CARD64 swap_count;
+ CARD64 last_swap_target; /* most recently queued swap target */
} DRI2DrawableRec, *DRI2DrawablePtr;
typedef struct _DRI2Screen *DRI2ScreenPtr;
-typedef struct _DRI2SwapData *DRI2SwapDataPtr;
+typedef struct _DRI2FrameEvent *DRI2FrameEventPtr;
-typedef struct _DRI2SwapData {
+#define container_of(ptr,type,mem) ((type *)((char *)(ptr) - offsetof(type, \
+ mem)))
+
+struct list {
+ struct list *prev, *next;
+};
+
+static inline void list_init(struct list *l)
+{
+ l->prev = l;
+ l->next = l;
+}
+
+static inline void __list_add(struct list *l, struct list *prev,
+ struct list *next)
+{
+ prev->next = l;
+ l->prev = prev;
+ l->next = next;
+ next->prev = l;
+}
+
+static inline void list_add(struct list *l, struct list *head)
+{
+ __list_add(l, head, head->next);
+}
+
+static inline void list_add_tail(struct list *l, struct list *head)
+{
+ __list_add(l, head->prev, head);
+}
+
+static inline void list_del(struct list *l)
+{
+ l->prev->next = l->next;
+ l->next->prev = l->prev;
+ list_init(l);
+}
+
+static inline Bool list_is_empty(struct list *l)
+{
+ return l->next == l;
+}
+
+#define list_foreach_safe(cur, tmp, head) \
+ for (cur = (head)->next, tmp = cur->next; cur != (head); \
+ cur = tmp, tmp = cur->next)
+
+enum DRI2FrameEventType {
+ DRI2_SWAP,
+ DRI2_WAITMSC,
+};
+
+typedef struct _DRI2FrameEvent {
DrawablePtr pDraw;
ScreenPtr pScreen;
+ ClientPtr client;
+ enum DRI2FrameEventType type;
int frame;
- DRI2SwapDataPtr next;
-} DRI2SwapDataRec;
+ struct list link;
+} DRI2FrameEventRec;
typedef struct _DRI2Screen {
const char *driverName;
@@ -77,12 +136,15 @@ typedef struct _DRI2Screen {
int fd;
unsigned int lastSequence;
drmEventContext event_context;
- DRI2SwapDataPtr swaps; /* Pending swap list */
+ struct list swaps;
DRI2CreateBufferProcPtr CreateBuffer;
DRI2DestroyBufferProcPtr DestroyBuffer;
DRI2CopyRegionProcPtr CopyRegion;
+ DRI2SetupSwapProcPtr SetupSwap;
DRI2SwapBuffersProcPtr SwapBuffers;
+ DRI2GetMSCProcPtr GetMSC;
+ DRI2SetupWaitMSCProcPtr SetupWaitMSC;
HandleExposuresProcPtr HandleExposures;
} DRI2ScreenRec;
@@ -140,6 +202,9 @@ DRI2CreateDrawable(DrawablePtr pDraw)
pPriv->swapsPending = 0;
pPriv->flipsPending = 0;
pPriv->blockedClient = NULL;
+ pPriv->swap_count = 0;
+ pPriv->swap_interval = 1;
+ pPriv->last_swap_target = 0;
if (pDraw->type == DRAWABLE_WINDOW)
{
@@ -382,46 +447,66 @@ DRI2FlipCheck(DrawablePtr pDraw)
return TRUE;
}
-static Bool DRI2AddSwap(DrawablePtr pDraw, int frame)
+static Bool DRI2AddFrameEvent(DrawablePtr pDraw, ClientPtr client,
+ enum DRI2FrameEventType type, int frame)
{
DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
DRI2DrawablePtr pPriv;
- DRI2SwapDataPtr new;
+ DRI2FrameEventPtr new;
pPriv = DRI2GetDrawable(pDraw);
if (pPriv == NULL)
return FALSE;
- new = xcalloc(1, sizeof(DRI2SwapDataRec));
+ new = xcalloc(1, sizeof(DRI2FrameEventRec));
if (!new)
return FALSE;
new->pScreen = pDraw->pScreen;
new->pDraw = pDraw;
+ new->client = client;
new->frame = frame;
- new->next = ds->swaps;
- ds->swaps = new;
+ new->type = type;
+
+ list_add_tail(&new->link, &ds->swaps);
return TRUE;
}
-static void DRI2RemoveSwap(DRI2SwapDataPtr swap)
+static void DRI2RemoveFrameEvent(DRI2FrameEventPtr event)
{
- ScreenPtr pScreen = swap->pScreen;
- DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
- DRI2SwapDataPtr cur = ds->swaps;
+ list_del(&event->link);
+ xfree(event);
+}
- while (cur) {
- if (cur == swap) {
- cur->next = swap->next;
- xfree(swap);
- }
- cur = cur->next;
- }
+static void
+DRI2WaitMSCComplete(DRI2FrameEventPtr swap, unsigned int sequence,
+ unsigned int tv_sec, unsigned int tv_usec)
+{
+ DrawablePtr pDraw = swap->pDraw;
+ DRI2DrawablePtr pPriv;
+
+ pPriv = DRI2GetDrawable(pDraw);
+ if (pPriv == NULL)
+ return;
+
+ ProcDRI2WaitMSCReply(swap->client, ((CARD64)tv_sec * 1000000) + tv_usec,
+ sequence, pPriv->swap_count);
+
+ if (pPriv->blockedClient)
+ AttendClient(pPriv->blockedClient);
+
+ pPriv->blockedClient = NULL;
+}
+
+/* Wake up clients waiting for flip/swap completion */
+static void DRI2SwapComplete(DRI2DrawablePtr pPriv)
+{
+ pPriv->swap_count++;
}
static void
-DRI2SwapSubmit(DRI2SwapDataPtr swap)
+DRI2SwapSubmit(DRI2FrameEventPtr swap)
{
DrawablePtr pDraw = swap->pDraw;
ScreenPtr pScreen = swap->pScreen;
@@ -471,8 +556,7 @@ DRI2SwapSubmit(DRI2SwapDataPtr swap)
box.y2 = pPriv->height;
REGION_INIT(pScreen, ®ion, &box, 0);
- DRI2CopyRegion(pDraw, ®ion,
- DRI2BufferFrontLeft, DRI2BufferBackLeft);
+ DRI2CopyRegion(pDraw, ®ion, DRI2BufferFrontLeft, DRI2BufferBackLeft);
pPriv->swapsPending--;
DRI2SwapComplete(pPriv);
@@ -482,14 +566,39 @@ 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;
+ DRI2DrawablePtr pPriv;
+ struct list *cur, *tmp;
+
+ if (list_is_empty(&ds->swaps)) {
+ ErrorF("tried to dequeue non-existent swap\n");
+ return;
+ }
- while (cur) {
- if (cur->frame == frame) {
- DRI2SwapSubmit(cur);
- DRI2RemoveSwap(cur);
+ list_foreach_safe(cur, tmp, &ds->swaps) {
+ DRI2FrameEventPtr swap = container_of(cur, DRI2FrameEventRec, link);
+
+ if (swap->frame != frame)
+ continue;
+
+ pPriv = DRI2GetDrawable(swap->pDraw);
+ if (pPriv == NULL) {
+ DRI2RemoveFrameEvent(swap);
+ ErrorF("no dri2priv??\n");
+ continue; /* FIXME: check priv refcounting */
+ }
+
+ switch (swap->type) {
+ case DRI2_SWAP:
+ DRI2SwapSubmit(swap);
+ break;
+ case DRI2_WAITMSC:
+ DRI2WaitMSCComplete(swap, frame, tv_sec, tv_usec);
+ break;
+ default:
+ /* Unknown type */
+ break;
}
- cur = cur->next;
+ DRI2RemoveFrameEvent(swap);
}
}
@@ -504,17 +613,24 @@ drm_wakeup_handler(pointer data, int err, pointer p)
}
int
-DRI2SwapBuffers(DrawablePtr pDraw, int interval)
+DRI2SwapBuffers(ClientPtr client, DrawablePtr pDraw, CARD64 target_msc,
+ CARD64 divisor, CARD64 remainder, CARD64 *swap_target)
{
DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
DRI2DrawablePtr pPriv;
- drmVBlank vbl;
+ CARD64 event_frame;
+ int ret;
pPriv = DRI2GetDrawable(pDraw);
if (pPriv == NULL)
return BadDrawable;
- vbl.request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT;
+ /*
+ * Swap target for this swap is last swap target + swap interval since
+ * we have to account for the current swap count, interval, and the
+ * number of pending swaps.
+ */
+ *swap_target = pPriv->last_swap_target + pPriv->swap_interval;
if (DRI2FlipCheck(pDraw)) {
/*
@@ -522,25 +638,28 @@ DRI2SwapBuffers(DrawablePtr pDraw, int interval)
* frame - 1 to honor the swap interval.
*/
pPriv->flipsPending++;
- if (interval > 1) {
- vbl.request.sequence = interval - 1;
+ if (pPriv->swap_interval > 1) {
+ *swap_target = *swap_target - 1;
/* fixme: prevent cliprect changes between now and the flip */
} else {
+ /* FIXME: perform immediate page flip */
DRI2SwapComplete(pPriv);
- return Success;
+ return 0;
}
} else {
pPriv->swapsPending++;
- vbl.request.sequence = interval;
}
- /* fixme: get correct crtc for this drawable */
- drmWaitVBlank(ds->fd, &vbl);
+ ret = (*ds->SetupSwap)(pDraw, *swap_target, divisor, remainder, ds,
+ &event_frame);
+ if (!ret)
+ return BadDrawable;
- /* Request an event for the requested frame */
- if (!DRI2AddSwap(pDraw, vbl.reply.sequence))
+ if (!DRI2AddFrameEvent(pDraw, client, DRI2_SWAP, event_frame))
return BadValue;
+ pPriv->last_swap_target = *swap_target;
+
return Success;
}
@@ -564,15 +683,88 @@ DRI2WaitSwap(ClientPtr client, DrawablePtr pDrawable)
return FALSE;
}
-/* Wake up clients waiting for flip/swap completion */
-void DRI2SwapComplete(void *data)
+void
+DRI2SwapInterval(DrawablePtr pDrawable, int interval)
{
- DRI2DrawablePtr pPriv = data;
+ DRI2DrawablePtr pPriv = DRI2GetDrawable(pDrawable);
- if (pPriv->blockedClient)
- AttendClient(pPriv->blockedClient);
+ /* fixme: check against arbitrary max? */
- pPriv->blockedClient = NULL;
+ pPriv->swap_interval = interval;
+}
+
+
+
+int
+DRI2GetMSC(DrawablePtr pDraw, CARD64 *ust, CARD64 *msc, CARD64 *sbc)
+{
+ DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
+ DRI2DrawablePtr pPriv;
+ Bool ret;
+
+ pPriv = DRI2GetDrawable(pDraw);
+ if (pPriv == NULL)
+ return BadDrawable;
+
+ if (!ds->GetMSC)
+ FatalError("advertised MSC support w/o driver hook\n");
+
+ ret = (*ds->GetMSC)(pDraw, ust, msc);
+ if (!ret)
+ return BadDrawable;
+
+ *sbc = pPriv->swap_count;
+
+ return Success;
+}
+
+int
+DRI2WaitMSC(ClientPtr client, DrawablePtr pDraw, CARD64 target_msc,
+ CARD64 divisor, CARD64 remainder)
+{
+ DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
+ DRI2DrawablePtr pPriv;
+ CARD64 event_frame;
+ Bool ret;
+
+ pPriv = DRI2GetDrawable(pDraw);
+ if (pPriv == NULL)
+ return BadDrawable;
+
+ ret = (*ds->SetupWaitMSC)(pDraw, target_msc, divisor, remainder, ds,
+ &event_frame);
+ if (!ret) {
+ ErrorF("setupmsc failed: %d\n", ret);
+ return BadDrawable;
+ }
+
+ ret = DRI2AddFrameEvent(pDraw, client, DRI2_WAITMSC, event_frame);
+ if (!ret)
+ return BadDrawable;
+
+ /* DDX returned > 0, block the client until its wait completes */
+
+ if (pPriv->blockedClient == NULL) {
+ IgnoreClient(client);
+ pPriv->blockedClient = client;
+ }
+
+ return Success;
+}
+
+int
+DRI2WaitSBC(DrawablePtr pDraw, CARD64 target_sbc, CARD64 *ust, CARD64 *msc,
+ CARD64 *sbc)
+{
+ DRI2DrawablePtr pPriv;
+
+ pPriv = DRI2GetDrawable(pDraw);
+ if (pPriv == NULL)
+ return BadDrawable;
+
+ /* fixme: put client to sleep until swap count hits target */
+
+ return Success;
}
void
@@ -668,12 +860,18 @@ DRI2ScreenInit(ScreenPtr pScreen, DRI2InfoPtr info)
ds->DestroyBuffer = info->DestroyBuffer;
ds->CopyRegion = info->CopyRegion;
- if (info->version >= 4)
+ if (info->version >= 4) {
+ ds->SetupSwap = info->SetupSwap;
ds->SwapBuffers = info->SwapBuffers;
+ ds->SetupWaitMSC = info->SetupWaitMSC;
+ ds->GetMSC = info->GetMSC;
+ }
ds->event_context.version = DRM_EVENT_CONTEXT_VERSION;
ds->event_context.vblank_handler = drm_vblank_handler;
+ list_init(&ds->swaps);
+
AddGeneralSocket(ds->fd);
RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA,
drm_wakeup_handler, ds);
diff --git a/hw/xfree86/dri2/dri2.h b/hw/xfree86/dri2/dri2.h
index e8f12d1..57f34bf 100644
--- a/hw/xfree86/dri2/dri2.h
+++ b/hw/xfree86/dri2/dri2.h
@@ -58,6 +58,12 @@ typedef void (*DRI2CopyRegionProcPtr)(DrawablePtr pDraw,
RegionPtr pRegion,
DRI2BufferPtr pDestBuffer,
DRI2BufferPtr pSrcBuffer);
+typedef int (*DRI2SetupSwapProcPtr)(DrawablePtr pDraw,
+ CARD64 target_msc,
+ CARD64 divisor,
+ CARD64 remainder,
+ void *data,
+ CARD64 *event_frame);
typedef Bool (*DRI2SwapBuffersProcPtr)(ScreenPtr pScreen,
DRI2BufferPtr pFrontBuffer,
DRI2BufferPtr pBackBuffer,
@@ -71,6 +77,14 @@ typedef DRI2BufferPtr (*DRI2CreateBufferProcPtr)(DrawablePtr pDraw,
unsigned int format);
typedef void (*DRI2DestroyBufferProcPtr)(DrawablePtr pDraw,
DRI2BufferPtr buffer);
+typedef int (*DRI2GetMSCProcPtr)(DrawablePtr pDraw, CARD64 *ust,
+ CARD64 *msc);
+typedef int (*DRI2SetupWaitMSCProcPtr)(DrawablePtr pDraw,
+ CARD64 target_msc,
+ CARD64 divisor,
+ CARD64 remainder,
+ void *data,
+ CARD64 *event_frame);
/**
* Version of the DRI2InfoRec structure defined in this header
@@ -86,8 +100,10 @@ typedef struct {
DRI2CreateBufferProcPtr CreateBuffer;
DRI2DestroyBufferProcPtr DestroyBuffer;
DRI2CopyRegionProcPtr CopyRegion;
+ DRI2SetupSwapProcPtr SetupSwap;
DRI2SwapBuffersProcPtr SwapBuffers;
-
+ DRI2GetMSCProcPtr GetMSC;
+ DRI2SetupWaitMSCProcPtr SetupWaitMSC;
} DRI2InfoRec, *DRI2InfoPtr;
extern _X_EXPORT Bool DRI2ScreenInit(ScreenPtr pScreen,
@@ -141,8 +157,23 @@ 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, int interval);
+extern _X_EXPORT void DRI2SwapInterval(DrawablePtr pDrawable, int interval);
+extern _X_EXPORT int DRI2SwapBuffers(ClientPtr client, DrawablePtr pDrawable,
+ CARD64 target_msc, CARD64 divisor,
+ CARD64 remainder, CARD64 *swap_target);
extern _X_EXPORT Bool DRI2WaitSwap(ClientPtr client, DrawablePtr pDrawable);
-extern _X_EXPORT void DRI2SwapComplete(void *data);
+
+extern _X_EXPORT int DRI2GetMSC(DrawablePtr pDrawable, CARD64 *ust,
+ CARD64 *msc, CARD64 *sbc);
+extern _X_EXPORT int DRI2WaitMSC(ClientPtr client, DrawablePtr pDrawable,
+ CARD64 target_msc, CARD64 divisor,
+ CARD64 remainder);
+extern _X_EXPORT int ProcDRI2WaitMSCReply(ClientPtr client, CARD64 ust,
+ CARD64 msc, CARD64 sbc);
+extern _X_EXPORT int DRI2WaitSBC(DrawablePtr pDraw, CARD64 target_sbc,
+ CARD64 *ust, CARD64 *msc, CARD64 *sbc);
+#define DRI2_WAITMSC_ERROR -1
+#define DRI2_WAITMSC_DONE 0
+#define DRI2_WAITMSC_BLOCK 1
#endif
diff --git a/hw/xfree86/dri2/dri2ext.c b/hw/xfree86/dri2/dri2ext.c
index d0fd341..012a7a2 100644
--- a/hw/xfree86/dri2/dri2ext.c
+++ b/hw/xfree86/dri2/dri2ext.c
@@ -253,9 +253,6 @@ ProcDRI2GetBuffers(ClientPtr client)
if (!validDrawable(client, stuff->drawable, &pDrawable, &status))
return status;
- if (DRI2WaitSwap(client, pDrawable))
- return client->noClientException;
-
attachments = (unsigned int *) &stuff[1];
buffers = DRI2GetBuffers(pDrawable, &width, &height,
attachments, stuff->count, &count);
@@ -279,9 +276,6 @@ ProcDRI2GetBuffersWithFormat(ClientPtr client)
if (!validDrawable(client, stuff->drawable, &pDrawable, &status))
return status;
- if (DRI2WaitSwap(client, pDrawable))
- return client->noClientException;
-
attachments = (unsigned int *) &stuff[1];
buffers = DRI2GetBuffersWithFormat(pDrawable, &width, &height,
attachments, stuff->count, &count);
@@ -327,11 +321,26 @@ ProcDRI2CopyRegion(ClientPtr client)
return client->noClientException;
}
+static void
+load_swap_reply(xDRI2SwapBuffersReply *rep, CARD64 sbc)
+{
+ rep->swap_hi = sbc >> 32;
+ rep->swap_lo = sbc & 0xffffffff;
+}
+
+static CARD64
+vals_to_card64(CARD32 lo, CARD32 hi)
+{
+ return (CARD64)hi << 32 | lo;
+}
+
static int
ProcDRI2SwapBuffers(ClientPtr client)
{
REQUEST(xDRI2SwapBuffersReq);
+ xDRI2SwapBuffersReply rep;
DrawablePtr pDrawable;
+ CARD64 target_msc, divisor, remainder, swap_target;
int status;
REQUEST_SIZE_MATCH(xDRI2SwapBuffersReq);
@@ -339,7 +348,132 @@ ProcDRI2SwapBuffers(ClientPtr client)
if (!validDrawable(client, stuff->drawable, &pDrawable, &status))
return status;
- return DRI2SwapBuffers(pDrawable, 0); /* get swap interval... */
+ target_msc = vals_to_card64(stuff->target_msc_lo, stuff->target_msc_hi);
+ divisor = vals_to_card64(stuff->divisor_lo, stuff->divisor_hi);
+ remainder = vals_to_card64(stuff->remainder_lo, stuff->remainder_hi);
+
+ status = DRI2SwapBuffers(client, pDrawable, target_msc, divisor, remainder,
+ &swap_target);
+ if (status != Success)
+ return BadDrawable;
+
+ rep.type = X_Reply;
+ rep.length = 0;
+ rep.sequenceNumber = client->sequence;
+ load_swap_reply(&rep, swap_target);
+
+ WriteToClient(client, sizeof(xDRI2SwapBuffersReply), &rep);
+
+ return client->noClientException;
+}
+
+static void
+load_msc_reply(xDRI2MSCReply *rep, CARD64 ust, CARD64 msc, CARD64 sbc)
+{
+ rep->ust_hi = ust >> 32;
+ rep->ust_lo = ust & 0xffffffff;
+ rep->msc_hi = msc >> 32;
+ rep->msc_lo = msc & 0xffffffff;
+ rep->sbc_hi = sbc >> 32;
+ rep->sbc_lo = sbc & 0xffffffff;
+}
+
+static int
+ProcDRI2GetMSC(ClientPtr client)
+{
+ REQUEST(xDRI2GetMSCReq);
+ xDRI2MSCReply rep;
+ DrawablePtr pDrawable;
+ CARD64 ust, msc, sbc;
+ int status;
+
+ REQUEST_SIZE_MATCH(xDRI2GetMSCReq);
+
+ if (!validDrawable(client, stuff->drawable, &pDrawable, &status))
+ return status;
+
+ status = DRI2GetMSC(pDrawable, &ust, &msc, &sbc);
+ if (status != Success)
+ return status;
+
+ rep.type = X_Reply;
+ rep.length = 0;
+ rep.sequenceNumber = client->sequence;
+ load_msc_reply(&rep, ust, msc, sbc);
+
+ WriteToClient(client, sizeof(xDRI2MSCReply), &rep);
+
+ return client->noClientException;
+}
+
+static int
+ProcDRI2WaitMSC(ClientPtr client)
+{
+ REQUEST(xDRI2WaitMSCReq);
+ DrawablePtr pDrawable;
+ CARD64 target, divisor, remainder;
+ int status;
+
+ /* FIXME: in restart case, client may be gone at this point */
+
+ REQUEST_SIZE_MATCH(xDRI2WaitMSCReq);
+
+ if (!validDrawable(client, stuff->drawable, &pDrawable, &status))
+ return status;
+
+ target = vals_to_card64(stuff->target_msc_lo, stuff->target_msc_hi);
+ divisor = vals_to_card64(stuff->divisor_lo, stuff->divisor_hi);
+ remainder = vals_to_card64(stuff->remainder_lo, stuff->remainder_hi);
+
+ status = DRI2WaitMSC(client, pDrawable, target, divisor, remainder);
+ if (status != Success)
+ return status;
+
+ return client->noClientException;
+}
+
+int
+ProcDRI2WaitMSCReply(ClientPtr client, CARD64 ust, CARD64 msc, CARD64 sbc)
+{
+ xDRI2MSCReply rep;
+
+ rep.type = X_Reply;
+ rep.length = 0;
+ rep.sequenceNumber = client->sequence;
+ load_msc_reply(&rep, ust, msc, sbc);
+
+ WriteToClient(client, sizeof(xDRI2MSCReply), &rep);
+
+ return client->noClientException;
+}
+
+static int
+ProcDRI2WaitSBC(ClientPtr client)
+{
+ REQUEST(xDRI2WaitSBCReq);
+ xDRI2MSCReply rep;
+ DrawablePtr pDrawable;
+ CARD64 target, ust, msc, sbc;
+ int status;
+
+ REQUEST_SIZE_MATCH(xDRI2WaitSBCReq);
+
+ if (!validDrawable(client, stuff->drawable, &pDrawable, &status))
+ return status;
+
+ target = vals_to_card64(stuff->target_sbc_lo, stuff->target_sbc_hi);
+ status = DRI2WaitSBC(pDrawable, target, &ust, &msc, &sbc);
+ if (status != Success)
+ return status;
+
+ rep.type = X_Reply;
+ rep.length = 0;
+ rep.sequenceNumber = client->sequence;
+ load_msc_reply(&rep, ust, msc, sbc);
+
+ WriteToClient(client, sizeof(xDRI2MSCReply), &rep);
+
+ return client->noClientException;
}
static int
@@ -372,6 +506,12 @@ ProcDRI2Dispatch (ClientPtr client)
return ProcDRI2GetBuffersWithFormat(client);
case X_DRI2SwapBuffers:
return ProcDRI2SwapBuffers(client);
+ case X_DRI2GetMSC:
+ return ProcDRI2GetMSC(client);
+ case X_DRI2WaitMSC:
+ return ProcDRI2WaitMSC(client);
+ case X_DRI2WaitSBC:
+ return ProcDRI2WaitSBC(client);
default:
return BadRequest;
}
commit 9419b678f41dcd5658e83ae644a7a3923030ca9f
Author: Jesse Barnes <jbarnes at virtuousgeek.org>
Date: Wed Sep 2 13:59:56 2009 -0700
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.
diff --git a/glx/glxdri2.c b/glx/glxdri2.c
index 4b89c31..cdf6794 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 3a52420..728944b 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 8f5e0c3..e462db5 100644
--- a/hw/xfree86/dri2/dri2.c
+++ b/hw/xfree86/dri2/dri2.c
@@ -56,15 +56,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;
@@ -72,7 +85,7 @@ typedef struct _DRI2Screen {
DRI2SwapBuffersProcPtr SwapBuffers;
HandleExposuresProcPtr HandleExposures;
-} DRI2ScreenRec, *DRI2ScreenPtr;
+} DRI2ScreenRec;
static DRI2ScreenPtr
DRI2GetScreen(ScreenPtr pScreen)
@@ -86,6 +99,9 @@ DRI2GetDrawable(DrawablePtr pDraw)
WindowPtr pWin;
PixmapPtr pPixmap;
+ if (!pDraw)
+ return NULL;
+
if (pDraw->type == DRAWABLE_WINDOW)
{
pWin = (WindowPtr) pDraw;
@@ -121,7 +137,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)
@@ -365,19 +382,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;
@@ -389,23 +451,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
@@ -416,7 +552,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);
@@ -427,19 +564,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
@@ -470,7 +603,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)
@@ -538,6 +671,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 42bdb09..e8f12d1 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 9f5f389..d0fd341 100644
--- a/hw/xfree86/dri2/dri2ext.c
+++ b/hw/xfree86/dri2/dri2ext.c
@@ -339,7 +339,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
More information about the xorg-commit
mailing list