Index | Thread | Search

From:
Christian Weisgerber <naddy@mips.inka.de>
Subject:
Re: Patch to stop tearing on Intel modesetting in Xenocara
To:
tech@openbsd.org
Date:
Wed, 5 Mar 2025 16:27:43 +0100

Download raw body.

Thread
  • Christian Weisgerber:

    Patch to stop tearing on Intel modesetting in Xenocara

jcs's backported "TearFree" patch...
https://marc.info/?l=openbsd-tech&m=171591279709952&w=2
... no longer applies to -current xenocara (xserver 21.1.16).

Here's a rebased version.  Builds, doesn't appear to break anything,
and still works, judging from cursory testing.

diff refs/heads/master refs/heads/tearfree
commit - 3cfba1062de7b97bc4c8bc467354a105b6ef4528
commit + 0fef6201bd7ac8fdd17086bc0c6d73554f7ca553
blob - 5a0146bbb667a07bee5b53ff5390e435976d2d4b
blob + 0b01c4ee0640677fbd2f2fff36ad1517135e0a4e
--- xserver/dix/pixmap.c
+++ xserver/dix/pixmap.c
@@ -262,12 +262,11 @@ PixmapStopDirtyTracking(DrawablePtr src, PixmapPtr sec
     return TRUE;
 }
 
-static void
-PixmapDirtyCopyArea(PixmapPtr dst,
-                    PixmapDirtyUpdatePtr dirty,
+void
+PixmapDirtyCopyArea(PixmapPtr dst, DrawablePtr src,
+                    int x, int y, int dst_x, int dst_y,
                     RegionPtr dirty_region)
 {
-    DrawablePtr src = dirty->src;
     ScreenPtr pScreen = src->pScreen;
     int n;
     BoxPtr b;
@@ -294,9 +293,8 @@ PixmapDirtyCopyArea(PixmapPtr dst,
         h = dst_box.y2 - dst_box.y1;
 
         pGC->ops->CopyArea(src, &dst->drawable, pGC,
-                           dirty->x + dst_box.x1, dirty->y + dst_box.y1, w, h,
-                           dirty->dst_x + dst_box.x1,
-                           dirty->dst_y + dst_box.y1);
+                           x + dst_box.x1, y + dst_box.y1, w, h,
+                           dst_x + dst_box.x1, dst_y + dst_box.y1);
         b++;
     }
     FreeScratchGC(pGC);
@@ -408,7 +406,8 @@ Bool PixmapSyncDirtyHelper(PixmapDirtyUpdatePtr dirty)
     RegionTranslate(&pixregion, -dirty->x, -dirty->y);
 
     if (!pScreen->root || dirty->rotation == RR_Rotate_0)
-        PixmapDirtyCopyArea(dst, dirty, &pixregion);
+        PixmapDirtyCopyArea(dst, dirty->src, dirty->x, dirty->y,
+                            dirty->dst_x, dirty->dst_y, &pixregion);
     else
         PixmapDirtyCompositeRotate(dst, dirty, &pixregion);
     pScreen->SourceValidate = SourceValidate;
blob - 1eb09bca35d26b0601337a1e2555690c3c93f726
blob + 33cf0e56a45b0202ab45cfc86538fd43e550fc80
--- xserver/hw/xfree86/common/xf86Module.h
+++ xserver/hw/xfree86/common/xf86Module.h
@@ -74,7 +74,7 @@
  * mask is 0xFFFF0000.
  */
 #define ABI_ANSIC_VERSION	SET_ABI_VERSION(0, 4)
-#define ABI_VIDEODRV_VERSION	SET_ABI_VERSION(25, 2)
+#define ABI_VIDEODRV_VERSION	SET_ABI_VERSION(25, 3)
 #define ABI_XINPUT_VERSION	SET_ABI_VERSION(24, 4)
 #define ABI_EXTENSION_VERSION	SET_ABI_VERSION(10, 0)
 
blob - 8d1b742efb3700a06a7e09703c6633a03029c677
blob + 34ddec424691e42f6ce360b20b816620b9e498d7
--- xserver/hw/xfree86/drivers/modesetting/dri2.c
+++ xserver/hw/xfree86/drivers/modesetting/dri2.c
@@ -483,7 +483,6 @@ ms_dri2_schedule_flip(ms_dri2_frame_event_ptr info)
     modesettingPtr ms = modesettingPTR(scrn);
     ms_dri2_buffer_private_ptr back_priv = info->back->driverPrivate;
     struct ms_dri2_vblank_event *event;
-    drmmode_crtc_private_ptr drmmode_crtc = info->crtc->driver_private;
 
     event = calloc(1, sizeof(struct ms_dri2_vblank_event));
     if (!event)
@@ -495,7 +494,7 @@ ms_dri2_schedule_flip(ms_dri2_frame_event_ptr info)
     event->event_data = info->event_data;
 
     if (ms_do_pageflip(screen, back_priv->pixmap, event,
-                       drmmode_crtc->vblank_pipe, FALSE,
+                       info->crtc, FALSE,
                        ms_dri2_flip_handler,
                        ms_dri2_flip_abort,
                        "DRI2-flip")) {
blob - 54099af9d0622c025d8f2b0efa6e1e0bfcf4c7dd
blob + 9109af6899836a0e6db08cfaafec2dd3427f77cf
--- xserver/hw/xfree86/drivers/modesetting/driver.c
+++ xserver/hw/xfree86/drivers/modesetting/driver.c
@@ -151,6 +151,7 @@ static const OptionInfoRec Options[] = {
     {OPTION_VARIABLE_REFRESH, "VariableRefresh", OPTV_BOOLEAN, {0}, FALSE},
     {OPTION_USE_GAMMA_LUT, "UseGammaLUT", OPTV_BOOLEAN, {0}, FALSE},
     {OPTION_ASYNC_FLIP_SECONDARIES, "AsyncFlipSecondaries", OPTV_BOOLEAN, {0}, FALSE},
+    {OPTION_TEARFREE, "TearFree", OPTV_BOOLEAN, {0}, FALSE},
     {-1, NULL, OPTV_NONE, {0}, FALSE}
 };
 
@@ -554,14 +555,16 @@ rotate_clip(PixmapPtr pixmap, BoxPtr rect, drmModeClip
 }
 
 static int
-dispatch_dirty_region(ScrnInfoPtr scrn, xf86CrtcPtr crtc,
-		      PixmapPtr pixmap, DamagePtr damage, int fb_id)
+dispatch_damages(ScrnInfoPtr scrn, xf86CrtcPtr crtc, RegionPtr dirty,
+                 PixmapPtr pixmap, DamagePtr damage, int fb_id)
 {
     modesettingPtr ms = modesettingPTR(scrn);
-    RegionPtr dirty = DamageRegion(damage);
     unsigned num_cliprects = REGION_NUM_RECTS(dirty);
     int ret = 0;
 
+    if (!ms->dirty_enabled)
+        return 0;
+
     if (num_cliprects) {
         drmModeClip *clip = xallocarray(num_cliprects, sizeof(drmModeClip));
         BoxPtr rect = REGION_RECTS(dirty);
@@ -585,13 +588,105 @@ dispatch_dirty_region(ScrnInfoPtr scrn, xf86CrtcPtr cr
             }
         }
 
+        if (ret == -EINVAL || ret == -ENOSYS) {
+            xf86DrvMsg(scrn->scrnIndex, X_INFO,
+                       "Disabling kernel dirty updates, not required.\n");
+            ms->dirty_enabled = FALSE;
+        }
+
         free(clip);
-        DamageEmpty(damage);
+        if (damage)
+            DamageEmpty(damage);
     }
     return ret;
 }
 
+static int
+dispatch_dirty_region(ScrnInfoPtr scrn, xf86CrtcPtr crtc,
+                      PixmapPtr pixmap, DamagePtr damage, int fb_id)
+{
+    return dispatch_damages(scrn, crtc, DamageRegion(damage),
+                            pixmap, damage, fb_id);
+}
+
 static void
+ms_tearfree_update_damages(ScreenPtr pScreen)
+{
+    ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
+    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
+    modesettingPtr ms = modesettingPTR(scrn);
+    RegionPtr dirty = DamageRegion(ms->damage);
+    int c, i;
+
+    if (RegionNil(dirty))
+        return;
+
+    for (c = 0; c < xf86_config->num_crtc; c++) {
+        xf86CrtcPtr crtc = xf86_config->crtc[c];
+        drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+        drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
+        RegionRec region;
+
+        /* Compute how much of the damage intersects with this CRTC */
+        RegionInit(&region, &crtc->bounds, 0);
+        RegionIntersect(&region, &region, dirty);
+
+        if (trf->buf[0].px) {
+            for (i = 0; i < ARRAY_SIZE(trf->buf); i++)
+                RegionUnion(&trf->buf[i].dmg, &trf->buf[i].dmg, &region);
+        } else {
+            /* Just notify the kernel of the damages if TearFree isn't used */
+            dispatch_damages(scrn, crtc, &region,
+                             pScreen->GetScreenPixmap(pScreen),
+                             NULL, ms->drmmode.fb_id);
+        }
+    }
+    DamageEmpty(ms->damage);
+}
+
+static void
+ms_tearfree_do_flips(ScreenPtr pScreen)
+{
+#ifdef GLAMOR_HAS_GBM
+    ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
+    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
+    modesettingPtr ms = modesettingPTR(scrn);
+    int c;
+
+    if (!ms->drmmode.tearfree_enable)
+        return;
+
+    for (c = 0; c < xf86_config->num_crtc; c++) {
+        xf86CrtcPtr crtc = xf86_config->crtc[c];
+        drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+        drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
+
+        if (!ms_tearfree_is_active_on_crtc(crtc)) {
+            /* Notify any lingering DRI clients waiting for a flip to finish */
+            ms_tearfree_dri_abort_all(crtc);
+            continue;
+        }
+
+        /* Skip if the last flip is still pending, a DRI client is flipping, or
+         * there isn't any damage on the front buffer.
+         */
+        if (trf->flip_seq || ms->drmmode.dri2_flipping ||
+            ms->drmmode.present_flipping ||
+            RegionNil(&trf->buf[trf->back_idx ^ 1].dmg))
+            continue;
+
+        /* Flip. If it fails, notify the kernel of the front buffer damages */
+        if (ms_do_tearfree_flip(pScreen, crtc)) {
+            dispatch_damages(scrn, crtc, &trf->buf[trf->back_idx ^ 1].dmg,
+                             trf->buf[trf->back_idx ^ 1].px, NULL,
+                             trf->buf[trf->back_idx ^ 1].fb_id);
+            RegionEmpty(&trf->buf[trf->back_idx ^ 1].dmg);
+        }
+    }
+#endif
+}
+
+static void
 dispatch_dirty(ScreenPtr pScreen)
 {
     ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
@@ -748,10 +843,13 @@ msBlockHandler(ScreenPtr pScreen, void *timeout)
     pScreen->BlockHandler = msBlockHandler;
     if (pScreen->isGPU && !ms->drmmode.reverse_prime_offload_mode)
         dispatch_secondary_dirty(pScreen);
+    else if (ms->drmmode.tearfree_enable)
+        ms_tearfree_update_damages(pScreen);
     else if (ms->dirty_enabled)
         dispatch_dirty(pScreen);
 
     ms_dirty_update(pScreen, timeout);
+    ms_tearfree_do_flips(pScreen);
 }
 
 static void
@@ -1283,10 +1381,35 @@ PreInit(ScrnInfoPtr pScrn, int flags)
     if (xf86ReturnOptValBool(ms->drmmode.Options, OPTION_ATOMIC, FALSE)) {
         ret = drmSetClientCap(ms->fd, DRM_CLIENT_CAP_ATOMIC, 1);
         ms->atomic_modeset = (ret == 0);
+        if (!ms->atomic_modeset)
+            xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Atomic modesetting not supported\n");
     } else {
         ms->atomic_modeset = FALSE;
     }
+    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+               "Atomic modesetting %sabled\n", ms->atomic_modeset ? "en" : "dis");
 
