[PATCH xf86-video-ati 09/10] Always allow Present page flipping with TearFree

Michel Dänzer michel at daenzer.net
Wed Aug 16 07:57:24 UTC 2017


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

Even if TearFree is active for the the CRTC we're synchronizing to. In
that case, for Present flips synchronized to vertical blank, the other
scanout buffer is immediately synchronized and flipped to during the
target vertical blank period. For Present flips not synchronized to
vertical blank, we simply use the MSC and timestamp values of the last
vertical blank period for timing purposes, and let the normal TearFree
mechanism handle display updates.

Signed-off-by: Michel Dänzer <michel.daenzer at amd.com>
---
 man/radeon.man        |  6 ++---
 src/drmmode_display.c | 61 ++++++++++++++++++++++++++++++++++++-----
 src/drmmode_display.h | 12 ++++++++-
 src/radeon_kms.c      | 27 ++++++++++++++++---
 src/radeon_present.c  | 75 +++++++++++++++++++++++++++++++++++++++++++--------
 5 files changed, 154 insertions(+), 27 deletions(-)

diff --git a/man/radeon.man b/man/radeon.man
index 1e9a7bebf..9fd863f30 100644
--- a/man/radeon.man
+++ b/man/radeon.man
@@ -285,10 +285,8 @@ Set the default value of the per-output 'TearFree' property, which controls
 tearing prevention using the hardware page flipping mechanism. TearFree is
 on for any CRTC associated with one or more outputs with TearFree on. Two
 separate scanout buffers need to be allocated for each CRTC with TearFree
-on. While TearFree is on for any CRTC, it may prevent clients from using
-DRI page flipping. If this option is set, the default value of the property
-is 'on' or 'off' accordingly. If this option isn't set, the default value of the
-property is
+on. If this option is set, the default value of the property is 'on' or 'off'
+accordingly. If this option isn't set, the default value of the property is
 .B auto,
 which means that TearFree is on for outputs with rotation or other RandR
 transforms, and for RandR 1.4 slave outputs, otherwise off.
diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index fa14e4b43..78d41f197 100644
--- a/src/drmmode_display.c
+++ b/src/drmmode_display.c
@@ -610,6 +610,14 @@ error:
 static void
 radeon_screen_damage_report(DamagePtr damage, RegionPtr region, void *closure)
 {
+	drmmode_crtc_private_ptr drmmode_crtc = closure;
+
+	if (drmmode_crtc->ignore_damage) {
+		RegionEmpty(&damage->damage);
+		drmmode_crtc->ignore_damage = FALSE;
+		return;
+	}
+
 	/* Only keep track of the extents */
 	RegionUninit(&damage->damage);
 	damage->damage.data = NULL;
@@ -2427,7 +2435,8 @@ drmmode_flip_handler(xf86CrtcPtr crtc, uint32_t frame, uint64_t usec, void *even
 
 	drmmode_fb_reference(pRADEONEnt->fd, &drmmode_crtc->fb,
 			     flipdata->fb);
-	if (drmmode_crtc->flip_pending == flipdata->fb) {
+	if (drmmode_crtc->tear_free ||
+	    drmmode_crtc->flip_pending == flipdata->fb) {
 		drmmode_fb_reference(pRADEONEnt->fd,
 				     &drmmode_crtc->flip_pending, NULL);
 	}
@@ -2996,13 +3005,16 @@ Bool radeon_do_pageflip(ScrnInfoPtr scrn, ClientPtr client,
         flipdata->fe_crtc = ref_crtc;
 
 	for (i = 0; i < config->num_crtc; i++) {
+		struct drmmode_fb *fb = flipdata->fb;
+
 		crtc = config->crtc[i];
+		drmmode_crtc = crtc->driver_private;
 
-		if (!drmmode_crtc_can_flip(crtc))
+		if (!drmmode_crtc_can_flip(crtc) ||
+		    (drmmode_crtc->tear_free && crtc != ref_crtc))
 			continue;
 
 		flipdata->flip_count++;
-		drmmode_crtc = crtc->driver_private;
 
 		drm_queue_seq = radeon_drm_queue_alloc(crtc, client, id,
 						       flipdata,
@@ -3014,10 +3026,39 @@ Bool radeon_do_pageflip(ScrnInfoPtr scrn, ClientPtr client,
 			goto error;
 		}
 
+		if (drmmode_crtc->tear_free) {
+			BoxRec extents = { .x1 = 0, .y1 = 0,
+					   .x2 = new_front->drawable.width,
+					   .y2 = new_front->drawable.height };
+			int scanout_id = drmmode_crtc->scanout_id ^ 1;
+
+			if (flip_sync == FLIP_ASYNC) {
+				if (!drmmode_wait_vblank(crtc,
+							 DRM_VBLANK_RELATIVE |
+							 DRM_VBLANK_EVENT,
+							 0, drm_queue_seq,
+							 NULL, NULL))
+					goto flip_error;
+				goto next;
+			}
+
+			fb = radeon_pixmap_get_fb(drmmode_crtc->scanout[scanout_id].pixmap);
+			if (!fb) {
+				ErrorF("Failed to get FB for TearFree flip\n");
+				goto error;
+			}
+
+			radeon_scanout_do_update(crtc, scanout_id,
+						 &new_front->drawable, &extents);
+
+			drmmode_crtc_wait_pending_event(drmmode_crtc, pRADEONEnt->fd,
+							drmmode_crtc->scanout_update_pending);
+		}
+
 		if (crtc == ref_crtc) {
 			if (drmmode_page_flip_target_absolute(pRADEONEnt,
 							      drmmode_crtc,
-							      flipdata->fb->handle,
+							      fb->handle,
 							      flip_flags,
 							      drm_queue_seq,
 							      target_msc) != 0)
@@ -3025,14 +3066,20 @@ Bool radeon_do_pageflip(ScrnInfoPtr scrn, ClientPtr client,
 		} else {
 			if (drmmode_page_flip_target_relative(pRADEONEnt,
 							      drmmode_crtc,
-							      flipdata->fb->handle,
+							      fb->handle,
 							      flip_flags,
 							      drm_queue_seq, 0) != 0)
 				goto flip_error;
 		}
 
-		drmmode_fb_reference(pRADEONEnt->fd, &drmmode_crtc->flip_pending,
-				     flipdata->fb);
+		if (drmmode_crtc->tear_free) {
+			drmmode_crtc->scanout_id ^= 1;
+			drmmode_crtc->ignore_damage = TRUE;
+		}
+
+	next:
+		drmmode_fb_reference(pRADEONEnt->fd,
+				     &drmmode_crtc->flip_pending, fb);
 		drm_queue_seq = 0;
 	}
 
diff --git a/src/drmmode_display.h b/src/drmmode_display.h
index fddf0e6a9..54abad894 100644
--- a/src/drmmode_display.h
+++ b/src/drmmode_display.h
@@ -85,6 +85,7 @@ typedef struct {
     struct drmmode_scanout rotate;
     struct drmmode_scanout scanout[2];
     DamagePtr scanout_damage;
+    Bool ignore_damage;
     RegionRec scanout_last_region;
     unsigned scanout_id;
     Bool scanout_update_pending;
@@ -106,6 +107,14 @@ typedef struct {
     struct drmmode_fb *flip_pending;
     /* The FB currently being scanned out by this CRTC, if any */
     struct drmmode_fb *fb;
+
+#ifdef HAVE_PRESENT_H
+    /* Deferred processing of Present vblank event */
+    uint64_t present_vblank_event_id;
+    uint64_t present_vblank_usec;
+    unsigned present_vblank_msc;
+    Bool present_flip_expected;
+#endif
 } drmmode_crtc_private_rec, *drmmode_crtc_private_ptr;
 
 typedef struct {
@@ -146,7 +155,8 @@ drmmode_crtc_can_flip(xf86CrtcPtr crtc)
     return crtc->enabled &&
 	drmmode_crtc->dpms_mode == DPMSModeOn &&
 	!drmmode_crtc->rotate.bo &&
-	!drmmode_crtc->scanout[drmmode_crtc->scanout_id].bo;
+	(drmmode_crtc->tear_free ||
+	 !drmmode_crtc->scanout[drmmode_crtc->scanout_id].bo);
 }
 
 
diff --git a/src/radeon_kms.c b/src/radeon_kms.c
index 17902334b..7febf1487 100644
--- a/src/radeon_kms.c
+++ b/src/radeon_kms.c
@@ -44,6 +44,10 @@
 
 #include "atipciids.h"
 
+#if HAVE_PRESENT_H
+#include <present.h>
+#endif
+
 /* DPMS */
 #ifdef HAVE_XEXTPROTO_71
 #include <X11/extensions/dpmsconst.h>
@@ -779,6 +783,15 @@ radeon_prime_scanout_flip_handler(xf86CrtcPtr crtc, uint32_t msc, uint64_t usec,
     drmmode_fb_reference(pRADEONEnt->fd, &drmmode_crtc->fb,
 			 drmmode_crtc->flip_pending);
     radeon_prime_scanout_flip_abort(crtc, event_data);
+
+#ifdef HAVE_PRESENT_H
+    if (drmmode_crtc->present_vblank_event_id) {
+	present_event_notify(drmmode_crtc->present_vblank_event_id,
+			     drmmode_crtc->present_vblank_usec,
+			     drmmode_crtc->present_vblank_msc);
+	drmmode_crtc->present_vblank_event_id = 0;
+    }
+#endif
 }
 
 static void
@@ -1001,10 +1014,14 @@ radeon_scanout_update_handler(xf86CrtcPtr crtc, uint32_t frame, uint64_t usec,
     ScreenPtr screen = crtc->scrn->pScreen;
     RegionPtr region = DamageRegion(drmmode_crtc->scanout_damage);
 
-    radeon_scanout_do_update(crtc, drmmode_crtc->scanout_id,
-			     &screen->GetWindowPixmap(screen->root)->drawable,
-			     &region->extents);
-    RegionEmpty(region);
+    if (crtc->enabled &&
+	!drmmode_crtc->flip_pending &&
+	drmmode_crtc->dpms_mode == DPMSModeOn) {
+	if (radeon_scanout_do_update(crtc, drmmode_crtc->scanout_id,
+				     &screen->GetWindowPixmap(screen->root)->drawable,
+				     &region->extents))
+	    RegionEmpty(region);
+    }
 
     radeon_scanout_update_abort(crtc, event_data);
 }
@@ -1021,6 +1038,7 @@ radeon_scanout_update(xf86CrtcPtr xf86_crtc)
 
     if (!xf86_crtc->enabled ||
 	drmmode_crtc->scanout_update_pending ||
+	drmmode_crtc->flip_pending ||
 	drmmode_crtc->dpms_mode != DPMSModeOn)
 	return;
 
@@ -1088,6 +1106,7 @@ radeon_scanout_flip(ScreenPtr pScreen, RADEONInfoPtr info,
     unsigned scanout_id;
 
     if (drmmode_crtc->scanout_update_pending ||
+	drmmode_crtc->flip_pending ||
 	drmmode_crtc->dpms_mode != DPMSModeOn)
 	return;
 
diff --git a/src/radeon_present.c b/src/radeon_present.c
index c7dde0ec7..176853d92 100644
--- a/src/radeon_present.c
+++ b/src/radeon_present.c
@@ -52,6 +52,7 @@ static present_screen_info_rec radeon_present_screen_info;
 
 struct radeon_present_vblank_event {
     uint64_t event_id;
+    Bool vblank_for_flip;
     Bool unflip;
 };
 
@@ -119,9 +120,26 @@ static void
 radeon_present_vblank_handler(xf86CrtcPtr crtc, unsigned int msc,
 			      uint64_t usec, void *data)
 {
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
     struct radeon_present_vblank_event *event = data;
 
-    present_event_notify(event->event_id, usec, msc);
+    if (event->vblank_for_flip &&
+	drmmode_crtc->tear_free &&
+	drmmode_crtc->scanout_update_pending) {
+	if (drmmode_crtc->present_vblank_event_id != 0) {
+	    xf86DrvMsg(crtc->scrn->scrnIndex, X_WARNING,
+		       "Need to handle previously deferred vblank event\n");
+	    present_event_notify(drmmode_crtc->present_vblank_event_id,
+				 drmmode_crtc->present_vblank_usec,
+				 drmmode_crtc->present_vblank_msc);
+	}
+
+	drmmode_crtc->present_vblank_event_id = event->event_id;
+	drmmode_crtc->present_vblank_msc = msc;
+	drmmode_crtc->present_vblank_usec = usec;
+    } else
+	present_event_notify(event->event_id, usec, msc);
+
     free(event);
 }
 
@@ -144,6 +162,7 @@ static int
 radeon_present_queue_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
 {
     xf86CrtcPtr xf86_crtc = crtc->devPrivate;
+    drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
     ScreenPtr screen = crtc->pScreen;
     struct radeon_present_vblank_event *event;
     uintptr_t drm_queue_seq;
@@ -152,6 +171,9 @@ radeon_present_queue_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
     if (!event)
 	return BadAlloc;
     event->event_id = event_id;
+    event->vblank_for_flip = drmmode_crtc->present_flip_expected;
+    drmmode_crtc->present_flip_expected = FALSE;
+
     drm_queue_seq = radeon_drm_queue_alloc(xf86_crtc,
 					   RADEON_DRM_QUEUE_CLIENT_DEFAULT,
 					   event_id, event,
@@ -226,8 +248,17 @@ radeon_present_check_unflip(ScrnInfoPtr scrn)
 	return FALSE;
 
     for (i = 0, num_crtcs_on = 0; i < config->num_crtc; i++) {
-	if (drmmode_crtc_can_flip(config->crtc[i]))
-	    num_crtcs_on++;
+	xf86CrtcPtr crtc = config->crtc[i];
+
+	if (drmmode_crtc_can_flip(crtc)) {
+	    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+
+	    if (drmmode_crtc->flip_pending)
+		return FALSE;
+
+	    if (!drmmode_crtc->tear_free)
+		num_crtcs_on++;
+	}
     }
 
     return num_crtcs_on > 0;
@@ -240,10 +271,20 @@ static Bool
 radeon_present_check_flip(RRCrtcPtr crtc, WindowPtr window, PixmapPtr pixmap,
 	      Bool sync_flip)
 {
+    xf86CrtcPtr xf86_crtc = crtc->devPrivate;
+    drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
     ScreenPtr screen = window->drawable.pScreen;
-    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    ScrnInfoPtr scrn = xf86_crtc->scrn;
+    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
     RADEONInfoPtr info = RADEONPTR(scrn);
     PixmapPtr screen_pixmap;
+    int num_crtcs_on;
+    int i;
+
+    drmmode_crtc->present_flip_expected = FALSE;
+
+    if (!scrn->vtSema)
+	return FALSE;
 
     if (!info->allowPageFlip)
 	return FALSE;
@@ -262,10 +303,18 @@ radeon_present_check_flip(RRCrtcPtr crtc, WindowPtr window, PixmapPtr pixmap,
 	radeon_present_get_pixmap_tiling_flags(info, screen_pixmap))
 	return FALSE;
 
-    if (!drmmode_crtc_can_flip(crtc->devPrivate))
+    for (i = 0, num_crtcs_on = 0; i < config->num_crtc; i++) {
+	if (drmmode_crtc_can_flip(config->crtc[i]))
+	    num_crtcs_on++;
+	else if (config->crtc[i] == crtc->devPrivate)
+	    return FALSE;
+    }
+
+    if (num_crtcs_on == 0)
 	return FALSE;
 
-    return radeon_present_check_unflip(scrn);
+    drmmode_crtc->present_flip_expected = TRUE;
+    return TRUE;
 }
 
 /*
@@ -304,18 +353,20 @@ static Bool
 radeon_present_flip(RRCrtcPtr crtc, uint64_t event_id, uint64_t target_msc,
                    PixmapPtr pixmap, Bool sync_flip)
 {
+    xf86CrtcPtr xf86_crtc = crtc->devPrivate;
+    drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
     ScreenPtr screen = crtc->pScreen;
-    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    ScrnInfoPtr scrn = xf86_crtc->scrn;
     RADEONInfoPtr info = RADEONPTR(scrn);
     struct radeon_present_vblank_event *event;
-    Bool ret;
+    Bool ret = FALSE;
 
     if (!radeon_present_check_flip(crtc, screen->root, pixmap, sync_flip))
-	return FALSE;
+	goto out;
 
     event = calloc(1, sizeof(struct radeon_present_vblank_event));
     if (!event)
-	return FALSE;
+	goto out;
 
     event->event_id = event_id;
 
@@ -332,6 +383,8 @@ radeon_present_flip(RRCrtcPtr crtc, uint64_t event_id, uint64_t target_msc,
     else
 	info->drmmode.present_flipping = TRUE;
 
+ out:
+    drmmode_crtc->present_flip_expected = FALSE;
     return ret;
 }
 
@@ -376,7 +429,7 @@ modeset:
 	xf86CrtcPtr crtc = config->crtc[i];
 	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
 
-	if (!crtc->enabled)
+	if (!crtc->enabled || drmmode_crtc->tear_free)
 	    continue;
 
 	if (drmmode_crtc->dpms_mode == DPMSModeOn)
-- 
2.14.1



More information about the amd-gfx mailing list