From 67905c600050df4e1c8a46053038a011accd9de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 27 Sep 2019 19:33:45 +0200 Subject: [PATCH] render: handle compositors that does buffer swapping Not all compositors support buffer re-use. I.e. they will call the frame callback *before* the previous buffer has been released. Effectively causing us to swap between two buffers. Previously, this made us enter an infinite re-render loop, since we considered the window 'dirty' (and in need of re-draw) when the buffer is different from last redraw. Now, we detect the buffer swapping case; size must match, and we must not have any other condition that require a full repaint. In this case, we can memcpy() the old buffer to the new one, without dirtying the entire grid. We then update only the dirty cells (and scroll damage). Note that there was a bug here, where we erased the old cursor *before* checking for a new buffer. This worked when the buffer had *not* changed. Now that we need to handle the case where it *has* changed, we must do the memcpy() *before* we erase the cursor, or the re-painted cell is lost. This makes foot work on Plasma, without burning CPU. The memcpy() does incur a performance penalty, but we're still (much) faster than e.g. konsole. In fact, we're still mostly on par with Alacritty. --- render.c | 106 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 45 deletions(-) diff --git a/render.c b/render.c index 87ba683b..bfd27faa 100644 --- a/render.c +++ b/render.c @@ -444,6 +444,66 @@ grid_render(struct terminal *term) bool all_clean = tll_length(term->grid->scroll_damage) == 0; + /* If we resized the window, or is flashing, or just stopped flashing */ + if (term->render.last_buf != buf || + term->flash.active || term->render.was_flashing || + term->is_searching != term->render.was_searching) + { + if (term->render.last_buf != NULL && + term->render.last_buf->width == buf->width && + term->render.last_buf->height == buf->height && + !term->flash.active && + !term->render.was_flashing && + term->is_searching == term->render.was_searching) + { + static bool has_warned = false; + if (!has_warned) { + LOG_WARN("it appears your Wayland compositor does not support buffer re-use for SHM clients; expect lower performance."); + has_warned = true; + } + + assert(term->render.last_buf->size == buf->size); + memcpy(buf->mmapped, term->render.last_buf->mmapped, buf->size); + } + + else { + /* Fill area outside the cell grid with the default background color */ + int rmargin = term->x_margin + term->cols * term->cell_width; + int bmargin = term->y_margin + term->rows * term->cell_height; + int rmargin_width = term->width - rmargin; + int bmargin_height = term->height - bmargin; + + uint32_t _bg = !term->reverse ? term->colors.bg : term->colors.fg; + pixman_color_t bg = color_hex_to_pixman_with_alpha(_bg, term->colors.alpha); + if (term->is_searching) + pixman_color_dim(&bg); + + pixman_image_fill_rectangles( + PIXMAN_OP_SRC, pix, &bg, 4, + (pixman_rectangle16_t[]){ + {0, 0, term->width, term->y_margin}, /* Top */ + {0, 0, term->x_margin, term->height}, /* Left */ + {rmargin, 0, rmargin_width, term->height}, /* Right */ + {0, bmargin, term->width, bmargin_height}}); /* Bottom */ + + wl_surface_damage_buffer( + term->wl.surface, 0, 0, term->width, term->y_margin); + wl_surface_damage_buffer( + term->wl.surface, 0, 0, term->x_margin, term->height); + wl_surface_damage_buffer( + term->wl.surface, rmargin, 0, rmargin_width, term->height); + wl_surface_damage_buffer( + term->wl.surface, 0, bmargin, term->width, bmargin_height); + + /* Force a full grid refresh */ + term_damage_view(term); + } + + term->render.last_buf = buf; + term->render.was_flashing = term->flash.active; + term->render.was_searching = term->is_searching; + } + /* Erase old cursor (if we rendered a cursor last time) */ if (term->render.last_cursor.cell != NULL) { struct cell *cell = term->render.last_cursor.cell; @@ -468,51 +528,7 @@ grid_render(struct terminal *term) * by the cursor, since only the final cell matters. */ all_clean = false; } - } - - if (term->flash.active) - term_damage_view(term); - - /* If we resized the window, or is flashing, or just stopped flashing */ - if (term->render.last_buf != buf || - term->flash.active || term->render.was_flashing || - term->is_searching != term->render.was_searching) - { - /* Fill area outside the cell grid with the default background color */ - int rmargin = term->x_margin + term->cols * term->cell_width; - int bmargin = term->y_margin + term->rows * term->cell_height; - int rmargin_width = term->width - rmargin; - int bmargin_height = term->height - bmargin; - - uint32_t _bg = !term->reverse ? term->colors.bg : term->colors.fg; - pixman_color_t bg = color_hex_to_pixman_with_alpha(_bg, term->colors.alpha); - if (term->is_searching) - pixman_color_dim(&bg); - - pixman_image_fill_rectangles( - PIXMAN_OP_SRC, pix, &bg, 4, - (pixman_rectangle16_t[]){ - {0, 0, term->width, term->y_margin}, /* Top */ - {0, 0, term->x_margin, term->height}, /* Left */ - {rmargin, 0, rmargin_width, term->height}, /* Right */ - {0, bmargin, term->width, bmargin_height}}); /* Bottom */ - - wl_surface_damage_buffer( - term->wl.surface, 0, 0, term->width, term->y_margin); - wl_surface_damage_buffer( - term->wl.surface, 0, 0, term->x_margin, term->height); - wl_surface_damage_buffer( - term->wl.surface, rmargin, 0, rmargin_width, term->height); - wl_surface_damage_buffer( - term->wl.surface, 0, bmargin, term->width, bmargin_height); - - /* Force a full grid refresh */ - term_damage_view(term); - - term->render.last_buf = buf; - term->render.was_flashing = term->flash.active; - term->render.was_searching = term->is_searching; - } + } tll_foreach(term->grid->scroll_damage, it) { switch (it->item.type) {