From: David Herrmann Date: Sat, 20 Sep 2014 09:12:44 +0000 (+0200) Subject: terminal: grdev: schedule virtual frame events if hw doesn't support it X-Git-Tag: v217~486 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=commitdiff_plain;h=7b12a45b2dc6993e3f31642df2cc9b528294da40 terminal: grdev: schedule virtual frame events if hw doesn't support it Whenever we cannot use hardware frame events, we now schedule a virtual frame event to make sure applications don't have to do this. Usually, applications render only on data changes, but we can further reduce render-time by also limiting rendering to vsyncs. --- diff --git a/src/libsystemd-terminal/grdev-drm.c b/src/libsystemd-terminal/grdev-drm.c index 00aac292d..3936a029f 100644 --- a/src/libsystemd-terminal/grdev-drm.c +++ b/src/libsystemd-terminal/grdev-drm.c @@ -346,6 +346,8 @@ static bool grdrm_modes_compatible(const struct drm_mode_modeinfo *a, const stru return false; if (a->vdisplay != b->vdisplay) return false; + if (a->vrefresh != b->vrefresh) + return false; return true; } @@ -1038,7 +1040,8 @@ static void grdrm_crtc_expose(grdrm_crtc *crtc) { pipe = crtc->pipe; if (pipe) { if (pipe->base.width != crtc->set.mode.hdisplay || - pipe->base.height != crtc->set.mode.vdisplay) { + pipe->base.height != crtc->set.mode.vdisplay || + pipe->base.vrefresh != crtc->set.mode.vrefresh) { grdev_pipe_free(&pipe->base); crtc->pipe = NULL; pipe = NULL; @@ -1127,6 +1130,12 @@ static void grdrm_crtc_commit_deep(grdrm_crtc *crtc, grdev_fb **slot) { pipe->base.flipping = false; pipe->base.flip = false; + /* We cannot schedule dummy page-flips on pipes, hence, the + * application would have to schedule their own frame-timers. + * To avoid duplicating that everywhere, we schedule our own + * timer and raise a fake FRAME event when it fires. */ + grdev_pipe_schedule(&pipe->base, 1); + if (!pipe->base.back) { for (i = 0; i < pipe->base.max_fbs; ++i) { if (!pipe->base.fbs[i]) @@ -1189,6 +1198,11 @@ static int grdrm_crtc_commit_flip(grdrm_crtc *crtc, grdev_fb **slot) { fb->flipid = cnt; *slot = NULL; + /* Raise fake FRAME event if it takes longer than 2 + * frames to receive the pageflip event. We assume the + * queue ran over or some other error happened. */ + grdev_pipe_schedule(&pipe->base, 2); + if (!pipe->base.back) { for (i = 0; i < pipe->base.max_fbs; ++i) { if (!pipe->base.fbs[i]) @@ -1501,6 +1515,7 @@ static int grdrm_pipe_new(grdrm_pipe **out, grdrm_crtc *crtc, struct drm_mode_mo pipe->crtc = crtc; pipe->base.width = mode->hdisplay; pipe->base.height = mode->vdisplay; + pipe->base.vrefresh = mode->vrefresh ? : 25; grdrm_pipe_name(name, crtc); r = grdev_pipe_add(&pipe->base, name, n_fbs); diff --git a/src/libsystemd-terminal/grdev-internal.h b/src/libsystemd-terminal/grdev-internal.h index 96830a714..f5915b16e 100644 --- a/src/libsystemd-terminal/grdev-internal.h +++ b/src/libsystemd-terminal/grdev-internal.h @@ -142,9 +142,11 @@ struct grdev_pipe { grdev_tile *tile; grdev_display_cache *cache; + sd_event_source *vsync_src; uint32_t width; uint32_t height; + uint32_t vrefresh; size_t max_fbs; grdev_fb *front; @@ -171,6 +173,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_pipe*, grdev_pipe_free); void grdev_pipe_ready(grdev_pipe *pipe, bool running); void grdev_pipe_frame(grdev_pipe *pipe); +void grdev_pipe_schedule(grdev_pipe *pipe, uint64_t frames); /* * Cards diff --git a/src/libsystemd-terminal/grdev.c b/src/libsystemd-terminal/grdev.c index 397da1b20..43d0c7c9b 100644 --- a/src/libsystemd-terminal/grdev.c +++ b/src/libsystemd-terminal/grdev.c @@ -574,6 +574,13 @@ grdev_pipe *grdev_find_pipe(grdev_card *card, const char *name) { return hashmap_get(card->pipe_map, name); } +static int pipe_vsync_fn(sd_event_source *src, uint64_t usec, void *userdata) { + grdev_pipe *pipe = userdata; + + grdev_pipe_frame(pipe); + return 0; +} + int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) { int r; @@ -585,6 +592,7 @@ int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) { assert_return(!pipe->cache, -EINVAL); assert_return(pipe->width > 0, -EINVAL); assert_return(pipe->height > 0, -EINVAL); + assert_return(pipe->vrefresh > 0, -EINVAL); assert_return(!pipe->enabled, -EINVAL); assert_return(!pipe->running, -EINVAL); assert_return(name, -EINVAL); @@ -605,6 +613,20 @@ int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) { if (r < 0) return r; + r = sd_event_add_time(pipe->card->session->context->event, + &pipe->vsync_src, + CLOCK_MONOTONIC, + 0, + 10 * USEC_PER_MSEC, + pipe_vsync_fn, + pipe); + if (r < 0) + return r; + + r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF); + if (r < 0) + return r; + r = hashmap_put(pipe->card->pipe_map, pipe->name, pipe); if (r < 0) return r; @@ -633,6 +655,7 @@ grdev_pipe *grdev_pipe_free(grdev_pipe *pipe) { tmp = *pipe; pipe->vtable->free(pipe); + sd_event_source_unref(tmp.vsync_src); grdev_tile_free(tmp.tile); card_modified(tmp.card); free(tmp.fbs); @@ -676,17 +699,15 @@ void grdev_pipe_ready(grdev_pipe *pipe, bool running) { pipe->running = running; /* runtime events for unused pipes are not interesting */ - if (pipe->cache) { + if (pipe->cache && pipe->enabled) { grdev_display *display = pipe->tile->display; assert(display); - if (running) { - if (pipe->enabled) - session_frame(display->session, display); - } else { + if (running) + session_frame(display->session, display); + else pipe->cache->incomplete = true; - } } } @@ -696,14 +717,44 @@ void grdev_pipe_frame(grdev_pipe *pipe) { assert(pipe); /* if pipe is unused, ignore any frame events */ - if (!pipe->cache) + if (!pipe->cache || !pipe->enabled) return; display = pipe->tile->display; assert(display); - if (pipe->enabled) - session_frame(display->session, display); + grdev_pipe_schedule(pipe, 0); + session_frame(display->session, display); +} + +void grdev_pipe_schedule(grdev_pipe *pipe, uint64_t frames) { + int r; + uint64_t ts; + + if (!frames) { + sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF); + return; + } + + r = sd_event_now(pipe->card->session->context->event, CLOCK_MONOTONIC, &ts); + if (r < 0) + goto error; + + ts += frames * USEC_PER_MSEC * 1000ULL / pipe->vrefresh; + + r = sd_event_source_set_time(pipe->vsync_src, ts); + if (r < 0) + goto error; + + r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_ONESHOT); + if (r < 0) + goto error; + + return; + +error: + log_debug("grdev: %s/%s/%s: cannot schedule vsync timer: %s", + pipe->card->session->name, pipe->card->name, pipe->name, strerror(-r)); } /*