VSYNC與畫面撕裂
VSYNC即vertical sync尔店,也稱為垂直同步,是一種圖形技術(shù),主要就是強(qiáng)制將幀速率與顯示器的刷新率同步嚣州,最初由 GPU 制造商提出鲫售,主要用來(lái)處理屏幕撕裂。首先了解下兩個(gè)名詞:FPS與屏幕刷新頻率
- 幀率[Frame Rate该肴,單位FPS]-顯卡生成幀的速率情竹,也可以認(rèn)為是數(shù)據(jù)處理的速度
- 屏幕刷新頻率 [Refresh Rate單位赫茲/HZ]:是指硬件設(shè)備刷新屏幕的頻率,值一般是固定的匀哄,以黑白電視的電子掃描槍類比秦效,比如60Hz的顯示屏,每16ms電子槍從上到下從左到右一行一行逐漸把圖片繪制出來(lái)涎嚼。
兩者要同步配合好才能高效的顯示圖像阱州,可以人為幀率對(duì)應(yīng)的是圖像數(shù)據(jù)的輸出,刷新率對(duì)應(yīng)的是圖像數(shù)據(jù)的屏幕展示苔货,如果幀率同設(shè)備的刷新率不一致鹊汛,而又沒(méi)有采用合適的同步技術(shù),會(huì)出現(xiàn)什么問(wèn)題呢滥嘴?可能會(huì)出現(xiàn)上述的屏幕撕裂[多幀的局部數(shù)據(jù)共同組成了一個(gè)完整幀]至耻,示意如下:
理論上來(lái)講,只要沒(méi)做到讀/寫線性同步就有幾率發(fā)生撕裂走触, 只有幀數(shù)據(jù)完整更新+顯示設(shè)備完整渲染才能阻止撕裂疤苹,相對(duì)應(yīng)的撕裂的復(fù)現(xiàn)場(chǎng)景有兩種:
- 1:顯示設(shè)備未完整渲染: 假設(shè)顯示設(shè)備只有一塊顯存存放顯示數(shù)據(jù),在沒(méi)有同步加鎖的情況下卧土,幀數(shù)據(jù)由CPU/GPU處理完可隨時(shí)寫入到顯存,如果恰好在上一幀A還沒(méi)100%在屏幕顯示完的時(shí)候旅敷,B幀到達(dá)颤霎,并且覆蓋了A涂滴,那么在繼續(xù)刷新下半部分時(shí)晴音,繪制的就是B幀數(shù)據(jù),此時(shí)就會(huì)出現(xiàn)上半部分是A下半部分是B首量,即發(fā)生屏幕撕裂:如下
- 2 幀數(shù)據(jù)未完整更新 :依舊假設(shè)顯示設(shè)備只有一塊顯存存放顯示數(shù)據(jù)进苍,如果在GPU覆蓋舊幀的間隙觉啊,也就是顯存數(shù)據(jù)沒(méi)有100%刷新的時(shí)候沈贝,通知渲染到屏幕,這個(gè)時(shí)候同樣會(huì)發(fā)生上述事情宋下,即使用了半成品的幀:撕裂幀。參考視頻
所以同步鎖的機(jī)制是撕裂的關(guān)鍵罩引,必須有這么一個(gè)機(jī)制告訴GPU顯卡枝笨,要等待當(dāng)前幀繪完整,才能替換當(dāng)前幀横浑,即VSYNC,VSYNC強(qiáng)制幀率和顯示器刷新頻率同步洒缀,如果當(dāng)前幀沒(méi)繪制完欺冀,即使下一幀準(zhǔn)備好了,也禁止使用下一幀脚猾,直到顯示器繪制完當(dāng)前幀,即:60HZ顯示器砰奕,開(kāi)了垂直同步后,顯示幀率就會(huì)限定最高60军援,即使顯卡輸出高達(dá)90FPS也沒(méi)用,甚至可以認(rèn)為他是一種妥協(xié)性優(yōu)化胸哥,一定程度上還會(huì)降低性能。以上都是針對(duì)一塊顯示存儲(chǔ)的情況庐船,理論上只要加鎖就能解決,讀的時(shí)候禁止寫筐钟,但這么做無(wú)疑會(huì)大大降低效率赋朦,所以不能簡(jiǎn)單依靠單純加鎖解決問(wèn)題。
雙緩沖+垂直同步
如何解決單緩沖+同步的性能問(wèn)題呢壹将?多增加一塊顯示存儲(chǔ)區(qū)能解決嗎?假定顯示設(shè)備有兩塊顯存诽俯,BackBuffer與FrontBuffer狱庇,可以簡(jiǎn)單的認(rèn)為CPU/GPU占據(jù)一個(gè)緩沖、當(dāng)前呈現(xiàn)的數(shù)據(jù)占據(jù)一個(gè)緩沖颜启,GPU/CPU 繪制更新BackBuffer,不需要關(guān)心正在展示的FrontBuffer缰盏,這就是雙緩沖淹遵,相比于單緩存,雙緩沖可讓寫與讀分離济炎,提高效率。但緊靠雙緩沖理論上解決不了撕裂的問(wèn)題须尚,BackBuffer畢竟也是要展示的,也要”拷貝“到FrontBuffer耐床,如果不對(duì)拷貝操作添加干預(yù),也可能出現(xiàn)撕裂撩轰,VSYNC機(jī)制必須兼具禁止在刷新的過(guò)程中更新FrontBuffer的功能,所有的COPY或者說(shuō)是Page flipping操作都要等待上一幀完全渲染完才可以堪嫂,渲染完成之后,顯示設(shè)備就按節(jié)奏可以發(fā)出下一個(gè)VSYNC信號(hào)镜廉,通知BackBuffer與FrontBuffer間進(jìn)行拷貝愚战,拷貝結(jié)束后齐遵,接著進(jìn)行下一幀屏幕渲染,這樣就能避免屏幕撕裂梗摇,當(dāng)然,如果BackBuffer還未來(lái)得及完成幀更新也是需要阻斷拷貝過(guò)程断序,否則就是渲染了半成品的幀,所以個(gè)人人為违诗,Vsync解決撕裂疮蹦、雙緩沖來(lái)解決性能。
It does this by preventing the GPU from doing anything to the display memory until the monitor has concluded its current refresh cycle — effectively not feeding it any more information until it’s ready for it. Through a combination of double buffering and page flipping, VSync synchronizes the drawing of frames onto the display only when it has finished a refresh cycle, so you shouldn’t ever see tears when VSync is enabled.
對(duì)Android系統(tǒng)而言阵苇,VSYNC除了強(qiáng)制幀率和顯示器刷新頻率同步外感论,還有其他很多作用,在Android Jelly Bean之前VSYNC使用的場(chǎng)景比較少比肄,只用在最后緩沖區(qū)切換囊陡,系統(tǒng)的其他環(huán)節(jié)沒(méi)用关斜,這種做法可能會(huì)讓CPU浪費(fèi)在其他低優(yōu)先級(jí)的業(yè)務(wù)上,如下圖:
如此情況就是一次jank
Jelly Bean之前VSYNC僅用在最后的圖像顯示階段痢畜,防止屏幕撕裂丁稀,但是并未協(xié)調(diào)UI的繪制,CPU對(duì)于顯示幀的處理是凌亂的线衫,VSYNC到達(dá)后惑折,如果CPU被其他任務(wù)占據(jù),UI繪制的執(zhí)行就會(huì)延遲惨驶,等到它開(kāi)始處理UI生成幀的時(shí)候,可能已經(jīng)處于16ms的中間屋确,這樣就很容易跨兩個(gè)VYSNC信號(hào)续扔,導(dǎo)致掉幀。在Jelly Bean中纱昧,下一幀的處理被限定在VSync信號(hào)到達(dá)時(shí),并且依賴Android的消息屏障機(jī)制呜投,將UI重繪消息的優(yōu)先級(jí)是提高存璃,其他的同步消息均不會(huì)執(zhí)行,由于是在每個(gè)VSYNC信號(hào)到達(dá)時(shí)就處理幀纵东,可以讓UI繪制充分使用16ms耗時(shí),可以盡量避免跨越兩幀的情況出現(xiàn)洒扎。
這種做法保證了UI繪制的執(zhí)行時(shí)間,雖然不能完全解決jank【比如本身繪制就超過(guò)16ms】袍冷,但是對(duì)于本來(lái)就小于16ms的任務(wù)是能保證的,從而降低jank的概率邓线,因此VSYNC+雙緩沖能夠很好降低單緩沖的性能問(wèn)題,降低延時(shí)骇陈。
雙緩沖的進(jìn)階:三緩沖
之前的VSYNC+雙緩沖流程圖示都是用1瑰抵、2、3代表第幀來(lái)表示更新流程二汛,接線來(lái)用緩沖區(qū)代表,看一下雙緩沖的數(shù)據(jù)流向逛球,理想情況下苫昌,16ms內(nèi)CPU處理完數(shù)據(jù)幸海,將緩沖區(qū)A交給GPU,GPU接著處理A物独,結(jié)束后,等下個(gè)VSYNC與前面展示緩沖區(qū)B交換婉陷,A進(jìn)行屏幕渲染官研,B回收用來(lái)繼續(xù)生成下一幀,如下圖所示:
在這種模型下担神,CPU與GPU其實(shí)是一種串行處理的操作,存在資源的浪費(fèi)妄讯,因?yàn)閮烧咂湟槐乜臻e,畢竟沒(méi)有多余的緩沖區(qū)讓其處理數(shù)據(jù)亥贸,理想情況下其實(shí)雙緩沖并未有什么不妥,但是一旦CPU或者GPU處理超時(shí)炕置,jank就很容易發(fā)生。
VSYNC+雙緩沖保證低延時(shí)垦沉,三緩沖保證穩(wěn)定性:讓閑置的資源動(dòng)起來(lái)
雙緩沖模型中顯示仍劈、CPU厕倍、GPU處理都會(huì)用到Buffer贩疙,VSYNC+雙緩沖在理想情況下是沒(méi)有問(wèn)題的,但如果某個(gè)環(huán)節(jié)出現(xiàn)問(wèn)題组民,那就不一樣了,比如某些幀耗時(shí)是[CPU 8ms +GPU 12ms]臭胜,超過(guò)了16ms癞尚,如下:
可以看到在第二個(gè)階段,存在CPU資源浪費(fèi)仪壮,雙緩沖只會(huì)提供兩個(gè)Buffer胳徽,B被GPU處理占用积锅,A正在用顯示养盗,那么在第二個(gè)16ms里面,CPU就無(wú)法獲取到Buffer處理UI更新往核,在Jank的階段空空等待。而且蝶缀,一般出現(xiàn)這種場(chǎng)景都是連續(xù)的:比如復(fù)雜視覺(jué)效果,那么GPU可能會(huì)一直超負(fù)荷翁都,CPU一直跟GPU搶Buffer,這樣帶來(lái)的問(wèn)題就是滾雪球似的掉幀鳍悠,一直浪費(fèi),完全沒(méi)有利用CPU與GPU并行處理的效率坐搔,成了串行處理,如下所示
如何處理呢蠢挡?多增加一個(gè)Buffer給CPU用凳忙,讓它提前忙起來(lái),這樣就能做到三方都有Buffer可用勤家,CPU不用跟GPU爭(zhēng)一個(gè)Buffer柳恐,真正實(shí)現(xiàn)并行處理。如下:
如上圖所示讼庇,雖然即使每幀需要20ms【CPU 8ms +GPU 12ms】伤提,但是由于多加了一個(gè)Buffer认烁,實(shí)現(xiàn)了CPU跟GPU并行,便可以做到了只在開(kāi)始掉一幀舶沛,后續(xù)卻不掉幀窗价,雙緩沖充分利用16ms做到低延時(shí),三緩沖保障了其穩(wěn)定性撼港,為什么4緩沖沒(méi)必要呢骤竹?因?yàn)槿齻€(gè)既可保證并行蒙揣,四個(gè)徒增資源浪費(fèi)开瞭。 在Android系統(tǒng)中,雙緩沖不僅僅是兩份存儲(chǔ)嗤详,它是一個(gè)概念,雙緩沖是一條鏈路递宅,不是某一個(gè)環(huán)節(jié)冬筒,是整個(gè)系統(tǒng)采用的一個(gè)機(jī)制,需要各個(gè)環(huán)節(jié)的支持舞痰,從APP到SurfaceFlinger、到圖像顯示都要參與協(xié)作玷禽。對(duì)于APP端而言呀打,每個(gè)Window都是一個(gè)雙緩沖的模型,一個(gè)Window對(duì)應(yīng)一個(gè)Surface贬丛,而每個(gè)Surface里至少映射兩個(gè)存儲(chǔ)區(qū),一個(gè)給圖層合成顯示用额获,一個(gè)給APP端圖形處理恭应,這便是應(yīng)于上層的雙緩沖。
總結(jié)
- 同步是防止畫面撕裂的關(guān)鍵昼榛,VSYNC同步能防止畫面撕裂
- VSYNC+雙緩沖在Android中能有序規(guī)劃渲染流程,降低延時(shí)
- Android已經(jīng)采用了雙緩沖奥喻,雙緩沖不僅僅是兩份存儲(chǔ),它是一個(gè)概念衫嵌,雙緩沖是一條鏈路,不是某一個(gè)環(huán)節(jié)结闸,是整個(gè)系統(tǒng)采用的一個(gè)機(jī)制酒朵,需要各個(gè)環(huán)節(jié)的支持,從APP到SurfaceFlinger蔫耽、到圖像顯示都要參與協(xié)作
- 三緩沖在UI復(fù)雜情況下能保證畫面的連續(xù)性,提高柔韌性
參考文檔
Google I/O 2012 - For Butter or Worse: Smoothing Out Performance in Android UIs
Android Performance Patterns: Understanding VSYNC
作者:看書(shū)的小蝸牛
Android VSYNC與圖形系統(tǒng)中的雙緩沖图甜、三緩沖淺析
僅供參考鳖眼,歡迎指正