Android幀緩衝區(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現原理分析(7)

     顯示屏的刷新頻率與顯示屏的掃描時序相關。顯示屏的掃描時序能夠參考Linux內核源代碼目錄下的Documentation/fb/framebuffer.txt文件。咱們結合圖2來簡單說明上述代碼是如何計算顯示屏的刷新頻率的。
圖 2 顯示屏掃描時序示意圖
        中間由xres和yres組成的區域即爲顯示屏的圖形繪製區,在繪製區的上、下、左和右分別有四個邊距upper_margin、lower_margin、left_margin和right_margin。此外,在顯示屏的最右邊以及最下邊還有一個水平同步區域hsync_len和一個垂直同步區域vsync_len。電子槍按照從左到右、從上到下的順序來顯示屏中打點,從而能夠將要渲染的圖形顯示在屏幕中。前面所提到的區域信息分別保存在fb_var_screnninfo結構體info的成員變量xres、yres、upper_margin、lower_margin、left_margin、right_margin、hsync_len和vsync_len。
        電子槍每在xres和yres所組成的區域中打一個點所花費的時間記錄在fb_var_screnninfo結構體info的成員變量pixclock,單位爲pico seconds,即10E-12秒。 
        電子槍從左到右掃描完成一行以後,都會處理關閉狀態,而且會從新折回到左邊去。因爲電子槍在從右到左折回的過程當中不須要打點,所以,這個過程會比從左到右掃描屏幕的過程要快,這個折回的時間大概就等於在xres和yres所組成的區域掃描(left_margin+right_margin)個點的時間。這樣,咱們就能夠認爲每渲染一行須要的時間爲(xres + left_margin + right_margin)* pixclock。
       一樣,電子槍從上到下掃描完成顯示屏以後,須要從右下角折回到左上角去,折回的時間大概等於在xres和yres所組成的區域中掃描(upper_margin + lower_margin)行所須要的時間。這樣,咱們就能夠認爲每渲染一屏圖形所須要的時間等於在xres和yres所組成的區域中掃描(yres + upper_margin + lower_margin)行所須要的時間。因爲在xres和yres所組成的區域中掃描一行所須要的時間爲(xres + left_margin + right_margin)* pixclock,所以,每渲染一屏圖形所須要的總時間就等於(yres + upper_margin + lower_margin)* (xres + left_margin + right_margin)* pixclock。
       每渲染一屏圖形須要的總時間通過計算以後,就保存在變量refreshQuotient中。注意,變量refreshQuotient所描述的時間的單位爲1E-12秒。這樣,將變量refreshQuotient的值倒過來,就能夠獲得設備顯示屏的刷新頻率。將這個頻率值乘以10E15次方以後,就獲得一個單位爲10E-3 HZ的刷新頻率,保存在變量refreshRate中。
       當Android系統在模擬器運行的時候,保存在fb_var_screnninfo結構體info的成員變量pixclock中的值可能等於0。在這種狀況下,前面計算獲得的變量refreshRate的值就會等於0。在這種狀況下,接下來的代碼會將變量refreshRate的值設置爲60 * 1000 * 10E-3 HZ,即將顯示屏的刷新頻率設置爲60HZ。
       再往下看函數mapFrameBufferLocked:
  1. if (int(info.width) <= 0 || int(info.height) <= 0) {  
  2.     // the driver doesn't return that information  
  3.     // default to 160 dpi  
  4.     info.width  = ((info.xres * 25.4f)/160.0f + 0.5f);  
  5.     info.height = ((info.yres * 25.4f)/160.0f + 0.5f);  
  6. }  
  7.   
  8. float xdpi = (info.xres * 25.4f) / info.width;  
  9. float ydpi = (info.yres * 25.4f) / info.height;  
  10. float fps  = refreshRate / 1000.0f;  

        這段代碼首先計算顯示屏的密度,即每英寸有多少個像素點,分別寬度和高度兩個維度,分別保存在變量xdpi和ydpi中。注意,fb_var_screeninfo結構體info的成員變量width和height用來描述顯示屏的寬度和高度,它們是以毫米(mm)爲單位的。
 
        這段代碼接着再將前面計算獲得的顯示屏刷新頻率的單位由10E-3 HZ轉換爲HZ,即幀每秒,而且保存在變量fps中。
        再往下看函數mapFrameBufferLocked:
  1. if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1)  
  2.     return -errno;  
  3.   
  4. if (finfo.smem_len <= 0)  
  5.     return -errno;  
  6.   
  7.   
  8. module->flags = flags;  
  9. module->info = info;  
  10. module->finfo = finfo;  
  11. module->xdpi = xdpi;  
  12. module->ydpi = ydpi;  
  13. module->fps = fps;  
       這段代碼再次經過IO控制命令FBIOGET_FSCREENINFO來得到系統幀緩衝區的固定信息,而且保存在fb_fix_screeninfo結構體finfo中,接下來再使用fb_fix_screeninfo結構體finfo以及前面獲得的系統幀緩衝區的其它信息來初始化參數module所描述的一個private_module_t結構體。
 
       最後,函數mapFrameBufferLocked就將系統幀緩衝區映射到當前進程的地址空間來:
  1.     /* 
  2.      * map the framebuffer 
  3.      */  
  4.   
  5.     int err;  
  6.     size_t fbSize = roundUpToPageSize(finfo.line_length * info.yres_virtual);  
  7.     module->framebuffer = new private_handle_t(dup(fd), fbSize, 0);  
  8.   
  9.     module->numBuffers = info.yres_virtual / info.yres;  
  10.     module->bufferMask = 0;  
  11.   
  12.     void* vaddr = mmap(0, fbSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);  
  13.     if (vaddr == MAP_FAILED) {  
  14.         LOGE("Error mapping the framebuffer (%s)", strerror(errno));  
  15.         return -errno;  
  16.     }  
  17.     module->framebuffer->base = intptr_t(vaddr);  
  18.     memset(vaddr, 0, fbSize);  
  19.     return 0;  
  20. }  
        表達式finfo.line_length * info.yres_virtual計算的是整個系統幀緩衝區的大小,它的值等於顯示屏行數(虛擬分辨率的高度值,info.yres_virtual)乘以每一行所佔用的字節數(finfo.line_length)。函數roundUpToPageSize用來將整個系統幀緩衝區的大小對齊到頁面邊界。對齊後的大小保存在變量fbSize中。
 
        表達式finfo.yres_virtual / info.yres計算的是整個系統幀緩衝區能夠劃分爲多少個圖形緩衝區來使用,這個數值保存在參數module所描述的一個private_module_t結構體的成員變量nmBuffers中。參數module所描述的一個private_module_t結構體的另一個成員變量bufferMask的值接着被設置爲0,表示系統幀緩衝區中的全部圖形緩衝區都是處於空閒狀態,即它們能夠分配出去給應用程序使用。
        系統幀緩衝區是經過調用函數mmap來映射到當前進程的地址空間來的。映射後獲得的地址空間使用一個private_handle_t結構體來描述,這個結構體的成員變量base保存的即爲系統幀緩衝區在當前進程的地址空間中的起始地址。這樣,Gralloc模塊之後就能夠從這塊地址空間中分配圖形緩衝區給當前進程使用。
        至此,fb設備的打開過程就分析完成了。在打開fb設備的過程當中,Gralloc模塊還完成了對系統幀緩衝區的初始化工做。接下來咱們繼續分析Gralloc模塊是如何分配圖形緩衝區給用戶空間的應用程序使用的。
相關文章
相關標籤/搜索