diff options
author | Derek Foreman <derek.foreman@collabora.com> | 2025-04-14 10:48:04 -0500 |
---|---|---|
committer | Daniel Stone <daniels@collabora.com> | 2025-06-27 14:51:49 +0100 |
commit | 5a48cedc7b8421d8342dd6a943705955217b0fd1 (patch) | |
tree | a233bb5db506f22c73ed0c0f0c645d4c3f64d171 | |
parent | 2d34d3289386831147c78c866b0808cdac9b19b0 (diff) |
There was an old kernel bug where the drm driver could give us a stale
timestamp. We worked around this by performing a page flip at the
start of the repaint loop.
Now that we support VRR, when we don't render our refresh rate drops to the
display's lowest possible refresh rate. This leaves us with a race where
the repaint loop could be started between the mode's refresh rate and the
lowest possible VRR rate - which is indistinguishable from a stale
timestamp. In this case we'd perform a needless page flip and potentially
miss an opportunity to render.
The kernel bug was introduced in v3.16 in commit 844b03f27739135 and later
fixed in v4.1 in commit fdb68e09bbb1c98
Since the vrr_capable property was introduced in v4.20 in commit
ba1b0f6c73d4ea1, any kernel that supports VRR is new enough not to give us
stale timestamps, so we don't have to miss these opportunities.
Signed-off-by: Derek Foreman <derek.foreman@collabora.com>
-rw-r--r-- | libweston/backend-drm/drm.c | 44 |
1 files changed, 33 insertions, 11 deletions
diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 712eb61d..14fc0c16 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -916,8 +916,6 @@ drm_output_start_repaint_loop(struct weston_output *output_base) struct drm_backend *backend = device->backend; struct weston_compositor *compositor = backend->compositor; struct timespec ts, tnow; - struct timespec vbl2now; - int64_t refresh_nsec; uint32_t flags = WP_PRESENTATION_FEEDBACK_INVALID; int ret; drmVBlank vbl = { @@ -958,19 +956,43 @@ drm_output_start_repaint_loop(struct weston_output *output_base) /* Error ret or zero timestamp means failure to get valid timestamp */ if ((ret == 0) && (vbl.reply.tval_sec > 0 || vbl.reply.tval_usec > 0)) { + bool stale_timestamp = false; + ts.tv_sec = vbl.reply.tval_sec; ts.tv_nsec = vbl.reply.tval_usec * 1000; - /* Valid timestamp for most recent vblank - not stale? - * Stale ts could happen on Linux 3.17+, so make sure it - * is not older than 1 refresh duration since now. + /* Between Linux 3.16 and Linux 4.1 there was a bug that + * could result in a stale timestamp being returned. We + * can catch that by checking if the timestamp we have + * is older than 1 refresh duration since now, and use a + * page flip to start the repaint loop. + * + * However, if we're using VRR, the time since the last + * vblank could be the display's longest possible frame + * time, which is longer than rfresh_nsec. That looks + * exactly like the bug we need to work around here, and + * the page flip workaround would result in an unnecessary + * delay. + * + * We know that the kernel bug was fixed in v4.1, before the + * much later introduction of the vrr_capable property we + * use to detect VRR. So we only need the bug fix if we don't + * have VRR. */ - weston_compositor_read_presentation_clock(compositor, - &tnow); - timespec_sub(&vbl2now, &tnow, &ts); - refresh_nsec = - millihz_to_nsec(output->base.current_mode->refresh); - if (timespec_to_nsec(&vbl2now) < refresh_nsec) { + if (output->base.vrr_mode == WESTON_VRR_MODE_NONE) { + struct timespec vbl2now; + int64_t refresh_nsec; + + weston_compositor_read_presentation_clock(compositor, + &tnow); + timespec_sub(&vbl2now, &tnow, &ts); + refresh_nsec = + millihz_to_nsec(output->base.current_mode->refresh); + if (timespec_to_nsec(&vbl2now) > refresh_nsec) + stale_timestamp = true; + } + + if (!stale_timestamp) { drm_output_update_msc(output, vbl.reply.sequence); weston_output_finish_frame(output_base, &ts, flags); return 0; |