diff --git a/kernel/dev/vt.c b/kernel/dev/vt.c index 18c02e5..87d5dca 100644 --- a/kernel/dev/vt.c +++ b/kernel/dev/vt.c @@ -373,12 +373,52 @@ vt_putc(struct vt *v, int glyph) } // Fill a run of cells with the current background, wrapping at the right edge. +// +// The grid is updated cell by cell -- it has to be -- but the *drawing* is +// done as rectangles, one per row span. Blanking a cell through the glyph +// path writes 128 pixels through the generic pixel routine; blanking a whole +// row as one fill writes the same pixels with the bounds checks hoisted out, +// and the console erases to end of screen on every shell prompt. At 160x50 +// that is the difference between a million checked pixel writes per prompt and +// fifty row fills. static void erase_run(struct vt *v, int col, int row, int count) { - while (count-- > 0 && row < v->rows) { - put_blank_at(v, col, row); - if (++col >= v->cols) { col = 0; row++; } + int active = is_active(v); + + while (count > 0 && row < v->rows) { + int n = v->cols - col; // to the end of this row + if (n > count) + n = count; + + // Only draw what actually changes. + // + // This is the whole reason a terminal keeps a grid. `ESC[J` -- erase to + // end of screen -- arrives after every shell prompt, and almost every + // time the screen below the cursor is *already* blank in exactly the + // colours it is being asked to blank to. Drawing it anyway means writing + // four megabytes to a framebuffer that runs at about 128 MB/s, which is + // 32 ms of nothing, on every prompt, and zsh redraws the prompt on every + // keystroke. + // + // The grid already knows. Comparing is free; writing is not. + int dirty = 0; + for (int i = 0; i < n; i++) { + struct vtcell *k = cell_at(v, col + i, row); + if (k->ch != ' ' || k->fg != v->fg || k->bg != v->bg || k->attr != 0) + dirty = 1; + k->ch = ' '; + k->fg = v->fg; + k->bg = v->bg; + k->attr = 0; + } + if (active && dirty) + gfx_fill_rect(col * gfx_char_w(), row * gfx_char_h(), + n * gfx_char_w(), gfx_char_h(), vt_rgb(v->bg)); + + count -= n; + col = 0; + row++; } } @@ -407,11 +447,29 @@ vt_erase_line(struct vt *v, int mode) void vt_clear(struct vt *v) { + int dirty = 0; + + for (int r = 0; r < v->rows; r++) { + for (int c = 0; c < v->cols; c++) { + struct vtcell *k = cell_at(v, c, r); + if (k->ch != ' ' || k->fg != v->fg || k->bg != v->bg || k->attr != 0) { + dirty = 1; + break; + } + } + if (dirty) + break; + } for (int r = 0; r < v->rows; r++) blank_row(v, r); v->cx = v->cy = 0; v->wrap_pending = 0; - vt_repaint(v); + + // A screen fill, not a repaint. vt_repaint draws every cell through the + // glyph path -- eight thousand of them -- to produce a screen that is + // entirely one colour. + if (is_active(v) && dirty) + gfx_clear(vt_rgb(v->bg)); }