前言
一個(gè)新的項(xiàng)目不管在什么情況下,畫面都只能維持30幀左右,不能達(dá)到60幀。
一般這種問(wèn)題首先是轉(zhuǎn)給性能組分析,那就讓我開始分析吧。
一、最簡(jiǎn)單的demo
首先我寫了一個(gè)最簡(jiǎn)單的demo,看看能不能達(dá)到60幀,結(jié)果無(wú)法只能達(dá)到30幀。

1.1 dequeueBuffer時(shí)間長(zhǎng)
一般就是沒(méi)有可用的buffer,SurfaceFlinger的消費(fèi)能力有問(wèn)題,需要去看SurfaceFlinger的Trace。
1.2 waiting for GPU completion時(shí)間長(zhǎng)
一般是GPU的性能不行導(dǎo)致了繪制時(shí)間過(guò)長(zhǎng),但是我的demo就畫了一根線,不可能是GPU性能的問(wèn)題,有可能是GPU沒(méi)有及時(shí)signal,導(dǎo)致了timeout。雖然我沒(méi)有找到GPU繪制完成signal代碼,但是我很快就放棄了這個(gè)思路。因?yàn)閣aitForever中雖然有3000ms的timeout溫馨提示,但是最后還是會(huì)繼續(xù)等,而且是timeout never。
status_t Fence::waitForever(const char* logname) {
ATRACE_CALL();
if (mFenceFd == -1) {
return NO_ERROR;
}
int warningTimeout = 3000;//溫馨提示3000ms,
int err = sync_wait(mFenceFd, warningTimeout);
if (err < 0 && errno == ETIME) {
ALOGE("waitForever: %s: fence %d didn't signal in %u ms", logname, mFenceFd.get(),
warningTimeout);
...
err = sync_wait(mFenceFd, TIMEOUT_NEVER);//這里是time out never
}
return err < 0 ? -errno : status_t(NO_ERROR);
}
加入waiting for HWC release以后,原來(lái)是release的fence信號(hào)signal慢了,導(dǎo)致的GPU completion的時(shí)間也變長(zhǎng)了(S平臺(tái)和之前的平臺(tái)對(duì)于release buffer的流程有所差異)。
為什么waiting for HWC release會(huì)慢就需要去看SurfaceFlinger了。

1.3 小結(jié)
兩個(gè)問(wèn)題點(diǎn)最后都需要指向到SurfaceFlinger,我們繼續(xù)查看SF的Trace。
PS:以后遇到waiting for GPU completion時(shí)間長(zhǎng)的問(wèn)題,不能直接下定論是GPU性能不行。
二、SurfaceFlinger分析
一看SurfaceFlinger發(fā)現(xiàn)非常奇怪的事情,sf竟然繪制一幀,丟一幀。

丟一幀的原因是framePending為true,hwcFrameMissed為true,gpuFrameMissed為false。
然后滿足了提前return的條件。
void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime) {
....
// Pending frames may trigger backpressure propagation.
const TracedOrdinal<bool> framePending = {"PrevFramePending",
previousFramePending(graceTimeForPresentFenceMs)};
const TracedOrdinal<bool> frameMissed = {"PrevFrameMissed",
framePending ||
(previousPresentTime >= 0 &&
(lastScheduledPresentTime <
previousPresentTime - frameMissedSlop))};
const TracedOrdinal<bool> hwcFrameMissed = {"PrevHwcFrameMissed",
mHadDeviceComposition && frameMissed};
const TracedOrdinal<bool> gpuFrameMissed = {"PrevGpuFrameMissed",
mHadClientComposition && frameMissed};
....
// framePending true
// frameMissed true
// hwcFrameMissed true
// gpuFrameMissed false
if (framePending) {
if ((hwcFrameMissed && !gpuFrameMissed) || mPropagateBackpressureClientComposition) {
signalLayerUpdate();
return;//滿足條件提前返回。
}
}
...
}
而且從圖中看到,waiting for presentFence,而且整個(gè)wait過(guò)程竟然需要27.4ms。也就說(shuō)sf合成后到開始刷新這一幀到屏幕需要27ms。一時(shí),我也無(wú)法繼續(xù)跟蹤下去了,因?yàn)閷?duì)HWC我不是很熟悉。
三、PLL_CLK值有問(wèn)題
好在驅(qū)動(dòng)工程師突然告訴我說(shuō)PLL_CLK有問(wèn)題,從475改成了560問(wèn)題就解決了。
當(dāng)時(shí)我就一面懵逼,PLL_CLK是什么東西,這個(gè)數(shù)值代表什么意思。
3.1 PLL_CLK是什么
PLL_CLK就是圖中CLK的那段波的頻率,也就每秒一次高低電頻發(fā)生的次數(shù)。

