present: add awareness for drivers with TearFree

When a driver uses TearFree to flip all frames synchronized to the vblank
interval, Present has no idea and still assumes that each presentation
occurs immediately upon copying its damages to the primary scanout. This
faulty assumption breaks presentation timestamping, potentially leading to
A/V de-synchronization and dropped frames.

Present needs to have some awareness of a driver using TearFree so that it
can know when each presentation actually becomes visible on the screen.

Teach Present about drivers using TearFree by expanding PresentFlipReason
to allow drivers to export some contextual info about TearFree when Present
cannot page-flip directly anyway.

PRESENT_FLIP_REASON_DRIVER_TEARFREE indicates that a driver has TearFree
enabled but doesn't have a TearFree flip currently pending.

PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING indicates that a driver has a
TearFree flip currently pending.

Signed-off-by: Sultan Alsawaf <sultan@kerneltoast.com>
This commit is contained in:
Sultan Alsawaf 2022-12-10 16:09:26 -08:00
parent 5f5690b804
commit 9108a2bf55
2 changed files with 71 additions and 15 deletions

View File

@ -29,7 +29,9 @@
typedef enum {
PRESENT_FLIP_REASON_UNKNOWN,
PRESENT_FLIP_REASON_BUFFER_FORMAT
PRESENT_FLIP_REASON_BUFFER_FORMAT,
PRESENT_FLIP_REASON_DRIVER_TEARFREE,
PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING
} PresentFlipReason;
typedef struct present_vblank present_vblank_rec, *present_vblank_ptr;

View File

@ -71,6 +71,7 @@ present_check_flip(RRCrtcPtr crtc,
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 @@ present_check_flip(RRCrtcPtr crtc,
if (!screen_priv->info->flip)
return FALSE;
/* Ask the driver for permission. Do this now to see if there's TearFree. */
if (screen_priv->info->version >= 1 && screen_priv->info->check_flip2) {
if (!(*screen_priv->info->check_flip2) (crtc, window, pixmap, sync_flip, &tmp_reason)) {
DebugPresent(("\td %08" PRIx32 " -> %08" PRIx32 "\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
/* It's fine to return now unless the page flip failure reason is
* PRESENT_FLIP_REASON_BUFFER_FORMAT; we must only output that
* reason if all the other checks pass.
*/
if (!reason || tmp_reason != PRESENT_FLIP_REASON_BUFFER_FORMAT) {
if (reason)
*reason = tmp_reason;
return FALSE;
}
}
} else if (screen_priv->info->check_flip) {
if (!(*screen_priv->info->check_flip) (crtc, window, pixmap, sync_flip)) {
DebugPresent(("\td %08" PRIx32 " -> %08" PRIx32 "\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
return FALSE;
}
}
/* Make sure the window hasn't been redirected with Composite */
window_pixmap = screen->GetWindowPixmap(window);
if (window_pixmap != screen->GetScreenPixmap(screen) &&
@ -123,17 +145,10 @@ present_check_flip(RRCrtcPtr crtc,
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 @@ present_check_flip_window (WindowPtr window)
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;
}
@ -537,6 +554,7 @@ present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
WindowPtr window = vblank->window;
ScreenPtr screen = window->drawable.pScreen;
present_screen_priv_ptr screen_priv = present_screen_priv(screen);
uint64_t completion_msc;
if (vblank && vblank->crtc) {
screen_priv=present_screen_priv(vblank->crtc->pScreen);
}
@ -560,7 +578,9 @@ present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
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 +647,30 @@ present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
present_execute_copy(vblank, crtc_msc);
/* The presentation will be visible at the next vblank with TearFree, so
* the PresentComplete notification needs to be sent at the next vblank.
* If TearFree is already flipping then the presentation will be visible
* at the *next* next vblank.
*/
completion_msc = crtc_msc + 1;
switch (vblank->reason) {
case PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING:
if (vblank->exec_msc < crtc_msc)
completion_msc++;
case PRESENT_FLIP_REASON_DRIVER_TEARFREE:
if (Success == screen_priv->queue_vblank(screen,
window,
vblank->crtc,
vblank->event_id,
completion_msc)) {
/* Ensure present_execute_post() runs at the next MSC */
vblank->exec_msc = vblank->target_msc;
vblank->queued = TRUE;
}
default:
break;
}
if (vblank->queued) {
xorg_list_add(&vblank->event_queue, &present_exec_queue);
xorg_list_append(&vblank->window_list,
@ -739,6 +783,11 @@ present_scmd_pixmap(WindowPtr window,
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 +816,12 @@ present_scmd_pixmap(WindowPtr window,
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);