+    /* TearFree requires glamor and, if PageFlip is enabled, universal planes */
+    if (xf86ReturnOptValBool(ms->drmmode.Options, OPTION_TEARFREE, TRUE)) {
+        if (pScrn->is_gpu) {
+            xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+                       "TearFree cannot synchronize PRIME; use 'PRIME Synchronization' instead\n");
+        } else if (ms->drmmode.glamor) {
+            /* Atomic modesetting implicitly enables universal planes */
+            if (!ms->drmmode.pageflip || ms->atomic_modeset ||
+                !drmSetClientCap(ms->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) {
+                ms->drmmode.tearfree_enable = TRUE;
+                xf86DrvMsg(pScrn->scrnIndex, X_INFO, "TearFree: enabled\n");
+            } else {
+                xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+                           "TearFree requires either universal planes, or setting 'Option \"PageFlip\" \"off\"'\n");
+            }
+        } else {
+            xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+                       "TearFree requires Glamor acceleration\n");
+        }
+    }
+
     ms->kms_has_modifiers = FALSE;
     ret = drmGetCap(ms->fd, DRM_CAP_ADDFB2_MODIFIERS, &value);
     if (ret == 0 && value != 0)
@@ -1634,13 +1757,13 @@ CreateScreenResources(ScreenPtr pScreen)
 
     err = drmModeDirtyFB(ms->fd, ms->drmmode.fb_id, NULL, 0);
 
-    if (err != -EINVAL && err != -ENOSYS) {
+    if ((err != -EINVAL && err != -ENOSYS) || ms->drmmode.tearfree_enable) {
         ms->damage = DamageCreate(NULL, NULL, DamageReportNone, TRUE,
                                   pScreen, rootPixmap);
 
         if (ms->damage) {
             DamageRegister(&rootPixmap->drawable, ms->damage);
-            ms->dirty_enabled = TRUE;
+            ms->dirty_enabled = err != -EINVAL && err != -ENOSYS;
             xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Damage tracking initialized\n");
         }
         else {
blob - 71aa8730ec2fd74bec50d2bf237b474abe5197df
blob + 6e1381d53fc6dd35b5e1d9e3322d24d593b01243
--- xserver/hw/xfree86/drivers/modesetting/driver.h
+++ xserver/hw/xfree86/drivers/modesetting/driver.h
@@ -61,6 +61,7 @@ typedef enum {
     OPTION_VARIABLE_REFRESH,
     OPTION_USE_GAMMA_LUT,
     OPTION_ASYNC_FLIP_SECONDARIES,
+    OPTION_TEARFREE,
 } modesettingOpts;
 
 typedef struct
@@ -86,10 +87,13 @@ struct ms_drm_queue {
     struct xorg_list list;
     xf86CrtcPtr crtc;
     uint32_t seq;
+    uint64_t msc;
     void *data;
     ScrnInfoPtr scrn;
     ms_drm_handler_proc handler;
     ms_drm_abort_proc abort;
+    Bool kernel_queued;
+    Bool aborted;
 };
 
 typedef struct _modesettingRec {
@@ -202,6 +206,8 @@ void ms_drm_abort(ScrnInfoPtr scrn,
                   void *match_data);
 void ms_drm_abort_seq(ScrnInfoPtr scrn, uint32_t seq);
 
+Bool ms_drm_queue_is_empty(void);
+
 Bool xf86_crtc_on(xf86CrtcPtr crtc);
 
 xf86CrtcPtr ms_dri2_crtc_covering_drawable(DrawablePtr pDraw);
@@ -232,14 +238,26 @@ typedef void (*ms_pageflip_abort_proc)(modesettingPtr 
 Bool ms_do_pageflip(ScreenPtr screen,
                     PixmapPtr new_front,
                     void *event,
-                    int ref_crtc_vblank_pipe,
+                    xf86CrtcPtr ref_crtc,
                     Bool async,
                     ms_pageflip_handler_proc pageflip_handler,
                     ms_pageflip_abort_proc pageflip_abort,
                     const char *log_prefix);
 
+Bool
+ms_tearfree_dri_abort(xf86CrtcPtr crtc,
+                      Bool (*match)(void *data, void *match_data),
+                      void *match_data);
+
+void
+ms_tearfree_dri_abort_all(xf86CrtcPtr crtc);
+
+Bool ms_do_tearfree_flip(ScreenPtr screen, xf86CrtcPtr crtc);
+
 #endif
 
 int ms_flush_drm_events(ScreenPtr screen);
+void ms_drain_drm_events(ScreenPtr screen);
 Bool ms_window_has_variable_refresh(modesettingPtr ms, WindowPtr win);
 void ms_present_set_screen_vrr(ScrnInfoPtr scrn, Bool vrr_enabled);
+Bool ms_tearfree_is_active_on_crtc(xf86CrtcPtr crtc);
blob - 3c0bc8e30815e88f72c2980b9c84891953f58141
blob + ff445cc9a975b05e00ddd457c3930d1082d18927
--- xserver/hw/xfree86/drivers/modesetting/drmmode_display.c
+++ xserver/hw/xfree86/drivers/modesetting/drmmode_display.c
@@ -635,6 +635,7 @@ drmmode_crtc_get_fb_id(xf86CrtcPtr crtc, uint32_t *fb_
 {
     drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
     drmmode_ptr drmmode = drmmode_crtc->drmmode;
+    drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
     int ret;
 
     *fb_id = 0;
@@ -649,6 +650,10 @@ drmmode_crtc_get_fb_id(xf86CrtcPtr crtc, uint32_t *fb_
             *x = drmmode_crtc->prime_pixmap_x;
         *y = 0;
     }
+    else if (trf->buf[trf->back_idx ^ 1].px) {
+        *fb_id = trf->buf[trf->back_idx ^ 1].fb_id;
+        *x = *y = 0;
+    }
     else if (drmmode_crtc->rotate_fb_id) {
         *fb_id = drmmode_crtc->rotate_fb_id;
         *x = *y = 0;
@@ -925,6 +930,10 @@ drmmode_crtc_set_mode(xf86CrtcPtr crtc, Bool test_only
     drmmode_ConvertToKMode(crtc->scrn, &kmode, &crtc->mode);
     ret = drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
                          fb_id, x, y, output_ids, output_count, &kmode);
+    if (!ret && !ms->atomic_modeset) {
+        drmmode_crtc->src_x = x;
+        drmmode_crtc->src_y = y;
+    }
 
     drmmode_set_ctm(crtc, ctm);
 
@@ -933,7 +942,8 @@ drmmode_crtc_set_mode(xf86CrtcPtr crtc, Bool test_only
 }
 
 int
-drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, uint32_t flags, void *data)
+drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, int x, int y,
+                  uint32_t flags, void *data)
 {
     modesettingPtr ms = modesettingPTR(crtc->scrn);
     drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
@@ -945,7 +955,7 @@ drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, ui
         if (!req)
             return 1;
 
-        ret = plane_add_props(req, crtc, fb_id, crtc->x, crtc->y);
+        ret = plane_add_props(req, crtc, fb_id, x, y);
         flags |= DRM_MODE_ATOMIC_NONBLOCK;
         if (ret == 0)
             ret = drmModeAtomicCommit(ms->fd, req, flags, data);
@@ -953,6 +963,26 @@ drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, ui
         return ret;
     }
 
+    /* The frame buffer source coordinates may change when switching between the
+     * primary frame buffer and a per-CRTC frame buffer. Set the correct source
+     * coordinates if they differ for this flip.
+     */
+    if (drmmode_crtc->src_x != x || drmmode_crtc->src_y != y) {
+        ret = drmModeSetPlane(ms->fd, drmmode_crtc->plane_id,
+                              drmmode_crtc->mode_crtc->crtc_id, fb_id, 0,
+                              0, 0, crtc->mode.HDisplay, crtc->mode.VDisplay,
+                              x << 16, y << 16, crtc->mode.HDisplay << 16,
+                              crtc->mode.VDisplay << 16);
+        if (ret) {
+            xf86DrvMsg(crtc->scrn->scrnIndex, X_WARNING,
+                       "error changing fb src coordinates for flip: %d\n", ret);
+            return ret;
+        }
+
+        drmmode_crtc->src_x = x;
+        drmmode_crtc->src_y = y;
+    }
+
     return drmModePageFlip(ms->fd, drmmode_crtc->mode_crtc->crtc_id,
                            fb_id, flags, data);
 }
@@ -1551,7 +1581,111 @@ drmmode_copy_fb(ScrnInfoPtr pScrn, drmmode_ptr drmmode
 #endif
 }
 
+void
+drmmode_copy_damage(xf86CrtcPtr crtc, PixmapPtr dst, RegionPtr dmg, Bool empty)
+{
+#ifdef GLAMOR_HAS_GBM
+    ScreenPtr pScreen = xf86ScrnToScreen(crtc->scrn);
+    DrawableRec *src;
+
+    /* Copy the screen's pixmap into the destination pixmap */
+    if (crtc->rotatedPixmap) {
+        src = &crtc->rotatedPixmap->drawable;
+        xf86RotateCrtcRedisplay(crtc, dst, src, dmg, FALSE);
+    } else {
+        src = &pScreen->GetScreenPixmap(pScreen)->drawable;
+        PixmapDirtyCopyArea(dst, src, 0, 0, -crtc->x, -crtc->y, dmg);
+    }
+
+    /* Reset the damages if requested */
+    if (empty)
+        RegionEmpty(dmg);
+
+    /* Wait until the GC operations finish */
+    modesettingPTR(crtc->scrn)->glamor.finish(pScreen);
+#endif
+}
+
+static void
+drmmode_shadow_fb_destroy(xf86CrtcPtr crtc, PixmapPtr pixmap,
+                          void *data, drmmode_bo *bo, uint32_t *fb_id);
+static void
+drmmode_destroy_tearfree_shadow(xf86CrtcPtr crtc)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+    drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
+    int i;
+
+    if (trf->flip_seq)
+        ms_drm_abort_seq(crtc->scrn, trf->flip_seq);
+
+    for (i = 0; i < ARRAY_SIZE(trf->buf); i++) {
+        if (trf->buf[i].px) {
+            drmmode_shadow_fb_destroy(crtc, trf->buf[i].px, (void *)(long)1,
+                                      &trf->buf[i].bo, &trf->buf[i].fb_id);
+            trf->buf[i].px = NULL;
+            RegionUninit(&trf->buf[i].dmg);
+        }
+    }
+}
+
+static PixmapPtr
+drmmode_shadow_fb_create(xf86CrtcPtr crtc, void *data, int width, int height,
+                         drmmode_bo *bo, uint32_t *fb_id);
 static Bool
+drmmode_create_tearfree_shadow(xf86CrtcPtr crtc)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+    drmmode_ptr drmmode = drmmode_crtc->drmmode;
+    drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
+    uint32_t w = crtc->mode.HDisplay, h = crtc->mode.VDisplay;
+    int i;
+
+    if (!drmmode->tearfree_enable)
+        return TRUE;
+
+    /* Destroy the old mode's buffers and make new ones */
+    drmmode_destroy_tearfree_shadow(crtc);
+    for (i = 0; i < ARRAY_SIZE(trf->buf); i++) {
+        trf->buf[i].px = drmmode_shadow_fb_create(crtc, NULL, w, h,
+                                                  &trf->buf[i].bo,
+                                                  &trf->buf[i].fb_id);
+        if (!trf->buf[i].px) {
+            drmmode_destroy_tearfree_shadow(crtc);
+            xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
+                       "shadow creation failed for TearFree buf%d\n", i);
+            return FALSE;
+        }
+        RegionInit(&trf->buf[i].dmg, &crtc->bounds, 0);
+    }
+
+    /* Initialize the front buffer with the current scanout */
+    drmmode_copy_damage(crtc, trf->buf[trf->back_idx ^ 1].px,
+                        &trf->buf[trf->back_idx ^ 1].dmg, TRUE);
+    return TRUE;
+}
+
+static void drmmmode_prepare_modeset(ScrnInfoPtr scrn)
+{
+    ScreenPtr pScreen = scrn->pScreen;
+    modesettingPtr ms = modesettingPTR(scrn);
+
+    if (ms->drmmode.pending_modeset)
+        return;
+
+    /*
+     * Force present to unflip everything before we might
+     * try lighting up new displays. This makes sure fancy
+     * modifiers can't cause the modeset to fail.
+     */
+    ms->drmmode.pending_modeset = TRUE;
+    present_check_flips(pScreen->root);
+    ms->drmmode.pending_modeset = FALSE;
+
+    ms_drain_drm_events(pScreen);
+}
+
+static Bool
 drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
                        Rotation rotation, int x, int y)
 {
@@ -1566,6 +1700,9 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePt
     Bool can_test;
     int i;
 
+    if (mode)
+        drmmmode_prepare_modeset(crtc->scrn);
+
     saved_mode = crtc->mode;
     saved_x = crtc->x;
     saved_y = crtc->y;
@@ -1584,6 +1721,10 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePt
         crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green,
                                crtc->gamma_blue, crtc->gamma_size);
 
+        ret = drmmode_create_tearfree_shadow(crtc);
+        if (!ret)
+            goto done;
+
         can_test = drmmode_crtc_can_test_mode(crtc);
         if (drmmode_crtc_set_mode(crtc, can_test)) {
             xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
@@ -1629,6 +1770,7 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePt
         crtc->y = saved_y;
         crtc->rotation = saved_rotation;
         crtc->mode = saved_mode;
+        drmmode_create_tearfree_shadow(crtc);
     } else
         crtc->active = TRUE;
 
@@ -1934,35 +2076,44 @@ drmmode_clear_pixmap(PixmapPtr pixmap)
 }
 
 static void *
