diff --git a/CHANGELOG.md b/CHANGELOG.md index bdd99381..54f479ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ (https://codeberg.org/dnkl/foot/issues/54). * **colors.selection-foreground** and **colors.selection-background** options to `footrc`. +* **tweak.render-timer** option to `footrc`. ### Deprecated diff --git a/config.c b/config.c index 46edf31c..0ed8b1a9 100644 --- a/config.c +++ b/config.c @@ -975,7 +975,29 @@ parse_section_tweak( const char *key, const char *value, struct config *conf, 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; if (!str_to_ulong(value, 10, &ns)) { 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_upper_ns = 16666666 / 2, /* half a frame period (60Hz) */ .max_shm_pool_size = 512 * 1024 * 1024, + .render_timer_osd = false, + .render_timer_log = false, }, .notifications = tll_init(), diff --git a/config.h b/config.h index 3c7ff1ae..7963aee7 100644 --- a/config.h +++ b/config.h @@ -127,6 +127,8 @@ struct config { bool hold_at_exit; struct { + bool render_timer_osd; + bool render_timer_log; uint64_t delayed_render_lower_ns; uint64_t delayed_render_upper_ns; off_t max_shm_pool_size; diff --git a/doc/footrc.5.scd b/doc/footrc.5.scd index df4993ed..853b0583 100644 --- a/doc/footrc.5.scd +++ b/doc/footrc.5.scd @@ -400,6 +400,12 @@ foot. When reporting bugs, please mention if, and to what, you have changed 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* These two values control the timeouts (in nanoseconds) that are used to mitigate screen flicker caused by clients writing large, diff --git a/render.c b/render.c index db611a50..7a56bbc5 100644 --- a/render.c +++ b/render.c @@ -24,7 +24,6 @@ #include "util.h" #include "xmalloc.h" -#define TIME_FRAME_RENDERING 0 #define TIME_SCROLL_DAMAGE 0 struct renderer { @@ -1300,6 +1299,54 @@ render_csd(struct terminal *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 render_scrollback_position(struct terminal *term) { @@ -1402,35 +1449,6 @@ render_scrollback_position(struct terminal *term) struct buffer *buf = shm_get_buffer( 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 */ int surf_top = 0; 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( win->scrollback_indicator_sub_surface, (term->width - margin - width) / 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); - if (region != NULL) { - wl_region_add(region, 0, 0, width, height); - wl_surface_set_opaque_region(win->scrollback_indicator_surface, region); - wl_region_destroy(region); - } + render_osd( + term, + win->scrollback_indicator_surface, win->scrollback_indicator_sub_surface, + buf, text, + 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( @@ -1485,10 +1523,9 @@ grid_render(struct terminal *term) if (term->is_shutting_down) return; -#if TIME_FRAME_RENDERING struct timeval start_time; - gettimeofday(&start_time, NULL); -#endif + if (term->conf->tweak.render_timer_osd || term->conf->tweak.render_timer_log) + gettimeofday(&start_time, NULL); assert(term->width > 0); assert(term->height > 0); @@ -1725,15 +1762,21 @@ grid_render(struct terminal *term) quirk_kde_damage_before_attach(term->window->surface); wl_surface_commit(term->window->surface); -#if TIME_FRAME_RENDERING - struct timeval end_time; - gettimeofday(&end_time, NULL); + if (term->conf->tweak.render_timer_osd || term->conf->tweak.render_timer_log) { + struct timeval end_time; + gettimeofday(&end_time, NULL); - struct timeval render_time; - timersub(&end_time, &start_time, &render_time); - LOG_INFO("frame rendered in %lds %ldus", - render_time.tv_sec, render_time.tv_usec); -#endif + struct timeval render_time; + timersub(&end_time, &start_time, &render_time); + + if (term->conf->tweak.render_timer_log) { + 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 diff --git a/shm.h b/shm.h index 0bfb3839..fc48308e 100644 --- a/shm.h +++ b/shm.h @@ -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_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_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)); } diff --git a/wayland.c b/wayland.c index 5f12d531..a10ed2b3 100644 --- a/wayland.c +++ b/wayland.c @@ -1197,6 +1197,20 @@ wayl_win_init(struct terminal *term) } 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; out: @@ -1223,6 +1237,11 @@ wayl_win_destroy(struct wl_window *win) * 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) { wl_surface_attach(win->scrollback_indicator_surface, NULL, 0, 0); wl_surface_commit(win->scrollback_indicator_surface); @@ -1252,6 +1271,10 @@ wayl_win_destroy(struct wl_window *win) tll_free(win->on_outputs); 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) wl_subsurface_destroy(win->scrollback_indicator_sub_surface); if (win->scrollback_indicator_surface != NULL) diff --git a/wayland.h b/wayland.h index 777c1517..dc07fb7a 100644 --- a/wayland.h +++ b/wayland.h @@ -280,6 +280,9 @@ struct wl_window { struct wl_surface *scrollback_indicator_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; tll(const struct monitor *) on_outputs; /* Outputs we're mapped on */