Page 1 of 1

Framebuffer Confusion

Posted: Tue Mar 09, 2021 5:23 am
by finarfin
Hi guys,

i've started implementing a basic framebuffer support for my kernel experiments.

I managed to set-up a framebufer using multiboot and grub, and also get all the information i need back from the bootloader, and also i was able to draw couple of pixels as well (after mapping the framebuffer address in the virtuall address space! :) )

Btw going through all the documentation i found that more questions than answers are rising, so i hope to find more help in the forum:
  • Many tutorial use VESA/VBE/Framebuffer terms, often in the same sentence, are the the same thing? Copmatible?
  • In the framebuffer multiboot info tag there is the field framebuffer_type that depending on the value tells what type of color_info are being used, and from what i can see it will implicitly tell how to draw a pixel on the screen, because if i'm not wrong it will be different from each type,is that correct?
  • Referring to the point above do i need to support all these three types? Or what is the most common (at least the first that i should support)
  • Talking about the color_info, what is going to influence the buffer_type? the chosen resolution? depth? Something else?
  • So when i draw a character on the screen probably is going to depend on the color_info? or i am totally confused? And is there a maximum resolution supported by the FB or it depends only on the hardware (i.e. fb can support resolutions like 920x1080) ?
  • I'm reading also about GOP, so question is: is grub querying uefi boot while setting/getting framebuffer data?
  • https://wiki.osdev.org/Drawing_In_Protected_Mode#Color For example shows two different way of plotting a pixel depending the mode info block. So question is: is possible to write a common putpixel function, or i need as many putpixel functions as supported modes i want to make available?
  • Multiboot2 framebuffer info tag data is based on https://wiki.osdev.org/Getting_VBE_Mode_Info? Or not?
  • here: https://wiki.osdev.org/PC_Screen_Font#D ... _character one of the variable declared is "scanline", and it states: in the comment
    /* number of bytes in each line, it's possible it's not screen width * bytesperpixel! */
    so is there a way to compute it? What are the common cases? Am i forced to read the VBE Mode info as explained here: https://wiki.osdev.org/Getting_VBE_Mode_Info ? Or again am i confusing things?
As you can see i'm probably very confused by all that stuff.
And i noticed (but maybe is just me), that the documentation available lack clarity on the stuff above. Probably i will have more questions coming, but these are the first where i'm struggling to find an answer!

Re: Framebuffer Confusion

Posted: Tue Mar 09, 2021 10:56 am
by bzt
finarfin wrote:Many tutorial use VESA/VBE/Framebuffer terms, often in the same sentence, are the the same thing? Copmatible?
VESA is the standard (what functions, how to call them), BIOS VBE is the implementation of that standard with a standardized ABI from a manufacturer (so you actually use VBE services to access VESA functions). Before VBE, all manufacturers used different ABI to implement VESA functionality (called SVGA typically through IO registers), VBE made the calling convention standardized on all cards (through BIOS services).

