一文摸透 垂直同步、雙緩沖、三緩沖

顯示繪制--垂直同步、雙緩沖、三緩沖

網(wǎng)上這類(lèi)的文章挺多,我看的時(shí)候也暈乎,有點(diǎn)是爬蟲(chóng)趴下來(lái)的格式圖片都掛了,有的參入和很多代碼方面的講解,一些概念性的平臺(tái)無(wú)關(guān)的機(jī)制如果能不涉及代碼細(xì)節(jié),可能會(huì)更好。

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

屏幕顯示圖像的原理

拿過(guò)去的CRT顯示器原理來(lái)說(shuō),CRT的電子槍按照上面的方式,從上到下逐行掃描,掃描完成以后顯示器就呈現(xiàn)一幀的畫(huà)面,然后電子槍就回到初始位置繼續(xù)下一次掃描。

為了把顯示器的顯示和系統(tǒng)視頻控制器同步,顯示器會(huì)用硬件時(shí)鐘產(chǎn)生一系列定時(shí)信號(hào)。
當(dāng)電子槍換下一行準(zhǔn)備掃描的時(shí)候,顯示器會(huì)發(fā)出一個(gè)水平同步信號(hào)HSync。
當(dāng)一幀畫(huà)面繪制完成后,電子槍回復(fù)到原位,準(zhǔn)備畫(huà)下一幀前,顯示器會(huì)發(fā)出一個(gè)垂直同步信號(hào)VSync。
顯示器通常以VSync信號(hào)的頻率來(lái)刷新。

計(jì)算機(jī)系統(tǒng)中的CPU、GPU、顯示器的大致協(xié)同工作如下:


image.jpeg

CPU計(jì)算好現(xiàn)實(shí)內(nèi)容提交到GPU,GPU渲染完成后將渲染結(jié)果放入幀緩沖區(qū),隨后視頻控制器就會(huì)按照VSync信號(hào)逐行讀取幀緩沖區(qū)的數(shù)據(jù),然后在顯示器上顯示。

即,電腦顯示一張畫(huà)面是分成兩個(gè)步驟完成的。

  • 第一步是CPU和顯卡把所要顯示的畫(huà)面數(shù)據(jù)計(jì)算出來(lái)。
  • 第二步是顯示器把這些數(shù)據(jù)寫(xiě)到屏幕上。

這兩步工作都需要時(shí)間,并且可以并行執(zhí)行,因?yàn)榫唧w執(zhí)行這兩個(gè)過(guò)程的硬件是相互獨(dú)立的(是cpu/顯卡 和 視頻控制器)。但是呢,這兩個(gè)工作的耗時(shí)是不同的。
cpu以及顯卡每秒能計(jì)算出的畫(huà)面數(shù)量是根據(jù)硬件性能決定的。 但是顯示器每秒刷新頻率是固定的(一般是60hz,所以每隔16.667ms就會(huì)刷新一次)。

這種兩邊速率不統(tǒng)一的問(wèn)題(先不說(shuō)誰(shuí)快誰(shuí)慢),引入了幀緩沖(FrameBuffer)的概念。

幀緩沖能在一定程度上提升效率,但還是有幾個(gè)問(wèn)題:

  • 畫(huà)面閃爍
  • 畫(huà)面撕裂
  • 跳幀
  • 卡頓

接下來(lái)聊聊顯示上會(huì)遇到的幾個(gè)問(wèn)題以及方案的演進(jìn):

畫(huà)面撕裂、跳幀、閃爍

image.jpeg

