[Merge] ~jeff250/ubuntu/+source/xorg-server:tearfree into ubuntu/+source/xorg-server:ubuntu/devel

Simon Chopin mp+457781 at code.launchpad.net
Wed Jan 10 09:30:51 UTC 2024


Hi! Thanks for the patches and helping improve Ubuntu.

As a random Core Dev I don't feel particularly comfortable sponsoring these changes without the opinion of someone at least a bit knowledgeable about xorg, so I pinged someone that's presumably a better fit on IRC so that they can have a look at it. Meanwhile I still do have a couple of comments about the metadata aspects to help future-proofing the contribution.

Cheers!

Diff comments:

> diff --git a/debian/changelog b/debian/changelog
> index 5858e27..d4be8f6 100644
> --- a/debian/changelog
> +++ b/debian/changelog
> @@ -1,3 +1,11 @@
> +xorg-server (2:21.1.10-1ubuntu2) noble; urgency=medium
> +
> +  * tearfree-modesetting.patch: Enable "TearFree" modesetting driver option.
> +  * tearfree-present-fixes.patch: Fix inaccurate PresentCompleteNotify timing
> +    for TearFree.

Since you don't have a bug reference in the changelog, it'd help to have a hint of the reason for the change in addition to the description of the patches.

Maybe something like:
* Backport opt-in TearFree mode in the modesetting driver
  - tearfree-modesetting.patch: Enable "TearFree" modesetting driver option.
  - tearfree-present-fixes.patch: Fix inaccurate PresentCompleteNotify timing
    for TearFree.

> +
> + -- Jeffrey Knockel <jeff at jeffreyknockel.com>  Mon, 18 Dec 2023 13:18:24 -0500
> +
>  xorg-server (2:21.1.10-1ubuntu1) noble; urgency=medium
>  
>    * Merge from Debian.
> diff --git a/debian/patches/tearfree-modesetting.patch b/debian/patches/tearfree-modesetting.patch
> new file mode 100644
> index 0000000..f17567a
> --- /dev/null
> +++ b/debian/patches/tearfree-modesetting.patch
> @@ -0,0 +1,1380 @@
> +Enable "TearFree" option for modesetting driver.
> +Index: xorg-server-21.1.6/dix/pixmap.c

If possible, could your patches follow the DEP-3 patch tagging guidelines? It makes it much easier to track patches through time.

https://dep-team.pages.debian.net/deps/dep3/

Notably, the Origin, Bug, Bug-Ubuntu and Applied-Upstream fields are very useful to know when updating the package to newer upstream versions, as it provides a lot of the necessary context.

For Bug-Ubuntu you can use a link to this MP.

