From a3c18e72f5804051c5111fea5f4fca488fa088ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Jan 2020 19:48:15 +0100 Subject: [PATCH 1/4] fdm: add support for hooks Hooks are functions executed just before going into a poll(). Or just after executing all FD handlers, if you like. --- fdm.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ fdm.h | 4 ++++ 2 files changed, 53 insertions(+) diff --git a/fdm.c b/fdm.c index 4592a9e8..9d14dca8 100644 --- a/fdm.c +++ b/fdm.c @@ -21,11 +21,17 @@ struct handler { bool deleted; }; +struct hook { + fdm_hook_t callback; + void *callback_data; +}; + struct fdm { int epoll_fd; bool is_polling; tll(struct handler *) fds; tll(struct handler *) deferred_delete; + tll(struct hook) hooks; }; struct fdm * @@ -43,6 +49,7 @@ fdm_init(void) .is_polling = false, .fds = tll_init(), .deferred_delete = tll_init(), + .hooks = tll_init(), }; return fdm; } @@ -56,11 +63,16 @@ fdm_destroy(struct fdm *fdm) if (tll_length(fdm->fds) > 0) LOG_WARN("FD list not empty"); + if (tll_length(fdm->hooks) > 0) + LOG_WARN("hook list not empty"); + assert(tll_length(fdm->fds) == 0); assert(tll_length(fdm->deferred_delete) == 0); + assert(tll_length(fdm->hooks) == 0); tll_free(fdm->fds); tll_free(fdm->deferred_delete); + tll_free(fdm->hooks); close(fdm->epoll_fd); free(fdm); } @@ -195,6 +207,37 @@ fdm_event_del(struct fdm *fdm, int fd, int events) return false; } +bool +fdm_hook_add(struct fdm *fdm, fdm_hook_t hook, void *data) +{ +#if defined(_DEBUG) + tll_foreach(fdm->hooks, it) { + if (it->item.callback == hook) { + LOG_ERR("hook=%p already registered", hook); + return false; + } + } +#endif + + tll_push_back(fdm->hooks, ((struct hook){hook, data})); + return true; +} + +bool +fdm_hook_del(struct fdm *fdm, fdm_hook_t hook) +{ + tll_foreach(fdm->hooks, it) { + if (it->item.callback != hook) + continue; + + tll_remove(fdm->hooks, it); + return true; + } + + LOG_WARN("hook=%p not registered, hook", hook); + return false; +} + bool fdm_poll(struct fdm *fdm) { @@ -204,6 +247,12 @@ fdm_poll(struct fdm *fdm) return false; } + tll_foreach(fdm->hooks, it) { + LOG_DBG("executing hook %p(fdm=%p, data=%p)", + it->item.callback, fdm, it->item.callback_data); + it->item.callback(fdm, it->item.callback_data); + } + struct epoll_event events[tll_length(fdm->fds)]; int r = epoll_wait(fdm->epoll_fd, events, tll_length(fdm->fds), -1); diff --git a/fdm.h b/fdm.h index 62d73ded..62f7616f 100644 --- a/fdm.h +++ b/fdm.h @@ -5,6 +5,7 @@ struct fdm; typedef bool (*fdm_handler_t)(struct fdm *fdm, int fd, int events, void *data); +typedef void (*fdm_hook_t)(struct fdm *fdm, void *data); struct fdm *fdm_init(void); void fdm_destroy(struct fdm *fdm); @@ -16,4 +17,7 @@ bool fdm_del_no_close(struct fdm *fdm, int fd); bool fdm_event_add(struct fdm *fdm, int fd, int events); bool fdm_event_del(struct fdm *fdm, int fd, int events); +bool fdm_hook_add(struct fdm *fdm, fdm_hook_t hook, void *data); +bool fdm_hook_del(struct fdm *fdm, fdm_hook_t hook); + bool fdm_poll(struct fdm *fdm); From 99f471d738e0c2a0dcb61a559be3be512659d113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Jan 2020 19:49:26 +0100 Subject: [PATCH 2/4] render: trigger terminal refreshes in an FDM hook In some cases, we end up calling render_refresh() multiple times in the same FDM iteration. This means will render the first update immediately, and then set the 'pending' flag, causing the updated content to be rendered in the next frame. This can cause flicker, or flashes, since we're presenting one or more intermediate frames until the final content is shown. Not to mention that it is inefficient to render multiple frames like this. Fix by: * render_refresh() only sets a flag in the terminal * install an FDM hook; this hook loops all terminals and executes what render_refresh() _used_ to do (that is, render immediately if we're not waiting for a frame callback, otherwise set 'pending' flag). for all terminals that have the 'refresh_needed' flag set. --- main.c | 6 ++++++ render.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++------ render.h | 7 ++++-- terminal.h | 1 + 4 files changed, 68 insertions(+), 8 deletions(-) diff --git a/main.c b/main.c index ec44b8b3..e418b584 100644 --- a/main.c +++ b/main.c @@ -19,6 +19,7 @@ #include "config.h" #include "fdm.h" +#include "render.h" #include "server.h" #include "shm.h" #include "terminal.h" @@ -218,6 +219,7 @@ main(int argc, char *const *argv) struct fdm *fdm = NULL; struct wayland *wayl = NULL; + struct renderer *renderer = NULL; struct terminal *term = NULL; struct server *server = NULL; struct shutdown_context shutdown_ctx = {.term = &term, .exit_code = EXIT_FAILURE}; @@ -238,6 +240,9 @@ main(int argc, char *const *argv) if ((wayl = wayl_init(&conf, fdm)) == NULL) goto out; + if ((renderer = render_init(fdm, wayl)) == NULL) + goto out; + if (!as_server && (term = term_init( &conf, fdm, wayl, conf.term, "foot", cwd, argc, argv, &term_shutdown_cb, &shutdown_ctx)) == NULL) { @@ -276,6 +281,7 @@ out: term_destroy(term); shm_fini(); + render_destroy(renderer); wayl_destroy(wayl); fdm_destroy(fdm); diff --git a/render.c b/render.c index d97da212..2da90187 100644 --- a/render.c +++ b/render.c @@ -22,6 +22,11 @@ #define min(x, y) ((x) < (y) ? (x) : (y)) #define max(x, y) ((x) > (y) ? (x) : (y)) +struct renderer { + struct fdm *fdm; + struct wayland *wayl; +}; + static struct { size_t total; size_t zero; /* commits presented in less than one frame interval */ @@ -29,6 +34,56 @@ static struct { size_t two; /* commits presented in two or more frame intervals */ } presentation_statistics = {0}; +static void +fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data) +{ + struct renderer *renderer = data; + tll_foreach(renderer->wayl->terms, it) { + struct terminal *term = it->item; + + if (!term->render.refresh_needed) + continue; + + assert(term->window->is_configured); + term->render.refresh_needed = false; + + if (term->window->frame_callback == NULL) { + LOG_INFO("rendering immediately"); + grid_render(term); + } else { + LOG_INFO("setting pending"); + term->render.pending = true; + } + } +} + +struct renderer * +render_init(struct fdm *fdm, struct wayland *wayl) +{ + struct renderer *renderer = calloc(1, sizeof(*renderer)); + *renderer = (struct renderer) { + .fdm = fdm, + .wayl = wayl, + }; + + if (!fdm_hook_add(fdm, &fdm_hook_refresh_pending_terminals, renderer)) { + LOG_ERR("failed to register FDM hook"); + free(renderer); + return NULL; + } + + return renderer; +} + +void +render_destroy(struct renderer *renderer) +{ + if (renderer == NULL) + return; + + fdm_hook_del(renderer->fdm, &fdm_hook_refresh_pending_terminals); +} + static void __attribute__((destructor)) log_presentation_statistics(void) { @@ -1079,10 +1134,5 @@ render_set_title(struct terminal *term, const char *_title) void render_refresh(struct terminal *term) { - assert(term->window->is_configured); - - if (term->window->frame_callback == NULL) - grid_render(term); - else - term->render.pending = true; + term->render.refresh_needed = true; } diff --git a/render.h b/render.h index ad6b6578..74d9b456 100644 --- a/render.h +++ b/render.h @@ -1,9 +1,12 @@ #pragma once #include "terminal.h" +#include "fdm.h" +#include "wayland.h" -struct font *attrs_to_font( - const struct terminal *term, const struct attributes *attrs); +struct renderer; +struct renderer *render_init(struct fdm *fdm, struct wayland *wayl); +void render_destroy(struct renderer *renderer); void grid_render(struct terminal *term); void render_resize(struct terminal *term, int width, int height); diff --git a/terminal.h b/terminal.h index 9d28ff41..fe33ff98 100644 --- a/terminal.h +++ b/terminal.h @@ -283,6 +283,7 @@ struct terminal { bool visual_focus; struct { + bool refresh_needed; int scrollback_lines; struct { From 647a299315a9d5a86fb0a767bcf18288246905b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Jan 2020 19:53:55 +0100 Subject: [PATCH 3/4] render: attrs_to_font() is not used outside of render.c --- render.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render.c b/render.c index 2da90187..1f58fd05 100644 --- a/render.c +++ b/render.c @@ -211,7 +211,7 @@ static const struct wp_presentation_feedback_listener presentation_feedback_list .discarded = &discarded, }; -struct font * +static struct font * attrs_to_font(const struct terminal *term, const struct attributes *attrs) { int idx = attrs->italic << 1 | attrs->bold; From 1060a4250b969b41dbb6ca00223d2ad3563d8ade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Jan 2020 19:56:59 +0100 Subject: [PATCH 4/4] term: more comments in the 'render' sub-struct --- terminal.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/terminal.h b/terminal.h index fe33ff98..e12a914e 100644 --- a/terminal.h +++ b/terminal.h @@ -283,9 +283,10 @@ struct terminal { bool visual_focus; struct { - bool refresh_needed; - int scrollback_lines; + bool refresh_needed; /* Terminal needs to be re-rendered, as soon-as-possible */ + int scrollback_lines; /* Number of scrollback lines, from conf (TODO: move out from render struct?) */ + /* Render threads + synchronization primitives */ struct { size_t count; sem_t start; @@ -304,7 +305,7 @@ struct terminal { struct cell *cell; /* For easy access to content */ } last_cursor; - bool pending; + bool pending; /* Need to re-render again, after next frame-callback */ struct buffer *last_buf; /* Buffer we rendered to last time */ bool was_flashing; /* Flash was active last time we rendered */ bool was_searching;