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

視圖渲染框架

UIKit是常用的框架,顯示读拆、動(dòng)畫(huà)都通過(guò)CoreAnimation擅憔。CoreAnimation是核心動(dòng)畫(huà),依賴(lài)于OpenGL ES做GPU渲染檐晕,CoreGraphics做CPU渲染暑诸;最底層的GraphicsHardWare是圖形硬件。

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

顯示邏輯

1武通、CoreAnimation提交會(huì)話(huà)霹崎,包括自己和子樹(shù)(view hierarchy)的layout狀態(tài)等;
2冶忱、RenderServer解析提交的子樹(shù)狀態(tài)尾菇,生成繪制指令;
3、GPU執(zhí)行繪制指令错沽;
4簿晓、顯示渲染后的數(shù)據(jù);

提交流程(以動(dòng)畫(huà)為例)

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

1憔儿、布局(Layout)

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

會(huì)造成CPU和I/O瓶頸谒臼;

2、顯示(Display)

通過(guò)drawRect繪制視圖耀里;繪制string(字符串)蜈缤;

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

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方法中實(shí)現(xiàn)如上代碼,UIKit會(huì)將自動(dòng)生成的CGContextRef 放入上下文堆棧湿颅。當(dāng)繪制完成后载绿,視圖的像素會(huì)被渲染到屏幕上;當(dāng)下次再次調(diào)用視圖的-setNeedsDisplay肖爵,將會(huì)再次調(diào)用-drawRect方法卢鹦。

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

解碼圖片劝堪;圖片格式轉(zhuǎn)換冀自;

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

4熬粗、提交(Commit)

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

如果子樹(shù)太復(fù)雜驻呐,會(huì)消耗很大灌诅,對(duì)性能造成影響;
盡可能簡(jiǎn)化viewTree含末;

當(dāng)顯示一個(gè)UIImageView時(shí)猜拾,Core Animation會(huì)創(chuàng)建一個(gè)OpenGL ES紋理,并確保在這個(gè)圖層中的位圖被上傳到對(duì)應(yīng)的紋理中佣盒。當(dāng)你重寫(xiě)-drawInContext
方法時(shí)挎袜,Core Animation會(huì)請(qǐng)求分配一個(gè)紋理,同時(shí)確保Core Graphics會(huì)將你在-drawInContext
中繪制的東西放入到紋理的位圖數(shù)據(jù)中肥惭。




渲染總流程

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

在 VSync 信號(hào)到來(lái)后盯仪,系統(tǒng)圖形服務(wù)會(huì)通過(guò) CADisplayLink 等機(jī)制通知 App,App 主線(xiàn)程開(kāi)始在 CPU 中計(jì)算顯示內(nèi)容蜜葱,比如視圖的創(chuàng)建全景、布局計(jì)算、圖片解碼牵囤、文本繪制等爸黄。隨后 CPU 會(huì)將計(jì)算好的內(nèi)容提交到 GPU 去,由 GPU 進(jìn)行變換揭鳞、合成馆纳、渲染。隨后 GPU 會(huì)把渲染結(jié)果提交到幀緩沖區(qū)去汹桦,等待下一次 VSync 信號(hào)到來(lái)時(shí)顯示到屏幕上。由于垂直同步的機(jī)制鉴裹,如果在一個(gè) VSync 時(shí)間內(nèi)舞骆,CPU 或者 GPU 沒(méi)有完成內(nèi)容提交,則那一幀就會(huì)被丟棄径荔,等待下一次機(jī)會(huì)再顯示督禽,而這時(shí)顯示屏?xí)A糁暗膬?nèi)容不變。這就是界面卡頓的原因总处。

渲染時(shí)機(jī)

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

這個(gè)函數(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];

渲染具體步驟

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

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

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

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

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

CADisplayLink簡(jiǎn)介

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

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

參考文章

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市饶碘,隨后出現(xiàn)的幾起案子待诅,更是在濱河造成了極大的恐慌,老刑警劉巖熊镣,帶你破解...
    沈念sama閱讀 212,542評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卑雁,死亡現(xiàn)場(chǎng)離奇詭異募书,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)测蹲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)莹捡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人扣甲,你說(shuō)我怎么就攤上這事篮赢。” “怎么了琉挖?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,021評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵启泣,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我示辈,道長(zhǎng)寥茫,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,682評(píng)論 1 284
  • 正文 為了忘掉前任矾麻,我火速辦了婚禮纱耻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘险耀。我一直安慰自己弄喘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布甩牺。 她就那樣靜靜地躺著蘑志,像睡著了一般。 火紅的嫁衣襯著肌膚如雪贬派。 梳的紋絲不亂的頭發(fā)上卖漫,一...
    開(kāi)封第一講書(shū)人閱讀 49,985評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音赠群,去河邊找鬼。 笑死旱幼,一個(gè)胖子當(dāng)著我的面吹牛查描,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播柏卤,決...
    沈念sama閱讀 39,107評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼冬三,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了缘缚?” 一聲冷哼從身側(cè)響起勾笆,我...
    開(kāi)封第一講書(shū)人閱讀 37,845評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎桥滨,沒(méi)想到半個(gè)月后窝爪,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體弛车,經(jīng)...
    沈念sama閱讀 44,299評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評(píng)論 2 327
  • 正文 我和宋清朗相戀三年蒲每,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了纷跛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,747評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡邀杏,死狀恐怖贫奠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情望蜡,我是刑警寧澤唤崭,帶...
    沈念sama閱讀 34,441評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站脖律,受9級(jí)特大地震影響谢肾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜状您,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評(píng)論 3 317
  • 文/蒙蒙 一勒叠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧膏孟,春花似錦眯分、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,828評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春逆甜,著一層夾襖步出監(jiān)牢的瞬間居凶,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,069評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工昆稿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人息拜。 一個(gè)月前我還...
    沈念sama閱讀 46,545評(píng)論 2 362
  • 正文 我出身青樓溉潭,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親少欺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子喳瓣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評(píng)論 2 350

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

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