iOS中的渲染
在iOS中渲染的整體流程如下所示
App通過調(diào)用CoreGraphics商叹、CoreAnimation悴能、CoreImage等框架的接口觸發(fā)圖形渲染操作
CoreGraphics萧诫、CoreAnimation伐庭、CoreImage等框架將渲染交由OpenGL ES,由OpenGL ES來驅(qū)動GPU做渲染狼荞,最后顯示到屏幕上
由于OpenGL ES 是跨平臺的由蘑,所以在他的實現(xiàn)中闽寡,是不能有任何窗口相關(guān)的代碼,而是讓各自的平臺為OpenGL ES提供載體尼酿。在ios中爷狈,如果需要使用OpenGL ES,就是通過CoreAnimation提供窗口裳擎,讓App可以去調(diào)用涎永。
iOS中渲染框架總結(jié)
主要由以下六種框架,表格中已經(jīng)說明了鹿响,就不再詳細解釋了
View 與 CALayer 的關(guān)系
首先分別簡單說下UIView和CALayer各自的作用
UIView
UIView屬于UIKIt
負責繪制圖形和動畫操作
用于界面布局和子視圖的管理
處理用戶的點擊事件
CALayer
CALayer屬于CoreAnimation
只負責顯示羡微,且顯示的是位圖
CALayer既用于UIKit,也用于APPKit抢野,
==> UIKit是iOS平臺的渲染框架,APPKit是Mac OSX系統(tǒng)下的渲染框架各墨,
==> 由于iOS和Mac兩個系統(tǒng)的界面布局并不是一致的指孤,iOS是基于多點觸控的交互方式,而Mac OSX是基于鼠標鍵盤的交互方式贬堵,且分別在對應(yīng)的框架中做了布局的操作恃轩,所以并不需要layer載體去布局,且不用迎合任何布局方式黎做。
【面試題】UIView和CALayer的關(guān)系
UIView基于UIKit框架叉跛,可以處理用戶觸摸事件,并管理子視圖
CALayer基于CoreAnimation蒸殿,而CoreAnimation是基于QuartzCode的筷厘。所以CALayer只負責顯示,不能處理用戶的觸摸事件
從父類來說宏所,CALayer繼承的是NSObject酥艳,而UIView是直接繼承自UIResponder的,所以UIVIew相比CALayer而言爬骤,只是多了事件處理功能充石,
從底層來說,UIView屬于UIKit的組件霞玄,而UIKit的組件到最后都會被分解成layer骤铃,存儲到圖層樹中
在應(yīng)用層面來說拉岁,需要與用戶交互時,使用UIView惰爬,不需要交互時喊暖,使用兩者都可以
UIView和CALayer的渲染
下圖可以說明view 和 layer之間是如何渲染的
界面觸發(fā)的方式有兩種
==> 通過loadView中子View的drawRect方法觸發(fā):會回調(diào)CoreAnimation中監(jiān)聽Runloop的BeforeWaiting的RunloopObserver,通過RunloopObserver來進一步調(diào)用CoreAnimation內(nèi)部的CA::Transaction::commit()补鼻,進而一步步走到drawRect方法
==> 用戶點擊事件觸發(fā):喚醒Runloop哄啄,由source1處理(__IOHIDEventSystemClientQueueCallback),并且在下一個runloop里由source0轉(zhuǎn)發(fā)給UIApplication(_UIApplicationHandleEventQueue)风范,從而能通過source0里的事件隊列來調(diào)用CoreAnimation內(nèi)部的CA::Transaction::commit();方法咨跌,進而一步一步的調(diào)用drawRect。
最終都會走到CoreAnimation中的CA::Transaction::commit()方法硼婿,從而來觸發(fā)UIView和CALayer的渲染
這時锌半,已經(jīng)到了CoreAnimation的內(nèi)部,即調(diào)用CA::Transaction::commit();來創(chuàng)建CATrasaction寇漫,然后進一步調(diào)用CALayer drawInContext:()
回調(diào)CALayer的Delegate(UIView)刊殉,問UIView沒有需要畫的內(nèi)容,即回調(diào)到drawRect:方法
在drawRect:方法里可以通過CoreGraphics函數(shù)或UIKit中對CoreGraphics封裝的方法進行畫圖操作
界面觸發(fā)的方式有兩種
==> 通過loadView中子View的drawRect方法觸發(fā):會回調(diào)CoreAnimation中監(jiān)聽Runloop的BeforeWaiting的RunloopObserver州胳,通過RunloopObserver來進一步調(diào)用CoreAnimation內(nèi)部的CA::Transaction::commit()记焊,進而一步步走到drawRect方法
==> 用戶點擊事件觸發(fā):喚醒Runloop,由source1處理(__IOHIDEventSystemClientQueueCallback)栓撞,并且在下一個runloop里由source0轉(zhuǎn)發(fā)給UIApplication(_UIApplicationHandleEventQueue)遍膜,從而能通過source0里的事件隊列來調(diào)用CoreAnimation內(nèi)部的CA::Transaction::commit();方法,進而一步一步的調(diào)用drawRect瓤湘。
最終都會走到CoreAnimation中的CA::Transaction::commit()方法瓢颅,從而來觸發(fā)UIView和CALayer的渲染
這時,已經(jīng)到了CoreAnimation的內(nèi)部弛说,即調(diào)用CA::Transaction::commit();來創(chuàng)建CATrasaction挽懦,然后進一步調(diào)用CALayer drawInContext:()
回調(diào)CALayer的Delegate(UIView),問UIView沒有需要畫的內(nèi)容木人,即回調(diào)到drawRect:方法
在drawRect:方法里可以通過CoreGraphics函數(shù)或UIKit中對CoreGraphics封裝的方法進行畫圖操作
CoreAnimation
在蘋果官方的描述中信柿,Render、Compose醒第,and animate visual elements角塑,CoreAnimationg中的動畫只是一部分,它其實是一個復(fù)合引擎淘讥,主要的職責包括 渲染圃伶、構(gòu)建和動畫實現(xiàn)。
ios中CoreAnimation如圖所示
ios中基于CoreAnimation構(gòu)建的框架有兩個:UIKit和APPKit
CoreAnimation 又是基于Metal 、CoreGraphics封裝的
蘋果為什么要基于UIView和CALayer提供兩個平行的層級關(guān)系(UIKit 和APPKit)窒朋?
職責分離搀罢,可以避免大量重復(fù)代碼
兩個系統(tǒng)交互規(guī)則不一致,雖然功能上類似侥猩,但實現(xiàn)上有顯著區(qū)別
CoreAnimation中的渲染流水線
CoreAnimation中渲染的流程如圖所示
主要分為兩部分:
CoreAnimation部分
GPU部分
CoreAnimation部分
App處理UIView榔至、UIButton等載體的事件,然后通過CPU完成對顯示內(nèi)容的計算欺劳,并將計算后的圖層進行打包唧取,在下一次runloop時,發(fā)送到渲染服務(wù)器
Render Server中主要對收到的準備顯示的內(nèi)容進行解碼划提,然后執(zhí)行OpenGL等相關(guān)程序枫弟,并調(diào)用GPU進行渲染
==> Render Server 操作分析
GPU部分
GPU中通過頂點著色器、片元著色器完成對顯示內(nèi)容的渲染鹏往,將結(jié)果存入幀緩存區(qū)
GPU通過幀緩存區(qū)淡诗、視頻控制器等相關(guān)部件,將其顯示到屏幕上
屏幕卡頓
屏幕卡頓是指圖形圖像的在顯示時出現(xiàn)了撕裂(即圖片錯位顯示)伊履、掉幀(重復(fù)顯示同一幀數(shù)據(jù))等問題韩容,導(dǎo)致用戶能直觀的從屏幕上看到的一種異常現(xiàn)象
為什么會出現(xiàn)這種情況呢唐瀑?下面就來詳細解說下屏幕卡頓
屏幕卡頓的原因
主要有以下三種原因
1.CPU和GPU在渲染的流水線中耗時過長群凶,導(dǎo)致從緩存區(qū)獲取位圖顯示時,下一幀的數(shù)據(jù)還沒有準備好哄辣,獲取的仍是上一幀的數(shù)據(jù)请梢,產(chǎn)生掉幀現(xiàn)象,掉幀就會導(dǎo)致屏幕卡頓
2.蘋果官方針對屏幕撕裂問題柔滔,目前一直使用的方案是垂直同步+雙緩存區(qū)溢陪,可以從根本上防止和解決屏幕撕裂萍虽,但是同時也導(dǎo)致了新的問題掉幀睛廊。雖然我們采用了雙緩存區(qū),但是我們并不能解決CPU和GPU處理圖形圖像的速度問題杉编,導(dǎo)致屏幕在接收到垂直信號時超全,數(shù)據(jù)尚未準備好,緩存區(qū)仍是上一幀的數(shù)據(jù)邓馒,因此導(dǎo)致掉幀
3.在垂直同步+雙緩存區(qū)的方案上嘶朱,再次進行優(yōu)化,將雙緩存區(qū)光酣,改為三緩存區(qū)疏遏,這樣其實也并不能從根本上解決掉幀的問題,只是比雙緩存區(qū)掉幀的概率小了很多,仍有掉幀的可能性财异,對于用戶而言倘零,可能是無感知的。
屏幕撕裂
如圖所示戳寸,屏幕撕裂就類似于這樣的情形
在講屏幕撕裂之前呈驶,首先說說屏幕是如何成像的,主要的流程是什么
屏幕成像過程
請看下面這張圖疫鹊,詳細說明了屏幕成像的一個流程
將需要顯示的圖像袖瞻,經(jīng)由GPU渲染
將渲染后的結(jié)果,存儲到幀緩存區(qū)拆吆,幀緩存區(qū)中存儲的格式是位圖
由視屏控制器從幀緩存區(qū)中讀取位圖聋迎,交由顯示器,從左上角逐行掃描進行顯示
屏幕撕裂的原因
在屏幕顯示圖形圖像的過程中锈拨,是不斷從幀緩存區(qū)獲取一幀一幀數(shù)據(jù)進行顯示的砌庄,
然后在渲染的過程中,幀緩存區(qū)中仍是舊的數(shù)據(jù)奕枢,屏幕拿到舊的數(shù)據(jù)去進行顯示娄昆,
在舊的數(shù)據(jù)沒有讀取完時 ,新的一幀數(shù)據(jù)處理好了缝彬,放入了緩存區(qū)萌焰,這時就會導(dǎo)致屏幕另一部分的顯示是獲取的線數(shù)據(jù),從而導(dǎo)致屏幕上呈現(xiàn)圖片不匹配谷浅,人物扒俯、景象等錯位顯示的情況。
圖示如下:
蘋果官方的解決方案
蘋果官方針對屏幕撕裂現(xiàn)象一疯,目前一直采用的是?垂直同步+雙緩存撼玄,該方案是強制要求同步,且是以掉幀為代價的墩邀。
以下是垂直同步+雙緩存的一個圖解過程掌猛,
垂直同步:是指給幀緩沖加鎖,當電子光束掃描的過程中眉睹,只有掃描完成了才會讀取下一幀的數(shù)據(jù)荔茬,而不是只讀取一部分
雙緩沖區(qū):采用兩個幀緩沖區(qū)用途GPU處理結(jié)果的存儲,當屏幕顯示其中一個緩存區(qū)內(nèi)容時竹海,另一個緩沖區(qū)繼續(xù)等待下一個緩沖結(jié)果慕蔚,兩個緩沖區(qū)依次進行交替
掉幀
采用蘋果的雙緩沖區(qū)方案后,又會出現(xiàn)新的問題斋配,掉幀孔飒。
什么是掉幀灌闺?簡單來說就是 屏幕重復(fù)顯示同一幀數(shù)據(jù)的情況就是掉幀
如圖所示:當前屏幕顯示的是A,在收到垂直信號后坏瞄,CPU和GPU處理的B還沒有準備好菩鲜,此時,屏幕顯示的仍然是A
針對掉幀情況惦积,我們可以在蘋果方案的基礎(chǔ)上進行優(yōu)化接校,即采用三緩存區(qū),意味著狮崩,在屏幕顯示時蛛勉,后面還準備了3個數(shù)據(jù)用于顯示。
文章內(nèi)容和圖片引用來自:Style_月月