diff options
author | Francisco Jerez <currojerez@riseup.net> | 2010-10-21 22:55:02 +0200 |
---|---|---|
committer | Francisco Jerez <currojerez@riseup.net> | 2010-10-22 15:33:03 +0200 |
commit | 70f0d2c886ceaa965ca4864788f4dd8e8f757a92 (patch) | |
tree | 804fa8deb203fdb2c469c4c2a794f017492933a5 | |
parent | 72dd4899ff036b09e3680ed2e09b3dbae3a7ba71 (diff) | |
download | xorg-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.man | 5 | ||||
-rw-r--r-- | src/drmmode_display.c | 4 | ||||
-rw-r--r-- | src/nouveau_dri2.c | 183 | ||||
-rw-r--r-- | src/nv_const.h | 4 | ||||
-rw-r--r-- | src/nv_driver.c | 10 | ||||
-rw-r--r-- | src/nv_proto.h | 3 | ||||
-rw-r--r-- | src/nv_type.h | 1 |
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(®ion, &box, 0); + nouveau_dri2_copy_region(draw, ®ion, 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, + ¤t_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; |