2

在基于帧缓冲区的控制台中渲染字符的最快方法是什么?我正在使用XNU发行版中的iso_font.h字体。

现在我正在使用这段代码来渲染一个字符,但它似乎效率不高:

px = px* ISO_CHAR_WIDTH;
py = py* (ISO_CHAR_HEIGHT-1);

for (int i = 0; i < 15; i += 1) 
{
    int sym = iso_font[c*16+i];

    int x = px;
    int y = py + i;

    for (int ii =0; ii < 8; ii++) 
    {
        x+=1;
        if ((sym & (1 << ii)))
        {
            fb_set_px(x,y,fg);
        }
        else 
        {
            fb_set_px(x,y,bg);
        }

    }   
}

而且我还想知道是否可以简化此代码:

void fb_set_px(x,y,hex){
    void*ptr = ((_base + (_bpr*y) + (_bpe*x)));
    unsigned int *p = (unsigned int *) ptr;
    *p=hex;
}

直到有太多行并且我需要重新绘制整个控制台(以滚动)时,它是不错的,此时会有很大的延迟。

4

2 回答 2

4

A few things that come to mind (this takes me back to the old DOS days!):-

1) Use incremental addressing for writing pixels:

  p = calculate address of x,y
    for line = 0 to 15
      for column = 0 to 7
        write to p
        increment p
      end
      p += stride - 8 (stride = distance in memory between vertically adjacent pixels)
    end

2) Eliminate the if in the inner loop:

 draw pixel (fg + (bg - fg) & (((sym >> column) & 1) - 1)

3) Use any help from the OS. This might be hardware acceleration for example.

4) When scrolling, don't redraw all the characters, just memmove the portion of the screen that will remain. e.g. do a memmove from line 1 to line 0 for number of lines - 1. Then clear the exposed area.

|-------|                             |-------|                      |-------|
|.......|                             |@@@@@@@|                      |@@@@@@@|
|@@@@@@@| scroll up a line => memmove |#######| then clear => memset |#######|
|#######|                             |#######|                      |       |
|-------|                             |-------|                      |-------|
于 2011-07-12T12:55:16.667 回答
2

通常,大多数硬件帧缓冲区,例如 VGA 帧缓冲区,都具有硬件滚动功能。不仅如此,一些帧缓冲区(不幸的是不是 VGA 字符控制台)会“环绕”,这意味着当您写入帧缓冲区的最后一个字节(或位),然后再次写入第一个字节时帧缓冲区的字节(或位),这些字节将作为屏幕上的下一行出现在硬件中。所以你可以做几件事:

  1. 完成后,使用帧缓冲区的硬件滚动功能滚动到下一行。这意味着您将首先清除帧缓冲区的下一行,在该行上写入您需要的字符,然后将帧缓冲区起始行指针加一。然后这将“出现”,就好像帧缓冲区已经向下滚动了一行。当您到达帧缓冲区内存的末尾时,请继续执行相同的过程,但您将清除的下一行将是帧缓冲区内存中的第一行。然后,您将继续增加帧缓冲区的起始地址(这仍然指向帧缓冲区内存末尾附近的某个位置),直到它也指向帧缓冲区内存中的最后一行,此时你'
  2. 如果您的帧缓冲区仍然是多页的,但没有环绕功能(如 VGA 字符控制台),则保留指向帧缓冲区开头的指针,以及当您到达最后一个“页面”时" 在内存中,用于将内存memcpy()中的最后一页复制到内存中帧缓冲区的第一页中……完成该步骤后,请保持相同的页面滚动例程,清除下一行,并且然后增加帧缓冲区的起始行指针,以产生向上滚动一行的错觉。页面复制将比之前使用的所有其他硬件页面滚动慢一点,但memcpy()非常有效,特别是如果您使用更大的内存类型(例如 along而不是 a )一次复制多个字节char

接下来,就您的访问功能fb_set_px而言,我会 1)将其inline放在头文件中,这样您就可以避免需要使用堆栈来设置激活帧的实际函数调用的开销,以及 2)您可以使用事实你的帧缓冲区只是一个内存数组,它实际上将一个指针数组映射到某种struct类型,该类型代表你的帧缓冲区的每个字符数据布局(即,一些帧缓冲区有一个字符的字节和另一个字节应用于角色的属性类型,如颜色等)。例如,您可以执行以下操作:

typedef struct frame_buffer_t
{
    unsigned char character;
    unsigned char attributes;
    //add or omit any fields that describe your frame-buffer data-layout
} __attribute__((packed)) frame_buffer_t;

//define as global variable
//make volatile to avoid any compiler re-ordering
unsigned volatile frame_buffer_t* framebuffer[MAX_ROWS_IN_FRAMEBUFFER];

int main()
{
    unsigned volatile frame_buffer_t* temp = global_frame_buffer_start_ptr;

    for (int i=0; i < MAX_ROWS_IN_FRAMEBUFFER; i++)
    {
        framebuffer[i] = temp;
        temp += NUM_COLUMNS;
    }

    //... more code
}

现在您可以简单地将帧缓冲区中的 ax,y 位置定位为

framebuffer[Y_POS][X_POS].character = SOME_VALUE;
于 2011-07-12T13:38:37.013 回答