Frame Buffer problem (UEFI)

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
Neyder
Posts: 7
Joined: Mon Feb 02, 2015 4:48 pm

Frame Buffer problem (UEFI)

Post by Neyder »

Hey !
I'm sorry if the question has already been asked, but almost everything I find is related to VBE which calls bios interrupts for switching between modes, etc.

I have a problem when drawing into the framebuffer provided by uefi, after ExitBootServices().
On VMWare, everything goes fine, I can fill the screen with a color an print text. But on real hardware, and Parallel Desktops, the image is broken.

This is how I fill the entire screen with a color, let say 0xAA0000.
The FB pointer and color are uint32

Code: Select all

addr = fb->base_addr;
sbytes = fb->hrez * fb->vrez;

while (sbytes--)
{
     *addr++ = bg_color;
}
On VMware, the entire screen is filled with the right color, but on Parallel Desktop, I got a strange image with individuals RGB pixels and the text is unaligned.
This is a screenshot of Hello World on AA0000 background
Image

If we look closer, we see RGB pixels
Image

Also, new row is not lfb_start + hrez, but something like hrez/2 + hrez/4 which equals VREZ

The only way to fill the background, is using the same byte for every color, even the alpha one, like 0xAAAAAAAA
When I try to plot a single pixel on a random place, I got 2 pixels on screen. On with the right color, an the other with a random one.
Image

If I fill the background 3px by 3 px using this template :

Code: Select all

*addr++ = 0xAA0000;
*addr++ = 0x00AA00;
*addr++ = 0x0000AA;
I got almost all screen filled with the right color, but with black lines every 3 pixels.
Image

When using 32bit color, like 0xFFAA0000, the "fouth" pixel is rendered, but colors correspond to nothing expected.

The Graphics Mode is set to 1024x768x24 (3 bytes per pixels),
Pixel format is 2 (Bit Mask)
and the bit mask is R= 0xFF0000, G = 0xFF00, B = 0xFF
FB Start = 3221225472, FB Size = 268435456
Scan line = 1024

Any ideas ?
Nable
Member
Member
Posts: 453
Joined: Tue Nov 08, 2011 11:35 am

Re: Frame Buffer problem (UEFI)

Post by Nable »

Neyder wrote: If I fill the background 3px by 3 px using this template :

Code: Select all

*addr++ = 0xAA0000;
*addr++ = 0x00AA00;
*addr++ = 0x0000AA;
What is the exact type of "addr" variable? And why are you sure that current mode is 24bpp (it may be 15/16bpp)?
Neyder
Posts: 7
Joined: Mon Feb 02, 2015 4:48 pm

Re: Frame Buffer problem (UEFI)

Post by Neyder »

Code: Select all

typedef unsigned int uint32_t;

uint32_t *addr;
And I guess its 24bits because UEFI is saying that Bytes Per Pixel = 3.
I'm not very sure how to test depth, I used ConfigurePixelBitMaskFormat() function from FrameBufferBltLib.c
Last edited by Neyder on Mon Feb 02, 2015 7:23 pm, edited 1 time in total.
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: Frame Buffer problem (UEFI)

Post by kzinti »

It looks like you are writing 32 bits 'pixels' to a 24 bpp frame buffer. This can't work. You will need to write 3 bytes per pixels, not 4.

The layout of a 32 bits frame buffer is typically

xx RR GG BB xx RR GG BB xx RR GG BB ...

The layout of a 24 bits frame buffer is typically

RR GG BB RR GG BB RR GG BB ...

I say typically, because the red and blue bytes are often swapped, especially in 24 bits mode.
Nable
Member
Member
Posts: 453
Joined: Tue Nov 08, 2011 11:35 am

Re: Frame Buffer problem (UEFI)

Post by Nable »

Neyder wrote:addr is uint32_t pointer.
Then it ("black lines every 3 pixels") is obvious if you try to reproduce these writes by hand in hexedit.
Try this:

Code: Select all

*addr++ = 0x00AA0000;
*addr++ = 0x0000AA00;
*addr++ = 0xAA0000AA;
Without the last 0xAA you get the following pattern:

Code: Select all

              | addr[0] |  | addr[1] |  | addr[2] |
              v         v  v         v  v         v
0x00000000:   00 00 AA 00  00 AA 00 00  AA 00 00 00
              ^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^
              pixel[0] pixel[1]  pixel[2]  pixel[3] - oops!

Neyder
Posts: 7
Joined: Mon Feb 02, 2015 4:48 pm

Re: Frame Buffer problem (UEFI)

Post by Neyder »

Dude ! It works =D
Thx a lot ! I understand better with your example.
I was assuming that one address = one pixel, because I was using 0xRRGGBB format, but I have 32 bit variable...
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: Frame Buffer problem (UEFI)

Post by gerryg400 »

Why did it work on Vmware ?
If a trainstation is where trains stop, what is a workstation ?
Neyder
Posts: 7
Joined: Mon Feb 02, 2015 4:48 pm

Re: Frame Buffer problem (UEFI)

Post by Neyder »

Because vmware mode was 32 bit depth, here we have 24 bits (3bpp), and we don't have 24bit variable type...
So an address (32bit) is one pixel + 1/3 of the next pixel, instead of one pixel like in 32bit depth (4bytes per pixel)

We need to draw 4 pixels at time using 3 addresses.
This code draw 4 pixels of the same color.

Code: Select all

     *addr++ = bg_color;
     *addr++ = bg_color >> 8;
     *addr++ = bg_color << 8 | bg_color >> 16;
Nable
Member
Member
Posts: 453
Joined: Tue Nov 08, 2011 11:35 am

Re: Frame Buffer problem (UEFI)

Post by Nable »

Neyder wrote: This code draw 4 pixels of the same color.

Code: Select all

     *addr++ = bg_color;
     *addr++ = bg_color >> 8;
     *addr++ = bg_color << 8 | bg_color >> 16;
No, color won't be the same in most cases. You should look at my "picture" once more.
MDenham
Member
Member
Posts: 62
Joined: Sat Nov 10, 2012 1:16 pm

Re: Frame Buffer problem (UEFI)

Post by MDenham »

Nable wrote:
Neyder wrote: This code draw 4 pixels of the same color.

Code: Select all

     *addr++ = bg_color;
     *addr++ = bg_color >> 8;
     *addr++ = bg_color << 8 | bg_color >> 16;
No, color won't be the same in most cases. You should look at my "picture" once more.
The suggested code was for clearing the background, not for general-purpose drawing. (It's also wrong; it leaves part of the second dword unset.)

For general-purpose drawing, assuming you want to work internally with 32-bit colors, it becomes somewhat of a mess (especially if you implement alpha at the same time). I suspect you'd be better off making your framebuffer pointer a char*/uint8* so that you can handle each color individually in this situation as that way your code ends up more readable.
Neyder
Posts: 7
Joined: Mon Feb 02, 2015 4:48 pm

Re: Frame Buffer problem (UEFI)

Post by Neyder »

Well done, thx everybody =)

Code: Select all

// For clearing the background

pixels = fb->width * fb->height;

...

else if (fb->depth == 24)
{
    uint8_t* addr = (uint8_t*)fb->start;
    while (pixels--)
    {
        if (fb->format == PIXEL_BGR)
        {
            *addr++ = (bg_color >> 16) & 255;
            *addr++ = (bg_color >> 8) & 255;
            *addr++ = bg_color & 255;
        }
        else // RGB
        {
            *addr++ = bg_color & 255;
            *addr++ = (bg_color >> 8) & 255;
            *addr++ = (bg_color >> 16) & 255;
        }
    }
}
else if (fb->depth == 32)
{
    uint32_t* addr = fb->start;
    while (pixels--)
    {
        *addr++ = bg_color;
    }
}
But I think I'm gonna use something similar to the EFI BLT Pixel format

