iOS繪制與渲染--渲染流程

視圖渲染框架

UIKit是常用的框架,顯示、動畫都通過CoreAnimation。CoreAnimation是核心動畫,依賴于OpenGL ES做GPU渲染,CoreGraphics做CPU渲染;最底層的GraphicsHardWare是圖形硬件。

下圖是另外一種表現(xiàn)的形式。在屏幕上顯示視圖,需要CPU和GPU一起協(xié)作。一部數(shù)據(jù)通過CoreGraphics、CoreImage由CPU預(yù)處理。最終通過OpenGL ES將數(shù)據(jù)傳送到 GPU,最終顯示到屏幕。
CoreImage支持CPU、GPU兩種處理模式。

顯示邏輯

1、CoreAnimation提交會話,包括自己和子樹(view hierarchy)的layout狀態(tài)等;
2、RenderServer解析提交的子樹狀態(tài),生成繪制指令;
3、GPU執(zhí)行繪制指令;
4、顯示渲染后的數(shù)據(jù);

提交流程(以動畫為例)

第2步為prepare to commit animation (layoutSubviews,drawRect:);

1、布局(Layout)

調(diào)用layoutSubviews方法;調(diào)用addSubview:方法;

會造成CPU和I/O瓶頸;

2、顯示(Display)

通過drawRect繪制視圖;繪制string(字符串);

會造成CPU和內(nèi)存瓶頸;每個UIView都有CALayer,同時圖層有一個像素存儲空間,存放視圖;調(diào)用-setNeedsDisplay的時候,僅會設(shè)置圖層為dirty。當(dāng)渲染系統(tǒng)準(zhǔn)備就緒,調(diào)用視圖的-display方法,同時裝配像素存儲空間,建立一個CoreGraphics上下文(CGContextRef),將上下文push進(jìn)上下文堆棧,繪圖程序進(jìn)入對應(yīng)的內(nèi)存存儲空間。

UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(10, 10)];
[path addLineToPoint:CGPointMake(20, 20)];
[path closePath];
path.lineWidth = 1;
[[UIColor redColor] setStroke];
[path stroke];

在-drawRect方法中實現(xiàn)如上代碼,UIKit會將自動生成的CGContextRef 放入上下文堆棧。當(dāng)繪制完成后,視圖的像素會被渲染到屏幕上;當(dāng)下次再次調(diào)用視圖的-setNeedsDisplay,將會再次調(diào)用-drawRect方法。

3、準(zhǔn)備提交(Prepare)

解碼圖片;圖片格式轉(zhuǎn)換;

GPU不支持的某些圖片格式,盡量使用GPU能支持的圖片格式;

4、提交(Commit)

打包layers并發(fā)送到渲染server;遞歸提交子樹的layers;

如果子樹太復(fù)雜,會消耗很大,對性能造成影響;
盡可能簡化viewTree;

當(dāng)顯示一個UIImageView時,Core Animation會創(chuàng)建一個OpenGL ES紋理,并確保在這個圖層中的位圖被上傳到對應(yīng)的紋理中。當(dāng)你重寫-drawInContext
方法時,Core Animation會請求分配一個紋理,同時確保Core Graphics會將你在-drawInContext
中繪制的東西放入到紋理的位圖數(shù)據(jù)中。




渲染總流程

CPU與GPU協(xié)作
同步時鐘觸發(fā)重繪
掉幀卡頓問題

在 VSync 信號到來后,系統(tǒng)圖形服務(wù)會通過 CADisplayLink 等機(jī)制通知 App,App 主線程開始在 CPU 中計算顯示內(nèi)容,比如視圖的創(chuàng)建、布局計算、圖片解碼、文本繪制等。隨后 CPU 會將計算好的內(nèi)容提交到 GPU 去,由 GPU 進(jìn)行變換、合成、渲染。隨后 GPU 會把渲染結(jié)果提交到幀緩沖區(qū)去,等待下一次 VSync 信號到來時顯示到屏幕上。由于垂直同步的機(jī)制,如果在一個 VSync 時間內(nèi),CPU 或者 GPU 沒有完成內(nèi)容提交,則那一幀就會被丟棄,等待下一次機(jī)會再顯示,而這時顯示屏?xí)A糁暗膬?nèi)容不變。這就是界面卡頓的原因。

渲染時機(jī)

