mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-08 10:06:22 -05:00
render: use shm_scroll() when we believe it will be faster
shm_scroll() is fast when memmove() is slow. That is, when scrolling a *small* amount of lines, shm_scroll() is fast. Conversely, when scrolling a *large* number of lines, memmove() is fast. For now, assume the two methods perform _roughly_ the same given a certain number of bytes they have to touch. This means we should use shm_scroll() when number of scroll lines is less than half the screen. Otherwise we use memmove. Since we need to repair the bottom scroll region after a shm_scroll, those lines are added to the count when determining which method to use. TODO: * Check if it's worth to do shm scrolling when we have a top scroll region. * Do more performance testing with a) various amount of scrolling lines, and b) larger bottom scroll regions.
This commit is contained in:
parent
1224807f50
commit
ed987b2de7
1 changed files with 187 additions and 55 deletions
242
render.c
242
render.c
|
|
@ -22,6 +22,9 @@
|
|||
#include "selection.h"
|
||||
#include "shm.h"
|
||||
|
||||
#define TIME_FRAME_RENDERING 0
|
||||
#define TIME_SCROLL_DAMAGE 0
|
||||
|
||||
#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||
#define max(x, y) ((x) > (y) ? (x) : (y))
|
||||
|
||||
|
|
@ -475,30 +478,181 @@ draw_cursor:
|
|||
return cell_cols;
|
||||
}
|
||||
|
||||
static void
|
||||
render_margin(struct terminal *term, struct buffer *buf, int start_line, int end_line)
|
||||
{
|
||||
/* Fill area outside the cell grid with the default background color */
|
||||
int rmargin = term->width - term->margins.right;
|
||||
int bmargin = term->height - term->margins.bottom;
|
||||
|
||||
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);
|
||||
|
||||
if (start_line == 0) {
|
||||
pixman_image_fill_rectangles(
|
||||
PIXMAN_OP_SRC, buf->pix, &bg, 1,
|
||||
&(pixman_rectangle16_t){0, 0, term->width, term->margins.top});
|
||||
wl_surface_damage_buffer(
|
||||
term->window->surface, 0, 0, term->width, term->margins.top);
|
||||
}
|
||||
|
||||
if (end_line == term->rows) {
|
||||
pixman_image_fill_rectangles(
|
||||
PIXMAN_OP_SRC, buf->pix, &bg, 1,
|
||||
&(pixman_rectangle16_t){0, bmargin, term->width, term->margins.bottom});
|
||||
wl_surface_damage_buffer(
|
||||
term->window->surface, 0, bmargin, term->width, term->margins.bottom);
|
||||
}
|
||||
|
||||
pixman_image_fill_rectangles(
|
||||
PIXMAN_OP_SRC, buf->pix, &bg, 2,
|
||||
(pixman_rectangle16_t[]){
|
||||
{0, term->margins.top + start_line * term->cell_height,
|
||||
term->margins.left, (end_line - start_line) * term->cell_height}, /* Left */
|
||||
{rmargin, term->margins.top, term->margins.right, (end_line - start_line) * term->cell_height}, /* Right */
|
||||
});
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
term->window->surface, 0, term->margins.top, term->margins.left, (end_line - start_line) * term->cell_height);
|
||||
wl_surface_damage_buffer(
|
||||
term->window->surface, rmargin, term->margins.top, term->margins.right, (end_line - start_line) * term->cell_height);
|
||||
}
|
||||
|
||||
static void
|
||||
grid_render_scroll(struct terminal *term, struct buffer *buf,
|
||||
const struct damage *dmg)
|
||||
{
|
||||
int dst_y = term->margins.top + (dmg->scroll.region.start + 0) * term->cell_height;
|
||||
int src_y = term->margins.top + (dmg->scroll.region.start + dmg->scroll.lines) * term->cell_height;
|
||||
int height = (dmg->scroll.region.end - dmg->scroll.region.start - dmg->scroll.lines) * term->cell_height;
|
||||
|
||||
LOG_DBG("damage: SCROLL: %d-%d by %d lines (dst-y: %d, src-y: %d, "
|
||||
"height: %d, stride: %d, mmap-size: %zu)",
|
||||
dmg->scroll.region.start, dmg->scroll.region.end,
|
||||
dmg->scroll.lines,
|
||||
dst_y, src_y, height, buf->stride,
|
||||
buf->size);
|
||||
LOG_DBG(
|
||||
"damage: SCROLL: %d-%d by %d lines",
|
||||
dmg->scroll.region.start, dmg->scroll.region.end, dmg->scroll.lines);
|
||||
|
||||
if (height > 0) {
|
||||
if (height <= 0)
|
||||
return;
|
||||
|
||||
#if TIME_SCROLL_DAMAGE
|
||||
struct timeval start_time;
|
||||
gettimeofday(&start_time, NULL);
|
||||
#endif
|
||||
|
||||
int dst_y = term->margins.top + (dmg->scroll.region.start + 0) * term->cell_height;
|
||||
int src_y = term->margins.top + (dmg->scroll.region.start + dmg->scroll.lines) * term->cell_height;
|
||||
|
||||
/*
|
||||
* SHM scrolling can be *much* faster, but it depends on how many
|
||||
* lines we're scrolling, and how much repairing we need to do.
|
||||
*
|
||||
* In short, scrolling a *large* number of rows is faster with a
|
||||
* memmove, while scrolling a *small* number of lines is faster
|
||||
* with SHM scrolling.
|
||||
*
|
||||
* However, since we need to restore the bottom scrolling region
|
||||
* when SHM scrolling, we also need to take this into account.
|
||||
*
|
||||
* Finally, restoring the window margins is a *huge* performance
|
||||
* hit when scrolling a large number of lines (in addition to the
|
||||
* sloweness of SHM scrolling as method).
|
||||
*
|
||||
* So, we need to figure out when to SHM scroll, and when to
|
||||
* memmove.
|
||||
*
|
||||
* For now, assume that the both methods perform rougly the same,
|
||||
* given an equal number of bytes to move/allocate, and use the
|
||||
* method that results in the least amount of bytes to touch.
|
||||
*
|
||||
* Since number of lines directly translates to bytes, we can
|
||||
* simply count lines.
|
||||
*
|
||||
* SHM scrolling needs to first "move" (punch hole + allocate)
|
||||
* dmg->scroll.lines number of lines, and then we need to restore
|
||||
* the bottom scroll region.
|
||||
*
|
||||
* If the total number of lines is less than half the screen - use
|
||||
* SHM. Otherwise use memmove.
|
||||
*/
|
||||
bool try_shm_scroll =
|
||||
dmg->scroll.region.start == 0 &&
|
||||
dmg->scroll.lines + (term->rows - dmg->scroll.region.end) < term->rows / 2;
|
||||
|
||||
bool did_shm_scroll = false;
|
||||
|
||||
//try_shm_scroll = false;
|
||||
//try_shm_scroll = true;
|
||||
|
||||
if (try_shm_scroll) {
|
||||
did_shm_scroll = shm_scroll(
|
||||
term->wl->shm, buf, dmg->scroll.lines * term->cell_height);
|
||||
|
||||
/*
|
||||
* When SHM scrolling succeeded, the scrolled in area is made
|
||||
* up of newly allocated, zero-initialized memory. Thus we'll
|
||||
* need to both copy the bottom scrolling region, and
|
||||
* re-render the window margins.
|
||||
*
|
||||
* This is different from when we scroll with a simple
|
||||
* memmove, since in that case, the scrolling region and
|
||||
* margins are *copied*, and thus the original region+margin
|
||||
* remains in place.
|
||||
*/
|
||||
|
||||
if (!did_shm_scroll)
|
||||
LOG_DBG("fast scroll failed");
|
||||
} else {
|
||||
LOG_DBG(
|
||||
"skipping SHM scroll "
|
||||
"(region.start=%d, bottom-region-lines=%d, term-lines=%d)",
|
||||
dmb->scroll.region.start, scroll_region_lines, term->rows);
|
||||
}
|
||||
|
||||
if (did_shm_scroll) {
|
||||
|
||||
/* Restore bottom scrolling region */
|
||||
if (dmg->scroll.region.end < term->rows) {
|
||||
int src = dmg->scroll.region.end - dmg->scroll.lines;
|
||||
int dst = dmg->scroll.region.end;
|
||||
size_t amount = max(0, term->rows - dmg->scroll.region.end);
|
||||
|
||||
LOG_DBG("memmoving %zu lines of scroll region", amount);
|
||||
|
||||
assert(src >= 0);
|
||||
|
||||
uint8_t *raw = buf->mmapped;
|
||||
memmove(raw + dst * term->cell_height * buf->stride,
|
||||
raw + src * term->cell_height * buf->stride,
|
||||
amount * term->cell_height * buf->stride);
|
||||
}
|
||||
|
||||
/* Restore margins */
|
||||
render_margin(
|
||||
term, buf, dmg->scroll.region.end - dmg->scroll.lines, term->rows);
|
||||
}
|
||||
|
||||
/* Fallback for when we either cannot do SHM scrolling, or it failed */
|
||||
if (!did_shm_scroll) {
|
||||
uint8_t *raw = buf->mmapped;
|
||||
memmove(raw + dst_y * buf->stride,
|
||||
raw + src_y * buf->stride,
|
||||
height * buf->stride);
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
term->window->surface, term->margins.left, dst_y, term->width - term->margins.left - term->margins.right, height);
|
||||
}
|
||||
|
||||
#if TIME_SCROLL_DAMAGE
|
||||
struct timeval end_time;
|
||||
gettimeofday(&end_time, NULL);
|
||||
|
||||
struct timeval memmove_time;
|
||||
timersub(&end_time, &start_time, &memmove_time);
|
||||
LOG_INFO("scrolled %dKB (%d lines) using %s in %lds %ldus",
|
||||
height * buf->stride / 1024, dmg->scroll.lines,
|
||||
did_shm_scroll ? "SHM" : try_shm_scroll ? "memmove (SHM failed)" : "memmove",
|
||||
memmove_time.tv_sec, memmove_time.tv_usec);
|
||||
#endif
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
term->window->surface, term->margins.left, dst_y,
|
||||
term->width - term->margins.left - term->margins.right, height);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -1056,8 +1210,6 @@ grid_render(struct terminal *term)
|
|||
if (term->is_shutting_down)
|
||||
return;
|
||||
|
||||
#define TIME_FRAME_RENDERING 0
|
||||
|
||||
#if TIME_FRAME_RENDERING
|
||||
struct timeval start_time;
|
||||
gettimeofday(&start_time, NULL);
|
||||
|
|
@ -1070,12 +1222,7 @@ grid_render(struct terminal *term)
|
|||
struct buffer *buf = shm_get_buffer(
|
||||
term->wl->shm, term->width, term->height, cookie);
|
||||
|
||||
wl_surface_attach(term->window->surface, buf->wl_buf, 0, 0);
|
||||
|
||||
pixman_image_t *pix = buf->pix;
|
||||
pixman_region16_t clip;
|
||||
pixman_region_init_rect(&clip, term->margins.left, term->margins.top, term->cols * term->cell_width, term->rows * term->cell_height);
|
||||
pixman_image_set_clip_region(pix, &clip);
|
||||
pixman_image_set_clip_region(buf->pix, NULL);
|
||||
|
||||
/* If we resized the window, or is flashing, or just stopped flashing */
|
||||
if (term->render.last_buf != buf ||
|
||||
|
|
@ -1091,7 +1238,10 @@ grid_render(struct terminal *term)
|
|||
{
|
||||
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.");
|
||||
LOG_WARN(
|
||||
"it appears your Wayland compositor does not support "
|
||||
"buffer re-use for SHM clients; expect lower "
|
||||
"performance.");
|
||||
has_warned = true;
|
||||
}
|
||||
|
||||
|
|
@ -1100,35 +1250,7 @@ grid_render(struct terminal *term)
|
|||
}
|
||||
|
||||
else {
|
||||
/* Fill area outside the cell grid with the default background color */
|
||||
int rmargin = term->width - term->margins.right;
|
||||
int bmargin = term->height - term->margins.bottom;
|
||||
|
||||
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_set_clip_region(pix, NULL);
|
||||
pixman_image_fill_rectangles(
|
||||
PIXMAN_OP_SRC, pix, &bg, 4,
|
||||
(pixman_rectangle16_t[]){
|
||||
{0, 0, term->width, term->margins.top}, /* Top */
|
||||
{0, 0, term->margins.left, term->height}, /* Left */
|
||||
{rmargin, 0, term->margins.right, term->height}, /* Right */
|
||||
{0, bmargin, term->width, term->margins.bottom}}); /* Bottom */
|
||||
pixman_image_set_clip_region(pix, &clip);
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
term->window->surface, 0, 0, term->width, term->margins.top);
|
||||
wl_surface_damage_buffer(
|
||||
term->window->surface, 0, 0, term->margins.left, term->height);
|
||||
wl_surface_damage_buffer(
|
||||
term->window->surface, rmargin, 0, term->margins.right, term->height);
|
||||
wl_surface_damage_buffer(
|
||||
term->window->surface, 0, bmargin, term->width, term->margins.bottom);
|
||||
|
||||
/* Force a full grid refresh */
|
||||
render_margin(term, buf, 0, term->rows);
|
||||
term_damage_view(term);
|
||||
}
|
||||
|
||||
|
|
@ -1137,6 +1259,13 @@ grid_render(struct terminal *term)
|
|||
term->render.was_searching = term->is_searching;
|
||||
}
|
||||
|
||||
pixman_region16_t clip;
|
||||
pixman_region_init_rect(
|
||||
&clip,
|
||||
term->margins.left, term->margins.top,
|
||||
term->cols * term->cell_width, term->rows * term->cell_height);
|
||||
pixman_image_set_clip_region(buf->pix, &clip);
|
||||
|
||||
/* Erase old cursor (if we rendered a cursor last time) */
|
||||
if (term->render.last_cursor.cell != NULL) {
|
||||
|
||||
|
|
@ -1147,7 +1276,7 @@ grid_render(struct terminal *term)
|
|||
/* If cell is already dirty, it will be rendered anyway */
|
||||
if (cell->attrs.clean) {
|
||||
cell->attrs.clean = 0;
|
||||
int cols = render_cell(term, pix, cell, at.col, at.row, false);
|
||||
int cols = render_cell(term, buf->pix, cell, at.col, at.row, false);
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
term->window->surface,
|
||||
|
|
@ -1181,6 +1310,9 @@ grid_render(struct terminal *term)
|
|||
tll_remove(term->grid->scroll_damage, it);
|
||||
}
|
||||
|
||||
pixman_image_set_clip_region(buf->pix, &clip);
|
||||
wl_surface_attach(term->window->surface, buf->wl_buf, 0, 0);
|
||||
|
||||
if (term->render.workers.count > 0) {
|
||||
|
||||
term->render.workers.buf = buf;
|
||||
|
|
@ -1220,7 +1352,7 @@ grid_render(struct terminal *term)
|
|||
if (!row->dirty)
|
||||
continue;
|
||||
|
||||
render_row(term, pix, row, r);
|
||||
render_row(term, buf->pix, row, r);
|
||||
row->dirty = false;
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
|
|
@ -1274,7 +1406,7 @@ grid_render(struct terminal *term)
|
|||
cell->attrs.clean = 0;
|
||||
term->render.last_cursor.cell = cell;
|
||||
int cols_updated = render_cell(
|
||||
term, pix, cell, term->cursor.point.col, view_aligned_row, true);
|
||||
term, buf->pix, cell, term->cursor.point.col, view_aligned_row, true);
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
term->window->surface,
|
||||
|
|
@ -1283,13 +1415,13 @@ grid_render(struct terminal *term)
|
|||
cols_updated * term->cell_width, term->cell_height);
|
||||
}
|
||||
|
||||
render_sixel_images(term, pix);
|
||||
render_sixel_images(term, buf->pix);
|
||||
|
||||
if (term->flash.active) {
|
||||
/* Note: alpha is pre-computed in each color component */
|
||||
/* TODO: dim while searching */
|
||||
pixman_image_fill_rectangles(
|
||||
PIXMAN_OP_OVER, pix,
|
||||
PIXMAN_OP_OVER, buf->pix,
|
||||
&(pixman_color_t){.red=0x7fff, .green=0x7fff, .blue=0, .alpha=0x7fff},
|
||||
1, &(pixman_rectangle16_t){0, 0, term->width, term->height});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue