]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[fbcon] Avoid redrawing unchanged characters when scrolling
authorMichael Brown <mcb30@ipxe.org>
Fri, 25 Apr 2025 12:24:21 +0000 (13:24 +0100)
committerMichael Brown <mcb30@ipxe.org>
Fri, 25 Apr 2025 12:44:18 +0000 (13:44 +0100)
Scrolling currently involves redrawing every character cell, which can
be frustratingly slow on large framebuffer consoles.  Accelerate this
operation by skipping the redraw for any unchanged character cells.

In the common case that large areas of the screen contain whitespace,
this optimises away the vast majority of the redrawing operations.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/core/fbcon.c

index f2b2ea566c7a3e0bf750af2a0f0546e9ce4b763e..43f73fbac46047737bb1eb882756f3ce31d5577d 100644 (file)
@@ -233,22 +233,46 @@ static void fbcon_redraw ( struct fbcon *fbcon ) {
  * @v fbcon            Frame buffer console
  */
 static void fbcon_scroll ( struct fbcon *fbcon ) {
-       size_t row_len;
+       const struct fbcon_text_cell *old;
+       struct fbcon_text_cell *new;
+       unsigned int xpos;
+       unsigned int ypos;
+       unsigned int character;
+       uint32_t foreground;
+       uint32_t background;
 
        /* Sanity check */
        assert ( fbcon->ypos == fbcon->character.height );
 
        /* Scroll up character array */
-       row_len = ( fbcon->character.width * sizeof ( fbcon->text.cells[0] ) );
-       memmove ( fbcon_cell ( fbcon, 0, 0 ), fbcon_cell ( fbcon, 0, 1 ),
-                 ( row_len * ( fbcon->character.height - 1 ) ) );
-       fbcon_clear ( fbcon, ( fbcon->character.height - 1 ) );
+       new = fbcon_cell ( fbcon, 0, 0 );
+       old = fbcon_cell ( fbcon, 0, 1 );
+       for ( ypos = 0 ; ypos < ( fbcon->character.height - 1 ) ; ypos++ ) {
+               for ( xpos = 0 ; xpos < fbcon->character.width ; xpos++ ) {
+                       /* Redraw character (if changed) */
+                       character = old->character;
+                       foreground = old->foreground;
+                       background = old->background;
+                       if ( ( new->character != character ) ||
+                            ( new->foreground != foreground ) ||
+                            ( new->background != background ) ) {
+                               new->character = character;
+                               new->foreground = foreground;
+                               new->background = background;
+                               fbcon_draw ( fbcon, new, xpos, ypos );
+                       }
+                       new++;
+                       old++;
+               }
+       }
+
+       /* Clear bottom row */
+       fbcon_clear ( fbcon, ypos );
+       for ( xpos = 0 ; xpos < fbcon->character.width ; xpos++ )
+               fbcon_draw ( fbcon, new++, xpos, ypos );
 
        /* Update cursor position */
        fbcon->ypos--;
-
-       /* Redraw all characters */
-       fbcon_redraw ( fbcon );
 }
 
 /**