上面已經(jīng)提到過:Core Animation 在 RunLoop 中注冊了一個 Observer 監(jiān)聽 BeforeWaiting(即將進(jìn)入休眠) 和 Exit (即將退出Loop) 事件 。當(dāng)在操作 UI 時,比如改變了 Frame、更新了 UIView/CALayer 的層次時,或者手動調(diào)用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay方法后,這個 UIView/CALayer 就被標(biāo)記為待處理,并被提交到一個全局的容器去。當(dāng)Oberver監(jiān)聽的事件到來時,回調(diào)執(zhí)行函數(shù)中會遍歷所有待處理的UIView/CAlayer 以執(zhí)行實際的繪制和調(diào)整,并更新 UI 界面。

這個函數(shù)內(nèi)部的調(diào)用棧大概是這樣的:

_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()
    QuartzCore:CA::Transaction::observer_callback:
        CA::Transaction::commit();
            CA::Context::commit_transaction();
                CA::Layer::layout_and_display_if_needed();
                    CA::Layer::layout_if_needed();
                          [CALayer layoutSublayers];
                          [UIView layoutSubviews];
                    CA::Layer::display_if_needed();
                          [CALayer display];
                          [UIView drawRect];

渲染具體步驟

動畫和屏幕上組合的圖層實際上被一個單獨的進(jìn)程管理,即所謂的渲染服務(wù)。
當(dāng)運(yùn)行一段動畫時,這個過程會被四個分離的階段打破:

  1. 布局--準(zhǔn)備視圖的層級關(guān)系,設(shè)置圖層屬性
  2. 顯示--圖層的寄宿圖片被繪制的階段。涉及到-drawRect和-drawLayer:inContext:等方法
  3. 準(zhǔn)備--準(zhǔn)備發(fā)送動畫數(shù)據(jù)給渲染服務(wù)的階段。比如圖片解碼
  4. 提交--打包所有圖層和動畫屬性,通過IPC發(fā)送到渲染服務(wù)

渲染服務(wù)拿到數(shù)據(jù)后,反序列化成一個叫做渲染樹的圖層樹,使用這個樹狀結(jié)構(gòu),渲染服務(wù)隊動畫的每一幀做如下工作:

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

所以一共六個階段:最后兩個階段在動畫過程中不停地重復(fù),前五個階段都在軟件層面處理(通過CPU),只有最后一個被GPU執(zhí)行。而且,你真正只能控制前兩個階段:布局和顯示。剩下的在CoreAnimation內(nèi)部處理。

CADisplayLink簡介

當(dāng)你設(shè)置一個NSTimer,他會被插入到當(dāng)前任務(wù)列表中,然后直到指定時間過去之后才會被執(zhí)行。但是何時啟動定時器并沒有一個時間上限,而且它只會在列表中上一個任務(wù)完成之后開始執(zhí)行。這通常會導(dǎo)致有幾毫秒的延遲,但是如果上一個任務(wù)過了很久才完成就會導(dǎo)致延遲很長一段時間。

用CADisplayLink而不是NSTimer,會保證幀率足夠連續(xù),使得動畫看起來更加平滑,但即使CADisplayLink也不能保證每一幀都按計劃執(zhí)行,一些失去控制的離散的任務(wù)或者事件(例如資源緊張的后臺程序)可能會導(dǎo)致動畫偶爾地丟幀。當(dāng)使用NSTimer的時候,一旦有機(jī)會計時器就會開啟,但是CADisplayLink卻不一樣:如果它丟失了幀,就會直接忽略它們,然后在下一次更新的時候接著運(yùn)行。

參考文章

iOS開發(fā)-視圖渲染與性能優(yōu)化
iOS 事件處理機(jī)制與圖像渲染過程

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

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

  • 書寫的很好,翻譯的也棒!感謝譯者,感謝感謝! iOS-Core-Animation-Advanced-Techni...
    錢噓噓閱讀 2,439評論 0 6
  • 在iOS中隨處都可以看到絢麗的動畫效果,實現(xiàn)這些動畫的過程并不復(fù)雜,今天將帶大家一窺ios動畫全貌。在這里你可以看...
    每天刷兩次牙閱讀 8,693評論 6 30
  • 本文內(nèi)容系全文轉(zhuǎn)載自微信開發(fā)團(tuán)隊的《iOS 事件處理機(jī)制與圖像渲染過程》 目錄 iOS 事件處理機(jī)制與圖像渲染過程...
    Vinc閱讀 1,811評論 1 20
  • ubuntu 服務(wù)器管理 安裝 Ubuntu 以后,需要對系統(tǒng)的安全性進(jìn)行加固,并在日常使用中時刻關(guān)注系統(tǒng)安全。本...
    Luwnto閱讀 1,364評論 1 1
  • 李少琦 后來,我們慢慢成長,卻似乎從未長大。我們常常忽悠他人,卻從不換位思考,所謂的真誠也只是套路。 故事還得從B...
    愛心驛站閱讀 793評論 0 0

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