1. 程式人生 > 其它 >轉載:一文摸透 垂直同步、雙緩衝、三緩衝

轉載:一文摸透 垂直同步、雙緩衝、三緩衝

顯示繪製--垂直同步、雙緩衝、三緩衝

網上這類的文章挺多,我看的時候也暈乎,有點是爬蟲趴下來的格式圖片都掛了,有的參入和很多程式碼方面的講解,一些概念性的平臺無關的機制如果能不涉及程式碼細節,可能會更好。

我嘗試用這篇文章,把嘗試把這三個東西講清楚。(前置知識:需要先了解什麼是掉幀,16ms這個數字怎麼來的)

螢幕顯示影象的原理

   

拿過去的CRT顯示器原理來說,CRT的電子槍按照上面的方式,從上到下逐行掃描,掃描完成以後顯示器就呈現一幀的畫面,然後電子槍就回到初始位置繼續下一次掃描。

為了把顯示器的顯示和系統視訊控制器同步,顯示器會用硬體時鐘產生一系列定時訊號。
當電子槍換下一行準備掃描的時候,顯示器會發出一個水平同步訊號HSync。
當一幀畫面繪製完成後,電子槍回覆到原位,準備畫下一幀前,顯示器會發出一個垂直同步訊號VSync。
顯示器通常以VSync訊號的頻率來重新整理。

計算機系統中的CPU、GPU、顯示器的大致協同工作如下:

CPU計算好現實內容提交到GPU,GPU渲染完成後將渲染結果放入幀緩衝區,隨後視訊控制器就會按照VSync訊號逐行讀取幀緩衝區的資料,然後在顯示器上顯示。

即,電腦顯示一張畫面是分成兩個步驟完成的。

  • 第一步是CPU和顯示卡把所要顯示的畫面資料計算出來。
  • 第二步是顯示器把這些資料寫到螢幕上。

這兩步工作都需要時間,並且可以並行執行,因為具體執行這兩個過程的硬體是相互獨立的(是cpu/顯示卡 和 視訊控制器)。但是呢,這兩個工作的耗時是不同的。
cpu以及顯示卡每秒能計算出的畫面數量是根據硬體效能決定的。 但是顯示器每秒重新整理頻率是固定的(一般是60hz,所以每隔16.667ms就會重新整理一次)。

這種兩邊速率不統一的問題(先不說誰快誰慢),引入了幀緩衝(FrameBuffer)的概念。

幀緩衝能在一定程度上提升效率,但還是有幾個問題:

  • 畫面閃爍
  • 畫面撕裂
  • 跳幀
  • 卡頓

接下來聊聊顯示上會遇到的幾個問題以及方案的演進:

畫面撕裂、跳幀、閃爍

如上面說過的,顯示器重新整理的時候是從最上面的一行畫素開始逐行向下重新整理,所以從頂端到底部的重新整理是有時間差的。如果顯示卡的效能很強,也就是顯示卡幀率大於螢幕重新整理率的時候,就會出現螢幕上半部分還停留在上一幀的畫面,新的一幀的資料已經拷貝上來了,那麼螢幕的下半部分渲染出來的就是下一幀的畫面-----這種情況被稱為畫面撕裂(問題-1)。
如果顯示卡再快一點,那麼下一幀的影象還沒來得及顯示,下下一幀的資料就覆蓋上來了,中間這幀就跳過了-----這種情況被稱為跳幀(問題-2)。
反過來,如果顯示卡幀率小於顯示器重新整理率,那每次在螢幕上看到的可能不是完整的圖形,每次看到的圖形比上次更完整一些。於是在使用者看起來,畫面是卡頓掉幀不順滑(問題-3)。
在單緩衝的場景下,渲染下一幀的時候先清除畫布的當前檢視,這樣就會導致畫面看起來閃爍,比如大學時候在win32的GDI+寫過小遊戲的朋友一定有印象,不使用額外手段的情況下,畫面動起來的時候是會一閃一閃的。(問題-4)。

方案:針對這問題-1和-2,引入了垂直同步的技術

垂直同步(V-Sync),開啟後GPU會等待顯示器的VSync訊號發出後再進行新的一幀渲染和緩衝區更新。即,把顯示卡幀率鎖定為顯示器的重新整理率,

由上述結論我們只能得到,垂直同步可以在顯示卡幀率比顯示器重新整理率高的時候解決撕裂和跳幀的問題。但是,顯示卡幀率小於顯示器重新整理率的時候,也就是問題-3和問題-4,引入了雙緩衝技術

雙緩衝技術,GPU會預先渲染好一幀放入一個緩衝區內,讓視訊控制器讀取,當下一幀渲染好後,GPU會直接把視訊控制器的指標指向第二個緩衝區。也就是說,在一幀被渲染完以後才會交給螢幕顯示,不會看到“半成品畫面”。並且有兩個緩衝區互換,不需要在顯示前臺清理畫布,所以不會閃爍。

安卓在4.1引入了是三快取+垂直同步的機制。

下面再來說一下Android的三重緩衝:先對比總結一下上面說的幾種情況

 

GPU幀率小於顯示器重新整理率的時候還是會出現下面的情況(掉幀):

這樣當掉幀的時候,第二個16ms時間段內,顯示控制器佔用一個Buffer,GPU暫用一個Buffer。兩個Buffer都被佔用,導致CPU空閒下來浪費了資源,因為垂直同步的原因只有V-SYNC時間點CPU才能觸發繪製工作。

這時候引入第三個Buffer。這個Tripple Buffer機制利用CPU/GPU的空閒等待時間提前準備好資料,但是不一定會使用。

如上圖所示,一開始會掉幀一次後面就不會掉幀了。這個所謂的引入Buffer的機制,就和App和SurfaceFlinger通訊的時使用的匿名共享記憶體Ashmem裡的資料結構SharedClient裡的SSHaredBufferStack關聯上了,這個stack有16個位置,在4.1以後這個啟用了3個緩衝位。這塊SurfaceFlinger的文章我會進行專門的整理。

參考文獻:
https://source.android.com/devices/graphics/index.html
https://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/
https://developer.android.google.cn/topic/performance/vitals/render#java

作者:歐禕
連結:https://www.jianshu.com/p/b5fe01cfc753
來源:簡書