扣丁書屋

Android Systrace 基礎知識(11) - Triple Buffer 解讀

本文是 Systrace 系列文章的第十一篇,主要是對 Systrace 中的 Triple Buffer 進行簡單介紹,簡單介紹了如何在 Systrace 中判斷卡頓情況的發生,進行初步的定位和分析,以及介紹 Triple Buffer 的引入對性能的影響

本系列的目的是通過 Systrace 這個工具,從另外一個角度來看待 Android 系統整體的運行,同時也從另外一個角度來對 Framework 進行學習。也許你看了很多講 Framework 的文章,但是總是記不住代碼,或者不清楚其運行的流程,也許從 Systrace 這個圖形化的角度,你可以理解的更深入一些。

系列文章目錄

  1. [Systrace 簡介[1]]
  2. [Systrace 基礎知識 - Systrace 預備知識[2]]
  3. [Systrace 基礎知識 - Why 60 fps ?[3]]
  4. [Systrace 基礎知識 - SystemServer 解讀[4]]
  5. [Systrace 基礎知識 - SurfaceFlinger 解讀[5]]
  6. [Systrace 基礎知識 - Input 解讀[6]]
  7. [Systrace 基礎知識 - Vsync 解讀[7]]
  8. [Systrace 基礎知識 - Vsync-App :基于 Choreographer 的渲染機制詳解[8]]
  9. [Systrace 基礎知識 - MainThread 和 RenderThread 解讀[9]]
  10. [Systrace 基礎知識 - Binder 和鎖競爭解讀[10]]
  11. Systrace 基礎知識 - Triple Buffer 解讀[11]
  12. Systrace 基礎知識 - CPU Info 解讀[12]

怎么定義掉幀?

Systrace 中可以看到應用的掉幀情況,我們經??吹秸f主線程超過 16.6 ms 就會掉幀,其實不然,這和我們這一篇文章講到的 Triple Buffer 和一定的關系,一般來說,Systrace 中我們從 App 端和 SurfaceFlinger 端一起來判斷掉幀情況

App 端判斷掉幀

如果之前沒有看過 Systrace 的話,僅僅從理論上來說,下面這個 Trace 中的應用是掉幀了,其主線程的繪制時間超過了 16.6ms ,但其實不一定,因為 BufferQueue 和 TripleBuffer 的存在,此時 BufferQueue 中可能還有上一幀或者上上一幀準備好的 Buffer,可以直接被 SurfaceFlinger 拿去做合成,當然也可能沒有

App 端掉幀

「所以從 Systrace 的 App 端我們是無法直接判斷是否掉幀的,需要從 Systrace 里面的 SurfaceFlinger 端去看」

SurfaceFlinger 端判斷掉幀

SurfaceFlinger 端掉幀

SurfaceFlinger 端可以看到 SurfaceFlinger 主線程和合成情況和應用對應的 BufferQueue 中 Buffer 的情況。如上圖,就是一個掉幀的例子。App 沒有及時渲染完成,且此時 BufferQueue 中也沒有前幾幀的 Buffer,所以這一幀 SurfaceFlinger 沒有合成對應 App 的 Layer,在用戶看來這里就掉了一幀

而在第一張圖中我們說從 App 端無法看出是否掉幀,那張圖對應的 SurfaceFlinger 的 Trace 如下, 可以看到由于有 Triple Buffer 的存在, SF 這里有之前 App 的 Buffer,所以盡管 App 測一幀超過了 16.6 ms, 但是 SF 這里依然有可用來合成的 Buffer, 所以沒有掉幀

未掉幀

邏輯掉幀

上面的掉幀我們是從渲染這邊來看的,這種掉幀在 Systrace 中可以很容易就發現;還存在一種掉幀情況叫「邏輯掉幀」

「邏輯掉幀」指的是由于應用自己的代碼邏輯問題,導致畫面更新的時候,不是以均勻或者物理曲線的方式,而是出現跳躍更新的情況,這種掉幀一般在 Systrace 上沒法看出來,但是用戶在使用的時候可以明顯感覺到

