天天看點

Android圖形顯示系統(一)簡介1. 從下層往上層了解2. VSync以及三緩沖總結:

轉載請注明出處:http://blog.csdn.net/a740169405/article/details/70548443

簡介

本文講解的内容是Android4.1以後的系統機制,将從整體上分析Android圖形顯示系統的結構,不深入分析每一層内部的代碼實作,更多的是使用流程圖和結構圖來讓大家了解Android是如何繪制、合成圖形并顯示到螢幕上。

本文将從三個層次進行講解,大緻如下圖:

Android圖形顯示系統(一)簡介1. 從下層往上層了解2. VSync以及三緩沖總結:

可以了解為上層生産,下層消費模型。

其中每一層之間的資料傳遞是使用Buffer(圖形緩沖區)作為載體:

Android圖形顯示系統(一)簡介1. 從下層往上層了解2. VSync以及三緩沖總結:

這裡的緩沖區,大家可以了解為帶有寬高和像素密度的記憶體區塊。

1. 從下層往上層了解

1.1 顯示屏

顯示屏上的内容,是從硬體幀緩沖區讀取的,大緻讀取過程為:從Buffer的起始位址開始,從上往下,從左往右掃描整個Buffer,将内容映射到顯示屏上:

Android圖形顯示系統(一)簡介1. 從下層往上層了解2. VSync以及三緩沖總結:

當然,螢幕上的内容需要需要不斷的更新,如果在同一個Buffer進行讀取和寫入(合成)操作,将會導緻螢幕顯示多幀内容。是以硬體層除了提供一個Buffer用于螢幕顯示,還提供了一個Buffer用于背景的圖形合成,也就是我們常說的雙緩沖:

Android圖形顯示系統(一)簡介1. 從下層往上層了解2. VSync以及三緩沖總結:

上圖中包含兩個緩沖區:

前緩沖區:用來顯示内容到螢幕的幀緩沖區

後緩沖區:用于背景合成下一幀圖形的幀緩沖區

假設前一幀顯示完畢,後一幀準備好了,螢幕将會開始讀取下一幀的内容,也就是開始讀取上圖中的後緩沖區的内容:

Android圖形顯示系統(一)簡介1. 從下層往上層了解2. VSync以及三緩沖總結:

此時,前後緩沖區進行一次角色互換,之前的後緩沖區變為前緩沖區,進行圖形的顯示,之前的前緩沖區則變為後緩沖區,進行圖形的合成。

然而,理想很豐滿,現實很骨感,上面假設“目前一幀顯示完畢,後一幀準備好了”的情況,在現實中這兩個事件并非同時完成。那麼,螢幕掃描緩沖區的速度和系統合成幀的速度之間有什麼關系呢,帶着這個疑惑我們看看下面兩個概念:

螢幕重新整理率(HZ):代表螢幕在一秒内重新整理螢幕的次數,Android手機一般為60HZ(也就是1秒重新整理60幀,大約16.67毫秒重新整理1幀)

系統幀速率(FPS):代表了系統在一秒内合成的幀數,該值的大小由系統算法和硬體決定。

我們用以下兩個假設來分析兩者的關系:

① 螢幕重新整理速率比系統幀速率快

此時,在前緩沖區内容全部映射到螢幕上之後,後緩沖區尚未準備好下一幀,螢幕将無法讀取下一幀,是以隻能繼續顯示目前一幀的圖形,造成一幀顯示多次,也就是卡頓。

② 系統幀速率比螢幕重新整理率快

此時,螢幕未完全把前緩沖區的一幀映射到螢幕,而系統已經在後緩沖區準備好了下一幀,并要求讀取下一幀到螢幕,将會導緻螢幕上半部分是上一幀的圖形,而下半部分是下一幀的圖形,造成螢幕上顯示多幀,也就是螢幕撕裂。

上面兩種情況,都會導緻問題,根本原因就是兩個緩沖區的操作速率不一緻,解決辦法就是讓螢幕控制前後緩沖區的切換,讓系統幀速率配合螢幕重新整理率的節奏。

那麼螢幕是如何控制這個節奏的呢?

垂直同步(VSync):當螢幕從緩沖區掃描完一幀到螢幕上之後,開始掃描下一幀之前,發出的一個同步信号,該信号用來切換前緩沖區和後緩沖區。

通過上面的分析可以看出,螢幕的顯示節奏是固定的,作業系統需要配合螢幕的顯示,在固定的時間内準備好下一幀,以供螢幕進行顯示。兩者通過VSync信号來實作同步。

關于螢幕這一塊的知識點講解到這,不再深入分析,接下來我們将會了解後緩沖區的圖形合成者。

