[PATCH] Add Option "TearFree"

Michel Dänzer michel at daenzer.net
Mon Apr 20 02:54:58 PDT 2015


From: Michel Dänzer <michel.daenzer at amd.com>

Avoids tearing by flipping between two scanout BOs per (non-rotated) CRTC

Signed-off-by: Michel Dänzer <michel.daenzer at amd.com>
---
 man/radeon.man        |   7 +++
 src/drmmode_display.c |  78 ++++++++++++++++-------------
 src/drmmode_display.h |   5 +-
 src/radeon.h          |   2 +
 src/radeon_kms.c      | 136 ++++++++++++++++++++++++++++++++++++++------------
 5 files changed, 158 insertions(+), 70 deletions(-)

diff --git a/man/radeon.man b/man/radeon.man
index 2703773..f0a6be1 100644
--- a/man/radeon.man
+++ b/man/radeon.man
@@ -276,6 +276,13 @@ Enable DRI2 page flipping.  The default is
 .B on.
 Pageflipping is supported on all radeon hardware.
 .TP
+.BI "Option \*qTearFree\*q \*q" boolean \*q
+Enable tearing prevention using the hardware page flipping mechanism. This
+option currently doesn't have any effect for rotated CRTCs. It requires
+allocating two separate scanout buffers for each non-rotated CRTC. Enabling
+this option currently disables Option \*qEnablePageFlip\*q. The default is
+.B off.
+.TP
 .BI "Option \*qAccelMethod\*q \*q" "string" \*q
 Chooses between available acceleration architectures.  Valid values are
 .B EXA
diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index 1f22869..ce6cd80 100644
--- a/src/drmmode_display.c
+++ b/src/drmmode_display.c
@@ -488,6 +488,10 @@ drmmode_crtc_scanout_destroy(drmmode_ptr drmmode,
 		scanout->bo = NULL;
 	}
 
+	if (scanout->damage) {
+		DamageDestroy(scanout->damage);
+		scanout->damage = NULL;
+	}
 }
 
 void
@@ -501,12 +505,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]);
 	}
 }
 
@@ -704,44 +705,49 @@ 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(radeon_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(radeon_screen_damage_report,
+								     NULL, DamageReportRawRegion,
+								     TRUE, pScreen, NULL);
+						DamageRegister(&pScreen->GetScreenPixmap(pScreen)->drawable,
+							       drmmode_crtc->scanout[i].damage);
+					}
+
+					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);
+
+					x = y = 0;
 				}
 
-				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);
-
-				fb_id = drmmode_crtc->scanout.fb_id;
-				x = y = 0;
+				fb_id = drmmode_crtc->scanout[drmmode_crtc->scanout_id].fb_id;
 			}
 		}
 		ret = drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
diff --git a/src/drmmode_display.h b/src/drmmode_display.h
index 43a3a4a..1908b46 100644
--- a/src/drmmode_display.h
+++ b/src/drmmode_display.h
@@ -75,6 +75,7 @@ typedef struct {
 struct drmmode_scanout {
     struct radeon_bo *bo;
     PixmapPtr pixmap;
+    DamagePtr damage;
     unsigned fb_id;
     int width, height;
 };
@@ -85,8 +86,8 @@ typedef struct {
     int hw_id;
     struct radeon_bo *cursor_bo;
     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;
diff --git a/src/radeon.h b/src/radeon.h
index dbc1660..1c794ce 100644
--- a/src/radeon.h
+++ b/src/radeon.h
@@ -152,6 +152,7 @@ typedef enum {
     OPTION_DELETE_DP12,
     OPTION_DRI3,
     OPTION_SHADOW_PRIMARY,
+    OPTION_TEAR_FREE,
 } RADEONOpts;
 
 
@@ -477,6 +478,7 @@ typedef struct {
     Bool              accelOn;
     Bool              use_glamor;
     Bool              shadow_primary;
+    Bool              tear_free;
     Bool	      exa_pixmaps;
     Bool              exa_force_create;
     XF86ModReqInfo    exaReq;
diff --git a/src/radeon_kms.c b/src/radeon_kms.c
index 64593ab..e18f85a 100644
--- a/src/radeon_kms.c
+++ b/src/radeon_kms.c
@@ -78,6 +78,7 @@ const OptionInfoRec RADEONOptions_KMS[] = {
     { OPTION_SWAPBUFFERS_WAIT,"SwapbuffersWait", OPTV_BOOLEAN, {0}, FALSE },
     { OPTION_DELETE_DP12,    "DeleteUnusedDP12Displays", OPTV_BOOLEAN, {0}, FALSE},
     { OPTION_DRI3,           "DRI3",             OPTV_BOOLEAN, {0}, FALSE },
+    { OPTION_TEAR_FREE,      "TearFree",         OPTV_BOOLEAN, {0}, FALSE },
     { -1,                    NULL,               OPTV_NONE,    {0}, FALSE }
 };
 
@@ -320,21 +321,11 @@ radeon_scanout_extents_intersect(BoxPtr extents, int x, int y, int w, int h)
     return (extents->x1 < extents->x2 && extents->y1 < extents->y2);
 }
 
-static void
-radeon_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
-radeon_scanout_update_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec,
-			      void *event_data)
+static Bool
+radeon_scanout_do_update(xf86CrtcPtr xf86_crtc, int scanout_id)
 {
-    xf86CrtcPtr xf86_crtc = event_data;
     drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
+    ScrnInfoPtr scrn;
     DamagePtr pDamage;
     RegionPtr pRegion;
     DrawablePtr pDraw;
@@ -344,26 +335,28 @@ radeon_scanout_update_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec,
     RADEONInfoPtr info;
     Bool force;
 
-    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 (!radeon_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);
+    scrn = xf86_crtc->scrn;
     info = RADEONPTR(scrn);
     force = info->accel_state->force;
     info->accel_state->force = TRUE;
@@ -380,14 +373,28 @@ radeon_scanout_update_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec,
 
     radeon_cs_flush_indirect(scrn);
 
-clear_damage:
-    RegionEmpty(pRegion);
+    return TRUE;
+}
+
+static void
+radeon_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
+radeon_scanout_update_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec,
+			      void *event_data)
+{
+    radeon_scanout_do_update(event_data, 0);
+
+    radeon_scanout_update_abort(scrn, event_data);
+}
+
+static void
 radeon_scanout_update(xf86CrtcPtr xf86_crtc)
 {
     drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
@@ -400,11 +407,11 @@ radeon_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;
 
@@ -412,7 +419,7 @@ radeon_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 (!radeon_scanout_extents_intersect(&extents, xf86_crtc->x, xf86_crtc->y,
 					  pDraw->width, pDraw->height))
@@ -445,6 +452,60 @@ radeon_scanout_update(xf86CrtcPtr xf86_crtc)
     drmmode_crtc->scanout_update_pending = TRUE;
 }
 