舉一個簡單的例子,比如說列表滑動的時候,如果我們滑動松手后列表的每一幀前進步長是一個均勻變化的曲線,最后趨近于 0,這樣就是完美的;但是如果出現這一幀相比上一幀走了 20,下一幀相比這一幀走了 10,下下一幀相比下一幀走了 30,這種就是跳躍更新,在 Systrace 上每一幀都是及時渲染且 SurfaceFlinger 都及時合成的,但是用戶用起來就是覺得會卡. 不過我列舉的這個例子中,Android 已經針對這種情況做了優化,感興趣的可以去看一下 android/view/animation/AnimationUtils.java 這個類,重點看下面三個方法的使用

public static void lockAnimationClock(long vsyncMillis)
public static void unlockAnimationClock()
public static long currentAnimationTimeMillis()

Android 系統的動畫一般不會有這個問題,但是應用開發者就保不齊會寫這種代碼,比如做動畫的時候根據**當前的時間(而不是 Vsync 到來的時間)**來計算動畫屬性變化的情況,這種情況下,一旦出現掉幀,動畫的變化就會變得不均勻,感興趣的可以自己思考一下這一塊

另外 Android 出現掉幀情況的原因非常多,各位可以參考下面三篇文章食用:

  1. Android 中的卡頓丟幀原因概述 - 方法論[13]
  2. Android 中的卡頓丟幀原因概述 - 系統篇[14]
  3. Android 中的卡頓丟幀原因概述 - 應用篇[15]

BufferQueue 和 Triple Buffer

BufferQueue

首先看一下 BufferQueue,BufferQueue 是一個生產者(Producer)-消費者(Consumer)模型中的數據結構,一般來說,消費者(Consumer) 創建 BufferQueue,而生產者(Producer) 一般不和 BufferQueue 在同一個進程里面

BufferQueue

其運行邏輯如下

  1. 當生產者(Producer) 需要 Buffer 時,它通過調用 dequeueBuffer()并指定 Buffer 的寬度,高度,像素格式和使用標志,從 BufferQueue 請求釋放 Buffer
  2. 生產者(Producer) 將填充緩沖區,并通過調用 queueBuffer()將緩沖區返回到隊列。
  3. 消費者(Consumer) 使用 acquireBuffer()獲取 Buffer 并消費 Buffer 的內容
  4. 使用完成后,消費者(Consumer)將通過調用 releaseBuffer()將 Buffer 返回到隊列

Android 通過 Vsync 機制來控制 Buffer 在 BufferQueue 中的流動時機,如果對 Vsync 機制不了解,可以參考下面這兩篇文章,看完后你會有個大概的了解

  1. Systrace 基礎知識 - Vsync 解讀[16]
  2. Android 基于 Choreographer 的渲染機制詳解[17]

上面的流程比較抽象,這里舉一個具體的例子,方便大家理解上面那張圖,對后續了解 Systrace 中的 BufferQueue 也會有幫助。

在 Android App 的渲染流程里面,App 就是個生產者(Producer) ,而 SurfaceFlinger 是一個消費者(Consumer),所以上面的流程就可以翻譯為

  1. 「App」 需要 Buffer 時,它通過調用 dequeueBuffer()并指定 Buffer 的寬度,高度,像素格式和使用標志,從 BufferQueue 請求釋放 Buffer
  2. 「App」 可以用 cpu 進行渲染也可以調用用 gpu 來進行渲染,渲染完成后,通過調用 queueBuffer()將緩沖區返回到 App 對應的 BufferQueue(如果是 gpu 渲染的話,這里還有個 gpu 處理的過程)
  3. 「SurfaceFlinger」 在收到 Vsync 信號之后,開始準備合成,使用 acquireBuffer()獲取 App 對應的 BufferQueue 中的 Buffer 并進行合成操作
  4. 合成結束后,「SurfaceFlinger」 將通過調用 releaseBuffer()將 Buffer 返回到 App 對應的 BufferQueue

理解了 BufferQueue 的作用后,接下來來講解一下 BufferQueue 中的 Buffer

從上面的圖可以看到,BufferQueue 中的生產者和消費者通過 dequeueBuffer、queueBuffer、acquireBuffer、releaseBuffer 來申請或者釋放 Buffer,那么 BufferQueue 中需要幾個 Buffer 來進行運轉呢?下面從單 Buffer,雙 Buffer 和 Triple Buffer 的角度分析(注意這里只是從 Buffer 的角度來做分析的, 比如 App 測涉及到 Buffer 的是 RenderThread , 不過由于 RenderThread 與 MainThread 有一定的聯系, 比如 unBlockUiThread 執行的時機, MainThread 也會因為 RenderThread 執行慢而被 Block 住)