Framebuffer is a different thing, it's a more general term, it means the screen is described by memory mapped IO. Other standards (like GOP) for example have different functions, but might have the same framebuffer (after all, they could use the same video card with the same hardware circuitry but with different ROMs).
finarfin wrote:In the framebuffer multiboot info tag there is the field framebuffer_type that depending on the value tells what type of color_info are being used, and from what i can see it will implicitly tell how to draw a pixel on the screen, because if i'm not wrong it will be different from each type,is that correct?
Yes, that byte tells you how a packed pixel looks like (there are other modes, like palette, but nobody uses those these days). So basically you have 15 bits (5r-5g-5b and 5b-5g-5r), 16 bits (5r-6g-5b and 5b-6g-5r), 24 bits (don't use that, very unoptimal), 32 bits (8r-8g-8b and 8b-8g-8r, the most significant byte not used).
finarfin wrote:Referring to the point above do i need to support all these three types? Or what is the most common (at least the first that i should support)
Nope, it's enough if you support 32 bit types. The most common is 8b-8g-8r, where the blue channel is in the least significant byte.
finarfin wrote:Talking about the color_info, what is going to influence the buffer_type? the chosen resolution? depth? Something else?
Pitch. That's the number of bytes in memory for a row. It's often the same as width times bytes per pixel, but not necessarily, so watch out! For example 800x600 32 bit might have not 3200 bytes in a row, but often 4096 instead. This means in the framebuffer first come the first line's 3200 bytes, then 896 bytes unused, then the second line's 3200 bytes, another 896 bytes unused etc.
finarfin wrote:So when i draw a character on the screen probably is going to depend on the color_info? or i am totally confused? And is there a maximum resolution supported by the FB or it depends only on the hardware (i.e. fb can support resolutions like 920x1080) ?
Depends on the hardware only. VESA is capable of describing video modes up to 65535 x 65535.
finarfin wrote:I'm reading also about GOP, so question is: is grub querying uefi boot while setting/getting framebuffer data?
Not sure, I don't use Grub. But my loader for example does that, it sets up the same framebuffer for your kernel on all platforms. If the firmware supports VESA then with that, if it supports GOP then with that.
finarfin wrote:https://wiki.osdev.org/Drawing_In_Protected_Mode#Color For example shows two different way of plotting a pixel depending the mode info block. So question is: is possible to write a common putpixel function, or i need as many putpixel functions as supported modes i want to make available?
Possible, but very ineffective and slow. For performance reasons it's better if you have separate implementations.
finarfin wrote:Multiboot2 framebuffer info tag data is based on https://wiki.osdev.org/Getting_VBE_Mode_Info? Or not?
Yes. That's why it could be problematic on UEFI with GOP and impossible to use on other firmware. Things like window, banks, capability bits etc. are not portable. What's the smallest common denominator among different platform is: width, height, pitch, and the packed pixel format (not the way how it's reported, but how it's actually used) and linear framebuffer (that is, the whole framebuffer is accessible at a linearly addressable memory region). All the other fields are pretty much VESA specific, but some could be calculated (like number of characters on screen for example).
finarfin wrote:here: https://wiki.osdev.org/PC_Screen_Font#D ... _character one of the variable declared is "scanline", and it states: in the comment
/* number of bytes in each line, it's possible it's not screen width * bytesperpixel! */
so is there a way to compute it? What are the common cases? Am i forced to read the VBE Mode info as explained here: https://wiki.osdev.org/Getting_VBE_Mode_Info ? Or again am i confusing things?
Nope, you cannot compute that since it's the property of the card. You can query it though (pitch and scanline are the same things, different libraries call that differently). For example GOP's Blt function does not calculate with scanline, it needs the gap (not 4096 from the example above, but the 896). However these are all the same, all used to describe how many bytes encode a row on screen.
finarfin wrote:As you can see i'm probably very confused by all that stuff.
And i noticed (but maybe is just me), that the documentation available lack clarity on the stuff above. Probably i will have more questions coming, but these are the first where i'm struggling to find an answer!
No, I don't think that the documentation would lack clarity. Video modes evolved a lot over the years, so this is a huge topic, and the wiki had to evolve with that.
finarfin wrote:Elen síla lúmenn' omentielvo
Pedo melon a minno.

Cheers,
bzt

Re: Framebuffer Confusion

Posted: Tue Mar 09, 2021 2:14 pm
by finarfin
Hi bzt, many thanks for your detailed reply!
bzt wrote:
finarfin wrote:In the framebuffer multiboot info tag there is the field framebuffer_type that depending on the value tells what type of color_info are being used, and from what i can see it will implicitly tell how to draw a pixel on the screen, because if i'm not wrong it will be different from each type,is that correct?
Yes, that byte tells you how a packed pixel looks like (there are other modes, like palette, but nobody uses those these days). So basically you have 15 bits (5r-5g-5b and 5b-5g-5r), 16 bits (5r-6g-5b and 5b-6g-5r), 24 bits (don't use that, very unoptimal), 32 bits (8r-8g-8b and 8b-8g-8r, the most significant byte not used).
finarfin wrote:Referring to the point above do i need to support all these three types? Or what is the most common (at least the first that i should support)
Nope, it's enough if you support 32 bit types. The most common is 8b-8g-8r, where the blue channel is in the least significant byte.

Ok so technically is 24bit used.
bzt wrote:
finarfin wrote:https://wiki.osdev.org/Drawing_In_Protected_Mode#Color For example shows two different way of plotting a pixel depending the mode info block. So question is: is possible to write a common putpixel function, or i need as many putpixel functions as supported modes i want to make available?
Possible, but very ineffective and slow. For performance reasons it's better if you have separate implementations.
So since i can't foresee it at compile time, i need to have kind of putpixel wrapper that based on the configuration is going to use the right function, or something like during kernel setup, initialize just a pointer to a function with the correct putpixel function (i'm writing my kernel in C) and use it everytime i need to draw something? Something like that? Sounds reasonable?
bzt wrote:
finarfin wrote:here: https://wiki.osdev.org/PC_Screen_Font#D ... _character one of the variable declared is "scanline", and it states: in the comment
/* number of bytes in each line, it's possible it's not screen width * bytesperpixel! */
so is there a way to compute it? What are the common cases? Am i forced to read the VBE Mode info as explained here: https://wiki.osdev.org/Getting_VBE_Mode_Info ? Or again am i confusing things?
Nope, you cannot compute that since it's the property of the card. You can query it though (pitch and scanline are the same things, different libraries call that differently). For example GOP's Blt function does not calculate with scanline, it needs the gap (not 4096 from the example above, but the 896). However these are all the same, all used to describe how many bytes encode a row on screen.
So you say i need to query it (i suppose you mean using the vbe modeinfo) and then you also say that pitch and scanline are the same things, so since multiboot is returning me the pitch, i can use the value from multiboot correct? and wherever i found scanline replace it with pitch right? (i just found the definition of ptich)
bzt wrote:
finarfin wrote:Elen síla lúmenn' omentielvo
Pedo melon a minno.

Cheers,
bzt
Thanks!
Annon allen!

Re: Framebuffer Confusion

Posted: Tue Mar 09, 2021 3:34 pm
by Korona
Technically, VESA is not the standard itself but the committee designing the VBE standard. VESA is also responsible for other standards such as DDC, EDID and DisplayPort: https://vesa.org/.

Re: Framebuffer Confusion

Posted: Tue Mar 09, 2021 6:48 pm
by Octocontrabass
finarfin wrote:Many tutorial use VESA/VBE/Framebuffer terms, often in the same sentence, are the the same thing? Copmatible?
VESA is a standards organization. VBE (VESA BIOS Extensions) is a standard for configuring a SVGA graphics card using BIOS calls. The framebuffer is where you write pixels to put them on the screen.
finarfin wrote:Referring to the point above do i need to support all these three types? Or what is the most common (at least the first that i should support)
Type 1 is the most common. Type 0 is usually rare unless you specifically request it. Type 2 is text mode.
finarfin wrote:Talking about the color_info, what is going to influence the buffer_type? the chosen resolution? depth? Something else?
If you pick text mode, you get type 2. Otherwise, it's controlled by depth: 8 or less is type 0, more than 8 is type 1.
finarfin wrote:So when i draw a character on the screen probably is going to depend on the color_info? or i am totally confused? And is there a maximum resolution supported by the FB or it depends only on the hardware (i.e. fb can support resolutions like 920x1080) ?
You'll use color_info to determine which values to write to get the color you want. The maximum resolution is limited by whatever the hardware and firmware both support. Most type 1 framebuffers will have the same color_info, so you can write optimized routines for the most common cases.
finarfin wrote:I'm reading also about GOP, so question is: is grub querying uefi boot while setting/getting framebuffer data?
If GRUB is loaded by UEFI, yes.
finarfin wrote:https://wiki.osdev.org/Drawing_In_Protected_Mode#Color For example shows two different way of plotting a pixel depending the mode info block. So question is: is possible to write a common putpixel function, or i need as many putpixel functions as supported modes i want to make available?
It's possible to make a generic putpixel function, but it will be very slow. Even if you make an optimized version for one particular pixel format, it'll still be pretty slow since you have an entire function call for each pixel.
finarfin wrote:Multiboot2 framebuffer info tag data is based on https://wiki.osdev.org/Getting_VBE_Mode_Info? Or not?
Only if the bootloader used VBE.
finarfin wrote:here: https://wiki.osdev.org/PC_Screen_Font#D ... _character one of the variable declared is "scanline",
That's the framebuffer pitch. Multiboot2 includes that information.

finarfin wrote:Ok so technically is 24bit used.
It's uncommon, but you may encounter it.
finarfin wrote:So since i can't foresee it at compile time, i need to have kind of putpixel wrapper that based on the configuration is going to use the right function, or something like during kernel setup, initialize just a pointer to a function with the correct putpixel function (i'm writing my kernel in C) and use it everytime i need to draw something? Something like that? Sounds reasonable?
Sounds reasonable to me. You might still want a generic function you can use when you haven't yet written the optimized versions for a particular pixel format.
finarfin wrote:So you say i need to query it (i suppose you mean using the vbe modeinfo) and then you also say that pitch and scanline are the same things, so since multiboot is returning me the pitch, i can use the value from multiboot correct? and wherever i found scanline replace it with pitch right? (i just found the definition of ptich)
Right. Keep in mind the pitch is in bytes, not pixels.

