terminal: add --fps to limit framerate

Instead of redrawing on change, we now use a framerate-timer which
redraws the screen. This timer stays active for 1s after the last redraw
so we do not enable/disable the timer while the console is under heavy
work-load. This still needs to be benchmarked but it seems to work nicely.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
This commit is contained in:
David Herrmann 2012-08-21 17:56:34 +02:00
parent a05815eb71
commit 8398f4b0f9
3 changed files with 81 additions and 16 deletions

View File

@ -432,6 +432,7 @@ static void print_help()
"\t --fbdev [off] Use fbdev instead of DRM\n"
"\t --dumb [off] Use dumb DRM instead of hardware-\n"
"\t accelerated DRM devices\n"
"\t --fps [50] Limit frame-rate\n"
"\n"
"Input Device Options:\n"
"\t --xkb-layout <layout> [us] Set XkbLayout for input devices\n"
@ -523,6 +524,7 @@ struct conf_option options[] = {
CONF_OPTION_BOOL(0, "silent", NULL, &kmscon_conf.silent, false),
CONF_OPTION_BOOL(0, "fbdev", NULL, &kmscon_conf.use_fbdev, false),
CONF_OPTION_BOOL(0, "dumb", NULL, &kmscon_conf.dumb, false),
CONF_OPTION_UINT(0, "fps", NULL, &kmscon_conf.fps, 50),
CONF_OPTION_BOOL('s', "switchvt", NULL, &kmscon_conf.switchvt, false),
CONF_OPTION_BOOL('l', "login", aftercheck_login, &kmscon_conf.login, false),
CONF_OPTION_STRING('t', "term", NULL, &kmscon_conf.term, "vt220"),

View File

@ -79,6 +79,9 @@ struct kmscon_conf_t {
/* color palette */
char *palette;
/* frame-rate limit */
unsigned int fps;
};
extern struct kmscon_conf_t kmscon_conf;

View File

@ -67,7 +67,9 @@ struct kmscon_terminal {
unsigned int min_cols;
unsigned int min_rows;
bool redraw;
unsigned int fps;
unsigned int redraw;
struct ev_timer *redraw_timer;
struct kmscon_console *console;
struct kmscon_vte *vte;
struct kmscon_pty *pty;
@ -76,9 +78,8 @@ struct kmscon_terminal {
void *data;
};
static void draw_all(struct ev_eloop *eloop, void *unused, void *data)
static void redraw(struct kmscon_terminal *term)
{
struct kmscon_terminal *term = data;
struct uterm_screen *screen;
struct kmscon_dlist *iter;
struct screen *ent;
@ -86,9 +87,6 @@ static void draw_all(struct ev_eloop *eloop, void *unused, void *data)
if (!term->awake)
return;
ev_eloop_unregister_idle_cb(term->eloop, draw_all, term);
term->redraw = false;
kmscon_dlist_for_each(iter, &term->screens) {
ent = kmscon_dlist_entry(iter, struct screen, list);
screen = ent->screen;
@ -98,18 +96,47 @@ static void draw_all(struct ev_eloop *eloop, void *unused, void *data)
}
}
static void redraw_timer_event(struct ev_timer *timer, uint64_t num, void *data)
{
struct kmscon_terminal *term = data;
/* When a redraw is scheduled, the redraw-counter is set to the current
* frame-rate. If this counter is full, we know that data changed and we
* need to redraw the terminal. We then decrement the counter until it
* drops to zero. This guarantees that we stay active for 1s without
* a call to ev_timer_enable/disable() which required syscalls which can
* be quite slow.
* If a redraw is schedule in the meantime, the counter is reset to the
* framerate and we have to redraw the screen. If it drops to zero, that
* is, 1s after the last redraw, we disable the timer to stop consuming
* CPU-power.
* TODO: On _really_ slow machines we might want to avoid fps-limits
* and redraw on change. We also should check whether waking up for the
* fps-timeouts for 1s is really faster than calling
* ev_timer_enable/disable() all the time. */
if (term->redraw-- != term->fps) {
if (!term->redraw) {
ev_timer_disable(term->redraw_timer);
log_debug("disable-timer");
}
return;
}
log_debug("draw");
redraw(term);
}
static void schedule_redraw(struct kmscon_terminal *term)
{
int ret;
if (term->redraw || !term->awake)
if (!term->awake)
return;
ret = ev_eloop_register_idle_cb(term->eloop, draw_all, term);
if (ret)
log_warn("cannot schedule redraw");
else
term->redraw = true;
log_debug("schedule");
if (!term->redraw)
ev_timer_enable(term->redraw_timer);
if (term->redraw < term->fps)
term->redraw = term->fps;
}
/*
@ -354,6 +381,8 @@ int kmscon_terminal_new(struct kmscon_terminal **out,
{
struct kmscon_terminal *term;
int ret;
struct itimerspec spec;
unsigned long fps;
if (!out || !loop || !input)
return -EINVAL;
@ -368,6 +397,19 @@ int kmscon_terminal_new(struct kmscon_terminal **out,
term->input = input;
kmscon_dlist_init(&term->screens);
if (kmscon_conf.fps) {
fps = 1000000000ULL / kmscon_conf.fps;
if (fps == 0)
fps = 1000000000ULL / 100;
else if (fps > 200000000ULL)
fps = 200000000ULL;
} else {
fps = 1000000000ULL / 25;
}
term->fps = 1000000000ULL / fps;
log_debug("FPS: %lu TIMER: %lu", term->fps, fps);
ret = kmscon_console_new(&term->console);
if (ret)
goto err_free;
@ -385,6 +427,20 @@ int kmscon_terminal_new(struct kmscon_terminal **out,
if (ret)
goto err_pty;
memset(&spec, 0, sizeof(spec));
spec.it_value.tv_nsec = 1;
spec.it_interval.tv_nsec = fps;
ret = ev_timer_new(&term->redraw_timer, &spec, redraw_timer_event,
term, log_llog);
if (ret)
goto err_input;
ev_timer_disable(term->redraw_timer);
ret = ev_eloop_add_timer(term->eloop, term->redraw_timer);
if (ret)
goto err_timer;
ev_eloop_ref(term->eloop);
uterm_input_ref(term->input);
*out = term;
@ -392,6 +448,10 @@ int kmscon_terminal_new(struct kmscon_terminal **out,
log_debug("new terminal object %p", term);
return 0;
err_timer:
ev_timer_unref(term->redraw_timer);
err_input:
uterm_input_unregister_cb(term->input, input_event, term);
err_pty:
kmscon_pty_unref(term->pty);
err_vte:
@ -422,12 +482,12 @@ void kmscon_terminal_unref(struct kmscon_terminal *term)
log_debug("free terminal object %p", term);
kmscon_terminal_close(term);
rm_all_screens(term);
ev_eloop_rm_timer(term->redraw_timer);
ev_timer_unref(term->redraw_timer);
uterm_input_unregister_cb(term->input, input_event, term);
kmscon_pty_unref(term->pty);
kmscon_vte_unref(term->vte);
kmscon_console_unref(term->console);
if (term->redraw)
ev_eloop_unregister_idle_cb(term->eloop, draw_all, term);
uterm_input_unref(term->input);
ev_eloop_unref(term->eloop);
free(term);