summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Eure <ian@retrospec.tv>2026-03-27 20:49:08 -0700
committerIan Eure <ian@retrospec.tv>2026-03-28 09:01:48 -0700
commitdd080e7fda2be54e2bcec3814473f90b326cb256 (patch)
tree3e8b49e88c078c47d682ba7ee8a72f03401e4d61
parent990268790761dd49c4944742ebb73e7fa1442ab2 (diff)
gnu: xorg-server: Apply tearfree patch for modesetting driver.
Much of the modern Linux video stack depends on using kernel modesetting (KMS) drivers. Because of this, not much effort has been put into the legacy stuff, which have grown less supported and more buggy. Unfortunately, the modesetting Xorg driver didn’t initially have support for tear-free video (which most legacy drivers did), requiring additional software -- a compositor -- to fix this. Sadly, there’s no good option for a compositor -- they all require hardware-specific tweaking and tuning, and tend to be quite buggy. Support for a "TearFree" option merged in 2022[1], but there hasn’t been an Xorg release since it was merged. This PR applies the patch from that MR to the last release. It required a one-character edit to apply cleanly, necesitating inclusion in the Guix repo. I tested this on bare metal and it appears to work well. [1]: https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1006 * gnu/local.mk (dist_patch_DATA): Add patch. * gnu/packages/patches/xorg-server-tearfree-modesetting.patch: New file. * gnu/packages/xorg.scm (xorg-server): Apply modesetting tearfree patch. Change-Id: I60a705b35cb51bfd7de79aba406bc4b7b3934e48
-rw-r--r--gnu/local.mk1
-rw-r--r--gnu/packages/patches/xorg-server-tearfree-modesetting.patch1403
-rw-r--r--gnu/packages/xorg.scm15
3 files changed, 1414 insertions, 5 deletions
diff --git a/gnu/local.mk b/gnu/local.mk
index fc3bb46bc95..d4019b94f1f 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -2606,6 +2606,7 @@ dist_patch_DATA = \
2606 %D%/packages/patches/xiphos-glib.patch \ 2606 %D%/packages/patches/xiphos-glib.patch \
2607 %D%/packages/patches/xlispstat-fix-compilation-with-modern-gcc.patch \ 2607 %D%/packages/patches/xlispstat-fix-compilation-with-modern-gcc.patch \
2608 %D%/packages/patches/xmonad-dynamic-linking.patch \ 2608 %D%/packages/patches/xmonad-dynamic-linking.patch \
2609 %D%/packages/patches/xorg-server-tearfree-modesetting.patch \
2609 %D%/packages/patches/xplanet-1.3.1-cxx11-eof.patch \ 2610 %D%/packages/patches/xplanet-1.3.1-cxx11-eof.patch \
2610 %D%/packages/patches/xplanet-1.3.1-libdisplay_DisplayOutput.cpp.patch \ 2611 %D%/packages/patches/xplanet-1.3.1-libdisplay_DisplayOutput.cpp.patch \
2611 %D%/packages/patches/xplanet-1.3.1-libimage_gif.c.patch \ 2612 %D%/packages/patches/xplanet-1.3.1-libimage_gif.c.patch \
diff --git a/gnu/packages/patches/xorg-server-tearfree-modesetting.patch b/gnu/packages/patches/xorg-server-tearfree-modesetting.patch
new file mode 100644
index 00000000000..8ea8055d920
--- /dev/null
+++ b/gnu/packages/patches/xorg-server-tearfree-modesetting.patch
@@ -0,0 +1,1403 @@
1Patch taken from upstream MR
2https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1006 -- merged,
3but not yet included in a release.
4
5A minor edit to the ABI_VIDEODRV_VERSION hunk was needed to apply to 21.1.21.
6
7
8diff --git a/dix/pixmap.c b/dix/pixmap.c
9index 5a0146bbb667a07bee5b53ff5390e435976d2d4b..0b01c4ee0640677fbd2f2fff36ad1517135e0a4e 100644
10--- a/dix/pixmap.c
11+++ b/dix/pixmap.c
12@@ -262,12 +262,11 @@ PixmapStopDirtyTracking(DrawablePtr src, PixmapPtr secondary_dst)
13 return TRUE;
14 }
15
16-static void
17-PixmapDirtyCopyArea(PixmapPtr dst,
18- PixmapDirtyUpdatePtr dirty,
19+void
20+PixmapDirtyCopyArea(PixmapPtr dst, DrawablePtr src,
21+ int x, int y, int dst_x, int dst_y,
22 RegionPtr dirty_region)
23 {
24- DrawablePtr src = dirty->src;
25 ScreenPtr pScreen = src->pScreen;
26 int n;
27 BoxPtr b;
28@@ -294,9 +293,8 @@ PixmapDirtyCopyArea(PixmapPtr dst,
29 h = dst_box.y2 - dst_box.y1;
30
31 pGC->ops->CopyArea(src, &dst->drawable, pGC,
32- dirty->x + dst_box.x1, dirty->y + dst_box.y1, w, h,
33- dirty->dst_x + dst_box.x1,
34- dirty->dst_y + dst_box.y1);
35+ x + dst_box.x1, y + dst_box.y1, w, h,
36+ dst_x + dst_box.x1, dst_y + dst_box.y1);
37 b++;
38 }
39 FreeScratchGC(pGC);
40@@ -408,7 +406,8 @@ Bool PixmapSyncDirtyHelper(PixmapDirtyUpdatePtr dirty)
41 RegionTranslate(&pixregion, -dirty->x, -dirty->y);
42
43 if (!pScreen->root || dirty->rotation == RR_Rotate_0)
44- PixmapDirtyCopyArea(dst, dirty, &pixregion);
45+ PixmapDirtyCopyArea(dst, dirty->src, dirty->x, dirty->y,
46+ dirty->dst_x, dirty->dst_y, &pixregion);
47 else
48 PixmapDirtyCompositeRotate(dst, dirty, &pixregion);
49 pScreen->SourceValidate = SourceValidate;
50diff --git a/hw/xfree86/common/xf86Module.h b/hw/xfree86/common/xf86Module.h
51index 33cf0e56a45b0202ab45cfc86538fd43e550fc80..c59e53a55c68f1a55262e3b85865192e68380a5f 100644
52--- a/hw/xfree86/common/xf86Module.h
53+++ b/hw/xfree86/common/xf86Module.h
54@@ -74,7 +74,7 @@
55 * mask is 0xFFFF0000.
56 */
57 #define ABI_ANSIC_VERSION SET_ABI_VERSION(0, 4)
58-#define ABI_VIDEODRV_VERSION SET_ABI_VERSION(25, 2)
59+#define ABI_VIDEODRV_VERSION SET_ABI_VERSION(25, 4)
60 #define ABI_XINPUT_VERSION SET_ABI_VERSION(24, 4)
61 #define ABI_EXTENSION_VERSION SET_ABI_VERSION(10, 0)
62
63diff --git a/hw/xfree86/drivers/modesetting/driver.c b/hw/xfree86/drivers/modesetting/driver.c
64index fe3315a9c49cfdb545f062e77db46fc2e3dcc42e..7a7d5c3887fcf226347582c3c3482ab395aa04e3 100644
65--- a/hw/xfree86/drivers/modesetting/driver.c
66+++ b/hw/xfree86/drivers/modesetting/driver.c
67@@ -145,6 +145,7 @@ static const OptionInfoRec Options[] = {
68 {OPTION_VARIABLE_REFRESH, "VariableRefresh", OPTV_BOOLEAN, {0}, FALSE},
69 {OPTION_USE_GAMMA_LUT, "UseGammaLUT", OPTV_BOOLEAN, {0}, FALSE},
70 {OPTION_ASYNC_FLIP_SECONDARIES, "AsyncFlipSecondaries", OPTV_BOOLEAN, {0}, FALSE},
71+ {OPTION_TEARFREE, "TearFree", OPTV_BOOLEAN, {0}, FALSE},
72 {-1, NULL, OPTV_NONE, {0}, FALSE}
73 };
74
75@@ -548,14 +549,16 @@ rotate_clip(PixmapPtr pixmap, BoxPtr rect, drmModeClip *clip, Rotation rotation)
76 }
77
78 static int
79-dispatch_dirty_region(ScrnInfoPtr scrn, xf86CrtcPtr crtc,
80- PixmapPtr pixmap, DamagePtr damage, int fb_id)
81+dispatch_damages(ScrnInfoPtr scrn, xf86CrtcPtr crtc, RegionPtr dirty,
82+ PixmapPtr pixmap, DamagePtr damage, int fb_id)
83 {
84 modesettingPtr ms = modesettingPTR(scrn);
85- RegionPtr dirty = DamageRegion(damage);
86 unsigned num_cliprects = REGION_NUM_RECTS(dirty);
87 int ret = 0;
88
89+ if (!ms->dirty_enabled)
90+ return 0;
91+
92 if (num_cliprects) {
93 drmModeClip *clip = xallocarray(num_cliprects, sizeof(drmModeClip));
94 BoxPtr rect = REGION_RECTS(dirty);
95@@ -579,12 +582,102 @@ dispatch_dirty_region(ScrnInfoPtr scrn, xf86CrtcPtr crtc,
96 }
97 }
98
99+ if (ret == -EINVAL || ret == -ENOSYS) {
100+ xf86DrvMsg(scrn->scrnIndex, X_INFO,
101+ "Disabling kernel dirty updates, not required.\n");
102+ ms->dirty_enabled = FALSE;
103+ }
104+
105 free(clip);
106- DamageEmpty(damage);
107+ if (damage)
108+ DamageEmpty(damage);
109 }
110 return ret;
111 }
112
113+static int
114+dispatch_dirty_region(ScrnInfoPtr scrn, xf86CrtcPtr crtc,
115+ PixmapPtr pixmap, DamagePtr damage, int fb_id)
116+{
117+ return dispatch_damages(scrn, crtc, DamageRegion(damage),
118+ pixmap, damage, fb_id);
119+}
120+
121+static void
122+ms_tearfree_update_damages(ScreenPtr pScreen)
123+{
124+ ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
125+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
126+ modesettingPtr ms = modesettingPTR(scrn);
127+ RegionPtr dirty = DamageRegion(ms->damage);
128+ int c, i;
129+
130+ if (RegionNil(dirty))
131+ return;
132+
133+ for (c = 0; c < xf86_config->num_crtc; c++) {
134+ xf86CrtcPtr crtc = xf86_config->crtc[c];
135+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
136+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
137+ RegionRec region;
138+
139+ /* Compute how much of the damage intersects with this CRTC */
140+ RegionInit(&region, &crtc->bounds, 0);
141+ RegionIntersect(&region, &region, dirty);
142+
143+ if (trf->buf[0].px) {
144+ for (i = 0; i < ARRAY_SIZE(trf->buf); i++)
145+ RegionUnion(&trf->buf[i].dmg, &trf->buf[i].dmg, &region);
146+ } else {
147+ /* Just notify the kernel of the damages if TearFree isn't used */
148+ dispatch_damages(scrn, crtc, &region,
149+ pScreen->GetScreenPixmap(pScreen),
150+ NULL, ms->drmmode.fb_id);
151+ }
152+ }
153+ DamageEmpty(ms->damage);
154+}
155+
156+static void
157+ms_tearfree_do_flips(ScreenPtr pScreen)
158+{
159+#ifdef GLAMOR_HAS_GBM
160+ ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
161+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
162+ modesettingPtr ms = modesettingPTR(scrn);
163+ int c;
164+
165+ if (!ms->drmmode.tearfree_enable)
166+ return;
167+
168+ for (c = 0; c < xf86_config->num_crtc; c++) {
169+ xf86CrtcPtr crtc = xf86_config->crtc[c];
170+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
171+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
172+
173+ /* Skip disabled CRTCs and those which aren't using TearFree */
174+ if (!trf->buf[0].px || !crtc->scrn->vtSema || !xf86_crtc_on(crtc))
175+ continue;
176+
177+ /* Skip if the last flip is still pending, a DRI client is flipping, or
178+ * there isn't any damage on the front buffer.
179+ */
180+ if (trf->flip_seq || ms->drmmode.dri2_flipping ||
181+ ms->drmmode.present_flipping ||
182+ RegionNil(&trf->buf[trf->back_idx ^ 1].dmg))
183+ continue;
184+
185+ /* Flip. If it fails, notify the kernel of the front buffer damages */
186+ if (ms_do_tearfree_flip(pScreen, crtc)) {
187+ dispatch_damages(scrn, crtc, &trf->buf[trf->back_idx ^ 1].dmg,
188+ trf->buf[trf->back_idx ^ 1].px, NULL,
189+ trf->buf[trf->back_idx ^ 1].fb_id);
190+ RegionEmpty(&trf->buf[trf->back_idx ^ 1].dmg);
191+ }
192+ }
193+#endif
194+}
195+
196 static void
197 dispatch_dirty(ScreenPtr pScreen)
198 {
199@@ -606,12 +699,9 @@ dispatch_dirty(ScreenPtr pScreen)
200
201 ret = dispatch_dirty_region(scrn, crtc, pixmap, ms->damage, fb_id);
202 if (ret == -EINVAL || ret == -ENOSYS) {
203- ms->dirty_enabled = FALSE;
204 DamageUnregister(ms->damage);
205 DamageDestroy(ms->damage);
206 ms->damage = NULL;
207- xf86DrvMsg(scrn->scrnIndex, X_INFO,
208- "Disabling kernel dirty updates, not required.\n");
209 return;
210 }
211 }
212@@ -742,10 +832,13 @@ msBlockHandler(ScreenPtr pScreen, void *timeout)
213 pScreen->BlockHandler = msBlockHandler;
214 if (pScreen->isGPU && !ms->drmmode.reverse_prime_offload_mode)
215 dispatch_secondary_dirty(pScreen);
216+ else if (ms->drmmode.tearfree_enable)
217+ ms_tearfree_update_damages(pScreen);
218 else if (ms->dirty_enabled)
219 dispatch_dirty(pScreen);
220
221 ms_dirty_update(pScreen, timeout);
222+ ms_tearfree_do_flips(pScreen);
223 }
224
225 static void
226@@ -1281,6 +1374,27 @@ PreInit(ScrnInfoPtr pScrn, int flags)
227 ms->atomic_modeset = FALSE;
228 }
229
230+ /* TearFree requires glamor and, if PageFlip is enabled, universal planes */
231+ if (xf86ReturnOptValBool(ms->drmmode.Options, OPTION_TEARFREE, FALSE)) {
232+ if (pScrn->is_gpu) {
233+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
234+ "TearFree cannot synchronize PRIME; use 'PRIME Synchronization' instead\n");
235+ } else if (ms->drmmode.glamor) {
236+ /* Atomic modesetting implicitly enables universal planes */
237+ if (!ms->drmmode.pageflip || ms->atomic_modeset ||
238+ !drmSetClientCap(ms->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) {
239+ ms->drmmode.tearfree_enable = TRUE;
240+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "TearFree: enabled\n");
241+ } else {
242+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
243+ "TearFree requires either universal planes, or setting 'Option \"PageFlip\" \"off\"'\n");
244+ }
245+ } else {
246+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
247+ "TearFree requires Glamor acceleration\n");
248+ }
249+ }
250+
251 ms->kms_has_modifiers = FALSE;
252 ret = drmGetCap(ms->fd, DRM_CAP_ADDFB2_MODIFIERS, &value);
253 if (ret == 0 && value != 0)
254@@ -1628,13 +1742,13 @@ CreateScreenResources(ScreenPtr pScreen)
255
256 err = drmModeDirtyFB(ms->fd, ms->drmmode.fb_id, NULL, 0);
257
258- if (err != -EINVAL && err != -ENOSYS) {
259+ if ((err != -EINVAL && err != -ENOSYS) || ms->drmmode.tearfree_enable) {
260 ms->damage = DamageCreate(NULL, NULL, DamageReportNone, TRUE,
261 pScreen, rootPixmap);
262
263 if (ms->damage) {
264 DamageRegister(&rootPixmap->drawable, ms->damage);
265- ms->dirty_enabled = TRUE;
266+ ms->dirty_enabled = err != -EINVAL && err != -ENOSYS;
267 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Damage tracking initialized\n");
268 }
269 else {
270diff --git a/hw/xfree86/drivers/modesetting/driver.h b/hw/xfree86/drivers/modesetting/driver.h
271index 71aa8730ec2fd74bec50d2bf237b474abe5197df..3f2b1d1aef4170793a4c2bfd18543130ef5aa34f 100644
272--- a/hw/xfree86/drivers/modesetting/driver.h
273+++ b/hw/xfree86/drivers/modesetting/driver.h
274@@ -61,6 +61,7 @@ typedef enum {
275 OPTION_VARIABLE_REFRESH,
276 OPTION_USE_GAMMA_LUT,
277 OPTION_ASYNC_FLIP_SECONDARIES,
278+ OPTION_TEARFREE,
279 } modesettingOpts;
280
281 typedef struct
282@@ -86,10 +87,13 @@ struct ms_drm_queue {
283 struct xorg_list list;
284 xf86CrtcPtr crtc;
285 uint32_t seq;
286+ uint64_t msc;
287 void *data;
288 ScrnInfoPtr scrn;
289 ms_drm_handler_proc handler;
290 ms_drm_abort_proc abort;
291+ Bool kernel_queued;
292+ Bool aborted;
293 };
294
295 typedef struct _modesettingRec {
296@@ -238,6 +242,8 @@ Bool ms_do_pageflip(ScreenPtr screen,
297 ms_pageflip_abort_proc pageflip_abort,
298 const char *log_prefix);
299
300+Bool ms_do_tearfree_flip(ScreenPtr screen, xf86CrtcPtr crtc);
301+
302 #endif
303
304 int ms_flush_drm_events(ScreenPtr screen);
305diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.c b/hw/xfree86/drivers/modesetting/drmmode_display.c
306index 65e8e63353a04693de76fba1c808c8bfcfd63676..8f8e4060a9fc27c4dbc7b82c4fa01f9fbbb8463c 100644
307--- a/hw/xfree86/drivers/modesetting/drmmode_display.c
308+++ b/hw/xfree86/drivers/modesetting/drmmode_display.c
309@@ -632,6 +632,7 @@ drmmode_crtc_get_fb_id(xf86CrtcPtr crtc, uint32_t *fb_id, int *x, int *y)
310 {
311 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
312 drmmode_ptr drmmode = drmmode_crtc->drmmode;
313+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
314 int ret;
315
316 *fb_id = 0;
317@@ -646,6 +647,10 @@ drmmode_crtc_get_fb_id(xf86CrtcPtr crtc, uint32_t *fb_id, int *x, int *y)
318 *x = drmmode_crtc->prime_pixmap_x;
319 *y = 0;
320 }
321+ else if (trf->buf[trf->back_idx ^ 1].px) {
322+ *fb_id = trf->buf[trf->back_idx ^ 1].fb_id;
323+ *x = *y = 0;
324+ }
325 else if (drmmode_crtc->rotate_fb_id) {
326 *fb_id = drmmode_crtc->rotate_fb_id;
327 *x = *y = 0;
328@@ -922,6 +927,10 @@ drmmode_crtc_set_mode(xf86CrtcPtr crtc, Bool test_only)
329 drmmode_ConvertToKMode(crtc->scrn, &kmode, &crtc->mode);
330 ret = drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
331 fb_id, x, y, output_ids, output_count, &kmode);
332+ if (!ret && !ms->atomic_modeset) {
333+ drmmode_crtc->src_x = x;
334+ drmmode_crtc->src_y = y;
335+ }
336
337 drmmode_set_ctm(crtc, ctm);
338
339@@ -930,7 +939,8 @@ drmmode_crtc_set_mode(xf86CrtcPtr crtc, Bool test_only)
340 }
341
342 int
343-drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, uint32_t flags, void *data)
344+drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, int x, int y,
345+ uint32_t flags, void *data)
346 {
347 modesettingPtr ms = modesettingPTR(crtc->scrn);
348 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
349@@ -942,7 +952,7 @@ drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, uint32_t flags, void *data)
350 if (!req)
351 return 1;
352
353- ret = plane_add_props(req, crtc, fb_id, crtc->x, crtc->y);
354+ ret = plane_add_props(req, crtc, fb_id, x, y);
355 flags |= DRM_MODE_ATOMIC_NONBLOCK;
356 if (ret == 0)
357 ret = drmModeAtomicCommit(ms->fd, req, flags, data);
358@@ -950,6 +960,26 @@ drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, uint32_t flags, void *data)
359 return ret;
360 }
361
362+ /* The frame buffer source coordinates may change when switching between the
363+ * primary frame buffer and a per-CRTC frame buffer. Set the correct source
364+ * coordinates if they differ for this flip.
365+ */
366+ if (drmmode_crtc->src_x != x || drmmode_crtc->src_y != y) {
367+ ret = drmModeSetPlane(ms->fd, drmmode_crtc->plane_id,
368+ drmmode_crtc->mode_crtc->crtc_id, fb_id, 0,
369+ 0, 0, crtc->mode.HDisplay, crtc->mode.VDisplay,
370+ x << 16, y << 16, crtc->mode.HDisplay << 16,
371+ crtc->mode.VDisplay << 16);
372+ if (ret) {
373+ xf86DrvMsg(crtc->scrn->scrnIndex, X_WARNING,
374+ "error changing fb src coordinates for flip: %d\n", ret);
375+ return ret;
376+ }
377+
378+ drmmode_crtc->src_x = x;
379+ drmmode_crtc->src_y = y;
380+ }
381+
382 return drmModePageFlip(ms->fd, drmmode_crtc->mode_crtc->crtc_id,
383 fb_id, flags, data);
384 }
385@@ -1548,6 +1578,90 @@ drmmode_copy_fb(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
386 #endif
387 }
388
389+void
390+drmmode_copy_damage(xf86CrtcPtr crtc, PixmapPtr dst, RegionPtr dmg, Bool empty)
391+{
392+#ifdef GLAMOR_HAS_GBM
393+ ScreenPtr pScreen = xf86ScrnToScreen(crtc->scrn);
394+ DrawableRec *src;
395+
396+ /* Copy the screen's pixmap into the destination pixmap */
397+ if (crtc->rotatedPixmap) {
398+ src = &crtc->rotatedPixmap->drawable;
399+ xf86RotateCrtcRedisplay(crtc, dst, src, dmg, FALSE);
400+ } else {
401+ src = &pScreen->GetScreenPixmap(pScreen)->drawable;
402+ PixmapDirtyCopyArea(dst, src, 0, 0, -crtc->x, -crtc->y, dmg);
403+ }
404+
405+ /* Reset the damages if requested */
406+ if (empty)
407+ RegionEmpty(dmg);
408+
409+ /* Wait until the GC operations finish */
410+ modesettingPTR(crtc->scrn)->glamor.finish(pScreen);
411+#endif
412+}
413+
414+static void
415+drmmode_shadow_fb_destroy(xf86CrtcPtr crtc, PixmapPtr pixmap,
416+ void *data, drmmode_bo *bo, uint32_t *fb_id);
417+static void
418+drmmode_destroy_tearfree_shadow(xf86CrtcPtr crtc)
419+{
420+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
421+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
422+ int i;
423+
424+ if (trf->flip_seq)
425+ ms_drm_abort_seq(crtc->scrn, trf->flip_seq);
426+
427+ for (i = 0; i < ARRAY_SIZE(trf->buf); i++) {
428+ if (trf->buf[i].px) {
429+ drmmode_shadow_fb_destroy(crtc, trf->buf[i].px, (void *)(long)1,
430+ &trf->buf[i].bo, &trf->buf[i].fb_id);
431+ trf->buf[i].px = NULL;
432+ RegionUninit(&trf->buf[i].dmg);
433+ }
434+ }
435+}
436+
437+static PixmapPtr
438+drmmode_shadow_fb_create(xf86CrtcPtr crtc, void *data, int width, int height,
439+ drmmode_bo *bo, uint32_t *fb_id);
440+static Bool
441+drmmode_create_tearfree_shadow(xf86CrtcPtr crtc)
442+{
443+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
444+ drmmode_ptr drmmode = drmmode_crtc->drmmode;
445+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
446+ uint32_t w = crtc->mode.HDisplay, h = crtc->mode.VDisplay;
447+ int i;
448+
449+ if (!drmmode->tearfree_enable)
450+ return TRUE;
451+
452+ /* Destroy the old mode's buffers and make new ones */
453+ drmmode_destroy_tearfree_shadow(crtc);
454+ for (i = 0; i < ARRAY_SIZE(trf->buf); i++) {
455+ trf->buf[i].px = drmmode_shadow_fb_create(crtc, NULL, w, h,
456+ &trf->buf[i].bo,
457+ &trf->buf[i].fb_id);
458+ if (!trf->buf[i].px) {
459+ drmmode_destroy_tearfree_shadow(crtc);
460+ xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
461+ "shadow creation failed for TearFree buf%d\n", i);
462+ return FALSE;
463+ }
464+ RegionInit(&trf->buf[i].dmg, &crtc->bounds, 0);
465+ }
466+
467+ /* Initialize the front buffer with the current scanout */
468+ drmmode_copy_damage(crtc, trf->buf[trf->back_idx ^ 1].px,
469+ &trf->buf[trf->back_idx ^ 1].dmg, TRUE);
470+ return TRUE;
471+}
472+
473 static Bool
474 drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
475 Rotation rotation, int x, int y)
476@@ -1581,6 +1695,10 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
477 crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green,
478 crtc->gamma_blue, crtc->gamma_size);
479
480+ ret = drmmode_create_tearfree_shadow(crtc);
481+ if (!ret)
482+ goto done;
483+
484 can_test = drmmode_crtc_can_test_mode(crtc);
485 if (drmmode_crtc_set_mode(crtc, can_test)) {
486 xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
487@@ -1626,6 +1744,7 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
488 crtc->y = saved_y;
489 crtc->rotation = saved_rotation;
490 crtc->mode = saved_mode;
491+ drmmode_create_tearfree_shadow(crtc);
492 } else
493 crtc->active = TRUE;
494
495@@ -1931,33 +2050,42 @@ drmmode_clear_pixmap(PixmapPtr pixmap)
496 }
497
498 static void *
499-drmmode_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
500+drmmode_shadow_fb_allocate(xf86CrtcPtr crtc, int width, int height,
501+ drmmode_bo *bo, uint32_t *fb_id)
502 {
503 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
504 drmmode_ptr drmmode = drmmode_crtc->drmmode;
505 int ret;
506
507- if (!drmmode_create_bo(drmmode, &drmmode_crtc->rotate_bo,
508- width, height, drmmode->kbpp)) {
509+ if (!drmmode_create_bo(drmmode, bo, width, height, drmmode->kbpp)) {
510 xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
511 "Couldn't allocate shadow memory for rotated CRTC\n");
512 return NULL;
513 }
514
515- ret = drmmode_bo_import(drmmode, &drmmode_crtc->rotate_bo,
516- &drmmode_crtc->rotate_fb_id);
517+ ret = drmmode_bo_import(drmmode, bo, fb_id);
518
519 if (ret) {
520 ErrorF("failed to add rotate fb\n");
521- drmmode_bo_destroy(drmmode, &drmmode_crtc->rotate_bo);
522+ drmmode_bo_destroy(drmmode, bo);
523 return NULL;
524 }
525
526 #ifdef GLAMOR_HAS_GBM
527 if (drmmode->gbm)
528- return drmmode_crtc->rotate_bo.gbm;
529+ return bo->gbm;
530 #endif
531- return drmmode_crtc->rotate_bo.dumb;
532+ return bo->dumb;
533+}
534+
535+static void *
536+drmmode_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
537+{
538+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
539+
540+ return drmmode_shadow_fb_allocate(crtc, width, height,
541+ &drmmode_crtc->rotate_bo,
542+ &drmmode_crtc->rotate_fb_id);
543 }
544
545 static PixmapPtr
546@@ -1983,70 +2111,91 @@ static Bool
547 drmmode_set_pixmap_bo(drmmode_ptr drmmode, PixmapPtr pixmap, drmmode_bo *bo);
548
549 static PixmapPtr
550-drmmode_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
551+drmmode_shadow_fb_create(xf86CrtcPtr crtc, void *data, int width, int height,
552+ drmmode_bo *bo, uint32_t *fb_id)
553 {
554 ScrnInfoPtr scrn = crtc->scrn;
555 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
556 drmmode_ptr drmmode = drmmode_crtc->drmmode;
557- uint32_t rotate_pitch;
558- PixmapPtr rotate_pixmap;
559+ uint32_t pitch;
560+ PixmapPtr pixmap;
561 void *pPixData = NULL;
562
563 if (!data) {
564- data = drmmode_shadow_allocate(crtc, width, height);
565+ data = drmmode_shadow_fb_allocate(crtc, width, height, bo, fb_id);
566 if (!data) {
567 xf86DrvMsg(scrn->scrnIndex, X_ERROR,
568- "Couldn't allocate shadow pixmap for rotated CRTC\n");
569+ "Couldn't allocate shadow pixmap for CRTC\n");
570 return NULL;
571 }
572 }
573
574- if (!drmmode_bo_has_bo(&drmmode_crtc->rotate_bo)) {
575+ if (!drmmode_bo_has_bo(bo)) {
576 xf86DrvMsg(scrn->scrnIndex, X_ERROR,
577- "Couldn't allocate shadow pixmap for rotated CRTC\n");
578+ "Couldn't allocate shadow pixmap for CRTC\n");
579 return NULL;
580 }
581
582- pPixData = drmmode_bo_map(drmmode, &drmmode_crtc->rotate_bo);
583- rotate_pitch = drmmode_bo_get_pitch(&drmmode_crtc->rotate_bo);
584+ pPixData = drmmode_bo_map(drmmode, bo);
585+ pitch = drmmode_bo_get_pitch(bo);
586
587- rotate_pixmap = drmmode_create_pixmap_header(scrn->pScreen,
588- width, height,
589- scrn->depth,
590- drmmode->kbpp,
591- rotate_pitch,
592- pPixData);
593+ pixmap = drmmode_create_pixmap_header(scrn->pScreen,
594+ width, height,
595+ scrn->depth,
596+ drmmode->kbpp,
597+ pitch,
598+ pPixData);
599
600- if (rotate_pixmap == NULL) {
601+ if (pixmap == NULL) {
602 xf86DrvMsg(scrn->scrnIndex, X_ERROR,
603- "Couldn't allocate shadow pixmap for rotated CRTC\n");
604+ "Couldn't allocate shadow pixmap for CRTC\n");
605 return NULL;
606 }
607
608- drmmode_set_pixmap_bo(drmmode, rotate_pixmap, &drmmode_crtc->rotate_bo);
609+ drmmode_set_pixmap_bo(drmmode, pixmap, bo);
610+
611+ return pixmap;
612+}
613+
614+static PixmapPtr
615+drmmode_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
616+{
617+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
618
619- return rotate_pixmap;
620+ return drmmode_shadow_fb_create(crtc, data, width, height,
621+ &drmmode_crtc->rotate_bo,
622+ &drmmode_crtc->rotate_fb_id);
623 }
624
625 static void
626-drmmode_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *data)
627+drmmode_shadow_fb_destroy(xf86CrtcPtr crtc, PixmapPtr pixmap,
628+ void *data, drmmode_bo *bo, uint32_t *fb_id)
629 {
630 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
631 drmmode_ptr drmmode = drmmode_crtc->drmmode;
632
633- if (rotate_pixmap) {
634- rotate_pixmap->drawable.pScreen->DestroyPixmap(rotate_pixmap);
635+ if (pixmap) {
636+ pixmap->drawable.pScreen->DestroyPixmap(pixmap);
637 }
638
639 if (data) {
640- drmModeRmFB(drmmode->fd, drmmode_crtc->rotate_fb_id);
641- drmmode_crtc->rotate_fb_id = 0;
642+ drmModeRmFB(drmmode->fd, *fb_id);
643+ *fb_id = 0;
644
645- drmmode_bo_destroy(drmmode, &drmmode_crtc->rotate_bo);
646- memset(&drmmode_crtc->rotate_bo, 0, sizeof drmmode_crtc->rotate_bo);
647+ drmmode_bo_destroy(drmmode, bo);
648+ memset(bo, 0, sizeof(*bo));
649 }
650 }
651
652+static void
653+drmmode_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr pixmap, void *data)
654+{
655+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
656+
657+ drmmode_shadow_fb_destroy(crtc, pixmap, data, &drmmode_crtc->rotate_bo,
658+ &drmmode_crtc->rotate_fb_id);
659+}
660+
661 static void
662 drmmode_crtc_destroy(xf86CrtcPtr crtc)
663 {
664@@ -2380,6 +2529,7 @@ drmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res
665 drmmode_crtc->drmmode = drmmode;
666 drmmode_crtc->vblank_pipe = drmmode_crtc_vblank_pipe(num);
667 xorg_list_init(&drmmode_crtc->mode_list);
668+ drmmode_crtc->next_msc = UINT64_MAX;
669
670 props = drmModeObjectGetProperties(drmmode->fd, mode_res->crtcs[num],
671 DRM_MODE_OBJECT_CRTC);
672@@ -4242,6 +4392,7 @@ drmmode_free_bos(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
673 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
674
675 dumb_bo_destroy(drmmode->fd, drmmode_crtc->cursor_bo);
676+ drmmode_destroy_tearfree_shadow(crtc);
677 }
678 }
679
680diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.h b/hw/xfree86/drivers/modesetting/drmmode_display.h
681index 2a9a915293a5ab092bd4de9749b795228a505b6f..145cb8cc7bac04da8f83d55dd85699523a4b873a 100644
682--- a/hw/xfree86/drivers/modesetting/drmmode_display.h
683+++ b/hw/xfree86/drivers/modesetting/drmmode_display.h
684@@ -135,6 +135,7 @@ typedef struct {
685 Bool async_flip_secondaries;
686 Bool dri2_enable;
687 Bool present_enable;
688+ Bool tearfree_enable;
689
690 uint32_t vrr_prop_id;
691 Bool use_ctm;
692@@ -166,6 +167,19 @@ typedef struct {
693 uint64_t *modifiers;
694 } drmmode_format_rec, *drmmode_format_ptr;
695
696+typedef struct {
697+ drmmode_bo bo;
698+ uint32_t fb_id;
699+ PixmapPtr px;
700+ RegionRec dmg;
701+} drmmode_shadow_fb_rec, *drmmode_shadow_fb_ptr;
702+
703+typedef struct {
704+ drmmode_shadow_fb_rec buf[2];
705+ uint32_t back_idx;
706+ uint32_t flip_seq;
707+} drmmode_tearfree_rec, *drmmode_tearfree_ptr;
708+
709 typedef struct {
710 drmmode_ptr drmmode;
711 drmModeCrtcPtr mode_crtc;
712@@ -184,11 +198,14 @@ typedef struct {
713
714 drmmode_bo rotate_bo;
715 unsigned rotate_fb_id;
716+ drmmode_tearfree_rec tearfree;
717
718 PixmapPtr prime_pixmap;
719 PixmapPtr prime_pixmap_back;
720 unsigned prime_pixmap_x;
721
722+ int src_x, src_y;
723+
724 /**
725 * @{ MSC (vblank count) handling for the PRESENT extension.
726 *
727@@ -200,6 +217,8 @@ typedef struct {
728 uint64_t msc_high;
729 /** @} */
730
731+ uint64_t next_msc;
732+
733 Bool need_modeset;
734 struct xorg_list mode_list;
735
736@@ -308,8 +327,11 @@ void drmmode_get_default_bpp(ScrnInfoPtr pScrn, drmmode_ptr drmmmode,
737 int *depth, int *bpp);
738
739 void drmmode_copy_fb(ScrnInfoPtr pScrn, drmmode_ptr drmmode);
740+void drmmode_copy_damage(xf86CrtcPtr crtc, PixmapPtr dst, RegionPtr damage,
741+ Bool empty);
742
743-int drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, uint32_t flags, void *data);
744+int drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, int x, int y,
745+ uint32_t flags, void *data);
746
747 Bool drmmode_crtc_get_fb_id(xf86CrtcPtr crtc, uint32_t *fb_id, int *x, int *y);
748
749diff --git a/hw/xfree86/drivers/modesetting/modesetting.man b/hw/xfree86/drivers/modesetting/modesetting.man
750index 71790011ec93f903a7b689980578086de144e70b..9e40b04f32251ac6090d16ccbb59324272f6ae7a 100644
751--- a/hw/xfree86/drivers/modesetting/modesetting.man
752+++ b/hw/xfree86/drivers/modesetting/modesetting.man
753@@ -109,6 +109,17 @@ When enabled, this option allows the driver to use gamma ramps with more
754 entries, if supported by the kernel. By default, GAMMA_LUT will be used for
755 kms drivers which are known to be safe for use of GAMMA_LUT.
756 .TP
757+.BI "Option \*qTearFree\*q \*q" boolean \*q
758+Enable tearing prevention using the hardware page flipping mechanism.
759+It allocates two extra scanout buffers for each CRTC and utilizes damage
760+tracking to minimize buffer copying and skip unnecessary flips when the
761+screen's contents have not changed. It works on transformed screens too, such
762+as rotated and scaled CRTCs. When PageFlip is enabled, fullscreen DRI
763+applications will still have the discretion to not use tearing prevention.
764+.br
765+The default is
766+.B off.
767+.TP
768 .SH "SEE ALSO"
769 @xservername@(@appmansuffix@), @xconfigfile@(@filemansuffix@), Xserver(@appmansuffix@),
770 X(@miscmansuffix@)
771diff --git a/hw/xfree86/drivers/modesetting/pageflip.c b/hw/xfree86/drivers/modesetting/pageflip.c
772index 23ee95f9a68d96c037c33ce28e2afb7761c552bb..8d57047efa6823bedd5de9f930fe2aa4ef6b3ce4 100644
773--- a/hw/xfree86/drivers/modesetting/pageflip.c
774+++ b/hw/xfree86/drivers/modesetting/pageflip.c
775@@ -35,8 +35,8 @@
776 * Returns a negative value on error, 0 if there was nothing to process,
777 * or 1 if we handled any events.
778 */
779-int
780-ms_flush_drm_events(ScreenPtr screen)
781+static int
782+ms_flush_drm_events_timeout(ScreenPtr screen, int timeout)
783 {
784 ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
785 modesettingPtr ms = modesettingPTR(scrn);
786@@ -45,7 +45,7 @@ ms_flush_drm_events(ScreenPtr screen)
787 int r;
788
789 do {
790- r = xserver_poll(&p, 1, 0);
791+ r = xserver_poll(&p, 1, timeout);
792 } while (r == -1 && (errno == EINTR || errno == EAGAIN));
793
794 /* If there was an error, r will be < 0. Return that. If there was
795@@ -63,6 +63,12 @@ ms_flush_drm_events(ScreenPtr screen)
796 return 1;
797 }
798
799+int
800+ms_flush_drm_events(ScreenPtr screen)
801+{
802+ return ms_flush_drm_events_timeout(screen, 0);
803+}
804+
805 #ifdef GLAMOR_HAS_GBM
806
807 /*
808@@ -160,11 +166,32 @@ ms_pageflip_abort(void *data)
809 }
810
811 static Bool
812-do_queue_flip_on_crtc(modesettingPtr ms, xf86CrtcPtr crtc,
813- uint32_t flags, uint32_t seq)
814+do_queue_flip_on_crtc(ScreenPtr screen, xf86CrtcPtr crtc, uint32_t flags,
815+ uint32_t seq, uint32_t fb_id, int x, int y)
816 {
817- return drmmode_crtc_flip(crtc, ms->drmmode.fb_id, flags,
818- (void *) (uintptr_t) seq);
819+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
820+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
821+
822+ while (drmmode_crtc_flip(crtc, fb_id, x, y, flags, (void *)(long)seq)) {
823+ /* We may have failed because the event queue was full. Flush it
824+ * and retry. If there was nothing to flush, then we failed for
825+ * some other reason and should just return an error.
826+ */
827+ if (ms_flush_drm_events(screen) <= 0) {
828+ /* The failure could be caused by a pending TearFree flip, in which
829+ * case we should wait until there's a new event and try again.
830+ */
831+ if (!trf->flip_seq || ms_flush_drm_events_timeout(screen, -1) < 0) {
832+ ms_drm_abort_seq(crtc->scrn, seq);
833+ return TRUE;
834+ }
835+ }
836+
837+ /* We flushed some events, so try again. */
838+ xf86DrvMsg(crtc->scrn->scrnIndex, X_WARNING, "flip queue retry\n");
839+ }
840+
841+ return FALSE;
842 }
843
844 enum queue_flip_status {
845@@ -205,20 +232,9 @@ queue_flip_on_crtc(ScreenPtr screen, xf86CrtcPtr crtc,
846 /* take a reference on flipdata for use in flip */
847 flipdata->flip_count++;
848
849- while (do_queue_flip_on_crtc(ms, crtc, flags, seq)) {
850- /* We may have failed because the event queue was full. Flush it
851- * and retry. If there was nothing to flush, then we failed for
852- * some other reason and should just return an error.
853- */
854- if (ms_flush_drm_events(screen) <= 0) {
855- /* Aborting will also decrement flip_count and free(flip). */
856- ms_drm_abort_seq(scrn, seq);
857- return QUEUE_FLIP_DRM_FLUSH_FAILED;
858- }
859-
860- /* We flushed some events, so try again. */
861- xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue retry\n");
862- }
863+ if (do_queue_flip_on_crtc(screen, crtc, flags, seq, ms->drmmode.fb_id,
864+ crtc->x, crtc->y))
865+ return QUEUE_FLIP_DRM_FLUSH_FAILED;
866
867 /* The page flip succeeded. */
868 return QUEUE_FLIP_SUCCESS;
869@@ -465,4 +481,50 @@ error_out:
870 #endif /* GLAMOR_HAS_GBM */
871 }
872
873+static void
874+ms_tearfree_flip_abort(void *data)
875+{
876+ drmmode_tearfree_ptr trf = data;
877+
878+ trf->flip_seq = 0;
879+}
880+
881+static void
882+ms_tearfree_flip_handler(uint64_t msc, uint64_t usec, void *data)
883+{
884+ drmmode_tearfree_ptr trf = data;
885+
886+ /* Swap the buffers and complete the flip */
887+ trf->back_idx ^= 1;
888+ trf->flip_seq = 0;
889+}
890+
891+Bool
892+ms_do_tearfree_flip(ScreenPtr screen, xf86CrtcPtr crtc)
893+{
894+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
895+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
896+ uint32_t idx = trf->back_idx, seq;
897+
898+ seq = ms_drm_queue_alloc(crtc, trf, ms_tearfree_flip_handler,
899+ ms_tearfree_flip_abort);
900+ if (!seq)
901+ goto no_flip;
902+
903+ /* Copy the damage to the back buffer and then flip it at the vblank */
904+ drmmode_copy_damage(crtc, trf->buf[idx].px, &trf->buf[idx].dmg, TRUE);
905+ if (do_queue_flip_on_crtc(screen, crtc, DRM_MODE_PAGE_FLIP_EVENT,
906+ seq, trf->buf[idx].fb_id, 0, 0))
907+ goto no_flip;
908+
909+ trf->flip_seq = seq;
910+ return FALSE;
911+
912+no_flip:
913+ xf86DrvMsg(crtc->scrn->scrnIndex, X_WARNING,
914+ "TearFree flip failed, rendering frame without TearFree\n");
915+ drmmode_copy_damage(crtc, trf->buf[idx ^ 1].px,
916+ &trf->buf[idx ^ 1].dmg, FALSE);
917+ return TRUE;
918+}
919 #endif
920diff --git a/hw/xfree86/drivers/modesetting/present.c b/hw/xfree86/drivers/modesetting/present.c
921index c3266d87116a8841ea4c9c302f43d8e706c427e6..642f7baaff93228b7b350b8f084c0a47d2b74655 100644
922--- a/hw/xfree86/drivers/modesetting/present.c
923+++ b/hw/xfree86/drivers/modesetting/present.c
924@@ -318,14 +318,32 @@ ms_present_check_flip(RRCrtcPtr crtc,
925 modesettingPtr ms = modesettingPTR(scrn);
926
927 if (ms->drmmode.sprites_visible > 0)
928- return FALSE;
929+ goto no_flip;
930
931 if(!ms_present_check_unflip(crtc, window, pixmap, sync_flip, reason))
932- return FALSE;
933+ goto no_flip;
934
935 ms->flip_window = window;
936
937 return TRUE;
938+
939+no_flip:
940+ /* Export some info about TearFree if Present can't flip anyway */
941+ if (reason && ms->drmmode.tearfree_enable) {
942+ xf86CrtcPtr xf86_crtc = crtc->devPrivate;
943+ drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
944+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
945+
946+ if (trf->buf[0].px) {
947+ if (trf->flip_seq)
948+ /* The driver has a TearFree flip pending */
949+ *reason = PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING;
950+ else
951+ /* The driver uses TearFree flips and there's no flip pending */
952+ *reason = PRESENT_FLIP_REASON_DRIVER_TEARFREE;
953+ }
954+ }
955+ return FALSE;
956 }
957
958 /*
959diff --git a/hw/xfree86/drivers/modesetting/vblank.c b/hw/xfree86/drivers/modesetting/vblank.c
960index ea9e7a88c5dfb55b553d66e34070f99e3fda44ac..4d250aa3480b704fdb94325885a1e453abb0c429 100644
961--- a/hw/xfree86/drivers/modesetting/vblank.c
962+++ b/hw/xfree86/drivers/modesetting/vblank.c
963@@ -260,6 +260,51 @@ ms_get_kernel_ust_msc(xf86CrtcPtr crtc,
964 }
965 }
966
967+static void
968+ms_drm_set_seq_msc(uint32_t seq, uint64_t msc)
969+{
970+ struct ms_drm_queue *q;
971+
972+ xorg_list_for_each_entry(q, &ms_drm_queue, list) {
973+ if (q->seq == seq) {
974+ q->msc = msc;
975+ break;
976+ }
977+ }
978+}
979+
980+static void
981+ms_drm_set_seq_queued(uint32_t seq, uint64_t msc)
982+{
983+ drmmode_crtc_private_ptr drmmode_crtc;
984+ struct ms_drm_queue *q;
985+
986+ xorg_list_for_each_entry(q, &ms_drm_queue, list) {
987+ if (q->seq == seq) {
988+ drmmode_crtc = q->crtc->driver_private;
989+ if (msc < drmmode_crtc->next_msc)
990+ drmmode_crtc->next_msc = msc;
991+ q->msc = msc;
992+ q->kernel_queued = TRUE;
993+ break;
994+ }
995+ }
996+}
997+
998+static Bool
999+ms_queue_coalesce(xf86CrtcPtr crtc, uint32_t seq, uint64_t msc)
1000+{
1001+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1002+
1003+ /* If the next MSC is too late, then this event can't be coalesced */
1004+ if (msc < drmmode_crtc->next_msc)
1005+ return FALSE;
1006+
1007+ /* Set the target MSC on this sequence number */
1008+ ms_drm_set_seq_msc(seq, msc);
1009+ return TRUE;
1010+}
1011+
1012 Bool
1013 ms_queue_vblank(xf86CrtcPtr crtc, ms_queue_flag flags,
1014 uint64_t msc, uint64_t *msc_queued, uint32_t seq)
1015@@ -271,6 +316,10 @@ ms_queue_vblank(xf86CrtcPtr crtc, ms_queue_flag flags,
1016 drmVBlank vbl;
1017 int ret;
1018
1019+ /* Try coalescing this event into another to avoid event queue exhaustion */
1020+ if (flags == MS_QUEUE_ABSOLUTE && ms_queue_coalesce(crtc, seq, msc))
1021+ return TRUE;
1022+
1023 for (;;) {
1024 /* Queue an event at the specified sequence */
1025 if (ms->has_queue_sequence || !ms->tried_queue_sequence) {
1026@@ -287,8 +336,10 @@ ms_queue_vblank(xf86CrtcPtr crtc, ms_queue_flag flags,
1027 ret = drmCrtcQueueSequence(ms->fd, drmmode_crtc->mode_crtc->crtc_id,
1028 drm_flags, msc, &kernel_queued, seq);
1029 if (ret == 0) {
1030+ msc = ms_kernel_msc_to_crtc_msc(crtc, kernel_queued, TRUE);
1031+ ms_drm_set_seq_queued(seq, msc);
1032 if (msc_queued)
1033- *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, kernel_queued, TRUE);
1034+ *msc_queued = msc;
1035 ms->has_queue_sequence = TRUE;
1036 return TRUE;
1037 }
1038@@ -310,8 +361,10 @@ ms_queue_vblank(xf86CrtcPtr crtc, ms_queue_flag flags,
1039 vbl.request.signal = seq;
1040 ret = drmWaitVBlank(ms->fd, &vbl);
1041 if (ret == 0) {
1042+ msc = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence, FALSE);
1043+ ms_drm_set_seq_queued(seq, msc);
1044 if (msc_queued)
1045- *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence, FALSE);
1046+ *msc_queued = msc;
1047 return TRUE;
1048 }
1049 check:
1050@@ -418,13 +471,15 @@ ms_drm_queue_alloc(xf86CrtcPtr crtc,
1051 if (!ms_drm_seq)
1052 ++ms_drm_seq;
1053 q->seq = ms_drm_seq++;
1054+ q->msc = UINT64_MAX;
1055 q->scrn = scrn;
1056 q->crtc = crtc;
1057 q->data = data;
1058 q->handler = handler;
1059 q->abort = abort;
1060
1061- xorg_list_add(&q->list, &ms_drm_queue);
1062+ /* Keep the list formatted in ascending order of sequence number */
1063+ xorg_list_append(&q->list, &ms_drm_queue);
1064
1065 return q->seq;
1066 }
1067@@ -437,9 +492,18 @@ ms_drm_queue_alloc(xf86CrtcPtr crtc,
1068 static void
1069 ms_drm_abort_one(struct ms_drm_queue *q)
1070 {
1071+ if (q->aborted)
1072+ return;
1073+
1074+ /* Don't remove vblank events if they were queued in the kernel */
1075+ if (q->kernel_queued) {
1076+ q->abort(q->data);
1077+ q->aborted = TRUE;
1078+ } else {
1079 xorg_list_del(&q->list);
1080 q->abort(q->data);
1081 free(q);
1082+ }
1083 }
1084
1085 /**
1086@@ -500,18 +564,61 @@ ms_drm_sequence_handler(int fd, uint64_t frame, uint64_t ns, Bool is64bit, uint6
1087 {
1088 struct ms_drm_queue *q, *tmp;
1089 uint32_t seq = (uint32_t) user_data;
1090+ xf86CrtcPtr crtc = NULL;
1091+ drmmode_crtc_private_ptr drmmode_crtc;
1092+ uint64_t msc, next_msc = UINT64_MAX;
1093
1094- xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
1095+ /* Handle the seq for this event first in order to get the CRTC */
1096+ xorg_list_for_each_entry(q, &ms_drm_queue, list) {
1097 if (q->seq == seq) {
1098- uint64_t msc;
1099-
1100- msc = ms_kernel_msc_to_crtc_msc(q->crtc, frame, is64bit);
1101+ crtc = q->crtc;
1102+ msc = ms_kernel_msc_to_crtc_msc(crtc, frame, is64bit);
1103 xorg_list_del(&q->list);
1104- q->handler(msc, ns / 1000, q->data);
1105+ if (!q->aborted)
1106+ q->handler(msc, ns / 1000, q->data);
1107 free(q);
1108 break;
1109 }
1110 }
1111+
1112+ if (!crtc)
1113+ return;
1114+
1115+ /* Now run all of the vblank events for this CRTC with an expired MSC */
1116+ xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
1117+ if (q->crtc == crtc && q->msc <= msc) {
1118+ xorg_list_del(&q->list);
1119+ if (!q->aborted)
1120+ q->handler(msc, ns / 1000, q->data);
1121+ free(q);
1122+ }
1123+ }
1124+
1125+ /* Find this CRTC's next queued MSC and next non-queued MSC to be handled */
1126+ msc = UINT64_MAX;
1127+ xorg_list_for_each_entry(q, &ms_drm_queue, list) {
1128+ if (q->crtc == crtc) {
1129+ if (q->kernel_queued) {
1130+ if (q->msc < next_msc)
1131+ next_msc = q->msc;
1132+ } else if (q->msc < msc) {
1133+ msc = q->msc;
1134+ seq = q->seq;
1135+ }
1136+ }
1137+ }
1138+
1139+ /* Queue an event if the next queued MSC isn't soon enough */
1140+ drmmode_crtc = crtc->driver_private;
1141+ drmmode_crtc->next_msc = next_msc;
1142+ if (msc < next_msc && !ms_queue_vblank(crtc, MS_QUEUE_ABSOLUTE, msc, NULL, seq)) {
1143+ xf86DrvMsg(crtc->scrn->scrnIndex, X_WARNING,
1144+ "failed to queue next vblank event, aborting lost events\n");
1145+ xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
1146+ if (q->crtc == crtc && q->msc < next_msc)
1147+ ms_drm_abort_one(q);
1148+ }
1149+ }
1150 }
1151
1152 static void
1153diff --git a/hw/xfree86/modes/xf86Crtc.h b/hw/xfree86/modes/xf86Crtc.h
1154index e36adbe0033a9afb1f1a56d6ff0934a3f22ec894..e9db8e3e53863b4b78692023a831c1dc8f8769ae 100644
1155--- a/hw/xfree86/modes/xf86Crtc.h
1156+++ b/hw/xfree86/modes/xf86Crtc.h
1157@@ -912,6 +912,11 @@ extern _X_EXPORT void
1158 extern _X_EXPORT Bool
1159 xf86CrtcRotate(xf86CrtcPtr crtc);
1160
1161+extern _X_EXPORT void
1162+ xf86RotateCrtcRedisplay(xf86CrtcPtr crtc, PixmapPtr dst_pixmap,
1163+ DrawableRec *src_drawable, RegionPtr region,
1164+ Bool transform_src);
1165+
1166 /*
1167 * Clean up any rotation data, used when a crtc is turned off
1168 * as well as when rotation is disabled.
1169diff --git a/hw/xfree86/modes/xf86Rotate.c b/hw/xfree86/modes/xf86Rotate.c
1170index ea9c43c0f237acffaa808b8826d25a489d0bb48d..944a01b1c01ba9107e7e2bbcccd8bb2aa12fe29d 100644
1171--- a/hw/xfree86/modes/xf86Rotate.c
1172+++ b/hw/xfree86/modes/xf86Rotate.c
1173@@ -39,13 +39,13 @@
1174 #include "X11/extensions/dpmsconst.h"
1175 #include "X11/Xatom.h"
1176
1177-static void
1178-xf86RotateCrtcRedisplay(xf86CrtcPtr crtc, RegionPtr region)
1179+void
1180+xf86RotateCrtcRedisplay(xf86CrtcPtr crtc, PixmapPtr dst_pixmap,
1181+ DrawableRec *src_drawable, RegionPtr region,
1182+ Bool transform_src)
1183 {
1184 ScrnInfoPtr scrn = crtc->scrn;
1185 ScreenPtr screen = scrn->pScreen;
1186- WindowPtr root = screen->root;
1187- PixmapPtr dst_pixmap = crtc->rotatedPixmap;
1188 PictFormatPtr format = PictureWindowFormat(screen->root);
1189 int error;
1190 PicturePtr src, dst;
1191@@ -57,7 +57,7 @@ xf86RotateCrtcRedisplay(xf86CrtcPtr crtc, RegionPtr region)
1192 return;
1193
1194 src = CreatePicture(None,
1195- &root->drawable,
1196+ src_drawable,
1197 format,
1198 CPSubwindowMode,
1199 &include_inferiors, serverClient, &error);
1200@@ -70,9 +70,11 @@ xf86RotateCrtcRedisplay(xf86CrtcPtr crtc, RegionPtr region)
1201 if (!dst)
1202 return;
1203
1204- error = SetPictureTransform(src, &crtc->crtc_to_framebuffer);
1205- if (error)
1206- return;
1207+ if (transform_src) {
1208+ error = SetPictureTransform(src, &crtc->crtc_to_framebuffer);
1209+ if (error)
1210+ return;
1211+ }
1212 if (crtc->transform_in_use && crtc->filter)
1213 SetPicturePictFilter(src, crtc->filter, crtc->params, crtc->nparams);
1214
1215@@ -205,7 +207,9 @@ xf86RotateRedisplay(ScreenPtr pScreen)
1216
1217 /* update damaged region */
1218 if (RegionNotEmpty(&crtc_damage))
1219- xf86RotateCrtcRedisplay(crtc, &crtc_damage);
1220+ xf86RotateCrtcRedisplay(crtc, crtc->rotatedPixmap,
1221+ &pScreen->root->drawable,
1222+ &crtc_damage, TRUE);
1223
1224 RegionUninit(&crtc_damage);
1225 }
1226diff --git a/include/pixmap.h b/include/pixmap.h
1227index 7144bfb30e8ba1fcd1b21e21e33db31d24460080..e251690d5620b3d2cf969a1097349624a15bd392 100644
1228--- a/include/pixmap.h
1229+++ b/include/pixmap.h
1230@@ -134,4 +134,9 @@ PixmapStopDirtyTracking(DrawablePtr src, PixmapPtr slave_dst);
1231 extern _X_EXPORT Bool
1232 PixmapSyncDirtyHelper(PixmapDirtyUpdatePtr dirty);
1233
1234+extern _X_EXPORT void
1235+PixmapDirtyCopyArea(PixmapPtr dst, DrawablePtr src,
1236+ int x, int y, int dst_x, int dst_y,
1237+ RegionPtr dirty_region);
1238+
1239 #endif /* PIXMAP_H */
1240diff --git a/present/present.h b/present/present.h
1241index d41b36033f93fa9b8e0ff02c13f6fa9ec8524208..1d7b0ce42f192f5b0751cf31ad896d74560e207f 100644
1242--- a/present/present.h
1243+++ b/present/present.h
1244@@ -29,7 +29,9 @@
1245
1246 typedef enum {
1247 PRESENT_FLIP_REASON_UNKNOWN,
1248- PRESENT_FLIP_REASON_BUFFER_FORMAT
1249+ PRESENT_FLIP_REASON_BUFFER_FORMAT,
1250+ PRESENT_FLIP_REASON_DRIVER_TEARFREE,
1251+ PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING
1252 } PresentFlipReason;
1253
1254 typedef struct present_vblank present_vblank_rec, *present_vblank_ptr;
1255diff --git a/present/present_scmd.c b/present/present_scmd.c
1256index 239055bc10d8c934555356c4213cda37dbff711e..46fd9a1fd3341715cf3ccbe3c6fa1d1f8fca7dd6 100644
1257--- a/present/present_scmd.c
1258+++ b/present/present_scmd.c
1259@@ -71,6 +71,7 @@ present_check_flip(RRCrtcPtr crtc,
1260 PixmapPtr window_pixmap;
1261 WindowPtr root = screen->root;
1262 present_screen_priv_ptr screen_priv = present_screen_priv(screen);
1263+ PresentFlipReason tmp_reason = PRESENT_FLIP_REASON_UNKNOWN;
1264
1265 if (crtc) {
1266 screen_priv = present_screen_priv(crtc->pScreen);
1267@@ -91,6 +92,27 @@ present_check_flip(RRCrtcPtr crtc,
1268 if (!screen_priv->info->flip)
1269 return FALSE;
1270
1271+ /* Ask the driver for permission. Do this now to see if there's TearFree. */
1272+ if (screen_priv->info->version >= 1 && screen_priv->info->check_flip2) {
1273+ if (!(*screen_priv->info->check_flip2) (crtc, window, pixmap, sync_flip, &tmp_reason)) {
1274+ DebugPresent(("\td %08" PRIx32 " -> %08" PRIx32 "\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
1275+ /* It's fine to return now unless the page flip failure reason is
1276+ * PRESENT_FLIP_REASON_BUFFER_FORMAT; we must only output that
1277+ * reason if all the other checks pass.
1278+ */
1279+ if (!reason || tmp_reason != PRESENT_FLIP_REASON_BUFFER_FORMAT) {
1280+ if (reason)
1281+ *reason = tmp_reason;
1282+ return FALSE;
1283+ }
1284+ }
1285+ } else if (screen_priv->info->check_flip) {
1286+ if (!(*screen_priv->info->check_flip) (crtc, window, pixmap, sync_flip)) {
1287+ DebugPresent(("\td %08" PRIx32 " -> %08" PRIx32 "\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
1288+ return FALSE;
1289+ }
1290+ }
1291+
1292 /* Make sure the window hasn't been redirected with Composite */
1293 window_pixmap = screen->GetWindowPixmap(window);
1294 if (window_pixmap != screen->GetScreenPixmap(screen) &&
1295@@ -123,17 +145,10 @@ present_check_flip(RRCrtcPtr crtc,
1296 return FALSE;
1297 }
1298
1299- /* Ask the driver for permission */
1300- if (screen_priv->info->version >= 1 && screen_priv->info->check_flip2) {
1301- if (!(*screen_priv->info->check_flip2) (crtc, window, pixmap, sync_flip, reason)) {
1302- DebugPresent(("\td %08" PRIx32 " -> %08" PRIx32 "\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
1303- return FALSE;
1304- }
1305- } else if (screen_priv->info->check_flip) {
1306- if (!(*screen_priv->info->check_flip) (crtc, window, pixmap, sync_flip)) {
1307- DebugPresent(("\td %08" PRIx32 " -> %08" PRIx32 "\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
1308- return FALSE;
1309- }
1310+ if (tmp_reason == PRESENT_FLIP_REASON_BUFFER_FORMAT) {
1311+ if (reason)
1312+ *reason = tmp_reason;
1313+ return FALSE;
1314 }
1315
1316 return TRUE;
1317@@ -456,7 +471,9 @@ present_check_flip_window (WindowPtr window)
1318 xorg_list_for_each_entry(vblank, &window_priv->vblank, window_list) {
1319 if (vblank->queued && vblank->flip && !present_check_flip(vblank->crtc, window, vblank->pixmap, vblank->sync_flip, NULL, 0, 0, &reason)) {
1320 vblank->flip = FALSE;
1321- vblank->reason = reason;
1322+ /* Don't spuriously flag this as a TearFree presentation */
1323+ if (reason < PRESENT_FLIP_REASON_DRIVER_TEARFREE)
1324+ vblank->reason = reason;
1325 if (vblank->sync_flip)
1326 vblank->exec_msc = vblank->target_msc;
1327 }
1328@@ -537,6 +554,7 @@ present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
1329 WindowPtr window = vblank->window;
1330 ScreenPtr screen = window->drawable.pScreen;
1331 present_screen_priv_ptr screen_priv = present_screen_priv(screen);
1332+ uint64_t completion_msc;
1333 if (vblank && vblank->crtc) {
1334 screen_priv=present_screen_priv(vblank->crtc->pScreen);
1335 }
1336@@ -560,7 +578,9 @@ present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
1337 xorg_list_del(&vblank->window_list);
1338 vblank->queued = FALSE;
1339
1340- if (vblank->pixmap && vblank->window) {
1341+ if (vblank->pixmap && vblank->window &&
1342+ (vblank->reason < PRESENT_FLIP_REASON_DRIVER_TEARFREE ||
1343+ vblank->exec_msc != vblank->target_msc)) {
1344
1345 if (vblank->flip) {
1346
1347@@ -627,6 +647,30 @@ present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
1348
1349 present_execute_copy(vblank, crtc_msc);
1350
1351+ /* The presentation will be visible at the next vblank with TearFree, so
1352+ * the PresentComplete notification needs to be sent at the next vblank.
1353+ * If TearFree is already flipping then the presentation will be visible
1354+ * at the *next* next vblank.
1355+ */
1356+ completion_msc = crtc_msc + 1;
1357+ switch (vblank->reason) {
1358+ case PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING:
1359+ if (vblank->exec_msc < crtc_msc)
1360+ completion_msc++;
1361+ case PRESENT_FLIP_REASON_DRIVER_TEARFREE:
1362+ if (Success == screen_priv->queue_vblank(screen,
1363+ window,
1364+ vblank->crtc,
1365+ vblank->event_id,
1366+ completion_msc)) {
1367+ /* Ensure present_execute_post() runs at the next MSC */
1368+ vblank->exec_msc = vblank->target_msc;
1369+ vblank->queued = TRUE;
1370+ }
1371+ default:
1372+ break;
1373+ }
1374+
1375 if (vblank->queued) {
1376 xorg_list_add(&vblank->event_queue, &present_exec_queue);
1377 xorg_list_append(&vblank->window_list,
1378@@ -739,6 +783,11 @@ present_scmd_pixmap(WindowPtr window,
1379 if (vblank->crtc != target_crtc || vblank->target_msc != target_msc)
1380 continue;
1381
1382+ /* Too late to abort now if TearFree execution already happened */
1383+ if (vblank->reason >= PRESENT_FLIP_REASON_DRIVER_TEARFREE &&
1384+ vblank->exec_msc == vblank->target_msc)
1385+ continue;
1386+
1387 present_vblank_scrap(vblank);
1388 if (vblank->flip_ready)
1389 present_re_execute(vblank);
1390@@ -767,7 +816,12 @@ present_scmd_pixmap(WindowPtr window,
1391
1392 vblank->event_id = ++present_scmd_event_id;
1393
1394- if (vblank->flip && vblank->sync_flip)
1395+ /* The soonest presentation is crtc_msc+2 if TearFree is already flipping */
1396+ if (vblank->reason == PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING &&
1397+ !msc_is_after(vblank->exec_msc, crtc_msc + 1))
1398+ vblank->exec_msc -= 2;
1399+ else if (vblank->reason >= PRESENT_FLIP_REASON_DRIVER_TEARFREE ||
1400+ (vblank->flip && vblank->sync_flip))
1401 vblank->exec_msc--;
1402
1403 xorg_list_append(&vblank->event_queue, &present_exec_queue);
diff --git a/gnu/packages/xorg.scm b/gnu/packages/xorg.scm
index e8f5d348e7a..364e3415ac1 100644
--- a/gnu/packages/xorg.scm
+++ b/gnu/packages/xorg.scm
@@ -5216,19 +5216,22 @@ by the Xorg server.")
5216 (base32 5216 (base32
5217 "06dm3bxb1xgzn994wfk8a1irajhms78k1f14c2p5nr1zbdafbjy0")) 5217 "06dm3bxb1xgzn994wfk8a1irajhms78k1f14c2p5nr1zbdafbjy0"))
5218 (patches 5218 (patches
5219 (list 5219 (cons
5220 ;; See: 5220 ;; See:
5221 ;; https://lists.fedoraproject.org/archives/list/devel@lists. 5221 ;; https://lists.fedoraproject.org/archives/list/devel@lists.
5222 ;; fedoraproject.org/message/JU655YB7AM4OOEQ4MOMCRHJTYJ76VFOK/ 5222 ;; fedoraproject.org/message/JU655YB7AM4OOEQ4MOMCRHJTYJ76VFOK/
5223 (origin 5223 (origin
5224 (method url-fetch) 5224 (method url-fetch)
5225 (uri (string-append 5225 (uri (string-append
5226 "http://pkgs.fedoraproject.org/cgit/rpms/xorg-x11-server.git" 5226 "https://src.fedoraproject.org/rpms/xorg-x11-server/raw/"
5227 "/plain/06_use-intel-only-on-pre-gen4.diff")) 5227 "ee515e44b07e37689abf48cf2fffb41578f3bc1d/f/"
5228 "06_use-intel-only-on-pre-gen4.diff"))
5228 (sha256 5229 (sha256
5229 (base32 5230 (base32
5230 "0mm70y058r8s9y9jiv7q2myv0ycnaw3iqzm7d274410s0ik38w7q")) 5231 "0mm70y058r8s9y9jiv7q2myv0ycnaw3iqzm7d274410s0ik38w7q"))
5231 (file-name "xorg-server-use-intel-only-on-pre-gen4.diff")))))) 5232 (file-name "xorg-server-use-intel-only-on-pre-gen4.diff"))
5233 ;; https://codeberg.org/guix/guix/issues/1006
5234 (search-patches "xorg-server-tearfree-modesetting.patch")))))
5232 (build-system gnu-build-system) 5235 (build-system gnu-build-system)
5233 (propagated-inputs 5236 (propagated-inputs
5234 ;; The following libraries are required by xorg-server.pc. 5237 ;; The following libraries are required by xorg-server.pc.
@@ -5341,7 +5344,9 @@ draggable titlebars and borders.")
5341 "/xserver/xorg-server-" version ".tar.xz")) 5344 "/xserver/xorg-server-" version ".tar.xz"))
5342 (sha256 5345 (sha256
5343 (base32 5346 (base32
5344 "12g0g9ksswzx1kgn23gvrpa570fnpkdkmw1dfqjjg4422a884744"))))))) 5347 "12g0g9ksswzx1kgn23gvrpa570fnpkdkmw1dfqjjg4422a884744"))
5348 ;; Exclude the modesetting tearfree patch, it doesn't apply to 21.1.15.
5349 (patches (list-head (origin-patches (package-source xorg-server)) 1)))))))
5345 5350
5346;;; XXX: Not really at home, but unless we break the inheritance between 5351;;; XXX: Not really at home, but unless we break the inheritance between
5347;;; tigervnc-server and xorg-server, it must live here to avoid cyclic module 5352;;; tigervnc-server and xorg-server, it must live here to avoid cyclic module