前言
一個新的項目不管在什么情況下椭更,畫面都只能維持30幀左右,不能達到60幀蛾魄。
一般這種問題首先是轉給性能組分析虑瀑,那就讓我開始分析吧。
一滴须、最簡單的demo
首先我寫了一個最簡單的demo缴川,看看能不能達到60幀,結果無法只能達到30幀描馅。
1.1 dequeueBuffer時間長
一般就是沒有可用的buffer,SurfaceFlinger的消費能力有問題而线,需要去看SurfaceFlinger的Trace铭污。
1.2 waiting for GPU completion時間長
一般是GPU的性能不行導致了繪制時間過長恋日,但是我的demo就畫了一根線,不可能是GPU性能的問題嘹狞,有可能是GPU沒有及時signal岂膳,導致了timeout。雖然我沒有找到GPU繪制完成signal代碼磅网,但是我很快就放棄了這個思路谈截。因為waitForever中雖然有3000ms的timeout溫馨提示,但是最后還是會繼續(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以后燎潮,原來是release的fence信號signal慢了喻鳄,導致的GPU completion的時間也變長了(S平臺和之前的平臺對于release buffer的流程有所差異)。
為什么waiting for HWC release會慢就需要去看SurfaceFlinger了确封。
1.3 小結
兩個問題點最后都需要指向到SurfaceFlinger除呵,我們繼續(xù)查看SF的Trace。
PS:以后遇到waiting for GPU completion時間長的問題爪喘,不能直接下定論是GPU性能不行颜曾。
二、SurfaceFlinger分析
一看SurfaceFlinger發(fā)現非常奇怪的事情秉剑,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聚请,而且整個wait過程竟然需要27.4ms荠雕。也就說sf合成后到開始刷新這一幀到屏幕需要27ms。一時驶赏,我也無法繼續(xù)跟蹤下去了炸卑,因為對HWC我不是很熟悉。
三煤傍、PLL_CLK值有問題
好在驅動工程師突然告訴我說PLL_CLK有問題盖文,從475改成了560問題就解決了。
當時我就一面懵逼蚯姆,PLL_CLK是什么東西五续,這個數值代表什么意思洒敏。
3.1 PLL_CLK是什么
PLL_CLK就是圖中CLK的那段波的頻率,也就每秒一次高低電頻發(fā)生的次數疙驾。
3.2 CMD屏PLL_CLK計算公式
(Data rate) = width * height * 1.2 * total_bit_per_pixel * frame_per_second / total_lane_num
DSI采用的是雙邊采樣凶伙,則clk等于數據速率的一半,也就是說一個clk周期內傳送2位它碎,所以你計算出來的值還要除以2
即PLL_CLOCK = Data rate / 2 (單位是MHZ)
PS:其中1.2應該是一個經驗值函荣。
經過計算我們屏幕PLL_CLK合適的值應該是559左右
width = 1080 (屏幕分辨率是1080 * 2400)
height = 2400
total_bit_per_pixel = 24 (RGB值,每個字節(jié)是8位扳肛,三個字節(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秒內60hz的手機需要傳遞的數據是多少敞峭。
屏幕的寬x屏幕的高x每個像素點的數據量x每秒的幀率踊谋。
1080x2400x24x60
由于有4根傳輸線,并且一次高低電頻可以傳輸2次旋讹,所以PLL_CLOCK至少要達到以下數值
1080x2400x24x60/4/2
但是不能那么小氣殖蚕,加上一個經驗值1.2
1080x2400x24x60x1.2/4/2 = 559872000 = 559.872MHZ
3.3 小結
之前設置的PLL_CLK值過小,傳輸速率過低沉迹,導致前一幀無法在一個vsync周期內將屏幕的數據傳輸給屏幕睦疫,導致這一幀的presentFence等待signal時間過久,然后sf主動丟了一幀鞭呕,從而導致屏幕從60fps降為了30fps蛤育。但是目前presentFence和傳輸數據給屏幕之前的關系,我還沒有找到對應的代碼葫松,因為我對驅動不是很熟悉瓦糕。
四、整個過程還原
可以用已經掌握的知識來還原整個上層的流程腋么,整個過程更加清晰了咕娄。
總結
整個問題還是非常有意思的,強烈推薦大家閱讀參考資料中的文章珊擂,讓我對屏幕顯示畫面有了更加深入的理解圣勒,而且也終于理解了為什么畫面會有出現撕裂。
參考資料
http://www.reibang.com/p/df46e4b39428
這幾個圖畫的是真好摧扇,仍不住轉載一下
尾巴
當然有時間還是想去看看顯示驅動那塊的代碼扛稽,給自己留幾個問題吁峻。
有知道朋友歡迎留言解惑。
presentFence 喚醒的代碼位置
GPU completion 喚醒的代碼位置
HWC release 喚醒的代碼位置