Re: Framebuffer Confusion

Posted: Tue Mar 09, 2021 7:22 pm
by bzt
finarfin wrote:Ok so technically is 24bit used.
Well yes, but it's unfortunate to call it like this, because there's also a 24 bit mode (don't use that). Let's call it 32 bit (where only 24 bits are used, and 8 is there for padding only). When you start to handle window buffers, you'll need an alpha channel for transparent windows. Then you'll realize how benefitial it is that the framebuffer has an extra byte so that you don't have to make strange offset calculations and word+byte or byte+word copies, instead a simple repne movsd will do which is also a lot faster.
finarfin wrote:So since i can't foresee it at compile time, i need to have kind of putpixel wrapper that based on the configuration is going to use the right function, or something like during kernel setup, initialize just a pointer to a function with the correct putpixel function (i'm writing my kernel in C) and use it everytime i need to draw something? Something like that? Sounds reasonable?
That's one way to do it. But if you think about it, if one pixel would be 32 bits, then you'll use the same code no matter what.

Here's a code example (just for demonstration purposes only, I haven't tested it):

Code: Select all

unsigned int rgb_to_pixel(int r,int g,int b)
{
    if (red_shift >= 0) r <<= red_shift; else r >>= -red_shift;
    if (green_shift >= 0) g <<= green_shift; else g >>= -green_shift;
    if (blue_shift >= 0) b <<= blue_shift; else b >>= -blue_shift;
    r &= red_mask;
    g &= green_mask;
    b &= blue_mask;
    return r | g | b;
}

