顯示繪制--垂直同步、雙緩沖、三緩沖
網上這類的文章挺多宇植,我看的時候也暈乎,有點是爬蟲趴下來的格式圖片都掛了忙上,有的參入和很多代碼方面的講解闲坎,一些概念性的平臺無關的機制如果能不涉及代碼細節(jié)茬斧,可能會更好梗逮。
我嘗試用這篇文章,把嘗試把這三個東西講清楚娄蔼。(前置知識:需要先了解什么是掉幀底哗,16ms這個數字怎么來的)
屏幕顯示圖像的原理
拿過去的CRT顯示器原理來說,CRT的電子槍按照上面的方式涕癣,從上到下逐行掃描前标,掃描完成以后顯示器就呈現(xiàn)一幀的畫面,然后電子槍就回到初始位置繼續(xù)下一次掃描同眯。
為了把顯示器的顯示和系統(tǒng)視頻控制器同步唯鸭,顯示器會用硬件時鐘產生一系列定時信號。
當電子槍換下一行準備掃描的時候明肮,顯示器會發(fā)出一個水平同步信號HSync缭付。
當一幀畫面繪制完成后,電子槍回復到原位陷猫,準備畫下一幀前,顯示器會發(fā)出一個垂直同步信號VSync足陨。
顯示器通常以VSync信號的頻率來刷新娇未。
計算機系統(tǒng)中的CPU、GPU镊讼、顯示器的大致協(xié)同工作如下:
CPU計算好現(xiàn)實內容提交到GPU,GPU渲染完成后將渲染結果放入幀緩沖區(qū)卸亮,隨后視頻控制器就會按照VSync信號逐行讀取幀緩沖區(qū)的數據嚼松,然后在顯示器上顯示。
即寝受,電腦顯示一張畫面是分成兩個步驟完成的罕偎。
- 第一步是CPU和顯卡把所要顯示的畫面數據計算出來。
- 第二步是顯示器把這些數據寫到屏幕上甩苛。
這兩步工作都需要時間俏站,并且可以并行執(zhí)行,因為具體執(zhí)行這兩個過程的硬件是相互獨立的(是cpu/顯卡 和 視頻控制器)墨林。但是呢犯祠,這兩個工作的耗時是不同的。
cpu以及顯卡每秒能計算出的畫面數量是根據硬件性能決定的衡载。 但是顯示器每秒刷新頻率是固定的(一般是60hz,所以每隔16.667ms就會刷新一次)弃榨。
這種兩邊速率不統(tǒng)一的問題(先不說誰快誰慢)猜揪,引入了幀緩沖(FrameBuffer)的概念。
幀緩沖能在一定程度上提升效率,但還是有幾個問題:
- 畫面閃爍
- 畫面撕裂
- 跳幀
- 卡頓
接下來聊聊顯示上會遇到的幾個問題以及方案的演進:
畫面撕裂拴念、跳幀、閃爍
如上面說過的风瘦,顯示器刷新的時候是從最上面的一行像素開始逐行向下刷新公般,所以從頂端到底部的刷新是有時間差的。如果顯卡的性能很強瞬雹,也就是顯卡幀率大于屏幕刷新率的時候刽虹,就會出現(xiàn)屏幕上半部分還停留在上一幀的畫面,新的一幀的數據已經拷貝上來了涌哲,那么屏幕的下半部分渲染出來的就是下一幀的畫面-----這種情況被稱為畫面撕裂(問題-1)阀圾。
如果顯卡再快一點,那么下一幀的圖像還沒來得及顯示初烘,下下一幀的數據就覆蓋上來了,中間這幀就跳過了-----這種情況被稱為跳幀(問題-2)综膀。
反過來局齿,如果顯卡幀率小于顯示器刷新率,那每次在屏幕上看到的可能不是完整的圖形讥此,每次看到的圖形比上次更完整一些谣妻。于是在用戶看起來,畫面是卡頓掉幀不順滑(問題-3)他巨。
在單緩沖的場景下,渲染下一幀的時候先清除畫布的當前視圖染突,這樣就會導致畫面看起來閃爍份企,比如大學時候在win32的GDI+寫過小游戲的朋友一定有印象,不使用額外手段的情況下司志,畫面動起來的時候是會一閃一閃的。(問題-4)囚霸。
方案:針對這問題-1和-2吧史,引入了垂直同步的技術。
垂直同步(V-Sync)吨述,開啟后GPU會等待顯示器的VSync信號發(fā)出后再進行新的一幀渲染和緩沖區(qū)更新钞脂。即,把顯卡幀率鎖定為顯示器的刷新率邓夕,
由上述結論我們只能得到阎毅,垂直同步可以在顯卡幀率比顯示器刷新率高的時候解決撕裂和跳幀的問題。但是矿咕,顯卡幀率小于顯示器刷新率的時候狼钮,也就是問題-3和問題-4,引入了雙緩沖技術莲镣。
雙緩沖技術涎拉,GPU會預先渲染好一幀放入一個緩沖區(qū)內的圆,讓視頻控制器讀取区岗,當下一幀渲染好后毁枯,GPU會直接把視頻控制器的指針指向第二個緩沖區(qū)种玛。也就是說,在一幀被渲染完以后才會交給屏幕顯示赂韵,不會看到“半成品畫面”。并且有兩個緩沖區(qū)互換肄满,不需要在顯示前臺清理畫布质涛,所以不會閃爍汇陆。
安卓在4.1引入了是三緩存+垂直同步的機制。
下面再來說一下Android的三重緩沖:先對比總結一下上面說的幾種情況
GPU幀率小于顯示器刷新率的時候還是會出現(xiàn)下面的情況(掉幀):
這樣當掉幀的時候,第二個16ms時間段內教寂,顯示控制器占用一個Buffer,GPU暫用一個Buffer导梆。兩個Buffer都被占用因妇,導致CPU空閑下來浪費了資源,因為垂直同步的原因只有V-SYNC時間點CPU才能觸發(fā)繪制工作狡忙。
這時候引入第三個Buffer址芯。這個Tripple Buffer機制利用CPU/GPU的空閑等待時間提前準備好數據窜觉,但是不一定會使用禀挫。
如上圖所示拓颓,一開始會掉幀一次后面就不會掉幀了。這個所謂的引入Buffer的機制砰左,就和App和SurfaceFlinger通信的時使用的匿名共享內存Ashmem里的數據結構SharedClient里的SSHaredBufferStack關聯(lián)上了场航,這個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