轉(zhuǎn)載_深入理解 iOS Rendering Process(Objective-C)

2018.4.18
如原作者要求刪除图焰,請聯(lián)系我鞠柄,立刪猜丹。
作者:Lision
鏈接:深入理解 iOS Rendering Process
來源:Cocoa開發(fā)者社區(qū)
著作權(quán)歸作者所有鳖昌。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處唯欣。

Cover

作者:Lision

前言

iOS 最早名為 iPhone OS嘹吨,是 Apple 公司專門為其硬件設備開發(fā)的操作系統(tǒng),最初于 2007 年隨第一代 iPhone 推出境氢,后擴展為支持 Apple 公司旗下的其他硬件設備蟀拷,如 iPod、iPad 等萍聊。

作為一名 iOS Developer问芬,相信大多數(shù)人都有寫出過造成 iOS 設備卡頓的代碼經(jīng)歷,相應的也有過想方設法優(yōu)化卡頓代碼的經(jīng)驗寿桨。

本文將從 OpenGL 的角度結(jié)合 Apple 官方給出的部分資料此衅,介紹 iOS Rendering Process 的概念及其整個底層渲染管道的各個流程。

相信在理解了 iOS Rendering Process 的底層各個階段之后亭螟,我們可以在平日的開發(fā)工作之中寫出性能更高的代碼挡鞍,在解決幀率不足的顯示卡頓問題時也可以多一些思路~

索引

  • iOS Rendering Process 概念

  • iOS Rendering 技術(shù)框架

  • OpenGL 主要渲染步驟

  • OpenGL Render Pipeline

  • Core Animation Pipeline

  • Commit Transaction

  • Animation

  • 全文總結(jié)

  • 擴展閱讀

iOS Rendering Process 概念

iOS Rendering Process 譯為 iOS 渲染流程,本文特指 iOS 設備從設置將要顯示的圖元數(shù)據(jù)到最終在設備屏幕成像的整個過程预烙。

在開始剖析 iOS Rendering Process 之前墨微,我們需要對 iOS 的渲染概念有一個基本的認知:

基于平鋪的渲染

iOS 設備的屏幕分為 N * N 像素的圖塊,每個圖塊都適合于 SoC 緩存扁掸,幾何體在圖塊內(nèi)被大量拆分翘县,只有在所有幾何體全部提交之后才可以進行光柵化(Rasterization)。

Rasterization

Note: 這里的光柵化指將屏幕上面被大量拆分出來的幾何體渲染為像素點的過程谴分。

Rasterization

iOS Rendering 技術(shù)框架

事實上 iOS 渲染相關的層級劃分大概如下:

iOS 渲染相關的層級劃分

UIKit

嘛~ 作為一名 iOS Developer 來說锈麸,應該對 UIKit 都不陌生,我們?nèi)粘i_發(fā)中使用的用戶交互組件都來自于 UIKit Framework牺蹄,我們通過設置 UIKit 組件的 Layout 以及 BackgroundColor 等屬性來完成日常的界面繪畫工作忘伞。

其實 UIKit Framework 自身并不具備在屏幕成像的能力,它主要負責對用戶操作事件的響應,事件響應的傳遞大體是經(jīng)過逐層的視圖樹遍歷實現(xiàn)的氓奈。

那么我們?nèi)粘懙?UIKit 組件為什么可以呈現(xiàn)在 iOS 設備的屏幕上呢匿刮?

Core Animation

Core Animation 其實是一個令人誤解的命名。你可能認為它只是用來做動畫的探颈,但實際上它是從一個叫做**Layer Kit **這么一個不怎么和動畫有關的名字演變而來的,所以做動畫僅僅是 Core Animation 特性的冰山一角训措。

Core Animation 本質(zhì)上可以理解為是一個復合引擎伪节,旨在盡可能快的組合屏幕上不同的顯示內(nèi)容。這些顯示內(nèi)容被分解成獨立的圖層绩鸣,即 CALayer怀大,CALayer 才是你所能在屏幕上看見的一切的基礎。

