From 15bfeea745fa9657b3802b2959153cc7f4fe017d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 7 May 2021 20:21:27 +0200 Subject: [PATCH] =?UTF-8?q?render:=20wip:=20re-apply=20last=20frame?= =?UTF-8?q?=E2=80=99s=20damage=20when=20forced=20to=20double=20buffer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we are forced to swap between two buffers, re-apply the old frame’s damage to the current buffer, before applying the current frame’s damage. First, while applying this frame’s scroll damage, copy it to the buffer’s scroll damage list (so that we can access it via term->render.last_buf). Also, when iterating and rendering the grid, build a pixman region of the damaged regions. This is currently done on a per-row basis. This is also stored in the buffer. Now, when being forced to double buffer, first iterate the old buffer’s damage, and re-apply it to the current buffer. Then, composite the old buffer on top of the current buffer, using the old frame’s damage region as clip region. This effectively copies everything that was rendered to the last frame. Remember, this is on a per-row basis. Then we go on and render the frame as usual. Note that it would be _really_ nice if we could subtract the current frame’s damage region from the clip region (no point in copying areas we’re going to overwrite anyway). Unfortunately, that’s harder than it looks; the current frame’s damage region is only valid *after* this frame’s scroll damage have been applied, while the last frame’s damage region is only valid *before* it’s been applied. Translating one to the other isn’t easy, since scroll damage isn’t just about counting lines - there may be multiple scroll damage records, each with its own scrolling region. This creates very complex scenarios. --- render.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 132 insertions(+), 12 deletions(-) diff --git a/render.c b/render.c index 43b6ee6f..3c97df97 100644 --- a/render.c +++ b/render.c @@ -2042,6 +2042,29 @@ grid_render(struct terminal *term) struct buffer *buf = shm_get_buffer( term->wl->shm, term->width, term->height, cookie, true, 1 + term->render.workers.count); + /* Mark old cursor cell as dirty, to force it to be re-rendered */ + if (term->render.last_cursor.row != NULL && !term->render.last_cursor.hidden) { + struct row *row = term->render.last_cursor.row; + struct cell *cell = &row->cells[term->render.last_cursor.col]; + cell->attrs.clean = 0; + row->dirty = true; + } + + /* Remember current cursor position, for the next frame */ + term->render.last_cursor.row = grid_row(term->grid, term->grid->cursor.point.row); + term->render.last_cursor.col = term->grid->cursor.point.col; + term->render.last_cursor.hidden = term->hide_cursor; + + /* Mark current cursor cell as dirty, to ensure it is rendered */ + if (!term->hide_cursor) { + const struct coord *cursor = &term->grid->cursor.point; + + struct row *row = grid_row(term->grid, cursor->row); + struct cell *cell = &row->cells[cursor->col]; + cell->attrs.clean = 0; + row->dirty = true; + } + /* 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 || @@ -2071,7 +2094,91 @@ grid_render(struct terminal *term) } xassert(term->render.last_buf->size == buf->size); +#if 0 memcpy(buf->mmapped, term->render.last_buf->mmapped, buf->size); + tll_free(term->render.last_buf->scroll_damage); +#else + +#if 0 + /* + * TODO: remove this frame’s damage from the region we + * copy from the old frame. + * + * - this frame’s dirty region is only valid *after* we’ve + * applied its scroll damage. + * - last frame’s dirty region is only valid *before* + * we’ve applied this frame’s scroll damage. + * + * Can we transform one of the regions? It’s not trivial, + * since scroll damage isn’t just about counting lines; + * there may be multiple damage records, each with + * different scrolling regions. + */ + pixman_region32_t dirty; + pixman_region32_init(&dirty); + + bool full_repaint_needed UNUSED = true; + for (int r = 0; r < term->rows; r++) { + const struct row *row = grid_row_in_view(term->grid, r); + bool row_all_dirty = true; + for (int c = 0; c < term->cols; c++) { + if (row->cells[c].attrs.clean) { + row_all_dirty = false; + full_repaint_needed = false; + break; + } + } + + if (row_all_dirty) { + pixman_region32_union_rect( + &dirty, &dirty, + term->margins.left, + term->margins.top + r * term->cell_height, + term->width - term->margins.left - term->margins.right, + term->cell_height); + } + } +#endif + tll_foreach(term->render.last_buf->scroll_damage, it) { + switch (it->item.type) { + case DAMAGE_SCROLL: + if (term->grid->view == term->grid->offset) + grid_render_scroll(term, buf, &it->item); + break; + + case DAMAGE_SCROLL_REVERSE: + if (term->grid->view == term->grid->offset) + grid_render_scroll_reverse(term, buf, &it->item); + break; + + case DAMAGE_SCROLL_IN_VIEW: + grid_render_scroll(term, buf, &it->item); + break; + + case DAMAGE_SCROLL_REVERSE_IN_VIEW: + grid_render_scroll_reverse(term, buf, &it->item); + break; + } + + tll_remove(term->render.last_buf->scroll_damage, it); + } + +#if 0 + pixman_region32_subtract(&dirty, &term->render.last_buf->dirty, &dirty); + pixman_image_set_clip_region32(buf->pix[0], &dirty); +#else + pixman_image_set_clip_region32(buf->pix[0], &term->render.last_buf->dirty); +#endif + + pixman_image_composite32( + PIXMAN_OP_SRC, term->render.last_buf->pix[0], NULL, buf->pix[0], + 0, 0, 0, 0, 0, 0, term->width, term->height); + + pixman_image_set_clip_region32(buf->pix[0], NULL); +#if 0 + pixman_region32_fini(&dirty); +#endif +#endif } else { @@ -2085,8 +2192,15 @@ grid_render(struct terminal *term) term->render.was_searching = term->is_searching; } + if (term->render.last_buf != NULL) + tll_free(term->render.last_buf->scroll_damage); + buf->age = 0; + xassert(tll_length(buf->scroll_damage) == 0); + tll_foreach(term->grid->scroll_damage, it) { + tll_push_back(buf->scroll_damage, it->item); + switch (it->item.type) { case DAMAGE_SCROLL: if (term->grid->view == term->grid->offset) @@ -2129,6 +2243,7 @@ grid_render(struct terminal *term) */ selection_dirty_cells(term); +#if 0 /* Mark old cursor cell as dirty, to force it to be re-rendered */ if (term->render.last_cursor.row != NULL && !term->render.last_cursor.hidden) { struct row *row = term->render.last_cursor.row; @@ -2151,7 +2266,7 @@ grid_render(struct terminal *term) cell->attrs.clean = 0; row->dirty = true; } - +#endif /* Translate offset-relative row to view-relative, unless cursor * is hidden, then we just set it to -1 */ struct coord cursor = {-1, -1}; @@ -2173,18 +2288,22 @@ grid_render(struct terminal *term) xassert(tll_length(term->render.workers.queue) == 0); } + pixman_region32_clear(&buf->dirty); int first_dirty_row = -1; for (int r = 0; r < term->rows; r++) { struct row *row = grid_row_in_view(term->grid, r); if (!row->dirty) { if (first_dirty_row >= 0) { + int x = term->margins.left; + int y = term->margins.top + first_dirty_row * term->cell_height; + int width = term->width - term->margins.left - term->margins.right; + int height = (r - first_dirty_row) * term->cell_height; + wl_surface_damage_buffer( - term->window->surface, - term->margins.left, - term->margins.top + first_dirty_row * term->cell_height, - term->width - term->margins.left - term->margins.right, - (r - first_dirty_row) * term->cell_height); + term->window->surface, x, y, width, height); + pixman_region32_union_rect( + &buf->dirty, &buf->dirty, x, y, width, height); } first_dirty_row = -1; continue; @@ -2205,12 +2324,13 @@ grid_render(struct terminal *term) } if (first_dirty_row >= 0) { - wl_surface_damage_buffer( - term->window->surface, - term->margins.left, - term->margins.top + first_dirty_row * term->cell_height, - term->width - term->margins.left - term->margins.right, - (term->rows - first_dirty_row) * term->cell_height); + int x = term->margins.left; + int y = term->margins.top + first_dirty_row * term->cell_height; + int width = term->width - term->margins.left - term->margins.right; + int height = (term->rows - first_dirty_row) * term->cell_height; + + wl_surface_damage_buffer(term->window->surface, x, y, width, height); + pixman_region32_union_rect(&buf->dirty, &buf->dirty, x, y, width, height); } /* Signal workers the frame is done */