-drmmode_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
+drmmode_shadow_fb_allocate(xf86CrtcPtr crtc, int width, int height,
+                           drmmode_bo *bo, uint32_t *fb_id)
 {
     drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
     drmmode_ptr drmmode = drmmode_crtc->drmmode;
     int ret;
 
-    if (!drmmode_create_bo(drmmode, &drmmode_crtc->rotate_bo,
-                           width, height, drmmode->kbpp)) {
+    if (!drmmode_create_bo(drmmode, bo, width, height, drmmode->kbpp)) {
         xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
                "Couldn't allocate shadow memory for rotated CRTC\n");
         return NULL;
     }
 
-    ret = drmmode_bo_import(drmmode, &drmmode_crtc->rotate_bo,
-                            &drmmode_crtc->rotate_fb_id);
+    ret = drmmode_bo_import(drmmode, bo, fb_id);
 
     if (ret) {
         ErrorF("failed to add rotate fb\n");
-        drmmode_bo_destroy(drmmode, &drmmode_crtc->rotate_bo);
+        drmmode_bo_destroy(drmmode, bo);
         return NULL;
     }
 
 #ifdef GLAMOR_HAS_GBM
     if (drmmode->gbm)
-        return drmmode_crtc->rotate_bo.gbm;
+        return bo->gbm;
 #endif
-    return drmmode_crtc->rotate_bo.dumb;
+    return bo->dumb;
 }
 
+static void *
+drmmode_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+
+    return drmmode_shadow_fb_allocate(crtc, width, height,
+                                      &drmmode_crtc->rotate_bo,
+                                      &drmmode_crtc->rotate_fb_id);
+}
+
 static PixmapPtr
 drmmode_create_pixmap_header(ScreenPtr pScreen, int width, int height,
                              int depth, int bitsPerPixel, int devKind,
@@ -1986,71 +2137,91 @@ static Bool
 drmmode_set_pixmap_bo(drmmode_ptr drmmode, PixmapPtr pixmap, drmmode_bo *bo);
 
 static PixmapPtr
-drmmode_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
+drmmode_shadow_fb_create(xf86CrtcPtr crtc, void *data, int width, int height,
+                         drmmode_bo *bo, uint32_t *fb_id)
 {
     ScrnInfoPtr scrn = crtc->scrn;
     drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
     drmmode_ptr drmmode = drmmode_crtc->drmmode;
-    uint32_t rotate_pitch;
-    PixmapPtr rotate_pixmap;
+    uint32_t pitch;
+    PixmapPtr pixmap;
     void *pPixData = NULL;
 
     if (!data) {
-        data = drmmode_shadow_allocate(crtc, width, height);
+        data = drmmode_shadow_fb_allocate(crtc, width, height, bo, fb_id);
         if (!data) {
             xf86DrvMsg(scrn->scrnIndex, X_ERROR,
-                       "Couldn't allocate shadow pixmap for rotated CRTC\n");
+                       "Couldn't allocate shadow pixmap for CRTC\n");
             return NULL;
         }
     }
 
-    if (!drmmode_bo_has_bo(&drmmode_crtc->rotate_bo)) {
+    if (!drmmode_bo_has_bo(bo)) {
         xf86DrvMsg(scrn->scrnIndex, X_ERROR,
-                   "Couldn't allocate shadow pixmap for rotated CRTC\n");
+                   "Couldn't allocate shadow pixmap for CRTC\n");
         return NULL;
     }
 
-    pPixData = drmmode_bo_map(drmmode, &drmmode_crtc->rotate_bo);
-    rotate_pitch = drmmode_bo_get_pitch(&drmmode_crtc->rotate_bo);
+    pPixData = drmmode_bo_map(drmmode, bo);
+    pitch = drmmode_bo_get_pitch(bo);
+    pixmap = drmmode_create_pixmap_header(scrn->pScreen,
+                                          width, height,
+                                          scrn->depth,
+                                          drmmode->kbpp,
+                                          pitch,
+                                          pPixData);
 
-    rotate_pixmap = drmmode_create_pixmap_header(scrn->pScreen,
-                                                 width, height,
-                                                 scrn->depth,
-                                                 drmmode->kbpp,
-                                                 rotate_pitch,
-                                                 pPixData);
-
-    if (rotate_pixmap == NULL) {
+    if (pixmap == NULL) {
         xf86DrvMsg(scrn->scrnIndex, X_ERROR,
-                   "Couldn't allocate shadow pixmap for rotated CRTC\n");
+                   "Couldn't allocate shadow pixmap for CRTC\n");
         return NULL;
     }
 
-    drmmode_set_pixmap_bo(drmmode, rotate_pixmap, &drmmode_crtc->rotate_bo);
+    drmmode_set_pixmap_bo(drmmode, pixmap, bo);
 
-    return rotate_pixmap;
+    return pixmap;
 }
 