其實很多同學都應該知道 CALayer呀闻,UIKit 中需要在屏幕呈現(xiàn)的組件內(nèi)部都有一個對應的 CALayer化借,也就是所謂的 Backing Layer。正是因為一一對應捡多,所以 CALayer 也是樹形結(jié)構(gòu)的蓖康,我們稱之為圖層樹

視圖的職責就是創(chuàng)建并管理這個圖層垒手,以確保當子視圖在層級關系中添加或者被移除的時候蒜焊,他們關聯(lián)的圖層同樣對應在層級關系樹當中有相同的操作

但是為什么 iOS 要基于 UIView 和 CALayer 提供兩個平行的層級關系呢科贬?為什么不用一個簡單的層級關系來處理所有事情呢泳梆?

原因在于要做職責分離,這樣也能避免很多****重復代碼榜掌。在 iOS 和 Mac OS X 兩個平臺上优妙,事件和用戶交互有很多地方的不同,基于多點觸控的用戶界面和基于鼠標鍵盤的交互有著本質(zhì)的區(qū)別憎账,這就是為什么 iOS 有 UIKit 和 UIView套硼,而 Mac OS X 有 AppKit 和 NSView 的原因。他們功能上很相似鼠哥,但是在實現(xiàn)上有著顯著的區(qū)別熟菲。

Note: 實際上,這里并不是兩個層級關系朴恳,而是四個抄罕,每一個都扮演不同的角色,除了視圖樹和圖層樹之外于颖,還存在呈現(xiàn)樹和渲染樹呆贿。

OpenGL ES & Core Graphics

OpenGL ES

OpenGL ES 簡稱 GLES,即 OpenGL for Embedded Systems,是 OpenGL 的子集做入,通常面向圖形硬件加速處理單元(GPU)渲染 2D 和 3D 計算機圖形冒晰,例如視頻游戲使用的計算機圖形。

OpenGL ES 專為智能手機竟块,平板電腦壶运,視頻游戲機和 PDA 等嵌入式系統(tǒng)而設計 。OpenGL ES 是“歷史上應用最廣泛的 3D 圖形 API”浪秘。

Core Graphics

Core Graphics Framework 基于 Quartz 高級繪圖引擎蒋情。它提供了具有無與倫比的輸出保真度的低級別輕量級 2D 渲染。您可以使用此框架來處理基于路徑的繪圖耸携,轉(zhuǎn)換棵癣,顏色管理,離屏渲染夺衍,圖案狈谊,漸變和陰影,圖像數(shù)據(jù)管理沟沙,圖像創(chuàng)建和圖像遮罩以及 PDF 文檔創(chuàng)建河劝,顯示和分析。

Note: 在 Mac OS X 中矛紫,Core Graphics 還包括用于處理顯示硬件丧裁,低級用戶輸入事件和窗口系統(tǒng)的服務。

Graphics Hardware

Graphics Hardware 譯為圖形硬件含衔,iOS 設備中也有自己的圖形硬件設備煎娇,也就是我們經(jīng)常提及的 GPU。

圖形處理單元(GPU)是一種專用電子電路贪染,旨在快速操作和改變存儲器缓呛,以加速在用于輸出到顯示設備的幀緩沖器中創(chuàng)建圖像。GPU 被用于嵌入式系統(tǒng)杭隙,手機哟绊,個人電腦,工作站和游戲控制臺√翟鳎現(xiàn)代 GPU 在處理計算機圖形和圖像方面非常高效票髓,并且 GPU 的高度并行結(jié)構(gòu)使其在大塊數(shù)據(jù)并行處理的算法中比通用 CPU 更有效。

OpenGL 主要渲染步驟

OpenGL 全稱 Open Graphics Library铣耘,譯為開放圖形庫洽沟,是用于渲染 2D 和 3D 矢量圖形的跨語言,跨平臺應用程序編程接口(API)蜗细。OpenGL 可以直接訪問 GPU裆操,以實現(xiàn)硬件加速渲染怒详。

一個用來渲染圖像的 OpenGL 程序主要可以大致分為以下幾個步驟:

  • 設置圖元數(shù)據(jù)

  • 著色器-shader 計算圖元數(shù)據(jù)(位置·顏色·其他)

  • 光柵化-rasterization 渲染為像素

  • fragment shader,決定最終成像

  • 其他操作(顯示·隱藏·融合)

