From 38797a33117222dadbc89e5f21ed8cd5deef9bea Mon Sep 17 00:00:00 2001 From: Michel Dänzer Date: Fri, 2 Sep 2016 11:08:28 +0900 Subject: Make TearFree effective with PRIME slave scanout TearFree can now prevent tearing with any possible display configuration. Note that there may still be inter-GPU tearing if the primary GPU uses a different driver. v2: * Also test dirty->slave_dst in radeon_prime_scanout_do_update Reviewed-by: Alex Deucher [v1] --- src/drmmode_display.c | 33 ++++++++++++--- src/drmmode_display.h | 1 + src/radeon_kms.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 132 insertions(+), 13 deletions(-) diff --git a/src/drmmode_display.c b/src/drmmode_display.c index 2b80c21d..34f77351 100644 --- a/src/drmmode_display.c +++ b/src/drmmode_display.c @@ -528,10 +528,19 @@ drmmode_crtc_scanout_destroy(drmmode_ptr drmmode, static void drmmode_crtc_scanout_free(drmmode_crtc_private_ptr drmmode_crtc) { - drmmode_crtc_scanout_destroy(drmmode_crtc->drmmode, - &drmmode_crtc->scanout[0]); - drmmode_crtc_scanout_destroy(drmmode_crtc->drmmode, - &drmmode_crtc->scanout[1]); + if (drmmode_crtc->flip_pending) { + drmmode_crtc->scanout_destroy[0] = drmmode_crtc->scanout[0]; + drmmode_crtc->scanout[0].pixmap = NULL; + drmmode_crtc->scanout[0].bo = NULL; + drmmode_crtc->scanout_destroy[1] = drmmode_crtc->scanout[1]; + drmmode_crtc->scanout[1].pixmap = NULL; + drmmode_crtc->scanout[1].bo = NULL; + } else { + drmmode_crtc_scanout_destroy(drmmode_crtc->drmmode, + &drmmode_crtc->scanout[0]); + drmmode_crtc_scanout_destroy(drmmode_crtc->drmmode, + &drmmode_crtc->scanout[1]); + } if (drmmode_crtc->scanout_damage) { DamageDestroy(drmmode_crtc->scanout_damage); @@ -1120,11 +1129,12 @@ static Bool drmmode_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix) { drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + RADEONInfoPtr info = RADEONPTR(crtc->scrn); if (!ppix) { if (crtc->randr_crtc->scanout_pixmap) PixmapStopDirtyTracking(crtc->randr_crtc->scanout_pixmap, - drmmode_crtc->scanout[0].pixmap); + drmmode_crtc->scanout[drmmode_crtc->scanout_id].pixmap); drmmode_crtc_scanout_free(drmmode_crtc); return TRUE; } @@ -1134,6 +1144,14 @@ drmmode_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix) ppix->drawable.height)) return FALSE; + if (info->tear_free && + !drmmode_crtc_scanout_create(crtc, &drmmode_crtc->scanout[1], + ppix->drawable.width, + ppix->drawable.height)) { + drmmode_crtc_scanout_free(drmmode_crtc); + return FALSE; + } + #ifdef HAS_DIRTYTRACKING_ROTATION PixmapStartDirtyTracking(ppix, drmmode_crtc->scanout[0].pixmap, 0, 0, 0, 0, RR_Rotate_0); @@ -2200,6 +2218,11 @@ drmmode_clear_pending_flip(xf86CrtcPtr crtc) drmmode_crtc_dpms(crtc, drmmode_crtc->pending_dpms_mode); } + + drmmode_crtc_scanout_destroy(drmmode_crtc->drmmode, + &drmmode_crtc->scanout_destroy[0]); + drmmode_crtc_scanout_destroy(drmmode_crtc->drmmode, + &drmmode_crtc->scanout_destroy[1]); } static void diff --git a/src/drmmode_display.h b/src/drmmode_display.h index 5df9773d..7602eb82 100644 --- a/src/drmmode_display.h +++ b/src/drmmode_display.h @@ -84,6 +84,7 @@ typedef struct { struct radeon_bo *cursor_bo; struct drmmode_scanout rotate; struct drmmode_scanout scanout[2]; + struct drmmode_scanout scanout_destroy[2]; DamagePtr scanout_damage; RegionRec scanout_last_region; unsigned scanout_id; diff --git a/src/radeon_kms.c b/src/radeon_kms.c index bcaa024c..9bcf657d 100644 --- a/src/radeon_kms.c +++ b/src/radeon_kms.c @@ -597,31 +597,56 @@ slave_has_sync_shared_pixmap(ScrnInfoPtr scrn, PixmapDirtyUpdatePtr dirty) return slave_scrn->driverName == scrn->driverName; } -void -radeon_prime_scanout_update_handler(xf86CrtcPtr crtc, uint32_t frame, uint64_t usec, - void *event_data) +static Bool +radeon_prime_scanout_do_update(xf86CrtcPtr crtc, unsigned scanout_id) { ScrnInfoPtr scrn = crtc->scrn; ScreenPtr screen = scrn->pScreen; + RADEONInfoPtr info = RADEONPTR(scrn); drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; PixmapPtr scanoutpix = crtc->randr_crtc->scanout_pixmap; PixmapDirtyUpdatePtr dirty; + Bool ret = FALSE; xorg_list_for_each_entry(dirty, &screen->pixmap_dirty_list, ent) { - if (dirty->src == scanoutpix && - dirty->slave_dst == drmmode_crtc->scanout[0].pixmap) { + if (dirty->src == scanoutpix && dirty->slave_dst == + drmmode_crtc->scanout[scanout_id ^ info->tear_free].pixmap) { RegionPtr region; if (master_has_sync_shared_pixmap(scrn, dirty)) radeon_sync_shared_pixmap(dirty); region = dirty_region(dirty); + if (RegionNil(region)) + goto destroy; + + if (info->tear_free) { + RegionTranslate(region, crtc->x, crtc->y); + radeon_sync_scanout_pixmaps(crtc, region, scanout_id); + radeon_cs_flush_indirect(scrn); + RegionCopy(&drmmode_crtc->scanout_last_region, region); + RegionTranslate(region, -crtc->x, -crtc->y); + dirty->slave_dst = drmmode_crtc->scanout[scanout_id].pixmap; + } + redisplay_dirty(dirty, region); + ret = TRUE; + destroy: RegionDestroy(region); break; } } + return ret; +} + +void +radeon_prime_scanout_update_handler(xf86CrtcPtr crtc, uint32_t frame, uint64_t usec, + void *event_data) +{ + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + + radeon_prime_scanout_do_update(crtc, 0); drmmode_crtc->scanout_update_pending = FALSE; } @@ -678,9 +703,75 @@ radeon_prime_scanout_update(PixmapDirtyUpdatePtr dirty) drmmode_crtc->scanout_update_pending = TRUE; } +static void +radeon_prime_scanout_flip_abort(xf86CrtcPtr crtc, void *event_data) +{ + drmmode_crtc_private_ptr drmmode_crtc = event_data; + + drmmode_crtc->scanout_update_pending = FALSE; + drmmode_clear_pending_flip(crtc); +} + +static void +radeon_prime_scanout_flip(PixmapDirtyUpdatePtr ent) +{ + ScreenPtr screen = ent->slave_dst->drawable.pScreen; + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn); + xf86CrtcPtr crtc = NULL; + drmmode_crtc_private_ptr drmmode_crtc = NULL; + uintptr_t drm_queue_seq; + unsigned scanout_id; + int c; + + /* Find the CRTC which is scanning out from this slave pixmap */ + for (c = 0; c < xf86_config->num_crtc; c++) { + crtc = xf86_config->crtc[c]; + drmmode_crtc = crtc->driver_private; + scanout_id = drmmode_crtc->scanout_id; + if (drmmode_crtc->scanout[scanout_id].pixmap == ent->slave_dst) + break; + } + + if (c == xf86_config->num_crtc || + !crtc->enabled || + drmmode_crtc->scanout_update_pending || + !drmmode_crtc->scanout[drmmode_crtc->scanout_id].pixmap || + drmmode_crtc->pending_dpms_mode != DPMSModeOn) + return; + + scanout_id = drmmode_crtc->scanout_id ^ 1; + if (!radeon_prime_scanout_do_update(crtc, scanout_id)) + return; + + drm_queue_seq = radeon_drm_queue_alloc(crtc, + RADEON_DRM_QUEUE_CLIENT_DEFAULT, + RADEON_DRM_QUEUE_ID_DEFAULT, + drmmode_crtc, NULL, + radeon_prime_scanout_flip_abort); + if (drm_queue_seq == RADEON_DRM_QUEUE_ERROR) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "Allocating DRM event queue entry failed for PRIME flip.\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, (void*)drm_queue_seq)) { + 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; + drmmode_crtc->flip_pending = TRUE; +} + static void radeon_dirty_update(ScrnInfoPtr scrn) { + RADEONInfoPtr info = RADEONPTR(scrn); ScreenPtr screen = scrn->pScreen; PixmapDirtyUpdatePtr ent; RegionPtr region; @@ -700,10 +791,14 @@ radeon_dirty_update(ScrnInfoPtr scrn) region = dirty_region(region_ent); - if (RegionNotEmpty(region)) - radeon_prime_scanout_update(ent); - else + if (RegionNotEmpty(region)) { + if (info->tear_free) + radeon_prime_scanout_flip(ent); + else + radeon_prime_scanout_update(ent); + } else { DamageEmpty(region_ent->damage); + } RegionDestroy(region); } else { -- cgit v1.2.3