Single Buffer

單 Buffer 的情況下,因為只有一個 Buffer 可用,那么這個 Buffer 既要用來做合成顯示,又要被應用拿去做渲染

Single Buffer

理想情況下,單 Buffer 是可以完成任務的(有 Vsync-Offset 存在的情況下)

  1. App 收到 Vsync 信號,獲取 Buffer 開始渲染
  2. 間隔 Vsync-Offset 時間后,SurfaceFlinger 收到 Vsync 信號,開始合成
  3. 屏幕刷新,我們看到合成后的畫面

Single Buffer 理想情況

但是很不幸,理想情況我們也就想一想,這期間如果 App 渲染或者 SurfaceFlinger 合成在屏幕顯示刷新之前還沒有完成,那么屏幕刷新的時候,拿到的 Buffer 就是不完整的,在用戶看來,就有種撕裂的感覺

Single Buffer 非理想情況

當然 Single Buffer 已經沒有在使用,上面只是一個例子

Double Buffer

Double Buffer 相當于 BufferQueue 中有兩個 Buffer 可供輪轉,消費者在消費 Buffer 的同時,生產者也可以拿到備用的 Buffer 進行生產操作

Double Buffer

下面我們來看理想情況下,Double Buffer 的工作流程

DoubleBufferPipline_NoJank

但是 Double Buffer 也會存在性能上的問題,比如下面的情況,App 連續兩幀生產都超過 Vsync 周期(準確的說是錯過 SurfaceFlinger 的合成時機) ,就會出現掉幀情況

Double Buffer

Triple Buffer

Triple Buffer 中,我們又加入了一個 BackBuffer ,這樣的話 BufferQueue 里面就有三個 Buffer 可以輪轉了,當 FrontBuffer 在被使用的時候,App 有兩個空閑的 Buffer 可以拿去生產,就算生產過程中有 GPU 超時,CPU 任然可以拿到新的 Buffer 進行生產(「即 SurfaceFling 消費 FrontBuffer,GPU 使用一個 BackBuffer,CPU 使用一個 BackBuffer」)

Triple Buffer

下面就是引入 Triple Buffer 之后,解決了 Double Buffer 中遇到的由于 Buffer 不足引起的掉幀問題

解決第二個掉幀

這里把兩個圖放到一起來看,方便大家做對比(一個是 Double Buffer 掉幀兩次,一個是使用 Triple Buffer 只掉了一幀)

TripleBuffer_VS_DoubleBuffer

Triple Buffer 的作用

緩解掉幀

從上一節 Double Buffer 和 Triple Buffer 的對比圖可以看到,在這種情況下(出現連續主線程超時),三個 Buffer 的輪轉有助于緩解掉幀出現的次數(從掉幀兩次 -> 只掉幀一次)

所以從第一節如何定義掉幀這里我們就知道,App 主線程超時不一定會導致掉幀,由于 Triple Buffer 的存在,部分 App 端的掉幀(主要是由于 GPU 導致),到 SurfaceFlinger 這里未必是掉幀,這是看 Systrace 的時候需要注意的一個點

緩解掉幀

減少主線程和渲染線程等待時間

「雙 Buffer 的輪轉」, App 主線程有時候必須要等待 SurfaceFlinger(消費者)釋放 Buffer 后,才能獲取 Buffer 進行生產,這時候就有個問題,現在大部分手機 SurfaceFlinger 和 App 同時收到 Vsync 信號,如果出現 App 主線程等待 SurfaceFlinger(消費者)釋放 Buffer ,那么勢必會讓 App 主線程的執行時間延后,比如下面這張圖,可以明顯看到:「Buffer B 并不是在 Vsync 信號來的時候開始被消費(因為還在使用),而是等 Buffer A 被消費后,Buffer B 被釋放,App 才能拿到 Buffer B 進行生產,這期間就有一定的延遲,會讓主線程可用的時間變短」

主線程和渲染線程等待時間

我們來看一下在 Systrace 中的上面這種情況發生的時候的表現

減少主線程和渲染線程等待時間

