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:
parent
5f5690b804
commit
9108a2bf55
|
@ -29,7 +29,9 @@
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
PRESENT_FLIP_REASON_UNKNOWN,
|
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;
|
} PresentFlipReason;
|
||||||
|
|
||||||
typedef struct present_vblank present_vblank_rec, *present_vblank_ptr;
|
typedef struct present_vblank present_vblank_rec, *present_vblank_ptr;
|
||||||
|
|
|
@ -71,6 +71,7 @@ present_check_flip(RRCrtcPtr crtc,
|
||||||
PixmapPtr window_pixmap;
|
PixmapPtr window_pixmap;
|
||||||
WindowPtr root = screen->root;
|
WindowPtr root = screen->root;
|
||||||
present_screen_priv_ptr screen_priv = present_screen_priv(screen);
|
present_screen_priv_ptr screen_priv = present_screen_priv(screen);
|
||||||
|
PresentFlipReason tmp_reason = PRESENT_FLIP_REASON_UNKNOWN;
|
||||||
|
|
||||||
if (crtc) {
|
if (crtc) {
|
||||||
screen_priv = present_screen_priv(crtc->pScreen);
|
screen_priv = present_screen_priv(crtc->pScreen);
|
||||||
|
@ -91,6 +92,27 @@ present_check_flip(RRCrtcPtr crtc,
|
||||||
if (!screen_priv->info->flip)
|
if (!screen_priv->info->flip)
|
||||||
return FALSE;
|
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 */
|
/* Make sure the window hasn't been redirected with Composite */
|
||||||
window_pixmap = screen->GetWindowPixmap(window);
|
window_pixmap = screen->GetWindowPixmap(window);
|
||||||
if (window_pixmap != screen->GetScreenPixmap(screen) &&
|
if (window_pixmap != screen->GetScreenPixmap(screen) &&
|
||||||
|
@ -123,18 +145,11 @@ present_check_flip(RRCrtcPtr crtc,
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ask the driver for permission */
|
if (tmp_reason == PRESENT_FLIP_REASON_BUFFER_FORMAT) {
|
||||||
if (screen_priv->info->version >= 1 && screen_priv->info->check_flip2) {
|
if (reason)
|
||||||
if (!(*screen_priv->info->check_flip2) (crtc, window, pixmap, sync_flip, reason)) {
|
*reason = tmp_reason;
|
||||||
DebugPresent(("\td %08" PRIx32 " -> %08" PRIx32 "\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
|
|
||||||
return FALSE;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
@ -456,6 +471,8 @@ present_check_flip_window (WindowPtr window)
|
||||||
xorg_list_for_each_entry(vblank, &window_priv->vblank, window_list) {
|
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)) {
|
if (vblank->queued && vblank->flip && !present_check_flip(vblank->crtc, window, vblank->pixmap, vblank->sync_flip, NULL, 0, 0, &reason)) {
|
||||||
vblank->flip = FALSE;
|
vblank->flip = FALSE;
|
||||||
|
/* Don't spuriously flag this as a TearFree presentation */
|
||||||
|
if (reason < PRESENT_FLIP_REASON_DRIVER_TEARFREE)
|
||||||
vblank->reason = reason;
|
vblank->reason = reason;
|
||||||
if (vblank->sync_flip)
|
if (vblank->sync_flip)
|
||||||
vblank->exec_msc = vblank->target_msc;
|
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;
|
WindowPtr window = vblank->window;
|
||||||
ScreenPtr screen = window->drawable.pScreen;
|
ScreenPtr screen = window->drawable.pScreen;
|
||||||
present_screen_priv_ptr screen_priv = present_screen_priv(screen);
|
present_screen_priv_ptr screen_priv = present_screen_priv(screen);
|
||||||
|
uint64_t completion_msc;
|
||||||
if (vblank && vblank->crtc) {
|
if (vblank && vblank->crtc) {
|
||||||
screen_priv=present_screen_priv(vblank->crtc->pScreen);
|
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);
|
xorg_list_del(&vblank->window_list);
|
||||||
vblank->queued = FALSE;
|
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) {
|
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);
|
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) {
|
if (vblank->queued) {
|
||||||
xorg_list_add(&vblank->event_queue, &present_exec_queue);
|
xorg_list_add(&vblank->event_queue, &present_exec_queue);
|
||||||
xorg_list_append(&vblank->window_list,
|
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)
|
if (vblank->crtc != target_crtc || vblank->target_msc != target_msc)
|
||||||
continue;
|
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);
|
present_vblank_scrap(vblank);
|
||||||
if (vblank->flip_ready)
|
if (vblank->flip_ready)
|
||||||
present_re_execute(vblank);
|
present_re_execute(vblank);
|
||||||
|
@ -767,7 +816,12 @@ present_scmd_pixmap(WindowPtr window,
|
||||||
|
|
||||||
vblank->event_id = ++present_scmd_event_id;
|
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--;
|
vblank->exec_msc--;
|
||||||
|
|
||||||
xorg_list_append(&vblank->event_queue, &present_exec_queue);
|
xorg_list_append(&vblank->event_queue, &present_exec_queue);
|
||||||
|
|
Loading…
Reference in New Issue