+static PixmapPtr
+drmmode_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+
+    return drmmode_shadow_fb_create(crtc, data, width, height,
+                                    &drmmode_crtc->rotate_bo,
+                                    &drmmode_crtc->rotate_fb_id);
+}
+
 static void
-drmmode_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *data)
+drmmode_shadow_fb_destroy(xf86CrtcPtr crtc, PixmapPtr pixmap,
+                          void *data, drmmode_bo *bo, uint32_t *fb_id)
 {
     drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
     drmmode_ptr drmmode = drmmode_crtc->drmmode;
 
-    if (rotate_pixmap) {
-        rotate_pixmap->drawable.pScreen->DestroyPixmap(rotate_pixmap);
+    if (pixmap) {
+        pixmap->drawable.pScreen->DestroyPixmap(pixmap);
     }
 
     if (data) {
-        drmModeRmFB(drmmode->fd, drmmode_crtc->rotate_fb_id);
-        drmmode_crtc->rotate_fb_id = 0;
+        drmModeRmFB(drmmode->fd, *fb_id);
+        *fb_id = 0;
 
-        drmmode_bo_destroy(drmmode, &drmmode_crtc->rotate_bo);
-        memset(&drmmode_crtc->rotate_bo, 0, sizeof drmmode_crtc->rotate_bo);
+        drmmode_bo_destroy(drmmode, bo);
+        memset(bo, 0, sizeof(*bo));
     }
 }
 
 static void
+drmmode_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr pixmap, void *data)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+
+    drmmode_shadow_fb_destroy(crtc, pixmap, data, &drmmode_crtc->rotate_bo,
+                              &drmmode_crtc->rotate_fb_id);
+}
+
+static void
 drmmode_crtc_destroy(xf86CrtcPtr crtc)
 {
     drmmode_mode_ptr iterator, next;
@@ -2383,6 +2554,8 @@ drmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmo
     drmmode_crtc->drmmode = drmmode;
     drmmode_crtc->vblank_pipe = drmmode_crtc_vblank_pipe(num);
     xorg_list_init(&drmmode_crtc->mode_list);
+    xorg_list_init(&drmmode_crtc->tearfree.dri_flip_list);
+    drmmode_crtc->next_msc = UINT64_MAX;
 
     props = drmModeObjectGetProperties(drmmode->fd, mode_res->crtcs[num],
                                        DRM_MODE_OBJECT_CRTC);
@@ -3758,6 +3931,8 @@ drmmode_set_desired_modes(ScrnInfoPtr pScrn, drmmode_p
     Bool success = TRUE;
     int c;
 
+    drmmmode_prepare_modeset(pScrn);
+
     for (c = 0; c < config->num_crtc; c++) {
         xf86CrtcPtr crtc = config->crtc[c];
         drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
@@ -4277,6 +4452,7 @@ drmmode_free_bos(ScrnInfoPtr pScrn, drmmode_ptr drmmod
         drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
 
         dumb_bo_destroy(drmmode->fd, drmmode_crtc->cursor_bo);
+	drmmode_destroy_tearfree_shadow(crtc);
     }
 }
 
blob - 2760a171e3810d1796021f3cfdda3eb589aa7946
blob + 85e22029937327814c44e0d8a0a98576e83c6725
--- xserver/hw/xfree86/drivers/modesetting/drmmode_display.h
+++ xserver/hw/xfree86/drivers/modesetting/drmmode_display.h
@@ -138,9 +138,12 @@ typedef struct {
     Bool async_flip_secondaries;
     Bool dri2_enable;
     Bool present_enable;
+    Bool tearfree_enable;
 
     uint32_t vrr_prop_id;
     Bool use_ctm;
+
+    Bool pending_modeset;
 } drmmode_rec, *drmmode_ptr;
 
 typedef struct {
@@ -170,6 +173,20 @@ typedef struct {
 } drmmode_format_rec, *drmmode_format_ptr;
 
 typedef struct {
+    drmmode_bo bo;
+    uint32_t fb_id;
+    PixmapPtr px;
+    RegionRec dmg;
+} drmmode_shadow_fb_rec, *drmmode_shadow_fb_ptr;
+
+typedef struct {
+    drmmode_shadow_fb_rec buf[2];
+    struct xorg_list dri_flip_list;
+    uint32_t back_idx;
+    uint32_t flip_seq;
+} drmmode_tearfree_rec, *drmmode_tearfree_ptr;
+
+typedef struct {
     drmmode_ptr drmmode;
     drmModeCrtcPtr mode_crtc;
     uint32_t vblank_pipe;
@@ -187,11 +204,14 @@ typedef struct {
 
     drmmode_bo rotate_bo;
     unsigned rotate_fb_id;
+    drmmode_tearfree_rec tearfree;
 
     PixmapPtr prime_pixmap;
     PixmapPtr prime_pixmap_back;
     unsigned prime_pixmap_x;
 
+    int src_x, src_y;
+
     /**
      * @{ MSC (vblank count) handling for the PRESENT extension.
      *
@@ -203,6 +223,8 @@ typedef struct {
     uint64_t msc_high;
     /** @} */
 
+    uint64_t next_msc;
+
     Bool need_modeset;
     struct xorg_list mode_list;
 
@@ -311,8 +333,11 @@ void drmmode_get_default_bpp(ScrnInfoPtr pScrn, drmmod
                              int *depth, int *bpp);
 
 void drmmode_copy_fb(ScrnInfoPtr pScrn, drmmode_ptr drmmode);
+void drmmode_copy_damage(xf86CrtcPtr crtc, PixmapPtr dst, RegionPtr damage,
+                         Bool empty);
 
-int drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, uint32_t flags, void *data);
+int drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, int x, int y,
+                      uint32_t flags, void *data);
 
 Bool drmmode_crtc_get_fb_id(xf86CrtcPtr crtc, uint32_t *fb_id, int *x, int *y);
 
blob - 71790011ec93f903a7b689980578086de144e70b
blob + ea761e8a0576765b2c88ec2a33464f78218829be
--- xserver/hw/xfree86/drivers/modesetting/modesetting.man
+++ xserver/hw/xfree86/drivers/modesetting/modesetting.man
@@ -109,6 +109,17 @@ When enabled, this option allows the driver to use gam
 entries, if supported by the kernel. By default, GAMMA_LUT will be used for
 kms drivers which are known to be safe for use of GAMMA_LUT.
 .TP
+.BI "Option \*qTearFree\*q \*q" boolean \*q
+Enable tearing prevention using the hardware page flipping mechanism.
+It allocates two extra scanout buffers for each CRTC and utilizes damage
+tracking to minimize buffer copying and skip unnecessary flips when the
+screen's contents have not changed. It works on transformed screens too, such
+as rotated and scaled CRTCs. When PageFlip is enabled, fullscreen DRI
+applications will still have the discretion to not use tearing prevention.
+.br
+The default is
+.B on.
+.TP
 .SH "SEE ALSO"
 @xservername@(@appmansuffix@), @xconfigfile@(@filemansuffix@), Xserver(@appmansuffix@),
 X(@miscmansuffix@)
blob - 23ee95f9a68d96c037c33ce28e2afb7761c552bb
blob + f6903adb2d1ce92f856cf5d31ad2da6b14afba97
--- xserver/hw/xfree86/drivers/modesetting/pageflip.c
+++ xserver/hw/xfree86/drivers/modesetting/pageflip.c
@@ -35,8 +35,8 @@
  * Returns a negative value on error, 0 if there was nothing to process,
  * or 1 if we handled any events.
  */
-int
-ms_flush_drm_events(ScreenPtr screen)
+static int
+ms_flush_drm_events_timeout(ScreenPtr screen, int timeout)
 {
     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
     modesettingPtr ms = modesettingPTR(scrn);
@@ -45,7 +45,7 @@ ms_flush_drm_events(ScreenPtr screen)
     int r;
 
     do {
-            r = xserver_poll(&p, 1, 0);
+            r = xserver_poll(&p, 1, timeout);
     } while (r == -1 && (errno == EINTR || errno == EAGAIN));
 
     /* If there was an error, r will be < 0.  Return that.  If there was
@@ -63,6 +63,19 @@ ms_flush_drm_events(ScreenPtr screen)
     return 1;
 }
 
+int
+ms_flush_drm_events(ScreenPtr screen)
+{
+    return ms_flush_drm_events_timeout(screen, 0);
+}
+
+void
+ms_drain_drm_events(ScreenPtr screen)
+{
+    while (!ms_drm_queue_is_empty())
+        ms_flush_drm_events_timeout(screen, -1);
+}
+
 #ifdef GLAMOR_HAS_GBM
 
 /*
@@ -93,6 +106,8 @@ struct ms_crtc_pageflip {
     Bool on_reference_crtc;
     /* reference to the ms_flipdata */
     struct ms_flipdata *flipdata;
+    struct xorg_list node;
+    uint32_t tearfree_seq;
 };
 
 /**
@@ -136,7 +151,8 @@ ms_pageflip_handler(uint64_t msc, uint64_t ust, void *
                                 flipdata->fe_usec,
                                 flipdata->event);
 
-        drmModeRmFB(ms->fd, flipdata->old_fb_id);
+        if (flipdata->old_fb_id)
+            drmModeRmFB(ms->fd, flipdata->old_fb_id);
     }
     ms_pageflip_free(flip);
 }
@@ -160,11 +176,32 @@ ms_pageflip_abort(void *data)
 }
 
 static Bool
-do_queue_flip_on_crtc(modesettingPtr ms, xf86CrtcPtr crtc,
-                      uint32_t flags, uint32_t seq)
+do_queue_flip_on_crtc(ScreenPtr screen, xf86CrtcPtr crtc, uint32_t flags,
+                      uint32_t seq, uint32_t fb_id, int x, int y)
 {
-    return drmmode_crtc_flip(crtc, ms->drmmode.fb_id, flags,
-                             (void *) (uintptr_t) seq);
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+    drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
+
+    while (drmmode_crtc_flip(crtc, fb_id, x, y, flags, (void *)(long)seq)) {
+        /* We may have failed because the event queue was full.  Flush it
+         * and retry.  If there was nothing to flush, then we failed for
+         * some other reason and should just return an error.
+         */
+        if (ms_flush_drm_events(screen) <= 0) {
+            /* The failure could be caused by a pending TearFree flip, in which
+             * case we should wait until there's a new event and try again.
+             */
+            if (!trf->flip_seq || ms_flush_drm_events_timeout(screen, -1) < 0) {
+                ms_drm_abort_seq(crtc->scrn, seq);
+                return TRUE;
+            }
+        }
+
+        /* We flushed some events, so try again. */
+        xf86DrvMsg(crtc->scrn->scrnIndex, X_WARNING, "flip queue retry\n");
+    }
+
+    return FALSE;
 }
 
 enum queue_flip_status {
@@ -177,11 +214,10 @@ enum queue_flip_status {
 static int
 queue_flip_on_crtc(ScreenPtr screen, xf86CrtcPtr crtc,
                    struct ms_flipdata *flipdata,
-                   int ref_crtc_vblank_pipe, uint32_t flags)
+                   xf86CrtcPtr ref_crtc, uint32_t flags)
 {
     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
     modesettingPtr ms = modesettingPTR(scrn);
-    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
     struct ms_crtc_pageflip *flip;
     uint32_t seq;
 
@@ -193,7 +229,7 @@ queue_flip_on_crtc(ScreenPtr screen, xf86CrtcPtr crtc,
     /* Only the reference crtc will finally deliver its page flip
      * completion event. All other crtc's events will be discarded.
      */
-    flip->on_reference_crtc = (drmmode_crtc->vblank_pipe == ref_crtc_vblank_pipe);
+    flip->on_reference_crtc = crtc == ref_crtc;
     flip->flipdata = flipdata;
 
     seq = ms_drm_queue_alloc(crtc, flip, ms_pageflip_handler, ms_pageflip_abort);
@@ -205,21 +241,10 @@ queue_flip_on_crtc(ScreenPtr screen, xf86CrtcPtr crtc,
     /* take a reference on flipdata for use in flip */
     flipdata->flip_count++;
 
-    while (do_queue_flip_on_crtc(ms, crtc, flags, seq)) {
-        /* We may have failed because the event queue was full.  Flush it
-         * and retry.  If there was nothing to flush, then we failed for
-         * some other reason and should just return an error.
-         */
-        if (ms_flush_drm_events(screen) <= 0) {
-            /* Aborting will also decrement flip_count and free(flip). */
-            ms_drm_abort_seq(scrn, seq);
-            return QUEUE_FLIP_DRM_FLUSH_FAILED;
-        }
+    if (do_queue_flip_on_crtc(screen, crtc, flags, seq, ms->drmmode.fb_id,
+                              crtc->x, crtc->y))
+        return QUEUE_FLIP_DRM_FLUSH_FAILED;
 
-        /* We flushed some events, so try again. */
-        xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue retry\n");
-    }
-
     /* The page flip succeeded. */
     return QUEUE_FLIP_SUCCESS;
 }
@@ -294,20 +319,75 @@ ms_print_pageflip_error(int screen_index, const char *
     }
 }
 
+static Bool
+ms_tearfree_dri_flip(modesettingPtr ms, xf86CrtcPtr crtc, void *event,
+                     ms_pageflip_handler_proc pageflip_handler,
+                     ms_pageflip_abort_proc pageflip_abort)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+    drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
+    struct ms_crtc_pageflip *flip;
+    struct ms_flipdata *flipdata;
+    RegionRec region;
+    RegionPtr dirty;
 
+    if (!ms_tearfree_is_active_on_crtc(crtc))
+        return FALSE;
+
+    /* Check for damage on the primary scanout to know if TearFree will flip */
+    dirty = DamageRegion(ms->damage);
+    if (RegionNil(dirty))
+        return FALSE;
+
+    /* Compute how much of the current damage intersects with this CRTC */
+    RegionInit(&region, &crtc->bounds, 0);
+    RegionIntersect(&region, &region, dirty);
+
+    /* No damage on this CRTC means no TearFree flip. This means the DRI client
+     * didn't change this CRTC's contents at all with its presentation, possibly
+     * because its window is fully occluded by another window on this CRTC.
+     */
+    if (RegionNil(&region))
+        return FALSE;
+
+    flip = calloc(1, sizeof(*flip));
+    if (!flip)
+        return FALSE;
+
+    flipdata = calloc(1, sizeof(*flipdata));
+    if (!flipdata) {
+        free(flip);
+        return FALSE;
+    }
+
+    /* Only track the DRI client's fake flip on the reference CRTC, which aligns
+     * with the behavior of Present when a client copies its pixmap rather than
+     * directly flipping it onto the display.
+     */
+    flip->on_reference_crtc = TRUE;
+    flip->flipdata = flipdata;
+    flip->tearfree_seq = trf->flip_seq;
+    flipdata->screen = xf86ScrnToScreen(crtc->scrn);
+    flipdata->event = event;
+    flipdata->flip_count = 1;
+    flipdata->event_handler = pageflip_handler;
+    flipdata->abort_handler = pageflip_abort;
+
+    /* Keep the list in FIFO order so that clients are notified in order */
+    xorg_list_append(&flip->node, &trf->dri_flip_list);
+    return TRUE;
+}
+
 Bool
 ms_do_pageflip(ScreenPtr screen,
                PixmapPtr new_front,
                void *event,
-               int ref_crtc_vblank_pipe,
+               xf86CrtcPtr ref_crtc,
                Bool async,
                ms_pageflip_handler_proc pageflip_handler,
                ms_pageflip_abort_proc pageflip_abort,
                const char *log_prefix)
 {
-#ifndef GLAMOR_HAS_GBM
-    return FALSE;
-#else
     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
     modesettingPtr ms = modesettingPTR(scrn);
     xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
@@ -315,6 +395,22 @@ ms_do_pageflip(ScreenPtr screen,
     uint32_t flags;
     int i;
     struct ms_flipdata *flipdata;
+
+    /* A NULL pixmap indicates this DRI client's pixmap is to be flipped through
+     * TearFree instead. The pixmap is already copied to the primary scanout at
+     * this point, so all that's left is to wire up this fake flip to TearFree
+     * so that TearFree can send a notification to the DRI client when the
+     * pixmap actually appears on the display. This is the only way to let DRI
+     * clients accurately know when their pixmaps appear on the display when
+     * TearFree is enabled.
+     */
+    if (!new_front) {
+        if (!ms_tearfree_dri_flip(ms, ref_crtc, event, pageflip_handler,
+                                  pageflip_abort))
+            goto error_free_event;
+        return TRUE;
+    }
+
     ms->glamor.block_handler(screen);
 
     new_front_bo.gbm = ms->glamor.gbm_bo_from_pixmap(screen, new_front);
@@ -324,7 +420,7 @@ ms_do_pageflip(ScreenPtr screen,
         xf86DrvMsg(scrn->scrnIndex, X_ERROR,
                    "%s: Failed to get GBM BO for flip to new front.\n",
                    log_prefix);
-        return FALSE;
+        goto error_free_event;
     }
 
     flipdata = calloc(1, sizeof(struct ms_flipdata));
@@ -332,7 +428,7 @@ ms_do_pageflip(ScreenPtr screen,
         drmmode_bo_destroy(&ms->drmmode, &new_front_bo);
         xf86DrvMsg(scrn->scrnIndex, X_ERROR,
                    "%s: Failed to allocate flipdata.\n", log_prefix);
-        return FALSE;
+        goto error_free_event;
     }
 
     flipdata->event = event;
@@ -380,7 +476,6 @@ ms_do_pageflip(ScreenPtr screen,
     for (i = 0; i < config->num_crtc; i++) {
         enum queue_flip_status flip_status;
         xf86CrtcPtr crtc = config->crtc[i];
-        drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
 
         if (!xf86_crtc_on(crtc))
             continue;
@@ -401,13 +496,11 @@ ms_do_pageflip(ScreenPtr screen,
          * outputs in a "clone-mode" or "mirror-mode" configuration.
          */
         if (ms->drmmode.can_async_flip && ms->drmmode.async_flip_secondaries &&
-            (drmmode_crtc->vblank_pipe != ref_crtc_vblank_pipe) &&
-            (ref_crtc_vblank_pipe >= 0))
+            ref_crtc && crtc != ref_crtc)
             flags |= DRM_MODE_PAGE_FLIP_ASYNC;
 
         flip_status = queue_flip_on_crtc(screen, crtc, flipdata,
-                                         ref_crtc_vblank_pipe,
-                                         flags);
+                                         ref_crtc, flags);
 
         switch (flip_status) {
             case QUEUE_FLIP_ALLOC_FAILED:
@@ -456,13 +549,150 @@ error_out:
     drmmode_bo_destroy(&ms->drmmode, &new_front_bo);
     /* if only the local reference - free the structure,
      * else drop the local reference and return */
-    if (flipdata->flip_count == 1)
+    if (flipdata->flip_count == 1) {
         free(flipdata);
-    else
+    } else {
         flipdata->flip_count--;
+        return FALSE;
+    }
 
+error_free_event:
+    /* Free the event since the caller has no way to know it's safe to free */
+    free(event);
     return FALSE;
-#endif /* GLAMOR_HAS_GBM */
 }
 
+Bool
+ms_tearfree_dri_abort(xf86CrtcPtr crtc,
+                      Bool (*match)(void *data, void *match_data),
+                      void *match_data)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+    drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
+    struct ms_crtc_pageflip *flip;
+
+    /* The window is getting destroyed; abort without notifying the client */
+    xorg_list_for_each_entry(flip, &trf->dri_flip_list, node) {
+        if (match(flip->flipdata->event, match_data)) {
+            xorg_list_del(&flip->node);
+            ms_pageflip_abort(flip);
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+void
+ms_tearfree_dri_abort_all(xf86CrtcPtr crtc)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+    drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
+    struct ms_crtc_pageflip *flip, *tmp;
+    uint64_t usec = 0, msc = 0;
+
+    /* Nothing to abort if there aren't any DRI clients waiting for a flip */
+    if (xorg_list_is_empty(&trf->dri_flip_list))
+        return;
+
+    /* Even though we're aborting, these clients' pixmaps were actually blitted,
+     * so technically the presentation isn't aborted. That's why the normal
+     * handler is called instead of the abort handler, along with the current
+     * time and MSC for this CRTC.
+     */
+    ms_get_crtc_ust_msc(crtc, &usec, &msc);
+    xorg_list_for_each_entry_safe(flip, tmp, &trf->dri_flip_list, node)
+        ms_pageflip_handler(msc, usec, flip);
+    xorg_list_init(&trf->dri_flip_list);
+}
+
+static void
+ms_tearfree_dri_notify(drmmode_tearfree_ptr trf, uint64_t msc, uint64_t usec)
+{
+    struct ms_crtc_pageflip *flip, *tmp;
+
+    xorg_list_for_each_entry_safe(flip, tmp, &trf->dri_flip_list, node) {
+        /* If a TearFree flip was already pending at the time this DRI client's
+         * pixmap was copied, then the pixmap isn't contained in this TearFree
+         * flip, but will be part of the next TearFree flip instead.
+         */
+        if (flip->tearfree_seq) {
+            flip->tearfree_seq = 0;
+        } else {
+            xorg_list_del(&flip->node);
+            ms_pageflip_handler(msc, usec, flip);
+        }
+    }
+}
+
+static void
+ms_tearfree_flip_abort(void *data)
+{
+    xf86CrtcPtr crtc = data;
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+    drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
+
+    trf->flip_seq = 0;
+    ms_tearfree_dri_abort_all(crtc);
+}
+
+static void
+ms_tearfree_flip_handler(uint64_t msc, uint64_t usec, void *data)
+{
+    xf86CrtcPtr crtc = data;
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+    drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
+
+    /* Swap the buffers and complete the flip */
+    trf->back_idx ^= 1;
+    trf->flip_seq = 0;
+
+    /* Notify DRI clients that their pixmaps are now visible on the display */
+    ms_tearfree_dri_notify(trf, msc, usec);
+}
+
+Bool
+ms_do_tearfree_flip(ScreenPtr screen, xf86CrtcPtr crtc)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+    drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
+    uint32_t idx = trf->back_idx, seq;
+
+    seq = ms_drm_queue_alloc(crtc, crtc, ms_tearfree_flip_handler,
+                             ms_tearfree_flip_abort);
+    if (!seq) {
+        /* Need to notify the DRI clients if a sequence wasn't allocated. Once a
+         * sequence is allocated, explicitly performing this cleanup isn't
+         * necessary since it's already done as part of aborting the sequence.
+         */
+        ms_tearfree_dri_abort_all(crtc);
+        goto no_flip;
+    }
+
+    /* Copy the damage to the back buffer and then flip it at the vblank */
+    drmmode_copy_damage(crtc, trf->buf[idx].px, &trf->buf[idx].dmg, TRUE);
+    if (do_queue_flip_on_crtc(screen, crtc, DRM_MODE_PAGE_FLIP_EVENT,
+                              seq, trf->buf[idx].fb_id, 0, 0))
+        goto no_flip;
+
+    trf->flip_seq = seq;
+    return FALSE;
+
+no_flip:
+    xf86DrvMsg(crtc->scrn->scrnIndex, X_WARNING,
+               "TearFree flip failed, rendering frame without TearFree\n");
+    drmmode_copy_damage(crtc, trf->buf[idx ^ 1].px,
+                        &trf->buf[idx ^ 1].dmg, FALSE);
+    return TRUE;
+}
 #endif
+
+Bool
+ms_tearfree_is_active_on_crtc(xf86CrtcPtr crtc)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+    drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
+
+    /* If TearFree is enabled, XServer owns the VT, and the CRTC is active */
+    return trf->buf[0].px && crtc->scrn->vtSema && xf86_crtc_on(crtc);
+}
blob - 0c9ee2bc673348774327abaff49ccf7a6b764050
blob + 9213753547a71f764c25c89eb3afac84e72c50e7
--- xserver/hw/xfree86/drivers/modesetting/present.c
+++ xserver/hw/xfree86/drivers/modesetting/present.c
@@ -165,7 +165,14 @@ ms_present_abort_vblank(RRCrtcPtr crtc, uint64_t event
 {
     ScreenPtr screen = crtc->pScreen;
     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+#ifdef GLAMOR_HAS_GBM
+    xf86CrtcPtr xf86_crtc = crtc->devPrivate;
 
+    /* Check if this is a fake flip routed through TearFree and abort it */
+    if (ms_tearfree_dri_abort(xf86_crtc, ms_present_event_match, &event_id))
+        return;
+#endif
+
     ms_drm_abort(scrn, ms_present_event_match, &event_id);
 }
 
@@ -318,14 +325,34 @@ ms_present_check_flip(RRCrtcPtr crtc,
     modesettingPtr ms = modesettingPTR(scrn);
 
     if (ms->drmmode.sprites_visible > 0)
-        return FALSE;
+        goto no_flip;
 
+    if (ms->drmmode.pending_modeset)
+        goto no_flip;
+
     if(!ms_present_check_unflip(crtc, window, pixmap, sync_flip, reason))
-        return FALSE;
+        goto no_flip;
 
     ms->flip_window = window;
 
     return TRUE;
+no_flip:
+    /* Export some info about TearFree if Present can't flip anyway */
+    if (reason) {
+        xf86CrtcPtr xf86_crtc = crtc->devPrivate;
+        drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
+        drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
+
+        if (ms_tearfree_is_active_on_crtc(xf86_crtc)) {
+            if (trf->flip_seq)
+                /* The driver has a TearFree flip pending */
+                *reason = PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING;
+            else
+                /* The driver uses TearFree flips and there's no flip pending */
+                *reason = PRESENT_FLIP_REASON_DRIVER_TEARFREE;
+        }
+    }
+    return FALSE;
 }
 
 /*
@@ -343,11 +370,12 @@ ms_present_flip(RRCrtcPtr crtc,
     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
     modesettingPtr ms = modesettingPTR(scrn);
     xf86CrtcPtr xf86_crtc = crtc->devPrivate;
-    drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
     Bool ret;
     struct ms_present_vblank_event *event;
 
-    if (!ms_present_check_flip(crtc, ms->flip_window, pixmap, sync_flip, NULL))
+    /* A NULL pixmap means this is a fake flip to be routed through TearFree */
+    if (pixmap &&
+        !ms_present_check_flip(crtc, ms->flip_window, pixmap, sync_flip, NULL))
         return FALSE;
 
     event = calloc(1, sizeof(struct ms_present_vblank_event));
@@ -360,6 +388,12 @@ ms_present_flip(RRCrtcPtr crtc,
     event->event_id = event_id;
     event->unflip = FALSE;
 
+    /* Register the fake flip (indicated by a NULL pixmap) with TearFree */
+    if (!pixmap)
+        return ms_do_pageflip(screen, NULL, event, xf86_crtc, FALSE,
+                              ms_present_flip_handler, ms_present_flip_abort,
+                              "Present-TearFree-flip");
+
     /* A window can only flip if it covers the entire X screen.
      * Only one window can flip at a time.
      *
@@ -371,7 +405,7 @@ ms_present_flip(RRCrtcPtr crtc,
         ms_present_set_screen_vrr(scrn, TRUE);
     }
 
-    ret = ms_do_pageflip(screen, pixmap, event, drmmode_crtc->vblank_pipe, !sync_flip,
+    ret = ms_do_pageflip(screen, pixmap, event, xf86_crtc, !sync_flip,
                          ms_present_flip_handler, ms_present_flip_abort,
                          "Present-flip");
     if (ret)
@@ -404,7 +438,7 @@ ms_present_unflip(ScreenPtr screen, uint64_t event_id)
         event->event_id = event_id;
         event->unflip = TRUE;
 
-        if (ms_do_pageflip(screen, pixmap, event, -1, FALSE,
+        if (ms_do_pageflip(screen, pixmap, event, NULL, FALSE,
                            ms_present_flip_handler, ms_present_flip_abort,
                            "Present-unflip")) {
             return;
blob - ea9e7a88c5dfb55b553d66e34070f99e3fda44ac
blob + c7aeb9f1697f8dfa2eaa025069d86339c6cef005
--- xserver/hw/xfree86/drivers/modesetting/vblank.c
+++ xserver/hw/xfree86/drivers/modesetting/vblank.c
@@ -260,6 +260,51 @@ ms_get_kernel_ust_msc(xf86CrtcPtr crtc,
     }
 }
 
+static void
+ms_drm_set_seq_msc(uint32_t seq, uint64_t msc)
+{
+    struct ms_drm_queue *q;
+
+    xorg_list_for_each_entry(q, &ms_drm_queue, list) {
+        if (q->seq == seq) {
+            q->msc = msc;
+            break;
+        }
+    }
+}
+
+static void
+ms_drm_set_seq_queued(uint32_t seq, uint64_t msc)
+{
+    drmmode_crtc_private_ptr drmmode_crtc;
+    struct ms_drm_queue *q;
+
+    xorg_list_for_each_entry(q, &ms_drm_queue, list) {
+        if (q->seq == seq) {
+            drmmode_crtc = q->crtc->driver_private;
+            if (msc < drmmode_crtc->next_msc)
+                drmmode_crtc->next_msc = msc;
+            q->msc = msc;
+            q->kernel_queued = TRUE;
+            break;
+        }
+    }
+}
+
+static Bool
+ms_queue_coalesce(xf86CrtcPtr crtc, uint32_t seq, uint64_t msc)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+
+    /* If the next MSC is too late, then this event can't be coalesced */
+    if (msc < drmmode_crtc->next_msc)
+        return FALSE;
+
+    /* Set the target MSC on this sequence number */
+    ms_drm_set_seq_msc(seq, msc);
+    return TRUE;
+}
+
 Bool
 ms_queue_vblank(xf86CrtcPtr crtc, ms_queue_flag flags,
                 uint64_t msc, uint64_t *msc_queued, uint32_t seq)
@@ -271,6 +316,10 @@ ms_queue_vblank(xf86CrtcPtr crtc, ms_queue_flag flags,
     drmVBlank vbl;
     int ret;
 
+    /* Try coalescing this event into another to avoid event queue exhaustion */
+    if (flags == MS_QUEUE_ABSOLUTE && ms_queue_coalesce(crtc, seq, msc))
+        return TRUE;
+
     for (;;) {
         /* Queue an event at the specified sequence */
         if (ms->has_queue_sequence || !ms->tried_queue_sequence) {
@@ -287,8 +336,10 @@ ms_queue_vblank(xf86CrtcPtr crtc, ms_queue_flag flags,
             ret = drmCrtcQueueSequence(ms->fd, drmmode_crtc->mode_crtc->crtc_id,
                                        drm_flags, msc, &kernel_queued, seq);
             if (ret == 0) {
+                msc = ms_kernel_msc_to_crtc_msc(crtc, kernel_queued, TRUE);
+                ms_drm_set_seq_queued(seq, msc);
                 if (msc_queued)
-                    *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, kernel_queued, TRUE);
+                    *msc_queued = msc;
                 ms->has_queue_sequence = TRUE;
                 return TRUE;
             }
@@ -310,8 +361,10 @@ ms_queue_vblank(xf86CrtcPtr crtc, ms_queue_flag flags,
         vbl.request.signal = seq;
         ret = drmWaitVBlank(ms->fd, &vbl);
         if (ret == 0) {
+            msc = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence, FALSE);
+            ms_drm_set_seq_queued(seq, msc);
             if (msc_queued)
-                *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence, FALSE);
+                *msc_queued = msc;
             return TRUE;
         }
     check:
@@ -418,13 +471,15 @@ ms_drm_queue_alloc(xf86CrtcPtr crtc,
     if (!ms_drm_seq)
         ++ms_drm_seq;
     q->seq = ms_drm_seq++;
+    q->msc = UINT64_MAX;
     q->scrn = scrn;
     q->crtc = crtc;
     q->data = data;
     q->handler = handler;
     q->abort = abort;
 
-    xorg_list_add(&q->list, &ms_drm_queue);
+    /* Keep the list formatted in ascending order of sequence number */
+    xorg_list_append(&q->list, &ms_drm_queue);
 
     return q->seq;
 }
@@ -437,9 +492,18 @@ ms_drm_queue_alloc(xf86CrtcPtr crtc,
 static void
 ms_drm_abort_one(struct ms_drm_queue *q)
 {
+    if (q->aborted)
+        return;
+
+    /* Don't remove vblank events if they were queued in the kernel */
+    if (q->kernel_queued) {
+        q->abort(q->data);
+        q->aborted = TRUE;
+    } else {
         xorg_list_del(&q->list);
         q->abort(q->data);
         free(q);
+    }
 }
 
 /**
@@ -500,18 +564,66 @@ ms_drm_sequence_handler(int fd, uint64_t frame, uint64
 {
     struct ms_drm_queue *q, *tmp;
     uint32_t seq = (uint32_t) user_data;
+    xf86CrtcPtr crtc = NULL;
+    drmmode_crtc_private_ptr drmmode_crtc;
+    uint64_t msc, next_msc = UINT64_MAX;
 
-    xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
+    /* Handle the seq for this event first in order to get the CRTC */
+    xorg_list_for_each_entry(q, &ms_drm_queue, list) {
         if (q->seq == seq) {
-            uint64_t msc;
+            crtc = q->crtc;
+            msc = ms_kernel_msc_to_crtc_msc(crtc, frame, is64bit);
 
-            msc = ms_kernel_msc_to_crtc_msc(q->crtc, frame, is64bit);
+            /* Write the current MSC to this event to ensure its handler runs in
+             * the loop below. This is done because we don't want to run the
+             * handler right now, since we need to ensure all events are handled
+             * in FIFO order with respect to one another. Otherwise, if this
+             * event were handled first just because it was queued to the
+             * kernel, it could run before older events expiring at this MSC.
+             */
+            q->msc = msc;
+            break;
+        }
+    }
+
+    if (!crtc)
+        return;
+
+    /* Now run all of the vblank events for this CRTC with an expired MSC */
+    xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
+        if (q->crtc == crtc && q->msc <= msc) {
             xorg_list_del(&q->list);
-            q->handler(msc, ns / 1000, q->data);
+            if (!q->aborted)
+                q->handler(msc, ns / 1000, q->data);
             free(q);
-            break;
         }
     }
+
+    /* Find this CRTC's next queued MSC and next non-queued MSC to be handled */
+    msc = UINT64_MAX;
+    xorg_list_for_each_entry(q, &ms_drm_queue, list) {
+        if (q->crtc == crtc) {
+            if (q->kernel_queued) {
+                if (q->msc < next_msc)
+                    next_msc = q->msc;
+            } else if (q->msc < msc) {
+                msc = q->msc;
+                seq = q->seq;
+            }
+        }
+    }
+
+    /* Queue an event if the next queued MSC isn't soon enough */
+    drmmode_crtc = crtc->driver_private;
+    drmmode_crtc->next_msc = next_msc;
+    if (msc < next_msc && !ms_queue_vblank(crtc, MS_QUEUE_ABSOLUTE, msc, NULL, seq)) {
+        xf86DrvMsg(crtc->scrn->scrnIndex, X_WARNING,
+                   "failed to queue next vblank event, aborting lost events\n");
+        xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
+            if (q->crtc == crtc && q->msc < next_msc)
+                ms_drm_abort_one(q);
+        }
+    }
 }
 
 static void
@@ -531,6 +643,12 @@ ms_drm_handler(int fd, uint32_t frame, uint32_t sec, u
 }
 
 Bool
+ms_drm_queue_is_empty(void)
+{
+    return xorg_list_is_empty(&ms_drm_queue);
+}
+
+Bool
 ms_vblank_screen_init(ScreenPtr screen)
 {
     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
blob - 1e87f0ea0de9168550071eba75953cf94c825118
blob + 0ca9f5dd0d54ed02576068816deb1ad5049bc164
--- xserver/hw/xfree86/modes/xf86Crtc.h
+++ xserver/hw/xfree86/modes/xf86Crtc.h
@@ -912,6 +912,12 @@ extern _X_EXPORT void
 extern _X_EXPORT Bool
  xf86CrtcRotate(xf86CrtcPtr crtc);
 
+extern _X_EXPORT void
+ xf86RotateCrtcRedisplay(xf86CrtcPtr crtc, PixmapPtr dst_pixmap,
+                         DrawableRec *src_drawable, RegionPtr region,
+                         Bool transform_src);
+
+
 /*
  * Clean up any rotation data, used when a crtc is turned off
  * as well as when rotation is disabled.
blob - ea9c43c0f237acffaa808b8826d25a489d0bb48d
blob + 944a01b1c01ba9107e7e2bbcccd8bb2aa12fe29d
--- xserver/hw/xfree86/modes/xf86Rotate.c
+++ xserver/hw/xfree86/modes/xf86Rotate.c
@@ -39,13 +39,13 @@
 #include "X11/extensions/dpmsconst.h"
 #include "X11/Xatom.h"
 
-static void
-xf86RotateCrtcRedisplay(xf86CrtcPtr crtc, RegionPtr region)
+void
+xf86RotateCrtcRedisplay(xf86CrtcPtr crtc, PixmapPtr dst_pixmap,
+                        DrawableRec *src_drawable, RegionPtr region,
+                        Bool transform_src)
 {
     ScrnInfoPtr scrn = crtc->scrn;
     ScreenPtr screen = scrn->pScreen;
-    WindowPtr root = screen->root;
-    PixmapPtr dst_pixmap = crtc->rotatedPixmap;
     PictFormatPtr format = PictureWindowFormat(screen->root);
     int error;
     PicturePtr src, dst;
@@ -57,7 +57,7 @@ xf86RotateCrtcRedisplay(xf86CrtcPtr crtc, RegionPtr re
         return;
 
     src = CreatePicture(None,
-                        &root->drawable,
+                        src_drawable,
                         format,
                         CPSubwindowMode,
                         &include_inferiors, serverClient, &error);
@@ -70,9 +70,11 @@ xf86RotateCrtcRedisplay(xf86CrtcPtr crtc, RegionPtr re
     if (!dst)
         return;
 
-    error = SetPictureTransform(src, &crtc->crtc_to_framebuffer);
-    if (error)
-        return;
+    if (transform_src) {
+        error = SetPictureTransform(src, &crtc->crtc_to_framebuffer);
+        if (error)
+            return;
+    }
     if (crtc->transform_in_use && crtc->filter)
         SetPicturePictFilter(src, crtc->filter, crtc->params, crtc->nparams);
 
@@ -205,7 +207,9 @@ xf86RotateRedisplay(ScreenPtr pScreen)
 
                 /* update damaged region */
                 if (RegionNotEmpty(&crtc_damage))
-                    xf86RotateCrtcRedisplay(crtc, &crtc_damage);
+                    xf86RotateCrtcRedisplay(crtc, crtc->rotatedPixmap,
+                                            &pScreen->root->drawable,
+                                            &crtc_damage, TRUE);
 
                 RegionUninit(&crtc_damage);
             }
blob - 7144bfb30e8ba1fcd1b21e21e33db31d24460080
blob + e251690d5620b3d2cf969a1097349624a15bd392
--- xserver/include/pixmap.h
+++ xserver/include/pixmap.h
@@ -134,4 +134,9 @@ PixmapStopDirtyTracking(DrawablePtr src, PixmapPtr sla
 extern _X_EXPORT Bool
 PixmapSyncDirtyHelper(PixmapDirtyUpdatePtr dirty);
 
+extern _X_EXPORT void
+PixmapDirtyCopyArea(PixmapPtr dst, DrawablePtr src,
+                    int x, int y, int dst_x, int dst_y,
+                    RegionPtr dirty_region);
+
 #endif                          /* PIXMAP_H */
blob - d41b36033f93fa9b8e0ff02c13f6fa9ec8524208
blob + c6762cecc92fdce4a293a15d93c23ea6c2b9a0b7
--- xserver/present/present.h
+++ xserver/present/present.h
@@ -29,7 +29,15 @@
 
 typedef enum {
     PRESENT_FLIP_REASON_UNKNOWN,
-    PRESENT_FLIP_REASON_BUFFER_FORMAT
+    PRESENT_FLIP_REASON_BUFFER_FORMAT,
+
+    /* Don't add new flip reasons after the TearFree ones, since it's expected
+     * that the TearFree reasons are the highest ones in order to allow doing
+     * `reason >= PRESENT_FLIP_REASON_DRIVER_TEARFREE` to check if a reason is
+     * PRESENT_FLIP_REASON_DRIVER_TEARFREE{_FLIPPING}.
+     */
+    PRESENT_FLIP_REASON_DRIVER_TEARFREE,
+    PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING
 } PresentFlipReason;
 
 typedef struct present_vblank present_vblank_rec, *present_vblank_ptr;
@@ -149,6 +157,9 @@ present_event_notify(uint64_t event_id, uint64_t ust, 
 extern _X_EXPORT Bool
 present_screen_init(ScreenPtr screen, present_screen_info_ptr info);
 
+extern _X_EXPORT void
+present_check_flips(WindowPtr window);
+
 typedef void (*present_complete_notify_proc)(WindowPtr window,
                                              CARD8 kind,
                                              CARD8 mode,
blob - 239055bc10d8c934555356c4213cda37dbff711e
blob + de8cd09c20a63c66d8be117c5179ecf8f1789998
--- xserver/present/present_scmd.c
+++ xserver/present/present_scmd.c
@@ -71,6 +71,7 @@ present_check_flip(RRCrtcPtr            crtc,
     PixmapPtr                   window_pixmap;
     WindowPtr                   root = screen->root;
     present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+    PresentFlipReason           tmp_reason = PRESENT_FLIP_REASON_UNKNOWN;
 
     if (crtc) {
        screen_priv = present_screen_priv(crtc->pScreen);
@@ -91,6 +92,27 @@ present_check_flip(RRCrtcPtr            crtc,
     if (!screen_priv->info->flip)
         return FALSE;
 
+    /* Ask the driver for permission. Do this now to see if there's TearFree. */
+    if (screen_priv->info->version >= 1 && screen_priv->info->check_flip2) {
+        if (!(*screen_priv->info->check_flip2) (crtc, window, pixmap, sync_flip, &tmp_reason)) {
+            DebugPresent(("\td %08" PRIx32 " -> %08" PRIx32 "\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
+            /* It's fine to return now unless the page flip failure reason is
+             * PRESENT_FLIP_REASON_BUFFER_FORMAT; we must only output that
+             * reason if all the other checks pass.
+             */
+            if (!reason || tmp_reason != PRESENT_FLIP_REASON_BUFFER_FORMAT) {
+                if (reason)
+                    *reason = tmp_reason;
+                return FALSE;
+            }
+        }
+    } else if (screen_priv->info->check_flip) {
+        if (!(*screen_priv->info->check_flip) (crtc, window, pixmap, sync_flip)) {
+            DebugPresent(("\td %08" PRIx32 " -> %08" PRIx32 "\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
+            return FALSE;
+        }
+    }
+
     /* Make sure the window hasn't been redirected with Composite */
     window_pixmap = screen->GetWindowPixmap(window);
     if (window_pixmap != screen->GetScreenPixmap(screen) &&
@@ -123,17 +145,10 @@ present_check_flip(RRCrtcPtr            crtc,
         return FALSE;
     }
 
-    /* Ask the driver for permission */
-    if (screen_priv->info->version >= 1 && screen_priv->info->check_flip2) {
-        if (!(*screen_priv->info->check_flip2) (crtc, window, pixmap, sync_flip, reason)) {
-            DebugPresent(("\td %08" PRIx32 " -> %08" PRIx32 "\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
-            return FALSE;
-        }
-    } else if (screen_priv->info->check_flip) {
-        if (!(*screen_priv->info->check_flip) (crtc, window, pixmap, sync_flip)) {
-            DebugPresent(("\td %08" PRIx32 " -> %08" PRIx32 "\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
-            return FALSE;
-        }
+    if (tmp_reason == PRESENT_FLIP_REASON_BUFFER_FORMAT) {
+        if (reason)
+            *reason = tmp_reason;
+        return FALSE;
     }
 
     return TRUE;
@@ -456,7 +471,9 @@ present_check_flip_window (WindowPtr window)
     xorg_list_for_each_entry(vblank, &window_priv->vblank, window_list) {
         if (vblank->queued && vblank->flip && !present_check_flip(vblank->crtc, window, vblank->pixmap, vblank->sync_flip, NULL, 0, 0, &reason)) {
             vblank->flip = FALSE;
-            vblank->reason = reason;
+            /* Don't spuriously flag this as a TearFree presentation */
+            if (reason < PRESENT_FLIP_REASON_DRIVER_TEARFREE)
+                vblank->reason = reason;
             if (vblank->sync_flip)
                 vblank->exec_msc = vblank->target_msc;
         }
@@ -560,7 +577,9 @@ present_execute(present_vblank_ptr vblank, uint64_t us
     xorg_list_del(&vblank->window_list);
     vblank->queued = FALSE;
 
-    if (vblank->pixmap && vblank->window) {
+    if (vblank->pixmap && vblank->window &&
+        (vblank->reason < PRESENT_FLIP_REASON_DRIVER_TEARFREE ||
+         vblank->exec_msc != vblank->target_msc)) {
 
         if (vblank->flip) {
 
@@ -627,6 +646,51 @@ present_execute(present_vblank_ptr vblank, uint64_t us
 
         present_execute_copy(vblank, crtc_msc);
 
+        /* With TearFree, there's no way to tell exactly when the presentation
+         * will be visible except by waiting for a notification from the kernel
+         * driver indicating that the page flip is complete. This is because the
+         * CRTC's MSC can change while the target MSC is calculated and even
+         * while the page flip IOCTL is sent to the kernel due to scheduling
+         * delays and/or unfortunate timing. Even worse, a page flip isn't
+         * actually guaranteed to be finished after one vblank; it may be
+         * several MSCs until a flip actually finishes depending on delays and
+         * load in hardware.
+         *
+         * So, to get a notification from the driver with TearFree active, the
+         * driver expects a present_flip() call with a NULL pixmap to indicate
+         * that this is a fake flip for a pixmap that's already been copied to
+         * the primary scanout, which will then be flipped by TearFree. TearFree
+         * will then send a notification once the flip containing this pixmap is
+         * complete.
+         *
+         * If the fake flip attempt fails, then fall back to just enqueuing a
+         * vblank event targeting the next MSC.
+         */
+        if (!vblank->queued &&
+            vblank->reason >= PRESENT_FLIP_REASON_DRIVER_TEARFREE) {
+            uint64_t completion_msc = crtc_msc + 1;
+
+            /* If TearFree is already flipping then the presentation will be
+             * visible at the *next* next vblank. This calculation only matters
+             * for the vblank event fallback.
+             */
+            if (vblank->reason == PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING &&
+                vblank->exec_msc < crtc_msc)
+                    completion_msc++;
+
+            /* Try the fake flip first and then fall back to a vblank event */
+            if (present_flip(vblank->crtc, vblank->event_id, 0, NULL, TRUE) ||
+                Success == screen_priv->queue_vblank(screen,
+                                                     window,
+                                                     vblank->crtc,
+                                                     vblank->event_id,
+                                                     completion_msc)) {
+                /* Ensure present_execute_post() runs at the next execution */
+                vblank->exec_msc = vblank->target_msc;
+                vblank->queued = TRUE;
+            }
+        }
+
         if (vblank->queued) {
             xorg_list_add(&vblank->event_queue, &present_exec_queue);
             xorg_list_append(&vblank->window_list,
@@ -739,6 +803,11 @@ present_scmd_pixmap(WindowPtr window,
             if (vblank->crtc != target_crtc || vblank->target_msc != target_msc)
                 continue;
 
+            /* Too late to abort now if TearFree execution already happened */
+            if (vblank->reason >= PRESENT_FLIP_REASON_DRIVER_TEARFREE &&
+                vblank->exec_msc == vblank->target_msc)
+                continue;
+
             present_vblank_scrap(vblank);
             if (vblank->flip_ready)
                 present_re_execute(vblank);
@@ -767,7 +836,12 @@ present_scmd_pixmap(WindowPtr window,
 
     vblank->event_id = ++present_scmd_event_id;
 
-    if (vblank->flip && vblank->sync_flip)
+    /* The soonest presentation is crtc_msc+2 if TearFree is already flipping */
+    if (vblank->reason == PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING &&
+        !msc_is_after(vblank->exec_msc, crtc_msc + 1))
+        vblank->exec_msc -= 2;
+    else if (vblank->reason >= PRESENT_FLIP_REASON_DRIVER_TEARFREE ||
+             (vblank->flip && vblank->sync_flip))
         vblank->exec_msc--;
 
     xorg_list_append(&vblank->event_queue, &present_exec_queue);
blob - 2c29aafd23509143b491cc9c6209234faa026b33
blob + ef56ff779abfd97994a52721cce4b42a79798d99
--- xserver/present/present_screen.c
+++ xserver/present/present_screen.c
@@ -191,6 +191,26 @@ present_screen_priv_init(ScreenPtr screen)
     return screen_priv;
 }
 
+static int
+check_flip_visit(WindowPtr window, void *data)
+{
+    ScreenPtr screen = window->drawable.pScreen;
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+
+    if (!screen_priv)
+        return WT_DONTWALKCHILDREN;
+
+    screen_priv->check_flip_window(window);
+
+    return WT_WALKCHILDREN;
+}
+
+void
+present_check_flips(WindowPtr window)
+{
+    TraverseTree(window, check_flip_visit, NULL);
+}
+
 /*
  * Initialize a screen for use with present in default screen flip mode (scmd)
  */
-- 
Christian "naddy" Weisgerber                          naddy@mips.inka.de