OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 11:28 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 8 posts ] 
Author Message
 Post subject: Problems with character rendering
PostPosted: Wed May 25, 2022 11:23 pm 
Offline

Joined: Sat Apr 30, 2022 5:57 am
Posts: 18
The problem is that nothing is displayed whatsoever. I've tried both normally callling the terminal_writestring() function,
and directly calling terminal_putc(), and neither display anything. Here's the relevant code & a link to the Github repository.

For context, this is in a UEFI environment after ExitBootServices has been called.

bootloader_tty.h:
Code:
#include "font.h"

uint64_t* framebuffer_addr = 0;
uint32_t pitch = 0;
int framebuffer_width = 0;
int framebuffer_height = 0;

int cursorX = 0;
int cursorY = 0;

int consoleHeight = -1;
int consoleWidth = -1;

// Initilizes the "terminal" with the given framebuffer address and pitch
void terminal_initialize(uint64_t* _framebuffer_addr, uint32_t _pitch, int width, int height)
{
    framebuffer_addr = _framebuffer_addr;
    pitch = _pitch;
    framebuffer_width = width;
    framebuffer_height = height;

    consoleHeight = framebuffer_height / 8; // Console height is framebuffer height / 8 because the characters are 8 pixels in size
    consoleWidth = framebuffer_width / 8; // Console width is the same
}

// Prints character 'c' at X, Y
void terminal_putc(char c, int x, int y, int fgcolor)
{
   uint64_t *dest;
   uint64_t *dest32;
   unsigned char *src;
   int row;
   uint32_t fgcolor32;

   fgcolor32 = fgcolor | (fgcolor << 8) | (fgcolor << 16) | (fgcolor << 24);
   src = &font[0] + c * 16;
   dest = framebuffer_addr + y * (pitch * 32) + x;
   for(row = 0; row < 8; row++) {
      if(*src != 0) {
         uint32_t mask_low = font_mask[*src][0];
         uint32_t mask_high = font_mask[*src][1];
         dest32 = dest;
         dest32[0] = (dest[0] & ~mask_low) | (fgcolor32 & mask_low);
         dest32[1] = (dest[1] & ~mask_high) | (fgcolor32 & mask_high);
      }
      src++;
      dest += (pitch * 32);
   }
}

// Writes the string `data` of length `length` to the "terminal"
void terminal_write(const char* data, size_t length)
{
    if (consoleWidth == -1 || consoleWidth == -1)
    {
        // Console hasn't been initilized, display error
        // TODO: Actually display error

        return;
    }

    for (size_t i = 0; i < length; i++)
    {
        char c = data[i];

        if (c == '\n')
        {
            cursorY++;
            break;
        }

        terminal_putc(c, cursorX * 8, cursorY * 8, 0xFF);

        cursorX++;

        if (cursorX > consoleWidth)
        {
            cursorX = 0;
            cursorY++;
        }
    }
   
}

// Writes the string `data` to the "terminal"
void terminal_writestring(const char* data)
{
   terminal_write(data, strlen(data));
}


The way I'm calling the functions:
Code:
    // Initilize the terminal so printing is possible
    terminal_initialize((uint64_t*)gop->Mode->FrameBufferBase, gop->Mode->Info->PixelsPerScanLine, gop->Mode->Info->HorizontalResolution, gop->Mode->Info->VerticalResolution);

    terminal_putc('!', 100, 100, 0xFF);

    terminal_writestring("Loading kernel...\n");


Github repo


Top
 Profile  
 
 Post subject: Re: Problems with character rendering
PostPosted: Thu May 26, 2022 1:45 am 
Offline
Member
Member

Joined: Mon Jul 05, 2021 6:57 pm
Posts: 118
I don't see how this can work:

Code:
   for(row = 0; row < 8; row++) {
      if(*src != 0) {
         uint32_t mask_low = font_mask[*src][0];
         uint32_t mask_high = font_mask[*src][1];
         dest32 = dest;
         dest32[0] = (dest[0] & ~mask_low) | (fgcolor32 & mask_low);
         dest32[1] = (dest[1] & ~mask_high) | (fgcolor32 & mask_high);
      }
      src++;
      dest += (pitch * 32);
   }


You're looping through 8 rows, but what about the 8 columns? It looks like you're setting 2 pixels per row:

Code:
         dest32[0] = (dest[0] & ~mask_low) | (fgcolor32 & mask_low);
         dest32[1] = (dest[1] & ~mask_high) | (fgcolor32 & mask_high);


