From c57da33308a81fa575179238a0415abcb8b34908 Mon Sep 17 00:00:00 2001 From: Michel Dänzer Date: Tue, 9 Jun 2015 12:39:21 +0900 Subject: Add Option "TearFree" Avoids tearing by flipping between two scanout BOs per (non-rotated) CRTC (Cherry picked from radeon commit 43159ef400c3b18b9f4d3e6fa1c4aef2d60d38fe) Reviewed-by: Alex Deucher --- src/amdgpu_drv.h | 2 + src/amdgpu_kms.c | 138 ++++++++++++++++++++++++++++++++++++++------------ src/drmmode_display.c | 79 ++++++++++++++++------------- src/drmmode_display.h | 5 +- 4 files changed, 154 insertions(+), 70 deletions(-) diff --git a/src/amdgpu_drv.h b/src/amdgpu_drv.h index 55be626..e7bdf7f 100644 --- a/src/amdgpu_drv.h +++ b/src/amdgpu_drv.h @@ -143,6 +143,7 @@ typedef enum { OPTION_ACCEL_METHOD, OPTION_DRI3, OPTION_SHADOW_PRIMARY, + OPTION_TEAR_FREE, } AMDGPUOpts; #define AMDGPU_VSYNC_TIMEOUT 20000 /* Maximum wait for VSYNC (in usecs) */ @@ -205,6 +206,7 @@ typedef struct { uint_fast32_t gpu_synced; Bool use_glamor; Bool shadow_primary; + Bool tear_free; /* general */ OptionInfoPtr Options; diff --git a/src/amdgpu_kms.c b/src/amdgpu_kms.c index 5a672c9..f6ccbd4 100644 --- a/src/amdgpu_kms.c +++ b/src/amdgpu_kms.c @@ -68,6 +68,7 @@ const OptionInfoRec AMDGPUOptions_KMS[] = { {OPTION_ACCEL_METHOD, "AccelMethod", OPTV_STRING, {0}, FALSE}, { OPTION_DRI3, "DRI3", OPTV_BOOLEAN, {0}, FALSE }, {OPTION_SHADOW_PRIMARY, "ShadowPrimary", OPTV_BOOLEAN, {0}, FALSE}, + {OPTION_TEAR_FREE, "TearFree", OPTV_BOOLEAN, {0}, FALSE}, {-1, NULL, OPTV_NONE, {0}, FALSE} }; @@ -222,20 +223,9 @@ amdgpu_scanout_extents_intersect(BoxPtr extents, int x, int y, int w, int h) return (extents->x1 < extents->x2 && extents->y1 < extents->y2); } -static void -amdgpu_scanout_update_abort(ScrnInfoPtr scrn, void *event_data) -{ - xf86CrtcPtr xf86_crtc = event_data; - drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private; - - drmmode_crtc->scanout_update_pending = FALSE; -} - -static void -amdgpu_scanout_update_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec, - void *event_data) +static Bool +amdgpu_scanout_do_update(xf86CrtcPtr xf86_crtc, int scanout_id) { - xf86CrtcPtr xf86_crtc = event_data; drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private; DamagePtr pDamage; RegionPtr pRegion; @@ -244,23 +234,24 @@ amdgpu_scanout_update_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec, GCPtr gc; BoxRec extents; - if (!drmmode_crtc->scanout.pixmap || - drmmode_crtc->dpms_mode != DPMSModeOn) - goto out; + if (drmmode_crtc->dpms_mode != DPMSModeOn || + !drmmode_crtc->scanout[scanout_id].pixmap) + return FALSE; - pDamage = drmmode_crtc->scanout_damage; + pDamage = drmmode_crtc->scanout[scanout_id].damage; if (!pDamage) - goto out; + return FALSE; pRegion = DamageRegion(pDamage); if (!RegionNotEmpty(pRegion)) - goto out; + return FALSE; - pDraw = &drmmode_crtc->scanout.pixmap->drawable; + pDraw = &drmmode_crtc->scanout[scanout_id].pixmap->drawable; extents = *RegionExtents(pRegion); + RegionEmpty(pRegion); if (!amdgpu_scanout_extents_intersect(&extents, xf86_crtc->x, xf86_crtc->y, pDraw->width, pDraw->height)) - goto clear_damage; + return FALSE; pScreen = pDraw->pScreen; gc = GetScratchGC(pDraw->depth, pScreen); @@ -273,15 +264,29 @@ amdgpu_scanout_update_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec, extents.x1, extents.y1); FreeScratchGC(gc); - amdgpu_glamor_flush(scrn); + amdgpu_glamor_flush(xf86_crtc->scrn); -clear_damage: - RegionEmpty(pRegion); + return TRUE; +} + +static void +amdgpu_scanout_update_abort(ScrnInfoPtr scrn, void *event_data) +{ + xf86CrtcPtr xf86_crtc = event_data; + drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private; -out: drmmode_crtc->scanout_update_pending = FALSE; } +static void +amdgpu_scanout_update_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec, + void *event_data) +{ + amdgpu_scanout_do_update(event_data, 0); + + amdgpu_scanout_update_abort(scrn, event_data); +} + static void amdgpu_scanout_update(xf86CrtcPtr xf86_crtc) { @@ -295,11 +300,11 @@ amdgpu_scanout_update(xf86CrtcPtr xf86_crtc) BoxRec extents; if (drmmode_crtc->scanout_update_pending || - !drmmode_crtc->scanout.pixmap || + !drmmode_crtc->scanout[0].pixmap || drmmode_crtc->dpms_mode != DPMSModeOn) return; - pDamage = drmmode_crtc->scanout_damage; + pDamage = drmmode_crtc->scanout[0].damage; if (!pDamage) return; @@ -307,7 +312,7 @@ amdgpu_scanout_update(xf86CrtcPtr xf86_crtc) if (!RegionNotEmpty(pRegion)) return; - pDraw = &drmmode_crtc->scanout.pixmap->drawable; + pDraw = &drmmode_crtc->scanout[0].pixmap->drawable; extents = *RegionExtents(pRegion); if (!amdgpu_scanout_extents_intersect(&extents, xf86_crtc->x, xf86_crtc->y, pDraw->width, pDraw->height)) @@ -339,6 +344,61 @@ amdgpu_scanout_update(xf86CrtcPtr xf86_crtc) drmmode_crtc->scanout_update_pending = TRUE; } + +static void +amdgpu_scanout_flip_abort(ScrnInfoPtr scrn, void *event_data) +{ + drmmode_crtc_private_ptr drmmode_crtc = event_data; + + drmmode_crtc->scanout_update_pending = FALSE; +} + +static void +amdgpu_scanout_flip_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec, void *event_data) +{ + amdgpu_scanout_flip_abort(scrn, event_data); +} + +static void +amdgpu_scanout_flip(ScreenPtr pScreen, AMDGPUInfoPtr info, + xf86CrtcPtr xf86_crtc) +{ + drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private; + ScrnInfoPtr scrn; + struct amdgpu_drm_queue_entry *drm_queue_entry; + unsigned scanout_id; + + if (drmmode_crtc->scanout_update_pending) + return; + + scanout_id = drmmode_crtc->scanout_id ^ 1; + if (!amdgpu_scanout_do_update(xf86_crtc, scanout_id)) + return; + + scrn = xf86_crtc->scrn; + drm_queue_entry = amdgpu_drm_queue_alloc(scrn, AMDGPU_DRM_QUEUE_CLIENT_DEFAULT, + AMDGPU_DRM_QUEUE_ID_DEFAULT, + drmmode_crtc, + amdgpu_scanout_flip_handler, + amdgpu_scanout_flip_abort); + if (!drm_queue_entry) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "Allocating DRM event queue entry failed.\n"); + return; + } + + if (drmModePageFlip(drmmode_crtc->drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, + drmmode_crtc->scanout[scanout_id].fb_id, + DRM_MODE_PAGE_FLIP_EVENT, drm_queue_entry)) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue failed in %s: %s\n", + __func__, strerror(errno)); + return; + } + + drmmode_crtc->scanout_id = scanout_id; + drmmode_crtc->scanout_update_pending = TRUE; +} + static void AMDGPUBlockHandler_KMS(BLOCKHANDLER_ARGS_DECL) { SCREEN_PTR(arg); @@ -349,12 +409,16 @@ static void AMDGPUBlockHandler_KMS(BLOCKHANDLER_ARGS_DECL) (*pScreen->BlockHandler) (BLOCKHANDLER_ARGS); pScreen->BlockHandler = AMDGPUBlockHandler_KMS; - if (info->shadow_primary) { + if (info->tear_free || info->shadow_primary) { xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); int c; - for (c = 0; c < xf86_config->num_crtc; c++) - amdgpu_scanout_update(xf86_config->crtc[c]); + for (c = 0; c < xf86_config->num_crtc; c++) { + if (info->tear_free) + amdgpu_scanout_flip(pScreen, info, xf86_config->crtc[c]); + else + amdgpu_scanout_update(xf86_config->crtc[c]); + } } if (info->use_glamor) @@ -681,6 +745,13 @@ Bool AMDGPUPreInit_KMS(ScrnInfoPtr pScrn, int flags) } if (info->use_glamor) { + info->tear_free = xf86ReturnOptValBool(info->Options, + OPTION_TEAR_FREE, FALSE); + + if (info->tear_free) + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, + "TearFree enabled\n"); + info->shadow_primary = xf86ReturnOptValBool(info->Options, OPTION_SHADOW_PRIMARY, FALSE); @@ -691,11 +762,12 @@ Bool AMDGPUPreInit_KMS(ScrnInfoPtr pScrn, int flags) info->allowPageFlip = xf86ReturnOptValBool(info->Options, OPTION_PAGE_FLIP, TRUE); - if (info->shadow_primary) { + if (info->tear_free || info->shadow_primary) { xf86DrvMsg(pScrn->scrnIndex, info->allowPageFlip ? X_WARNING : X_DEFAULT, "KMS Pageflipping: disabled%s\n", - info->allowPageFlip ? " because of ShadowPrimary" : ""); + info->allowPageFlip ? + " because of ShadowPrimary/TearFree" : ""); info->allowPageFlip = FALSE; } else { xf86DrvMsg(pScrn->scrnIndex, X_INFO, diff --git a/src/drmmode_display.c b/src/drmmode_display.c index 79fbecf..19f0f19 100644 --- a/src/drmmode_display.c +++ b/src/drmmode_display.c @@ -325,6 +325,10 @@ drmmode_crtc_scanout_destroy(drmmode_ptr drmmode, scanout->bo = NULL; } + if (scanout->damage) { + DamageDestroy(scanout->damage); + scanout->damage = NULL; + } } void @@ -338,12 +342,9 @@ drmmode_scanout_free(ScrnInfoPtr scrn) xf86_config->crtc[c]->driver_private; drmmode_crtc_scanout_destroy(drmmode_crtc->drmmode, - &drmmode_crtc->scanout); - - if (drmmode_crtc->scanout_damage) { - DamageDestroy(drmmode_crtc->scanout_damage); - drmmode_crtc->scanout_damage = NULL; - } + &drmmode_crtc->scanout[0]); + drmmode_crtc_scanout_destroy(drmmode_crtc->drmmode, + &drmmode_crtc->scanout[1]); } } @@ -527,43 +528,51 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, x = drmmode_crtc->prime_pixmap_x; y = 0; - drmmode_crtc_scanout_destroy(drmmode, &drmmode_crtc->scanout); + drmmode_crtc_scanout_destroy(drmmode, &drmmode_crtc->scanout[0]); + drmmode_crtc_scanout_destroy(drmmode, &drmmode_crtc->scanout[1]); } else #endif if (drmmode_crtc->rotate.fb_id) { fb_id = drmmode_crtc->rotate.fb_id; x = y = 0; - drmmode_crtc_scanout_destroy(drmmode, &drmmode_crtc->scanout); - } else if (info->shadow_primary) { - drmmode_crtc_scanout_create(crtc, - &drmmode_crtc->scanout, - NULL, mode->HDisplay, - mode->VDisplay); - - if (drmmode_crtc->scanout.pixmap) { - RegionPtr pRegion; - BoxPtr pBox; - - if (!drmmode_crtc->scanout_damage) { - drmmode_crtc->scanout_damage = - DamageCreate(amdgpu_screen_damage_report, - NULL, DamageReportRawRegion, - TRUE, pScreen, NULL); - DamageRegister(&pScreen->GetScreenPixmap(pScreen)->drawable, - drmmode_crtc->scanout_damage); - } + drmmode_crtc_scanout_destroy(drmmode, &drmmode_crtc->scanout[0]); + drmmode_crtc_scanout_destroy(drmmode, &drmmode_crtc->scanout[1]); + } else if (info->tear_free || info->shadow_primary) { + for (i = 0; i < (info->tear_free ? 2 : 1); i++) { + drmmode_crtc_scanout_create(crtc, + &drmmode_crtc->scanout[i], + NULL, mode->HDisplay, + mode->VDisplay); + + if (drmmode_crtc->scanout[i].pixmap) { + RegionPtr pRegion; + BoxPtr pBox; + + if (!drmmode_crtc->scanout[i].damage) { + drmmode_crtc->scanout[i].damage = + DamageCreate(amdgpu_screen_damage_report, + NULL, DamageReportRawRegion, + TRUE, pScreen, NULL); + DamageRegister(&pScreen->GetScreenPixmap(pScreen)->drawable, + drmmode_crtc->scanout[i].damage); + } - pRegion = DamageRegion(drmmode_crtc->scanout_damage); - RegionUninit(pRegion); - pRegion->data = NULL; - pBox = RegionExtents(pRegion); - pBox->x1 = min(pBox->x1, x); - pBox->y1 = min(pBox->y1, y); - pBox->x2 = max(pBox->x2, x + mode->HDisplay); - pBox->y2 = max(pBox->y2, y + mode->VDisplay); + pRegion = DamageRegion(drmmode_crtc->scanout[i].damage); + RegionUninit(pRegion); + pRegion->data = NULL; + pBox = RegionExtents(pRegion); + pBox->x1 = min(pBox->x1, x); + pBox->y1 = min(pBox->y1, y); + pBox->x2 = max(pBox->x2, x + mode->HDisplay); + pBox->y2 = max(pBox->y2, y + mode->VDisplay); + } + } - fb_id = drmmode_crtc->scanout.fb_id; + if (drmmode_crtc->scanout[0].pixmap && + (!info->tear_free || drmmode_crtc->scanout[1].pixmap)) { + drmmode_crtc->scanout_id = 0; + fb_id = drmmode_crtc->scanout[0].fb_id; x = y = 0; } } diff --git a/src/drmmode_display.h b/src/drmmode_display.h index 0ba358e..8262e05 100644 --- a/src/drmmode_display.h +++ b/src/drmmode_display.h @@ -73,6 +73,7 @@ typedef struct { struct drmmode_scanout { struct amdgpu_buffer *bo; PixmapPtr pixmap; + DamagePtr damage; unsigned fb_id; int width, height; }; @@ -83,8 +84,8 @@ typedef struct { int hw_id; struct amdgpu_buffer *cursor_buffer; struct drmmode_scanout rotate; - struct drmmode_scanout scanout; - DamagePtr scanout_damage; + struct drmmode_scanout scanout[2]; + unsigned scanout_id; Bool scanout_update_pending; int dpms_mode; CARD64 dpms_last_ust; -- cgit v1.2.3