幀率FPS 是 Frame Per Second 的縮寫,意思是每秒產(chǎn)生畫面的個數(shù),是一個軟件的概念,與屏幕刷新率這個硬件概念要區(qū)分開,F(xiàn)PS 是由軟件系統(tǒng)決定的。如果幀率為60fps,也就是1/60 ~= 16.67ms要更新一次屏幕。
需要注意的是,顯示器并不是一次性將畫面顯示到屏幕上,而是從左到右,從上到下逐行掃描,順序顯示整屏的像素點,不過這一過程快到人眼無法察覺到變化。

當(dāng)幀率和刷新率不一致,會發(fā)生如下情況:
屏幕刷新速率比系統(tǒng)幀速率快
此時,在前緩沖區(qū)內(nèi)容全部映射到屏幕上之后,后緩沖區(qū)尚未準(zhǔn)備好下一幀,屏幕將無法讀取下一幀,所以只能繼續(xù)顯示當(dāng)前一幀的圖形,造成一幀顯示多次,也就是卡頓(Jank)。
系統(tǒng)幀速率比屏幕刷新率快
此時,屏幕未完全把緩沖區(qū)的一幀映射到屏幕,而系統(tǒng)已經(jīng)在準(zhǔn)備好了下一幀,并寫入到緩沖區(qū)中,要求讀取下一幀到屏幕,將會導(dǎo)致屏幕上半部分是上一幀的圖形,而下半部分是下一幀的圖形,造成屏幕上顯示多幀,也就是屏幕撕裂(Tearing)。
Buffer
顯示屏上的內(nèi)容,是從硬件幀緩沖區(qū)讀取的,大致讀取過程為:從Buffer的起始地址開始,從上往下,從左往右掃描整個Buffer,將內(nèi)容映射到顯示屏上:
Vsync+Double Buffer
為了解決撕裂的問題,Android使用了Vsync+Double Buffer技術(shù)
Double Buffer

由于圖像繪制和屏幕讀取使用的同一個buffer,所以屏幕刷新的時候可能讀取到的是不完整的一幀畫面。因此我們使用雙緩存技術(shù),如上圖所示,讓圖像繪制和屏幕擁有的各自的buffer,cpu/gpu始終將完成的一幀圖像寫入到Back Buffer,顯示器讀取使用Frame Buffer,當(dāng)屏幕刷新時,F(xiàn)rame Buffer并不會發(fā)生變化,當(dāng)Back Buffer準(zhǔn)備就緒時才進行兩個buffer交換。
問題來了,什么時候進行兩個Buffer的交換呢?
Vsync
VSync 是垂直同期( Vertical Synchronization) 的簡稱。VSync 信號負責(zé)調(diào)度從 Back Buffer 到 Frame Buffer 的復(fù)制操作,可認為該復(fù)制操作在瞬間完成,因為實際上雙緩沖的實現(xiàn)方式是交換 Back Buffer 和 Frame Buffer 的內(nèi)存地址。
具體流程可以用下圖來描述:

其中:
CPU和GPU代表上層的繪制執(zhí)行者,各做一部分工作,本文章中對CPU和GPU的工作不進行介紹。
Composite代表的是Android系統(tǒng)服務(wù)對多個Surface的合成
Background Buffer和Front Buffer(Frame Buffer)分別代表的是硬件幀緩沖區(qū)中的前緩沖和后緩沖
顯示屏掃描完一幀之后,會發(fā)出VSync信號來切換并顯示下一幀
上面的流程中,存在一個問題,屏幕的VSync信號只是用來控制幀緩沖區(qū)的切換,并未控制上層的繪制節(jié)奏,也就是說上層CPU/GPU的生產(chǎn)節(jié)奏和屏幕的顯示節(jié)奏是脫離的:

上圖中,橫軸表示時間,縱軸表示Buffer的使用者,每個長方形表示Buffer的使用,長方形的寬度代表使用時長,VSync代表垂直同步信號,兩個VSync信號之間間隔16.6ms。此圖描述了Android在4.1系統(tǒng)版本之前,上層的繪圖流程在沒有VSync信號的時候,出現(xiàn)的繪制問題。
我們從時間為0開始看,當(dāng)前屏幕顯示第0幀,上層CPU開始計算第1幀的紋理,計算完成后,交由GPU進行柵格化。當(dāng)下一個垂直同步信號到來,屏幕顯示下一幀,這時候,上層CPU并未馬上開始準(zhǔn)備下一幀(不管出于什么原因,可能正在處理其他任務(wù)),而當(dāng)CPU開始準(zhǔn)備下一幀的時候已經(jīng)太晚了,下一個VSync信號來臨的時候,GPU未能繪制完第二幀的處理,導(dǎo)致屏幕再次顯示上一幀,造成卡頓。
Drawing With VSync
因為上層不知道VSync信號已經(jīng)發(fā)出,導(dǎo)致上層未能開始CPU的計算。google在Android 4.1系統(tǒng)中加入了上層接收垂直同步信號的邏輯,大致流程如下:

屏幕在顯示完一幀后,發(fā)出的垂直同步除了通知幀緩沖區(qū)的切換之外,該消息還會發(fā)送到上層,通知上層開始繪制下一幀。