Note: 其實還有一些非必要的步驟踪区,與本文主題不相關昆烁,這里點到為止。

我們?nèi)粘i_發(fā)時使用 UIKit 布局視圖控件缎岗,設置透明度等等都屬于設置圖元數(shù)據(jù)這步静尼,這也是我們?nèi)粘i_發(fā)中可以影響 OpenGL 渲染的主要步驟。

OpenGL Render Pipeline

如果有同學看過 WWDC 的一些演講稿或者接觸過一些 OpenGL 知識传泊,應該對 Render Pipeline 這個專業(yè)術(shù)語并不陌生茅郎。

不過 Render Pipeline 實在是一個初次見面不太容易理解的詞,它譯為渲染管道或渤,也有譯為渲染管線的...

其實 Render Pipeline 指的是從應用程序數(shù)據(jù)轉(zhuǎn)換到最終渲染的圖像之間的一系列數(shù)據(jù)處理過程

好比我們上文中提到的 OpenGL 主要渲染步驟一樣奕扣,我們開發(fā)應用程序時在設置圖元數(shù)據(jù)這步為視圖控件的設定布局薪鹦,背景顏色,透明度以及陰影等等數(shù)據(jù)惯豆。

下面以 OpenGL 4.5 的 Render Pipeline 為例介紹一下:

流動

這些圖元數(shù)據(jù)流入 OpenGL 中池磁,傳入頂點著色器(vetex shader),然后頂點著色器對其進行著色器內(nèi)部的處理后流出楷兽。之后可能進入細分著色階段(tessellation shading stage)地熄,其中又有可能分為細分控制著色器和細分賦值著色器兩部分處理,還可能會進入幾何著色階段(geometry shading stage)芯杀,數(shù)據(jù)從中傳遞端考。最后都會走片元著色階段(fragment shading stage)

Note: 圖元數(shù)據(jù)是以 copy 的形式流入 shader 的揭厚,shader 一般會以特殊的類似全局變量的形式接收數(shù)據(jù)却特。

OpenGL 在最終成像之前還會經(jīng)歷一個階段名為計算著色階段(compute shaing stage),這個階段 OpenGL 會計算最重要在屏幕中成像的像素位置以及顏色筛圆,如果在之前提交代碼時用到了 CALayer 會引起**blending **的顯示效果(例如 Shadow)或者視圖顏色或內(nèi)容圖片的 alpha 通道開啟裂明,都將會加大這個階段 OpenGL 的工作量。

Core Animation Pipeline

上文說到了 iOS 設備之所以可以成像不是因為 UIKit 而是因為 LayerKit太援,即 Core Animation闽晦。

Core Animation 圖層,即 CALayer 中包含一個屬性 contents提岔,我們可以通過給這個屬性賦值來控制 CALayer 成像的內(nèi)容仙蛉。這個屬性的類型定義為 id,在程序編譯時不論我們給 contents 賦予任何類型的值碱蒙,都是可以編譯通過的捅儒。但實踐中,如果 contents 賦值類型不是 CGImage,那么你將會得到一個空白圖層巧还。

Note: 造成 contents 屬性的奇怪表現(xiàn)的原因是 Mac OS X 的歷史包袱鞭莽,它之所以被定義為 id 類型是因為在 Mac OS X 中這個屬性對 CGImage 和 NSImage 類型的值都起作用。但是在 iOS 中麸祷,如果你賦予一個 UIImage 屬性的值澎怒,僅僅會得到一個空白圖層

說完 Core Animation 的 contents 屬性阶牍,下面介紹一下 iOS 中 Core Animation Pipeline:

  • 在 Application 中布局 UIKit 視圖控件間接的關聯(lián) Core Animation 圖層

  • Core Animation 圖層相關的數(shù)據(jù)提交到 iOS Render Server喷面,即 OpenGL ES & Core Graphics

  • Render Server 將與 GPU 通信把數(shù)據(jù)經(jīng)過處理之后傳遞給 GPU

  • GPU 調(diào)用 iOS 當前設備渲染相關的圖形設備 Display

渲染

