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.
This commit is contained in:
parent
3ec19e5d91
commit
7b12a45b2d
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in a new issue