void drawline(int x0, int y0, int x1, int x2, unsigned int fg) { ... }
void drawbox(int x, int y, int w, int h, unsigned int fg) { ... }
And then all the other functions (drawline, drawbox, etc.) just use the unsigned int pixel returned by this function in "fg", they don't have to know nor care about the actual channel order.

You can also use special double buffering: there window has a fixed pixel format, and the visual has another pixel format (same or could be different). The window can handle its own pixels with the same code (like say 32 bit RGBA), and there's a special blitting function that copies to the visual (the framebuffer) converting between pixel formats. The advantage is, you'll only need one blitter per visual pixel format (that's 2 functions for 32 bit, and 6/7 functions if you also want to support hicolor). You can optimize that blitter to the extreme, and you only need to select the blitter function once in run-time during initialization (again, for demonstration only, not tested code):

Code: Select all

void (*blitter_t)(int x, int y, int w, int h);
blitter_t *blitter;

void init_fb() {
  switch(fb->format) {
    /* speed optimized versions */
    case PIXEL_RGB_15: blitter = blit_rgba32_to_rgb555; break;
    case PIXEL_BGR_15: blitter = blit_rgba32_to_bgr555; break;
    case PIXEL_RGB_16: blitter = blit_rgba32_to_rgb565; break;
    case PIXEL_BGR_16: blitter = blit_rgba32_to_bgr565; break;
    case PIXEL_RGB_32: blitter = blit_rgba32_to_rgb888; break;
    case PIXEL_BGR_32: blitter = blit_rgba32_to_bgr888; break;
    /* fallback using shifts and masks, slow, but but can handle any format */
    default: blitter = blit_rgba32_to_somethingdefinedbybitmasks; break;
  }
}

void window_flush()
{
  /* copy from window buffer to framebuffer */
  (*blitter)(0, 0, fb->width, fb->height);
}
With this, your code won't need to know the pixel format in compilation time, all your drawing primitives can simply use RGBA 32 bit. (And if you limit the query to 32 bit modes only, then you'll only need 2 blitters: rgba and bgra). This is quite different to setting up a putpixel function dynamically, because that would disallow compiler optimizations in the drawing primitive functions, while this does not interfere with the compiler's opts. With multiple windows, you'll have to copy the window buffers to the screen eventually (called compositing), so that's not a drawback.
finarfin wrote:So you say i need to query it (i suppose you mean using the vbe modeinfo)
Yes, exactly. It also returns the channel shifts and masks I talked about above. It's the same for GOP, but there it's a bit more trickier because there are more formats (to describe the pixel format). For example, in this tutorial 800x600 was reported with PixelBlueGreenRedReserved8BitPerColor format, while 1024x768 reported in PixelBitMask format. Despite of the different description, both are describing the same BGRA 32 bit as the packed pixel format (see the calculated bitmasks on the screenshot).
finarfin wrote:and then you also say that pitch and scanline are the same things, so since multiboot is returning me the pitch, i can use the value from multiboot correct? and wherever i found scanline replace it with pitch right? (i just found the definition of ptich)
Well technically yes. However reading through lots and lots of specs and docs I've noticed that if the size of the row is given in pixels, it's more likely called scanline, and if it's given in bytes, then it's more likely called pitch. But this isn't a rule, just my observation, I know about libraries that use byte based scanline and pixel based pitch. The point is, the size of one row is just as important argument like the screen's width or height for example.
(Ralf Brown's calls it scan line, but VBE actually reports it in bytes and not pixels for example)
Korona wrote:Technically, VESA is not the standard itself but the committee designing the VBE standard.
I stand corrected, that's right. Regardless it is the BIOS VBE that you're interfacing with.

Cheers,
bzt