1. 程式人生 > >Android的16ms和垂直同步以及三重快取

Android的16ms和垂直同步以及三重快取

前言

手機螢幕是由許多的畫素點組成的,每個畫素點通過顯示不同的顏色最終螢幕呈現各種各樣的影象。手機系統的型別和手機硬體的不同導致UI的流暢性體驗個不一致。

螢幕展示的顏色資料

  • 在GPU中有一塊緩衝區叫做 Frame Buffer ,這個幀緩衝區可以認為是儲存畫素值的二位陣列。
  • 陣列中的每一個值就對應了手機螢幕的畫素點需要顯示的顏色。
  • 由於這個幀緩衝區的數值是在不斷變化的,所以只要完成對螢幕的重新整理就可以顯示不同的影象了.。
  • 至於重新整理工作手記的邏輯電路會定期的重新整理 Frame Buffer的 目前主流的重新整理頻率為60次/秒 折算出來就是16ms重新整理一次。

GPU的Frame Buffer中的資料

  • GPU 除了幀緩衝區用以交給手機螢幕進行繪製外. 還有一個緩衝區 Back Buffer 這個用以交給應用的,讓CPU往裡面填充資料。
  • GPU會定期交換 Back Buffer 和 Frame Buffer ,也就是對Back Buffer中的資料進行柵格化後將其轉到 Frame Buffer 然後交給螢幕進行顯示繪製,同時讓原先的Frame Buffer 變成 Back Buffer 讓程式處理。

Android的16ms

在Android中我們一般都會提到16ms繪製一次,那麼到底是那裡控制這16ms的呢?

Choreographer類中我們有一個方法獲取螢幕重新整理速率:

public final class Choreographer {
    private static float getRefreshRate() {
        DisplayInfo di = DisplayManagerGlobal.getInstance().getDisplayInfo(
                Display.DEFAULT_DISPLAY);
        return di.refreshRate;
    }
}

/**
 * Describes the characteristics of a particular logical display.
 * @hide
*/
public final class DisplayInfo implements Parcelable { /** * The refresh rate of this display in frames per second. * <p> * The value of this field is indeterminate if the logical display is presented on * more than one physical display. * </p> */ public float refreshRate; } final class VirtualDisplayAdapter extends DisplayAdapter { private final class VirtualDisplayDevice extends DisplayDevice implements DeathRecipient { @Override public DisplayDeviceInfo getDisplayDeviceInfoLocked() { if (mInfo == null) { mInfo = new DisplayDeviceInfo(); mInfo.name = mName; mInfo.uniqueId = getUniqueId(); mInfo.width = mWidth; mInfo.height = mHeight; mInfo.refreshRate = 60; /***部分程式碼省略***/ } return mInfo; } } }

一秒60幀,計算下來大概16.7ms一幀。

螢幕繪製

作為嚴重影響Android口碑問題之一的UI流暢性差的問題,首先在Android 4.1版本中得到了有效處理。其解決方法就是本文要介紹的Project Butter。

Project Butter對Android Display系統進行了重構,引入了三個核心元素,即VSYNC、Triple Buffer和Choreographer。其中, VSYNC是理解Project Buffer的核心。VSYNC是Vertical Synchronization(垂直同步)的縮寫,是一種在PC上已經很早就廣泛使用的技術。 可簡單的把它認為是一種定時中斷。

接下來,將圍繞VSYNC來介紹Android Display系統的工作方式。請注意,後續討論將以Display為基準,將其劃分成16ms長度的時間段, 在每一時間段中,Display顯示一幀資料(相當於每秒60幀)。時間段從1開始編號。

沒有VSYNC的情況:

image

由上圖可知

1.時間從0開始,進入第一個16ms:Display顯示第0幀,CPU處理完第一幀後,GPU緊接其後處理繼續第一幀。三者互不干擾,一切正常。 2.時間進入第二個16ms:因為早在上一個16ms時間內,第1幀已經由CPU,GPU處理完畢。故Display可以直接顯示第1幀。顯示沒有問題。但在本16ms期間,CPU和GPU 卻並未及時去繪製第2幀資料(注意前面的空白區),而是在本週期快結束時,CPU/GPU才去處理第2幀資料。 3.時間進入第3個16ms,此時Display應該顯示第2幀資料,但由於CPU和GPU還沒有處理完第2幀資料,故Display只能繼續顯示第一幀的資料,結果使得第1 幀多畫了一次(對應時間段上標註了一個Jank)。 4.通過上述分析可知,此處發生Jank的關鍵問題在於,為何第1個16ms段內,CPU/GPU沒有及時處理第2幀資料?原因很簡單,CPU可能是在忙別的事情(比如某個應用通過sleep 固定時間來實現動畫的逐幀顯示),不知道該到處理UI繪製的時間了。可CPU一旦想起來要去處理第2幀資料,時間又錯過了!

NSYNC的出現

為解決這個問題,Project Buffer引入了VSYNC,這類似於時鐘中斷。結果如圖所示:

image

由圖可知,每收到VSYNC中斷,CPU就開始處理各幀資料。整個過程非常完美。 不過,仔細琢磨圖2卻會發現一個新問題:圖2中,CPU和GPU處理資料的速度似乎都能在16ms內完成,而且還有時間空餘,也就是說,CPU/GPU的FPS(幀率,Frames Per Second)要高於Display的FPS。確實如此。由於CPU/GPU只在收到VSYNC時才開始資料處理,故它們的FPS被拉低到與Display的FPS相同。但這種處理並沒有什麼問題,因為Android裝置的Display FPS一般是60,其對應的顯示效果非常平滑。 如果CPU/GPU的FPS小於Display的FPS,會是什麼情況呢?請看下圖:

image

由圖可知: 1.在第二個16ms時間段,Display本應顯示B幀,但卻因為GPU還在處理B幀,導致A幀被重複顯示。 2.同理,在第二個16ms時間段內,CPU無所事事,因為A Buffer被Display在使用。B Buffer被GPU在使用。注意,一旦過了VSYNC時間點, CPU就不能被觸發以處理繪製工作了。

三級快取

為什麼CPU不能在第二個16ms處開始繪製工作呢?原因就是隻有兩個Buffer。如果有第三個Buffer的存在,CPU就能直接使用它, 而不至於空閒。出於這一思路就引出了Triple Buffer。結果如圖所示:

image

由圖可知: 第二個16ms時間段,CPU使用C Buffer繪圖。雖然還是會多顯示A幀一次,但後續顯示就比較順暢了。 是不是Buffer越多越好呢?回答是否定的。由圖4可知,在第二個時間段內,CPU繪製的第C幀資料要到第四個16ms才能顯示, 這比雙Buffer情況多了16ms延遲。所以,Buffer最好還是兩個,三個足矣。

以上對VSYNC進行了理論分析,其實也引出了Project Buffer的三個關鍵點: 核心關鍵:需要VSYNC定時中斷。 Triple Buffer:當雙Buffer不夠使用時,該系統可分配第三塊Buffer。 另外,還有一個非常隱祕的關鍵點:即將繪製工作都統一到VSYNC時間點上。這就是Choreographer的作用。在它的統一指揮下,應用的繪製工作都將變得井井有條。

文章到這裡就全部講述完啦,若有其他需要交流的可以留言哦~!~!

想閱讀作者的更多文章,可以檢視我 個人部落格 和公共號:

振興書城