From: Michel Dänzer <[email protected]>

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)

Signed-off-by: Michel Dänzer <[email protected]>
---
 src/amdgpu_kms.c      | 112 ++++++++++++++++++++++++++++++++++++++++++++++----
 src/drmmode_display.c |  33 ++++++++++++---
 src/drmmode_display.h |   1 +
 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;
-- 
2.9.3

_______________________________________________
amd-gfx mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

Reply via email to