時間從屏幕顯示第0幀開始,CPU開始準(zhǔn)備第1幀圖形的處理,好了之后交給GPU進行處理,在上層收到下一個VSync之后,CPU立馬開始第2幀的處理,上層繪圖的節(jié)奏就和VSync信號保持一致了,整個繪圖非常流暢。
小結(jié)一下Vsync的作用:當(dāng)屏幕掃描完第1幀畫面之后,系統(tǒng)發(fā)送VSync信號,這時會發(fā)生三件事:
交換兩個緩存區(qū)(framebuffer、backbuffer)內(nèi)容。
顯示器開始顯示第2幀內(nèi)容,也就是交換后的framebuffer內(nèi)容。
CPU/GPU開始計算處理第三幀的內(nèi)容,并在處理好內(nèi)容后放到backbuffer中。

提到垂直同步這里就多提一句,其實我認為對于PC上的大型游戲來說,只有配置足夠高,高到顯卡輸出幀率可以穩(wěn)定的高于顯示器的刷新頻率,才有開啟垂直同步的必要。因為只有這個時候,畫面撕裂才會真正成為一個問題。而對于很多情況下主機性能不足導(dǎo)致游戲輸出幀率低于顯示器的刷新頻率的情況下,尤其是幀率穩(wěn)定在40~60之間時,開啟垂直同步可能會導(dǎo)致幀率倍數(shù)級的下降(具體原因我們在Graphic架構(gòu)一文中提到過,當(dāng)幀生成速度不及VSync速度時,幀率的下降不是平緩的,而且很可能是倍數(shù)級的。當(dāng)然這在android系統(tǒng)上并非嚴(yán)重問題,因為android上很少有高速的復(fù)雜場景的頻繁切換。事實上,在Android的普通應(yīng)用場景下,VSync的使用不僅不會降低幀率,還可以有效解決卡頓問題)。[8]
Android Project Butter(黃油計劃)
問題解決了嗎?沒有,因為試想一下,如果CPU和GPU沒能在下一個VSync信號到來之前完成下一幀的繪制工作,又會是怎么樣的呢?

還是從屏幕顯示第A幀開始,時間進入第一個16.6ms,CPU和GPU合成第B幀,當(dāng)下一個VSync信號到來的時候,GPU未能及時完成第B幀的繪制,此時,屏幕顯示又持有一個Buffer用于顯示,這樣的話,就導(dǎo)致兩個緩沖區(qū)都被占用了,屏幕只能繼續(xù)顯示上一幀,GPU繼續(xù)在另一個緩沖區(qū)中合成第B幀,此時CPU無法開始下一幀的合成,因為緩沖區(qū)用完了,CPU只有在VSync信號來的時候才開始繪制下一幀,也是就是說在第二個16.6ms時間內(nèi),CPU一直處于空閑狀態(tài),未進行下一幀的計算。只有等到第二個VSync信號來了之后,CPU才開始在繪制下一幀。
如果CPU和GPU需要合成的圖形太多,將會導(dǎo)致連續(xù)性的卡頓,如果CPU和GPU大部分時候都無法在16.6ms完成一幀的繪制,將會導(dǎo)致連續(xù)的卡頓現(xiàn)象。于是Android推出了黃油計劃,目的就是讓Android能讓黃油般順滑,引入了Triple Buffer技術(shù),在雙緩存的基礎(chǔ)上多加了一個buffer。

緩存區(qū)backBuffer用于CPU/GPU圖形處理
緩存區(qū)TripleBuffer用于CPU/GPU圖形處理
緩存區(qū)frameBuffer用于顯示器顯示
從上圖可以看出,在第一個VSync到來時,盡管顯示器占了一個Buffer,GPU占了一個Buffer,CPU仍然可以在第三個Buffer中開始下一幀的計算,整個顯示過程就開始時卡頓了一幀,之后都是流暢的。
可以理解為 CPU、GPU、Display每個人都有一個緩存區(qū),這樣三個就能同時做自己的事而互不影響,最大化利用每個模塊。
總結(jié):
fps幀率是軟件的概念,可以理解為gpu每秒生產(chǎn)幀的個數(shù),refresh rate刷新率是硬件的概念,為顯示器每秒刷新的次數(shù)。
單緩沖區(qū)時,當(dāng)幀率和刷新率不匹配會出現(xiàn)卡頓和撕裂現(xiàn)象,后面引入了Double Buffer、Vsync和Triple Buffer等概念來解決卡頓和撕裂問題。
Android4.1之前,VSync信號并未傳遞給上層,導(dǎo)致Buffer的生產(chǎn)與消費節(jié)奏不統(tǒng)一,Android4.1之后,上層開始繪制時機都放到了VSync信號的到來時候。
除了在上層引入VSync機制,Android在4.1還加入了三緩沖,目的是當(dāng)GPU和Display都占用buffer的時候,cpu可以在下一個Vsync到來時獲取Buffer從而正常的工作,減少卡頓的產(chǎn)生。
android 幀率 顯示 安卓屏幕幀率顯示
https://blog.51cto.com/u_14402/6569817