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

Proposed by Jeffrey Knockel
Status: Work in progress
Proposed branch: ~jeff250/ubuntu/+source/xorg-server:tearfree
Merge into: ubuntu/+source/xorg-server:ubuntu/devel
Diff against target: 2058 lines (+2030/-0)
4 files modified
debian/changelog (+9/-0)
debian/patches/series (+4/-0)
debian/patches/tearfree-modesetting.patch (+1385/-0)
debian/patches/tearfree-present-fixes.patch (+632/-0)
Reviewer Review Type Date Requested Status
Timo Aaltonen (community) Disapprove
Ubuntu Sponsors Pending
git-ubuntu import Pending
Review via email: mp+457781@code.launchpad.net

Description of the change

While the Xorg server can eliminate tearing in many cases, in others, such as in the absence of a compositor or in the presence of xrandr rotation or scaling, tearing may still be present (see, e.g., [1,2,3]). To address these cases, this merge request contains Sultan Alsawaf's patches from [4] (specifically, from [5]) and from [6] to implement "TearFree" mode in the modesetting driver. "TearFree" mode is considered mature and robust by upstream and has since been enabled by default in the modesetting driver [7], and the feature is similar to the "TearFree" modes already implemented in other DDX drivers. However, while this merge request implements "TearFree" mode for the modesetting driver, this merge request does not yet include upstream's patches to enable the mode by default. Instead, enabling "TearFree" in the modesetting driver will still require something such as the following added to xorg.conf:

Section "Device"
    Identifier "Graphics Adapter"
    Driver "modesetting"
    Option "TearFree" "true"
EndSection

[1] https://bugs.launchpad.net/xorg-server/+bug/1754284
[2] https://bugs.launchpad.net/xorg-server/+bug/1846398
[3] https://bugs.launchpad.net/xorg-server/+bug/1853094
[4] https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1006
[5] https://github.com/kerneltoast/xserver/tree/new-tearfree-21.1.5
[6] https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1042
[7] https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1224

To post a comment you must log in.
Revision history for this message
Simon Chopin (schopin) wrote :

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!

Revision history for this message
Jeffrey Knockel (jeff250) wrote :

> 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!

Thanks for your feedback. I have addressed your comments in the latest force-push. Hopefully it follows guidelines now. :) I also look forward to hearing what the other developer has to say.

Revision history for this message
Paride Legovini (paride) wrote :

Like Simon before me, I don't feel fully comfortable in merging this big diff as I have no experience working with the xorg packages.

I pointed out a couple of minor issues with the DEP-3 headers of the patches you added, should you need a reference for the syntax of those headers see [1]. Also ideally please split the diff in separate commits (one per added patch, plus one for the d/changelog changes).

Once you find or file an Ubuntu bug describing what you're going to fix here, please reference it in the new d/changelog entry using the `LP: #nnnnnn` syntax. This way Launchpad will auto-update the bug after the package gets uploaded.

A couple of extra questions:

1. In case you are familiar with Xorg's release schedule, when do you think these patches will be part of a new release?

2. Is there a Debian bug about the issue?

I'll try to get the attention of somebody more knowledgeable with Xorg.

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

Revision history for this message
Jeffrey Knockel (jeff250) wrote :

> Like Simon before me, I don't feel fully comfortable in merging this big diff
> as I have no experience working with the xorg packages.
>
> I pointed out a couple of minor issues with the DEP-3 headers of the patches
> you added, should you need a reference for the syntax of those headers see
> [1]. Also ideally please split the diff in separate commits (one per added
> patch, plus one for the d/changelog changes).

Thanks, I will address these issues.

> Once you find or file an Ubuntu bug describing what you're going to fix here,
> please reference it in the new d/changelog entry using the `LP: #nnnnnn`
> syntax. This way Launchpad will auto-update the bug after the package gets
> uploaded.

There are a number of filed bugs (e.g., [1,2]) addressed by this diff, but my proposed diff won't fix the issue for users by default -- a user must still opt-in to running the modesetting driver in tearfree mode by modifying their xorg.conf file. Is it appropriate to list these bugs and have them automatically closed if the fix is opt-in? (Note that upstream has already accepted a patch [3] to enable tearfree mode by default, i.e., to make it opt-out, but I decided to omit this patch due to its recency and my assumption that it would be easier to get tearfree mode accepted into Ubuntu as opt-in vs. opt-out; however, I can include this patch and make it opt-out if this would be more desirable.)

[1] https://bugs.launchpad.net/xorg-server/+bug/1846398
[2] https://bugs.launchpad.net/xorg-server/+bug/1853094
[3] https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1224

> A couple of extra questions:
>
> 1. In case you are familiar with Xorg's release schedule, when do you think
> these patches will be part of a new release?

I have no special familiarity with Xorg's release schedule, but my understanding as an outsider is that there is no longer any official "maintainer" of the Xorg X server. This makes me pessimistic that there will be anything but security fixes in future releases for the foreseeable future.

> 2. Is there a Debian bug about the issue?

I did a search and found that [4,5] are likely related, especially [5] as the user reports the issue only occurring after switching from the intel DDX driver to the modesetting one.

[4] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=839239
[5] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1003806

In general though it doesn't seem as though the debian bugtrackers have the same depth of reporting of the addressed issue as Ubuntu's.

> I'll try to get the attention of somebody more knowledgeable with Xorg.

Thanks, I understand that this is a tricky diff. :)

16282b7... by Jeffrey Knockel <email address hidden>

Enable "TearFree" option for modesetting driver

c8f4878... by Jeffrey Knockel <email address hidden>

Fix inaccurate Present timing for TearFree

9c5de4b... by Timo Aaltonen

2:21.1.11-1ubuntu1 (patches unapplied)

Imported using git-ubuntu import.

Revision history for this message
Andreas Hasenack (ahasenack) wrote :

I went ahead and added a slot for Timo to review this :)

Revision history for this message
Andreas Hasenack (ahasenack) wrote :

The upstream patch[1] has this hunk:

hw/xfree86/common/xf86Module.h:
-#define ABI_VIDEODRV_VERSION SET_ABI_VERSION(25, 3)
+#define ABI_VIDEODRV_VERSION SET_ABI_VERSION(25, 4)

Your debdiff is not touching that. Precaution, or something else?

But, this is over my head, I don't know if patching xorg like this debdiff has other implications. I would be much more comfortable if these were in a new release.

I'll note that 21.1.11 was tagged on Jan 16th, 2024, so quite recently, and 21.1.10 a month before, so releases are happening.

1. https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1006/diffs

Revision history for this message
Andreas Hasenack (ahasenack) wrote :

Oh, and you should rebase this on top of what is in noble at the moment, which is 2:21.1.11-1ubuntu1

609edb0... by Jeffrey Knockel <email address hidden>

Update debian/changelog with TearFree fixes

Revision history for this message
Jeffrey Knockel (jeff250) wrote :

> I went ahead and added a slot for Timo to review this :)

Thanks!

> The upstream patch[1] has this hunk:
>
> hw/xfree86/common/xf86Module.h:
> -#define ABI_VIDEODRV_VERSION SET_ABI_VERSION(25, 3)
> +#define ABI_VIDEODRV_VERSION SET_ABI_VERSION(25, 4)
>
> Your debdiff is not touching that. Precaution, or something else?
>
> But, this is over my head, I don't know if patching xorg like this debdiff has
> other implications.

This change is tricky because the current ABI_VIDEODRV_VERSION of our version (21.1.11) is 25.2, so I don't know if it makes sense to bump it to 25.4, if we don't include the changes from 25.2 to 25.3.

The original author of the upstream merge request did not include this change in his port [1] of the changes to 21.1.x, which my debdiff is based on. However, he may have been faced with the same dilemma.

[1] https://github.com/kerneltoast/xserver/tree/new-tearfree-21.1.5

> I would be much more comfortable if these were in a new release.
>
> I'll note that 21.1.11 was tagged on Jan 16th, 2024, so quite recently, and
> 21.1.10 a month before, so releases are happening.

Yes I would feel more comfortable if these were in a new release as well. :) One of my motivations for submitting this merge request was because the last major release of Xorg's X server (21.1.0) was in 2021. My understanding of their recent releases is that they contain security fixes, minor bug fixes, and only small additions (e.g., PresentWindowDestroyed events).

> Oh, and you should rebase this on top of what is in noble at the moment, which
> is 2:21.1.11-1ubuntu1

Done!

Revision history for this message
Timo Aaltonen (tjaalton) wrote :

Hi, I pinged upstream about having a new release now that many of the long-standing MR's have been merged, and it might happen. And I'd much rather have this via an upstream release than as another distro patch..

Maybe I'll push a git snapshot to Debian experimental to see what breaks.

Revision history for this message
Robie Basak (racb) wrote :

Hi,

This is still in the sponsorship queue, but like the others I'm reluctant to add an extensive distro patch without the maintenance question being answered. I wouldn't add it without an ack from the desktop team. But it sounds like there might be movement upstream, so I guess there's no action to take for this in Ubuntu at the moment.

To clear up the queue, I'm going to set the MP to Work In Progress as it's not ready to merge as-is. If the status changes and the desktop team have indicated that they're willing to maintain the patch, or you need help in some other way, please change the status back to Needs review and it should reappear in the queue.

Thanks!

Revision history for this message
Jeffrey Knockel (jeff250) wrote :

> Hi, I pinged upstream about having a new release now that many of the long-standing MR's have been merged, and it might happen.

That would be great!

> Maybe I'll push a git snapshot to Debian experimental to see what breaks.

That would be amazing, and I would love to test it.

Revision history for this message
Timo Aaltonen (tjaalton) wrote :

Hi, looking more closely this bumps the driver abi and we can't do that as that would break the nvidia driver. Such changes have to come from upstream via official releases so that the vendors can then fix their OOT drivers to support the new driver abi

review: Disapprove
Revision history for this message
Jeffrey Knockel (jeff250) wrote :

> Hi, looking more closely this bumps the driver abi and we can't do that as
> that would break the nvidia driver. Such changes have to come from upstream
> via official releases so that the vendors can then fix their OOT drivers to
> support the new driver abi

From what I can tell, the driver ABI bump comes from the addition [1] of a function, so I believe that this change is unlikely to cause incompatibilities. Does this help with the approval of this merge request?

From the same discussion thread [1], I believe that the function added to the ABI could be removed from it and a similar function simply copy-and-pasted into the modesetting driver, similar to what the discussion thread hints is already done in the amdgpu/ati driver. If the patch were modified in this manner to avoid changing the ABI, would this affect your stance here?

[1] https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1006#note_1672887

Unmerged commits

609edb0... by Jeffrey Knockel <email address hidden>

Update debian/changelog with TearFree fixes

c8f4878... by Jeffrey Knockel <email address hidden>

Fix inaccurate Present timing for TearFree

16282b7... by Jeffrey Knockel <email address hidden>

Enable "TearFree" option for modesetting driver

9c5de4b... by Timo Aaltonen

2:21.1.11-1ubuntu1 (patches unapplied)