Note: 由于 iOS 設備目前的顯示屏最大支持** 60 FPS** 的刷新率,所以每個處理間隔為 16.67 ms走孽。

可以看到從 Commit Transaction 之后我們的圖元數(shù)據(jù)就將會在下一次 RunLoop 時被 Application 發(fā)送給底層的 Render Server惧辈,底層 Render Server 直接面向 GPU 經(jīng)過一些列的數(shù)據(jù)處理將處理完畢的數(shù)據(jù)傳遞給 GPU,然后 GPU 負責渲染工作磕瓷,根據(jù)當前 iOS 設備的屏幕計算圖像像素位置以及像素 alpha 通道混色計算等等最終在當前 iOS 設備的顯示屏中呈現(xiàn)圖像盒齿。

嘛~ 由于 Core Animation Pipeline 中 Render Server 包含 OpenGL ES & Core Graphics,其中 OpenGL ES 的渲染可以參考上文 OpenGL Render Pipeline 理解困食。

Commit Transaction

Core Animation Pipeline 的整個管線中 iOS 常規(guī)開發(fā)一般可以影響到的范圍也就僅僅是在 Application 中布局 UIKit 視圖控件間接的關聯(lián) Core Animation 圖層這一級边翁,即 Commit Transaction 之前的一些操作

那么在 Commit Transaction 之前我們一般要做的事情有哪些硕盹?

  • Layout符匾,構(gòu)建視圖

  • Display,繪制視圖

  • Prepare瘩例,額外的 Core Animation 工作

  • Commit啊胶,打包圖層并將它們發(fā)送到 Render Server

Layout

在 Layout 階段我們能做的是把 constraint 寫的盡量高效,iOS 的 Layout Constraint 類似于 Android 的 Relative Layout垛贤。

Note: Emmmmm... 據(jù)觀察 iOS 的 Layout Constraint 在書寫時應該盡量少的依賴于視圖樹中同層級的兄弟視圖節(jié)點创淡,它會拖慢整個視圖樹的 Layout 計算過程。

這個階段的 Layout 計算工作是在 CPU 完成的南吮,包括 layoutSubviews 方法的重載琳彩,addSubview: 方法填充子視圖等

Display

其實這里的 Display 僅僅是我們設置 iOS 設備要最終成像的圖元數(shù)據(jù)而已,重載視圖 drawRect: 方法可以自定義 UIView 的顯示部凑,其原理是在 drawRect: 方法內(nèi)部繪制 bitmap露乏。

Note: 重載 drawRect: 方法繪制 bitmap 過程使用 CPU 和 內(nèi)存

所以重載 drawRect: 使用不當會造成 CPU 負載過重涂邀,App 內(nèi)存飆升等問題瘟仿。

Prepare

這個步驟屬于附加步驟,一般處理圖像的解碼 & 轉(zhuǎn)換等操作比勉。

Commit

Commit 步驟指打包圖層并將它們發(fā)送到 Render Server劳较。

Note: Commit 操作會遞歸執(zhí)行驹止,由于圖層和視圖一樣是以樹形結(jié)構(gòu)存在的,當圖層樹過于復雜時 Commit 操作的開銷也會非常大观蜗。

CATransaction

CATransaction 是 Core Animation 中用于將多個圖層樹操作分配到渲染樹的原子更新中的機制臊恋,對圖層樹的每個修改都必須是事務的一部分。

CATransaction 類沒有屬性或者實例方法墓捻,并且也不能用 +alloc 和 -init 方法創(chuàng)建它抖仅,我們只能用類方法 +begin 和 +commit 分別來入棧或者出棧砖第。

事實上任何可動畫化的圖層屬性都會被添加到棧頂?shù)氖聞粘仿憧梢酝ㄟ^ +setAnimationDuration: 方法設置當前事務的動畫時間,或者通過 +animationDuration 方法來獲取時長值(默認 0.25 秒)梧兼。

Core Animation 在每個 RunLoop 周期中自動開始一次新的事務放吩,即使你不顯式地使用 [CATransaction begin] 開始一次事務,在一個特定 RunLoop 循環(huán)中的任何屬性的變化都會被收集起來羽杰,然后做一次 0.25 秒的動畫(CALayer 隱式動畫)渡紫。