+static void
+radeon_scanout_flip_abort(ScrnInfoPtr scrn, void *event_data)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = event_data;
+
+    drmmode_crtc->scanout_update_pending = FALSE;
+}
+
+static void
+radeon_scanout_flip_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec, void *event_data)
+{
+    radeon_scanout_flip_abort(scrn, event_data);
+}
+
+static void
+radeon_scanout_flip(ScreenPtr pScreen, RADEONInfoPtr info,
+		    xf86CrtcPtr xf86_crtc)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
+    ScrnInfoPtr scrn;
+    struct radeon_drm_queue_entry *drm_queue_entry;
+    unsigned scanout_id;
+
+    if (drmmode_crtc->scanout_update_pending)
+	return;
+
+    scanout_id = drmmode_crtc->scanout_id ^ 1;
+    if (!radeon_scanout_do_update(xf86_crtc, scanout_id))
+	return;
+
+    scrn = xf86_crtc->scrn;
+    drm_queue_entry = radeon_drm_queue_alloc(scrn, RADEON_DRM_QUEUE_CLIENT_DEFAULT,
+					     RADEON_DRM_QUEUE_ID_DEFAULT,
+					     drmmode_crtc,
+					     radeon_scanout_flip_handler,
+					     radeon_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: %s\n",
+		   strerror(errno));
+	return;
+    }
+
+    drmmode_crtc->scanout_id = scanout_id;
+    drmmode_crtc->scanout_update_pending = TRUE;
+}
+
 static void RADEONBlockHandler_KMS(BLOCKHANDLER_ARGS_DECL)
 {
     SCREEN_PTR(arg);
@@ -455,12 +516,16 @@ static void RADEONBlockHandler_KMS(BLOCKHANDLER_ARGS_DECL)
     (*pScreen->BlockHandler) (BLOCKHANDLER_ARGS);
     pScreen->BlockHandler = RADEONBlockHandler_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++)
-	    radeon_scanout_update(xf86_config->crtc[c]);
+	for (c = 0; c < xf86_config->num_crtc; c++) {
+	    if (info->tear_free)
+		radeon_scanout_flip(pScreen, info, xf86_config->crtc[c]);
+	    else
+		radeon_scanout_update(xf86_config->crtc[c]);
+	}
     }
 
     radeon_cs_flush_indirect(pScrn);
@@ -1092,15 +1157,22 @@ Bool RADEONPreInit_KMS(ScrnInfoPtr pScrn, int flags)
     }
 #endif
 
+    info->tear_free = xf86ReturnOptValBool(info->Options, OPTION_TEAR_FREE,
+					   FALSE);
+
+    if (info->shadow_primary)
+	xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "TearFree enabled\n");
+
     if (info->dri2.pKernelDRMVersion->version_minor >= 8) {
 	info->allowPageFlip = xf86ReturnOptValBool(info->Options,
 						   OPTION_PAGE_FLIP, TRUE);
 #if USE_GLAMOR
-	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
 #endif
-- 
2.1.4



More information about the xorg-driver-ati mailing list