Shouldn't you be looping through bits in the mask, for example, and setting one pixel per bit? It's not clear to me what 'font_mask' is either nor what is stored in 'font'. Typically you'd have a single array with the bitmask defining the shape of each character (it could be a 2-dimensional array, but having two arrays seems odd).


Top
 Profile  
 
 Post subject: Re: Problems with character rendering
PostPosted: Thu May 26, 2022 1:48 am 
Offline
Member
Member

Joined: Wed Mar 30, 2011 12:31 am
Posts: 676
The code you blindly copied from the wiki and which was explained to you in your other thread is for 8bpp framebuffers. UEFI is absolutely not going to give you one of those. There are also bugs in the original code on the wiki that haven't been addressed.

_________________
toaruos on github | toaruos.org | gitlab | twitter | bim - a text editor


Top
 Profile  
 
 Post subject: Re: Problems with character rendering
PostPosted: Thu May 26, 2022 2:04 am 
Offline

Joined: Sat Apr 30, 2022 5:57 am
Posts: 18
klange wrote:
The code you blindly copied from the wiki and which was explained to you in your other thread is for 8bpp framebuffers. UEFI is absolutely not going to give you one of those. There are also bugs in the original code on the wiki that haven't been addressed.


That's pretty much what I expected, but honestly I have no clue how I could get a version that works for the 32bpp framebuffer that UEFI gives me on my machine. I tried to understand the principle behind the optimization, but it just makes no sense to me. Of course, that's probably because I don't understand some core concept of what exactly the code is doing.


Top
 Profile  
 
 Post subject: Re: Problems with character rendering
PostPosted: Thu May 26, 2022 2:45 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 4591
Location: Chichester, UK
Possibly best to stick with the unoptimised version until you are in a position to understand the optimised code.

Copy-pasting code that you don't understand is always going to lead to tears before bedtime.


Top
 Profile  
 
 Post subject: Re: Problems with character rendering
PostPosted: Thu May 26, 2022 3:21 am 
Offline
Member
Member

Joined: Mon Jul 05, 2021 6:57 pm
Posts: 118
ThatCodingGuy89 wrote:
That's pretty much what I expected, but honestly I have no clue how I could get a version that works for the 32bpp framebuffer that UEFI gives me on my machine. I tried to understand the principle behind the optimization, but it just makes no sense to me. Of course, that's probably because I don't understand some core concept of what exactly the code is doing.

Oh I see, you've borrowed code for 8bpp. At least the code makes sense in that context.

But, not unsurprisingly, you can't use an optimised technique for blitting fonts to 8bpp framebuffers for doing the same to a 32bpp framebuffer. I suggest just sticking with the simpler form of the code which (assuming your "putpixel" works) will work correctly regardless. You're unlikely to find that the speed is a problem at this stage.


Top
 Profile  
 
 Post subject: Re: Problems with character rendering
PostPosted: Sat May 28, 2022 10:16 am 
Offline
Member
Member

Joined: Wed Aug 30, 2017 8:24 am
Posts: 1593
You know, it would probably be better to understand what it is you are doing.

I imagine you have some font that assigns a monochrome 8x16 picture to each code point. So for capital A you would start with something like
Code:
   --------
   --------
   --------
   --####--
   -#----#-
   -#----#-
   -#----#-
   -#----#-
   -######-
   -#----#-
   -#----#-
   -#----#-
   -#----#-
   --------
   --------
   --------
and end at
Code:
static const uint8_t cap_a[16] = {0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00};

Now what do you do with that? What do you actually want? You want to take eight pixels in a row and paint each of them white if the corresponding bit in the font is set, else paint them black (yes, yes, more generally, foreground and background color, respectively, but let us start simply for now). For 8bpp, 0xFF is a white pixel and 0x00 is a black one, but for 32bpp, it is 0xFFFFFFFF and 0x00000000 respectively. So on an 8bpp framebuffer, you want to turn each byte of font data into eight bytes of framebuffer data, thus turning each byte into 64 bits. But on a 32bpp FB, you want to turn each byte into 32 bytes of FB data.

