1980 lines
72 KiB
Diff
1980 lines
72 KiB
Diff
Submitted By: Rahul Chandra <rahulshaku2_at_gmail_dot_com>
|
|
Updated By: Douglas R. Reno <renodr at linuxfromscratch dot org>
|
|
Date: 2023-07-20
|
|
Updated Date: 2024-01-17
|
|
Initial Package Version: 21.1.8
|
|
Updated Package Version: 21.1.11
|
|
Upstream Status: In Master
|
|
Origin: Master Gitlab
|
|
Description: Backport of the TearFree option from Xorg master
|
|
Description (Update): Removed the hunk for meson.build since it has been
|
|
applied in 21.1.11.
|
|
diff --color -Naur --exclude .git xorg-server-21.1.8/dix/pixmap.c xorg-server-21.1.8-patched/dix/pixmap.c
|
|
--- xorg-server-21.1.8/dix/pixmap.c 2023-03-29 08:55:03.000000000 -0400
|
|
+++ xorg-server-21.1.8-patched/dix/pixmap.c 2023-07-19 23:22:36.142198348 -0400
|
|
@@ -262,12 +262,11 @@
|
|
return TRUE;
|
|
}
|
|
|
|
-static void
|
|
-PixmapDirtyCopyArea(PixmapPtr dst,
|
|
- PixmapDirtyUpdatePtr dirty,
|
|
+void
|
|
+PixmapDirtyCopyArea(PixmapPtr dst, DrawablePtr src,
|
|
+ int x, int y, int dst_x, int dst_y,
|
|
RegionPtr dirty_region)
|
|
{
|
|
- DrawablePtr src = dirty->src;
|
|
ScreenPtr pScreen = src->pScreen;
|
|
int n;
|
|
BoxPtr b;
|
|
@@ -294,14 +293,14 @@
|
|
h = dst_box.y2 - dst_box.y1;
|
|
|
|
pGC->ops->CopyArea(src, &dst->drawable, pGC,
|
|
- dirty->x + dst_box.x1, dirty->y + dst_box.y1, w, h,
|
|
- dirty->dst_x + dst_box.x1,
|
|
- dirty->dst_y + dst_box.y1);
|
|
+ x + dst_box.x1, y + dst_box.y1, w, h,
|
|
+ dst_x + dst_box.x1, dst_y + dst_box.y1);
|
|
b++;
|
|
}
|
|
FreeScratchGC(pGC);
|
|
}
|
|
|
|
+
|
|
static void
|
|
PixmapDirtyCompositeRotate(PixmapPtr dst_pixmap,
|
|
PixmapDirtyUpdatePtr dirty,
|
|
@@ -408,7 +407,8 @@
|
|
RegionTranslate(&pixregion, -dirty->x, -dirty->y);
|
|
|
|
if (!pScreen->root || dirty->rotation == RR_Rotate_0)
|
|
- PixmapDirtyCopyArea(dst, dirty, &pixregion);
|
|
+ PixmapDirtyCopyArea(dst, dirty->src, dirty->x, dirty->y,
|
|
+ dirty->dst_x, dirty->dst_y, &pixregion);
|
|
else
|
|
PixmapDirtyCompositeRotate(dst, dirty, &pixregion);
|
|
pScreen->SourceValidate = SourceValidate;
|
|
diff --color -Naur --exclude .git xorg-server-21.1.8/hw/xfree86/drivers/modesetting/driver.c xorg-server-21.1.8-patched/hw/xfree86/drivers/modesetting/driver.c
|
|
--- xorg-server-21.1.8/hw/xfree86/drivers/modesetting/driver.c 2023-03-29 08:55:03.000000000 -0400
|
|
+++ xorg-server-21.1.8-patched/hw/xfree86/drivers/modesetting/driver.c 2023-07-19 23:11:47.097508779 -0400
|
|
@@ -145,6 +145,7 @@
|
|
{OPTION_VARIABLE_REFRESH, "VariableRefresh", OPTV_BOOLEAN, {0}, FALSE},
|
|
{OPTION_USE_GAMMA_LUT, "UseGammaLUT", OPTV_BOOLEAN, {0}, FALSE},
|
|
{OPTION_ASYNC_FLIP_SECONDARIES, "AsyncFlipSecondaries", OPTV_BOOLEAN, {0}, FALSE},
|
|
+ {OPTION_TEARFREE, "TearFree", OPTV_BOOLEAN, {0}, FALSE},
|
|
{-1, NULL, OPTV_NONE, {0}, FALSE}
|
|
};
|
|
|
|
@@ -515,15 +516,49 @@
|
|
return TRUE;
|
|
}
|
|
|
|
+static void
|
|
+rotate_clip(PixmapPtr pixmap, BoxPtr rect, drmModeClip *clip, Rotation rotation)
|
|
+{
|
|
+ int w = pixmap->drawable.width;
|
|
+ int h = pixmap->drawable.height;
|
|
+
|
|
+ if (rotation == RR_Rotate_90) {
|
|
+ /* Rotate 90 degrees counter clockwise */
|
|
+ clip->x1 = rect->y1;
|
|
+ clip->x2 = rect->y2;
|
|
+ clip->y1 = w - rect->x2;
|
|
+ clip->y2 = w - rect->x1;
|
|
+ } else if (rotation == RR_Rotate_180) {
|
|
+ /* Rotate 180 degrees */
|
|
+ clip->x1 = w - rect->x2;
|
|
+ clip->x2 = w - rect->x1;
|
|
+ clip->y1 = h - rect->y2;
|
|
+ clip->y2 = h - rect->y1;
|
|
+ } else if (rotation == RR_Rotate_270) {
|
|
+ /* Rotate 90 degrees clockwise */
|
|
+ clip->x1 = h - rect->y2;
|
|
+ clip->x2 = h - rect->y1;
|
|
+ clip->y1 = rect->x1;
|
|
+ clip->y2 = rect->x2;
|
|
+ } else {
|
|
+ clip->x1 = rect->x1;
|
|
+ clip->x2 = rect->x2;
|
|
+ clip->y1 = rect->y1;
|
|
+ clip->y2 = rect->y2;
|
|
+ }
|
|
+}
|
|
+
|
|
static int
|
|
-dispatch_dirty_region(ScrnInfoPtr scrn,
|
|
- PixmapPtr pixmap, DamagePtr damage, int fb_id)
|
|
+dispatch_damages(ScrnInfoPtr scrn, xf86CrtcPtr crtc, RegionPtr dirty,
|
|
+ PixmapPtr pixmap, DamagePtr damage, int fb_id)
|
|
{
|
|
modesettingPtr ms = modesettingPTR(scrn);
|
|
- RegionPtr dirty = DamageRegion(damage);
|
|
unsigned num_cliprects = REGION_NUM_RECTS(dirty);
|
|
int ret = 0;
|
|
|
|
+ if (!ms->dirty_enabled)
|
|
+ return 0;
|
|
+
|
|
if (num_cliprects) {
|
|
drmModeClip *clip = xallocarray(num_cliprects, sizeof(drmModeClip));
|
|
BoxPtr rect = REGION_RECTS(dirty);
|
|
@@ -532,13 +567,9 @@
|
|
if (!clip)
|
|
return -ENOMEM;
|
|
|
|
- /* XXX no need for copy? */
|
|
- for (i = 0; i < num_cliprects; i++, rect++) {
|
|
- clip[i].x1 = rect->x1;
|
|
- clip[i].y1 = rect->y1;
|
|
- clip[i].x2 = rect->x2;
|
|
- clip[i].y2 = rect->y2;
|
|
- }
|
|
+ /* Rotate and copy rects into clips */
|
|
+ for (i = 0; i < num_cliprects; i++, rect++)
|
|
+ rotate_clip(pixmap, rect, &clip[i], crtc->rotation);
|
|
|
|
/* TODO query connector property to see if this is needed */
|
|
ret = drmModeDirtyFB(ms->fd, fb_id, clip, num_cliprects);
|
|
@@ -551,30 +582,130 @@
|
|
}
|
|
}
|
|
|
|
+ if (ret == -EINVAL || ret == -ENOSYS) {
|
|
+ xf86DrvMsg(scrn->scrnIndex, X_INFO,
|
|
+ "Disabling kernel dirty updates, not required.\n");
|
|
+ ms->dirty_enabled = FALSE;
|
|
+ }
|
|
+
|
|
free(clip);
|
|
- DamageEmpty(damage);
|
|
+ if (damage)
|
|
+ DamageEmpty(damage);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
+static int
|
|
+dispatch_dirty_region(ScrnInfoPtr scrn, xf86CrtcPtr crtc,
|
|
+ PixmapPtr pixmap, DamagePtr damage, int fb_id)
|
|
+{
|
|
+ return dispatch_damages(scrn, crtc, DamageRegion(damage),
|
|
+ pixmap, damage, fb_id);
|
|
+}
|
|
+
|
|
+static void
|
|
+ms_tearfree_update_damages(ScreenPtr pScreen)
|
|
+{
|
|
+ ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
|
|
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
|
|
+ modesettingPtr ms = modesettingPTR(scrn);
|
|
+ RegionPtr dirty = DamageRegion(ms->damage);
|
|
+ int c, i;
|
|
+
|
|
+ if (RegionNil(dirty))
|
|
+ return;
|
|
+
|
|
+ for (c = 0; c < xf86_config->num_crtc; c++) {
|
|
+ xf86CrtcPtr crtc = xf86_config->crtc[c];
|
|
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
|
|
+ RegionRec region;
|
|
+
|
|
+ /* Compute how much of the damage intersects with this CRTC */
|
|
+ RegionInit(®ion, &crtc->bounds, 0);
|
|
+ RegionIntersect(®ion, ®ion, dirty);
|
|
+
|
|
+ if (trf->buf[0].px) {
|
|
+ for (i = 0; i < ARRAY_SIZE(trf->buf); i++)
|
|
+ RegionUnion(&trf->buf[i].dmg, &trf->buf[i].dmg, ®ion);
|
|
+ } else {
|
|
+ /* Just notify the kernel of the damages if TearFree isn't used */
|
|
+ dispatch_damages(scrn, crtc, ®ion,
|
|
+ pScreen->GetScreenPixmap(pScreen),
|
|
+ NULL, ms->drmmode.fb_id);
|
|
+ }
|
|
+ }
|
|
+ DamageEmpty(ms->damage);
|
|
+}
|
|
+
|
|
+static void
|
|
+ms_tearfree_do_flips(ScreenPtr pScreen)
|
|
+{
|
|
+#ifdef GLAMOR_HAS_GBM
|
|
+ ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
|
|
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
|
|
+ modesettingPtr ms = modesettingPTR(scrn);
|
|
+ int c;
|
|
+
|
|
+ if (!ms->drmmode.tearfree_enable)
|
|
+ return;
|
|
+
|
|
+ for (c = 0; c < xf86_config->num_crtc; c++) {
|
|
+ xf86CrtcPtr crtc = xf86_config->crtc[c];
|
|
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
|
|
+
|
|
+ if (!ms_tearfree_is_active_on_crtc(crtc)) {
|
|
+ /* Notify any lingering DRI clients waiting for a flip to finish */
|
|
+ ms_tearfree_dri_abort_all(crtc);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /* Skip if the last flip is still pending, a DRI client is flipping, or
|
|
+ * there isn't any damage on the front buffer.
|
|
+ */
|
|
+ if (trf->flip_seq || ms->drmmode.dri2_flipping ||
|
|
+ ms->drmmode.present_flipping ||
|
|
+ RegionNil(&trf->buf[trf->back_idx ^ 1].dmg))
|
|
+ continue;
|
|
+
|
|
+ /* Flip. If it fails, notify the kernel of the front buffer damages */
|
|
+ if (ms_do_tearfree_flip(pScreen, crtc)) {
|
|
+ dispatch_damages(scrn, crtc, &trf->buf[trf->back_idx ^ 1].dmg,
|
|
+ trf->buf[trf->back_idx ^ 1].px, NULL,
|
|
+ trf->buf[trf->back_idx ^ 1].fb_id);
|
|
+ RegionEmpty(&trf->buf[trf->back_idx ^ 1].dmg);
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+}
|
|
+
|
|
static void
|
|
dispatch_dirty(ScreenPtr pScreen)
|
|
{
|
|
ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
|
|
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
|
|
modesettingPtr ms = modesettingPTR(scrn);
|
|
PixmapPtr pixmap = pScreen->GetScreenPixmap(pScreen);
|
|
- int fb_id = ms->drmmode.fb_id;
|
|
- int ret;
|
|
+ uint32_t fb_id;
|
|
+ int ret, c, x, y ;
|
|
|
|
- ret = dispatch_dirty_region(scrn, pixmap, ms->damage, fb_id);
|
|
- if (ret == -EINVAL || ret == -ENOSYS) {
|
|
- ms->dirty_enabled = FALSE;
|
|
- DamageUnregister(ms->damage);
|
|
- DamageDestroy(ms->damage);
|
|
- ms->damage = NULL;
|
|
- xf86DrvMsg(scrn->scrnIndex, X_INFO,
|
|
- "Disabling kernel dirty updates, not required.\n");
|
|
- return;
|
|
+ for (c = 0; c < xf86_config->num_crtc; c++) {
|
|
+ xf86CrtcPtr crtc = xf86_config->crtc[c];
|
|
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
+
|
|
+ if (!drmmode_crtc)
|
|
+ continue;
|
|
+
|
|
+ drmmode_crtc_get_fb_id(crtc, &fb_id, &x, &y);
|
|
+
|
|
+ ret = dispatch_dirty_region(scrn, crtc, pixmap, ms->damage, fb_id);
|
|
+ if (ret == -EINVAL || ret == -ENOSYS) {
|
|
+ DamageUnregister(ms->damage);
|
|
+ DamageDestroy(ms->damage);
|
|
+ ms->damage = NULL;
|
|
+ return;
|
|
+ }
|
|
}
|
|
}
|
|
|
|
@@ -586,7 +717,7 @@
|
|
DamagePtr damage = ppriv->secondary_damage;
|
|
int fb_id = ppriv->fb_id;
|
|
|
|
- dispatch_dirty_region(scrn, ppix, damage, fb_id);
|
|
+ dispatch_dirty_region(scrn, crtc, ppix, damage, fb_id);
|
|
}
|
|
|
|
static void
|
|
@@ -703,10 +834,13 @@
|
|
pScreen->BlockHandler = msBlockHandler;
|
|
if (pScreen->isGPU && !ms->drmmode.reverse_prime_offload_mode)
|
|
dispatch_secondary_dirty(pScreen);
|
|
+ else if (ms->drmmode.tearfree_enable)
|
|
+ ms_tearfree_update_damages(pScreen);
|
|
else if (ms->dirty_enabled)
|
|
dispatch_dirty(pScreen);
|
|
|
|
ms_dirty_update(pScreen, timeout);
|
|
+ ms_tearfree_do_flips(pScreen);
|
|
}
|
|
|
|
static void
|
|
@@ -1238,9 +1372,34 @@
|
|
if (xf86ReturnOptValBool(ms->drmmode.Options, OPTION_ATOMIC, FALSE)) {
|
|
ret = drmSetClientCap(ms->fd, DRM_CLIENT_CAP_ATOMIC, 1);
|
|
ms->atomic_modeset = (ret == 0);
|
|
+ if (!ms->atomic_modeset)
|
|
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Atomic modesetting not supported\n");
|
|
} else {
|
|
ms->atomic_modeset = FALSE;
|
|
}
|
|
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
|
|
+ "Atomic modesetting %sabled\n", ms->atomic_modeset ? "en" : "dis");
|
|
+
|
|
+ /* TearFree requires glamor and, if PageFlip is enabled, universal planes */
|
|
+ if (xf86ReturnOptValBool(ms->drmmode.Options, OPTION_TEARFREE, FALSE)) {
|
|
+ if (pScrn->is_gpu) {
|
|
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
|
|
+ "TearFree cannot synchronize PRIME; use 'PRIME Synchronization' instead\n");
|
|
+ } else if (ms->drmmode.glamor) {
|
|
+ /* Atomic modesetting implicitly enables universal planes */
|
|
+ if (!ms->drmmode.pageflip || ms->atomic_modeset ||
|
|
+ !drmSetClientCap(ms->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) {
|
|
+ ms->drmmode.tearfree_enable = TRUE;
|
|
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "TearFree: enabled\n");
|
|
+ } else {
|
|
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
|
|
+ "TearFree requires either universal planes, or setting 'Option \"PageFlip\" \"off\"'\n");
|
|
+ }
|
|
+ } else {
|
|
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
|
|
+ "TearFree requires Glamor acceleration\n");
|
|
+ }
|
|
+ }
|
|
|
|
ms->kms_has_modifiers = FALSE;
|
|
ret = drmGetCap(ms->fd, DRM_CAP_ADDFB2_MODIFIERS, &value);
|
|
@@ -1589,13 +1748,13 @@
|
|
|
|
err = drmModeDirtyFB(ms->fd, ms->drmmode.fb_id, NULL, 0);
|
|
|
|
- if (err != -EINVAL && err != -ENOSYS) {
|
|
+ if ((err != -EINVAL && err != -ENOSYS) || ms->drmmode.tearfree_enable) {
|
|
ms->damage = DamageCreate(NULL, NULL, DamageReportNone, TRUE,
|
|
pScreen, rootPixmap);
|
|
|
|
if (ms->damage) {
|
|
DamageRegister(&rootPixmap->drawable, ms->damage);
|
|
- ms->dirty_enabled = TRUE;
|
|
+ ms->dirty_enabled = err != -EINVAL && err != -ENOSYS;
|
|
xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Damage tracking initialized\n");
|
|
}
|
|
else {
|
|
diff --color -Naur --exclude .git xorg-server-21.1.8/hw/xfree86/drivers/modesetting/driver.h xorg-server-21.1.8-patched/hw/xfree86/drivers/modesetting/driver.h
|
|
--- xorg-server-21.1.8/hw/xfree86/drivers/modesetting/driver.h 2023-03-29 08:55:03.000000000 -0400
|
|
+++ xorg-server-21.1.8-patched/hw/xfree86/drivers/modesetting/driver.h 2023-07-19 23:11:47.097508779 -0400
|
|
@@ -61,6 +61,7 @@
|
|
OPTION_VARIABLE_REFRESH,
|
|
OPTION_USE_GAMMA_LUT,
|
|
OPTION_ASYNC_FLIP_SECONDARIES,
|
|
+ OPTION_TEARFREE,
|
|
} modesettingOpts;
|
|
|
|
typedef struct
|
|
@@ -86,10 +87,13 @@
|
|
struct xorg_list list;
|
|
xf86CrtcPtr crtc;
|
|
uint32_t seq;
|
|
+ uint64_t msc;
|
|
void *data;
|
|
ScrnInfoPtr scrn;
|
|
ms_drm_handler_proc handler;
|
|
ms_drm_abort_proc abort;
|
|
+ Bool kernel_queued;
|
|
+ Bool aborted;
|
|
};
|
|
|
|
typedef struct _modesettingRec {
|
|
@@ -232,14 +236,25 @@
|
|
Bool ms_do_pageflip(ScreenPtr screen,
|
|
PixmapPtr new_front,
|
|
void *event,
|
|
- int ref_crtc_vblank_pipe,
|
|
+ xf86CrtcPtr ref_crtc,
|
|
Bool async,
|
|
ms_pageflip_handler_proc pageflip_handler,
|
|
ms_pageflip_abort_proc pageflip_abort,
|
|
const char *log_prefix);
|
|
|
|
+Bool
|
|
+ms_tearfree_dri_abort(xf86CrtcPtr crtc,
|
|
+ Bool (*match)(void *data, void *match_data),
|
|
+ void *match_data);
|
|
+
|
|
+void
|
|
+ms_tearfree_dri_abort_all(xf86CrtcPtr crtc);
|
|
+
|
|
+Bool ms_do_tearfree_flip(ScreenPtr screen, xf86CrtcPtr crtc);
|
|
+
|
|
#endif
|
|
|
|
int ms_flush_drm_events(ScreenPtr screen);
|
|
Bool ms_window_has_variable_refresh(modesettingPtr ms, WindowPtr win);
|
|
void ms_present_set_screen_vrr(ScrnInfoPtr scrn, Bool vrr_enabled);
|
|
+Bool ms_tearfree_is_active_on_crtc(xf86CrtcPtr crtc);
|
|
diff --color -Naur --exclude .git xorg-server-21.1.8/hw/xfree86/drivers/modesetting/drmmode_display.c xorg-server-21.1.8-patched/hw/xfree86/drivers/modesetting/drmmode_display.c
|
|
--- xorg-server-21.1.8/hw/xfree86/drivers/modesetting/drmmode_display.c 2023-03-29 08:55:03.000000000 -0400
|
|
+++ xorg-server-21.1.8-patched/hw/xfree86/drivers/modesetting/drmmode_display.c 2023-07-19 23:11:47.097508779 -0400
|
|
@@ -519,13 +519,13 @@
|
|
}
|
|
|
|
static int
|
|
-drmmode_CompareKModes(drmModeModeInfo * kmode, drmModeModeInfo * other)
|
|
+drmmode_CompareKModes(const drmModeModeInfo * kmode, const drmModeModeInfo * other)
|
|
{
|
|
return memcmp(kmode, other, sizeof(*kmode));
|
|
}
|
|
|
|
static int
|
|
-drm_mode_ensure_blob(xf86CrtcPtr crtc, drmModeModeInfo mode_info)
|
|
+drm_mode_ensure_blob(xf86CrtcPtr crtc, const drmModeModeInfo* mode_info)
|
|
{
|
|
modesettingPtr ms = modesettingPTR(crtc->scrn);
|
|
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
@@ -533,14 +533,14 @@
|
|
int ret;
|
|
|
|
if (drmmode_crtc->current_mode &&
|
|
- drmmode_CompareKModes(&drmmode_crtc->current_mode->mode_info, &mode_info) == 0)
|
|
+ drmmode_CompareKModes(&drmmode_crtc->current_mode->mode_info, mode_info) == 0)
|
|
return 0;
|
|
|
|
mode = calloc(sizeof(drmmode_mode_rec), 1);
|
|
if (!mode)
|
|
return -1;
|
|
|
|
- mode->mode_info = mode_info;
|
|
+ mode->mode_info = *mode_info;
|
|
ret = drmModeCreatePropertyBlob(ms->fd,
|
|
&mode->mode_info,
|
|
sizeof(mode->mode_info),
|
|
@@ -589,7 +589,7 @@
|
|
drmModeModeInfo kmode;
|
|
|
|
drmmode_ConvertToKMode(crtc->scrn, &kmode, &crtc->mode);
|
|
- ret |= drm_mode_ensure_blob(crtc, kmode);
|
|
+ ret |= drm_mode_ensure_blob(crtc, &kmode);
|
|
|
|
ret |= crtc_add_prop(req, drmmode_crtc,
|
|
DRMMODE_CRTC_ACTIVE, 1);
|
|
@@ -627,11 +627,12 @@
|
|
return ms->atomic_modeset;
|
|
}
|
|
|
|
-static Bool
|
|
+Bool
|
|
drmmode_crtc_get_fb_id(xf86CrtcPtr crtc, uint32_t *fb_id, int *x, int *y)
|
|
{
|
|
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
drmmode_ptr drmmode = drmmode_crtc->drmmode;
|
|
+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
|
|
int ret;
|
|
|
|
*fb_id = 0;
|
|
@@ -646,6 +647,10 @@
|
|
*x = drmmode_crtc->prime_pixmap_x;
|
|
*y = 0;
|
|
}
|
|
+ else if (trf->buf[trf->back_idx ^ 1].px) {
|
|
+ *fb_id = trf->buf[trf->back_idx ^ 1].fb_id;
|
|
+ *x = *y = 0;
|
|
+ }
|
|
else if (drmmode_crtc->rotate_fb_id) {
|
|
*fb_id = drmmode_crtc->rotate_fb_id;
|
|
*x = *y = 0;
|
|
@@ -922,6 +927,10 @@
|
|
drmmode_ConvertToKMode(crtc->scrn, &kmode, &crtc->mode);
|
|
ret = drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
|
|
fb_id, x, y, output_ids, output_count, &kmode);
|
|
+ if (!ret && !ms->atomic_modeset) {
|
|
+ drmmode_crtc->src_x = x;
|
|
+ drmmode_crtc->src_y = y;
|
|
+ }
|
|
|
|
drmmode_set_ctm(crtc, ctm);
|
|
|
|
@@ -930,7 +939,8 @@
|
|
}
|
|
|
|
int
|
|
-drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, uint32_t flags, void *data)
|
|
+drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, int x, int y,
|
|
+ uint32_t flags, void *data)
|
|
{
|
|
modesettingPtr ms = modesettingPTR(crtc->scrn);
|
|
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
@@ -942,7 +952,7 @@
|
|
if (!req)
|
|
return 1;
|
|
|
|
- ret = plane_add_props(req, crtc, fb_id, crtc->x, crtc->y);
|
|
+ ret = plane_add_props(req, crtc, fb_id, x, y);
|
|
flags |= DRM_MODE_ATOMIC_NONBLOCK;
|
|
if (ret == 0)
|
|
ret = drmModeAtomicCommit(ms->fd, req, flags, data);
|
|
@@ -950,6 +960,26 @@
|
|
return ret;
|
|
}
|
|
|
|
+ /* The frame buffer source coordinates may change when switching between the
|
|
+ * primary frame buffer and a per-CRTC frame buffer. Set the correct source
|
|
+ * coordinates if they differ for this flip.
|
|
+ */
|
|
+ if (drmmode_crtc->src_x != x || drmmode_crtc->src_y != y) {
|
|
+ ret = drmModeSetPlane(ms->fd, drmmode_crtc->plane_id,
|
|
+ drmmode_crtc->mode_crtc->crtc_id, fb_id, 0,
|
|
+ 0, 0, crtc->mode.HDisplay, crtc->mode.VDisplay,
|
|
+ x << 16, y << 16, crtc->mode.HDisplay << 16,
|
|
+ crtc->mode.VDisplay << 16);
|
|
+ if (ret) {
|
|
+ xf86DrvMsg(crtc->scrn->scrnIndex, X_WARNING,
|
|
+ "error changing fb src coordinates for flip: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ drmmode_crtc->src_x = x;
|
|
+ drmmode_crtc->src_y = y;
|
|
+ }
|
|
+
|
|
return drmModePageFlip(ms->fd, drmmode_crtc->mode_crtc->crtc_id,
|
|
fb_id, flags, data);
|
|
}
|
|
@@ -1548,6 +1578,90 @@
|
|
#endif
|
|
}
|
|
|
|
+void
|
|
+drmmode_copy_damage(xf86CrtcPtr crtc, PixmapPtr dst, RegionPtr dmg, Bool empty)
|
|
+{
|
|
+#ifdef GLAMOR_HAS_GBM
|
|
+ ScreenPtr pScreen = xf86ScrnToScreen(crtc->scrn);
|
|
+ DrawableRec *src;
|
|
+
|
|
+ /* Copy the screen's pixmap into the destination pixmap */
|
|
+ if (crtc->rotatedPixmap) {
|
|
+ src = &crtc->rotatedPixmap->drawable;
|
|
+ xf86RotateCrtcRedisplay(crtc, dst, src, dmg, FALSE);
|
|
+ } else {
|
|
+ src = &pScreen->GetScreenPixmap(pScreen)->drawable;
|
|
+ PixmapDirtyCopyArea(dst, src, 0, 0, -crtc->x, -crtc->y, dmg);
|
|
+ }
|
|
+
|
|
+ /* Reset the damages if requested */
|
|
+ if (empty)
|
|
+ RegionEmpty(dmg);
|
|
+
|
|
+ /* Wait until the GC operations finish */
|
|
+ modesettingPTR(crtc->scrn)->glamor.finish(pScreen);
|
|
+#endif
|
|
+}
|
|
+
|
|
+static void
|
|
+drmmode_shadow_fb_destroy(xf86CrtcPtr crtc, PixmapPtr pixmap,
|
|
+ void *data, drmmode_bo *bo, uint32_t *fb_id);
|
|
+static void
|
|
+drmmode_destroy_tearfree_shadow(xf86CrtcPtr crtc)
|
|
+{
|
|
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
|
|
+ int i;
|
|
+
|
|
+ if (trf->flip_seq)
|
|
+ ms_drm_abort_seq(crtc->scrn, trf->flip_seq);
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(trf->buf); i++) {
|
|
+ if (trf->buf[i].px) {
|
|
+ drmmode_shadow_fb_destroy(crtc, trf->buf[i].px, (void *)(long)1,
|
|
+ &trf->buf[i].bo, &trf->buf[i].fb_id);
|
|
+ trf->buf[i].px = NULL;
|
|
+ RegionUninit(&trf->buf[i].dmg);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static PixmapPtr
|
|
+drmmode_shadow_fb_create(xf86CrtcPtr crtc, void *data, int width, int height,
|
|
+ drmmode_bo *bo, uint32_t *fb_id);
|
|
+static Bool
|
|
+drmmode_create_tearfree_shadow(xf86CrtcPtr crtc)
|
|
+{
|
|
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
+ drmmode_ptr drmmode = drmmode_crtc->drmmode;
|
|
+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
|
|
+ uint32_t w = crtc->mode.HDisplay, h = crtc->mode.VDisplay;
|
|
+ int i;
|
|
+
|
|
+ if (!drmmode->tearfree_enable)
|
|
+ return TRUE;
|
|
+
|
|
+ /* Destroy the old mode's buffers and make new ones */
|
|
+ drmmode_destroy_tearfree_shadow(crtc);
|
|
+ for (i = 0; i < ARRAY_SIZE(trf->buf); i++) {
|
|
+ trf->buf[i].px = drmmode_shadow_fb_create(crtc, NULL, w, h,
|
|
+ &trf->buf[i].bo,
|
|
+ &trf->buf[i].fb_id);
|
|
+ if (!trf->buf[i].px) {
|
|
+ drmmode_destroy_tearfree_shadow(crtc);
|
|
+ xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
|
|
+ "shadow creation failed for TearFree buf%d\n", i);
|
|
+ return FALSE;
|
|
+ }
|
|
+ RegionInit(&trf->buf[i].dmg, &crtc->bounds, 0);
|
|
+ }
|
|
+
|
|
+ /* Initialize the front buffer with the current scanout */
|
|
+ drmmode_copy_damage(crtc, trf->buf[trf->back_idx ^ 1].px,
|
|
+ &trf->buf[trf->back_idx ^ 1].dmg, TRUE);
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
static Bool
|
|
drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
|
|
Rotation rotation, int x, int y)
|
|
@@ -1581,6 +1695,10 @@
|
|
crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green,
|
|
crtc->gamma_blue, crtc->gamma_size);
|
|
|
|
+ ret = drmmode_create_tearfree_shadow(crtc);
|
|
+ if (!ret)
|
|
+ goto done;
|
|
+
|
|
can_test = drmmode_crtc_can_test_mode(crtc);
|
|
if (drmmode_crtc_set_mode(crtc, can_test)) {
|
|
xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
|
|
@@ -1626,6 +1744,7 @@
|
|
crtc->y = saved_y;
|
|
crtc->rotation = saved_rotation;
|
|
crtc->mode = saved_mode;
|
|
+ drmmode_create_tearfree_shadow(crtc);
|
|
} else
|
|
crtc->active = TRUE;
|
|
|
|
@@ -1931,33 +2050,42 @@
|
|
}
|
|
|
|
static void *
|
|
-drmmode_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
|
|
+drmmode_shadow_fb_allocate(xf86CrtcPtr crtc, int width, int height,
|
|
+ drmmode_bo *bo, uint32_t *fb_id)
|
|
{
|
|
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
drmmode_ptr drmmode = drmmode_crtc->drmmode;
|
|
int ret;
|
|
|
|
- if (!drmmode_create_bo(drmmode, &drmmode_crtc->rotate_bo,
|
|
- width, height, drmmode->kbpp)) {
|
|
+ if (!drmmode_create_bo(drmmode, bo, width, height, drmmode->kbpp)) {
|
|
xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
|
|
"Couldn't allocate shadow memory for rotated CRTC\n");
|
|
return NULL;
|
|
}
|
|
|
|
- ret = drmmode_bo_import(drmmode, &drmmode_crtc->rotate_bo,
|
|
- &drmmode_crtc->rotate_fb_id);
|
|
+ ret = drmmode_bo_import(drmmode, bo, fb_id);
|
|
|
|
if (ret) {
|
|
ErrorF("failed to add rotate fb\n");
|
|
- drmmode_bo_destroy(drmmode, &drmmode_crtc->rotate_bo);
|
|
+ drmmode_bo_destroy(drmmode, bo);
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef GLAMOR_HAS_GBM
|
|
if (drmmode->gbm)
|
|
- return drmmode_crtc->rotate_bo.gbm;
|
|
+ return bo->gbm;
|
|
#endif
|
|
- return drmmode_crtc->rotate_bo.dumb;
|
|
+ return bo->dumb;
|
|
+}
|
|
+
|
|
+static void *
|
|
+drmmode_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
|
|
+{
|
|
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
+
|
|
+ return drmmode_shadow_fb_allocate(crtc, width, height,
|
|
+ &drmmode_crtc->rotate_bo,
|
|
+ &drmmode_crtc->rotate_fb_id);
|
|
}
|
|
|
|
static PixmapPtr
|
|
@@ -1983,71 +2111,92 @@
|
|
drmmode_set_pixmap_bo(drmmode_ptr drmmode, PixmapPtr pixmap, drmmode_bo *bo);
|
|
|
|
static PixmapPtr
|
|
-drmmode_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
|
|
+drmmode_shadow_fb_create(xf86CrtcPtr crtc, void *data, int width, int height,
|
|
+ drmmode_bo *bo, uint32_t *fb_id)
|
|
{
|
|
ScrnInfoPtr scrn = crtc->scrn;
|
|
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
drmmode_ptr drmmode = drmmode_crtc->drmmode;
|
|
- uint32_t rotate_pitch;
|
|
- PixmapPtr rotate_pixmap;
|
|
+ uint32_t pitch;
|
|
+ PixmapPtr pixmap;
|
|
void *pPixData = NULL;
|
|
|
|
if (!data) {
|
|
- data = drmmode_shadow_allocate(crtc, width, height);
|
|
+ data = drmmode_shadow_fb_allocate(crtc, width, height, bo, fb_id);
|
|
if (!data) {
|
|
xf86DrvMsg(scrn->scrnIndex, X_ERROR,
|
|
- "Couldn't allocate shadow pixmap for rotated CRTC\n");
|
|
+ "Couldn't allocate shadow pixmap for CRTC\n");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
- if (!drmmode_bo_has_bo(&drmmode_crtc->rotate_bo)) {
|
|
+ if (!drmmode_bo_has_bo(bo)) {
|
|
xf86DrvMsg(scrn->scrnIndex, X_ERROR,
|
|
- "Couldn't allocate shadow pixmap for rotated CRTC\n");
|
|
+ "Couldn't allocate shadow pixmap for CRTC\n");
|
|
return NULL;
|
|
}
|
|
|
|
- pPixData = drmmode_bo_map(drmmode, &drmmode_crtc->rotate_bo);
|
|
- rotate_pitch = drmmode_bo_get_pitch(&drmmode_crtc->rotate_bo);
|
|
+ pPixData = drmmode_bo_map(drmmode, bo);
|
|
+ pitch = drmmode_bo_get_pitch(bo);
|
|
|
|
- rotate_pixmap = drmmode_create_pixmap_header(scrn->pScreen,
|
|
- width, height,
|
|
- scrn->depth,
|
|
- drmmode->kbpp,
|
|
- rotate_pitch,
|
|
- pPixData);
|
|
+ pixmap = drmmode_create_pixmap_header(scrn->pScreen,
|
|
+ width, height,
|
|
+ scrn->depth,
|
|
+ drmmode->kbpp,
|
|
+ pitch,
|
|
+ pPixData);
|
|
|
|
- if (rotate_pixmap == NULL) {
|
|
+ if (pixmap == NULL) {
|
|
xf86DrvMsg(scrn->scrnIndex, X_ERROR,
|
|
- "Couldn't allocate shadow pixmap for rotated CRTC\n");
|
|
+ "Couldn't allocate shadow pixmap for CRTC\n");
|
|
return NULL;
|
|
}
|
|
|
|
- drmmode_set_pixmap_bo(drmmode, rotate_pixmap, &drmmode_crtc->rotate_bo);
|
|
+ drmmode_set_pixmap_bo(drmmode, pixmap, bo);
|
|
|
|
- return rotate_pixmap;
|
|
+ return pixmap;
|
|
+}
|
|
+
|
|
+static PixmapPtr
|
|
+drmmode_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
|
|
+{
|
|
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
+
|
|
+ return drmmode_shadow_fb_create(crtc, data, width, height,
|
|
+ &drmmode_crtc->rotate_bo,
|
|
+ &drmmode_crtc->rotate_fb_id);
|
|
}
|
|
|
|
static void
|
|
-drmmode_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *data)
|
|
+drmmode_shadow_fb_destroy(xf86CrtcPtr crtc, PixmapPtr pixmap,
|
|
+ void *data, drmmode_bo *bo, uint32_t *fb_id)
|
|
{
|
|
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
drmmode_ptr drmmode = drmmode_crtc->drmmode;
|
|
|
|
- if (rotate_pixmap) {
|
|
- rotate_pixmap->drawable.pScreen->DestroyPixmap(rotate_pixmap);
|
|
+ if (pixmap) {
|
|
+ pixmap->drawable.pScreen->DestroyPixmap(pixmap);
|
|
}
|
|
|
|
if (data) {
|
|
- drmModeRmFB(drmmode->fd, drmmode_crtc->rotate_fb_id);
|
|
- drmmode_crtc->rotate_fb_id = 0;
|
|
+ drmModeRmFB(drmmode->fd, *fb_id);
|
|
+ *fb_id = 0;
|
|
|
|
- drmmode_bo_destroy(drmmode, &drmmode_crtc->rotate_bo);
|
|
- memset(&drmmode_crtc->rotate_bo, 0, sizeof drmmode_crtc->rotate_bo);
|
|
+ drmmode_bo_destroy(drmmode, bo);
|
|
+ memset(bo, 0, sizeof(*bo));
|
|
}
|
|
}
|
|
|
|
static void
|
|
+drmmode_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr pixmap, void *data)
|
|
+{
|
|
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
+
|
|
+ drmmode_shadow_fb_destroy(crtc, pixmap, data, &drmmode_crtc->rotate_bo,
|
|
+ &drmmode_crtc->rotate_fb_id);
|
|
+}
|
|
+
|
|
+static void
|
|
drmmode_crtc_destroy(xf86CrtcPtr crtc)
|
|
{
|
|
drmmode_mode_ptr iterator, next;
|
|
@@ -2380,6 +2529,8 @@
|
|
drmmode_crtc->drmmode = drmmode;
|
|
drmmode_crtc->vblank_pipe = drmmode_crtc_vblank_pipe(num);
|
|
xorg_list_init(&drmmode_crtc->mode_list);
|
|
+ xorg_list_init(&drmmode_crtc->tearfree.dri_flip_list);
|
|
+ drmmode_crtc->next_msc = UINT64_MAX;
|
|
|
|
props = drmModeObjectGetProperties(drmmode->fd, mode_res->crtcs[num],
|
|
DRM_MODE_OBJECT_CRTC);
|
|
@@ -4242,6 +4393,7 @@
|
|
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
|
|
dumb_bo_destroy(drmmode->fd, drmmode_crtc->cursor_bo);
|
|
+ drmmode_destroy_tearfree_shadow(crtc);
|
|
}
|
|
}
|
|
|
|
diff --color -Naur --exclude .git xorg-server-21.1.8/hw/xfree86/drivers/modesetting/drmmode_display.h xorg-server-21.1.8-patched/hw/xfree86/drivers/modesetting/drmmode_display.h
|
|
--- xorg-server-21.1.8/hw/xfree86/drivers/modesetting/drmmode_display.h 2023-03-29 08:55:03.000000000 -0400
|
|
+++ xorg-server-21.1.8-patched/hw/xfree86/drivers/modesetting/drmmode_display.h 2023-07-19 23:11:47.097508779 -0400
|
|
@@ -135,6 +135,7 @@
|
|
Bool async_flip_secondaries;
|
|
Bool dri2_enable;
|
|
Bool present_enable;
|
|
+ Bool tearfree_enable;
|
|
|
|
uint32_t vrr_prop_id;
|
|
Bool use_ctm;
|
|
@@ -167,6 +168,20 @@
|
|
} drmmode_format_rec, *drmmode_format_ptr;
|
|
|
|
typedef struct {
|
|
+ drmmode_bo bo;
|
|
+ uint32_t fb_id;
|
|
+ PixmapPtr px;
|
|
+ RegionRec dmg;
|
|
+} drmmode_shadow_fb_rec, *drmmode_shadow_fb_ptr;
|
|
+
|
|
+typedef struct {
|
|
+ drmmode_shadow_fb_rec buf[2];
|
|
+ struct xorg_list dri_flip_list;
|
|
+ uint32_t back_idx;
|
|
+ uint32_t flip_seq;
|
|
+} drmmode_tearfree_rec, *drmmode_tearfree_ptr;
|
|
+
|
|
+typedef struct {
|
|
drmmode_ptr drmmode;
|
|
drmModeCrtcPtr mode_crtc;
|
|
uint32_t vblank_pipe;
|
|
@@ -184,11 +199,14 @@
|
|
|
|
drmmode_bo rotate_bo;
|
|
unsigned rotate_fb_id;
|
|
+ drmmode_tearfree_rec tearfree;
|
|
|
|
PixmapPtr prime_pixmap;
|
|
PixmapPtr prime_pixmap_back;
|
|
unsigned prime_pixmap_x;
|
|
|
|
+ int src_x, src_y;
|
|
+
|
|
/**
|
|
* @{ MSC (vblank count) handling for the PRESENT extension.
|
|
*
|
|
@@ -200,6 +218,8 @@
|
|
uint64_t msc_high;
|
|
/** @} */
|
|
|
|
+ uint64_t next_msc;
|
|
+
|
|
Bool need_modeset;
|
|
struct xorg_list mode_list;
|
|
|
|
@@ -308,8 +328,13 @@
|
|
int *depth, int *bpp);
|
|
|
|
void drmmode_copy_fb(ScrnInfoPtr pScrn, drmmode_ptr drmmode);
|
|
+void drmmode_copy_damage(xf86CrtcPtr crtc, PixmapPtr dst, RegionPtr damage,
|
|
+ Bool empty);
|
|
+
|
|
+int drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, int x, int y,
|
|
+ uint32_t flags, void *data);
|
|
|
|
-int drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, uint32_t flags, void *data);
|
|
+Bool drmmode_crtc_get_fb_id(xf86CrtcPtr crtc, uint32_t *fb_id, int *x, int *y);
|
|
|
|
void drmmode_set_dpms(ScrnInfoPtr scrn, int PowerManagementMode, int flags);
|
|
void drmmode_crtc_set_vrr(xf86CrtcPtr crtc, Bool enabled);
|
|
diff --color -Naur --exclude .git xorg-server-21.1.8/hw/xfree86/drivers/modesetting/modesetting.man xorg-server-21.1.8-patched/hw/xfree86/drivers/modesetting/modesetting.man
|
|
--- xorg-server-21.1.8/hw/xfree86/drivers/modesetting/modesetting.man 2023-03-29 08:55:03.000000000 -0400
|
|
+++ xorg-server-21.1.8-patched/hw/xfree86/drivers/modesetting/modesetting.man 2023-07-19 23:11:47.097508779 -0400
|
|
@@ -109,6 +109,21 @@
|
|
entries, if supported by the kernel. By default, GAMMA_LUT will be used for
|
|
kms drivers which are known to be safe for use of GAMMA_LUT.
|
|
.TP
|
|
+.BI "Option \*qTearFree\*q \*q" boolean \*q
|
|
+Enable tearing prevention using the hardware page flipping mechanism.
|
|
+It allocates two extra scanout buffers for each CRTC and utilizes damage
|
|
+tracking to minimize buffer copying and skip unnecessary flips when the
|
|
+screen's contents have not changed. It works on transformed screens too, such
|
|
+as rotated and scaled CRTCs. When PageFlip is enabled, fullscreen DRI
|
|
+applications will still have the discretion to not use tearing prevention.
|
|
+.br
|
|
+The default is
|
|
+.B off.
|
|
+.TP
|
|
+.BI "Option \*qAtomic\*q \*q" boolean \*q
|
|
+Enable atomic modesetting when supported. The default is
|
|
+.B off.
|
|
+.TP
|
|
.SH "SEE ALSO"
|
|
@xservername@(@appmansuffix@), @xconfigfile@(@filemansuffix@), Xserver(@appmansuffix@),
|
|
X(@miscmansuffix@)
|
|
diff --color -Naur --exclude .git xorg-server-21.1.8/hw/xfree86/drivers/modesetting/pageflip.c xorg-server-21.1.8-patched/hw/xfree86/drivers/modesetting/pageflip.c
|
|
--- xorg-server-21.1.8/hw/xfree86/drivers/modesetting/pageflip.c 2023-03-29 08:55:03.000000000 -0400
|
|
+++ xorg-server-21.1.8-patched/hw/xfree86/drivers/modesetting/pageflip.c 2023-07-19 23:11:47.098508794 -0400
|
|
@@ -35,8 +35,8 @@
|
|
* Returns a negative value on error, 0 if there was nothing to process,
|
|
* or 1 if we handled any events.
|
|
*/
|
|
-int
|
|
-ms_flush_drm_events(ScreenPtr screen)
|
|
+static int
|
|
+ms_flush_drm_events_timeout(ScreenPtr screen, int timeout)
|
|
{
|
|
ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
|
|
modesettingPtr ms = modesettingPTR(scrn);
|
|
@@ -45,7 +45,7 @@
|
|
int r;
|
|
|
|
do {
|
|
- r = xserver_poll(&p, 1, 0);
|
|
+ r = xserver_poll(&p, 1, timeout);
|
|
} while (r == -1 && (errno == EINTR || errno == EAGAIN));
|
|
|
|
/* If there was an error, r will be < 0. Return that. If there was
|
|
@@ -63,6 +63,12 @@
|
|
return 1;
|
|
}
|
|
|
|
+int
|
|
+ms_flush_drm_events(ScreenPtr screen)
|
|
+{
|
|
+ return ms_flush_drm_events_timeout(screen, 0);
|
|
+}
|
|
+
|
|
#ifdef GLAMOR_HAS_GBM
|
|
|
|
/*
|
|
@@ -93,6 +99,8 @@
|
|
Bool on_reference_crtc;
|
|
/* reference to the ms_flipdata */
|
|
struct ms_flipdata *flipdata;
|
|
+ struct xorg_list node;
|
|
+ uint32_t tearfree_seq;
|
|
};
|
|
|
|
/**
|
|
@@ -136,7 +144,8 @@
|
|
flipdata->fe_usec,
|
|
flipdata->event);
|
|
|
|
- drmModeRmFB(ms->fd, flipdata->old_fb_id);
|
|
+ if (flipdata->old_fb_id)
|
|
+ drmModeRmFB(ms->fd, flipdata->old_fb_id);
|
|
}
|
|
ms_pageflip_free(flip);
|
|
}
|
|
@@ -160,11 +169,32 @@
|
|
}
|
|
|
|
static Bool
|
|
-do_queue_flip_on_crtc(modesettingPtr ms, xf86CrtcPtr crtc,
|
|
- uint32_t flags, uint32_t seq)
|
|
+do_queue_flip_on_crtc(ScreenPtr screen, xf86CrtcPtr crtc, uint32_t flags,
|
|
+ uint32_t seq, uint32_t fb_id, int x, int y)
|
|
{
|
|
- return drmmode_crtc_flip(crtc, ms->drmmode.fb_id, flags,
|
|
- (void *) (uintptr_t) seq);
|
|
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
|
|
+
|
|
+ while (drmmode_crtc_flip(crtc, fb_id, x, y, flags, (void *)(long)seq)) {
|
|
+ /* We may have failed because the event queue was full. Flush it
|
|
+ * and retry. If there was nothing to flush, then we failed for
|
|
+ * some other reason and should just return an error.
|
|
+ */
|
|
+ if (ms_flush_drm_events(screen) <= 0) {
|
|
+ /* The failure could be caused by a pending TearFree flip, in which
|
|
+ * case we should wait until there's a new event and try again.
|
|
+ */
|
|
+ if (!trf->flip_seq || ms_flush_drm_events_timeout(screen, -1) < 0) {
|
|
+ ms_drm_abort_seq(crtc->scrn, seq);
|
|
+ return TRUE;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* We flushed some events, so try again. */
|
|
+ xf86DrvMsg(crtc->scrn->scrnIndex, X_WARNING, "flip queue retry\n");
|
|
+ }
|
|
+
|
|
+ return FALSE;
|
|
}
|
|
|
|
enum queue_flip_status {
|
|
@@ -177,11 +207,10 @@
|
|
static int
|
|
queue_flip_on_crtc(ScreenPtr screen, xf86CrtcPtr crtc,
|
|
struct ms_flipdata *flipdata,
|
|
- int ref_crtc_vblank_pipe, uint32_t flags)
|
|
+ xf86CrtcPtr ref_crtc, uint32_t flags)
|
|
{
|
|
ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
|
|
modesettingPtr ms = modesettingPTR(scrn);
|
|
- drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
struct ms_crtc_pageflip *flip;
|
|
uint32_t seq;
|
|
|
|
@@ -193,7 +222,7 @@
|
|
/* Only the reference crtc will finally deliver its page flip
|
|
* completion event. All other crtc's events will be discarded.
|
|
*/
|
|
- flip->on_reference_crtc = (drmmode_crtc->vblank_pipe == ref_crtc_vblank_pipe);
|
|
+ flip->on_reference_crtc = crtc == ref_crtc;
|
|
flip->flipdata = flipdata;
|
|
|
|
seq = ms_drm_queue_alloc(crtc, flip, ms_pageflip_handler, ms_pageflip_abort);
|
|
@@ -205,20 +234,9 @@
|
|
/* take a reference on flipdata for use in flip */
|
|
flipdata->flip_count++;
|
|
|
|
- while (do_queue_flip_on_crtc(ms, crtc, flags, seq)) {
|
|
- /* We may have failed because the event queue was full. Flush it
|
|
- * and retry. If there was nothing to flush, then we failed for
|
|
- * some other reason and should just return an error.
|
|
- */
|
|
- if (ms_flush_drm_events(screen) <= 0) {
|
|
- /* Aborting will also decrement flip_count and free(flip). */
|
|
- ms_drm_abort_seq(scrn, seq);
|
|
- return QUEUE_FLIP_DRM_FLUSH_FAILED;
|
|
- }
|
|
-
|
|
- /* We flushed some events, so try again. */
|
|
- xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue retry\n");
|
|
- }
|
|
+ if (do_queue_flip_on_crtc(screen, crtc, flags, seq, ms->drmmode.fb_id,
|
|
+ crtc->x, crtc->y))
|
|
+ return QUEUE_FLIP_DRM_FLUSH_FAILED;
|
|
|
|
/* The page flip succeeded. */
|
|
return QUEUE_FLIP_SUCCESS;
|
|
@@ -294,20 +312,75 @@
|
|
}
|
|
}
|
|
|
|
+static Bool
|
|
+ms_tearfree_dri_flip(modesettingPtr ms, xf86CrtcPtr crtc, void *event,
|
|
+ ms_pageflip_handler_proc pageflip_handler,
|
|
+ ms_pageflip_abort_proc pageflip_abort)
|
|
+{
|
|
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
|
|
+ struct ms_crtc_pageflip *flip;
|
|
+ struct ms_flipdata *flipdata;
|
|
+ RegionRec region;
|
|
+ RegionPtr dirty;
|
|
+
|
|
+ if (!ms_tearfree_is_active_on_crtc(crtc))
|
|
+ return FALSE;
|
|
+
|
|
+ /* Check for damage on the primary scanout to know if TearFree will flip */
|
|
+ dirty = DamageRegion(ms->damage);
|
|
+ if (RegionNil(dirty))
|
|
+ return FALSE;
|
|
+
|
|
+ /* Compute how much of the current damage intersects with this CRTC */
|
|
+ RegionInit(®ion, &crtc->bounds, 0);
|
|
+ RegionIntersect(®ion, ®ion, dirty);
|
|
+
|
|
+ /* No damage on this CRTC means no TearFree flip. This means the DRI client
|
|
+ * didn't change this CRTC's contents at all with its presentation, possibly
|
|
+ * because its window is fully occluded by another window on this CRTC.
|
|
+ */
|
|
+ if (RegionNil(®ion))
|
|
+ return FALSE;
|
|
+
|
|
+ flip = calloc(1, sizeof(*flip));
|
|
+ if (!flip)
|
|
+ return FALSE;
|
|
+
|
|
+ flipdata = calloc(1, sizeof(*flipdata));
|
|
+ if (!flipdata) {
|
|
+ free(flip);
|
|
+ return FALSE;
|
|
+ }
|
|
+
|
|
+ /* Only track the DRI client's fake flip on the reference CRTC, which aligns
|
|
+ * with the behavior of Present when a client copies its pixmap rather than
|
|
+ * directly flipping it onto the display.
|
|
+ */
|
|
+ flip->on_reference_crtc = TRUE;
|
|
+ flip->flipdata = flipdata;
|
|
+ flip->tearfree_seq = trf->flip_seq;
|
|
+ flipdata->screen = xf86ScrnToScreen(crtc->scrn);
|
|
+ flipdata->event = event;
|
|
+ flipdata->flip_count = 1;
|
|
+ flipdata->event_handler = pageflip_handler;
|
|
+ flipdata->abort_handler = pageflip_abort;
|
|
+
|
|
+ /* Keep the list in FIFO order so that clients are notified in order */
|
|
+ xorg_list_append(&flip->node, &trf->dri_flip_list);
|
|
+ return TRUE;
|
|
+}
|
|
|
|
Bool
|
|
ms_do_pageflip(ScreenPtr screen,
|
|
PixmapPtr new_front,
|
|
void *event,
|
|
- int ref_crtc_vblank_pipe,
|
|
+ xf86CrtcPtr ref_crtc,
|
|
Bool async,
|
|
ms_pageflip_handler_proc pageflip_handler,
|
|
ms_pageflip_abort_proc pageflip_abort,
|
|
const char *log_prefix)
|
|
{
|
|
-#ifndef GLAMOR_HAS_GBM
|
|
- return FALSE;
|
|
-#else
|
|
ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
|
|
modesettingPtr ms = modesettingPTR(scrn);
|
|
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
|
|
@@ -315,6 +388,22 @@
|
|
uint32_t flags;
|
|
int i;
|
|
struct ms_flipdata *flipdata;
|
|
+
|
|
+ /* A NULL pixmap indicates this DRI client's pixmap is to be flipped through
|
|
+ * TearFree instead. The pixmap is already copied to the primary scanout at
|
|
+ * this point, so all that's left is to wire up this fake flip to TearFree
|
|
+ * so that TearFree can send a notification to the DRI client when the
|
|
+ * pixmap actually appears on the display. This is the only way to let DRI
|
|
+ * clients accurately know when their pixmaps appear on the display when
|
|
+ * TearFree is enabled.
|
|
+ */
|
|
+ if (!new_front) {
|
|
+ if (!ms_tearfree_dri_flip(ms, ref_crtc, event, pageflip_handler,
|
|
+ pageflip_abort))
|
|
+ goto error_free_event;
|
|
+ return TRUE;
|
|
+ }
|
|
+
|
|
ms->glamor.block_handler(screen);
|
|
|
|
new_front_bo.gbm = ms->glamor.gbm_bo_from_pixmap(screen, new_front);
|
|
@@ -324,7 +413,7 @@
|
|
xf86DrvMsg(scrn->scrnIndex, X_ERROR,
|
|
"%s: Failed to get GBM BO for flip to new front.\n",
|
|
log_prefix);
|
|
- return FALSE;
|
|
+ goto error_free_event;
|
|
}
|
|
|
|
flipdata = calloc(1, sizeof(struct ms_flipdata));
|
|
@@ -332,7 +421,7 @@
|
|
drmmode_bo_destroy(&ms->drmmode, &new_front_bo);
|
|
xf86DrvMsg(scrn->scrnIndex, X_ERROR,
|
|
"%s: Failed to allocate flipdata.\n", log_prefix);
|
|
- return FALSE;
|
|
+ goto error_free_event;
|
|
}
|
|
|
|
flipdata->event = event;
|
|
@@ -380,7 +469,6 @@
|
|
for (i = 0; i < config->num_crtc; i++) {
|
|
enum queue_flip_status flip_status;
|
|
xf86CrtcPtr crtc = config->crtc[i];
|
|
- drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
|
|
if (!xf86_crtc_on(crtc))
|
|
continue;
|
|
@@ -401,13 +489,11 @@
|
|
* outputs in a "clone-mode" or "mirror-mode" configuration.
|
|
*/
|
|
if (ms->drmmode.can_async_flip && ms->drmmode.async_flip_secondaries &&
|
|
- (drmmode_crtc->vblank_pipe != ref_crtc_vblank_pipe) &&
|
|
- (ref_crtc_vblank_pipe >= 0))
|
|
+ ref_crtc && crtc != ref_crtc)
|
|
flags |= DRM_MODE_PAGE_FLIP_ASYNC;
|
|
|
|
flip_status = queue_flip_on_crtc(screen, crtc, flipdata,
|
|
- ref_crtc_vblank_pipe,
|
|
- flags);
|
|
+ ref_crtc, flags);
|
|
|
|
switch (flip_status) {
|
|
case QUEUE_FLIP_ALLOC_FAILED:
|
|
@@ -456,13 +542,150 @@
|
|
drmmode_bo_destroy(&ms->drmmode, &new_front_bo);
|
|
/* if only the local reference - free the structure,
|
|
* else drop the local reference and return */
|
|
- if (flipdata->flip_count == 1)
|
|
+ if (flipdata->flip_count == 1) {
|
|
free(flipdata);
|
|
- else
|
|
+ } else {
|
|
flipdata->flip_count--;
|
|
+ return FALSE;
|
|
+ }
|
|
|
|
+error_free_event:
|
|
+ /* Free the event since the caller has no way to know it's safe to free */
|
|
+ free(event);
|
|
return FALSE;
|
|
-#endif /* GLAMOR_HAS_GBM */
|
|
}
|
|
|
|
+Bool
|
|
+ms_tearfree_dri_abort(xf86CrtcPtr crtc,
|
|
+ Bool (*match)(void *data, void *match_data),
|
|
+ void *match_data)
|
|
+{
|
|
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
|
|
+ struct ms_crtc_pageflip *flip;
|
|
+
|
|
+ /* The window is getting destroyed; abort without notifying the client */
|
|
+ xorg_list_for_each_entry(flip, &trf->dri_flip_list, node) {
|
|
+ if (match(flip->flipdata->event, match_data)) {
|
|
+ xorg_list_del(&flip->node);
|
|
+ ms_pageflip_abort(flip);
|
|
+ return TRUE;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+void
|
|
+ms_tearfree_dri_abort_all(xf86CrtcPtr crtc)
|
|
+{
|
|
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
|
|
+ struct ms_crtc_pageflip *flip, *tmp;
|
|
+ uint64_t usec = 0, msc = 0;
|
|
+
|
|
+ /* Nothing to abort if there aren't any DRI clients waiting for a flip */
|
|
+ if (xorg_list_is_empty(&trf->dri_flip_list))
|
|
+ return;
|
|
+
|
|
+ /* Even though we're aborting, these clients' pixmaps were actually blitted,
|
|
+ * so technically the presentation isn't aborted. That's why the normal
|
|
+ * handler is called instead of the abort handler, along with the current
|
|
+ * time and MSC for this CRTC.
|
|
+ */
|
|
+ ms_get_crtc_ust_msc(crtc, &usec, &msc);
|
|
+ xorg_list_for_each_entry_safe(flip, tmp, &trf->dri_flip_list, node)
|
|
+ ms_pageflip_handler(msc, usec, flip);
|
|
+ xorg_list_init(&trf->dri_flip_list);
|
|
+}
|
|
+
|
|
+static void
|
|
+ms_tearfree_dri_notify(drmmode_tearfree_ptr trf, uint64_t msc, uint64_t usec)
|
|
+{
|
|
+ struct ms_crtc_pageflip *flip, *tmp;
|
|
+
|
|
+ xorg_list_for_each_entry_safe(flip, tmp, &trf->dri_flip_list, node) {
|
|
+ /* If a TearFree flip was already pending at the time this DRI client's
|
|
+ * pixmap was copied, then the pixmap isn't contained in this TearFree
|
|
+ * flip, but will be part of the next TearFree flip instead.
|
|
+ */
|
|
+ if (flip->tearfree_seq) {
|
|
+ flip->tearfree_seq = 0;
|
|
+ } else {
|
|
+ xorg_list_del(&flip->node);
|
|
+ ms_pageflip_handler(msc, usec, flip);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+ms_tearfree_flip_abort(void *data)
|
|
+{
|
|
+ xf86CrtcPtr crtc = data;
|
|
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
|
|
+
|
|
+ trf->flip_seq = 0;
|
|
+ ms_tearfree_dri_abort_all(crtc);
|
|
+}
|
|
+
|
|
+static void
|
|
+ms_tearfree_flip_handler(uint64_t msc, uint64_t usec, void *data)
|
|
+{
|
|
+ xf86CrtcPtr crtc = data;
|
|
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
|
|
+
|
|
+ /* Swap the buffers and complete the flip */
|
|
+ trf->back_idx ^= 1;
|
|
+ trf->flip_seq = 0;
|
|
+
|
|
+ /* Notify DRI clients that their pixmaps are now visible on the display */
|
|
+ ms_tearfree_dri_notify(trf, msc, usec);
|
|
+}
|
|
+
|
|
+Bool
|
|
+ms_do_tearfree_flip(ScreenPtr screen, xf86CrtcPtr crtc)
|
|
+{
|
|
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
|
|
+ uint32_t idx = trf->back_idx, seq;
|
|
+
|
|
+ seq = ms_drm_queue_alloc(crtc, crtc, ms_tearfree_flip_handler,
|
|
+ ms_tearfree_flip_abort);
|
|
+ if (!seq) {
|
|
+ /* Need to notify the DRI clients if a sequence wasn't allocated. Once a
|
|
+ * sequence is allocated, explicitly performing this cleanup isn't
|
|
+ * necessary since it's already done as part of aborting the sequence.
|
|
+ */
|
|
+ ms_tearfree_dri_abort_all(crtc);
|
|
+ goto no_flip;
|
|
+ }
|
|
+
|
|
+ /* Copy the damage to the back buffer and then flip it at the vblank */
|
|
+ drmmode_copy_damage(crtc, trf->buf[idx].px, &trf->buf[idx].dmg, TRUE);
|
|
+ if (do_queue_flip_on_crtc(screen, crtc, DRM_MODE_PAGE_FLIP_EVENT,
|
|
+ seq, trf->buf[idx].fb_id, 0, 0))
|
|
+ goto no_flip;
|
|
+
|
|
+ trf->flip_seq = seq;
|
|
+ return FALSE;
|
|
+
|
|
+no_flip:
|
|
+ xf86DrvMsg(crtc->scrn->scrnIndex, X_WARNING,
|
|
+ "TearFree flip failed, rendering frame without TearFree\n");
|
|
+ drmmode_copy_damage(crtc, trf->buf[idx ^ 1].px,
|
|
+ &trf->buf[idx ^ 1].dmg, FALSE);
|
|
+ return TRUE;
|
|
+}
|
|
#endif
|
|
+
|
|
+Bool
|
|
+ms_tearfree_is_active_on_crtc(xf86CrtcPtr crtc)
|
|
+{
|
|
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
|
|
+
|
|
+ /* If TearFree is enabled, XServer owns the VT, and the CRTC is active */
|
|
+ return trf->buf[0].px && crtc->scrn->vtSema && xf86_crtc_on(crtc);
|
|
+}
|
|
diff --color -Naur --exclude .git xorg-server-21.1.8/hw/xfree86/drivers/modesetting/present.c xorg-server-21.1.8-patched/hw/xfree86/drivers/modesetting/present.c
|
|
--- xorg-server-21.1.8/hw/xfree86/drivers/modesetting/present.c 2023-03-29 08:55:03.000000000 -0400
|
|
+++ xorg-server-21.1.8-patched/hw/xfree86/drivers/modesetting/present.c 2023-07-19 23:11:47.098508794 -0400
|
|
@@ -165,6 +165,13 @@
|
|
{
|
|
ScreenPtr screen = crtc->pScreen;
|
|
ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
|
|
+#ifdef GLAMOR_HAS_GBM
|
|
+ xf86CrtcPtr xf86_crtc = crtc->devPrivate;
|
|
+
|
|
+ /* Check if this is a fake flip routed through TearFree and abort it */
|
|
+ if (ms_tearfree_dri_abort(xf86_crtc, ms_present_event_match, &event_id))
|
|
+ return;
|
|
+#endif
|
|
|
|
ms_drm_abort(scrn, ms_present_event_match, &event_id);
|
|
}
|
|
@@ -318,14 +325,32 @@
|
|
modesettingPtr ms = modesettingPTR(scrn);
|
|
|
|
if (ms->drmmode.sprites_visible > 0)
|
|
- return FALSE;
|
|
+ goto no_flip;
|
|
|
|
if(!ms_present_check_unflip(crtc, window, pixmap, sync_flip, reason))
|
|
- return FALSE;
|
|
+ goto no_flip;
|
|
|
|
ms->flip_window = window;
|
|
|
|
return TRUE;
|
|
+
|
|
+no_flip:
|
|
+ /* Export some info about TearFree if Present can't flip anyway */
|
|
+ if (reason) {
|
|
+ xf86CrtcPtr xf86_crtc = crtc->devPrivate;
|
|
+ drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
|
|
+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
|
|
+
|
|
+ if (ms_tearfree_is_active_on_crtc(xf86_crtc)) {
|
|
+ if (trf->flip_seq)
|
|
+ /* The driver has a TearFree flip pending */
|
|
+ *reason = PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING;
|
|
+ else
|
|
+ /* The driver uses TearFree flips and there's no flip pending */
|
|
+ *reason = PRESENT_FLIP_REASON_DRIVER_TEARFREE;
|
|
+ }
|
|
+ }
|
|
+ return FALSE;
|
|
}
|
|
|
|
/*
|
|
@@ -343,11 +368,12 @@
|
|
ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
|
|
modesettingPtr ms = modesettingPTR(scrn);
|
|
xf86CrtcPtr xf86_crtc = crtc->devPrivate;
|
|
- drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
|
|
Bool ret;
|
|
struct ms_present_vblank_event *event;
|
|
|
|
- if (!ms_present_check_flip(crtc, ms->flip_window, pixmap, sync_flip, NULL))
|
|
+ /* A NULL pixmap means this is a fake flip to be routed through TearFree */
|
|
+ if (pixmap &&
|
|
+ !ms_present_check_flip(crtc, ms->flip_window, pixmap, sync_flip, NULL))
|
|
return FALSE;
|
|
|
|
event = calloc(1, sizeof(struct ms_present_vblank_event));
|
|
@@ -360,6 +386,12 @@
|
|
event->event_id = event_id;
|
|
event->unflip = FALSE;
|
|
|
|
+ /* Register the fake flip (indicated by a NULL pixmap) with TearFree */
|
|
+ if (!pixmap)
|
|
+ return ms_do_pageflip(screen, NULL, event, xf86_crtc, FALSE,
|
|
+ ms_present_flip_handler, ms_present_flip_abort,
|
|
+ "Present-TearFree-flip");
|
|
+
|
|
/* A window can only flip if it covers the entire X screen.
|
|
* Only one window can flip at a time.
|
|
*
|
|
@@ -371,7 +403,7 @@
|
|
ms_present_set_screen_vrr(scrn, TRUE);
|
|
}
|
|
|
|
- ret = ms_do_pageflip(screen, pixmap, event, drmmode_crtc->vblank_pipe, !sync_flip,
|
|
+ ret = ms_do_pageflip(screen, pixmap, event, xf86_crtc, !sync_flip,
|
|
ms_present_flip_handler, ms_present_flip_abort,
|
|
"Present-flip");
|
|
if (ret)
|
|
@@ -403,7 +435,7 @@
|
|
event->unflip = TRUE;
|
|
|
|
if (ms_present_check_unflip(NULL, screen->root, pixmap, TRUE, NULL) &&
|
|
- ms_do_pageflip(screen, pixmap, event, -1, FALSE,
|
|
+ ms_do_pageflip(screen, pixmap, event, NULL, FALSE,
|
|
ms_present_flip_handler, ms_present_flip_abort,
|
|
"Present-unflip")) {
|
|
return;
|
|
diff --color -Naur --exclude .git xorg-server-21.1.8/hw/xfree86/drivers/modesetting/vblank.c xorg-server-21.1.8-patched/hw/xfree86/drivers/modesetting/vblank.c
|
|
--- xorg-server-21.1.8/hw/xfree86/drivers/modesetting/vblank.c 2023-03-29 08:55:03.000000000 -0400
|
|
+++ xorg-server-21.1.8-patched/hw/xfree86/drivers/modesetting/vblank.c 2023-07-19 23:11:47.098508794 -0400
|
|
@@ -260,6 +260,51 @@
|
|
}
|
|
}
|
|
|
|
+static void
|
|
+ms_drm_set_seq_msc(uint32_t seq, uint64_t msc)
|
|
+{
|
|
+ struct ms_drm_queue *q;
|
|
+
|
|
+ xorg_list_for_each_entry(q, &ms_drm_queue, list) {
|
|
+ if (q->seq == seq) {
|
|
+ q->msc = msc;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+ms_drm_set_seq_queued(uint32_t seq, uint64_t msc)
|
|
+{
|
|
+ drmmode_crtc_private_ptr drmmode_crtc;
|
|
+ struct ms_drm_queue *q;
|
|
+
|
|
+ xorg_list_for_each_entry(q, &ms_drm_queue, list) {
|
|
+ if (q->seq == seq) {
|
|
+ drmmode_crtc = q->crtc->driver_private;
|
|
+ if (msc < drmmode_crtc->next_msc)
|
|
+ drmmode_crtc->next_msc = msc;
|
|
+ q->msc = msc;
|
|
+ q->kernel_queued = TRUE;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static Bool
|
|
+ms_queue_coalesce(xf86CrtcPtr crtc, uint32_t seq, uint64_t msc)
|
|
+{
|
|
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
+
|
|
+ /* If the next MSC is too late, then this event can't be coalesced */
|
|
+ if (msc < drmmode_crtc->next_msc)
|
|
+ return FALSE;
|
|
+
|
|
+ /* Set the target MSC on this sequence number */
|
|
+ ms_drm_set_seq_msc(seq, msc);
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
Bool
|
|
ms_queue_vblank(xf86CrtcPtr crtc, ms_queue_flag flags,
|
|
uint64_t msc, uint64_t *msc_queued, uint32_t seq)
|
|
@@ -271,6 +316,10 @@
|
|
drmVBlank vbl;
|
|
int ret;
|
|
|
|
+ /* Try coalescing this event into another to avoid event queue exhaustion */
|
|
+ if (flags == MS_QUEUE_ABSOLUTE && ms_queue_coalesce(crtc, seq, msc))
|
|
+ return TRUE;
|
|
+
|
|
for (;;) {
|
|
/* Queue an event at the specified sequence */
|
|
if (ms->has_queue_sequence || !ms->tried_queue_sequence) {
|
|
@@ -287,8 +336,10 @@
|
|
ret = drmCrtcQueueSequence(ms->fd, drmmode_crtc->mode_crtc->crtc_id,
|
|
drm_flags, msc, &kernel_queued, seq);
|
|
if (ret == 0) {
|
|
+ msc = ms_kernel_msc_to_crtc_msc(crtc, kernel_queued, TRUE);
|
|
+ ms_drm_set_seq_queued(seq, msc);
|
|
if (msc_queued)
|
|
- *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, kernel_queued, TRUE);
|
|
+ *msc_queued = msc;
|
|
ms->has_queue_sequence = TRUE;
|
|
return TRUE;
|
|
}
|
|
@@ -310,8 +361,10 @@
|
|
vbl.request.signal = seq;
|
|
ret = drmWaitVBlank(ms->fd, &vbl);
|
|
if (ret == 0) {
|
|
+ msc = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence, FALSE);
|
|
+ ms_drm_set_seq_queued(seq, msc);
|
|
if (msc_queued)
|
|
- *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence, FALSE);
|
|
+ *msc_queued = msc;
|
|
return TRUE;
|
|
}
|
|
check:
|
|
@@ -418,13 +471,15 @@
|
|
if (!ms_drm_seq)
|
|
++ms_drm_seq;
|
|
q->seq = ms_drm_seq++;
|
|
+ q->msc = UINT64_MAX;
|
|
q->scrn = scrn;
|
|
q->crtc = crtc;
|
|
q->data = data;
|
|
q->handler = handler;
|
|
q->abort = abort;
|
|
|
|
- xorg_list_add(&q->list, &ms_drm_queue);
|
|
+ /* Keep the list formatted in ascending order of sequence number */
|
|
+ xorg_list_append(&q->list, &ms_drm_queue);
|
|
|
|
return q->seq;
|
|
}
|
|
@@ -437,9 +492,18 @@
|
|
static void
|
|
ms_drm_abort_one(struct ms_drm_queue *q)
|
|
{
|
|
+ if (q->aborted)
|
|
+ return;
|
|
+
|
|
+ /* Don't remove vblank events if they were queued in the kernel */
|
|
+ if (q->kernel_queued) {
|
|
+ q->abort(q->data);
|
|
+ q->aborted = TRUE;
|
|
+ } else {
|
|
xorg_list_del(&q->list);
|
|
q->abort(q->data);
|
|
free(q);
|
|
+ }
|
|
}
|
|
|
|
/**
|
|
@@ -500,16 +564,64 @@
|
|
{
|
|
struct ms_drm_queue *q, *tmp;
|
|
uint32_t seq = (uint32_t) user_data;
|
|
+ xf86CrtcPtr crtc = NULL;
|
|
+ drmmode_crtc_private_ptr drmmode_crtc;
|
|
+ uint64_t msc, next_msc = UINT64_MAX;
|
|
|
|
- xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
|
|
+ /* Handle the seq for this event first in order to get the CRTC */
|
|
+ xorg_list_for_each_entry(q, &ms_drm_queue, list) {
|
|
if (q->seq == seq) {
|
|
- uint64_t msc;
|
|
+ crtc = q->crtc;
|
|
+ msc = ms_kernel_msc_to_crtc_msc(crtc, frame, is64bit);
|
|
+
|
|
+ /* Write the current MSC to this event to ensure its handler runs in
|
|
+ * the loop below. This is done because we don't want to run the
|
|
+ * handler right now, since we need to ensure all events are handled
|
|
+ * in FIFO order with respect to one another. Otherwise, if this
|
|
+ * event were handled first just because it was queued to the
|
|
+ * kernel, it could run before older events expiring at this MSC.
|
|
+ */
|
|
+ q->msc = msc;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
|
|
- msc = ms_kernel_msc_to_crtc_msc(q->crtc, frame, is64bit);
|
|
+ if (!crtc)
|
|
+ return;
|
|
+
|
|
+ /* Now run all of the vblank events for this CRTC with an expired MSC */
|
|
+ xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
|
|
+ if (q->crtc == crtc && q->msc <= msc) {
|
|
xorg_list_del(&q->list);
|
|
- q->handler(msc, ns / 1000, q->data);
|
|
+ if (!q->aborted)
|
|
+ q->handler(msc, ns / 1000, q->data);
|
|
free(q);
|
|
- break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Find this CRTC's next queued MSC and next non-queued MSC to be handled */
|
|
+ msc = UINT64_MAX;
|
|
+ xorg_list_for_each_entry(q, &ms_drm_queue, list) {
|
|
+ if (q->crtc == crtc) {
|
|
+ if (q->kernel_queued) {
|
|
+ if (q->msc < next_msc)
|
|
+ next_msc = q->msc;
|
|
+ } else if (q->msc < msc) {
|
|
+ msc = q->msc;
|
|
+ seq = q->seq;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Queue an event if the next queued MSC isn't soon enough */
|
|
+ drmmode_crtc = crtc->driver_private;
|
|
+ drmmode_crtc->next_msc = next_msc;
|
|
+ if (msc < next_msc && !ms_queue_vblank(crtc, MS_QUEUE_ABSOLUTE, msc, NULL, seq)) {
|
|
+ xf86DrvMsg(crtc->scrn->scrnIndex, X_WARNING,
|
|
+ "failed to queue next vblank event, aborting lost events\n");
|
|
+ xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
|
|
+ if (q->crtc == crtc && q->msc < next_msc)
|
|
+ ms_drm_abort_one(q);
|
|
}
|
|
}
|
|
}
|
|
diff --color -Naur --exclude .git xorg-server-21.1.8/hw/xfree86/modes/xf86Crtc.h xorg-server-21.1.8-patched/hw/xfree86/modes/xf86Crtc.h
|
|
--- xorg-server-21.1.8/hw/xfree86/modes/xf86Crtc.h 2023-03-29 08:55:03.000000000 -0400
|
|
+++ xorg-server-21.1.8-patched/hw/xfree86/modes/xf86Crtc.h 2023-07-19 23:14:28.826509432 -0400
|
|
@@ -837,11 +837,11 @@
|
|
static _X_INLINE xf86OutputPtr
|
|
xf86CompatOutput(ScrnInfoPtr pScrn)
|
|
{
|
|
- xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
|
|
+ xf86CrtcConfigPtr config;
|
|
|
|
if (xf86CrtcConfigPrivateIndex == -1)
|
|
return NULL;
|
|
-
|
|
+ config = XF86_CRTC_CONFIG_PTR(pScrn);
|
|
if (config->compat_output < 0)
|
|
return NULL;
|
|
return config->output[config->compat_output];
|
|
@@ -912,6 +912,11 @@
|
|
extern _X_EXPORT Bool
|
|
xf86CrtcRotate(xf86CrtcPtr crtc);
|
|
|
|
+extern _X_EXPORT void
|
|
+ xf86RotateCrtcRedisplay(xf86CrtcPtr crtc, PixmapPtr dst_pixmap,
|
|
+ DrawableRec *src_drawable, RegionPtr region,
|
|
+ Bool transform_src);
|
|
+
|
|
/*
|
|
* Clean up any rotation data, used when a crtc is turned off
|
|
* as well as when rotation is disabled.
|
|
diff --color -Naur --exclude .git xorg-server-21.1.8/hw/xfree86/modes/xf86Rotate.c xorg-server-21.1.8-patched/hw/xfree86/modes/xf86Rotate.c
|
|
--- xorg-server-21.1.8/hw/xfree86/modes/xf86Rotate.c 2023-03-29 08:55:03.000000000 -0400
|
|
+++ xorg-server-21.1.8-patched/hw/xfree86/modes/xf86Rotate.c 2023-07-19 23:19:57.089448424 -0400
|
|
@@ -39,13 +39,15 @@
|
|
#include "X11/extensions/dpmsconst.h"
|
|
#include "X11/Xatom.h"
|
|
|
|
-static void
|
|
-xf86RotateCrtcRedisplay(xf86CrtcPtr crtc, RegionPtr region)
|
|
+
|
|
+
|
|
+void
|
|
+xf86RotateCrtcRedisplay(xf86CrtcPtr crtc, PixmapPtr dst_pixmap,
|
|
+ DrawableRec *src_drawable, RegionPtr region,
|
|
+ Bool transform_src)
|
|
{
|
|
ScrnInfoPtr scrn = crtc->scrn;
|
|
ScreenPtr screen = scrn->pScreen;
|
|
- WindowPtr root = screen->root;
|
|
- PixmapPtr dst_pixmap = crtc->rotatedPixmap;
|
|
PictFormatPtr format = PictureWindowFormat(screen->root);
|
|
int error;
|
|
PicturePtr src, dst;
|
|
@@ -57,7 +59,7 @@
|
|
return;
|
|
|
|
src = CreatePicture(None,
|
|
- &root->drawable,
|
|
+ src_drawable,
|
|
format,
|
|
CPSubwindowMode,
|
|
&include_inferiors, serverClient, &error);
|
|
@@ -70,9 +72,11 @@
|
|
if (!dst)
|
|
return;
|
|
|
|
- error = SetPictureTransform(src, &crtc->crtc_to_framebuffer);
|
|
- if (error)
|
|
- return;
|
|
+ if (transform_src) {
|
|
+ error = SetPictureTransform(src, &crtc->crtc_to_framebuffer);
|
|
+ if (error)
|
|
+ return;
|
|
+ }
|
|
if (crtc->transform_in_use && crtc->filter)
|
|
SetPicturePictFilter(src, crtc->filter, crtc->params, crtc->nparams);
|
|
|
|
@@ -205,7 +209,9 @@
|
|
|
|
/* update damaged region */
|
|
if (RegionNotEmpty(&crtc_damage))
|
|
- xf86RotateCrtcRedisplay(crtc, &crtc_damage);
|
|
+ xf86RotateCrtcRedisplay(crtc, crtc->rotatedPixmap,
|
|
+ &pScreen->root->drawable,
|
|
+ &crtc_damage, TRUE);
|
|
|
|
RegionUninit(&crtc_damage);
|
|
}
|
|
diff --color -Naur --exclude .git xorg-server-21.1.8/include/pixmap.h xorg-server-21.1.8-patched/include/pixmap.h
|
|
--- xorg-server-21.1.8/include/pixmap.h 2023-03-29 08:55:03.000000000 -0400
|
|
+++ xorg-server-21.1.8-patched/include/pixmap.h 2023-07-19 23:25:00.751651234 -0400
|
|
@@ -105,13 +105,15 @@
|
|
|
|
extern _X_EXPORT Bool CreateScratchPixmapsForScreen(ScreenPtr /*pScreen */ );
|
|
|
|
-extern _X_EXPORT void FreeScratchPixmapsForScreen(ScreenPtr /*pScreen */ );
|
|
+extern _X_EXPORT Bool PixmapScreenInit(ScreenPtr /*pScreen */ );
|
|
|
|
extern _X_EXPORT PixmapPtr AllocatePixmap(ScreenPtr /*pScreen */ ,
|
|
int /*pixDataSize */ );
|
|
|
|
extern _X_EXPORT void FreePixmap(PixmapPtr /*pPixmap */ );
|
|
|
|
+extern _X_EXPORT void FreeScratchPixmapsForScreen(ScreenPtr /*pScreen */ );
|
|
+
|
|
extern _X_EXPORT PixmapPtr
|
|
PixmapShareToSecondary(PixmapPtr pixmap, ScreenPtr secondary);
|
|
|
|
@@ -134,4 +136,9 @@
|
|
extern _X_EXPORT Bool
|
|
PixmapSyncDirtyHelper(PixmapDirtyUpdatePtr dirty);
|
|
|
|
+extern _X_EXPORT void
|
|
+PixmapDirtyCopyArea(PixmapPtr dst, DrawablePtr src,
|
|
+ int x, int y, int dst_x, int dst_y,
|
|
+ RegionPtr dirty_region);
|
|
+
|
|
#endif /* PIXMAP_H */
|
|
|
|
diff --color -Naur --exclude .git xorg-server-21.1.8/present/present.c xorg-server-21.1.8-patched/present/present.c
|
|
--- xorg-server-21.1.8/present/present.c 2023-03-29 08:55:03.000000000 -0400
|
|
+++ xorg-server-21.1.8-patched/present/present.c 2023-07-19 23:11:47.098508794 -0400
|
|
@@ -157,7 +157,7 @@
|
|
uint64_t remainder,
|
|
uint32_t options)
|
|
{
|
|
- const Bool synced_flip = !(options & PresentOptionAsync);
|
|
+ const Bool synced_flip = !(options & PresentAllAsyncOptions);
|
|
uint64_t target_msc;
|
|
|
|
/* If the specified target-msc lies in the future, then this
|
|
diff --color -Naur --exclude .git xorg-server-21.1.8/present/present.h xorg-server-21.1.8-patched/present/present.h
|
|
--- xorg-server-21.1.8/present/present.h 2023-03-29 08:55:03.000000000 -0400
|
|
+++ xorg-server-21.1.8-patched/present/present.h 2023-07-19 23:11:47.098508794 -0400
|
|
@@ -29,7 +29,15 @@
|
|
|
|
typedef enum {
|
|
PRESENT_FLIP_REASON_UNKNOWN,
|
|
- PRESENT_FLIP_REASON_BUFFER_FORMAT
|
|
+ PRESENT_FLIP_REASON_BUFFER_FORMAT,
|
|
+
|
|
+ /* Don't add new flip reasons after the TearFree ones, since it's expected
|
|
+ * that the TearFree reasons are the highest ones in order to allow doing
|
|
+ * `reason >= PRESENT_FLIP_REASON_DRIVER_TEARFREE` to check if a reason is
|
|
+ * PRESENT_FLIP_REASON_DRIVER_TEARFREE{_FLIPPING}.
|
|
+ */
|
|
+ PRESENT_FLIP_REASON_DRIVER_TEARFREE,
|
|
+ PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING
|
|
} PresentFlipReason;
|
|
|
|
typedef struct present_vblank present_vblank_rec, *present_vblank_ptr;
|
|
diff --color -Naur --exclude .git xorg-server-21.1.8/present/present_scmd.c xorg-server-21.1.8-patched/present/present_scmd.c
|
|
--- xorg-server-21.1.8/present/present_scmd.c 2023-03-29 08:55:03.000000000 -0400
|
|
+++ xorg-server-21.1.8-patched/present/present_scmd.c 2023-07-19 23:11:47.099508810 -0400
|
|
@@ -71,6 +71,7 @@
|
|
PixmapPtr window_pixmap;
|
|
WindowPtr root = screen->root;
|
|
present_screen_priv_ptr screen_priv = present_screen_priv(screen);
|
|
+ PresentFlipReason tmp_reason = PRESENT_FLIP_REASON_UNKNOWN;
|
|
|
|
if (crtc) {
|
|
screen_priv = present_screen_priv(crtc->pScreen);
|
|
@@ -91,6 +92,27 @@
|
|
if (!screen_priv->info->flip)
|
|
return FALSE;
|
|
|
|
+ /* Ask the driver for permission. Do this now to see if there's TearFree. */
|
|
+ if (screen_priv->info->version >= 1 && screen_priv->info->check_flip2) {
|
|
+ if (!(*screen_priv->info->check_flip2) (crtc, window, pixmap, sync_flip, &tmp_reason)) {
|
|
+ DebugPresent(("\td %08" PRIx32 " -> %08" PRIx32 "\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
|
|
+ /* It's fine to return now unless the page flip failure reason is
|
|
+ * PRESENT_FLIP_REASON_BUFFER_FORMAT; we must only output that
|
|
+ * reason if all the other checks pass.
|
|
+ */
|
|
+ if (!reason || tmp_reason != PRESENT_FLIP_REASON_BUFFER_FORMAT) {
|
|
+ if (reason)
|
|
+ *reason = tmp_reason;
|
|
+ return FALSE;
|
|
+ }
|
|
+ }
|
|
+ } else if (screen_priv->info->check_flip) {
|
|
+ if (!(*screen_priv->info->check_flip) (crtc, window, pixmap, sync_flip)) {
|
|
+ DebugPresent(("\td %08" PRIx32 " -> %08" PRIx32 "\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
|
|
+ return FALSE;
|
|
+ }
|
|
+ }
|
|
+
|
|
/* Make sure the window hasn't been redirected with Composite */
|
|
window_pixmap = screen->GetWindowPixmap(window);
|
|
if (window_pixmap != screen->GetScreenPixmap(screen) &&
|
|
@@ -115,7 +137,7 @@
|
|
|
|
/* Does the window match the pixmap exactly? */
|
|
if (window->drawable.x != 0 || window->drawable.y != 0 ||
|
|
-#ifdef COMPOSITE
|
|
+#if defined(COMPOSITE) || defined(ROOTLESS)
|
|
window->drawable.x != pixmap->screen_x || window->drawable.y != pixmap->screen_y ||
|
|
#endif
|
|
window->drawable.width != pixmap->drawable.width ||
|
|
@@ -123,17 +145,10 @@
|
|
return FALSE;
|
|
}
|
|
|
|
- /* Ask the driver for permission */
|
|
- if (screen_priv->info->version >= 1 && screen_priv->info->check_flip2) {
|
|
- if (!(*screen_priv->info->check_flip2) (crtc, window, pixmap, sync_flip, reason)) {
|
|
- DebugPresent(("\td %08" PRIx32 " -> %08" PRIx32 "\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
|
|
- return FALSE;
|
|
- }
|
|
- } else if (screen_priv->info->check_flip) {
|
|
- if (!(*screen_priv->info->check_flip) (crtc, window, pixmap, sync_flip)) {
|
|
- DebugPresent(("\td %08" PRIx32 " -> %08" PRIx32 "\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
|
|
- return FALSE;
|
|
- }
|
|
+ if (tmp_reason == PRESENT_FLIP_REASON_BUFFER_FORMAT) {
|
|
+ if (reason)
|
|
+ *reason = tmp_reason;
|
|
+ return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
@@ -456,7 +471,9 @@
|
|
xorg_list_for_each_entry(vblank, &window_priv->vblank, window_list) {
|
|
if (vblank->queued && vblank->flip && !present_check_flip(vblank->crtc, window, vblank->pixmap, vblank->sync_flip, NULL, 0, 0, &reason)) {
|
|
vblank->flip = FALSE;
|
|
- vblank->reason = reason;
|
|
+ /* Don't spuriously flag this as a TearFree presentation */
|
|
+ if (reason < PRESENT_FLIP_REASON_DRIVER_TEARFREE)
|
|
+ vblank->reason = reason;
|
|
if (vblank->sync_flip)
|
|
vblank->exec_msc = vblank->target_msc;
|
|
}
|
|
@@ -560,7 +577,9 @@
|
|
xorg_list_del(&vblank->window_list);
|
|
vblank->queued = FALSE;
|
|
|
|
- if (vblank->pixmap && vblank->window) {
|
|
+ if (vblank->pixmap && vblank->window &&
|
|
+ (vblank->reason < PRESENT_FLIP_REASON_DRIVER_TEARFREE ||
|
|
+ vblank->exec_msc != vblank->target_msc)) {
|
|
|
|
if (vblank->flip) {
|
|
|
|
@@ -627,6 +646,51 @@
|
|
|
|
present_execute_copy(vblank, crtc_msc);
|
|
|
|
+ /* With TearFree, there's no way to tell exactly when the presentation
|
|
+ * will be visible except by waiting for a notification from the kernel
|
|
+ * driver indicating that the page flip is complete. This is because the
|
|
+ * CRTC's MSC can change while the target MSC is calculated and even
|
|
+ * while the page flip IOCTL is sent to the kernel due to scheduling
|
|
+ * delays and/or unfortunate timing. Even worse, a page flip isn't
|
|
+ * actually guaranteed to be finished after one vblank; it may be
|
|
+ * several MSCs until a flip actually finishes depending on delays and
|
|
+ * load in hardware.
|
|
+ *
|
|
+ * So, to get a notification from the driver with TearFree active, the
|
|
+ * driver expects a present_flip() call with a NULL pixmap to indicate
|
|
+ * that this is a fake flip for a pixmap that's already been copied to
|
|
+ * the primary scanout, which will then be flipped by TearFree. TearFree
|
|
+ * will then send a notification once the flip containing this pixmap is
|
|
+ * complete.
|
|
+ *
|
|
+ * If the fake flip attempt fails, then fall back to just enqueuing a
|
|
+ * vblank event targeting the next MSC.
|
|
+ */
|
|
+ if (!vblank->queued &&
|
|
+ vblank->reason >= PRESENT_FLIP_REASON_DRIVER_TEARFREE) {
|
|
+ uint64_t completion_msc = crtc_msc + 1;
|
|
+
|
|
+ /* If TearFree is already flipping then the presentation will be
|
|
+ * visible at the *next* next vblank. This calculation only matters
|
|
+ * for the vblank event fallback.
|
|
+ */
|
|
+ if (vblank->reason == PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING &&
|
|
+ vblank->exec_msc < crtc_msc)
|
|
+ completion_msc++;
|
|
+
|
|
+ /* Try the fake flip first and then fall back to a vblank event */
|
|
+ if (present_flip(vblank->crtc, vblank->event_id, 0, NULL, TRUE) ||
|
|
+ Success == screen_priv->queue_vblank(screen,
|
|
+ window,
|
|
+ vblank->crtc,
|
|
+ vblank->event_id,
|
|
+ completion_msc)) {
|
|
+ /* Ensure present_execute_post() runs at the next execution */
|
|
+ vblank->exec_msc = vblank->target_msc;
|
|
+ vblank->queued = TRUE;
|
|
+ }
|
|
+ }
|
|
+
|
|
if (vblank->queued) {
|
|
xorg_list_add(&vblank->event_queue, &present_exec_queue);
|
|
xorg_list_append(&vblank->window_list,
|
|
@@ -739,6 +803,11 @@
|
|
if (vblank->crtc != target_crtc || vblank->target_msc != target_msc)
|
|
continue;
|
|
|
|
+ /* Too late to abort now if TearFree execution already happened */
|
|
+ if (vblank->reason >= PRESENT_FLIP_REASON_DRIVER_TEARFREE &&
|
|
+ vblank->exec_msc == vblank->target_msc)
|
|
+ continue;
|
|
+
|
|
present_vblank_scrap(vblank);
|
|
if (vblank->flip_ready)
|
|
present_re_execute(vblank);
|
|
@@ -767,7 +836,12 @@
|
|
|
|
vblank->event_id = ++present_scmd_event_id;
|
|
|
|
- if (vblank->flip && vblank->sync_flip)
|
|
+ /* The soonest presentation is crtc_msc+2 if TearFree is already flipping */
|
|
+ if (vblank->reason == PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING &&
|
|
+ !msc_is_after(vblank->exec_msc, crtc_msc + 1))
|
|
+ vblank->exec_msc -= 2;
|
|
+ else if (vblank->reason >= PRESENT_FLIP_REASON_DRIVER_TEARFREE ||
|
|
+ (vblank->flip && vblank->sync_flip))
|
|
vblank->exec_msc--;
|
|
|
|
xorg_list_append(&vblank->event_queue, &present_exec_queue);
|