summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSultan Alsawaf <sultan@kerneltoast.com>2023-01-22 23:31:52 -0800
committerSultan Alsawaf <sultan@kerneltoast.com>2023-02-28 22:43:37 -0800
commit1fab978a9585cc7fea483583bc1c76e9e48e4f35 (patch)
treed4275778b04e724ae3abe9a51ef105df53955e2b
parentd4bd39f1a5db2c5ee5ff251f1982bbe6aa5c0f7f (diff)
downloadxserver-1fab978a9585cc7fea483583bc1c76e9e48e4f35.tar.gz
present: Fix inaccurate PresentCompleteNotify timing for TearFree
The timing of PresentCompleteNotify events is inaccurate when a driver uses TearFree because there's no way to know exactly when a presentation will appear on the display without receiving a notification directly from the driver indicating that the TearFree flip containing a presentation's pixmap is actually visible on the display. To fix the inaccurate PresentCompleteNotify timing, make use of the new assumption that drivers which export TearFree permit a NULL pixmap to be passed to their flip callback in order to make a presentation track the exact TearFree flip responsible for rendering it onto the display. Signed-off-by: Sultan Alsawaf <sultan@kerneltoast.com> Acked-by: Martin Roukala <martin.roukala@mupuf.org>
-rw-r--r--present/present_scmd.c58
1 files changed, 38 insertions, 20 deletions
diff --git a/present/present_scmd.c b/present/present_scmd.c
index 59feb001b..d378f0016 100644
--- a/present/present_scmd.c
+++ b/present/present_scmd.c
@@ -646,30 +646,48 @@ 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.
+ /* 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) {
+ if (!vblank->queued &&
+ vblank->reason >= PRESENT_FLIP_REASON_DRIVER_TEARFREE) {
uint64_t completion_msc = crtc_msc + 1;
- switch (vblank->reason) {
- case PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING:
- if (vblank->exec_msc < crtc_msc)
+ /* 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++;
- 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;
+
+ /* 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;
}
}