summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMario Kleiner <mario.kleiner.de@gmail.com>2015-06-21 21:25:14 +0200
committerPekka Paalanen <pekka.paalanen@collabora.co.uk>2015-07-16 14:08:26 +0300
commitf507ec3587b00057bf97d11b7e8e57d1972303d1 (patch)
tree1d01fe3fad81d8cdef6a8e6853d65c412732e693
parentd7894d052a8e8944d20e1f751f3275382e48cc12 (diff)
compositor-drm: Allow instant start of repaint loop. (v4)
drm_output_start_repaint_loop() incurred a delay of one refresh cycle by using a no-op page-flip to get an accurate vblank timestamp as reference. This causes unwanted lag whenever Weston exited its repaint loop, e.g., whenever an application wants to repaint with less than full video refresh rate but still minimum lag. Try to use the drmWaitVblank ioctl to get a proper timestamp instantaneously without lag. If that does not work, fall back to the old method of idle page-flip. This optimization will work on any drm/kms driver which supports high precision vblank timestamping. As of Linux 4.0 these would be intel, radeon and nouveau on all their supported gpu's. On kms drivers without instant high precision timestamping support, the kernel is supposed to return a timestamp of zero when calling drmWaitVblank() to query the current vblank count and time iff vblank irqs are currently disabled, because the only way to get a valid timestamp on such kms drivers is to enable vblank interrupts and then wait a bit for the next vblank irq to take a new valid timestamp. The caller is supposed to poll until at next vblank irq it gets a valid non-zero timestamp if it needs a timestamp. This zero-timestamp signalling works up to Linux 3.17, but got broken due to a regression in Linux 3.18 and later. On Linux 3.18+ with kms drivers that don't have high precision timestamping, the kernel erroneously returns a stale timestamp from an earlier vblank, ie. the vblank count and timestamp are mismatched. A patch is under way to fix this, but to deal with broken kernels, we also check non-zero timestamps if they are more than one refresh duration in the past, as this indicates a stale/invalid timestamp, so we need to take the page-flip fallback for restarting the repaint loop. v2: Implement review suggestions by Pekka Paalanen, especially extend the commit message to describe when and why the instant restart won't work due to missing Linux kernel functionality or a Linux kernel regression. Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com> Reviewed-by: Daniel Stone <daniels@collabora.com> v3: Fix timespec_to_nsec() which was computing picoseconds, use the new timespec-util.h helpers. v4: Rebased to master, split long lines. Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk>
-rw-r--r--Makefile.am1
-rw-r--r--src/compositor-drm.c43
2 files changed, 43 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am
index eb3e8673..f71587f5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -254,6 +254,7 @@ drm_backend_la_SOURCES = \
src/compositor-drm.c \
$(INPUT_BACKEND_SOURCES) \
shared/helpers.h \
+ shared/timespec-util.h \
src/libbacklight.c \
src/libbacklight.h
diff --git a/src/compositor-drm.c b/src/compositor-drm.c
index 85eb783a..3b91fe4f 100644
--- a/src/compositor-drm.c
+++ b/src/compositor-drm.c
@@ -47,6 +47,7 @@
#include <libudev.h>
#include "shared/helpers.h"
+#include "shared/timespec-util.h"
#include "libbacklight.h"
#include "compositor.h"
#include "gl-renderer.h"
@@ -230,6 +231,9 @@ static const char default_seat[] = "seat0";
static void
drm_output_set_cursor(struct drm_output *output);
+static void
+drm_output_update_msc(struct drm_output *output, unsigned int seq);
+
static int
drm_sprite_crtc_supported(struct drm_output *output, uint32_t supported)
{
@@ -732,7 +736,15 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
struct drm_backend *backend = (struct drm_backend *)
output_base->compositor->backend;
uint32_t fb_id;
- struct timespec ts;
+ struct timespec ts, tnow;
+ struct timespec vbl2now;
+ int64_t refresh_nsec;
+ int ret;
+ drmVBlank vbl = {
+ .request.type = DRM_VBLANK_RELATIVE,
+ .request.sequence = 0,
+ .request.signal = 0,
+ };
if (output->destroy_pending)
return;
@@ -742,6 +754,35 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
goto finish_frame;
}
+ /* Try to get current msc and timestamp via instant query */
+ vbl.request.type |= drm_waitvblank_pipe(output);
+ ret = drmWaitVBlank(backend->drm.fd, &vbl);
+
+ /* Error ret or zero timestamp means failure to get valid timestamp */
+ if ((ret == 0) && (vbl.reply.tval_sec > 0 || vbl.reply.tval_usec > 0)) {
+ 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.
+ */
+ weston_compositor_read_presentation_clock(backend->compositor,
+ &tnow);
+ timespec_sub(&vbl2now, &tnow, &ts);
+ refresh_nsec =
+ millihz_to_nsec(output->base.current_mode->refresh);
+ if (timespec_to_nsec(&vbl2now) < refresh_nsec) {
+ drm_output_update_msc(output, vbl.reply.sequence);
+ weston_output_finish_frame(output_base, &ts,
+ PRESENTATION_FEEDBACK_INVALID);
+ return;
+ }
+ }
+
+ /* Immediate query didn't provide valid timestamp.
+ * Use pageflip fallback.
+ */
fb_id = output->current->fb_id;
if (drmModePageFlip(backend->drm.fd, output->crtc_id, fb_id,