本文學習來源為 Apple tech-talks,僅自己作為學習筆記.如有誤解之處,請不吝指教.
什么是卡頓?
用戶使用App時候可能會輕點按鈕,滑動等操作,得到反饋或在層級中進行視圖轉換,這些動畫構建了一種,用戶和屏幕內容的視覺連接感
但是,動畫如果卡頓,會導致動畫畫面跳躍,打破這種連接感,這會使人感到困惑而非愉悅(強迫癥)
任何時候屏幕上出現 晚于預計的幀都屬于卡頓
show case
在這里我們看到一個膳食計劃app,當用戶在屏幕上滑動手指時,滾動視圖會隨著上移內容作出響應,但在滾動的同時,我們留意到了內容的跳躍.我們來一幀一幀的去看這個
我們就能看到在前三幀里,我們的手指,隨著內容移動,但是在下一幀里,內容似乎不動了,這是因為第三幀重復了 停留在顯示器上形成了另一幀,最后才是第四幀,它似乎跳到了我們手指的位置
第三幀重復了,第四幀延遲了,用戶就看到了卡頓
卡頓產生的原因
卡頓的出現是由于渲染循環 沒有按時完成一幀 我們就來看一下渲染循環
什么是渲染循環?(Render Loop)
渲染循環是一個連續性的過程 通過觸碰事件 傳送給 app 然后轉化到用戶界面 向操作系統傳送 最終呈獻給用戶 這就是循環 隨著設備的刷新率發生
在iPhone和iPad中每秒有60幀,這意味著每 16.67毫秒就可以顯示一個新幀, 在iPad Pro上刷新率是每秒 120 幀,這意味著每8.33 毫秒就可以顯示一個新幀
在每一幀的初始觸發事件的硬件叫做VSYNC,VSYNC表示新幀必須準備就緒的時間 我們在顯示軌跡上將它們標記出來 所以很容易看到截止期渲染循環和 VSYNC 的時間一致 它必須要始終命中檢查點 來讓每幀都做好準備
App渲染的過程
App處理階段
app中的第一個階段,操作舉動會被處理,造成用戶界面的變化,這項工作必須在下一個 VSYNC 之前完成, 這樣下一個階段就能開始了
渲染服務處理階段
第二階段在一個叫做渲染服務器的獨立進程中進行,這個階段用戶界面才真正被渲染,這項工作也必須在下一個 VSYNC之前完成,這樣一幀就能顯示出來了
顯示階段
第三階段, 在顯示之前 對這一幀進行雙幀處理,我們把這稱之為雙緩沖 但還有另外一個模式,為了避免卡頓 系統可能會切換到三緩沖,為渲染服務器提供了一個額外的幀持續時間來完成工作,這是備用模式,這里不做過多講解
整個渲染循環由五個階段組成 循環從第一個階段開始 事件階段 在這個階段 你的 app 處理觸碰事件 決定用戶界面是否需要變化 下一個是提交階段 在提交階段 你的 app 會更新用戶界面 向渲染服務器提交渲染命令 在下一個 VSYNC 中 渲染服務器處理命令 在渲染準備階段 為在圖形處理器上繪制做好準備 在渲染執行階段 圖形處理器將 用戶界面的最后圖像繪制出來 所以在下一個 VSYNC 這一幀將會呈現給用戶
事件階段 Event
你的 app 處理觸碰事件 決定用戶界面是否需要變化
提交階段 commit
在提交階段 你的 app 會更新用戶界面,向渲染服務器提交渲染命令,在下一個 VSYNC 中 渲染服務器處理命令
渲染準備階段 Render prepare
在渲染準備階段 為在圖形處理器上繪制做好準備
渲染執行階段 Render execute
渲染執行階段 圖形處理器將 用戶界面的最后圖像繪制出來
顯示階段 DisPlay
將這一幀將會呈現給用戶
要想每一幀都擁有流暢的用戶體驗 每個階段都很重要
事件處理階段
當 app 更新了圖層的限制范圍 Core Animation 同時會調用 setNeedsLayout 它能夠分辨所有圖層 必須要以重新計算布局作為回應
系統會合并這些“需要布局”的請求 并在提交階段按順序執行 以減少重復工作
提交階段 commit
如果需要任何布局 一旦事件階段結束后 提交階段會自動開始 首先 系統會挑選需要布局的圖層 從父級到子級依次布局
布局是常見的性能瓶頸 所以要牢記 你的 app 只有幾毫秒 來完成這項工作 有些視圖還需要自定義繪圖 像標記、圖像視圖 或者只是任何覆蓋 drawRect 的視圖 如果這些視圖需要視覺更新 他們必須調用 setNeedsDisplay 像布局一樣 系統會合并這些請求 完成所有布局后執行這些操作
有了 Core Animation 這些圖層就變成了圖片 現在所有的圖層都已經布局并繪制好了 整個修改好的圖層樹會被收集 并且發送到渲染服務器進行渲染
渲染準備階段 Render prepare
這里負責將我們的圖層樹轉換為 真正可顯示的圖像
在準備階段 渲染服務器迭代app的圖層樹,準備一個線性管線 這樣圖形處理器就能執行命令
從最上面的圖層開始 它是從父級到子級 同級到同級間進行的 所以圖層是從后到前安排的
渲染執行階段 Render execute
渲染執行階段,這個線性管線從圖形處理器經過 每個圖層組成了最后的紋理 有些圖層需要更長的時間來渲染 這也是我們稍后我們需要討論的,常見性能瓶頸
顯示階段 DisPlay
所以一旦圖形處理器執行,并在右側渲染圖像 就準備好展示在下一個 VSYNC 了
每個渲染循環的階段都具有性能敏感性 并且都有截止期 截止期就是下一個 VSYNC 為了達到目標幀速率 保持低輸入延遲 整個過程實際上是在每一幀中并行進行的
這樣管線就成了并行的 在系統渲染前一個的同時 我們的 app 可以準備一個新幀 所以每個截止期的錯失都很重要
既然大家看到了渲染循環是如何工作的 我們來深入幾種可能在 app 上 看到的卡頓類型
發生卡頓的類型?
提交卡頓發生在 app 的處理中 commit hitch
提交卡頓就是 app 花費過長時間 來處理或提交事件
這個提交用了太長時間,錯過了截止期,所以在下一個 VSYNC渲染服務區沒有東西處理,所以現在必須等待下一個 VSYNC 開始渲染,現在我們把幀傳送時間推遲了一幀,以毫秒計時 這是 iPhone 或 iPad 上的 16.67 毫秒
如果提交工作花了更長的時間 通過了下一個 VSYNC 那么這一幀就晚了兩幀 或者說是 33.34 毫秒在這 33.34 毫秒中,用戶都無法得到順暢的滾動
發生在渲染服務器 Render hitch
渲染卡頓 這種現象會在渲染服務器無法 按時準備或者 執行圖層樹時出現
在這里 顯然執行階段時間過長 超過了 VSYNC 的界限 因此這一幀無法按時準備好 綠色的畫面比預期的晚了一幀
于是我們又有了 16 毫秒的卡頓時間
如何測量卡頓?
檢測卡頓指標 卡頓時間比 Hitch time ratio
解釋: 標準化為總時間 我們就能在不同的實踐中交叉比較 它是由每秒中的卡頓毫秒時間來測定的 所以它代表著設備在每秒內出現卡頓的 毫秒數
舉??說明
在這里我們可以看到 30 幀 在一臺 iPhone 上這是半秒的工作量
每一幀都到達了限定期限 用戶看不到卡頓 卡頓時間為零 卡頓時間比也為零
現在我們看到在底部顯示條上的 間隔不相交區間,發生卡頓的幀
有些在屏幕上的幀比其他的長,有些提交和渲染造成了卡頓,如果我們將這個卡頓時間加起來,結果就是 100.02 毫秒 超過了半秒,我們就得到了每秒 200.04 毫秒的卡頓時間比,
這僅僅是一個例子,大體上來說,這些是建議的目標卡頓時間比,這是我們在 Apple 的工具中使用的,雖然目標是零毫秒每秒的卡頓
1.五毫秒每秒以下的卡頓 -> 不易被用戶察覺到的
2.在五到十毫秒每秒的卡頓之間->用戶就會察覺到一些中斷
3.超過 10 毫秒每秒的卡頓 -> 這些卡頓就會嚴重影響用戶體驗
你應該立即研究如何優化渲染循環
本篇結束.
后續請留意關注,檢測卡頓的方式.