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:
parent
a05815eb71
commit
8398f4b0f9
@ -432,6 +432,7 @@ static void print_help()
|
|||||||
"\t --fbdev [off] Use fbdev instead of DRM\n"
|
"\t --fbdev [off] Use fbdev instead of DRM\n"
|
||||||
"\t --dumb [off] Use dumb DRM instead of hardware-\n"
|
"\t --dumb [off] Use dumb DRM instead of hardware-\n"
|
||||||
"\t accelerated DRM devices\n"
|
"\t accelerated DRM devices\n"
|
||||||
|
"\t --fps [50] Limit frame-rate\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Input Device Options:\n"
|
"Input Device Options:\n"
|
||||||
"\t --xkb-layout <layout> [us] Set XkbLayout for input devices\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, "silent", NULL, &kmscon_conf.silent, false),
|
||||||
CONF_OPTION_BOOL(0, "fbdev", NULL, &kmscon_conf.use_fbdev, false),
|
CONF_OPTION_BOOL(0, "fbdev", NULL, &kmscon_conf.use_fbdev, false),
|
||||||
CONF_OPTION_BOOL(0, "dumb", NULL, &kmscon_conf.dumb, 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('s', "switchvt", NULL, &kmscon_conf.switchvt, false),
|
||||||
CONF_OPTION_BOOL('l', "login", aftercheck_login, &kmscon_conf.login, false),
|
CONF_OPTION_BOOL('l', "login", aftercheck_login, &kmscon_conf.login, false),
|
||||||
CONF_OPTION_STRING('t', "term", NULL, &kmscon_conf.term, "vt220"),
|
CONF_OPTION_STRING('t', "term", NULL, &kmscon_conf.term, "vt220"),
|
||||||
|
@ -79,6 +79,9 @@ struct kmscon_conf_t {
|
|||||||
|
|
||||||
/* color palette */
|
/* color palette */
|
||||||
char *palette;
|
char *palette;
|
||||||
|
|
||||||
|
/* frame-rate limit */
|
||||||
|
unsigned int fps;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct kmscon_conf_t kmscon_conf;
|
extern struct kmscon_conf_t kmscon_conf;
|
||||||
|
@ -67,7 +67,9 @@ struct kmscon_terminal {
|
|||||||
unsigned int min_cols;
|
unsigned int min_cols;
|
||||||
unsigned int min_rows;
|
unsigned int min_rows;
|
||||||
|
|
||||||
bool redraw;
|
unsigned int fps;
|
||||||
|
unsigned int redraw;
|
||||||
|
struct ev_timer *redraw_timer;
|
||||||
struct kmscon_console *console;
|
struct kmscon_console *console;
|
||||||
struct kmscon_vte *vte;
|
struct kmscon_vte *vte;
|
||||||
struct kmscon_pty *pty;
|
struct kmscon_pty *pty;
|
||||||
@ -76,9 +78,8 @@ struct kmscon_terminal {
|
|||||||
void *data;
|
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 uterm_screen *screen;
|
||||||
struct kmscon_dlist *iter;
|
struct kmscon_dlist *iter;
|
||||||
struct screen *ent;
|
struct screen *ent;
|
||||||
@ -86,9 +87,6 @@ static void draw_all(struct ev_eloop *eloop, void *unused, void *data)
|
|||||||
if (!term->awake)
|
if (!term->awake)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ev_eloop_unregister_idle_cb(term->eloop, draw_all, term);
|
|
||||||
term->redraw = false;
|
|
||||||
|
|
||||||
kmscon_dlist_for_each(iter, &term->screens) {
|
kmscon_dlist_for_each(iter, &term->screens) {
|
||||||
ent = kmscon_dlist_entry(iter, struct screen, list);
|
ent = kmscon_dlist_entry(iter, struct screen, list);
|
||||||
screen = ent->screen;
|
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)
|
static void schedule_redraw(struct kmscon_terminal *term)
|
||||||
{
|
{
|
||||||
int ret;
|
if (!term->awake)
|
||||||
|
|
||||||
if (term->redraw || !term->awake)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ret = ev_eloop_register_idle_cb(term->eloop, draw_all, term);
|
log_debug("schedule");
|
||||||
if (ret)
|
if (!term->redraw)
|
||||||
log_warn("cannot schedule redraw");
|
ev_timer_enable(term->redraw_timer);
|
||||||
else
|
if (term->redraw < term->fps)
|
||||||
term->redraw = true;
|
term->redraw = term->fps;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -354,6 +381,8 @@ int kmscon_terminal_new(struct kmscon_terminal **out,
|
|||||||
{
|
{
|
||||||
struct kmscon_terminal *term;
|
struct kmscon_terminal *term;
|
||||||
int ret;
|
int ret;
|
||||||
|
struct itimerspec spec;
|
||||||
|
unsigned long fps;
|
||||||
|
|
||||||
if (!out || !loop || !input)
|
if (!out || !loop || !input)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -368,6 +397,19 @@ int kmscon_terminal_new(struct kmscon_terminal **out,
|
|||||||
term->input = input;
|
term->input = input;
|
||||||
kmscon_dlist_init(&term->screens);
|
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);
|
ret = kmscon_console_new(&term->console);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_free;
|
goto err_free;
|
||||||
@ -385,6 +427,20 @@ int kmscon_terminal_new(struct kmscon_terminal **out,
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto err_pty;
|
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);
|
ev_eloop_ref(term->eloop);
|
||||||
uterm_input_ref(term->input);
|
uterm_input_ref(term->input);
|
||||||
*out = term;
|
*out = term;
|
||||||
@ -392,6 +448,10 @@ int kmscon_terminal_new(struct kmscon_terminal **out,
|
|||||||
log_debug("new terminal object %p", term);
|
log_debug("new terminal object %p", term);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_timer:
|
||||||
|
ev_timer_unref(term->redraw_timer);
|
||||||
|
err_input:
|
||||||
|
uterm_input_unregister_cb(term->input, input_event, term);
|
||||||
err_pty:
|
err_pty:
|
||||||
kmscon_pty_unref(term->pty);
|
kmscon_pty_unref(term->pty);
|
||||||
err_vte:
|
err_vte:
|
||||||
@ -422,12 +482,12 @@ void kmscon_terminal_unref(struct kmscon_terminal *term)
|
|||||||
log_debug("free terminal object %p", term);
|
log_debug("free terminal object %p", term);
|
||||||
kmscon_terminal_close(term);
|
kmscon_terminal_close(term);
|
||||||
rm_all_screens(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);
|
uterm_input_unregister_cb(term->input, input_event, term);
|
||||||
kmscon_pty_unref(term->pty);
|
kmscon_pty_unref(term->pty);
|
||||||
kmscon_vte_unref(term->vte);
|
kmscon_vte_unref(term->vte);
|
||||||
kmscon_console_unref(term->console);
|
kmscon_console_unref(term->console);
|
||||||
if (term->redraw)
|
|
||||||
ev_eloop_unregister_idle_cb(term->eloop, draw_all, term);
|
|
||||||
uterm_input_unref(term->input);
|
uterm_input_unref(term->input);
|
||||||
ev_eloop_unref(term->eloop);
|
ev_eloop_unref(term->eloop);
|
||||||
free(term);
|
free(term);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user