而 三個 Buffer 輪轉的情況下,則基本不會有這種情況的發生,渲染線程一般在 dequeueBuffer 的時候,都可以順利拿到可用的 Buffer (當然如果 dequeueBuffer 本身耗時那就不是這里的討論范圍了)

降低 GPU 和 SurfaceFlinger 瓶頸

這個比較好理解,雙 Buffer 的時候,App 生產的 Buffer 必須要及時拿去讓 GPU 進行渲染,然后 SurfaceFlinger 才能進行合成,一旦 GPU 超時,就很容易出現 SurfaceFlinger 無法及時合成而導致掉幀

在三個 Buffer 輪轉的時候,App 生產的 Buffer 可以及早進入 BufferQueue,讓 GPU 去進行渲染(因為不需要等待,就算這里積累了 2 個 Buffer,下下一幀才去合成,這里也會提早進行,而不是在真正使用之前去匆忙讓 GPU 去渲染),另外 SurfaceFlinger 本身的負載如果比較大,三個 Buffer 輪轉也會有效降低 dequeueBuffer 的等待時間

比如下面兩張圖,就是對應的 SurfaceFlinger 和 App 的「雙 Buffer 掉幀」情況,由于 SurfaceFlinger 本身就比較耗時(特定場景),而 App 的 dequeueBuffer 得不到及時的響應,導致發生了比較嚴重的掉幀情況。在換成 Triple Buffer 之后,這種情況就基本上沒有了

降低 GPU 和 SurfaceFlinger 瓶頸

降低 GPU 和 SurfaceFlinger 瓶頸

Debug Triple Buffer

Dumpsys SurfaceFlinger

dumpsys SurfaceFlinger 可以查看 SurfaceFlinger 輸出的眾多當前的狀態,比如一些性能指標、Buffer 狀態、圖層信息等,后續有篇幅的話可以單獨拿出來講,下面是截取的 Double Buffer 情況下和 Triple Buffer 情況下的各個 App 的 Buffer 使用情況,可以看到不同的 App,在負載不一樣的情況下,對 Triple Buffer 的使用率是不一樣的;Double Buffer 則完全使用的是雙 Buffer

Dumpsys SurfaceFlinger

關閉 Triple Buffer

不同 Android 版本屬性設置不一樣(這是 Google 的一個邏輯 Bug,Android 10 上面已經修復了)

Android 版本 <= Android P

//控制代碼
property_get("ro.sf.disable_triple_buffer", value, "1");
mLayerTripleBufferingDisabled = atoi(value);
ALOGI_IF(mLayerTripleBufferingDisabled, "Disabling Triple Buffering");

「修改對應的屬性值,然后重啟 Framework」

//按順序執行下面的語句(需要 Root 權限)
adb root
adb shell setprop ro.sf.disable_triple_buffer 0
adb shell stop && adb shell start

Android 版本 > Android P

//控制代碼
property_get("ro.sf.disable_triple_buffer", value, "0");
mLayerTripleBufferingDisabled = atoi(value);
ALOGI_IF(mLayerTripleBufferingDisabled, "Disabling Triple Buffering");

「修改對應的屬性值,然后重啟 Framework」

//按順序執行下面的語句(需要 Root 權限)
adb root
adb shell setprop ro.sf.disable_triple_buffer 1
adb shell stop && adb shell start

參考

  1. https://source.android.google.cn/devices/graphics[18]

附件

本文涉及到的附件也上傳了,各位下載后解壓,使用 「Chrome」 瀏覽器打開即可點此鏈接下載文章所涉及到的 Systrace 附件[19]

Reference

[1]Systrace 簡介: https://www.androidperformance.com/2019/05/28/Android-Systrace-About/

[2]Systrace 基礎知識 - Systrace 預備知識: https://www.androidperformance.com/2019/07/23/Android-Systrace-Pre/

[3]Systrace 基礎知識 - Why 60 fps ?: https://www.androidperformance.com/2019/05/27/why-60-fps/

[4]Systrace 基礎知識 - SystemServer 解讀: https://www.androidperformance.com/2019/06/29/Android-Systrace-SystemServer/

[5]Systrace 基礎知識 - SurfaceFlinger 解讀: https://www.androidperformance.com/2020/02/14/Android-Systrace-SurfaceFlinger/

[6]Systrace 基礎知識 - Input 解讀: https://www.androidperformance.com/2019/11/04/Android-Systrace-Input/

