diff options
| author | Berke Güzel <wenekar1@gmail.com> | 2026-10-13 14:10:31 +0300 |
|---|---|---|
| committer | Berke Güzel <wenekar1@gmail.com> | 2026-10-13 14:10:31 +0300 |
| commit | 7633ab2c60b40bd5aa67ef391b2a0d149685742f (patch) | |
| tree | 37d4f89722605a177715f400eb103382ff08e2c3 /src/screen.c | |
| parent | 274abc013e4b914b005191a7f74eaf2961a1e59e (diff) | |
Optimize screen scrolling, SIMD wrap padding, and reflow loops
- Batch full-screen scroll rotations, eliminating row-by-row scroll loops
- Use block memmove for sub-region scrolling, eliminating heap/stack row swaps
- Vectorize CELL_WRAP_PAD flag setting with AVX2/SSE2/NEON in screen.h
- Replace scalar tab-stop loops with 64-bit word stamping
- Optimize active row backwards scan in resize and erase mode 2
Diffstat (limited to 'src/screen.c')
| -rw-r--r-- | src/screen.c | 286 |
1 files changed, 223 insertions, 63 deletions
diff --git a/src/screen.c b/src/screen.c index d6a408d..afd9b4a 100644 --- a/src/screen.c +++ b/src/screen.c @@ -163,10 +163,8 @@ const struct cell *term_ring_row(const struct term *t, int row) if (got < t->cols) { fill_cells(out + got, t->cols - got, TERM_COLOR_FG, TERM_COLOR_BG); int line_rows = term_hist_line_rows(&t->hist, id, t->cols); - if (seg + 1 < line_rows || (row + 1 == total && hist_is_open(&t->hist))) { - for (int x = got; x < t->cols; x++) - out[x].flags |= CELL_WRAP_PAD; - } + if (seg + 1 < line_rows || (row + 1 == total && hist_is_open(&t->hist))) + set_cells_wrap_pad(out + got, t->cols - got); } return out; } @@ -204,10 +202,8 @@ const struct cell *term_screen_view_row(const struct term *t, int y) if (got < t->cols) { fill_cells(out + got, t->cols - got, TERM_COLOR_FG, TERM_COLOR_BG); int line_rows = term_hist_line_rows(&t->hist, id, t->cols); - if (seg + 1 < line_rows || (id + 1 == hist_end_id(&t->hist) && hist_is_open(&t->hist))) { - for (int x = got; x < t->cols; x++) - out[x].flags |= CELL_WRAP_PAD; - } + if (seg + 1 < line_rows || (id + 1 == hist_end_id(&t->hist) && hist_is_open(&t->hist))) + set_cells_wrap_pad(out + got, t->cols - got); } return out; } @@ -561,10 +557,10 @@ void term_screen_erase(struct term *t, int mode) int rows = t->rows; int cols = t->cols; const struct srow *slots = t->scr.slot; - int p = t->scr.top ? t->scr.top - 1 : rows - 1; + int p = phys_row(t, rows - 1); for (int y = rows - 1; y >= 0; y--) { const struct srow *r = &slots[p]; - p = p ? p - 1 : rows - 1; + p = (p == 0) ? rows - 1 : p - 1; if (TERM_EXPECT(r->len == 0, 1)) { if (TERM_EXPECT((r->flags & RF_WRAPPED) != 0, 0)) tail_clean = false; @@ -577,8 +573,7 @@ void term_screen_erase(struct term *t, int mode) tail_clean = false; } if (last_active >= 0 && !t->alternate) { - for (int y = 0; y <= last_active; y++) - term_screen_scroll_up(t); + term_screen_scroll_region(t, 0, rows - 1, last_active + 1, false, true); if (tail_clean) { term_hist_view_reset_bottom(&t->view); return; @@ -612,6 +607,63 @@ void term_screen_erase(struct term *t, int mode) } } +static void rotate_and_clear_srows(struct term *t, struct srow *base, int height, + int count, bool down, bool def) +{ + struct srow tmp[64]; + struct srow *saved = (count <= 64) ? tmp : malloc((size_t)count * sizeof(struct srow)); + if (!saved) + return; + + if (!down) { + memcpy(saved, base, (size_t)count * sizeof(struct srow)); + memmove(base, base + count, (size_t)(height - count) * sizeof(struct srow)); + memcpy(base + height - count, saved, (size_t)count * sizeof(struct srow)); + + for (int i = 0; i < height - count; i++) + base[i].version++; + + for (int i = 0; i < count; i++) { + struct srow *dst = &base[height - count + i]; + dst->kind = ROW_CELLS; + dst->uid = ++t->next_row_uid; + dst->version = 1; + if (def) { + dst->len = 0; + dst->flags = 0; + } else { + fill_cells(dst->cells, t->cols, t->cur_fg, t->cur_bg); + dst->len = (uint16_t)t->cols; + dst->flags = RF_TAIL_PADDED; + } + } + } else { + memcpy(saved, base + height - count, (size_t)count * sizeof(struct srow)); + memmove(base + count, base, (size_t)(height - count) * sizeof(struct srow)); + memcpy(base, saved, (size_t)count * sizeof(struct srow)); + + for (int i = 0; i < height - count; i++) + base[count + i].version++; + + for (int i = 0; i < count; i++) { + struct srow *dst = &base[i]; + dst->kind = ROW_CELLS; + dst->uid = ++t->next_row_uid; + dst->version = 1; + if (def) { + dst->len = 0; + dst->flags = 0; + } else { + fill_cells(dst->cells, t->cols, t->cur_fg, t->cur_bg); + dst->len = (uint16_t)t->cols; + dst->flags = RF_TAIL_PADDED; + } + } + } + if (saved != tmp) + free(saved); +} + void term_screen_scroll_region(struct term *t, int top, int bottom, int count, bool down, bool history) { @@ -621,10 +673,93 @@ void term_screen_scroll_region(struct term *t, int top, int bottom, int count, if (count > height) count = height; - if (!down && history && top == 0 && bottom == t->rows - 1) { - while (count-- > 0) - term_screen_scroll_up(t); - return; + /* Whole active screen scroll: optimize using ring rotation. */ + if (top == 0 && bottom == t->rows - 1) { + struct screen *s = term_active_screen(t); + int rows = s->rows; + int orig_top = s->top; + bool def = using_default_colors(t); + + if (!down) { + bool has_hist = history && screen_has_history(t); + if (TERM_EXPECT(t->prompt_y >= 0, 0) && !t->alternate) { + if (history) { + if (t->prompt_y > 0) + t->prompt_y = t->prompt_y > count ? t->prompt_y - count : 0; + } else { + t->prompt_y -= count; + if (t->prompt_y < 0) { + t->prompt_y = -1; + t->has_prompt = false; + } + } + } + for (int i = 0; i < count; i++) { + int p = orig_top + i; + if (p >= rows) p -= rows; + struct srow *row = &s->slot[p]; + if (TERM_EXPECT(has_hist, 1)) + term_screen_push_row(t, row); + else + screen_drop_top(s, row); + if (history) { + screen_new_row(t, row); + } else { + row->kind = ROW_CELLS; + row->uid = ++t->next_row_uid; + row->version++; + if (def) { + row->len = 0; + row->flags = 0; + } else { + fill_cells(row->cells, t->cols, t->cur_fg, t->cur_bg); + row->len = (uint16_t)t->cols; + row->flags = RF_TAIL_PADDED; + } + } + } + int new_top = orig_top + count; + if (new_top >= rows) new_top -= rows; + s->top = new_top; + clamp_selection_after_scroll(t); + t->full_gen++; + return; + } else { + /* Full screen scroll down */ + if (!t->alternate && t->prompt_y >= 0) { + t->prompt_y += count; + if (t->prompt_y >= rows) { + t->prompt_y = -1; + t->has_prompt = false; + } + } + for (int i = 0; i < rows - count; i++) { + int p = orig_top + i; + if (p >= rows) p -= rows; + s->slot[p].version++; + } + for (int i = 0; i < count; i++) { + int p = orig_top + (rows - count) + i; + if (p >= rows) p -= rows; + struct srow *row = &s->slot[p]; + row->kind = ROW_CELLS; + row->uid = ++t->next_row_uid; + row->version = 1; + if (def) { + row->len = 0; + row->flags = 0; + } else { + fill_cells(row->cells, t->cols, t->cur_fg, t->cur_bg); + row->len = (uint16_t)t->cols; + row->flags = RF_TAIL_PADDED; + } + } + int new_top = orig_top - count; + if (new_top < 0) new_top += rows; + s->top = new_top; + t->full_gen++; + return; + } } if (!down && history && top == 0 && !t->alternate) { @@ -640,44 +775,28 @@ void term_screen_scroll_region(struct term *t, int top, int bottom, int count, bool def = using_default_colors(t); if (count == height) { - for (int y = top; y <= bottom; y++) { - struct srow *dst = screen_get_row(s, y); - dst->kind = ROW_CELLS; - dst->uid = ++t->next_row_uid; - dst->version++; - if (def) { - dst->len = 0; - dst->flags = 0; - } else { - fill_cells(dst->cells, t->cols, t->cur_fg, t->cur_bg); - dst->len = (uint16_t)t->cols; - dst->flags = RF_TAIL_PADDED; - } - } - } else { - struct srow tmp[256]; - struct srow *buf = (height <= 256) ? tmp : malloc((size_t)height * sizeof(struct srow)); - if (!buf) - return; - - for (int i = 0; i < height; i++) - buf[i] = *screen_get_row(s, top + i); - - for (int i = 0; i < height; i++) { - int src_i = down ? i - count : i + count; - struct srow *dst = screen_get_row(s, top + i); - if (src_i >= 0 && src_i < height) { - *dst = buf[src_i]; + int start = phys_row(t, top); + if (start + height <= s->rows) { + for (int i = 0; i < height; i++) { + struct srow *dst = &s->slot[start + i]; + dst->kind = ROW_CELLS; + dst->uid = ++t->next_row_uid; dst->version++; - } else { - /* The row takes the buffers of a row that left the region. */ - int dead_i = down ? (height - count + i) : (i - (height - count)); - dst->cells = buf[dead_i].cells; - dst->ascii = buf[dead_i].ascii; - dst->text = buf[dead_i].text; + if (def) { + dst->len = 0; + dst->flags = 0; + } else { + fill_cells(dst->cells, t->cols, t->cur_fg, t->cur_bg); + dst->len = (uint16_t)t->cols; + dst->flags = RF_TAIL_PADDED; + } + } + } else { + for (int y = top; y <= bottom; y++) { + struct srow *dst = screen_get_row(s, y); dst->kind = ROW_CELLS; dst->uid = ++t->next_row_uid; - dst->version = 1; + dst->version++; if (def) { dst->len = 0; dst->flags = 0; @@ -688,8 +807,27 @@ void term_screen_scroll_region(struct term *t, int top, int bottom, int count, } } } - if (buf != tmp) - free(buf); + } else { + int start = phys_row(t, top); + if (start + height <= s->rows) { + rotate_and_clear_srows(t, s->slot + start, height, count, down, def); + } else { + struct srow tmp[256]; + struct srow *buf = (height <= 256) ? tmp : malloc((size_t)height * sizeof(struct srow)); + if (!buf) + return; + int n1 = s->rows - start; + int n2 = height - n1; + memcpy(buf, s->slot + start, (size_t)n1 * sizeof(struct srow)); + memcpy(buf + n1, s->slot, (size_t)n2 * sizeof(struct srow)); + + rotate_and_clear_srows(t, buf, height, count, down, def); + + memcpy(s->slot + start, buf, (size_t)n1 * sizeof(struct srow)); + memcpy(s->slot, buf + n1, (size_t)n2 * sizeof(struct srow)); + if (buf != tmp) + free(buf); + } } if (!t->alternate && t->prompt_y >= top && t->prompt_y <= bottom) { @@ -1047,12 +1185,22 @@ static bool reflow_append(struct reflow_target *r, const struct cell *cells, int int cursor, int saved) { bool has_wide = false; - for (int i = 0; i < len; i++) { - if (cells[i].flags & (CELL_WIDE | CELL_TRAIL | CELL_WRAP_PAD)) { + const uint16_t wide_flags = CELL_WIDE | CELL_TRAIL | CELL_WRAP_PAD; + int i = 0; + for (; i + 4 <= len; i += 4) { + if ((cells[i].flags | cells[i + 1].flags | cells[i + 2].flags | cells[i + 3].flags) & wide_flags) { has_wide = true; break; } } + if (!has_wide) { + for (; i < len; i++) { + if (cells[i].flags & wide_flags) { + has_wide = true; + break; + } + } + } bool ok = has_wide ? reflow_append_wide(r, cells, len, cursor, saved) : reflow_append_span(r, cells, len, cursor, saved); if (!ok) @@ -1104,8 +1252,7 @@ static int pull_history_rows(struct term *t, struct screen *dst_scr, int cols, i if (got < cols) { fill_cells(dst->cells + got, cols - got, TERM_COLOR_FG, TERM_COLOR_BG); if (wrapped) { - for (int x = got; x < cols; x++) - dst->cells[x].flags |= CELL_WRAP_PAD; + set_cells_wrap_pad(dst->cells + got, cols - got); got = cols; } } @@ -1213,8 +1360,11 @@ static bool term_resize_internal(struct term *t, int cols, int rows) pull = pull_history_rows(t, &new_scr, cols, rows - old_rows); int y = pull; + int p_curr = active_scr->top; for (int i = 0; i < old_rows && y < rows; i++, y++) { - struct srow *old_r = screen_get_row(active_scr, i); + struct srow *old_r = &active_scr->slot[p_curr]; + srow_materialize_text(old_r); + p_curr = (p_curr + 1 == old_rows) ? 0 : p_curr + 1; copy_srow_resized(&new_scr.slot[y], old_r, old_cols, cols); } int first_new = y; @@ -1264,11 +1414,14 @@ static bool term_resize_internal(struct term *t, int cols, int rows) } int active = old_rows; - while (active > t->cursor_y + 1 && active > t->saved.y + 1) { - struct srow *sr = screen_get_row(&t->scr, active - 1); - if (sr->len || (sr->flags & RF_WRAPPED)) + int min_active = t->cursor_y >= t->saved.y ? t->cursor_y + 1 : t->saved.y + 1; + int p_scan = phys_row(t, old_rows - 1); + while (active > min_active) { + const struct srow *sr = &t->scr.slot[p_scan]; + if (sr->len != 0 || (sr->flags & RF_WRAPPED) != 0) break; active--; + p_scan = (p_scan == 0) ? old_rows - 1 : p_scan - 1; } struct reflow_target r = { @@ -1298,8 +1451,11 @@ static bool term_resize_internal(struct term *t, int cols, int rows) size_t line_cap = (size_t)old_cols + (size_t)t->saved.x + 2; struct cell *line_buf = grow(&t->resize.line, line_cap * sizeof(struct cell)); + int p_src = t->scr.top; for (int y = 0; y < active; y++) { - struct srow *src = screen_get_row(&t->scr, y); + struct srow *src = &t->scr.slot[p_src]; + srow_materialize_text(src); + p_src = (p_src + 1 == old_rows) ? 0 : p_src + 1; int len = src->len; bool wrapped = (src->flags & RF_WRAPPED) != 0; @@ -1488,7 +1644,11 @@ bool term_resize(struct term *t, struct term_vector2i size) int first_tab = (t->tabs_cap + 7) & ~7; if (first_tab == 0) first_tab = 8; - for (int c = first_tab; c < new_cap; c += 8) + uint64_t tab8 = 1; + int c = first_tab; + for (; c + 8 <= new_cap; c += 8) + memcpy(new_tabs + c, &tab8, sizeof(tab8)); + if (c < new_cap) new_tabs[c] = 1; t->tabs = new_tabs; t->tabs_cap = new_cap; |