So, for a 32-bpp FB, the easiest is probably to model the FB as a pitch x height array of 32-bit units. The nice thing about pointers into arrays is also that they are also simultaneously spans of the array, so you could write this like something like:
Code:
void render_char_32bpp(uint32_t *fb, size_t pix_x, size_t pix_y, const uint8_t font[static 16], size_t fb_pitch)
{
  uint32_t *line = fb + pix_y * fb_pitch + pix_x;
  for (size_t i = 0; i < 16; i++) {
    for (size_t j = 0; j < 8; j++) {
      line[j] = ((font[i] >> (7-j)) & 1)? WHITE_PIXEL : BLACK_PIXEL;
    }
    line += fb_pitch;
  }
}
Of course, that is nowhere near the end. For one, you could save the X and Y parameters by making the initial calculation of "line" external and handing that over to the function to start with. That way, a possible string rendering function would be able to just keep track of that pointer itself, and would only have to advance one pointer between the characters. Also, the function should probably stop rendering when reaching FB width or height. As it stands, it must be called with all parameters firmly in bounds, or else it'll write all over some other memory. Although, if the callers can guarantee that they will only ever call the function with correct parameters then that checking will not be necessary.

That routine above is the principle idea for all framebuffers, the only difference is how you model the FB. In a 32bpp FB, it might be as array of 32-bit units. In an 8bpp FB, you might use an array of 8-bit units. Only a minor change is needed to also give it foreground and background colors as parameters, rather than hardcoding white and black in these roles, and then you have your working version.

I suspect it might be fast enough that way already, because any firmware worth its salt is going to set the memory type for the frame buffers as write-combining. Thus it really doesn't matter much if you access everything as 8-bits, 16-bits, or 32-bits, it's getting combined to 64 bytes anyway.

_________________
Carpe diem!


Top
 Profile  
 
 Post subject: Re: Problems with character rendering
PostPosted: Sat May 28, 2022 11:31 am 
Offline

Joined: Sat Apr 30, 2022 5:57 am
Posts: 18
nullplan wrote:
You know, it would probably be better to understand what it is you are doing.

I imagine you have some font that assigns a monochrome 8x16 picture to each code point. So for capital A you would start with something like
Code:
   --------
   --------
   --------
   --####--
   -#----#-
   -#----#-
   -#----#-
   -#----#-
   -######-
   -#----#-
   -#----#-
   -#----#-
   -#----#-
   --------
   --------
   --------
and end at
Code:
static const uint8_t cap_a[16] = {0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00};

Now what do you do with that? What do you actually want? You want to take eight pixels in a row and paint each of them white if the corresponding bit in the font is set, else paint them black (yes, yes, more generally, foreground and background color, respectively, but let us start simply for now). For 8bpp, 0xFF is a white pixel and 0x00 is a black one, but for 32bpp, it is 0xFFFFFFFF and 0x00000000 respectively. So on an 8bpp framebuffer, you want to turn each byte of font data into eight bytes of framebuffer data, thus turning each byte into 64 bits. But on a 32bpp FB, you want to turn each byte into 32 bytes of FB data.

So, for a 32-bpp FB, the easiest is probably to model the FB as a pitch x height array of 32-bit units. The nice thing about pointers into arrays is also that they are also simultaneously spans of the array, so you could write this like something like:
Code:
void render_char_32bpp(uint32_t *fb, size_t pix_x, size_t pix_y, const uint8_t font[static 16], size_t fb_pitch)
{
  uint32_t *line = fb + pix_y * fb_pitch + pix_x;
  for (size_t i = 0; i < 16; i++) {
    for (size_t j = 0; j < 8; j++) {
      line[j] = ((font[i] >> (7-j)) & 1)? WHITE_PIXEL : BLACK_PIXEL;
    }
    line += fb_pitch;
  }
}
Of course, that is nowhere near the end. For one, you could save the X and Y parameters by making the initial calculation of "line" external and handing that over to the function to start with. That way, a possible string rendering function would be able to just keep track of that pointer itself, and would only have to advance one pointer between the characters. Also, the function should probably stop rendering when reaching FB width or height. As it stands, it must be called with all parameters firmly in bounds, or else it'll write all over some other memory. Although, if the callers can guarantee that they will only ever call the function with correct parameters then that checking will not be necessary.

That routine above is the principle idea for all framebuffers, the only difference is how you model the FB. In a 32bpp FB, it might be as array of 32-bit units. In an 8bpp FB, you might use an array of 8-bit units. Only a minor change is needed to also give it foreground and background colors as parameters, rather than hardcoding white and black in these roles, and then you have your working version.

I suspect it might be fast enough that way already, because any firmware worth its salt is going to set the memory type for the frame buffers as write-combining. Thus it really doesn't matter much if you access everything as 8-bits, 16-bits, or 32-bits, it's getting combined to 64 bytes anyway.


Ohhh. That makes so much more sense than when I was staring at the code attempting to understand it. Still going to stick to the version I have working until I notice a actual major performance hit, because the more complex I make things, the more difficult it's going to be to understand my own code later.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 8 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: No registered users and 83 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group