文章都是通過閱讀源碼分析出來的,還在不斷完善與改進(jìn)中,其中難免有些地方理解得不對(duì),歡迎大家批評(píng)指正。
轉(zhuǎn)載請(qǐng)注明:From LXS. http://blog.csdn.net/uiop78uiop78/
GUI系統(tǒng)之SurfaceFlinger章節(jié)目錄:
blog.csdn.net/uiop78uiop78/article/details/8954508
從這一小節(jié)開始,我們正式切入SurfaceFlinger的分析。為了保持講解的連貫性,部分內(nèi)容可能在前面的章節(jié)中已經(jīng)有所涉及了,接下來將會(huì)對(duì)其中的細(xì)節(jié)做更多的擴(kuò)展講解。
內(nèi)容組織如下:
l 首先介紹Android 4.1引入的新特性(Project Butter),理解這個(gè)項(xiàng)目是必要的,可以說SurfaceFlinger有很大一部分的內(nèi)容就是圍繞它來的
l SurfaceFlinger的啟動(dòng)過程及工作方式
l SurfaceFlinger與BufferQueue及應(yīng)用程序間的關(guān)系
l SurfaceFlinger對(duì)VSYNC信號(hào)的處理過程(重點(diǎn))
直譯過來,就是“黃油計(jì)劃”,為什么叫這個(gè)名字呢?這個(gè)Project的目的是為了改善用戶抱怨最多的Android幾大缺陷之一,即UI響應(yīng)速度——Google希望這一新計(jì)劃可以讓Android系統(tǒng)擺脫UI交互上給人帶來的“滯后”感,而能像黃油一般“順滑”。Google在2012年的I/O大會(huì)上宣布了這一計(jì)劃,并在Android 4.1中正式搭載了實(shí)現(xiàn)機(jī)制。
Butter中有兩個(gè)重要的組成部分,即VSync和Triple Buffering。下面先分別介紹引入它們的原因。
喜歡玩游戲或者看電影的讀者可能遇到過這樣的情形:
某些游戲場面好像是幾個(gè)場景“拼湊”而成的
電影畫面不連貫,好像被“割裂”了
這樣子描述有點(diǎn)抽象,我們引用widipedia上的一張圖來看下實(shí)際的效果:
圖 11?20 Screen Tearing實(shí)例
引自http://en.wikipedia.org/wiki/File:Tearing_%28simulated%29.jpg
我們把這種顯示錯(cuò)誤稱為“screentearing”,那么為什么會(huì)出現(xiàn)這樣的情況呢?
相信大家都能得出結(jié)論,那就是屏幕上顯示的畫面實(shí)際上來源于多個(gè)“幀”。
在一個(gè)典型的顯示系統(tǒng)中,frame buffer代表了屏幕即將要顯示的一幀畫面。假如CPU/GPU繪圖過程與屏幕刷新所使用的buffer是同一塊,那么當(dāng)它們的速度不同步的時(shí)候,是很可能出現(xiàn)類似的畫面“割裂”的。舉個(gè)具體的例子來說,假設(shè)顯示器的刷新率為66Hz,而CPU/GPU繪圖能力則達(dá)到100Hz,也就是它們處理完成一幀數(shù)據(jù)分別需要0.015秒和0.01秒。
以時(shí)間為橫坐標(biāo)來描述接下來會(huì)發(fā)生的事情,如下圖:
圖 11?21 Screen Tearing產(chǎn)生過程分析
上半部分的方框表示在不同的時(shí)間點(diǎn)時(shí)顯示屏的內(nèi)容(加深的部分),下半部分則是同一時(shí)間點(diǎn)時(shí)frame buffer中的數(shù)據(jù)狀態(tài),編號(hào)表示第幾個(gè)frame,不考慮清屏。
· 0.01秒
由于兩者速率相差不小,此時(shí)buffer中已經(jīng)準(zhǔn)備好了第1幀數(shù)據(jù),顯示器只顯示了第1幀畫面的2/3
· 0.015秒
第1幀畫面完整地顯示出來了,此時(shí)buffer中有1/3的部分已經(jīng)被填充上第2幀數(shù)據(jù)了
· 0.02秒
Buffer中已經(jīng)準(zhǔn)備好第2幀數(shù)據(jù),而顯示屏出現(xiàn)了screen tearing,有三分之一是第2幀內(nèi)容,其余的則屬于第1幀畫面
在單緩沖區(qū)的情況下,這個(gè)問題很難規(guī)避。所以之前我們介紹了雙緩沖技術(shù),基本原理就是采用兩塊buffer。一塊back buffer用于CPU/GPU后臺(tái)繪制,另一塊framebuffer則用于顯示,當(dāng)back buffer準(zhǔn)備就緒后,它們才進(jìn)行交換。不可否認(rèn),doublebuffering可以在很大程度上降低screen tearing錯(cuò)誤,但是它是萬能的嗎?
一個(gè)需要考慮的問題是我們什么時(shí)候進(jìn)行兩個(gè)緩沖區(qū)的交換呢?假如是back buffer準(zhǔn)備完成一幀數(shù)據(jù)以后就進(jìn)行,那么如果此時(shí)屏幕還沒有完整顯示上一幀內(nèi)容的話,肯定是會(huì)出問題的??磥碇荒苁堑鹊狡聊惶幚硗暌粠瑪?shù)據(jù)后,才可以執(zhí)行這一操作了。
我們知道,一個(gè)典型的顯示器有兩個(gè)重要特性,行頻和場頻。行頻(Horizontal ScanningFrequency)又稱為“水平掃描頻率”,是屏幕每秒鐘從左至右掃描的次數(shù); 場頻(Vertical Scanning Frequency)也稱為“垂直掃描頻率”,是每秒鐘整個(gè)屏幕刷新的次數(shù)。由此也可以得出它們的關(guān)系:行頻=場頻*縱坐標(biāo)分辨率。
當(dāng)掃描完一個(gè)屏幕后,設(shè)備需要重新回到第一行以進(jìn)入下一次的循環(huán),此時(shí)有一段時(shí)間空隙,稱為VerticalBlanking Interval(VBI)。大家應(yīng)該能想到了,這個(gè)時(shí)間點(diǎn)就是我們進(jìn)行緩沖區(qū)交換的最佳時(shí)間。因?yàn)榇藭r(shí)屏幕沒有在刷新,也就避免了交換過程中出現(xiàn)screentearing的狀況。VSync(垂直同步)是VerticalSynchronization的簡寫,它利用VBI時(shí)期出現(xiàn)的vertical sync pulse來保證雙緩沖在最佳時(shí)間點(diǎn)才進(jìn)行交換。
所以說V-sync這個(gè)概念并不是Google首創(chuàng)的,它在早些年前的PC機(jī)領(lǐng)域就已經(jīng)出現(xiàn)了。不過Android 4.1給它賦予了新的功用,稍后就可以看到。
上面我們討論的情況基于的假設(shè)是繪圖速度大于顯示速度,那么如果反過來呢?
圖 11?22繪圖過程沒有采用VSync同步的情況
引用自Google2012 I/O,作者Chet Haase和Romain Guy,AndroidUI Toolkit Engineers
這個(gè)圖中有三個(gè)元素,Display是顯示屏幕,GPU和CPU負(fù)責(zé)渲染幀數(shù)據(jù),每個(gè)幀以方框表示,并以數(shù)字進(jìn)行編號(hào),如0、1、2等等。VSync用于指導(dǎo)雙緩沖區(qū)的交換。
以時(shí)間的順序來看下將會(huì)發(fā)生的異常:
Step1. Display顯示第0幀數(shù)據(jù),此時(shí)CPU和GPU渲染第1幀畫面,而且趕在Display顯示下一幀前完成
Step2. 因?yàn)殇秩炯皶r(shí),Display在第0幀顯示完成后,也就是第1個(gè)VSync后,正常顯示第1幀
Step3. 由于某些原因,比如CPU資源被占用,系統(tǒng)沒有及時(shí)地開始處理第2幀,直到第2個(gè)VSync快來前才開始處理
Step4. 第2個(gè)VSync來時(shí),由于第2幀數(shù)據(jù)還沒有準(zhǔn)備就緒,顯示的還是第1幀。這種情況被Android開發(fā)組命名為“Jank”。
Step5. 當(dāng)?shù)?幀數(shù)據(jù)準(zhǔn)備完成后,它并不會(huì)馬上被顯示,而是要等待下一個(gè)VSync。
所以總的來說,就是屏幕平白無故地多顯示了一次第1幀。原因大家應(yīng)該都看到了,就是CPU沒有及時(shí)地開始著手處理第2幀的渲染工作,以致“延誤軍機(jī)”。 Android系統(tǒng)中一直存在著這個(gè)問題,即便是上一版本的Ice Cream Sandwich。
從Android 4.1Jelly Bean開始,VSync得到了進(jìn)一步的應(yīng)用。系統(tǒng)在收到VSync pulse后,將馬上開始下一幀的渲染。
圖 11?23 整個(gè)顯示系統(tǒng)都以VSync進(jìn)行同步
如上圖所示,一旦VSync出現(xiàn)后,CPU不再猶豫,緊接著就開始執(zhí)行buffer的準(zhǔn)備工作。大部分的Android顯示設(shè)備刷新率是60Hz,這也就意味著每一幀最多只能有1/60=16ms左右的準(zhǔn)備時(shí)間。假如CPU/GPU的FPS(FramesPer Second)高于這個(gè)值,那么這個(gè)方案是完美的,顯示效果將很好。
可是我們沒有辦法保證所有設(shè)備的硬件配置都能達(dá)到要求。假如CPU/GPU的性能無法滿足上圖的條件,又是什么情況呢?
在分析這一問題之前,我們先來看下正常情況下,采用雙緩沖區(qū)的系統(tǒng)的運(yùn)行情況。
圖 11?24 雙緩沖展示
這個(gè)圖采用了雙緩沖,以及前面介紹的VSync,可以看到整個(gè)過程還是相當(dāng)不錯(cuò)的,雖然CPU/GPU處理所用的時(shí)間時(shí)短時(shí)長,但總的來說都在16ms以內(nèi),因而不影響顯示效果。A和B分別代表兩個(gè)緩沖區(qū),它們不斷地交換來正確顯示畫面。
現(xiàn)在我們可以繼續(xù)分析FPS低于屏幕刷新率的情況。
如下圖所示:
圖 11?25 FPS低于屏幕刷新率的情況
當(dāng)CPU/GPU的處理時(shí)間超過16ms時(shí),第一個(gè)VSync到來時(shí),緩沖區(qū)B中的數(shù)據(jù)還沒有準(zhǔn)備好,于是只能繼續(xù)顯示之前A緩沖區(qū)中的內(nèi)容。而B完成后,又因?yàn)槿狈Sync pulse信號(hào),它只能等待下一個(gè)signal的來臨。于是在這一過程中,有一大段時(shí)間是被浪費(fèi)的。當(dāng)下一個(gè)VSync出現(xiàn)時(shí),CPU/GPU馬上執(zhí)行操作,此時(shí)它可操作的buffer是A,相應(yīng)的顯示屏對(duì)應(yīng)的就是B。這時(shí)看起來就是正常的。只不過由于執(zhí)行時(shí)間仍然超過16ms,導(dǎo)致下一次應(yīng)該執(zhí)行的緩沖區(qū)交換又被推遲了——如此循環(huán)反復(fù),便出現(xiàn)了越來越多的“Jank”。
那么有沒有規(guī)避的辦法呢?
很顯然,第一次的Jank看起來是沒有辦法的,除非升級(jí)硬件配置來加快FPS。我們關(guān)注的重點(diǎn)是被CPU/GPU浪費(fèi)的時(shí)間段,怎么才能充分利用起來呢?分析上述的過程,造成CPU/GPU無事可做的假象是因?yàn)楫?dāng)前已經(jīng)沒有可用的buffer了。換句話說,如果增加一個(gè)buffer,情況會(huì)不會(huì)好轉(zhuǎn)呢?
圖 11?26 Triple Buffering
Triple Buffering是MultipleBuffering的一種,指的是系統(tǒng)使用3個(gè)緩沖區(qū)用于顯示工作。我們來逐步分析下這個(gè)新機(jī)制是否有效。首先和預(yù)料中的一致,第一次“Jank”無可厚非。不過讓人欣慰的是,當(dāng)?shù)谝淮蜼Sync發(fā)生后,CPU不用再等待了,它會(huì)使用第三個(gè)buffer C來進(jìn)行下一幀數(shù)據(jù)的準(zhǔn)備工作。雖然對(duì)緩沖區(qū)C的處理所需時(shí)間同樣超過了16ms,但這并不影響顯示屏——第2次VSync到來后,它選擇buffer B進(jìn)行顯示;而第3次VSync時(shí),它會(huì)接著采用C,而不是像double buffering中所看到的情況一樣只能再顯示一遍B了。這樣子就有效地降低了系統(tǒng)顯示錯(cuò)誤的機(jī)率。
前面小節(jié)我們看到BufferQueue中最多有32個(gè)BufferSlot,不過在實(shí)際使用時(shí)具體值是可以設(shè)置的。
· TARGET_DISABLE_TRIPLE_BUFFERING
這個(gè)宏用于disable triple buffering。如果宏打開的話,Layer.cpp在onFirstRef有如下操作:
#ifdefTARGET_DISABLE_TRIPLE_BUFFERING
#warning"disabling triple buffering"
mSurfaceTexture->setBufferCountServer(2);
#else
mSurfaceTexture->setBufferCountServer(3);
#endif
也就是將mSurfaceTexture(即BufferQueue)中的mServerBufferCount設(shè)為2,否則就是3
· 對(duì)于應(yīng)用程序來說,它也可以通過ISurfaceTexture::setBufferCount來告訴BufferQueue它希望的Slot值,對(duì)應(yīng)的則是mClientBufferCount。默認(rèn)情況下這個(gè)變量是0,表示應(yīng)用端不關(guān)心到底有多少buffer可用。
· BufferQueue中還有另一個(gè)變量mBufferCount,默認(rèn)值是MIN_ASYNC_BUFFER_SLOTS。在具體的實(shí)現(xiàn)中,以上這三個(gè)變量都是要考慮到的,BufferQueue會(huì)通過權(quán)衡各個(gè)值來選擇最佳的解決方式
請(qǐng)大家務(wù)必理解本小節(jié)的幾個(gè)場景分析,明白采用TripleBuffering、VSync機(jī)制的原因。帶著這些理解進(jìn)入SurfaceFlinger的學(xué)習(xí),可以幫助我們“有的放矢”,對(duì)源碼的分析也能事半功倍。
聯(lián)系客服