Home > database >  C (OSDev) - How could I shift the contents of a 32-bit framebuffer upwards efficiently?
C (OSDev) - How could I shift the contents of a 32-bit framebuffer upwards efficiently?

Time:12-12

I'm working on writing a hobby operating system. Currently a large struggle that I'm having is attempting to scroll the framebuffer upwards. It's simply a 32-bit linear framebuffer. I have access to a few tools that could be helpful:

  • some of the mem* functions from libc: memset, memcpy, memmove, and memcmp
  • direct access to the framebuffer
  • the width, height, and size in bytes, of said framebuffer
  • a previous attempt that managed to scroll it up a few lines, albeit EXTREMELY slowly, it took roughly 25 seconds to scroll the framebuffer up by 5 pixels

speaking of which, my previous attempt:

for (uint64_t i = 0; i != atoi(numLines); i  ) {
    for (uint64_t j = 0; j != bootboot.fb_width; j  ) {
        for (uint64_t k = 1; k != bootboot.fb_size; k  ) {
            ((uint32_t *)&fb)[k - 1] = ((uint32_t *)&fb)[k];
        }
    }
}

A few things to note about the above:

  • numLines is a variable passed into the function, it's a char * that contains the number of lines to scroll up by, in a string. I eventually want this to be the number of actual text lines to scroll up by, but for now treating this as how many pixels to scroll up by is sufficient.
  • the bootboot struct is provided by the bootloader I use, it contains a few variables that could be of use: fb_width (the width of the framebuffer), fb_height (the height of the framebuffer), and fb_size (the size, in bytes, of the framebuffer)
  • the fb variable that I'm using the address of is also provided by the bootloader I use, it is a single byte that is placed at the first byte of the framebuffer, hence the need to cast it into a uint32_t * before using it.

Any and all help would be appreciated.

CodePudding user response:

If I read the code correctly, what's happening with the triple nested loops is:

  1. For every line to scroll,
  2. For every pixel that the framebuffer is wide,
  3. For every pixel in the entire framebuffer,
  4. Move that pixel backwards by one.

Essentially you're moving each pixel one pixel distance at a time, so it's no wonder it takes so long to scroll the framebuffer. The total number of pixel moves is (numLines * fb_width * fb_size), so if your framebuffer is 1024x768, that's 5*1024*1024*768 moves, which is 4,026,531,840 moves. That's basically 5000 times the amount of work required.

Instead, you'll want to loop over the framebuffer only once, calculate that pixel's start and its end pointer, and only do the move once. Or you can calculate the source, destination, and size of the move once and then use memmove. Here's my attempt at this (with excessive comments):

 // Convert string to integer
uint32_t numLinesInt = atoi(numLines);
 // The destination of the move is just the top of the framebuffer
uint32_t* destination = (uint32_t*)&fb;
 // Start the move from the top of the framebuffer plus however
 // many lines we want to scroll.
uint32_t* source = (uint32_t*)&fb  
    (numLinesInt * bootboot.fb_width);
 // The total number of pixels to move is the size of the
 // framebuffer minus the amount of lines we want to scroll.
uint32_t pixelSize = (bootboot.fb_height - numLinesInt)
    * bootboot.fb_width;
 // The total number of bytes is that times the size of one pixel.
uint32_t byteSize = pixelSize * sizeof(uint32_t);
 // Do the move
memmove(destination, byteSize, source, byteSize);

I haven't tested this, and I'm making a number of assumptions about how your framebuffer is laid out, so please make sure it works before using it. :)

(P.S. Also, if you put atoi(numLines) inside the end condition of the for loop, atoi will be called every time through the loop, instead of once at the beginning like you intended.)

  • Related