summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichel Dänzer <michel.daenzer@amd.com>2016-09-08 18:14:49 +0900
committerMichel Dänzer <michel@daenzer.net>2016-09-09 19:03:31 +0900
commit0f8df8584ad61a3e47fe303b34cd7b0c4ed08bb0 (patch)
treee51356876de3e100878bcbf8a26dc06b8b7c2fff
parentd6feed2cd78fe879aba4860a6d9bc2e388b9f135 (diff)
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. (Ported from radeon commit 38797a33117222dadbc89e5f21ed8cd5deef9bea) Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
-rw-r--r--src/amdgpu_kms.c112
-rw-r--r--src/drmmode_display.c33
-rw-r--r--src/drmmode_display.h1
3 files changed, 133 insertions, 13 deletions
diff --git a/src/amdgpu_kms.c b/src/amdgpu_kms.c
index 88b0be0..a159c84 100644
--- a/src/amdgpu_kms.c
+++ b/src/amdgpu_kms.c
@@ -507,31 +507,56 @@ slave_has_sync_shared_pixmap(ScrnInfoPtr scrn, PixmapDirtyUpdatePtr dirty)
return slave_scrn->driverName == scrn->driverName;
}
-void
-amdgpu_prime_scanout_update_handler(xf86CrtcPtr crtc, uint32_t frame, uint64_t usec,
- void *event_data)
+static Bool
+amdgpu_prime_scanout_do_update(xf86CrtcPtr crtc, unsigned scanout_id)
{
ScrnInfoPtr scrn = crtc->scrn;
ScreenPtr screen = scrn->pScreen;
+ AMDGPUInfoPtr info = AMDGPUPTR(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))
amdgpu_sync_shared_pixmap(dirty);
region = dirty_region(dirty);
+ if (RegionNil(region))
+ goto destroy;
+
+ if (info->tear_free) {
+ RegionTranslate(region, crtc->x, crtc->y);
+ amdgpu_sync_scanout_pixmaps(crtc, region, scanout_id);
+ amdgpu_glamor_flush(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
+amdgpu_prime_scanout_update_handler(xf86CrtcPtr crtc, uint32_t frame, uint64_t usec,
+ void *event_data)
+{
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+
+ amdgpu_prime_scanout_do_update(crtc, 0);
drmmode_crtc->scanout_update_pending = FALSE;
}
@@ -590,8 +615,75 @@ amdgpu_prime_scanout_update(PixmapDirtyUpdatePtr dirty)
}
static void
+amdgpu_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
+amdgpu_prime_scanout_flip(PixmapDirtyUpdatePtr ent)
+{
+ ScreenPtr screen = ent->slave_dst->drawable.pScreen;
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
+ 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 (!amdgpu_prime_scanout_do_update(crtc, scanout_id))
+ return;
+
+ drm_queue_seq = amdgpu_drm_queue_alloc(crtc,
+ AMDGPU_DRM_QUEUE_CLIENT_DEFAULT,
+ AMDGPU_DRM_QUEUE_ID_DEFAULT,
+ drmmode_crtc, NULL,
+ amdgpu_prime_scanout_flip_abort);
+ if (drm_queue_seq == AMDGPU_DRM_QUEUE_ERROR) {
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "Allocating DRM event queue entry failed for PRIME flip.\n");
+ return;
+ }
+
+ if (drmModePageFlip(pAMDGPUEnt->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
amdgpu_dirty_update(ScrnInfoPtr scrn)
{
+ AMDGPUInfoPtr info = AMDGPUPTR(scrn);
ScreenPtr screen = scrn->pScreen;
PixmapDirtyUpdatePtr ent;
RegionPtr region;
@@ -611,10 +703,14 @@ amdgpu_dirty_update(ScrnInfoPtr scrn)
region = dirty_region(region_ent);
- if (RegionNotEmpty(region))
- amdgpu_prime_scanout_update(ent);
- else
+ if (RegionNotEmpty(region)) {
+ if (info->tear_free)
+ amdgpu_prime_scanout_flip(ent);
+ else
+ amdgpu_prime_scanout_update(ent);
+ } else {
DamageEmpty(region_ent->damage);
+ }
RegionDestroy(region);
} else {
diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index 1e4622d..31aa1db 100644
--- a/src/drmmode_display.c
+++ b/src/drmmode_display.c
@@ -467,10 +467,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);
@@ -1062,11 +1071,12 @@ drmmode_crtc_gamma_set(xf86CrtcPtr crtc, uint16_t * red, uint16_t * green,
static Bool drmmode_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix)
{
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ AMDGPUInfoPtr info = AMDGPUPTR(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;
}
@@ -1076,6 +1086,14 @@ static Bool 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);
@@ -2010,6 +2028,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 6c5542c..4973bc2 100644
--- a/src/drmmode_display.h
+++ b/src/drmmode_display.h
@@ -84,6 +84,7 @@ typedef struct {
struct amdgpu_buffer *cursor_buffer;
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;