Imported using git-ubuntu import.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/debian/changelog b/debian/changelog
index d7d892a..c0a26cf 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,12 @@
1xorg-server (2:21.1.11-1ubuntu2) noble; urgency=medium
2
3 * Backport opt-in TearFree mode in the modesetting driver
4 - tearfree-modesetting.patch: Enable "TearFree" modesetting driver option.
5 - tearfree-present-fixes.patch: Fix inaccurate PresentCompleteNotify timing
6 for TearFree.
7
8 -- Jeffrey Knockel <jeff@jeffreyknockel.com> Fri, 19 Jan 2024 13:31:31 -0500
9
1xorg-server (2:21.1.11-1ubuntu1) noble; urgency=medium10xorg-server (2:21.1.11-1ubuntu1) noble; urgency=medium
211
3 * Merge from Debian.12 * Merge from Debian.
diff --git a/debian/patches/series b/debian/patches/series
index 6482af0..55f72fe 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -26,3 +26,7 @@ fix-default-permissions.patch
26reset-transforms-in-closescreen.diff26reset-transforms-in-closescreen.diff
27re-calculate-the-clock-and-refresh-rate.diff27re-calculate-the-clock-and-refresh-rate.diff
280001-dix-Force-update-LEDs-after-device-state-update-in-E.patch280001-dix-Force-update-LEDs-after-device-state-update-in-E.patch
29
30# tearfree fixes
31tearfree-modesetting.patch
32tearfree-present-fixes.patch
diff --git a/debian/patches/tearfree-modesetting.patch b/debian/patches/tearfree-modesetting.patch
29new file mode 10064433new file mode 100644
index 0000000..99e8dd3
--- /dev/null
+++ b/debian/patches/tearfree-modesetting.patch
@@ -0,0 +1,1385 @@
1Description: Enable "TearFree" option for modesetting driver
2Origin: upstream, https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1006
3Bug: https://gitlab.freedesktop.org/xorg/xserver/-/issues/244
4Bug-Ubuntu: https://bugs.launchpad.net/xorg-server/+bug/1754284
5Bug-Ubuntu: https://bugs.launchpad.net/xorg-server/+bug/1846398
6Bug-Ubuntu: https://bugs.launchpad.net/xorg-server/+bug/1853094
7Index: xorg-server-21.1.6/dix/pixmap.c
8===================================================================
9--- xorg-server-21.1.6.orig/dix/pixmap.c 2022-12-19 05:53:03.000000000 -0500
10+++ xorg-server-21.1.6/dix/pixmap.c 2023-01-09 10:11:45.537724859 -0500
11@@ -262,12 +262,11 @@
12 return TRUE;
13 }
14
15-static void
16-PixmapDirtyCopyArea(PixmapPtr dst,
17- PixmapDirtyUpdatePtr dirty,
18+void
19+PixmapDirtyCopyArea(PixmapPtr dst, DrawablePtr src,
20+ int x, int y, int dst_x, int dst_y,
21 RegionPtr dirty_region)
22 {
23- DrawablePtr src = dirty->src;
24 ScreenPtr pScreen = src->pScreen;
25 int n;
26 BoxPtr b;
27@@ -294,9 +293,8 @@
28 h = dst_box.y2 - dst_box.y1;
29
30 pGC->ops->CopyArea(src, &dst->drawable, pGC,
31- dirty->x + dst_box.x1, dirty->y + dst_box.y1, w, h,
32- dirty->dst_x + dst_box.x1,
33- dirty->dst_y + dst_box.y1);
34+ x + dst_box.x1, y + dst_box.y1, w, h,
35+ dst_x + dst_box.x1, dst_y + dst_box.y1);
36 b++;
37 }
38 FreeScratchGC(pGC);
39@@ -408,7 +406,8 @@
40 RegionTranslate(&pixregion, -dirty->x, -dirty->y);
41
42 if (!pScreen->root || dirty->rotation == RR_Rotate_0)
43- PixmapDirtyCopyArea(dst, dirty, &pixregion);
44+ PixmapDirtyCopyArea(dst, dirty->src, dirty->x, dirty->y,
45+ dirty->dst_x, dirty->dst_y, &pixregion);
46 else
47 PixmapDirtyCompositeRotate(dst, dirty, &pixregion);
48 pScreen->SourceValidate = SourceValidate;
49Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/driver.c
50===================================================================
51--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/driver.c 2022-12-19 05:53:03.000000000 -0500
52+++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/driver.c 2023-01-09 10:11:45.537724859 -0500
53@@ -145,6 +145,7 @@
54 {OPTION_VARIABLE_REFRESH, "VariableRefresh", OPTV_BOOLEAN, {0}, FALSE},
55 {OPTION_USE_GAMMA_LUT, "UseGammaLUT", OPTV_BOOLEAN, {0}, FALSE},
56 {OPTION_ASYNC_FLIP_SECONDARIES, "AsyncFlipSecondaries", OPTV_BOOLEAN, {0}, FALSE},
57+ {OPTION_TEARFREE, "TearFree", OPTV_BOOLEAN, {0}, FALSE},
58 {-1, NULL, OPTV_NONE, {0}, FALSE}
59 };
60
61@@ -516,14 +517,15 @@
62 }
63
64 static int
65-dispatch_dirty_region(ScrnInfoPtr scrn,
66- PixmapPtr pixmap, DamagePtr damage, int fb_id)
67+dispatch_damages(ScrnInfoPtr scrn, RegionPtr dirty, DamagePtr damage, int fb_id)
68 {
69 modesettingPtr ms = modesettingPTR(scrn);
70- RegionPtr dirty = DamageRegion(damage);
71 unsigned num_cliprects = REGION_NUM_RECTS(dirty);
72 int ret = 0;
73
74+ if (!ms->dirty_enabled)
75+ return 0;
76+
77 if (num_cliprects) {
78 drmModeClip *clip = xallocarray(num_cliprects, sizeof(drmModeClip));
79 BoxPtr rect = REGION_RECTS(dirty);
80@@ -551,12 +553,98 @@
81 }
82 }
83
84+ if (ret == -EINVAL || ret == -ENOSYS) {
85+ xf86DrvMsg(scrn->scrnIndex, X_INFO,
86+ "Disabling kernel dirty updates, not required.\n");
87+ ms->dirty_enabled = FALSE;
88+ }
89+
90 free(clip);
91- DamageEmpty(damage);
92+ if (damage)
93+ DamageEmpty(damage);
94 }
95 return ret;
96 }
97
98+static int
99+dispatch_dirty_region(ScrnInfoPtr scrn,
100+ PixmapPtr pixmap, DamagePtr damage, int fb_id)
101+{
102+ return dispatch_damages(scrn, DamageRegion(damage), damage, fb_id);
103+}
104+
105+static void
106+ms_tearfree_update_damages(ScreenPtr pScreen)
107+{
108+ ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
109+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
110+ modesettingPtr ms = modesettingPTR(scrn);
111+ RegionPtr dirty = DamageRegion(ms->damage);
112+ int c, i;
113+
114+ if (RegionNil(dirty))
115+ return;
116+
117+ for (c = 0; c < xf86_config->num_crtc; c++) {
118+ xf86CrtcPtr crtc = xf86_config->crtc[c];
119+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
120+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
121+ RegionRec region;
122+
123+ /* Compute how much of the damage intersects with this CRTC */
124+ RegionInit(&region, &crtc->bounds, 0);
125+ RegionIntersect(&region, &region, dirty);
126+
127+ if (trf->buf[0].px) {
128+ for (i = 0; i < ARRAY_SIZE(trf->buf); i++)
129+ RegionUnion(&trf->buf[i].dmg, &trf->buf[i].dmg, &region);
130+ } else {
131+ /* Just notify the kernel of the damages if TearFree isn't used */
132+ dispatch_damages(scrn, &region, NULL, ms->drmmode.fb_id);
133+ }
134+ }
135+ DamageEmpty(ms->damage);
136+}
137+
138+static void
139+ms_tearfree_do_flips(ScreenPtr pScreen)
140+{
141+#ifdef GLAMOR_HAS_GBM
142+ ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
143+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
144+ modesettingPtr ms = modesettingPTR(scrn);
145+ int c;
146+
147+ if (!ms->drmmode.tearfree_enable)
148+ return;
149+
150+ for (c = 0; c < xf86_config->num_crtc; c++) {
151+ xf86CrtcPtr crtc = xf86_config->crtc[c];
152+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
153+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
154+
155+ /* Skip disabled CRTCs and those which aren't using TearFree */
156+ if (!trf->buf[0].px || !crtc->scrn->vtSema || !xf86_crtc_on(crtc))
157+ continue;
158+
159+ /* Skip if the last flip is still pending, a DRI client is flipping, or
160+ * there isn't any damage on the front buffer.
161+ */
162+ if (trf->flip_seq || ms->drmmode.dri2_flipping ||
163+ ms->drmmode.present_flipping ||
164+ RegionNil(&trf->buf[trf->back_idx ^ 1].dmg))
165+ continue;
166+
167+ /* Flip. If it fails, notify the kernel of the front buffer damages */
168+ if (ms_do_tearfree_flip(pScreen, crtc)) {
169+ dispatch_damages(scrn, &trf->buf[trf->back_idx ^ 1].dmg,
170+ NULL, trf->buf[trf->back_idx ^ 1].fb_id);
171+ RegionEmpty(&trf->buf[trf->back_idx ^ 1].dmg);
172+ }
173+ }
174+#endif
175+}
176+
177 static void
178 dispatch_dirty(ScreenPtr pScreen)
179 {
180@@ -568,12 +656,9 @@
181
182 ret = dispatch_dirty_region(scrn, pixmap, ms->damage, fb_id);
183 if (ret == -EINVAL || ret == -ENOSYS) {
184- ms->dirty_enabled = FALSE;
185 DamageUnregister(ms->damage);
186 DamageDestroy(ms->damage);
187 ms->damage = NULL;
188- xf86DrvMsg(scrn->scrnIndex, X_INFO,
189- "Disabling kernel dirty updates, not required.\n");
190 return;
191 }
192 }
193@@ -703,10 +788,13 @@
194 pScreen->BlockHandler = msBlockHandler;
195 if (pScreen->isGPU && !ms->drmmode.reverse_prime_offload_mode)
196 dispatch_secondary_dirty(pScreen);
197+ else if (ms->drmmode.tearfree_enable)
198+ ms_tearfree_update_damages(pScreen);
199 else if (ms->dirty_enabled)
200 dispatch_dirty(pScreen);
201
202 ms_dirty_update(pScreen, timeout);
203+ ms_tearfree_do_flips(pScreen);
204 }
205
206 static void
207@@ -1242,6 +1330,27 @@
208 ms->atomic_modeset = FALSE;
209 }
210
211+ /* TearFree requires glamor and, if PageFlip is enabled, universal planes */
212+ if (xf86ReturnOptValBool(ms->drmmode.Options, OPTION_TEARFREE, FALSE)) {
213+ if (pScrn->is_gpu) {
214+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
215+ "TearFree cannot synchronize PRIME; use 'PRIME Synchronization' instead\n");
216+ } else if (ms->drmmode.glamor) {
217+ /* Atomic modesetting implicitly enables universal planes */
218+ if (!ms->drmmode.pageflip || ms->atomic_modeset ||
219+ !drmSetClientCap(ms->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) {
220+ ms->drmmode.tearfree_enable = TRUE;
221+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "TearFree: enabled\n");
222+ } else {
223+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
224+ "TearFree requires either universal planes, or setting 'Option \"PageFlip\" \"off\"'\n");
225+ }
226+ } else {
227+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
228+ "TearFree requires Glamor acceleration\n");
229+ }
230+ }
231+
232 ms->kms_has_modifiers = FALSE;
233 ret = drmGetCap(ms->fd, DRM_CAP_ADDFB2_MODIFIERS, &value);
234 if (ret == 0 && value != 0)
235@@ -1589,13 +1698,13 @@
236
237 err = drmModeDirtyFB(ms->fd, ms->drmmode.fb_id, NULL, 0);
238
239- if (err != -EINVAL && err != -ENOSYS) {
240+ if ((err != -EINVAL && err != -ENOSYS) || ms->drmmode.tearfree_enable) {
241 ms->damage = DamageCreate(NULL, NULL, DamageReportNone, TRUE,
242 pScreen, rootPixmap);
243
244 if (ms->damage) {
245 DamageRegister(&rootPixmap->drawable, ms->damage);
246- ms->dirty_enabled = TRUE;
247+ ms->dirty_enabled = err != -EINVAL && err != -ENOSYS;
248 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Damage tracking initialized\n");
249 }
250 else {
251Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/driver.h
252===================================================================
253--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/driver.h 2022-12-19 05:53:03.000000000 -0500
254+++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/driver.h 2023-01-09 10:11:45.541724797 -0500
255@@ -61,6 +61,7 @@
256 OPTION_VARIABLE_REFRESH,
257 OPTION_USE_GAMMA_LUT,
258 OPTION_ASYNC_FLIP_SECONDARIES,
259+ OPTION_TEARFREE,
260 } modesettingOpts;
261
262 typedef struct
263@@ -86,10 +87,13 @@
264 struct xorg_list list;
265 xf86CrtcPtr crtc;
266 uint32_t seq;
267+ uint64_t msc;
268 void *data;
269 ScrnInfoPtr scrn;
270 ms_drm_handler_proc handler;
271 ms_drm_abort_proc abort;
272+ Bool kernel_queued;
273+ Bool aborted;
274 };
275
276 typedef struct _modesettingRec {
277@@ -238,6 +242,8 @@
278 ms_pageflip_abort_proc pageflip_abort,
279 const char *log_prefix);
280
281+Bool ms_do_tearfree_flip(ScreenPtr screen, xf86CrtcPtr crtc);
282+
283 #endif
284
285 int ms_flush_drm_events(ScreenPtr screen);
286Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/drmmode_display.c
287===================================================================
288--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/drmmode_display.c 2023-01-09 10:11:15.278283797 -0500
289+++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/drmmode_display.c 2023-01-09 10:11:45.541724797 -0500
290@@ -632,6 +632,7 @@
291 {
292 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
293 drmmode_ptr drmmode = drmmode_crtc->drmmode;
294+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
295 int ret;
296
297 *fb_id = 0;
298@@ -646,6 +647,10 @@
299 *x = drmmode_crtc->prime_pixmap_x;
300 *y = 0;
301 }
302+ else if (trf->buf[trf->back_idx ^ 1].px) {
303+ *fb_id = trf->buf[trf->back_idx ^ 1].fb_id;
304+ *x = *y = 0;
305+ }
306 else if (drmmode_crtc->rotate_fb_id) {
307 *fb_id = drmmode_crtc->rotate_fb_id;
308 *x = *y = 0;
309@@ -922,6 +927,10 @@
310 drmmode_ConvertToKMode(crtc->scrn, &kmode, &crtc->mode);
311 ret = drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
312 fb_id, x, y, output_ids, output_count, &kmode);
313+ if (!ret && !ms->atomic_modeset) {
314+ drmmode_crtc->src_x = x;
315+ drmmode_crtc->src_y = y;
316+ }
317
318 drmmode_set_ctm(crtc, ctm);
319
320@@ -930,7 +939,8 @@
321 }
322
323 int
324-drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, uint32_t flags, void *data)
325+drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, int x, int y,
326+ uint32_t flags, void *data)
327 {
328 modesettingPtr ms = modesettingPTR(crtc->scrn);
329 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
330@@ -942,7 +952,7 @@
331 if (!req)
332 return 1;
333
334- ret = plane_add_props(req, crtc, fb_id, crtc->x, crtc->y);
335+ ret = plane_add_props(req, crtc, fb_id, x, y);
336 flags |= DRM_MODE_ATOMIC_NONBLOCK;
337 if (ret == 0)
338 ret = drmModeAtomicCommit(ms->fd, req, flags, data);
339@@ -950,6 +960,26 @@
340 return ret;
341 }
342
343+ /* The frame buffer source coordinates may change when switching between the
344+ * primary frame buffer and a per-CRTC frame buffer. Set the correct source
345+ * coordinates if they differ for this flip.
346+ */
347+ if (drmmode_crtc->src_x != x || drmmode_crtc->src_y != y) {
348+ ret = drmModeSetPlane(ms->fd, drmmode_crtc->plane_id,
349+ drmmode_crtc->mode_crtc->crtc_id, fb_id, 0,
350+ 0, 0, crtc->mode.HDisplay, crtc->mode.VDisplay,
351+ x << 16, y << 16, crtc->mode.HDisplay << 16,
352+ crtc->mode.VDisplay << 16);
353+ if (ret) {
354+ xf86DrvMsg(crtc->scrn->scrnIndex, X_WARNING,
355+ "error changing fb src coordinates for flip: %d\n", ret);
356+ return ret;
357+ }
358+
359+ drmmode_crtc->src_x = x;
360+ drmmode_crtc->src_y = y;
361+ }
362+
363 return drmModePageFlip(ms->fd, drmmode_crtc->mode_crtc->crtc_id,
364 fb_id, flags, data);
365 }
366@@ -1548,6 +1578,90 @@
367 #endif
368 }
369
370+void
371+drmmode_copy_damage(xf86CrtcPtr crtc, PixmapPtr dst, RegionPtr dmg, Bool empty)
372+{
373+#ifdef GLAMOR_HAS_GBM
374+ ScreenPtr pScreen = xf86ScrnToScreen(crtc->scrn);
375+ DrawableRec *src;
376+
377+ /* Copy the screen's pixmap into the destination pixmap */
378+ if (crtc->rotatedPixmap) {
379+ src = &crtc->rotatedPixmap->drawable;
380+ xf86RotateCrtcRedisplay(crtc, dst, src, dmg, FALSE);
381+ } else {
382+ src = &pScreen->GetScreenPixmap(pScreen)->drawable;
383+ PixmapDirtyCopyArea(dst, src, 0, 0, -crtc->x, -crtc->y, dmg);
384+ }
385+
386+ /* Reset the damages if requested */
387+ if (empty)
388+ RegionEmpty(dmg);
389+
390+ /* Wait until the GC operations finish */
391+ modesettingPTR(crtc->scrn)->glamor.finish(pScreen);
392+#endif
393+}
394+
395+static void
396+drmmode_shadow_fb_destroy(xf86CrtcPtr crtc, PixmapPtr pixmap,
397+ void *data, drmmode_bo *bo, uint32_t *fb_id);
398+static void
399+drmmode_destroy_tearfree_shadow(xf86CrtcPtr crtc)
400+{
401+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
402+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
403+ int i;
404+
405+ if (trf->flip_seq)
406+ ms_drm_abort_seq(crtc->scrn, trf->flip_seq);
407+
408+ for (i = 0; i < ARRAY_SIZE(trf->buf); i++) {
409+ if (trf->buf[i].px) {
410+ drmmode_shadow_fb_destroy(crtc, trf->buf[i].px, (void *)(long)1,
411+ &trf->buf[i].bo, &trf->buf[i].fb_id);
412+ trf->buf[i].px = NULL;
413+ RegionUninit(&trf->buf[i].dmg);
414+ }
415+ }
416+}
417+
418+static PixmapPtr
419+drmmode_shadow_fb_create(xf86CrtcPtr crtc, void *data, int width, int height,
420+ drmmode_bo *bo, uint32_t *fb_id);
421+static Bool
422+drmmode_create_tearfree_shadow(xf86CrtcPtr crtc)
423+{
424+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
425+ drmmode_ptr drmmode = drmmode_crtc->drmmode;
426+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
427+ uint32_t w = crtc->mode.HDisplay, h = crtc->mode.VDisplay;
428+ int i;
429+
430+ if (!drmmode->tearfree_enable)
431+ return TRUE;
432+
433+ /* Destroy the old mode's buffers and make new ones */
434+ drmmode_destroy_tearfree_shadow(crtc);
435+ for (i = 0; i < ARRAY_SIZE(trf->buf); i++) {
436+ trf->buf[i].px = drmmode_shadow_fb_create(crtc, NULL, w, h,
437+ &trf->buf[i].bo,
438+ &trf->buf[i].fb_id);
439+ if (!trf->buf[i].px) {
440+ drmmode_destroy_tearfree_shadow(crtc);
441+ xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
442+ "shadow creation failed for TearFree buf%d\n", i);
443+ return FALSE;
444+ }
445+ RegionInit(&trf->buf[i].dmg, &crtc->bounds, 0);
446+ }
447+
448+ /* Initialize the front buffer with the current scanout */
449+ drmmode_copy_damage(crtc, trf->buf[trf->back_idx ^ 1].px,
450+ &trf->buf[trf->back_idx ^ 1].dmg, TRUE);
451+ return TRUE;
452+}
453+
454 static Bool
455 drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
456 Rotation rotation, int x, int y)
457@@ -1581,6 +1695,10 @@
458 crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green,
459 crtc->gamma_blue, crtc->gamma_size);
460
461+ ret = drmmode_create_tearfree_shadow(crtc);
462+ if (!ret)
463+ goto done;
464+
465 can_test = drmmode_crtc_can_test_mode(crtc);
466 if (drmmode_crtc_set_mode(crtc, can_test)) {
467 xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
468@@ -1626,6 +1744,7 @@
469 crtc->y = saved_y;
470 crtc->rotation = saved_rotation;
471 crtc->mode = saved_mode;
472+ drmmode_create_tearfree_shadow(crtc);
473 } else
474 crtc->active = TRUE;
475
476@@ -1931,33 +2050,42 @@
477 }
478
479 static void *
480-drmmode_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
481+drmmode_shadow_fb_allocate(xf86CrtcPtr crtc, int width, int height,
482+ drmmode_bo *bo, uint32_t *fb_id)
483 {
484 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
485 drmmode_ptr drmmode = drmmode_crtc->drmmode;
486 int ret;
487
488- if (!drmmode_create_bo(drmmode, &drmmode_crtc->rotate_bo,
489- width, height, drmmode->kbpp)) {
490+ if (!drmmode_create_bo(drmmode, bo, width, height, drmmode->kbpp)) {
491 xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
492 "Couldn't allocate shadow memory for rotated CRTC\n");
493 return NULL;
494 }
495
496- ret = drmmode_bo_import(drmmode, &drmmode_crtc->rotate_bo,
497- &drmmode_crtc->rotate_fb_id);
498+ ret = drmmode_bo_import(drmmode, bo, fb_id);
499
500 if (ret) {
501 ErrorF("failed to add rotate fb\n");
502- drmmode_bo_destroy(drmmode, &drmmode_crtc->rotate_bo);
503+ drmmode_bo_destroy(drmmode, bo);
504 return NULL;
505 }
506
507 #ifdef GLAMOR_HAS_GBM
508 if (drmmode->gbm)
509- return drmmode_crtc->rotate_bo.gbm;
510+ return bo->gbm;
511 #endif
512- return drmmode_crtc->rotate_bo.dumb;
513+ return bo->dumb;
514+}
515+
516+static void *
517+drmmode_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
518+{
519+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
520+
521+ return drmmode_shadow_fb_allocate(crtc, width, height,
522+ &drmmode_crtc->rotate_bo,
523+ &drmmode_crtc->rotate_fb_id);
524 }
525
526 static PixmapPtr
527@@ -1983,71 +2111,92 @@
528 drmmode_set_pixmap_bo(drmmode_ptr drmmode, PixmapPtr pixmap, drmmode_bo *bo);
529
530 static PixmapPtr
531-drmmode_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
532+drmmode_shadow_fb_create(xf86CrtcPtr crtc, void *data, int width, int height,
533+ drmmode_bo *bo, uint32_t *fb_id)
534 {
535 ScrnInfoPtr scrn = crtc->scrn;
536 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
537 drmmode_ptr drmmode = drmmode_crtc->drmmode;
538- uint32_t rotate_pitch;
539- PixmapPtr rotate_pixmap;
540+ uint32_t pitch;
541+ PixmapPtr pixmap;
542 void *pPixData = NULL;
543
544 if (!data) {
545- data = drmmode_shadow_allocate(crtc, width, height);
546+ data = drmmode_shadow_fb_allocate(crtc, width, height, bo, fb_id);
547 if (!data) {
548 xf86DrvMsg(scrn->scrnIndex, X_ERROR,
549- "Couldn't allocate shadow pixmap for rotated CRTC\n");
550+ "Couldn't allocate shadow pixmap for CRTC\n");
551 return NULL;
552 }
553 }
554
555- if (!drmmode_bo_has_bo(&drmmode_crtc->rotate_bo)) {
556+ if (!drmmode_bo_has_bo(bo)) {
557 xf86DrvMsg(scrn->scrnIndex, X_ERROR,
558- "Couldn't allocate shadow pixmap for rotated CRTC\n");
559+ "Couldn't allocate shadow pixmap for CRTC\n");
560 return NULL;
561 }
562
563- pPixData = drmmode_bo_map(drmmode, &drmmode_crtc->rotate_bo);
564- rotate_pitch = drmmode_bo_get_pitch(&drmmode_crtc->rotate_bo);
565+ pPixData = drmmode_bo_map(drmmode, bo);
566+ pitch = drmmode_bo_get_pitch(bo);
567
568- rotate_pixmap = drmmode_create_pixmap_header(scrn->pScreen,
569- width, height,
570- scrn->depth,
571- drmmode->kbpp,
572- rotate_pitch,
573- pPixData);
574+ pixmap = drmmode_create_pixmap_header(scrn->pScreen,
575+ width, height,
576+ scrn->depth,
577+ drmmode->kbpp,
578+ pitch,
579+ pPixData);
580
581- if (rotate_pixmap == NULL) {
582+ if (pixmap == NULL) {
583 xf86DrvMsg(scrn->scrnIndex, X_ERROR,
584- "Couldn't allocate shadow pixmap for rotated CRTC\n");
585+ "Couldn't allocate shadow pixmap for CRTC\n");
586 return NULL;
587 }
588
589- drmmode_set_pixmap_bo(drmmode, rotate_pixmap, &drmmode_crtc->rotate_bo);
590+ drmmode_set_pixmap_bo(drmmode, pixmap, bo);
591+
592+ return pixmap;
593+}
594+
595+static PixmapPtr
596+drmmode_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
597+{
598+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
599
600- return rotate_pixmap;
601+ return drmmode_shadow_fb_create(crtc, data, width, height,
602+ &drmmode_crtc->rotate_bo,
603+ &drmmode_crtc->rotate_fb_id);
604 }
605
606 static void
607-drmmode_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *data)
608+drmmode_shadow_fb_destroy(xf86CrtcPtr crtc, PixmapPtr pixmap,
609+ void *data, drmmode_bo *bo, uint32_t *fb_id)
610 {
611 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
612 drmmode_ptr drmmode = drmmode_crtc->drmmode;
613
614- if (rotate_pixmap) {
615- rotate_pixmap->drawable.pScreen->DestroyPixmap(rotate_pixmap);
616+ if (pixmap) {
617+ pixmap->drawable.pScreen->DestroyPixmap(pixmap);
618 }
619
620 if (data) {
621- drmModeRmFB(drmmode->fd, drmmode_crtc->rotate_fb_id);
622- drmmode_crtc->rotate_fb_id = 0;
623+ drmModeRmFB(drmmode->fd, *fb_id);
624+ *fb_id = 0;
625
626- drmmode_bo_destroy(drmmode, &drmmode_crtc->rotate_bo);
627- memset(&drmmode_crtc->rotate_bo, 0, sizeof drmmode_crtc->rotate_bo);
628+ drmmode_bo_destroy(drmmode, bo);
629+ memset(bo, 0, sizeof(*bo));
630 }
631 }
632
633 static void
634+drmmode_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr pixmap, void *data)
635+{
636+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
637+
638+ drmmode_shadow_fb_destroy(crtc, pixmap, data, &drmmode_crtc->rotate_bo,
639+ &drmmode_crtc->rotate_fb_id);
640+}
641+
642+static void
643 drmmode_crtc_destroy(xf86CrtcPtr crtc)
644 {
645 drmmode_mode_ptr iterator, next;
646@@ -2380,6 +2529,7 @@
647 drmmode_crtc->drmmode = drmmode;
648 drmmode_crtc->vblank_pipe = drmmode_crtc_vblank_pipe(num);
649 xorg_list_init(&drmmode_crtc->mode_list);
650+ drmmode_crtc->next_msc = UINT64_MAX;
651
652 props = drmModeObjectGetProperties(drmmode->fd, mode_res->crtcs[num],
653 DRM_MODE_OBJECT_CRTC);
654@@ -4253,6 +4403,7 @@
655 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
656
657 dumb_bo_destroy(drmmode->fd, drmmode_crtc->cursor_bo);
658+ drmmode_destroy_tearfree_shadow(crtc);
659 }
660 }
661
662Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/drmmode_display.h
663===================================================================
664--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/drmmode_display.h 2022-12-19 05:53:03.000000000 -0500
665+++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/drmmode_display.h 2023-01-09 10:11:45.541724797 -0500
666@@ -135,6 +135,7 @@
667 Bool async_flip_secondaries;
668 Bool dri2_enable;
669 Bool present_enable;
670+ Bool tearfree_enable;
671
672 uint32_t vrr_prop_id;
673 Bool use_ctm;
674@@ -167,6 +168,19 @@
675 } drmmode_format_rec, *drmmode_format_ptr;
676
677 typedef struct {
678+ drmmode_bo bo;
679+ uint32_t fb_id;
680+ PixmapPtr px;
681+ RegionRec dmg;
682+} drmmode_shadow_fb_rec, *drmmode_shadow_fb_ptr;
683+
684+typedef struct {
685+ drmmode_shadow_fb_rec buf[2];
686+ uint32_t back_idx;
687+ uint32_t flip_seq;
688+} drmmode_tearfree_rec, *drmmode_tearfree_ptr;
689+
690+typedef struct {
691 drmmode_ptr drmmode;
692 drmModeCrtcPtr mode_crtc;
693 uint32_t vblank_pipe;
694@@ -184,11 +198,14 @@
695
696 drmmode_bo rotate_bo;
697 unsigned rotate_fb_id;
698+ drmmode_tearfree_rec tearfree;
699
700 PixmapPtr prime_pixmap;
701 PixmapPtr prime_pixmap_back;
702 unsigned prime_pixmap_x;
703
704+ int src_x, src_y;
705+
706 /**
707 * @{ MSC (vblank count) handling for the PRESENT extension.
708 *
709@@ -200,6 +217,8 @@
710 uint64_t msc_high;
711 /** @} */
712
713+ uint64_t next_msc;
714+
715 Bool need_modeset;
716 struct xorg_list mode_list;
717
718@@ -308,8 +327,11 @@
719 int *depth, int *bpp);
720
721 void drmmode_copy_fb(ScrnInfoPtr pScrn, drmmode_ptr drmmode);
722+void drmmode_copy_damage(xf86CrtcPtr crtc, PixmapPtr dst, RegionPtr damage,
723+ Bool empty);
724
725-int drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, uint32_t flags, void *data);
726+int drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, int x, int y,
727+ uint32_t flags, void *data);
728
729 void drmmode_set_dpms(ScrnInfoPtr scrn, int PowerManagementMode, int flags);
730 void drmmode_crtc_set_vrr(xf86CrtcPtr crtc, Bool enabled);
731Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/modesetting.man
732===================================================================
733--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/modesetting.man 2022-12-19 05:53:03.000000000 -0500
734+++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/modesetting.man 2023-01-09 10:11:45.541724797 -0500
735@@ -109,6 +109,17 @@
736 entries, if supported by the kernel. By default, GAMMA_LUT will be used for
737 kms drivers which are known to be safe for use of GAMMA_LUT.
738 .TP
739+.BI "Option \*qTearFree\*q \*q" boolean \*q
740+Enable tearing prevention using the hardware page flipping mechanism.
741+It allocates two extra scanout buffers for each CRTC and utilizes damage
742+tracking to minimize buffer copying and skip unnecessary flips when the
743+screen's contents have not changed. It works on transformed screens too, such
744+as rotated and scaled CRTCs. When PageFlip is enabled, fullscreen DRI
745+applications will still have the discretion to not use tearing prevention.
746+.br
747+The default is
748+.B off.
749+.TP
750 .SH "SEE ALSO"
751 @xservername@(@appmansuffix@), @xconfigfile@(@filemansuffix@), Xserver(@appmansuffix@),
752 X(@miscmansuffix@)
753Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/pageflip.c
754===================================================================
755--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/pageflip.c 2022-12-19 05:53:03.000000000 -0500
756+++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/pageflip.c 2023-01-09 10:11:45.541724797 -0500
757@@ -35,8 +35,8 @@
758 * Returns a negative value on error, 0 if there was nothing to process,
759 * or 1 if we handled any events.
760 */
761-int
762-ms_flush_drm_events(ScreenPtr screen)
763+static int
764+ms_flush_drm_events_timeout(ScreenPtr screen, int timeout)
765 {
766 ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
767 modesettingPtr ms = modesettingPTR(scrn);
768@@ -45,7 +45,7 @@
769 int r;
770
771 do {
772- r = xserver_poll(&p, 1, 0);
773+ r = xserver_poll(&p, 1, timeout);
774 } while (r == -1 && (errno == EINTR || errno == EAGAIN));
775
776 /* If there was an error, r will be < 0. Return that. If there was
777@@ -63,6 +63,12 @@
778 return 1;
779 }
780
781+int
782+ms_flush_drm_events(ScreenPtr screen)
783+{
784+ return ms_flush_drm_events_timeout(screen, 0);
785+}
786+
787 #ifdef GLAMOR_HAS_GBM
788
789 /*
790@@ -160,11 +166,32 @@
791 }
792
793 static Bool
794-do_queue_flip_on_crtc(modesettingPtr ms, xf86CrtcPtr crtc,
795- uint32_t flags, uint32_t seq)
796+do_queue_flip_on_crtc(ScreenPtr screen, xf86CrtcPtr crtc, uint32_t flags,
797+ uint32_t seq, uint32_t fb_id, int x, int y)
798 {
799- return drmmode_crtc_flip(crtc, ms->drmmode.fb_id, flags,
800- (void *) (uintptr_t) seq);
801+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
802+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
803+
804+ while (drmmode_crtc_flip(crtc, fb_id, x, y, flags, (void *)(long)seq)) {
805+ /* We may have failed because the event queue was full. Flush it
806+ * and retry. If there was nothing to flush, then we failed for
807+ * some other reason and should just return an error.
808+ */
809+ if (ms_flush_drm_events(screen) <= 0) {
810+ /* The failure could be caused by a pending TearFree flip, in which
811+ * case we should wait until there's a new event and try again.
812+ */
813+ if (!trf->flip_seq || ms_flush_drm_events_timeout(screen, -1) < 0) {
814+ ms_drm_abort_seq(crtc->scrn, seq);
815+ return TRUE;
816+ }
817+ }
818+
819+ /* We flushed some events, so try again. */
820+ xf86DrvMsg(crtc->scrn->scrnIndex, X_WARNING, "flip queue retry\n");
821+ }
822+
823+ return FALSE;
824 }
825
826 enum queue_flip_status {
827@@ -205,20 +232,9 @@
828 /* take a reference on flipdata for use in flip */
829 flipdata->flip_count++;
830
831- while (do_queue_flip_on_crtc(ms, crtc, flags, seq)) {
832- /* We may have failed because the event queue was full. Flush it
833- * and retry. If there was nothing to flush, then we failed for
834- * some other reason and should just return an error.
835- */
836- if (ms_flush_drm_events(screen) <= 0) {
837- /* Aborting will also decrement flip_count and free(flip). */
838- ms_drm_abort_seq(scrn, seq);
839- return QUEUE_FLIP_DRM_FLUSH_FAILED;
840- }
841-
842- /* We flushed some events, so try again. */
843- xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue retry\n");
844- }
845+ if (do_queue_flip_on_crtc(screen, crtc, flags, seq, ms->drmmode.fb_id,
846+ crtc->x, crtc->y))
847+ return QUEUE_FLIP_DRM_FLUSH_FAILED;
848
849 /* The page flip succeeded. */
850 return QUEUE_FLIP_SUCCESS;
851@@ -465,4 +481,50 @@
852 #endif /* GLAMOR_HAS_GBM */
853 }
854
855+static void
856+ms_tearfree_flip_abort(void *data)
857+{
858+ drmmode_tearfree_ptr trf = data;
859+
860+ trf->flip_seq = 0;
861+}
862+
863+static void
864+ms_tearfree_flip_handler(uint64_t msc, uint64_t usec, void *data)
865+{
866+ drmmode_tearfree_ptr trf = data;
867+
868+ /* Swap the buffers and complete the flip */
869+ trf->back_idx ^= 1;
870+ trf->flip_seq = 0;
871+}
872+
873+Bool
874+ms_do_tearfree_flip(ScreenPtr screen, xf86CrtcPtr crtc)
875+{
876+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
877+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
878+ uint32_t idx = trf->back_idx, seq;
879+
880+ seq = ms_drm_queue_alloc(crtc, trf, ms_tearfree_flip_handler,
881+ ms_tearfree_flip_abort);
882+ if (!seq)
883+ goto no_flip;
884+
885+ /* Copy the damage to the back buffer and then flip it at the vblank */
886+ drmmode_copy_damage(crtc, trf->buf[idx].px, &trf->buf[idx].dmg, TRUE);
887+ if (do_queue_flip_on_crtc(screen, crtc, DRM_MODE_PAGE_FLIP_EVENT,
888+ seq, trf->buf[idx].fb_id, 0, 0))
889+ goto no_flip;
890+
891+ trf->flip_seq = seq;
892+ return FALSE;
893+
894+no_flip:
895+ xf86DrvMsg(crtc->scrn->scrnIndex, X_WARNING,
896+ "TearFree flip failed, rendering frame without TearFree\n");
897+ drmmode_copy_damage(crtc, trf->buf[idx ^ 1].px,
898+ &trf->buf[idx ^ 1].dmg, FALSE);
899+ return TRUE;
900+}
901 #endif
902Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/present.c
903===================================================================
904--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/present.c 2022-12-19 05:53:03.000000000 -0500
905+++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/present.c 2023-01-09 10:11:45.541724797 -0500
906@@ -318,14 +318,32 @@
907 modesettingPtr ms = modesettingPTR(scrn);
908
909 if (ms->drmmode.sprites_visible > 0)
910- return FALSE;
911+ goto no_flip;
912
913 if(!ms_present_check_unflip(crtc, window, pixmap, sync_flip, reason))
914- return FALSE;
915+ goto no_flip;
916
917 ms->flip_window = window;
918
919 return TRUE;
920+
921+no_flip:
922+ /* Export some info about TearFree if Present can't flip anyway */
923+ if (reason && ms->drmmode.tearfree_enable) {
924+ xf86CrtcPtr xf86_crtc = crtc->devPrivate;
925+ drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
926+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
927+
928+ if (trf->buf[0].px) {
929+ if (trf->flip_seq)
930+ /* The driver has a TearFree flip pending */
931+ *reason = PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING;
932+ else
933+ /* The driver uses TearFree flips and there's no flip pending */
934+ *reason = PRESENT_FLIP_REASON_DRIVER_TEARFREE;
935+ }
936+ }
937+ return FALSE;
938 }
939
940 /*
941Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/vblank.c
942===================================================================
943--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/vblank.c 2022-12-19 05:53:03.000000000 -0500
944+++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/vblank.c 2023-01-09 10:11:45.541724797 -0500
945@@ -260,6 +260,51 @@
946 }
947 }
948
949+static void
950+ms_drm_set_seq_msc(uint32_t seq, uint64_t msc)
951+{
952+ struct ms_drm_queue *q;
953+
954+ xorg_list_for_each_entry(q, &ms_drm_queue, list) {
955+ if (q->seq == seq) {
956+ q->msc = msc;
957+ break;
958+ }
959+ }
960+}
961+
962+static void
963+ms_drm_set_seq_queued(uint32_t seq, uint64_t msc)
964+{
965+ drmmode_crtc_private_ptr drmmode_crtc;
966+ struct ms_drm_queue *q;
967+
968+ xorg_list_for_each_entry(q, &ms_drm_queue, list) {
969+ if (q->seq == seq) {
970+ drmmode_crtc = q->crtc->driver_private;
971+ if (msc < drmmode_crtc->next_msc)
972+ drmmode_crtc->next_msc = msc;
973+ q->msc = msc;
974+ q->kernel_queued = TRUE;
975+ break;
976+ }
977+ }
978+}
979+
980+static Bool
981+ms_queue_coalesce(xf86CrtcPtr crtc, uint32_t seq, uint64_t msc)
982+{
983+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
984+
985+ /* If the next MSC is too late, then this event can't be coalesced */
986+ if (msc < drmmode_crtc->next_msc)
987+ return FALSE;
988+
989+ /* Set the target MSC on this sequence number */
990+ ms_drm_set_seq_msc(seq, msc);
991+ return TRUE;
992+}
993+
994 Bool
995 ms_queue_vblank(xf86CrtcPtr crtc, ms_queue_flag flags,
996 uint64_t msc, uint64_t *msc_queued, uint32_t seq)
997@@ -271,6 +316,10 @@
998 drmVBlank vbl;
999 int ret;
1000
1001+ /* Try coalescing this event into another to avoid event queue exhaustion */
1002+ if (flags == MS_QUEUE_ABSOLUTE && ms_queue_coalesce(crtc, seq, msc))
1003+ return TRUE;
1004+
1005 for (;;) {
1006 /* Queue an event at the specified sequence */
1007 if (ms->has_queue_sequence || !ms->tried_queue_sequence) {
1008@@ -287,8 +336,10 @@
1009 ret = drmCrtcQueueSequence(ms->fd, drmmode_crtc->mode_crtc->crtc_id,
1010 drm_flags, msc, &kernel_queued, seq);
1011 if (ret == 0) {
1012+ msc = ms_kernel_msc_to_crtc_msc(crtc, kernel_queued, TRUE);
1013+ ms_drm_set_seq_queued(seq, msc);
1014 if (msc_queued)
1015- *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, kernel_queued, TRUE);
1016+ *msc_queued = msc;
1017 ms->has_queue_sequence = TRUE;
1018 return TRUE;
1019 }
1020@@ -310,8 +361,10 @@
1021 vbl.request.signal = seq;
1022 ret = drmWaitVBlank(ms->fd, &vbl);
1023 if (ret == 0) {
1024+ msc = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence, FALSE);
1025+ ms_drm_set_seq_queued(seq, msc);
1026 if (msc_queued)
1027- *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence, FALSE);
1028+ *msc_queued = msc;
1029 return TRUE;
1030 }
1031 check:
1032@@ -418,13 +471,15 @@
1033 if (!ms_drm_seq)
1034 ++ms_drm_seq;
1035 q->seq = ms_drm_seq++;
1036+ q->msc = UINT64_MAX;
1037 q->scrn = scrn;
1038 q->crtc = crtc;
1039 q->data = data;
1040 q->handler = handler;
1041 q->abort = abort;
1042
1043- xorg_list_add(&q->list, &ms_drm_queue);
1044+ /* Keep the list formatted in ascending order of sequence number */
1045+ xorg_list_append(&q->list, &ms_drm_queue);
1046
1047 return q->seq;
1048 }
1049@@ -437,9 +492,18 @@
1050 static void
1051 ms_drm_abort_one(struct ms_drm_queue *q)
1052 {
1053+ if (q->aborted)
1054+ return;
1055+
1056+ /* Don't remove vblank events if they were queued in the kernel */
1057+ if (q->kernel_queued) {
1058+ q->abort(q->data);
1059+ q->aborted = TRUE;
1060+ } else {
1061 xorg_list_del(&q->list);
1062 q->abort(q->data);
1063 free(q);
1064+ }
1065 }
1066
1067 /**
1068@@ -500,18 +564,61 @@
1069 {
1070 struct ms_drm_queue *q, *tmp;
1071 uint32_t seq = (uint32_t) user_data;
1072+ xf86CrtcPtr crtc = NULL;
1073+ drmmode_crtc_private_ptr drmmode_crtc;
1074+ uint64_t msc, next_msc = UINT64_MAX;
1075
1076- xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
1077+ /* Handle the seq for this event first in order to get the CRTC */
1078+ xorg_list_for_each_entry(q, &ms_drm_queue, list) {
1079 if (q->seq == seq) {
1080- uint64_t msc;
1081-
1082- msc = ms_kernel_msc_to_crtc_msc(q->crtc, frame, is64bit);
1083+ crtc = q->crtc;
1084+ msc = ms_kernel_msc_to_crtc_msc(crtc, frame, is64bit);
1085 xorg_list_del(&q->list);
1086- q->handler(msc, ns / 1000, q->data);
1087+ if (!q->aborted)
1088+ q->handler(msc, ns / 1000, q->data);
1089 free(q);
1090 break;
1091 }
1092 }
1093+
1094+ if (!crtc)
1095+ return;
1096+
1097+ /* Now run all of the vblank events for this CRTC with an expired MSC */
1098+ xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
1099+ if (q->crtc == crtc && q->msc <= msc) {
1100+ xorg_list_del(&q->list);
1101+ if (!q->aborted)
1102+ q->handler(msc, ns / 1000, q->data);
1103+ free(q);
1104+ }
1105+ }
1106+
1107+ /* Find this CRTC's next queued MSC and next non-queued MSC to be handled */
1108+ msc = UINT64_MAX;
1109+ xorg_list_for_each_entry(q, &ms_drm_queue, list) {
1110+ if (q->crtc == crtc) {
1111+ if (q->kernel_queued) {
1112+ if (q->msc < next_msc)
1113+ next_msc = q->msc;
1114+ } else if (q->msc < msc) {
1115+ msc = q->msc;
1116+ seq = q->seq;
1117+ }
1118+ }
1119+ }
1120+
1121+ /* Queue an event if the next queued MSC isn't soon enough */
1122+ drmmode_crtc = crtc->driver_private;
1123+ drmmode_crtc->next_msc = next_msc;
1124+ if (msc < next_msc && !ms_queue_vblank(crtc, MS_QUEUE_ABSOLUTE, msc, NULL, seq)) {
1125+ xf86DrvMsg(crtc->scrn->scrnIndex, X_WARNING,
1126+ "failed to queue next vblank event, aborting lost events\n");
1127+ xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
1128+ if (q->crtc == crtc && q->msc < next_msc)
1129+ ms_drm_abort_one(q);
1130+ }
1131+ }
1132 }
1133
1134 static void
1135Index: xorg-server-21.1.6/hw/xfree86/modes/xf86Crtc.h
1136===================================================================
1137--- xorg-server-21.1.6.orig/hw/xfree86/modes/xf86Crtc.h 2022-12-19 05:53:03.000000000 -0500
1138+++ xorg-server-21.1.6/hw/xfree86/modes/xf86Crtc.h 2023-01-09 10:11:45.541724797 -0500
1139@@ -912,6 +912,11 @@
1140 extern _X_EXPORT Bool
1141 xf86CrtcRotate(xf86CrtcPtr crtc);
1142
1143+extern _X_EXPORT void
1144+ xf86RotateCrtcRedisplay(xf86CrtcPtr crtc, PixmapPtr dst_pixmap,
1145+ DrawableRec *src_drawable, RegionPtr region,
1146+ Bool transform_src);
1147+
1148 /*
1149 * Clean up any rotation data, used when a crtc is turned off
1150 * as well as when rotation is disabled.
1151Index: xorg-server-21.1.6/hw/xfree86/modes/xf86Rotate.c
1152===================================================================
1153--- xorg-server-21.1.6.orig/hw/xfree86/modes/xf86Rotate.c 2022-12-19 05:53:03.000000000 -0500
1154+++ xorg-server-21.1.6/hw/xfree86/modes/xf86Rotate.c 2023-01-09 10:11:45.541724797 -0500
1155@@ -39,13 +39,13 @@
1156 #include "X11/extensions/dpmsconst.h"
1157 #include "X11/Xatom.h"
1158
1159-static void
1160-xf86RotateCrtcRedisplay(xf86CrtcPtr crtc, RegionPtr region)
1161+void
1162+xf86RotateCrtcRedisplay(xf86CrtcPtr crtc, PixmapPtr dst_pixmap,
1163+ DrawableRec *src_drawable, RegionPtr region,
1164+ Bool transform_src)
1165 {
1166 ScrnInfoPtr scrn = crtc->scrn;
1167 ScreenPtr screen = scrn->pScreen;
1168- WindowPtr root = screen->root;
1169- PixmapPtr dst_pixmap = crtc->rotatedPixmap;
1170 PictFormatPtr format = PictureWindowFormat(screen->root);
1171 int error;
1172 PicturePtr src, dst;
1173@@ -57,7 +57,7 @@
1174 return;
1175
1176 src = CreatePicture(None,
1177- &root->drawable,
1178+ src_drawable,
1179 format,
1180 CPSubwindowMode,
1181 &include_inferiors, serverClient, &error);
1182@@ -70,9 +70,11 @@
1183 if (!dst)
1184 return;
1185
1186- error = SetPictureTransform(src, &crtc->crtc_to_framebuffer);
1187- if (error)
1188- return;
1189+ if (transform_src) {
1190+ error = SetPictureTransform(src, &crtc->crtc_to_framebuffer);
1191+ if (error)
1192+ return;
1193+ }
1194 if (crtc->transform_in_use && crtc->filter)
1195 SetPicturePictFilter(src, crtc->filter, crtc->params, crtc->nparams);
1196
1197@@ -205,7 +207,9 @@
1198
1199 /* update damaged region */
1200 if (RegionNotEmpty(&crtc_damage))
1201- xf86RotateCrtcRedisplay(crtc, &crtc_damage);
1202+ xf86RotateCrtcRedisplay(crtc, crtc->rotatedPixmap,
1203+ &pScreen->root->drawable,
1204+ &crtc_damage, TRUE);
1205
1206 RegionUninit(&crtc_damage);
1207 }
1208Index: xorg-server-21.1.6/include/pixmap.h
1209===================================================================
1210--- xorg-server-21.1.6.orig/include/pixmap.h 2022-12-19 05:53:03.000000000 -0500
1211+++ xorg-server-21.1.6/include/pixmap.h 2023-01-09 10:11:45.541724797 -0500
1212@@ -134,4 +134,9 @@
1213 extern _X_EXPORT Bool
1214 PixmapSyncDirtyHelper(PixmapDirtyUpdatePtr dirty);
1215
1216+extern _X_EXPORT void
1217+PixmapDirtyCopyArea(PixmapPtr dst, DrawablePtr src,
1218+ int x, int y, int dst_x, int dst_y,
1219+ RegionPtr dirty_region);
1220+
1221 #endif /* PIXMAP_H */
1222Index: xorg-server-21.1.6/present/present.h
1223===================================================================
1224--- xorg-server-21.1.6.orig/present/present.h 2022-12-19 05:53:03.000000000 -0500
1225+++ xorg-server-21.1.6/present/present.h 2023-01-09 10:11:45.541724797 -0500
1226@@ -29,7 +29,9 @@
1227
1228 typedef enum {
1229 PRESENT_FLIP_REASON_UNKNOWN,
1230- PRESENT_FLIP_REASON_BUFFER_FORMAT
1231+ PRESENT_FLIP_REASON_BUFFER_FORMAT,
1232+ PRESENT_FLIP_REASON_DRIVER_TEARFREE,
1233+ PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING
1234 } PresentFlipReason;
1235
1236 typedef struct present_vblank present_vblank_rec, *present_vblank_ptr;
1237Index: xorg-server-21.1.6/present/present_scmd.c
1238===================================================================
1239--- xorg-server-21.1.6.orig/present/present_scmd.c 2023-01-09 10:11:15.134286918 -0500
1240+++ xorg-server-21.1.6/present/present_scmd.c 2023-01-09 10:11:45.541724797 -0500
1241@@ -71,6 +71,7 @@
1242 PixmapPtr window_pixmap;
1243 WindowPtr root = screen->root;
1244 present_screen_priv_ptr screen_priv = present_screen_priv(screen);
1245+ PresentFlipReason tmp_reason = PRESENT_FLIP_REASON_UNKNOWN;
1246
1247 if (crtc) {
1248 screen_priv = present_screen_priv(crtc->pScreen);
1249@@ -91,6 +92,27 @@
1250 if (!screen_priv->info->flip)
1251 return FALSE;
1252
1253+ /* Ask the driver for permission. Do this now to see if there's TearFree. */
1254+ if (screen_priv->info->version >= 1 && screen_priv->info->check_flip2) {
1255+ if (!(*screen_priv->info->check_flip2) (crtc, window, pixmap, sync_flip, &tmp_reason)) {
1256+ DebugPresent(("\td %08" PRIx32 " -> %08" PRIx32 "\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
1257+ /* It's fine to return now unless the page flip failure reason is
1258+ * PRESENT_FLIP_REASON_BUFFER_FORMAT; we must only output that
1259+ * reason if all the other checks pass.
1260+ */
1261+ if (!reason || tmp_reason != PRESENT_FLIP_REASON_BUFFER_FORMAT) {
1262+ if (reason)
1263+ *reason = tmp_reason;
1264+ return FALSE;
1265+ }
1266+ }
1267+ } else if (screen_priv->info->check_flip) {
1268+ if (!(*screen_priv->info->check_flip) (crtc, window, pixmap, sync_flip)) {
1269+ DebugPresent(("\td %08" PRIx32 " -> %08" PRIx32 "\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
1270+ return FALSE;
1271+ }
1272+ }
1273+
1274 /* Make sure the window hasn't been redirected with Composite */
1275 window_pixmap = screen->GetWindowPixmap(window);
1276 if (window_pixmap != screen->GetScreenPixmap(screen) &&
1277@@ -123,17 +145,10 @@
1278 return FALSE;
1279 }
1280
1281- /* Ask the driver for permission */
1282- if (screen_priv->info->version >= 1 && screen_priv->info->check_flip2) {
1283- if (!(*screen_priv->info->check_flip2) (crtc, window, pixmap, sync_flip, reason)) {
1284- DebugPresent(("\td %08" PRIx32 " -> %08" PRIx32 "\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
1285- return FALSE;
1286- }
1287- } else if (screen_priv->info->check_flip) {
1288- if (!(*screen_priv->info->check_flip) (crtc, window, pixmap, sync_flip)) {
1289- DebugPresent(("\td %08" PRIx32 " -> %08" PRIx32 "\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
1290- return FALSE;
1291- }
1292+ if (tmp_reason == PRESENT_FLIP_REASON_BUFFER_FORMAT) {
1293+ if (reason)
1294+ *reason = tmp_reason;
1295+ return FALSE;
1296 }
1297
1298 return TRUE;
1299@@ -462,7 +477,9 @@
1300 xorg_list_for_each_entry(vblank, &window_priv->vblank, window_list) {
1301 if (vblank->queued && vblank->flip && !present_check_flip(vblank->crtc, window, vblank->pixmap, vblank->sync_flip, NULL, 0, 0, &reason)) {
1302 vblank->flip = FALSE;
1303- vblank->reason = reason;
1304+ /* Don't spuriously flag this as a TearFree presentation */
1305+ if (reason < PRESENT_FLIP_REASON_DRIVER_TEARFREE)
1306+ vblank->reason = reason;
1307 if (vblank->sync_flip)
1308 vblank->exec_msc = vblank->target_msc;
1309 }
1310@@ -543,6 +560,7 @@
1311 WindowPtr window = vblank->window;
1312 ScreenPtr screen = window->drawable.pScreen;
1313 present_screen_priv_ptr screen_priv = present_screen_priv(screen);
1314+ uint64_t completion_msc;
1315 if (vblank && vblank->crtc) {
1316 screen_priv=present_screen_priv(vblank->crtc->pScreen);
1317 }
1318@@ -566,7 +584,9 @@
1319 xorg_list_del(&vblank->window_list);
1320 vblank->queued = FALSE;
1321
1322- if (vblank->pixmap && vblank->window) {
1323+ if (vblank->pixmap && vblank->window &&
1324+ (vblank->reason < PRESENT_FLIP_REASON_DRIVER_TEARFREE ||
1325+ vblank->exec_msc != vblank->target_msc)) {
1326
1327 if (vblank->flip) {
1328
1329@@ -633,6 +653,30 @@
1330
1331 present_execute_copy(vblank, crtc_msc);
1332
1333+ /* The presentation will be visible at the next vblank with TearFree, so
1334+ * the PresentComplete notification needs to be sent at the next vblank.
1335+ * If TearFree is already flipping then the presentation will be visible
1336+ * at the *next* next vblank.
1337+ */
1338+ completion_msc = crtc_msc + 1;
1339+ switch (vblank->reason) {
1340+ case PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING:
1341+ if (vblank->exec_msc < crtc_msc)
1342+ completion_msc++;
1343+ case PRESENT_FLIP_REASON_DRIVER_TEARFREE:
1344+ if (Success == screen_priv->queue_vblank(screen,
1345+ window,
1346+ vblank->crtc,
1347+ vblank->event_id,
1348+ completion_msc)) {
1349+ /* Ensure present_execute_post() runs at the next MSC */
1350+ vblank->exec_msc = vblank->target_msc;
1351+ vblank->queued = TRUE;
1352+ }
1353+ default:
1354+ break;
1355+ }
1356+
1357 if (vblank->queued) {
1358 xorg_list_add(&vblank->event_queue, &present_exec_queue);
1359 xorg_list_append(&vblank->window_list,
1360@@ -745,6 +789,11 @@
1361 if (vblank->crtc != target_crtc || vblank->target_msc != target_msc)
1362 continue;
1363
1364+ /* Too late to abort now if TearFree execution already happened */
1365+ if (vblank->reason >= PRESENT_FLIP_REASON_DRIVER_TEARFREE &&
1366+ vblank->exec_msc == vblank->target_msc)
1367+ continue;
1368+
1369 present_vblank_scrap(vblank);
1370 if (vblank->flip_ready)
1371 present_re_execute(vblank);
1372@@ -773,7 +822,12 @@
1373
1374 vblank->event_id = ++present_scmd_event_id;
1375
1376- if (vblank->flip && vblank->sync_flip)
1377+ /* The soonest presentation is crtc_msc+2 if TearFree is already flipping */
1378+ if (vblank->reason == PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING &&
1379+ !msc_is_after(vblank->exec_msc, crtc_msc + 1))
1380+ vblank->exec_msc -= 2;
1381+ else if (vblank->reason >= PRESENT_FLIP_REASON_DRIVER_TEARFREE ||
1382+ (vblank->flip && vblank->sync_flip))
1383 vblank->exec_msc--;
1384
1385 xorg_list_append(&vblank->event_queue, &present_exec_queue);
diff --git a/debian/patches/tearfree-present-fixes.patch b/debian/patches/tearfree-present-fixes.patch
0new file mode 1006441386new file mode 100644
index 0000000..ac3bb67
--- /dev/null
+++ b/debian/patches/tearfree-present-fixes.patch
@@ -0,0 +1,632 @@
1Description: Fix inaccurate PresentCompleteNotify timing for TearFree
2 Note that this patch can be considered a continuation of
3 tearfree-modesetting.patch and finishing its implementation. Similar changes
4 were initially part of the original upstream merge request but were split off
5 to make it more wieldy.
6Origin: upstream, https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1042
7
8Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/dri2.c
9===================================================================
10--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/dri2.c
11+++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/dri2.c
12@@ -483,7 +483,6 @@ ms_dri2_schedule_flip(ms_dri2_frame_even
13 modesettingPtr ms = modesettingPTR(scrn);
14 ms_dri2_buffer_private_ptr back_priv = info->back->driverPrivate;
15 struct ms_dri2_vblank_event *event;
16- drmmode_crtc_private_ptr drmmode_crtc = info->crtc->driver_private;
17
18 event = calloc(1, sizeof(struct ms_dri2_vblank_event));
19 if (!event)
20@@ -495,7 +494,7 @@ ms_dri2_schedule_flip(ms_dri2_frame_even
21 event->event_data = info->event_data;
22
23 if (ms_do_pageflip(screen, back_priv->pixmap, event,
24- drmmode_crtc->vblank_pipe, FALSE,
25+ info->crtc, FALSE,
26 ms_dri2_flip_handler,
27 ms_dri2_flip_abort,
28 "DRI2-flip")) {
29Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/driver.c
30===================================================================
31--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/driver.c
32+++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/driver.c
33@@ -623,9 +623,11 @@ ms_tearfree_do_flips(ScreenPtr pScreen)
34 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
35 drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
36
37- /* Skip disabled CRTCs and those which aren't using TearFree */
38- if (!trf->buf[0].px || !crtc->scrn->vtSema || !xf86_crtc_on(crtc))
39+ if (!ms_tearfree_is_active_on_crtc(crtc)) {
40+ /* Notify any lingering DRI clients waiting for a flip to finish */
41+ ms_tearfree_dri_abort_all(crtc);
42 continue;
43+ }
44
45 /* Skip if the last flip is still pending, a DRI client is flipping, or
46 * there isn't any damage on the front buffer.
47Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/driver.h
48===================================================================
49--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/driver.h
50+++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/driver.h
51@@ -236,12 +236,20 @@ typedef void (*ms_pageflip_abort_proc)(m
52 Bool ms_do_pageflip(ScreenPtr screen,
53 PixmapPtr new_front,
54 void *event,
55- int ref_crtc_vblank_pipe,
56+ xf86CrtcPtr ref_crtc,
57 Bool async,
58 ms_pageflip_handler_proc pageflip_handler,
59 ms_pageflip_abort_proc pageflip_abort,
60 const char *log_prefix);
61
62+Bool
63+ms_tearfree_dri_abort(xf86CrtcPtr crtc,
64+ Bool (*match)(void *data, void *match_data),
65+ void *match_data);
66+
67+void
68+ms_tearfree_dri_abort_all(xf86CrtcPtr crtc);
69+
70 Bool ms_do_tearfree_flip(ScreenPtr screen, xf86CrtcPtr crtc);
71
72 #endif
73@@ -249,3 +257,4 @@ Bool ms_do_tearfree_flip(ScreenPtr scree
74 int ms_flush_drm_events(ScreenPtr screen);
75 Bool ms_window_has_variable_refresh(modesettingPtr ms, WindowPtr win);
76 void ms_present_set_screen_vrr(ScrnInfoPtr scrn, Bool vrr_enabled);
77+Bool ms_tearfree_is_active_on_crtc(xf86CrtcPtr crtc);
78Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/drmmode_display.c
79===================================================================
80--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/drmmode_display.c
81+++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/drmmode_display.c
82@@ -2529,6 +2529,7 @@ drmmode_crtc_init(ScrnInfoPtr pScrn, drm
83 drmmode_crtc->drmmode = drmmode;
84 drmmode_crtc->vblank_pipe = drmmode_crtc_vblank_pipe(num);
85 xorg_list_init(&drmmode_crtc->mode_list);
86+ xorg_list_init(&drmmode_crtc->tearfree.dri_flip_list);
87 drmmode_crtc->next_msc = UINT64_MAX;
88
89 props = drmModeObjectGetProperties(drmmode->fd, mode_res->crtcs[num],
90Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/drmmode_display.h
91===================================================================
92--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/drmmode_display.h
93+++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/drmmode_display.h
94@@ -176,6 +176,7 @@ typedef struct {
95
96 typedef struct {
97 drmmode_shadow_fb_rec buf[2];
98+ struct xorg_list dri_flip_list;
99 uint32_t back_idx;
100 uint32_t flip_seq;
101 } drmmode_tearfree_rec, *drmmode_tearfree_ptr;
102Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/pageflip.c
103===================================================================
104--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/pageflip.c
105+++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/pageflip.c
106@@ -99,6 +99,8 @@ struct ms_crtc_pageflip {
107 Bool on_reference_crtc;
108 /* reference to the ms_flipdata */
109 struct ms_flipdata *flipdata;
110+ struct xorg_list node;
111+ uint32_t tearfree_seq;
112 };
113
114 /**
115@@ -142,7 +144,8 @@ ms_pageflip_handler(uint64_t msc, uint64
116 flipdata->fe_usec,
117 flipdata->event);
118
119- drmModeRmFB(ms->fd, flipdata->old_fb_id);
120+ if (flipdata->old_fb_id)
121+ drmModeRmFB(ms->fd, flipdata->old_fb_id);
122 }
123 ms_pageflip_free(flip);
124 }
125@@ -204,11 +207,10 @@ enum queue_flip_status {
126 static int
127 queue_flip_on_crtc(ScreenPtr screen, xf86CrtcPtr crtc,
128 struct ms_flipdata *flipdata,
129- int ref_crtc_vblank_pipe, uint32_t flags)
130+ xf86CrtcPtr ref_crtc, uint32_t flags)
131 {
132 ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
133 modesettingPtr ms = modesettingPTR(scrn);
134- drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
135 struct ms_crtc_pageflip *flip;
136 uint32_t seq;
137
138@@ -220,7 +222,7 @@ queue_flip_on_crtc(ScreenPtr screen, xf8
139 /* Only the reference crtc will finally deliver its page flip
140 * completion event. All other crtc's events will be discarded.
141 */
142- flip->on_reference_crtc = (drmmode_crtc->vblank_pipe == ref_crtc_vblank_pipe);
143+ flip->on_reference_crtc = crtc == ref_crtc;
144 flip->flipdata = flipdata;
145
146 seq = ms_drm_queue_alloc(crtc, flip, ms_pageflip_handler, ms_pageflip_abort);
147@@ -310,20 +312,75 @@ ms_print_pageflip_error(int screen_index
148 }
149 }
150
151+static Bool
152+ms_tearfree_dri_flip(modesettingPtr ms, xf86CrtcPtr crtc, void *event,
153+ ms_pageflip_handler_proc pageflip_handler,
154+ ms_pageflip_abort_proc pageflip_abort)
155+{
156+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
157+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
158+ struct ms_crtc_pageflip *flip;
159+ struct ms_flipdata *flipdata;
160+ RegionRec region;
161+ RegionPtr dirty;
162+
163+ if (!ms_tearfree_is_active_on_crtc(crtc))
164+ return FALSE;
165+
166+ /* Check for damage on the primary scanout to know if TearFree will flip */
167+ dirty = DamageRegion(ms->damage);
168+ if (RegionNil(dirty))
169+ return FALSE;
170+
171+ /* Compute how much of the current damage intersects with this CRTC */
172+ RegionInit(&region, &crtc->bounds, 0);
173+ RegionIntersect(&region, &region, dirty);
174+
175+ /* No damage on this CRTC means no TearFree flip. This means the DRI client
176+ * didn't change this CRTC's contents at all with its presentation, possibly
177+ * because its window is fully occluded by another window on this CRTC.
178+ */
179+ if (RegionNil(&region))
180+ return FALSE;
181+
182+ flip = calloc(1, sizeof(*flip));
183+ if (!flip)
184+ return FALSE;
185+
186+ flipdata = calloc(1, sizeof(*flipdata));
187+ if (!flipdata) {
188+ free(flip);
189+ return FALSE;
190+ }
191+
192+ /* Only track the DRI client's fake flip on the reference CRTC, which aligns
193+ * with the behavior of Present when a client copies its pixmap rather than
194+ * directly flipping it onto the display.
195+ */
196+ flip->on_reference_crtc = TRUE;
197+ flip->flipdata = flipdata;
198+ flip->tearfree_seq = trf->flip_seq;
199+ flipdata->screen = xf86ScrnToScreen(crtc->scrn);
200+ flipdata->event = event;
201+ flipdata->flip_count = 1;
202+ flipdata->event_handler = pageflip_handler;
203+ flipdata->abort_handler = pageflip_abort;
204+
205+ /* Keep the list in FIFO order so that clients are notified in order */
206+ xorg_list_append(&flip->node, &trf->dri_flip_list);
207+ return TRUE;
208+}
209
210 Bool
211 ms_do_pageflip(ScreenPtr screen,
212 PixmapPtr new_front,
213 void *event,
214- int ref_crtc_vblank_pipe,
215+ xf86CrtcPtr ref_crtc,
216 Bool async,
217 ms_pageflip_handler_proc pageflip_handler,
218 ms_pageflip_abort_proc pageflip_abort,
219 const char *log_prefix)
220 {
221-#ifndef GLAMOR_HAS_GBM
222- return FALSE;
223-#else
224 ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
225 modesettingPtr ms = modesettingPTR(scrn);
226 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
227@@ -331,6 +388,22 @@ ms_do_pageflip(ScreenPtr screen,
228 uint32_t flags;
229 int i;
230 struct ms_flipdata *flipdata;
231+
232+ /* A NULL pixmap indicates this DRI client's pixmap is to be flipped through
233+ * TearFree instead. The pixmap is already copied to the primary scanout at
234+ * this point, so all that's left is to wire up this fake flip to TearFree
235+ * so that TearFree can send a notification to the DRI client when the
236+ * pixmap actually appears on the display. This is the only way to let DRI
237+ * clients accurately know when their pixmaps appear on the display when
238+ * TearFree is enabled.
239+ */
240+ if (!new_front) {
241+ if (!ms_tearfree_dri_flip(ms, ref_crtc, event, pageflip_handler,
242+ pageflip_abort))
243+ goto error_free_event;
244+ return TRUE;
245+ }
246+
247 ms->glamor.block_handler(screen);
248
249 new_front_bo.gbm = ms->glamor.gbm_bo_from_pixmap(screen, new_front);
250@@ -340,7 +413,7 @@ ms_do_pageflip(ScreenPtr screen,
251 xf86DrvMsg(scrn->scrnIndex, X_ERROR,
252 "%s: Failed to get GBM BO for flip to new front.\n",
253 log_prefix);
254- return FALSE;
255+ goto error_free_event;
256 }
257
258 flipdata = calloc(1, sizeof(struct ms_flipdata));
259@@ -348,7 +421,7 @@ ms_do_pageflip(ScreenPtr screen,
260 drmmode_bo_destroy(&ms->drmmode, &new_front_bo);
261 xf86DrvMsg(scrn->scrnIndex, X_ERROR,
262 "%s: Failed to allocate flipdata.\n", log_prefix);
263- return FALSE;
264+ goto error_free_event;
265 }
266
267 flipdata->event = event;
268@@ -396,7 +469,6 @@ ms_do_pageflip(ScreenPtr screen,
269 for (i = 0; i < config->num_crtc; i++) {
270 enum queue_flip_status flip_status;
271 xf86CrtcPtr crtc = config->crtc[i];
272- drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
273
274 if (!xf86_crtc_on(crtc))
275 continue;
276@@ -417,13 +489,11 @@ ms_do_pageflip(ScreenPtr screen,
277 * outputs in a "clone-mode" or "mirror-mode" configuration.
278 */
279 if (ms->drmmode.can_async_flip && ms->drmmode.async_flip_secondaries &&
280- (drmmode_crtc->vblank_pipe != ref_crtc_vblank_pipe) &&
281- (ref_crtc_vblank_pipe >= 0))
282+ ref_crtc && crtc != ref_crtc)
283 flags |= DRM_MODE_PAGE_FLIP_ASYNC;
284
285 flip_status = queue_flip_on_crtc(screen, crtc, flipdata,
286- ref_crtc_vblank_pipe,
287- flags);
288+ ref_crtc, flags);
289
290 switch (flip_status) {
291 case QUEUE_FLIP_ALLOC_FAILED:
292@@ -472,31 +542,106 @@ error_out:
293 drmmode_bo_destroy(&ms->drmmode, &new_front_bo);
294 /* if only the local reference - free the structure,
295 * else drop the local reference and return */
296- if (flipdata->flip_count == 1)
297+ if (flipdata->flip_count == 1) {
298 free(flipdata);
299- else
300+ } else {
301 flipdata->flip_count--;
302+ return FALSE;
303+ }
304
305+error_free_event:
306+ /* Free the event since the caller has no way to know it's safe to free */
307+ free(event);
308 return FALSE;
309-#endif /* GLAMOR_HAS_GBM */
310+}
311+
312+Bool
313+ms_tearfree_dri_abort(xf86CrtcPtr crtc,
314+ Bool (*match)(void *data, void *match_data),
315+ void *match_data)
316+{
317+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
318+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
319+ struct ms_crtc_pageflip *flip;
320+
321+ /* The window is getting destroyed; abort without notifying the client */
322+ xorg_list_for_each_entry(flip, &trf->dri_flip_list, node) {
323+ if (match(flip->flipdata->event, match_data)) {
324+ xorg_list_del(&flip->node);
325+ ms_pageflip_abort(flip);
326+ return TRUE;
327+ }
328+ }
329+
330+ return FALSE;
331+}
332+
333+void
334+ms_tearfree_dri_abort_all(xf86CrtcPtr crtc)
335+{
336+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
337+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
338+ struct ms_crtc_pageflip *flip, *tmp;
339+ uint64_t usec = 0, msc = 0;
340+
341+ /* Nothing to abort if there aren't any DRI clients waiting for a flip */
342+ if (xorg_list_is_empty(&trf->dri_flip_list))
343+ return;
344+
345+ /* Even though we're aborting, these clients' pixmaps were actually blitted,
346+ * so technically the presentation isn't aborted. That's why the normal
347+ * handler is called instead of the abort handler, along with the current
348+ * time and MSC for this CRTC.
349+ */
350+ ms_get_crtc_ust_msc(crtc, &usec, &msc);
351+ xorg_list_for_each_entry_safe(flip, tmp, &trf->dri_flip_list, node)
352+ ms_pageflip_handler(msc, usec, flip);
353+ xorg_list_init(&trf->dri_flip_list);
354+}
355+
356+static void
357+ms_tearfree_dri_notify(drmmode_tearfree_ptr trf, uint64_t msc, uint64_t usec)
358+{
359+ struct ms_crtc_pageflip *flip, *tmp;
360+
361+ xorg_list_for_each_entry_safe(flip, tmp, &trf->dri_flip_list, node) {
362+ /* If a TearFree flip was already pending at the time this DRI client's
363+ * pixmap was copied, then the pixmap isn't contained in this TearFree
364+ * flip, but will be part of the next TearFree flip instead.
365+ */
366+ if (flip->tearfree_seq) {
367+ flip->tearfree_seq = 0;
368+ } else {
369+ xorg_list_del(&flip->node);
370+ ms_pageflip_handler(msc, usec, flip);
371+ }
372+ }
373 }
374
375 static void
376 ms_tearfree_flip_abort(void *data)
377 {
378- drmmode_tearfree_ptr trf = data;
379+ xf86CrtcPtr crtc = data;
380+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
381+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
382
383 trf->flip_seq = 0;
384+ ms_tearfree_dri_abort_all(crtc);
385 }
386
387 static void
388 ms_tearfree_flip_handler(uint64_t msc, uint64_t usec, void *data)
389 {
390- drmmode_tearfree_ptr trf = data;
391+ xf86CrtcPtr crtc = data;
392+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
393+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
394
395 /* Swap the buffers and complete the flip */
396 trf->back_idx ^= 1;
397 trf->flip_seq = 0;
398+
399+ /* Notify DRI clients that their pixmaps are now visible on the display */
400+ ms_tearfree_dri_notify(trf, msc, usec);
401 }
402
403 Bool
404@@ -506,10 +651,16 @@ ms_do_tearfree_flip(ScreenPtr screen, xf
405 drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
406 uint32_t idx = trf->back_idx, seq;
407
408- seq = ms_drm_queue_alloc(crtc, trf, ms_tearfree_flip_handler,
409+ seq = ms_drm_queue_alloc(crtc, crtc, ms_tearfree_flip_handler,
410 ms_tearfree_flip_abort);
411- if (!seq)
412+ if (!seq) {
413+ /* Need to notify the DRI clients if a sequence wasn't allocated. Once a
414+ * sequence is allocated, explicitly performing this cleanup isn't
415+ * necessary since it's already done as part of aborting the sequence.
416+ */
417+ ms_tearfree_dri_abort_all(crtc);
418 goto no_flip;
419+ }
420
421 /* Copy the damage to the back buffer and then flip it at the vblank */
422 drmmode_copy_damage(crtc, trf->buf[idx].px, &trf->buf[idx].dmg, TRUE);
423@@ -528,3 +679,13 @@ no_flip:
424 return TRUE;
425 }
426 #endif
427+
428+Bool
429+ms_tearfree_is_active_on_crtc(xf86CrtcPtr crtc)
430+{
431+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
432+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
433+
434+ /* If TearFree is enabled, XServer owns the VT, and the CRTC is active */
435+ return trf->buf[0].px && crtc->scrn->vtSema && xf86_crtc_on(crtc);
436+}
437Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/present.c
438===================================================================
439--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/present.c
440+++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/present.c
441@@ -165,6 +165,13 @@ ms_present_abort_vblank(RRCrtcPtr crtc,
442 {
443 ScreenPtr screen = crtc->pScreen;
444 ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
445+#ifdef GLAMOR_HAS_GBM
446+ xf86CrtcPtr xf86_crtc = crtc->devPrivate;
447+
448+ /* Check if this is a fake flip routed through TearFree and abort it */
449+ if (ms_tearfree_dri_abort(xf86_crtc, ms_present_event_match, &event_id))
450+ return;
451+#endif
452
453 ms_drm_abort(scrn, ms_present_event_match, &event_id);
454 }
455@@ -329,12 +336,12 @@ ms_present_check_flip(RRCrtcPtr crtc,
456
457 no_flip:
458 /* Export some info about TearFree if Present can't flip anyway */
459- if (reason && ms->drmmode.tearfree_enable) {
460+ if (reason) {
461 xf86CrtcPtr xf86_crtc = crtc->devPrivate;
462 drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
463 drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
464
465- if (trf->buf[0].px) {
466+ if (ms_tearfree_is_active_on_crtc(xf86_crtc)) {
467 if (trf->flip_seq)
468 /* The driver has a TearFree flip pending */
469 *reason = PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING;
470@@ -361,11 +368,12 @@ ms_present_flip(RRCrtcPtr crtc,
471 ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
472 modesettingPtr ms = modesettingPTR(scrn);
473 xf86CrtcPtr xf86_crtc = crtc->devPrivate;
474- drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
475 Bool ret;
476 struct ms_present_vblank_event *event;
477
478- if (!ms_present_check_flip(crtc, ms->flip_window, pixmap, sync_flip, NULL))
479+ /* A NULL pixmap means this is a fake flip to be routed through TearFree */
480+ if (pixmap &&
481+ !ms_present_check_flip(crtc, ms->flip_window, pixmap, sync_flip, NULL))
482 return FALSE;
483
484 event = calloc(1, sizeof(struct ms_present_vblank_event));
485@@ -378,6 +386,12 @@ ms_present_flip(RRCrtcPtr crtc,
486 event->event_id = event_id;
487 event->unflip = FALSE;
488
489+ /* Register the fake flip (indicated by a NULL pixmap) with TearFree */
490+ if (!pixmap)
491+ return ms_do_pageflip(screen, NULL, event, xf86_crtc, FALSE,
492+ ms_present_flip_handler, ms_present_flip_abort,
493+ "Present-TearFree-flip");
494+
495 /* A window can only flip if it covers the entire X screen.
496 * Only one window can flip at a time.
497 *
498@@ -389,7 +403,7 @@ ms_present_flip(RRCrtcPtr crtc,
499 ms_present_set_screen_vrr(scrn, TRUE);
500 }
501
502- ret = ms_do_pageflip(screen, pixmap, event, drmmode_crtc->vblank_pipe, !sync_flip,
503+ ret = ms_do_pageflip(screen, pixmap, event, xf86_crtc, !sync_flip,
504 ms_present_flip_handler, ms_present_flip_abort,
505 "Present-flip");
506 if (ret)
507@@ -421,7 +435,7 @@ ms_present_unflip(ScreenPtr screen, uint
508 event->unflip = TRUE;
509
510 if (ms_present_check_unflip(NULL, screen->root, pixmap, TRUE, NULL) &&
511- ms_do_pageflip(screen, pixmap, event, -1, FALSE,
512+ ms_do_pageflip(screen, pixmap, event, NULL, FALSE,
513 ms_present_flip_handler, ms_present_flip_abort,
514 "Present-unflip")) {
515 return;
516Index: xorg-server-21.1.6/hw/xfree86/drivers/modesetting/vblank.c
517===================================================================
518--- xorg-server-21.1.6.orig/hw/xfree86/drivers/modesetting/vblank.c
519+++ xorg-server-21.1.6/hw/xfree86/drivers/modesetting/vblank.c
520@@ -573,10 +573,15 @@ ms_drm_sequence_handler(int fd, uint64_t
521 if (q->seq == seq) {
522 crtc = q->crtc;
523 msc = ms_kernel_msc_to_crtc_msc(crtc, frame, is64bit);
524- xorg_list_del(&q->list);
525- if (!q->aborted)
526- q->handler(msc, ns / 1000, q->data);
527- free(q);
528+
529+ /* Write the current MSC to this event to ensure its handler runs in
530+ * the loop below. This is done because we don't want to run the
531+ * handler right now, since we need to ensure all events are handled
532+ * in FIFO order with respect to one another. Otherwise, if this
533+ * event were handled first just because it was queued to the
534+ * kernel, it could run before older events expiring at this MSC.
535+ */
536+ q->msc = msc;
537 break;
538 }
539 }
540Index: xorg-server-21.1.6/present/present.h
541===================================================================
542--- xorg-server-21.1.6.orig/present/present.h
543+++ xorg-server-21.1.6/present/present.h
544@@ -30,6 +30,12 @@
545 typedef enum {
546 PRESENT_FLIP_REASON_UNKNOWN,
547 PRESENT_FLIP_REASON_BUFFER_FORMAT,
548+
549+ /* Don't add new flip reasons after the TearFree ones, since it's expected
550+ * that the TearFree reasons are the highest ones in order to allow doing
551+ * `reason >= PRESENT_FLIP_REASON_DRIVER_TEARFREE` to check if a reason is
552+ * PRESENT_FLIP_REASON_DRIVER_TEARFREE{_FLIPPING}.
553+ */
554 PRESENT_FLIP_REASON_DRIVER_TEARFREE,
555 PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING
556 } PresentFlipReason;
557Index: xorg-server-21.1.6/present/present_scmd.c
558===================================================================
559--- xorg-server-21.1.6.orig/present/present_scmd.c
560+++ xorg-server-21.1.6/present/present_scmd.c
561@@ -560,7 +560,6 @@ present_execute(present_vblank_ptr vblan
562 WindowPtr window = vblank->window;
563 ScreenPtr screen = window->drawable.pScreen;
564 present_screen_priv_ptr screen_priv = present_screen_priv(screen);
565- uint64_t completion_msc;
566 if (vblank && vblank->crtc) {
567 screen_priv=present_screen_priv(vblank->crtc->pScreen);
568 }
569@@ -653,28 +652,49 @@ present_execute(present_vblank_ptr vblan
570
571 present_execute_copy(vblank, crtc_msc);
572
573- /* The presentation will be visible at the next vblank with TearFree, so
574- * the PresentComplete notification needs to be sent at the next vblank.
575- * If TearFree is already flipping then the presentation will be visible
576- * at the *next* next vblank.
577+ /* With TearFree, there's no way to tell exactly when the presentation
578+ * will be visible except by waiting for a notification from the kernel
579+ * driver indicating that the page flip is complete. This is because the
580+ * CRTC's MSC can change while the target MSC is calculated and even
581+ * while the page flip IOCTL is sent to the kernel due to scheduling
582+ * delays and/or unfortunate timing. Even worse, a page flip isn't
583+ * actually guaranteed to be finished after one vblank; it may be
584+ * several MSCs until a flip actually finishes depending on delays and
585+ * load in hardware.
586+ *
587+ * So, to get a notification from the driver with TearFree active, the
588+ * driver expects a present_flip() call with a NULL pixmap to indicate
589+ * that this is a fake flip for a pixmap that's already been copied to
590+ * the primary scanout, which will then be flipped by TearFree. TearFree
591+ * will then send a notification once the flip containing this pixmap is
592+ * complete.
593+ *
594+ * If the fake flip attempt fails, then fall back to just enqueuing a
595+ * vblank event targeting the next MSC.
596 */
597- completion_msc = crtc_msc + 1;
598- switch (vblank->reason) {
599- case PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING:
600- if (vblank->exec_msc < crtc_msc)
601- completion_msc++;
602- case PRESENT_FLIP_REASON_DRIVER_TEARFREE:
603- if (Success == screen_priv->queue_vblank(screen,
604+ if (!vblank->queued &&
605+ vblank->reason >= PRESENT_FLIP_REASON_DRIVER_TEARFREE) {
606+ uint64_t completion_msc = crtc_msc + 1;
607+
608+ /* If TearFree is already flipping then the presentation will be
609+ * visible at the *next* next vblank. This calculation only matters
610+ * for the vblank event fallback.
611+ */
612+ if (vblank->reason == PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING &&
613+ vblank->exec_msc < crtc_msc)
614+ completion_msc++;
615+
616+ /* Try the fake flip first and then fall back to a vblank event */
617+ if (present_flip(vblank->crtc, vblank->event_id, 0, NULL, TRUE) ||
618+ Success == screen_priv->queue_vblank(screen,
619 window,
620 vblank->crtc,
621 vblank->event_id,
622 completion_msc)) {
623- /* Ensure present_execute_post() runs at the next MSC */
624+ /* Ensure present_execute_post() runs at the next execution */
625 vblank->exec_msc = vblank->target_msc;
626 vblank->queued = TRUE;
627 }
628- default:
629- break;
630 }
631
632 if (vblank->queued) {

Subscribers

People subscribed via source and target branches