Note: CATransaction 支持嵌套

Animation

對于 App 用戶交互體驗提升最明顯的工作莫過于使用動畫了忽洛,那么 iOS 是如何處理動畫的渲染過程的呢?

日常開發(fā)中如果不是特別復雜的動畫我們一般會使用 UIView Animation 實現(xiàn)环肘,iOS 將 UIView Animation 的處理過程分為以下三個階段:

  • 調(diào)用 animateWithDuration:animations: 方法

  • 在 Animation Block 中進行 Layout欲虚,Display,Prepare悔雹,Commit

  • Render Server 根據(jù) Animation 逐幀渲染

Note

Note: 原理是 animateWithDuration:animations: 內(nèi)部使用了 CATransaction 來將整個 Animation Block 中的代碼作為原子操作 commit 給了 RunLoop复哆。

基于 CATransaction 實現(xiàn)鏈式動畫

事實上大多數(shù)的動畫交互都是有動畫執(zhí)行順序的,盡管 UIView Animation 很強大腌零,但是在寫一些順序動畫時使用 UIView Animation 只能在 + (void)animateWithDuration:delay:options:animations:completion: 方法的 completion block 中層級嵌套梯找,寫成一坨一坨 block 堆砌而成的代碼,實在是難以閱讀更別提后期維護了益涧。

在得知 UIView Animation 使用了 CATransaction 時锈锤,我們不禁會想到這個 completion block 是不是也是基于 CATransaction 實現(xiàn)的呢?

Bingo闲询!CATransaction 中有 +completionBlock 以及 +setCompletionBlock: 方法可以對應于 UIView Animation 的 completion block 的書寫久免。

Note: 我的一個開源庫** LSAnimator - 可多鏈式動畫庫 **動畫順序鏈接時也用到了 CATransaction

全文總結(jié)

結(jié)合上下文不難梳理出一個 iOS 最基本的完整渲染經(jīng)過(Rendering pass)扭弧。

Rendering pass

性能檢測思路

基于整篇文章的內(nèi)容歸納一下我們在日常的開發(fā)工作中遇到性能問題時檢測問題代碼的思路:

絲路

文章寫得比較用心(是我個人的原創(chuàng)文章阎姥,轉(zhuǎn)載請注明 https://lision.me/),如果發(fā)現(xiàn)錯誤會優(yōu)先在我的 個人博客 中更新鸽捻。如果有任何問題歡迎在我的微博 @Lision 聯(lián)系我~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末呼巴,一起剝皮案震驚了整個濱河市泽腮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌衣赶,老刑警劉巖诊赊,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異屑埋,居然都是意外死亡豪筝,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門摘能,熙熙樓的掌柜王于貴愁眉苦臉地迎上來续崖,“玉大人,你說我怎么就攤上這事团搞⊙贤” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵逻恐,是天一觀的道長像吻。 經(jīng)常有香客問我,道長复隆,這世上最難降的妖魔是什么拨匆? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮挽拂,結(jié)果婚禮上惭每,老公的妹妹穿的比我還像新娘。我一直安慰自己亏栈,他們只是感情好台腥,可當我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著绒北,像睡著了一般黎侈。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上闷游,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天峻汉,我揣著相機與錄音,去河邊找鬼脐往。 笑死俱济,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的钙勃。 我是一名探鬼主播蛛碌,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼辖源!你這毒婦竟也來了蔚携?” 一聲冷哼從身側(cè)響起希太,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎酝蜒,沒想到半個月后誊辉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡亡脑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年堕澄,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片霉咨。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡蛙紫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出途戒,到底是詐尸還是另有隱情坑傅,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布喷斋,位于F島的核電站唁毒,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏星爪。R本人自食惡果不足惜浆西,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望顽腾。 院中可真熱鬧近零,春花似錦、人聲如沸崔泵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽憎瘸。三九已至,卻和暖如春陈瘦,著一層夾襖步出監(jiān)牢的瞬間幌甘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工痊项, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留锅风,地道東北人。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓鞍泉,卻偏偏與公主長得像皱埠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子咖驮,可洞房花燭夜當晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容