1.2 SurfaceFlinger-圖形合成者

如果說螢幕是消費者,那麼SurfaceFlinger相對螢幕來說就是生産者,其具有如下特性:

  • 作為上層應用的消費者,硬體層的生産者。
  • 負責圖形的合成
  • 和ActivityManagerService一樣,是一個系統服務

為了更好的了解SurfaceFlinger這個服務的工作内容,以及他是如何做到一個承上啟下的作用,我們通過下面的這個界面分析:

Android圖形顯示系統(一)簡介1. 從下層往上層了解2. VSync以及三緩沖總結:

界面很簡單,拆開來看,包含微信、懸浮工具箱、通知欄、底部虛拟按鍵欄:

Android圖形顯示系統(一)簡介1. 從下層往上層了解2. VSync以及三緩沖總結:

我們可以先這樣了解上面這幅圖,上層每一個界面,其實都對應SufaceFlinger裡的一個Surface對象,上層将自己的内容繪制在對應的Surface内,接着,SufaceFlinger需要将所有上層對應的Surface内的圖形進行合成,具體看下圖:

Android圖形顯示系統(一)簡介1. 從下層往上層了解2. VSync以及三緩沖總結:

沒錯,SurfaceFlinger就是将多個Surface裡的内容進行合成,最後送出到螢幕的後緩沖區,等待螢幕的下一個垂直同步信号的到來,再顯示到螢幕上。

我們會發現SufaceFlinger通過螢幕後緩沖區與螢幕建立聯系。同時通過Surface與上層建立聯系。進而起到一個承上啟下的作用,是Android圖形系統結構中的關鍵組成部分。

為了繼續往上層講,我們需要了解什麼是Surface:

  • 對應上層的一個Window(對話框、Activity、狀态欄)
  • 作為上層圖形繪制的畫闆
  • Canvas是畫筆,上層通過調用Canvas的API向Surface上繪制圖形
  • Surface内部存在多個緩沖區,形成一個BufferQueue

如果說SurfaceFinger是圖形的合成者,那麼圖形的提供者就是上層。文章一開始就提到,圖形的傳遞是通過Buffer作為載體,Surface是對Buffer的進一步封裝,也就是說Surface内部具有多個Buffer供上層使用,如何管理這些Buffer呢?請看下面這個模型:

Android圖形顯示系統(一)簡介1. 從下層往上層了解2. VSync以及三緩沖總結:

Surface内部提供一個BufferQueue,與上層和SurfaceFlinger形成一個生産者消費者模型,上層對應Producer,SurfaceFlinger對應Consumer。三者通過Buffer産生聯系,每個Buffer都有四種狀态:

  • Free:可被上層使用
  • Dequeued:出列,正在被上層使用
  • Queued:入列,已完成上層繪制,等待SurfaceFlinger合成
  • Acquired:被擷取,SurfaceFlinger正持有該Buffer進行合成

Buffer的一次轉移過程大緻為:

  1. 從BufferQueue轉移到上層
  2. 上層繪制完成再放回BufferQueue
  3. 接着SurfaceFlinger再拿去合成
  4. 最後又放回BufferQueue

如此循環,形成一個Buffer被循環使用的過程。

關于SurfaceFlinger以及SurfaceFlinger與上層建立聯系的Surface講解完了,接下來看看上層是如何将圖形繪制到Surface的Buffer中。

1.3 上層繪圖

上層繪圖的大體流程見下圖:

Android圖形顯示系統(一)簡介1. 從下層往上層了解2. VSync以及三緩沖總結:

之前有說到,Surface裡的Buffer作為上層的畫闆,Canvas作為畫筆,通過調用Canvas的API完成圖形的繪制,上層通過調用draw方法來調用Canvas的API,當然這裡的draw方法并沒有真正的将圖形繪制到緩沖區,而是記錄了一下繪制指令,具體需要了解DisplayList相關隻是,後面會對其進行分析。

從流程上看:

  1. 測量View的寬高(Measure)
  2. 設定View的寬高位置(Layout)
  3. 建立顯示清單,并執行繪制(Draw)
  4. 生成多邊形和紋理
  5. 對多邊形和紋理進行栅格化操作

從執行者的角度看:

  1. CPU:Measure,Layout,紋理和多邊形生成,發送紋理和多邊形到GPU
  2. GPU:将CPU生成的紋理和多邊形進行栅格化以及合成

上面說的的紋理和多邊形還有栅格化以及合成,這裡不做具體的講解,需要了解的是圖形的繪制流程需要經過這些操作。從上面的分析可以看出,上層繪制圖形時需要經過CPU計算,再經過GPU計算。

