iOS 高級核心動畫 day09 - 性能調(diào)優(yōu):CPU、GPU、離屏渲染

一、CPU VS GPU

1. 對于計算機(jī)硬件,繪圖和動畫有哪兩種處理方式?為什么處理圖像盡量使用 GPU?

  • CPU:中央處理器
  • GPU:圖形處理器
  • 總體來說,我們可以用軟件(CPU)做任何事情,但是對于圖像處理,通常用硬件(GPU)會更快,因?yàn)?GPU 使用圖像對高度和浮點(diǎn)運(yùn)算做了優(yōu)化。

2. 就 CPU 和 GPU 而言,我們所說的動畫性能優(yōu)化,其實(shí)是讓這兩者怎么配合?

  • 大多數(shù)動畫性能優(yōu)化都是關(guān)于合理利用 GPU 和 CPU,使得它們都不會超出負(fù)荷。
  • 于是我們首先需要知道 Core Animation 是如何在這兩個處理器之間分配工作的。

3. Core Animation 處在 iOS 的核心地位:應(yīng)用內(nèi)和應(yīng)用間都會用到它,那對于 iOS 它處于什么層級呢?

  • 一個簡單的動畫可能同步顯示多個 app 的內(nèi)容,例如當(dāng)在 iPad 上多個程序之間使用手勢切換,會使得多個程序同時顯示在屏幕上。
  • 在一個特定的應(yīng)用程序中用代碼實(shí)現(xiàn)它是沒有意義的,因?yàn)樵?iOS 中不可能實(shí)現(xiàn)這種效果(APP 都是被沙箱管理,不能訪問別的視圖)。
  • 動畫和屏幕上組合的圖層實(shí)際上被一個單獨(dú)的進(jìn)程管理,而不是你的應(yīng)用程序。這個進(jìn)制就是所謂的渲染服務(wù)。
  • 在 iOS5 和之前的版本這個渲染服務(wù)叫做SpringBoard 進(jìn)程(同時管理著 iOS 的主屏)。在 iOS6 之后的版本叫做 BackBoard。

4. 一個動畫的展示過程?

在應(yīng)用程序內(nèi),當(dāng)運(yùn)行一段動畫時候,這個過程會被分離成如下四個階段

  • 布局 - 這是準(zhǔn)備你的視圖/圖層的層級關(guān)系,以及設(shè)置圖層屬性(位置,背景色,邊框等等)的階段。
  • 顯示 - 這個圖層的寄宿圖片被繪制的階段。繪制可能涉及你的 -drawRect:-drawLayer:inContext: 方法的調(diào)用路徑。
  • 準(zhǔn)備 - 這是 Core Animation 準(zhǔn)備發(fā)送動畫數(shù)據(jù)到渲染服務(wù)的階段。這同時也是 Core Animation將要執(zhí)行一些別的事務(wù)例如解碼動畫過程。
  • 提交 - 這是最后的階段,Core Animation 打包所有圖層和動畫屬性,然后通過 IPC(進(jìn)程間通信)發(fā)送到渲染服務(wù)進(jìn)行顯示。

但是上面四個階段僅僅發(fā)生在你的應(yīng)用程序之內(nèi),在動畫在屏幕上顯示之前仍然有更多的工作。一旦打包的圖層和動畫到達(dá)渲染服務(wù)進(jìn)程,它們會被反序列化來形成另一個叫做渲染樹的圖層樹。使用這個樹狀結(jié)構(gòu),渲染服務(wù)對動畫的每一幀做出如下工作:

  • ⑤ 對所有的圖層屬性計算中間值,設(shè)置 OpenGL 幾何形狀(紋理化的三角形)來執(zhí)行渲染
  • ⑥ 在屏幕上渲染可見的三角形

所以一共有六個階段;最后兩個階段在動畫過程中不停地重復(fù)。

5. 上面六個階段,哪些在 CPU?哪些在 GPU?哪些階段開發(fā)者能介入?

  • 前五個階段都在軟件層面處理(通過 CPU),只有最后一個被 GPU 執(zhí)行。
  • 而且,你真正只能控制前面兩個階段:布局和顯示。Core Animation 框架在內(nèi)部處理剩下的事務(wù),你也控制不了它。
  • 這個并不是個問題,因?yàn)樵诓季趾惋@示階段,你可以決定哪些由 CPU 執(zhí)行,哪些交給 GPU 去做。那么該如何判斷呢?

