summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrancisco Jerez <currojerez@riseup.net>2010-10-21 22:55:02 +0200
committerFrancisco Jerez <currojerez@riseup.net>2010-10-22 15:33:03 +0200
commit70f0d2c886ceaa965ca4864788f4dd8e8f757a92 (patch)
tree804fa8deb203fdb2c469c4c2a794f017492933a5
parent72dd4899ff036b09e3680ed2e09b3dbae3a7ba71 (diff)
downloadxorg-driver-xf86-video-nouveau-70f0d2c886ceaa965ca4864788f4dd8e8f757a92.tar.gz
dri2: Add sync-to-vblank support.
Note that you need a recent enough kernel for it to work. Use the "GLXVBlank" option to enable it (it defaults to disabled for now). Signed-off-by: Francisco Jerez <currojerez@riseup.net>
-rw-r--r--man/nouveau.man5
-rw-r--r--src/drmmode_display.c4
-rw-r--r--src/nouveau_dri2.c183
-rw-r--r--src/nv_const.h4
-rw-r--r--src/nv_driver.c10
-rw-r--r--src/nv_proto.h3
-rw-r--r--src/nv_type.h1
7 files changed, 202 insertions, 8 deletions
diff --git a/man/nouveau.man b/man/nouveau.man
index 0e4c3a2..a8961ad 100644
--- a/man/nouveau.man
+++ b/man/nouveau.man
@@ -75,6 +75,11 @@ Enable or disable use of the shadow framebuffer layer. Default: off.
.TP
.BI "Option \*qWrappedFB\*q \*q" boolean \*q
Enable or disable wfb, only affects nv50+. Useful for some legacy configurations where high rendering latency is perceived. Default: wfb is disabled.
+.TP
+.BI "Option \*qGLXVBlank\*q \*q" boolean \*q
+Synchronize GLX clients to VBlank. Useful where tearing is a problem,
+harmful if the GPU isn't fast enough to keep up with the monitor
+refresh rate. Default: off.
.SH "SEE ALSO"
__xservername__(__appmansuffix__), __xconfigfile__(__filemansuffix__), Xserver(__appmansuffix__), X(__miscmansuffix__)
.SH AUTHORS
diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index 6c0e910..115398a 100644
--- a/src/drmmode_display.c
+++ b/src/drmmode_display.c
@@ -1289,6 +1289,10 @@ drmmode_screen_init(ScreenPtr pScreen)
drmmode_ptr drmmode = drmmode_from_scrn(scrn);
drmmode_uevent_init(scrn);
+
+ /* Plug in a vblank event handler */
+ drmmode->event_context.version = DRM_EVENT_CONTEXT_VERSION;
+ drmmode->event_context.vblank_handler = nouveau_dri2_vblank_handler;
AddGeneralSocket(drmmode->fd);
/* Register a wakeup handler to get informed on DRM events */
diff --git a/src/nouveau_dri2.c b/src/nouveau_dri2.c
index 1387a56..edb950f 100644
--- a/src/nouveau_dri2.c
+++ b/src/nouveau_dri2.c
@@ -106,19 +106,187 @@ nouveau_dri2_copy_region(DrawablePtr pDraw, RegionPtr pRegion,
pGC->funcs->ChangeClip(pGC, CT_REGION, pCopyClip, 0);
ValidateGC(&pdpix->drawable, pGC);
- /* Throttle on the previous frame before blitting this one */
- if (dst->base.attachment == DRI2BufferFrontLeft) {
- struct nouveau_bo *bo = nouveau_pixmap_bo(dst->ppix);
- nouveau_bo_map(bo, NOUVEAU_BO_RD);
- nouveau_bo_unmap(bo);
- }
-
pGC->ops->CopyArea(&pspix->drawable, &pdpix->drawable, pGC, 0, 0,
pDraw->width, pDraw->height, 0, 0);
FreeScratchGC(pGC);
}
+struct nouveau_dri2_vblank_state {
+ enum {
+ SWAP
+ } action;
+
+ ClientPtr client;
+ XID draw;
+
+ DRI2BufferPtr dst;
+ DRI2BufferPtr src;
+ DRI2SwapEventPtr func;
+ void *data;
+};
+
+static Bool
+can_sync_to_vblank(DrawablePtr draw)
+{
+ ScrnInfoPtr scrn = xf86Screens[draw->pScreen->myNum];
+ NVPtr pNv = NVPTR(scrn);
+ PixmapPtr pix = NVGetDrawablePixmap(draw);
+
+ return pNv->glx_vblank &&
+ nouveau_exa_pixmap_is_onscreen(pix) &&
+ nv_window_belongs_to_crtc(scrn, draw->x, draw->y,
+ draw->width, draw->height);
+}
+
+static int
+nouveau_wait_vblank(DrawablePtr draw, int type, CARD64 msc,
+ CARD64 *pmsc, CARD64 *pust, void *data)
+{
+ ScrnInfoPtr scrn = xf86Screens[draw->pScreen->myNum];
+ NVPtr pNv = NVPTR(scrn);
+ int crtcs = nv_window_belongs_to_crtc(scrn, draw->x, draw->y,
+ draw->width, draw->height);
+ drmVBlank vbl;
+ int ret;
+
+ vbl.request.type = type | (crtcs == 2 ? DRM_VBLANK_SECONDARY : 0);
+ vbl.request.sequence = msc;
+ vbl.request.signal = (unsigned long)data;
+
+ ret = drmWaitVBlank(nouveau_device(pNv->dev)->fd, &vbl);
+ if (ret) {
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "Wait for VBlank failed: %s\n", strerror(errno));
+ return ret;
+ }
+
+ if (pmsc)
+ *pmsc = vbl.reply.sequence;
+ if (pust)
+ *pust = (CARD64)vbl.reply.tval_sec * 1000000 +
+ vbl.reply.tval_usec;
+ return 0;
+}
+
+static void
+nouveau_dri2_finish_swap(DrawablePtr draw, unsigned int frame,
+ unsigned int tv_sec, unsigned int tv_usec,
+ struct nouveau_dri2_vblank_state *s)
+{
+ ScrnInfoPtr scrn = xf86Screens[draw->pScreen->myNum];
+ NVPtr pNv = NVPTR(scrn);
+ PixmapPtr dst_pix = nouveau_dri2_buffer(s->dst)->ppix;
+ PixmapPtr src_pix = nouveau_dri2_buffer(s->src)->ppix;
+ struct nouveau_bo *dst_bo = nouveau_pixmap_bo(dst_pix);
+ struct nouveau_bo *src_bo = nouveau_pixmap_bo(src_pix);
+ struct nouveau_channel *chan = pNv->chan;
+ BoxRec box = { 0, 0, draw->width, draw->height };
+ RegionRec region;
+ int ret;
+
+ /* Throttle on the previous frame before swapping */
+ nouveau_bo_map(dst_bo, NOUVEAU_BO_RD);
+ nouveau_bo_unmap(dst_bo);
+
+ if (can_sync_to_vblank(draw)) {
+ int x1 = draw->x, y1 = draw->y,
+ x2 = draw->x + draw->width,
+ y2 = draw->y + draw->height;
+
+ /* Reference the back buffer to sync it to vblank */
+ WAIT_RING(chan, 1);
+ OUT_RELOC(chan, src_bo, 0,
+ NOUVEAU_BO_VRAM | NOUVEAU_BO_RD, 0, 0);
+
+ if (pNv->Architecture >= NV_ARCH_50)
+ NV50SyncToVBlank(dst_pix, x1, y1, x2, y2);
+ else
+ NV11SyncToVBlank(dst_pix, x1, y1, x2, y2);
+
+ FIRE_RING(chan);
+ }
+
+
+ RegionInit(&region, &box, 0);
+ nouveau_dri2_copy_region(draw, &region, s->dst, s->src);
+
+ DRI2SwapComplete(s->client, draw, frame, tv_sec, tv_usec,
+ DRI2_BLIT_COMPLETE, s->func, s->data);
+ free(s);
+}
+
+static Bool
+nouveau_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
+ DRI2BufferPtr dst, DRI2BufferPtr src,
+ CARD64 *target_msc, CARD64 divisor, CARD64 remainder,
+ DRI2SwapEventPtr func, void *data)
+{
+ struct nouveau_dri2_vblank_state *s;
+ CARD64 current_msc;
+ int ret;
+
+ /* Initialize a swap structure */
+ s = malloc(sizeof(*s));
+ if (!s)
+ return FALSE;
+
+ *s = (struct nouveau_dri2_vblank_state)
+ { SWAP, client, draw->id, dst, src, func, data };
+
+ if (can_sync_to_vblank(draw)) {
+ /* Get current sequence */
+ ret = nouveau_wait_vblank(draw, DRM_VBLANK_RELATIVE, 0,
+ &current_msc, NULL, NULL);
+ if (ret)
+ goto fail;
+
+ /* Calculate a swap target if we don't have one */
+ if (current_msc >= *target_msc && divisor)
+ *target_msc = current_msc + divisor
+ - (current_msc - remainder) % divisor;
+
+ /* Request a vblank event one frame before the target */
+ ret = nouveau_wait_vblank(draw, DRM_VBLANK_ABSOLUTE |
+ DRM_VBLANK_EVENT,
+ max(current_msc, *target_msc - 1),
+ NULL, NULL, s);
+ if (ret)
+ goto fail;
+
+ } else {
+ /* We can't/don't want to sync to vblank, just swap. */
+ nouveau_dri2_finish_swap(draw, 0, 0, 0, s);
+ }
+
+ return TRUE;
+
+fail:
+ free(s);
+ return FALSE;
+}
+
+void
+nouveau_dri2_vblank_handler(int fd, unsigned int frame,
+ unsigned int tv_sec, unsigned int tv_usec,
+ void *event_data)
+{
+ struct nouveau_dri2_vblank_state *s = event_data;
+ DrawablePtr draw;
+ int ret;
+
+ ret = dixLookupDrawable(&draw, s->draw, serverClient,
+ M_ANY, DixWriteAccess);
+ if (ret)
+ return;
+
+ switch (s->action) {
+ case SWAP:
+ nouveau_dri2_finish_swap(draw, frame, tv_sec, tv_usec, s);
+ break;
+ }
+}
+
Bool
nouveau_dri2_init(ScreenPtr pScreen)
{
@@ -138,6 +306,7 @@ nouveau_dri2_init(ScreenPtr pScreen)
dri2.CreateBuffer = nouveau_dri2_create_buffer;
dri2.DestroyBuffer = nouveau_dri2_destroy_buffer;
dri2.CopyRegion = nouveau_dri2_copy_region;
+ dri2.ScheduleSwap = nouveau_dri2_schedule_swap;
return DRI2ScreenInit(pScreen, &dri2);
}
diff --git a/src/nv_const.h b/src/nv_const.h
index 5e1bc50..c355cad 100644
--- a/src/nv_const.h
+++ b/src/nv_const.h
@@ -14,6 +14,7 @@ typedef enum {
OPTION_SHADOW_FB,
OPTION_VIDEO_KEY,
OPTION_WFB,
+ OPTION_GLX_VBLANK,
} NVOpts;
@@ -23,7 +24,8 @@ static const OptionInfoRec NVOptions[] = {
{ OPTION_NOACCEL, "NoAccel", OPTV_BOOLEAN, {0}, FALSE },
{ OPTION_SHADOW_FB, "ShadowFB", OPTV_BOOLEAN, {0}, FALSE },
{ OPTION_VIDEO_KEY, "VideoKey", OPTV_INTEGER, {0}, FALSE },
- { OPTION_WFB, "WrappedFB", OPTV_BOOLEAN, {0}, FALSE },
+ { OPTION_WFB, "WrappedFB", OPTV_BOOLEAN, {0}, FALSE },
+ { OPTION_GLX_VBLANK, "GLXVBlank", OPTV_BOOLEAN, {0}, FALSE },
{ -1, NULL, OPTV_NONE, {0}, FALSE }
};
diff --git a/src/nv_driver.c b/src/nv_driver.c
index cd8c001..e07ce8c 100644
--- a/src/nv_driver.c
+++ b/src/nv_driver.c
@@ -782,6 +782,16 @@ NVPreInit(ScrnInfoPtr pScrn, int flags)
pNv->tiled_scanout = TRUE;
}
+ if (!pNv->NoAccel && pNv->dev->chipset >= 0x11) {
+ from = X_DEFAULT;
+ if (xf86GetOptValBool(pNv->Options, OPTION_GLX_VBLANK,
+ &pNv->glx_vblank))
+ from = X_CONFIG;
+
+ xf86DrvMsg(pScrn->scrnIndex, from, "GLX sync to VBlank %s.\n",
+ pNv->glx_vblank ? "enabled" : "disabled");
+ }
+
if(xf86GetOptValInteger(pNv->Options, OPTION_VIDEO_KEY, &(pNv->videoKey))) {
xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "video key set to 0x%x\n",
pNv->videoKey);
diff --git a/src/nv_proto.h b/src/nv_proto.h
index 50f67f5..89411b2 100644
--- a/src/nv_proto.h
+++ b/src/nv_proto.h
@@ -19,6 +19,9 @@ void NVAccelFree(ScrnInfoPtr pScrn);
void NV11SyncToVBlank(PixmapPtr ppix, int x1, int y1, int x2, int y2);
/* in nouveau_dri2.c */
+void nouveau_dri2_vblank_handler(int fd, unsigned int frame,
+ unsigned int tv_sec, unsigned int tv_usec,
+ void *event_data);
Bool nouveau_dri2_init(ScreenPtr pScreen);
void nouveau_dri2_fini(ScreenPtr pScreen);
diff --git a/src/nv_type.h b/src/nv_type.h
index 9c22ea9..8290b09 100644
--- a/src/nv_type.h
+++ b/src/nv_type.h
@@ -50,6 +50,7 @@ typedef struct _NVRec {
Bool exa_force_cp;
Bool wfb_enabled;
Bool tiled_scanout;
+ Bool glx_vblank;
ScreenBlockHandlerProcPtr BlockHandler;
CreateScreenResourcesProcPtr CreateScreenResources;
CloseScreenProcPtr CloseScreen;