3.2 CMD屏PLL_CLK計(jì)算公式
(Data rate) = width * height * 1.2 * total_bit_per_pixel * frame_per_second / total_lane_num
DSI采用的是雙邊采樣,則clk等于數(shù)據(jù)速率的一半,也就是說(shuō)一個(gè)clk周期內(nèi)傳送2位,所以你計(jì)算出來(lái)的值還要除以2
即PLL_CLOCK = Data rate / 2 (單位是MHZ)
PS:其中1.2應(yīng)該是一個(gè)經(jīng)驗(yàn)值。
經(jīng)過(guò)計(jì)算我們屏幕PLL_CLK合適的值應(yīng)該是559左右
width = 1080 (屏幕分辨率是1080 * 2400)
height = 2400
total_bit_per_pixel = 24 (RGB值,每個(gè)字節(jié)是8位,三個(gè)字節(jié))
frame_per_second = 60 (60幀的屏幕)
total_lane_num = 4(4根線)
Data rate = 1080 * 2400 * 1.2 * 24 * 60 / 4 = 1119744000
即PLL_CLOCK = Data rate / 2 = 559872000 = 559.872MHZ
公式可能看不明白,這樣子解釋你就明白了。
1秒內(nèi)60hz的手機(jī)需要傳遞的數(shù)據(jù)是多少。
屏幕的寬x屏幕的高x每個(gè)像素點(diǎn)的數(shù)據(jù)量x每秒的幀率。
1080x2400x24x60
由于有4根傳輸線,并且一次高低電頻可以傳輸2次,所以PLL_CLOCK至少要達(dá)到以下數(shù)值
1080x2400x24x60/4/2
但是不能那么小氣,加上一個(gè)經(jīng)驗(yàn)值1.2
1080x2400x24x60x1.2/4/2 = 559872000 = 559.872MHZ
3.3 小結(jié)
之前設(shè)置的PLL_CLK值過(guò)小,傳輸速率過(guò)低,導(dǎo)致前一幀無(wú)法在一個(gè)vsync周期內(nèi)將屏幕的數(shù)據(jù)傳輸給屏幕,導(dǎo)致這一幀的presentFence等待signal時(shí)間過(guò)久,然后sf主動(dòng)丟了一幀,從而導(dǎo)致屏幕從60fps降為了30fps。但是目前presentFence和傳輸數(shù)據(jù)給屏幕之前的關(guān)系,我還沒(méi)有找到對(duì)應(yīng)的代碼,因?yàn)槲覍?duì)驅(qū)動(dòng)不是很熟悉。
四、整個(gè)過(guò)程還原
可以用已經(jīng)掌握的知識(shí)來(lái)還原整個(gè)上層的流程,整個(gè)過(guò)程更加清晰了。

總結(jié)
整個(gè)問(wèn)題還是非常有意思的,強(qiáng)烈推薦大家閱讀參考資料中的文章,讓我對(duì)屏幕顯示畫面有了更加深入的理解,而且也終于理解了為什么畫面會(huì)有出現(xiàn)撕裂。
參考資料
http://www.itdecent.cn/p/df46e4b39428
這幾個(gè)圖畫的是真好,仍不住轉(zhuǎn)載一下


尾巴
當(dāng)然有時(shí)間還是想去看看顯示驅(qū)動(dòng)那塊的代碼,給自己留幾個(gè)問(wèn)題。
有知道朋友歡迎留言解惑。
presentFence 喚醒的代碼位置
GPU completion 喚醒的代碼位置
HWC release 喚醒的代碼位置