經過上面的分析,整個Android的圖形繪制大體流程已經分析完成,接下來将會分析一些流程的具體實作,分析的内容包括:

  • Android 4.1 加入的VSync信号同步到上層以及三緩沖
  • 從上層往下層具體分析每一步流程

2. VSync以及三緩沖

2.1 Drawing Without VSync

從上面的講解可以看出,整個繪制流程的節奏,分成兩個生産者消費者模型,一個由螢幕和SurfaceFlinger構成,另一個由SurfaceFlinger和上層應用構成,具體流程可以用下圖來描述:

Android圖形顯示系統(一)簡介1. 從下層往上層了解2. VSync以及三緩沖總結:

其中:

  1. CPU和GPU代表上層的繪制執行者
  2. Composite代表的是SurfaceFlinger對多個Surface的合成
  3. Background Buffer和Front Buffer分别代表的是硬體幀緩沖區中的前緩沖和後緩沖
  4. 顯示屏掃描完一幀之後,會發出VSync信号來切換并顯示下一幀

上面的流程中,存在一個問題,螢幕的VSync信号隻是用來控制幀緩沖區的切換,并未控制上層的繪制節奏,也就是說上層的生産節奏和螢幕的顯示節奏是脫離的:

Android圖形顯示系統(一)簡介1. 從下層往上層了解2. VSync以及三緩沖總結:

上圖中,橫軸表示時間,縱軸表示Buffer的使用者,每個長方形表示Buffer的使用,長方形的寬度代表使用時長,VSync代表垂直同步信号,兩個VSync信号之間間隔16.6ms。此圖描述了Android在4.1系統版本之前,上層的繪圖流程在沒有VSync信号的時候,出現的繪制問題。

我們從時間為0開始看,目前螢幕顯示第0幀,上層CPU開始計算第1幀的紋理,計算完成後,交由GPU進行栅格化。當下一個垂直同步信号到來,螢幕顯示下一幀,這時候,上層CPU并未馬上開始準備下一幀,而當CPU開始準備下一幀的時候已經太晚了,下一個VSync信号來臨的時候,GPU未能繪制完第二幀的處理,導緻螢幕再次顯示上一幀,造成卡頓:

Android圖形顯示系統(一)簡介1. 從下層往上層了解2. VSync以及三緩沖總結:

2.2 Drawing With VSync

因為上層不知道VSync信号已經發出,導緻上層未能開始CPU的計算。google在Android 4.1系統中加入了上層接收垂直同步信号的邏輯,大緻流程如下:

Android圖形顯示系統(一)簡介1. 從下層往上層了解2. VSync以及三緩沖總結:

也就是說,螢幕在顯示完一幀後,發出的垂直同步除了通知幀緩沖區的切換之外,該消息還會發送到上層,通知上層開始繪制下一幀。

那麼,上層是如何接受這個VSync消息的呢?

2.2.1 Choreographer VSync信号的上層接收者

Google為上層設計了一個Choreographer類,翻譯成中文是“編舞者”,是希望通過它來控制上層的繪制(舞蹈)節奏。

首先看看Choreographer的類圖:

Android圖形顯示系統(一)簡介1. 從下層往上層了解2. VSync以及三緩沖總結:

可以發現,Choreographer需要向SurfaceFlinger來注冊一個VSync信号的接收器DisplayEventReceiver。同時在Choreographer的内部維護了一個CallbackQueue,用來儲存上層關心VSync信号的元件,包括ViewRootImpl,TextView,ValueAnimator等。

再看看上層接收VSync的時序圖:

Android圖形顯示系統(一)簡介1. 從下層往上層了解2. VSync以及三緩沖總結:

知道了Choreographer是上層用來接收VSync的角色之後,我們需要進一步了解VSync信号是如何控制上層的繪制的:

Android圖形顯示系統(一)簡介1. 從下層往上層了解2. VSync以及三緩沖總結:

一般,上層需要繪制新的UI都是因為View的requestLayout或者是invalidate方法被調用觸發的,我們以這個為起點,跟蹤上層View的繪制流程:

  1. requestLayout或者invalidate觸發更新視圖請求
  2. 更新請求傳遞到ViewRootImpl中,ViewRootImpl向主線程MessageQueue中加入一個阻塞器,該阻塞器将會攔截所有同步消息,也就是說此時,我們再通過Handler向主線程MessageQueue發送的所有Message都将無法被執行。
  3. ViewRootImpl向Choreographer注冊下一個VSync信号
  4. Choreographer通過DisplayEventReceiver向framework層注冊下一個VSync信号
  5. 當底層産生下一個VSync消息時,該信号将會發送給DisplayEventReceiver,最後傳遞給Choreographer
  6. Choreographer收到VSync信号之後,向主線程MessageQueue發送了一個異步消息,我們在第二步提到,ViewRootImpl向MessageQueue發送了一個同步消息阻塞器。這裡Choreographer發送的異步消息,是不會被阻塞器攔截的。
  7. 最後,異步消息的執行者是ViewRootImpl,也就是真正開始繪制下一幀了

至此,底層的VSync控制上層的邏輯就解釋完了,此時上層繪制圖形的流程與VSync信号的關系可以用下圖表示:

Android圖形顯示系統(一)簡介1. 從下層往上層了解2. VSync以及三緩沖總結:

時間從螢幕顯示第0幀開始,CPU開始準備第1幀圖形的處理,好了之後交給GPU進行處理,在上層收到下一個VSync之後,CPU立馬開始第2幀的處理,上層繪圖的節奏就和VSync信号保持一緻了,整個繪圖非常流暢。

然而,理想很豐滿,現實很骨感,如果CPU和GPU沒能在下一個VSync信号到來之前完成下一幀的繪制工作,又會是怎麼樣的呢?

Android圖形顯示系統(一)簡介1. 從下層往上層了解2. VSync以及三緩沖總結:

還是從螢幕顯示第A幀開始,時間進入第一個16.6ms,CPU和GPU合成第B幀,當下一個VSync信号到來的時候,GPU未能及時完成第B幀的繪制,此時,GPU占有一個Surface裡的Buffer,而同時SurfaceFlinger又持有一個Buffer用于合成顯示下一幀到螢幕,這樣的話,就導緻Surface裡的兩個緩沖區都被占用了。此時SurfaceFlinger隻能使用第A幀已經準備好的Buffer來合成,GPU繼續在另一個緩沖區中合成第B幀,此時CPU無法開始下一幀的合成,因為緩沖區用完了。另外一個不好的事情是CPU隻有在VSync信号來的時候才開始繪制下一幀,也是就是說在第二個16.6ms時間内,CPU一直處于空閑狀态,未進行下一幀的計算。

隻有等到第二個VSync信号來了之後,CPU才開始在繪制下一幀。如果CPU和GPU需要合成的圖形太多,将會導緻連續性的卡頓,如果CPU和GPU大部分時候都無法在16.6ms完成一幀的繪制,将會導緻連續的卡頓現象。

别着急,請看看Google的決絕方案。

2.3 parallel processing and triple buffering

沒錯,就是加入第三個Buffer,CPU和GPU還有SurfaceFlinger各占一個Buffer,并行處理圖形:

Android圖形顯示系統(一)簡介1. 從下層往上層了解2. VSync以及三緩沖總結:

從上圖可以看出,在第一個VSync到來時,盡管SurfaceFlinger占了一個Buffer,GPU又占了一個Buffer,CPU仍然可以在第三個Buffer中開始下一幀的計算,整個顯示過程就開始時卡頓了一幀,之後都是流暢的。

當然系統并非一直開啟三個Buffer,因為Buffer是需要消耗資源的,并且,我們會發現,上圖中,GPU處理好的圖形,需要跨越兩個VSync信号,才能顯示。這樣的話,給使用者的影響是一個延遲的現象。

為了解決該問題,我們需要再次從上層往下層了解Android繪制圖形的各個細節,并進行優化。對于應用程式開發人員來說,重點還是上層的優化,對自己的應用程式的記憶體,UI,資料等進行優化。

總結:

  • Android通過Buffer來儲存圖形資訊,為了讓圖形顯示的更加流程,在提供一一個Buffer用于顯示的同時,開辟一個或者多個Buffer用于背景圖形的合成。
  • Android4.1之前,VSync信号并未傳遞給上層,導緻生産與消費節奏不統一
  • Android4.1之後,上層開始繪制時機都放到了VSync信号的到來時候
  • 除了在上層引入VSync機制,Anroid在4.1還加入了三緩沖,用來減少卡頓的産生
  • 每個Surface都有自己的繪制流程,需要先經過CPU處理,再經過GPU處理,之後經過SurfaceFlinger與其他Surface繪制好的圖形和合成在一起,供螢幕顯示
  • VSync信号貫穿整個繪制流程,控制着整個Android圖形系統的節奏

以上内容可能有不對的地方,希望各路大神指教,下一篇文章将從上層往下層講解Android的圖形顯示流程,并把上層的圖形繪制流程展開講解,敬請期待!