[7]Systrace 基礎知識 - Vsync 解讀: https://www.androidperformance.com/2019/12/01/Android-Systrace-Vsync/

[8]Systrace 基礎知識 - Vsync-App :基于 Choreographer 的渲染機制詳解: https://androidperformance.com/2019/10/22/Android-Choreographer/

[9]Systrace 基礎知識 - MainThread 和 RenderThread 解讀: https://www.androidperformance.com/2019/11/06/Android-Systrace-MainThread-And-RenderThread/

[10]Systrace 基礎知識 - Binder 和鎖競爭解讀: https://www.androidperformance.com/2019/12/06/Android-Systrace-Binder/

[11]Systrace 基礎知識 - Triple Buffer 解讀: https://www.androidperformance.com/2019/12/15/Android-Systrace-Triple-Buffer

[12]Systrace 基礎知識 - CPU Info 解讀: https://www.androidperformance.com/2019/12/21/Android-Systrace-CPU

[13]Android 中的卡頓丟幀原因概述 - 方法論: https://www.androidperformance.com/2019/09/05/Android-Jank-Debug/

[14]Android 中的卡頓丟幀原因概述 - 系統篇: https://www.androidperformance.com/2019/09/05/Android-Jank-Due-To-System/

[15]Android 中的卡頓丟幀原因概述 - 應用篇: https://www.androidperformance.com/2019/09/05/Android-Jank-Due-To-App/

[16]Systrace 基礎知識 - Vsync 解讀: https://www.androidperformance.com/2019/12/01/Android-Systrace-Vsync/

[17]Android 基于 Choreographer 的渲染機制詳解: https://www.androidperformance.com/2019/10/22/Android-Choreographer/

[18]https://source.android.google.cn/devices/graphics: https://source.android.google.cn/devices/graphics

[19]點此鏈接下載文章所涉及到的 Systrace 附件: https://github.com/Gracker/SystraceForBlog/tree/master/Android_Systrace_TripleBuffer


https://mp.weixin.qq.com/s/ORsfUYNUCwM_0n4-NpSveA

Android Systrace 基礎知識(11) - Triple Buffer 解讀

發布于:3月以前  |  492次閱讀  |  詳細內容 ?

Android Systrace 基礎知識(7) - Vsync 解讀

發布于:3月以前  |  518次閱讀  |  詳細內容 ?

Android Systrace 基礎知識(6) - Input 解讀

發布于:3月以前  |  500次閱讀  |  詳細內容 ?

Android Systrace 基礎知識(5) - SurfaceFlinger 解讀

發布于:3月以前  |  518次閱讀  |  詳細內容 ?

Android Systrace 基礎知識(4) - SystemServer 解讀

發布于:3月以前  |  475次閱讀  |  詳細內容 ?

Android Systrace 基礎知識(3) - Why 60 fps ?

發布于:3月以前  |  418次閱讀  |  詳細內容 ?

Android Systrace 基礎知識 -- Systrace 簡介

發布于:3月以前  |  487次閱讀  |  詳細內容 ?

所屬標簽

最多閱讀

簡化Android的UI開發 2年以前  |  515103次閱讀
Android 深色模式適配原理分析 1年以前  |  26395次閱讀
Android 樣式系統 | 主題背景覆蓋 1年以前  |  7944次閱讀
Android Studio 生成so文件 及調用 1年以前  |  5565次閱讀
30分鐘搭建一個android的私有Maven倉庫 3年以前  |  4741次閱讀
Android設計與開發工作流 2年以前  |  4404次閱讀
Google Enjarify:可代替dex2jar的dex反編譯 3年以前  |  4387次閱讀
Android多渠道打包工具:apptools 3年以前  |  4021次閱讀
移動端常見崩潰指標 1年以前  |  3984次閱讀
Google Java編程風格規范(中文版) 3年以前  |  3932次閱讀
Android-模塊化-面向接口編程 1年以前  |  3844次閱讀
Android內存異常機制(用戶空間)_NE 1年以前  |  3808次閱讀
Android UI基本技術點 3年以前  |  3784次閱讀
Android死鎖初探 2年以前  |  3728次閱讀

手機掃碼閱讀
18禁止午夜福利体验区,人与动人物xxxx毛片人与狍,色男人窝网站聚色窝
<蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>