summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSultan Alsawaf <sultan@kerneltoast.com>2022-12-19 23:39:23 -0800
committerSultan Alsawaf <sultan@kerneltoast.com>2022-12-19 23:56:27 -0800
commita94dd95369941471774cc78d22474db95fc4bb50 (patch)
treec545fb484a9033732457701934bdaccab6669cb9
parent2d272b705c75353c5a357b785824f5993675a46a (diff)
downloadxserver-a94dd95369941471774cc78d22474db95fc4bb50.tar.gz
modesetting: add support for TearFree page flips
This adds support for TearFree page flips to eliminate tearing without the use of a compositor. It allocates two shadow buffers for each CRTC, a back buffer and a front buffer, and uses damage tracking to minimize excessive copying between buffers and skip unnecessary flips when the screen's contents remain unchanged. It works on transformed screens too, such as rotated and scaled CRTCs. When PageFlip is enabled, TearFree won't force fullscreen DRI clients to synchronize their page flips to the vblank interval. TearFree is disabled by default. Signed-off-by: Sultan Alsawaf <sultan@kerneltoast.com>
-rw-r--r--hw/xfree86/drivers/modesetting/driver.c132
-rw-r--r--hw/xfree86/drivers/modesetting/driver.h3
-rw-r--r--hw/xfree86/drivers/modesetting/drmmode_display.c119
-rw-r--r--hw/xfree86/drivers/modesetting/drmmode_display.h19
-rw-r--r--hw/xfree86/drivers/modesetting/modesetting.man11
-rw-r--r--hw/xfree86/drivers/modesetting/pageflip.c70
-rw-r--r--hw/xfree86/drivers/modesetting/present.c22
7 files changed, 360 insertions, 16 deletions
diff --git a/hw/xfree86/drivers/modesetting/driver.c b/hw/xfree86/drivers/modesetting/driver.c
index fe3315a9c..7a7d5c388 100644
--- a/hw/xfree86/drivers/modesetting/driver.c
+++ b/hw/xfree86/drivers/modesetting/driver.c
@@ -145,6 +145,7 @@ static const OptionInfoRec Options[] = {
{OPTION_VARIABLE_REFRESH, "VariableRefresh", OPTV_BOOLEAN, {0}, FALSE},
{OPTION_USE_GAMMA_LUT, "UseGammaLUT", OPTV_BOOLEAN, {0}, FALSE},
{OPTION_ASYNC_FLIP_SECONDARIES, "AsyncFlipSecondaries", OPTV_BOOLEAN, {0}, FALSE},
+ {OPTION_TEARFREE, "TearFree", OPTV_BOOLEAN, {0}, FALSE},
{-1, NULL, OPTV_NONE, {0}, FALSE}
};
@@ -548,14 +549,16 @@ rotate_clip(PixmapPtr pixmap, BoxPtr rect, drmModeClip *clip, Rotation rotation)
}
static int
-dispatch_dirty_region(ScrnInfoPtr scrn, xf86CrtcPtr crtc,
- PixmapPtr pixmap, DamagePtr damage, int fb_id)
+dispatch_damages(ScrnInfoPtr scrn, xf86CrtcPtr crtc, RegionPtr dirty,
+ PixmapPtr pixmap, DamagePtr damage, int fb_id)
{
modesettingPtr ms = modesettingPTR(scrn);
- RegionPtr dirty = DamageRegion(damage);
unsigned num_cliprects = REGION_NUM_RECTS(dirty);
int ret = 0;
+ if (!ms->dirty_enabled)
+ return 0;
+
if (num_cliprects) {
drmModeClip *clip = xallocarray(num_cliprects, sizeof(drmModeClip));
BoxPtr rect = REGION_RECTS(dirty);
@@ -579,12 +582,102 @@ dispatch_dirty_region(ScrnInfoPtr scrn, xf86CrtcPtr crtc,
}
}
+ if (ret == -EINVAL || ret == -ENOSYS) {
+ xf86DrvMsg(scrn->scrnIndex, X_INFO,
+ "Disabling kernel dirty updates, not required.\n");
+ ms->dirty_enabled = FALSE;
+ }
+
free(clip);
- DamageEmpty(damage);
+ if (damage)
+ DamageEmpty(damage);
}
return ret;
}
+static int
+dispatch_dirty_region(ScrnInfoPtr scrn, xf86CrtcPtr crtc,
+ PixmapPtr pixmap, DamagePtr damage, int fb_id)
+{
+ return dispatch_damages(scrn, crtc, DamageRegion(damage),
+ pixmap, damage, fb_id);
+}
+
+static void
+ms_tearfree_update_damages(ScreenPtr pScreen)
+{
+ ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
+ modesettingPtr ms = modesettingPTR(scrn);
+ RegionPtr dirty = DamageRegion(ms->damage);
+ int c, i;
+
+ if (RegionNil(dirty))
+ return;
+
+ for (c = 0; c < xf86_config->num_crtc; c++) {
+ xf86CrtcPtr crtc = xf86_config->crtc[c];
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
+ RegionRec region;
+
+ /* Compute how much of the damage intersects with this CRTC */
+ RegionInit(&region, &crtc->bounds, 0);
+ RegionIntersect(&region, &region, dirty);
+
+ if (trf->buf[0].px) {
+ for (i = 0; i < ARRAY_SIZE(trf->buf); i++)
+ RegionUnion(&trf->buf[i].dmg, &trf->buf[i].dmg, &region);
+ } else {
+ /* Just notify the kernel of the damages if TearFree isn't used */
+ dispatch_damages(scrn, crtc, &region,
+ pScreen->GetScreenPixmap(pScreen),
+ NULL, ms->drmmode.fb_id);
+ }
+ }
+ DamageEmpty(ms->damage);
+}
+
+static void
+ms_tearfree_do_flips(ScreenPtr pScreen)
+{
+#ifdef GLAMOR_HAS_GBM
+ ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
+ modesettingPtr ms = modesettingPTR(scrn);
+ int c;
+
+ if (!ms->drmmode.tearfree_enable)
+ return;
+
+ for (c = 0; c < xf86_config->num_crtc; c++) {
+ xf86CrtcPtr crtc = xf86_config->crtc[c];
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
+
+ /* Skip disabled CRTCs and those which aren't using TearFree */
+ if (!trf->buf[0].px || !crtc->scrn->vtSema || !xf86_crtc_on(crtc))
+ continue;
+
+ /* Skip if the last flip is still pending, a DRI client is flipping, or
+ * there isn't any damage on the front buffer.
+ */
+ if (trf->flip_seq || ms->drmmode.dri2_flipping ||
+ ms->drmmode.present_flipping ||
+ RegionNil(&trf->buf[trf->back_idx ^ 1].dmg))
+ continue;
+
+ /* Flip. If it fails, notify the kernel of the front buffer damages */
+ if (ms_do_tearfree_flip(pScreen, crtc)) {
+ dispatch_damages(scrn, 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)
{
@@ -606,12 +699,9 @@ dispatch_dirty(ScreenPtr pScreen)
ret = dispatch_dirty_region(scrn, crtc, 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;
}
}
@@ -742,10 +832,13 @@ msBlockHandler(ScreenPtr pScreen, void *timeout)
pScreen->BlockHandler = msBlockHandler;
if (pScreen->isGPU && !ms->drmmode.reverse_prime_offload_mode)
dispatch_secondary_dirty(pScreen);
+ else if (ms->drmmode.tearfree_enable)
+ ms_tearfree_update_damages(pScreen);
else if (ms->dirty_enabled)
dispatch_dirty(pScreen);
ms_dirty_update(pScreen, timeout);
+ ms_tearfree_do_flips(pScreen);
}
static void
@@ -1281,6 +1374,27 @@ PreInit(ScrnInfoPtr pScrn, int flags)
ms->atomic_modeset = FALSE;
}
+ /* TearFree requires glamor and, if PageFlip is enabled, universal planes */
+ if (xf86ReturnOptValBool(ms->drmmode.Options, OPTION_TEARFREE, FALSE)) {
+ if (pScrn->is_gpu) {
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "TearFree cannot synchronize PRIME; use 'PRIME Synchronization' instead\n");
+ } else if (ms->drmmode.glamor) {
+ /* Atomic modesetting implicitly enables universal planes */
+ if (!ms->drmmode.pageflip || ms->atomic_modeset ||
+ !drmSetClientCap(ms->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) {
+ ms->drmmode.tearfree_enable = TRUE;
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "TearFree: enabled\n");
+ } else {
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "TearFree requires either universal planes, or setting 'Option \"PageFlip\" \"off\"'\n");
+ }
+ } else {
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "TearFree requires Glamor acceleration\n");
+ }
+ }
+
ms->kms_has_modifiers = FALSE;
ret = drmGetCap(ms->fd, DRM_CAP_ADDFB2_MODIFIERS, &value);
if (ret == 0 && value != 0)
@@ -1628,13 +1742,13 @@ CreateScreenResources(ScreenPtr pScreen)
err = drmModeDirtyFB(ms->fd, ms->drmmode.fb_id, NULL, 0);
- if (err != -EINVAL && err != -ENOSYS) {
+ if ((err != -EINVAL && err != -ENOSYS) || ms->drmmode.tearfree_enable) {
ms->damage = DamageCreate(NULL, NULL, DamageReportNone, TRUE,
pScreen, rootPixmap);
if (ms->damage) {
DamageRegister(&rootPixmap->drawable, ms->damage);
- ms->dirty_enabled = TRUE;
+ ms->dirty_enabled = err != -EINVAL && err != -ENOSYS;
xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Damage tracking initialized\n");
}
else {
diff --git a/hw/xfree86/drivers/modesetting/driver.h b/hw/xfree86/drivers/modesetting/driver.h
index 4f62646a0..3f2b1d1ae 100644
--- a/hw/xfree86/drivers/modesetting/driver.h
+++ b/hw/xfree86/drivers/modesetting/driver.h
@@ -61,6 +61,7 @@ typedef enum {
OPTION_VARIABLE_REFRESH,
OPTION_USE_GAMMA_LUT,
OPTION_ASYNC_FLIP_SECONDARIES,
+ OPTION_TEARFREE,
} modesettingOpts;
typedef struct
@@ -241,6 +242,8 @@ Bool ms_do_pageflip(ScreenPtr screen,
ms_pageflip_abort_proc pageflip_abort,
const char *log_prefix);
+Bool ms_do_tearfree_flip(ScreenPtr screen, xf86CrtcPtr crtc);
+
#endif
int ms_flush_drm_events(ScreenPtr screen);
diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.c b/hw/xfree86/drivers/modesetting/drmmode_display.c
index 65e959379..8f8e4060a 100644
--- a/hw/xfree86/drivers/modesetting/drmmode_display.c
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.c
@@ -632,6 +632,7 @@ 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 @@ drmmode_crtc_get_fb_id(xf86CrtcPtr crtc, uint32_t *fb_id, int *x, int *y)
*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_crtc_set_mode(xf86CrtcPtr crtc, Bool test_only)
drmmode_ConvertToKMode(crtc->scrn, &kmode, &crtc->mode);
ret = drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
fb_id, x, y, output_ids, output_count, &kmode);
+ if (!ret && !ms->atomic_modeset) {
+ drmmode_crtc->src_x = x;
+ drmmode_crtc->src_y = y;
+ }
drmmode_set_ctm(crtc, ctm);
@@ -951,6 +960,26 @@ drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, int x, int y,
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);
}
@@ -1549,6 +1578,90 @@ drmmode_copy_fb(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
#endif
}
+void
+drmmode_copy_damage(xf86CrtcPtr crtc, PixmapPtr dst, RegionPtr dmg, Bool empty)
+{
+#ifdef GLAMOR_HAS_GBM
+ ScreenPtr pScreen = xf86ScrnToScreen(crtc->scrn);
+ DrawableRec *src;
+
+ /* Copy the screen's pixmap into the destination pixmap */
+ if (crtc->rotatedPixmap) {
+ src = &crtc->rotatedPixmap->drawable;
+ xf86RotateCrtcRedisplay(crtc, dst, src, dmg, FALSE);
+ } else {
+ src = &pScreen->GetScreenPixmap(pScreen)->drawable;
+ PixmapDirtyCopyArea(dst, src, 0, 0, -crtc->x, -crtc->y, dmg);
+ }
+
+ /* Reset the damages if requested */
+ if (empty)
+ RegionEmpty(dmg);
+
+ /* Wait until the GC operations finish */
+ modesettingPTR(crtc->scrn)->glamor.finish(pScreen);
+#endif
+}
+
+static void
+drmmode_shadow_fb_destroy(xf86CrtcPtr crtc, PixmapPtr pixmap,
+ void *data, drmmode_bo *bo, uint32_t *fb_id);
+static void
+drmmode_destroy_tearfree_shadow(xf86CrtcPtr crtc)
+{
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
+ int i;
+
+ if (trf->flip_seq)
+ ms_drm_abort_seq(crtc->scrn, trf->flip_seq);
+
+ for (i = 0; i < ARRAY_SIZE(trf->buf); i++) {
+ if (trf->buf[i].px) {
+ drmmode_shadow_fb_destroy(crtc, trf->buf[i].px, (void *)(long)1,
+ &trf->buf[i].bo, &trf->buf[i].fb_id);
+ trf->buf[i].px = NULL;
+ RegionUninit(&trf->buf[i].dmg);
+ }
+ }
+}
+
+static PixmapPtr
+drmmode_shadow_fb_create(xf86CrtcPtr crtc, void *data, int width, int height,
+ drmmode_bo *bo, uint32_t *fb_id);
+static Bool
+drmmode_create_tearfree_shadow(xf86CrtcPtr crtc)
+{
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ drmmode_ptr drmmode = drmmode_crtc->drmmode;
+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
+ uint32_t w = crtc->mode.HDisplay, h = crtc->mode.VDisplay;
+ int i;
+
+ if (!drmmode->tearfree_enable)
+ return TRUE;
+
+ /* Destroy the old mode's buffers and make new ones */
+ drmmode_destroy_tearfree_shadow(crtc);
+ for (i = 0; i < ARRAY_SIZE(trf->buf); i++) {
+ trf->buf[i].px = drmmode_shadow_fb_create(crtc, NULL, w, h,
+ &trf->buf[i].bo,
+ &trf->buf[i].fb_id);
+ if (!trf->buf[i].px) {
+ drmmode_destroy_tearfree_shadow(crtc);
+ xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
+ "shadow creation failed for TearFree buf%d\n", i);
+ return FALSE;
+ }
+ RegionInit(&trf->buf[i].dmg, &crtc->bounds, 0);
+ }
+
+ /* Initialize the front buffer with the current scanout */
+ drmmode_copy_damage(crtc, trf->buf[trf->back_idx ^ 1].px,
+ &trf->buf[trf->back_idx ^ 1].dmg, TRUE);
+ return TRUE;
+}
+
static Bool
drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
Rotation rotation, int x, int y)
@@ -1582,6 +1695,10 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
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,
@@ -1627,6 +1744,7 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
crtc->y = saved_y;
crtc->rotation = saved_rotation;
crtc->mode = saved_mode;
+ drmmode_create_tearfree_shadow(crtc);
} else
crtc->active = TRUE;
@@ -4274,6 +4392,7 @@ drmmode_free_bos(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
dumb_bo_destroy(drmmode->fd, drmmode_crtc->cursor_bo);
+ drmmode_destroy_tearfree_shadow(crtc);
}
}
diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.h b/hw/xfree86/drivers/modesetting/drmmode_display.h
index d6ad15501..145cb8cc7 100644
--- a/hw/xfree86/drivers/modesetting/drmmode_display.h
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.h
@@ -135,6 +135,7 @@ typedef struct {
Bool async_flip_secondaries;
Bool dri2_enable;
Bool present_enable;
+ Bool tearfree_enable;
uint32_t vrr_prop_id;
Bool use_ctm;
@@ -167,6 +168,19 @@ typedef struct {
} drmmode_format_rec, *drmmode_format_ptr;
typedef struct {
+ drmmode_bo bo;
+ uint32_t fb_id;
+ PixmapPtr px;
+ RegionRec dmg;
+} drmmode_shadow_fb_rec, *drmmode_shadow_fb_ptr;
+
+typedef struct {
+ drmmode_shadow_fb_rec buf[2];
+ uint32_t back_idx;
+ uint32_t flip_seq;
+} drmmode_tearfree_rec, *drmmode_tearfree_ptr;
+
+typedef struct {
drmmode_ptr drmmode;
drmModeCrtcPtr mode_crtc;
uint32_t vblank_pipe;
@@ -184,11 +198,14 @@ typedef struct {
drmmode_bo rotate_bo;
unsigned rotate_fb_id;
+ drmmode_tearfree_rec tearfree;
PixmapPtr prime_pixmap;
PixmapPtr prime_pixmap_back;
unsigned prime_pixmap_x;
+ int src_x, src_y;
+
/**
* @{ MSC (vblank count) handling for the PRESENT extension.
*
@@ -310,6 +327,8 @@ void drmmode_get_default_bpp(ScrnInfoPtr pScrn, drmmode_ptr drmmmode,
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);
diff --git a/hw/xfree86/drivers/modesetting/modesetting.man b/hw/xfree86/drivers/modesetting/modesetting.man
index 71790011e..9e40b04f3 100644
--- a/hw/xfree86/drivers/modesetting/modesetting.man
+++ b/hw/xfree86/drivers/modesetting/modesetting.man
@@ -109,6 +109,17 @@ When enabled, this option allows the driver to use gamma ramps with more
entries, if supported by the kernel. By default, GAMMA_LUT will be used for
kms drivers which are known to be safe for use of GAMMA_LUT.
.TP
+.BI "Option \*qTearFree\*q \*q" boolean \*q
+Enable tearing prevention using the hardware page flipping mechanism.
+It allocates two extra scanout buffers for each CRTC and utilizes damage
+tracking to minimize buffer copying and skip unnecessary flips when the
+screen's contents have not changed. It works on transformed screens too, such
+as rotated and scaled CRTCs. When PageFlip is enabled, fullscreen DRI
+applications will still have the discretion to not use tearing prevention.
+.br
+The default is
+.B off.
+.TP
.SH "SEE ALSO"
@xservername@(@appmansuffix@), @xconfigfile@(@filemansuffix@), Xserver(@appmansuffix@),
X(@miscmansuffix@)
diff --git a/hw/xfree86/drivers/modesetting/pageflip.c b/hw/xfree86/drivers/modesetting/pageflip.c
index a51a10a2c..8d57047ef 100644
--- a/hw/xfree86/drivers/modesetting/pageflip.c
+++ b/hw/xfree86/drivers/modesetting/pageflip.c
@@ -35,8 +35,8 @@
* Returns a negative value on error, 0 if there was nothing to process,
* or 1 if we handled any events.
*/
-int
-ms_flush_drm_events(ScreenPtr screen)
+static int
+ms_flush_drm_events_timeout(ScreenPtr screen, int timeout)
{
ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
modesettingPtr ms = modesettingPTR(scrn);
@@ -45,7 +45,7 @@ ms_flush_drm_events(ScreenPtr screen)
int r;
do {
- r = xserver_poll(&p, 1, 0);
+ r = xserver_poll(&p, 1, timeout);
} while (r == -1 && (errno == EINTR || errno == EAGAIN));
/* If there was an error, r will be < 0. Return that. If there was
@@ -63,6 +63,12 @@ ms_flush_drm_events(ScreenPtr screen)
return 1;
}
+int
+ms_flush_drm_events(ScreenPtr screen)
+{
+ return ms_flush_drm_events_timeout(screen, 0);
+}
+
#ifdef GLAMOR_HAS_GBM
/*
@@ -163,14 +169,22 @@ static Bool
do_queue_flip_on_crtc(ScreenPtr screen, xf86CrtcPtr crtc, uint32_t flags,
uint32_t seq, uint32_t fb_id, int x, int y)
{
+ 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) {
- ms_drm_abort_seq(crtc->scrn, seq);
- return TRUE;
+ /* 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. */
@@ -467,4 +481,50 @@ error_out:
#endif /* GLAMOR_HAS_GBM */
}
+static void
+ms_tearfree_flip_abort(void *data)
+{
+ drmmode_tearfree_ptr trf = data;
+
+ trf->flip_seq = 0;
+}
+
+static void
+ms_tearfree_flip_handler(uint64_t msc, uint64_t usec, void *data)
+{
+ drmmode_tearfree_ptr trf = data;
+
+ /* Swap the buffers and complete the flip */
+ trf->back_idx ^= 1;
+ trf->flip_seq = 0;
+}
+
+Bool
+ms_do_tearfree_flip(ScreenPtr screen, xf86CrtcPtr crtc)
+{
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
+ uint32_t idx = trf->back_idx, seq;
+
+ seq = ms_drm_queue_alloc(crtc, trf, ms_tearfree_flip_handler,
+ ms_tearfree_flip_abort);
+ if (!seq)
+ goto no_flip;
+
+ /* Copy the damage to the back buffer and then flip it at the vblank */
+ drmmode_copy_damage(crtc, trf->buf[idx].px, &trf->buf[idx].dmg, TRUE);
+ if (do_queue_flip_on_crtc(screen, crtc, DRM_MODE_PAGE_FLIP_EVENT,
+ seq, trf->buf[idx].fb_id, 0, 0))
+ goto no_flip;
+
+ trf->flip_seq = seq;
+ return FALSE;
+
+no_flip:
+ xf86DrvMsg(crtc->scrn->scrnIndex, X_WARNING,
+ "TearFree flip failed, rendering frame without TearFree\n");
+ drmmode_copy_damage(crtc, trf->buf[idx ^ 1].px,
+ &trf->buf[idx ^ 1].dmg, FALSE);
+ return TRUE;
+}
#endif
diff --git a/hw/xfree86/drivers/modesetting/present.c b/hw/xfree86/drivers/modesetting/present.c
index c3266d871..642f7baaf 100644
--- a/hw/xfree86/drivers/modesetting/present.c
+++ b/hw/xfree86/drivers/modesetting/present.c
@@ -318,14 +318,32 @@ ms_present_check_flip(RRCrtcPtr crtc,
modesettingPtr ms = modesettingPTR(scrn);
if (ms->drmmode.sprites_visible > 0)
- return FALSE;
+ goto no_flip;
if(!ms_present_check_unflip(crtc, window, pixmap, sync_flip, reason))
- return FALSE;
+ goto no_flip;
ms->flip_window = window;
return TRUE;
+
+no_flip:
+ /* Export some info about TearFree if Present can't flip anyway */
+ if (reason && ms->drmmode.tearfree_enable) {
+ xf86CrtcPtr xf86_crtc = crtc->devPrivate;
+ drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
+ drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
+
+ if (trf->buf[0].px) {
+ if (trf->flip_seq)
+ /* The driver has a TearFree flip pending */
+ *reason = PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING;
+ else
+ /* The driver uses TearFree flips and there's no flip pending */
+ *reason = PRESENT_FLIP_REASON_DRIVER_TEARFREE;
+ }
+ }
+ return FALSE;
}
/*