From: Michel Dänzer <michel.daen...@amd.com>

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

Signed-off-by: Michel Dänzer <michel.daen...@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

_______________________________________________
xorg-driver-ati mailing list
xorg-driver-ati@lists.x.org
http://lists.x.org/mailman/listinfo/xorg-driver-ati

Reply via email to