2011-07-12 167 views
2

什麼是在基於控制檯的幀緩衝中渲染字符的最快方法?我正在使用XNU發行版中的iso_font.h字體。在控制檯中繪製字符的最快方法(framebuffer)?

現在,我使用此代碼來渲染一個字符,但它似乎並沒有太有效的:

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); 
     } 

    } 
} 

而且我也想知道,如果這個代碼可以simpliefied:

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

這是體面的,直到線路太多,我需要重新繪製整個控制檯(滾動)在哪一點上有一個重大延遲。

+0

非緩衝單個字符輸出永遠不會是快。在現代化的機器上這應該是一個非問題,但它仍然比人類能夠讀懂的更快。 –

回答

2

通常,大多數硬件幀緩衝區(例如VGA幀緩衝區)都具有硬件滾動功能。不僅如此,有些幀緩衝區(不幸的是不是VGA字符控制檯)將「環繞」,這意味着當您寫入幀緩衝區的最後一個字節(或多個位)時,然後再次寫入第一個字節(或位),這些字節將作爲屏幕上的下一行出現在硬件中。因此,您可以執行以下幾項操作:

  1. 使用幀緩衝區的硬件滾動功能在完成後滾動到下一行。這意味着您將首先清除幀緩衝區的下一行,將該行所需的字符寫入,然後將幀緩衝區起始行指針加1。這將會「出現」,好像幀緩衝區已經向下滾動一行。當你到達幀緩衝區內存的末尾時,保持相同的進程,但你將清除的下一行將成爲幀緩衝區內存的第一行。然後,您將繼續增加幀緩衝區的起始地址(這仍然指向接近幀緩衝區內存的末尾),直到它也指向幀緩衝區的內存中的最後一行,此時然後你會回到內存中的第一行。
  2. 如果你的幀緩衝區仍然是多頁但沒有環繞功能(如VGA字符控制檯),那麼保持一個指向幀緩衝區開始的指針,當你到達在內存中的最後一頁「頁面」中,使用memcpy()將內存中的最後一頁複製到內存中的幀緩衝區的第一頁中......一旦完成了該步驟,請保持相同的頁面滾動例程下一行,然後遞增幀緩衝區的起始行指針,以給出滾動單行的錯覺。頁面副本比直到那時使用的所有其他硬件頁面滾動要慢一些,但memcpy()是非常有效的,特別是如果您一次使用較大的內存類型(如long而不是char

接下來,只要你接入功能fb_set_px推移,我會1)使其inline一個頭文件裏面,讓你避免需要使用堆棧設置激活幀的實際函數調用的開銷, 2)你可以使用這樣的事實,即你的幀緩衝區只是一個存儲器陣列,實際上是將一個指針數組映射到代表frame_buffer的每個字符數據佈局的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 
} 

現在,你可以簡單地解決斧頭,y位置在您的幀緩衝區

framebuffer[Y_POS][X_POS].character = SOME_VALUE; 
+1

他正在使用基於像素的幀緩衝區(例如VGA模式13h),而不是基於字符的幀緩衝區。 – Skizz

+0

好的,謝謝澄清......是的,我的大部分建議都是針對基於字符的幀緩衝區,而不是像素之一...唯一的遺留物是可能有硬件包裝的事實,其中寫入幀緩衝區內存的最後部分將顯示在屏幕上的幀緩衝區內存緩衝區的第一部分的「上方」。然後,您可以調整幀緩衝存儲器陣列中的顯示起始指針,以便一次向上或向下移動一個像素(如果該功能存在)。他還應該內聯函數調用,以避免任何開銷。 – Jason

4

浮現在腦海中有幾件事情(這讓我回想起舊的DOS時代!): -

1)使用增量尋址寫像素:

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)消除,如果在內部循環中:

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

3)使用OS的任何幫助。例如,這可能是硬件加速。

4)當滾動時,不要重繪所有的字符,只是移動屏幕上的剩餘部分。例如對於行數,從第1行到第0行進行一次移動 - 1.然後清除暴露區域。

|-------|        |-------|      |-------| 
|.......|        |@@@@@@@|      |@@@@@@@| 
|@@@@@@@| scroll up a line => memmove |#######| then clear => memset |#######| 
|#######|        |#######|      |  | 
|-------|        |-------|      |-------|