> +===================================================================
> +--- xorg-server-21.1.6.orig/dix/pixmap.c	2022-12-19 05:53:03.000000000 -0500
> ++++ xorg-server-21.1.6/dix/pixmap.c	2023-01-09 10:11:45.537724859 -0500
> +@@ -262,12 +262,11 @@
> +     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 @@
> +         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 @@
> +     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;
> +Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/driver.c
> +===================================================================
> +--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/driver.c	2022-12-19 05:53:03.000000000 -0500
> ++++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/driver.c	2023-01-09 10:11:45.537724859 -0500
> +@@ -145,6 +145,7 @@
> +     {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}
> + };
> + 
> +@@ -516,14 +517,15 @@
> + }
> + 
> + static int
> +-dispatch_dirty_region(ScrnInfoPtr scrn,
> +-                      PixmapPtr pixmap, DamagePtr damage, int fb_id)
> ++dispatch_damages(ScrnInfoPtr scrn, RegionPtr dirty, 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);
> +@@ -551,12 +553,98 @@
> +             }
> +         }
> + 
> ++        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,
> ++                      PixmapPtr pixmap, DamagePtr damage, int fb_id)
> ++{
> ++    return dispatch_damages(scrn, DamageRegion(damage), 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, &region, 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;
> ++
> ++        /* Skip disabled CRTCs and those which aren't using TearFree */
> ++        if (!trf->buf[0].px || !crtc->scrn->vtSema || !xf86_crtc_on(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, &trf->buf[trf->back_idx ^ 1].dmg,
> ++                             NULL, trf->buf[trf->back_idx ^ 1].fb_id);
> ++            RegionEmpty(&trf->buf[trf->back_idx ^ 1].dmg);
> ++        }
> ++    }
> ++#endif
> ++}
> ++
> + static void
> + dispatch_dirty(ScreenPtr pScreen)
> + {
> +@@ -568,12 +656,9 @@
> + 
> +     ret = dispatch_dirty_region(scrn, pixmap, ms->damage, fb_id);
> +     if (ret == -EINVAL || ret == -ENOSYS) {
> +-        ms->dirty_enabled = FALSE;
> +         DamageUnregister(ms->damage);
> +         DamageDestroy(ms->damage);
> +         ms->damage = NULL;
> +-        xf86DrvMsg(scrn->scrnIndex, X_INFO,
> +-                   "Disabling kernel dirty updates, not required.\n");
> +         return;
> +     }
> + }
> +@@ -703,10 +788,13 @@
> +     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
> +@@ -1242,6 +1330,27 @@
> +         ms->atomic_modeset = FALSE;
> +     }
> + 
> ++    /* TearFree requires glamor and, if PageFlip is enabled, universal planes */
> ++    if (xf86ReturnOptValBool(ms->drmmode.Options, OPTION_TEARFREE, FALSE)) {
> ++        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)
> +@@ -1589,13 +1698,13 @@
> + 
> +     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 {
> +Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/driver.h
> +===================================================================
> +--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/driver.h	2022-12-19 05:53:03.000000000 -0500
> ++++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/driver.h	2023-01-09 10:11:45.541724797 -0500
> +@@ -61,6 +61,7 @@
> +     OPTION_VARIABLE_REFRESH,
> +     OPTION_USE_GAMMA_LUT,
> +     OPTION_ASYNC_FLIP_SECONDARIES,
> ++    OPTION_TEARFREE,
> + } modesettingOpts;
> + 
> + typedef struct
> +@@ -86,10 +87,13 @@
> +     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 {
> +@@ -238,6 +242,8 @@
> +                     ms_pageflip_abort_proc pageflip_abort,
> +                     const char *log_prefix);
> + 
> ++Bool ms_do_tearfree_flip(ScreenPtr screen, xf86CrtcPtr crtc);
> ++
> + #endif
> + 
> + int ms_flush_drm_events(ScreenPtr screen);
> +Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/drmmode_display.c
> +===================================================================
> +--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/drmmode_display.c	2023-01-09 10:11:15.278283797 -0500
> ++++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/drmmode_display.c	2023-01-09 10:11:45.541724797 -0500
> +@@ -632,6 +632,7 @@
> + {
> +     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;
> +@@ -646,6 +647,10 @@
> +             *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;
> +@@ -922,6 +927,10 @@
> +     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);
> + 
> +@@ -930,7 +939,8 @@
> + }
> + 
> + 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;
> +@@ -942,7 +952,7 @@
> +         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);
> +@@ -950,6 +960,26 @@
> +         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);
> + }
> +@@ -1548,6 +1578,90 @@
> + #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 Bool
> + drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
> +                        Rotation rotation, int x, int y)
> +@@ -1581,6 +1695,10 @@
> +         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,
> +@@ -1626,6 +1744,7 @@
> +         crtc->y = saved_y;
> +         crtc->rotation = saved_rotation;
> +         crtc->mode = saved_mode;
> ++        drmmode_create_tearfree_shadow(crtc);
> +     } else
> +         crtc->active = TRUE;
> + 
> +@@ -1931,33 +2050,42 @@
> + }
> + 
> + 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
> +@@ -1983,71 +2111,92 @@
> + 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);
> + 
> +-    rotate_pixmap = drmmode_create_pixmap_header(scrn->pScreen,
> +-                                                 width, height,
> +-                                                 scrn->depth,
> +-                                                 drmmode->kbpp,
> +-                                                 rotate_pitch,
> +-                                                 pPixData);
> ++    pixmap = drmmode_create_pixmap_header(scrn->pScreen,
> ++                                          width, height,
> ++                                          scrn->depth,
> ++                                          drmmode->kbpp,
> ++                                          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 pixmap;
> ++}
> ++
> ++static PixmapPtr
> ++drmmode_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
> ++{
> ++    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
> + 
> +-    return rotate_pixmap;
> ++    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;
> +@@ -2380,6 +2529,7 @@
> +     drmmode_crtc->drmmode = drmmode;
> +     drmmode_crtc->vblank_pipe = drmmode_crtc_vblank_pipe(num);
> +     xorg_list_init(&drmmode_crtc->mode_list);
> ++    drmmode_crtc->next_msc = UINT64_MAX;
> + 
> +     props = drmModeObjectGetProperties(drmmode->fd, mode_res->crtcs[num],
> +                                        DRM_MODE_OBJECT_CRTC);
> +@@ -4253,6 +4403,7 @@
> +         drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
> + 
> +         dumb_bo_destroy(drmmode->fd, drmmode_crtc->cursor_bo);
> ++        drmmode_destroy_tearfree_shadow(crtc);
> +     }
> + }
> + 
> +Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/drmmode_display.h
> +===================================================================
> +--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/drmmode_display.h	2022-12-19 05:53:03.000000000 -0500
> ++++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/drmmode_display.h	2023-01-09 10:11:45.541724797 -0500
> +@@ -135,6 +135,7 @@
> +     Bool async_flip_secondaries;
> +     Bool dri2_enable;
> +     Bool present_enable;
> ++    Bool tearfree_enable;
> + 
> +     uint32_t vrr_prop_id;
> +     Bool use_ctm;
> +@@ -167,6 +168,19 @@
> + } 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];
> ++    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;
> +@@ -184,11 +198,14 @@
> + 
> +     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.
> +      *
> +@@ -200,6 +217,8 @@
> +     uint64_t msc_high;
> +     /** @} */
> + 
> ++    uint64_t next_msc;
> ++
> +     Bool need_modeset;
> +     struct xorg_list mode_list;
> + 
> +@@ -308,8 +327,11 @@
> +                              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);
> + 
> + void drmmode_set_dpms(ScrnInfoPtr scrn, int PowerManagementMode, int flags);
> + void drmmode_crtc_set_vrr(xf86CrtcPtr crtc, Bool enabled);
> +Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/modesetting.man
> +===================================================================
> +--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/modesetting.man	2022-12-19 05:53:03.000000000 -0500
> ++++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/modesetting.man	2023-01-09 10:11:45.541724797 -0500
> +@@ -109,6 +109,17 @@
> + 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 off.
> ++.TP
> + .SH "SEE ALSO"
> + @xservername@(@appmansuffix@), @xconfigfile@(@filemansuffix@), Xserver(@appmansuffix@),
> + X(@miscmansuffix@)
> +Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/pageflip.c
> +===================================================================
> +--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/pageflip.c	2022-12-19 05:53:03.000000000 -0500
> ++++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/pageflip.c	2023-01-09 10:11:45.541724797 -0500
> +@@ -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 @@
> +     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,12 @@
> +     return 1;
> + }
> + 
> ++int
> ++ms_flush_drm_events(ScreenPtr screen)
> ++{
> ++    return ms_flush_drm_events_timeout(screen, 0);
> ++}
> ++
> + #ifdef GLAMOR_HAS_GBM
> + 
> + /*
> +@@ -160,11 +166,32 @@
> + }
> + 
> + 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 {
> +@@ -205,20 +232,9 @@
> +     /* 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;
> +-        }
> +-
> +-        /* We flushed some events, so try again. */
> +-        xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue retry\n");
> +-    }
> ++    if (do_queue_flip_on_crtc(screen, crtc, flags, seq, ms->drmmode.fb_id,
> ++                              crtc->x, crtc->y))
> ++        return QUEUE_FLIP_DRM_FLUSH_FAILED;
> + 
> +     /* The page flip succeeded. */
> +     return QUEUE_FLIP_SUCCESS;
> +@@ -465,4 +481,50 @@
> + #endif /* GLAMOR_HAS_GBM */
> + }
> + 
> ++static void
> ++ms_tearfree_flip_abort(void *data)
> ++{
> ++    drmmode_tearfree_ptr trf = data;
> ++
> ++    trf->flip_seq = 0;
> ++}
> ++
> ++static void
> ++ms_tearfree_flip_handler(uint64_t msc, uint64_t usec, void *data)
> ++{
> ++    drmmode_tearfree_ptr trf = data;
> ++
> ++    /* Swap the buffers and complete the flip */
> ++    trf->back_idx ^= 1;
> ++    trf->flip_seq = 0;
> ++}
> ++
> ++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, trf, ms_tearfree_flip_handler,
> ++                             ms_tearfree_flip_abort);
> ++    if (!seq)
> ++        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
> +Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/present.c
> +===================================================================
> +--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/present.c	2022-12-19 05:53:03.000000000 -0500
> ++++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/present.c	2023-01-09 10:11:45.541724797 -0500
> +@@ -318,14 +318,32 @@
> +     modesettingPtr ms = modesettingPTR(scrn);
> + 
> +     if (ms->drmmode.sprites_visible > 0)
> +-        return FALSE;
> ++        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 && ms->drmmode.tearfree_enable) {
> ++        xf86CrtcPtr xf86_crtc = crtc->devPrivate;
> ++        drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
> ++        drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
> ++
> ++        if (trf->buf[0].px) {
> ++            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;
> + }
> + 
> + /*
> +Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/vblank.c
> +===================================================================
> +--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/vblank.c	2022-12-19 05:53:03.000000000 -0500
> ++++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/vblank.c	2023-01-09 10:11:45.541724797 -0500
> +@@ -260,6 +260,51 @@
> +     }
> + }
> + 
> ++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 @@
> +     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 @@
> +             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 @@
> +         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 @@
> +     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 @@
> + 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,61 @@
> + {
> +     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;
> +-
> +-            msc = ms_kernel_msc_to_crtc_msc(q->crtc, frame, is64bit);
> ++            crtc = q->crtc;
> ++            msc = ms_kernel_msc_to_crtc_msc(crtc, frame, is64bit);
> +             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;
> +         }
> +     }
> ++
> ++    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);
> ++            if (!q->aborted)
> ++                q->handler(msc, ns / 1000, q->data);
> ++            free(q);
> ++        }
> ++    }
> ++
> ++    /* 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
> +Index: xorg-server-21.1.6/hw/xfree86/modes/xf86Crtc.h
> +===================================================================
> +--- xorg-server-21.1.6.orig/hw/xfree86/modes/xf86Crtc.h	2022-12-19 05:53:03.000000000 -0500
> ++++ xorg-server-21.1.6/hw/xfree86/modes/xf86Crtc.h	2023-01-09 10:11:45.541724797 -0500
> +@@ -912,6 +912,11 @@
> + 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.
> +Index: xorg-server-21.1.6/hw/xfree86/modes/xf86Rotate.c
> +===================================================================
> +--- xorg-server-21.1.6.orig/hw/xfree86/modes/xf86Rotate.c	2022-12-19 05:53:03.000000000 -0500
> ++++ xorg-server-21.1.6/hw/xfree86/modes/xf86Rotate.c	2023-01-09 10:11:45.541724797 -0500
> +@@ -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 @@
> +         return;
> + 
> +     src = CreatePicture(None,
> +-                        &root->drawable,
> ++                        src_drawable,
> +                         format,
> +                         CPSubwindowMode,
> +                         &include_inferiors, serverClient, &error);
> +@@ -70,9 +70,11 @@
> +     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 @@
> + 
> +                 /* update damaged region */
> +                 if (RegionNotEmpty(&crtc_damage))
> +-                    xf86RotateCrtcRedisplay(crtc, &crtc_damage);
> ++                    xf86RotateCrtcRedisplay(crtc, crtc->rotatedPixmap,
> ++                                            &pScreen->root->drawable,
> ++                                            &crtc_damage, TRUE);
> + 
> +                 RegionUninit(&crtc_damage);
> +             }
> +Index: xorg-server-21.1.6/include/pixmap.h
> +===================================================================
> +--- xorg-server-21.1.6.orig/include/pixmap.h	2022-12-19 05:53:03.000000000 -0500
> ++++ xorg-server-21.1.6/include/pixmap.h	2023-01-09 10:11:45.541724797 -0500
> +@@ -134,4 +134,9 @@
> + 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 */
> +Index: xorg-server-21.1.6/present/present.h
> +===================================================================
> +--- xorg-server-21.1.6.orig/present/present.h	2022-12-19 05:53:03.000000000 -0500
> ++++ xorg-server-21.1.6/present/present.h	2023-01-09 10:11:45.541724797 -0500
> +@@ -29,7 +29,9 @@
> + 
> + typedef enum {
> +     PRESENT_FLIP_REASON_UNKNOWN,
> +-    PRESENT_FLIP_REASON_BUFFER_FORMAT
> ++    PRESENT_FLIP_REASON_BUFFER_FORMAT,
> ++    PRESENT_FLIP_REASON_DRIVER_TEARFREE,
> ++    PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING
> + } PresentFlipReason;
> + 
> + typedef struct present_vblank present_vblank_rec, *present_vblank_ptr;
> +Index: xorg-server-21.1.6/present/present_scmd.c
> +===================================================================
> +--- xorg-server-21.1.6.orig/present/present_scmd.c	2023-01-09 10:11:15.134286918 -0500
> ++++ xorg-server-21.1.6/present/present_scmd.c	2023-01-09 10:11:45.541724797 -0500
> +@@ -71,6 +71,7 @@
> +     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 @@
> +     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 @@
> +         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;
> +@@ -462,7 +477,9 @@
> +     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;
> +         }
> +@@ -543,6 +560,7 @@
> +     WindowPtr                   window = vblank->window;
> +     ScreenPtr                   screen = window->drawable.pScreen;
> +     present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
> ++    uint64_t                    completion_msc;
> +     if (vblank && vblank->crtc) {
> +         screen_priv=present_screen_priv(vblank->crtc->pScreen);
> +     }
> +@@ -566,7 +584,9 @@
> +     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) {
> + 
> +@@ -633,6 +653,30 @@
> + 
> +         present_execute_copy(vblank, crtc_msc);
> + 
> ++        /* The presentation will be visible at the next vblank with TearFree, so
> ++         * the PresentComplete notification needs to be sent at the next vblank.
> ++         * If TearFree is already flipping then the presentation will be visible
> ++         * at the *next* next vblank.
> ++         */
> ++        completion_msc = crtc_msc + 1;
> ++        switch (vblank->reason) {
> ++        case PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING:
> ++            if (vblank->exec_msc < crtc_msc)
> ++                completion_msc++;
> ++        case PRESENT_FLIP_REASON_DRIVER_TEARFREE:
> ++            if (Success == screen_priv->queue_vblank(screen,
> ++                                                     window,
> ++                                                     vblank->crtc,
> ++                                                     vblank->event_id,
> ++                                                     completion_msc)) {
> ++                /* Ensure present_execute_post() runs at the next MSC */
> ++                vblank->exec_msc = vblank->target_msc;
> ++                vblank->queued = TRUE;
> ++            }
> ++        default:
> ++            break;
> ++        }
> ++
> +         if (vblank->queued) {
> +             xorg_list_add(&vblank->event_queue, &present_exec_queue);
> +             xorg_list_append(&vblank->window_list,
> +@@ -745,6 +789,11 @@
> +             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);
> +@@ -773,7 +822,12 @@
> + 
> +     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);


-- 
https://code.launchpad.net/~jeff250/ubuntu/+source/xorg-server/+git/xorg-server/+merge/457781
Your team Ubuntu Sponsors is requested to review the proposed merge of ~jeff250/ubuntu/+source/xorg-server:tearfree into ubuntu/+source/xorg-server:ubuntu/devel.




More information about the Ubuntu-sponsors mailing list