6. GPU 主要干些什么事情?

  • GPU 為一個具體的任務(wù)做了優(yōu)化:它用來采集圖片和形狀(三角形),運(yùn)行變換,應(yīng)用紋理和混合然后把它們輸送到屏幕上。

7. 有一些事情會降低(基于 GPU)圖層繪制(至少說兩點(diǎn))?

  • 重繪 - 主要由重疊的半透明圖層引起。GPU 的填充比例(用顏色填充像素的比率)是有限的,所以需要避免重繪(每一幀用相同的像素填充多次)的發(fā)生。
  • 離屏繪制 - 這發(fā)生在當(dāng)不能直接在屏幕上繪制,并且必須回到離屏圖片的上下文的時候。
  • 過大的圖片 - 如果視圖超過 GPU 支持的2048x2048或者4096x4096尺寸的紋理,就必須要用CPU在圖層每次顯示之前對圖片預(yù)處理,

8. 哪些 CPU 的操作會延遲動畫的開始時間,從而造成卡頓(至少說兩點(diǎn))?

  • 布局計算 - 如果你的視圖層級過于復(fù)雜,當(dāng)視圖呈現(xiàn)或者修改的時候,計算圖層幀率就會消耗一部分時間。
  • 大量視圖懶加載 - iOS只會當(dāng)視圖控制器的視圖顯示到屏幕上時才會加載它,但是同一瞬間太多視圖需要加載了。
  • 解壓圖片 - PNG或者JPEG壓縮之后的圖片文件會比同質(zhì)量的位圖小得多。但是在圖片繪制到屏幕上之前,必須把它擴(kuò)展成完整的未解壓的尺寸(通常等同于圖片寬 x 長 x 4個字節(jié))。

二、離屏渲染

1. 如何通過iOS模擬器判斷一個視圖,是否發(fā)生了離屏渲染?

  • 開啟 iOS 模擬器的下面這個選項(xiàng),如果視圖呈換色,就表明發(fā)生了離屏渲染
image.png

2. 為什么有些操作會發(fā)生離屏渲染?

  • 圖層的疊加繪制大概遵循“畫家算法”
  • 畫家算法:先繪制場景中離觀察者較遠(yuǎn)的物體,再繪制較近的物體。

先繪制紅色部分,再繪制?色部分,最后再繪制灰?部分,即可解決隱藏面消除的問題。即將場景按照物理距離和觀察者的距離遠(yuǎn)近排序,由遠(yuǎn)及近的繪制即可。

image.png

當(dāng)我們設(shè)置了cornerRadius以及masksToBounds進(jìn)行圓角+裁剪時,masksToBounds裁剪屬性會應(yīng)用到所有的圖層上。

image.png

本來我們從后往前繪制,繪制完一個圖層就可以丟棄了。但現(xiàn)在需要依次在 Offscreen Buffer中保存,等待圓角+裁剪處理,即引發(fā)了 離屏渲染 。

3. 為什么離屏渲染會降低效率?

  • 要創(chuàng)建離屏緩沖區(qū)
  • 要進(jìn)行緩沖區(qū)上下文的切換
  • 需要離屏渲染的計算通常比較復(fù)雜(不符合畫家算法)
渲染結(jié)果先經(jīng)過了離屏buffer,再到frame buffer

4. 同時設(shè)置 cornerRadiusmasksToBounds,也就是圓角 + 裁剪,一定會觸發(fā)離屏渲染嗎?

  • 不一定
  • 如果僅僅裁剪了一個背景顏色或者一個圖片,不會觸發(fā)離屏渲染(其實(shí)就是符合畫家算法,不需要另起離屏緩沖區(qū))

5. 小技巧:對于只有一張圖片的 UIButton,如何設(shè)置圓角而不觸發(fā)離屏渲染?

  • 下面這種做法不會觸發(fā)離屏渲染
// 下面這種做法不會觸發(fā)離屏渲染
btn.imageView.clipsToBounds = YES;
btn.imageView.layer.cornerRadius = 30;
  • 下面這種做法觸發(fā)離屏渲染
// 下面這種做法會觸發(fā)離屏渲染
btn.clipsToBounds = YES;
btn.layer.cornerRadius = 30;

參考好文章:
https://juejin.cn/post/6846687603316490254

https://medium.com/@jasonyuh/%E5%85%B3%E4%BA%8E%E7%A6%BB%E5%B1%8F%E6%B8%B2%E6%9F%93%E7%9A%84%E6%B7%B1%E5%85%A5%E7%A0%94%E7%A9%B6-e776f56b3e60

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容