config: add tweak.render-timer option

This can be set to 'none' (the default), 'osd', 'log' or 'both'.

When 'osd' is enabled, we'll render the frame rendering time to a
sub-surface after each frame.

When 'log' is enabled, the frame rendering time is logged on stderr.
This commit is contained in:
Daniel Eklöf 2020-08-13 18:35:17 +02:00
parent c7126c4076
commit 17070a0d54
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
8 changed files with 158 additions and 55 deletions

View file

@ -30,6 +30,7 @@
(https://codeberg.org/dnkl/foot/issues/54). (https://codeberg.org/dnkl/foot/issues/54).
* **colors.selection-foreground** and **colors.selection-background** * **colors.selection-foreground** and **colors.selection-background**
options to `footrc`. options to `footrc`.
* **tweak.render-timer** option to `footrc`.
### Deprecated ### Deprecated

View file

@ -975,7 +975,29 @@ parse_section_tweak(
const char *key, const char *value, struct config *conf, const char *key, const char *value, struct config *conf,
const char *path, unsigned lineno) const char *path, unsigned lineno)
{ {
if (strcmp(key, "delayed-render-lower") == 0) { if (strcmp(key, "render-timer") == 0) {
if (strcmp(value, "none") == 0) {
conf->tweak.render_timer_osd = false;
conf->tweak.render_timer_log = false;
} else if (strcmp(value, "osd") == 0) {
conf->tweak.render_timer_osd = true;
conf->tweak.render_timer_log = false;
} else if (strcmp(value, "log") == 0) {
conf->tweak.render_timer_osd = false;
conf->tweak.render_timer_log = true;
} else if (strcmp(value, "both") == 0) {
conf->tweak.render_timer_osd = true;
conf->tweak.render_timer_log = true;
} else {
LOG_AND_NOTIFY_ERR(
"%s:%d: [tweak]: %s: invalid 'render-timer' value, "
"expected one of 'none', 'osd', 'log' or 'both'",
path, lineno, value);
return false;
}
}
else if (strcmp(key, "delayed-render-lower") == 0) {
unsigned long ns; unsigned long ns;
if (!str_to_ulong(value, 10, &ns)) { if (!str_to_ulong(value, 10, &ns)) {
LOG_AND_NOTIFY_ERR("%s:%d: expected an integer, got '%s'", path, lineno, value); LOG_AND_NOTIFY_ERR("%s:%d: expected an integer, got '%s'", path, lineno, value);
@ -1291,6 +1313,8 @@ config_load(struct config *conf, const char *conf_path, bool errors_are_fatal)
.delayed_render_lower_ns = 500000, /* 0.5ms */ .delayed_render_lower_ns = 500000, /* 0.5ms */
.delayed_render_upper_ns = 16666666 / 2, /* half a frame period (60Hz) */ .delayed_render_upper_ns = 16666666 / 2, /* half a frame period (60Hz) */
.max_shm_pool_size = 512 * 1024 * 1024, .max_shm_pool_size = 512 * 1024 * 1024,
.render_timer_osd = false,
.render_timer_log = false,
}, },
.notifications = tll_init(), .notifications = tll_init(),

View file

@ -127,6 +127,8 @@ struct config {
bool hold_at_exit; bool hold_at_exit;
struct { struct {
bool render_timer_osd;
bool render_timer_log;
uint64_t delayed_render_lower_ns; uint64_t delayed_render_lower_ns;
uint64_t delayed_render_upper_ns; uint64_t delayed_render_upper_ns;
off_t max_shm_pool_size; off_t max_shm_pool_size;

View file

@ -400,6 +400,12 @@ foot.
When reporting bugs, please mention if, and to what, you have changed When reporting bugs, please mention if, and to what, you have changed
any of these options. any of these options.
*render-timer*
Enables a frame rendering timer, that prints the time it takes to
render each frame, in microseconds, either on-screen, to stderr,
or both. Valid values are *none*, *osd*, *log* and
*both*. Default: _none_.
*delayed-render-lower*, *delayed-render-upper* *delayed-render-lower*, *delayed-render-upper*
These two values control the timeouts (in nanoseconds) that are These two values control the timeouts (in nanoseconds) that are
used to mitigate screen flicker caused by clients writing large, used to mitigate screen flicker caused by clients writing large,

149
render.c
View file

@ -24,7 +24,6 @@
#include "util.h" #include "util.h"
#include "xmalloc.h" #include "xmalloc.h"
#define TIME_FRAME_RENDERING 0
#define TIME_SCROLL_DAMAGE 0 #define TIME_SCROLL_DAMAGE 0
struct renderer { struct renderer {
@ -1300,6 +1299,54 @@ render_csd(struct terminal *term)
render_csd_title(term); render_csd_title(term);
} }
static void
render_osd(struct terminal *term,
struct wl_surface *surf, struct wl_subsurface *sub_surf,
struct buffer *buf,
const wchar_t *text, uint32_t _fg, uint32_t _bg,
unsigned width, unsigned height, unsigned x, unsigned y)
{
pixman_color_t bg = color_hex_to_pixman(_bg);
pixman_image_fill_rectangles(
PIXMAN_OP_SRC, buf->pix[0], &bg, 1,
&(pixman_rectangle16_t){0, 0, width, height});
struct fcft_font *font = term->fonts[0];
pixman_color_t fg = color_hex_to_pixman(_fg);
for (size_t i = 0; i < wcslen(text); i++) {
const struct fcft_glyph *glyph = fcft_glyph_rasterize(
font, text[i], term->font_subpixel);
if (glyph == NULL)
continue;
pixman_image_t *src = pixman_image_create_solid_fill(&fg);
pixman_image_composite32(
PIXMAN_OP_OVER, src, glyph->pix, buf->pix[0], 0, 0, 0, 0,
x + glyph->x, y + font_baseline(term) - glyph->y,
glyph->width, glyph->height);
pixman_image_unref(src);
x += term->cell_width;
}
quirk_weston_subsurface_desync_on(sub_surf);
wl_surface_attach(surf, buf->wl_buf, 0, 0);
wl_surface_damage_buffer(surf, 0, 0, width, height);
wl_surface_set_buffer_scale(surf, term->scale);
struct wl_region *region = wl_compositor_create_region(term->wl->compositor);
if (region != NULL) {
wl_region_add(region, 0, 0, width, height);
wl_surface_set_opaque_region(surf, region);
wl_region_destroy(region);
}
wl_surface_commit(surf);
quirk_weston_subsurface_desync_off(sub_surf);
}
static void static void
render_scrollback_position(struct terminal *term) render_scrollback_position(struct terminal *term)
{ {
@ -1402,35 +1449,6 @@ render_scrollback_position(struct terminal *term)
struct buffer *buf = shm_get_buffer( struct buffer *buf = shm_get_buffer(
term->wl->shm, width, height, cookie, false, 1); term->wl->shm, width, height, cookie, false, 1);
pixman_color_t bg = color_hex_to_pixman(term->colors.table[8 + 4]);
pixman_image_fill_rectangles(
PIXMAN_OP_SRC, buf->pix[0], &bg, 1,
&(pixman_rectangle16_t){0, 0, width, height});
struct fcft_font *font = term->fonts[0];
pixman_color_t fg = color_hex_to_pixman(term->colors.table[0]);
/* Sub-surface relative coordinates */
unsigned x = width - margin - wcslen(text) * term->cell_width;
const unsigned y = margin;
for (size_t i = 0; i < wcslen(text); i++) {
const struct fcft_glyph *glyph = fcft_glyph_rasterize(
font, text[i], term->font_subpixel);
if (glyph == NULL)
continue;
pixman_image_t *src = pixman_image_create_solid_fill(&fg);
pixman_image_composite32(
PIXMAN_OP_OVER, src, glyph->pix, buf->pix[0], 0, 0, 0, 0,
x + glyph->x, y + font_baseline(term) - glyph->y,
glyph->width, glyph->height);
pixman_image_unref(src);
x += term->cell_width;
}
/* *Where* to render - parent relative coordinates */ /* *Where* to render - parent relative coordinates */
int surf_top = 0; int surf_top = 0;
switch (term->conf->scrollback.indicator.position) { switch (term->conf->scrollback.indicator.position) {
@ -1452,24 +1470,44 @@ render_scrollback_position(struct terminal *term)
} }
} }
quirk_weston_subsurface_desync_on(win->scrollback_indicator_sub_surface);
wl_subsurface_set_position( wl_subsurface_set_position(
win->scrollback_indicator_sub_surface, win->scrollback_indicator_sub_surface,
(term->width - margin - width) / scale, (term->width - margin - width) / scale,
(term->margins.top + surf_top) / scale); (term->margins.top + surf_top) / scale);
wl_surface_attach(win->scrollback_indicator_surface, buf->wl_buf, 0, 0);
wl_surface_damage_buffer(win->scrollback_indicator_surface, 0, 0, width, height);
wl_surface_set_buffer_scale(win->scrollback_indicator_surface, scale);
struct wl_region *region = wl_compositor_create_region(wayl->compositor); render_osd(
if (region != NULL) { term,
wl_region_add(region, 0, 0, width, height); win->scrollback_indicator_surface, win->scrollback_indicator_sub_surface,
wl_surface_set_opaque_region(win->scrollback_indicator_surface, region); buf, text,
wl_region_destroy(region); term->colors.table[0], term->colors.table[8 + 4],
} width, height, width - margin - wcslen(text) * term->cell_width, margin);
wl_surface_commit(win->scrollback_indicator_surface); }
quirk_weston_subsurface_desync_off(win->scrollback_indicator_sub_surface);
static void
render_render_timer(struct terminal *term, struct timeval render_time)
{
struct wl_window *win = term->window;
wchar_t text[256];
double usecs = render_time.tv_sec * 1000000 + render_time.tv_usec;
swprintf(text, sizeof(text), L"%.2f µs", usecs);
const int cell_count = wcslen(text);
const int margin = 3 * term->scale;
const int width = 2 * margin + cell_count * term->cell_width;
const int height = 2 * margin + term->cell_height;
unsigned long cookie = shm_cookie_render_timer(term);
struct buffer *buf = shm_get_buffer(
term->wl->shm, width, height, cookie, false, 1);
render_osd(
term,
win->render_timer_surface, win->render_timer_sub_surface,
buf, text,
term->colors.table[0], term->colors.table[8 + 1],
width, height, margin, margin);
} }
static void frame_callback( static void frame_callback(
@ -1485,10 +1523,9 @@ grid_render(struct terminal *term)
if (term->is_shutting_down) if (term->is_shutting_down)
return; return;
#if TIME_FRAME_RENDERING
struct timeval start_time; struct timeval start_time;
gettimeofday(&start_time, NULL); if (term->conf->tweak.render_timer_osd || term->conf->tweak.render_timer_log)
#endif gettimeofday(&start_time, NULL);
assert(term->width > 0); assert(term->width > 0);
assert(term->height > 0); assert(term->height > 0);
@ -1725,15 +1762,21 @@ grid_render(struct terminal *term)
quirk_kde_damage_before_attach(term->window->surface); quirk_kde_damage_before_attach(term->window->surface);
wl_surface_commit(term->window->surface); wl_surface_commit(term->window->surface);
#if TIME_FRAME_RENDERING if (term->conf->tweak.render_timer_osd || term->conf->tweak.render_timer_log) {
struct timeval end_time; struct timeval end_time;
gettimeofday(&end_time, NULL); gettimeofday(&end_time, NULL);
struct timeval render_time; struct timeval render_time;
timersub(&end_time, &start_time, &render_time); timersub(&end_time, &start_time, &render_time);
LOG_INFO("frame rendered in %lds %ldus",
render_time.tv_sec, render_time.tv_usec); if (term->conf->tweak.render_timer_log) {
#endif LOG_INFO("frame rendered in %lds %ld µs",
render_time.tv_sec, render_time.tv_usec);
}
if (term->conf->tweak.render_timer_osd)
render_render_timer(term, render_time);
}
} }
static void static void

3
shm.h
View file

@ -50,4 +50,5 @@ struct terminal;
static inline unsigned long shm_cookie_grid(const struct terminal *term) { return (unsigned long)((uintptr_t)term + 0); } static inline unsigned long shm_cookie_grid(const struct terminal *term) { return (unsigned long)((uintptr_t)term + 0); }
static inline unsigned long shm_cookie_search(const struct terminal *term) { return (unsigned long)((uintptr_t)term + 1); } static inline unsigned long shm_cookie_search(const struct terminal *term) { return (unsigned long)((uintptr_t)term + 1); }
static inline unsigned long shm_cookie_scrollback_indicator(const struct terminal *term) { return (unsigned long)(uintptr_t)term + 2; } static inline unsigned long shm_cookie_scrollback_indicator(const struct terminal *term) { return (unsigned long)(uintptr_t)term + 2; }
static inline unsigned long shm_cookie_csd(const struct terminal *term, int n) { return (unsigned long)((uintptr_t)term + 3 + (n)); } static inline unsigned long shm_cookie_render_timer(const struct terminal *term) { return (unsigned long)(uintptr_t)term + 3; }
static inline unsigned long shm_cookie_csd(const struct terminal *term, int n) { return (unsigned long)((uintptr_t)term + 4 + (n)); }

View file

@ -1197,6 +1197,20 @@ wayl_win_init(struct terminal *term)
} }
wl_surface_commit(win->surface); wl_surface_commit(win->surface);
if (conf->tweak.render_timer_osd) {
win->render_timer_surface = wl_compositor_create_surface(wayl->compositor);
if (win->render_timer_surface == NULL) {
LOG_ERR("failed to create render timer surface");
goto out;
}
win->render_timer_sub_surface = wl_subcompositor_get_subsurface(
wayl->sub_compositor, win->render_timer_surface, win->surface);
wl_subsurface_set_sync(win->render_timer_sub_surface);
wl_surface_set_user_data(win->render_timer_surface, win);
wl_subsurface_set_position(win->render_timer_sub_surface, 10, 10);
wl_surface_commit(win->render_timer_surface);
}
return win; return win;
out: out:
@ -1223,6 +1237,11 @@ wayl_win_destroy(struct wl_window *win)
* nor mouse focus). * nor mouse focus).
*/ */
if (win->render_timer_surface != NULL) {
wl_surface_attach(win->render_timer_surface, NULL, 0, 0);
wl_surface_commit(win->render_timer_surface);
}
if (win->scrollback_indicator_surface != NULL) { if (win->scrollback_indicator_surface != NULL) {
wl_surface_attach(win->scrollback_indicator_surface, NULL, 0, 0); wl_surface_attach(win->scrollback_indicator_surface, NULL, 0, 0);
wl_surface_commit(win->scrollback_indicator_surface); wl_surface_commit(win->scrollback_indicator_surface);
@ -1252,6 +1271,10 @@ wayl_win_destroy(struct wl_window *win)
tll_free(win->on_outputs); tll_free(win->on_outputs);
csd_destroy(win); csd_destroy(win);
if (win->render_timer_sub_surface != NULL)
wl_subsurface_destroy(win->render_timer_sub_surface);
if (win->render_timer_surface != NULL)
wl_surface_destroy(win->render_timer_surface);
if (win->scrollback_indicator_sub_surface != NULL) if (win->scrollback_indicator_sub_surface != NULL)
wl_subsurface_destroy(win->scrollback_indicator_sub_surface); wl_subsurface_destroy(win->scrollback_indicator_sub_surface);
if (win->scrollback_indicator_surface != NULL) if (win->scrollback_indicator_surface != NULL)

View file

@ -280,6 +280,9 @@ struct wl_window {
struct wl_surface *scrollback_indicator_surface; struct wl_surface *scrollback_indicator_surface;
struct wl_subsurface *scrollback_indicator_sub_surface; struct wl_subsurface *scrollback_indicator_sub_surface;
struct wl_surface *render_timer_surface;
struct wl_subsurface *render_timer_sub_surface;
struct wl_callback *frame_callback; struct wl_callback *frame_callback;
tll(const struct monitor *) on_outputs; /* Outputs we're mapped on */ tll(const struct monitor *) on_outputs; /* Outputs we're mapped on */