Code: Select all

color = { 0xRR, 0xGG, 0xBB, 0xAA }
It will be easier to use with different depths
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Frame Buffer problem (UEFI)

Post by Brendan »

Hi,

Just a quick note...

To get decent performance for 24-bpp, you want to work on 4 pixels at a time and pack them so that you can write three aligned dwords.

For clearing the background (all 4 pixels the same), this means something more like:

Code: Select all

   uint32_t v1 = red << 24 + green << 16 + blue << 8 + red;
   uint32_t v2 = v1 << 8 + green;
   uint32_t v3 = v2 << 8 + blue;
   uint32_t *dest = (uint32_t *)addr;

    while (pixels)
    {
        *dest++ = v1;
        *dest++ = v2;
        *dest++ = v3;
        pixels -= 4;
    }
Of course for 64-bit code you could go one step further and do 8 pixels with three 64-bit writes (or even further and use SSE to do 16 pixels as three 128-bit writes).


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Neyder
Posts: 7
Joined: Mon Feb 02, 2015 4:48 pm

Re: Frame Buffer problem (UEFI)

Post by Neyder »

Nice !

This will fill the background 8 pixels at once in 24 bits depth

Code: Select all

uint64_t* addr = fb->start;
pixels = (fb->width * fb->height) / 8;

// Draw Passes
const uint64_t pass1 = ((uint64_t)green << 56) + ((uint64_t)blue << 48) +
                       ((uint64_t)red << 40) + ((uint64_t)green << 32) +
                       (blue << 24) + (red << 16) + (green << 8) + blue;
const uint64_t pass2 = (pass1 << 8) + red;
const uint64_t pass3 = (pass2 << 8) + green;

while (pixels--)
{
    *addr++ = pass1;
    *addr++ = pass2;
    *addr++ = pass3;
}
And 2 pixels at once in 32 bit depth

Code: Select all

uint64_t* addr = (uint64_t*)fb->start;
pixels = (fb->width * fb->height) / 2;

const uint64_t color = (bg_color << 32) + bg_color;
while (pixels--) *addr++ = color;
I'm checking the pixel format earlier, before checking the color depth, so blue can be red, and red can be blue. code don't need to change here.
I will try SSE for the GUI =D
jnc100
Member
Member
Posts: 775
Joined: Mon Apr 09, 2007 12:10 pm
Location: London, UK
Contact:

Re: Frame Buffer problem (UEFI)

Post by jnc100 »

Out of interest, you may want to support the 32-bit blue-green-red-reserved pixel format as your default, as new machines which pass the Microsoft Hardware Compatibility Kit testing guidelines are guaranteed to support this mode (reference) and is thus the closest thing you'll find to a standard in the UEFI world. This removes the calculations you need to do to to support 24 bpp.

You'd typically iterate through the available modes (using the QueryMode and Mode->MaxMode members) and find the one which most closely fits your needs.

Regards,
John.
Neyder
Posts: 7
Joined: Mon Feb 02, 2015 4:48 pm

Re: Frame Buffer problem (UEFI)

Post by Neyder »

There is something strange with Parallels Desktop
Even if EFI is reporting that the selected mode pixel format is 1 (32bit BGR Reserved), the framebuffer seems to be 24bpp
My 32bpp code works on VMWare and my PC, but on Parallels, it will works only if I force 24bpp drawing.

When the mode pixel format is 2 (BitMask) I can check for the depth with ConfigurePixelBitMaskFormat() from FrameBufferBltLib.c
I'm not sure if it really works, but it successfully reported 3bpp

Is there a better way, other than Info->PixelFormat to check for color depth ?
I'm using gnu-efi

The only solution I found for now, is using the mode provided at boot time and to not change it.
It can be a bug in the Parallels firmware, maybe I should ignore it...
I'll try with other VM

Edit : Works well with VirtualBox too...
Post Reply