如上面說(shuō)過(guò)的,顯示器刷新的時(shí)候是從最上面的一行像素開(kāi)始逐行向下刷新,所以從頂端到底部的刷新是有時(shí)間差的。如果顯卡的性能很強(qiáng),也就是顯卡幀率大于屏幕刷新率的時(shí)候,就會(huì)出現(xiàn)屏幕上半部分還停留在上一幀的畫(huà)面,新的一幀的數(shù)據(jù)已經(jīng)拷貝上來(lái)了,那么屏幕的下半部分渲染出來(lái)的就是下一幀的畫(huà)面-----這種情況被稱(chēng)為畫(huà)面撕裂(問(wèn)題-1)。
如果顯卡再快一點(diǎn),那么下一幀的圖像還沒(méi)來(lái)得及顯示,下下一幀的數(shù)據(jù)就覆蓋上來(lái)了,中間這幀就跳過(guò)了-----這種情況被稱(chēng)為跳幀(問(wèn)題-2)。
反過(guò)來(lái),如果顯卡幀率小于顯示器刷新率,那每次在屏幕上看到的可能不是完整的圖形,每次看到的圖形比上次更完整一些。于是在用戶(hù)看起來(lái),畫(huà)面是卡頓掉幀不順滑(問(wèn)題-3)。
在單緩沖的場(chǎng)景下,渲染下一幀的時(shí)候先清除畫(huà)布的當(dāng)前視圖,這樣就會(huì)導(dǎo)致畫(huà)面看起來(lái)閃爍,比如大學(xué)時(shí)候在win32的GDI+寫(xiě)過(guò)小游戲的朋友一定有印象,不使用額外手段的情況下,畫(huà)面動(dòng)起來(lái)的時(shí)候是會(huì)一閃一閃的。(問(wèn)題-4)。

方案:針對(duì)這問(wèn)題-1和-2,引入了垂直同步的技術(shù)。

垂直同步(V-Sync),開(kāi)啟后GPU會(huì)等待顯示器的VSync信號(hào)發(fā)出后再進(jìn)行新的一幀渲染和緩沖區(qū)更新。即,把顯卡幀率鎖定為顯示器的刷新率,

由上述結(jié)論我們只能得到,垂直同步可以在顯卡幀率比顯示器刷新率高的時(shí)候解決撕裂和跳幀的問(wèn)題。但是,顯卡幀率小于顯示器刷新率的時(shí)候,也就是問(wèn)題-3和問(wèn)題-4,引入了雙緩沖技術(shù)。

雙緩沖技術(shù),GPU會(huì)預(yù)先渲染好一幀放入一個(gè)緩沖區(qū)內(nèi),讓視頻控制器讀取,當(dāng)下一幀渲染好后,GPU會(huì)直接把視頻控制器的指針指向第二個(gè)緩沖區(qū)。也就是說(shuō),在一幀被渲染完以后才會(huì)交給屏幕顯示,不會(huì)看到“半成品畫(huà)面”。并且有兩個(gè)緩沖區(qū)互換,不需要在顯示前臺(tái)清理畫(huà)布,所以不會(huì)閃爍。

安卓在4.1引入了是三緩存+垂直同步的機(jī)制。

下面再來(lái)說(shuō)一下Android的三重緩沖:先對(duì)比總結(jié)一下上面說(shuō)的幾種情況

image.jpeg
image.jpeg

GPU幀率小于顯示器刷新率的時(shí)候還是會(huì)出現(xiàn)下面的情況(掉幀):


image.jpeg

這樣當(dāng)?shù)魩臅r(shí)候,第二個(gè)16ms時(shí)間段內(nèi),顯示控制器占用一個(gè)Buffer,GPU暫用一個(gè)Buffer。兩個(gè)Buffer都被占用,導(dǎo)致CPU空閑下來(lái)浪費(fèi)了資源,因?yàn)榇怪蓖降脑蛑挥蠽-SYNC時(shí)間點(diǎn)CPU才能觸發(fā)繪制工作。

這時(shí)候引入第三個(gè)Buffer。這個(gè)Tripple Buffer機(jī)制利用CPU/GPU的空閑等待時(shí)間提前準(zhǔn)備好數(shù)據(jù),但是不一定會(huì)使用。


image.jpeg

如上圖所示,一開(kāi)始會(huì)掉幀一次后面就不會(huì)掉幀了。這個(gè)所謂的引入Buffer的機(jī)制,就和App和SurfaceFlinger通信的時(shí)使用的匿名共享內(nèi)存Ashmem里的數(shù)據(jù)結(jié)構(gòu)SharedClient里的SSHaredBufferStack關(guān)聯(lián)上了,這個(gè)stack有16個(gè)位置,在4.1以后這個(gè)啟用了3個(gè)緩沖位。這塊SurfaceFlinger的文章我會(huì)進(jìn)行專(zhuān)門(mén)的整理。

參考文獻(xiàn):
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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容