接每日一問(wèn)02冒黑,知道了渲染的大致流程以后熄攘,我們?cè)陂_發(fā)的時(shí)候要注意些什么呢?有什么會(huì)影響到渲染的性能孕蝉?知識(shí)的搬運(yùn)工就是我屡律,我又來(lái)了
接上文,我知道了圖像渲染主要是由GPU完成的昔驱,那么會(huì)不會(huì)有一些情況影響GPU的性能呢疹尾?
圖像對(duì)GPU的影響
1. 圖像多層次的合成—為何設(shè)置透明會(huì)增加GPU工作量
在圖形世界中,合成是一個(gè)描述不同位圖如何放到一起來(lái)創(chuàng)建你最終在屏幕上看到圖像的過(guò)程骤肛。
一個(gè)不透明的紅色蓋在藍(lán)色上那我們看到的就是一個(gè)藍(lán)色纳本,但一個(gè)半透明的紅色蓋在藍(lán)色讓我們得到的卻是一個(gè)紫色,這便是合成所要做的工作腋颠。
我們可以用下面這個(gè)公式來(lái)計(jì)算每一個(gè)像素:
R = S + D * ( 1 – Sa )
結(jié)果的顏色是源色彩(頂端紋理)+目標(biāo)顏色(低一層的紋理)*(1-源顏色的透明度)繁成。在這個(gè)公式中所有的顏色都假定已經(jīng)預(yù)先乘以了他們的透明度。
假定兩個(gè)紋理都完全不透明淑玫,比如 alpha=1.如果目標(biāo)紋理(低一層的紋理)是藍(lán)色(RGB=0,0,1)巾腕,并且源紋理(頂層的紋理)顏色是紅色(RGB=1,0,0)面睛,因?yàn)?Sa 為1,所以結(jié)果為:
R = S
如果源顏色層為50%的透明尊搬,比如 alpha=0.5叁鉴,既然 alpha 組成部分需要預(yù)先乘進(jìn) RGB 的值中,那么 S 的 RGB 值為(0.5, 0, 0)佛寿,公式看起來(lái)便會(huì)像這樣:
所以當(dāng)源紋理是完全不透明的時(shí)候幌墓,目標(biāo)像素就等于源紋理。這可以省下 GPU 很大的工作量
這也是為什么 CALayer 有一個(gè)叫做 opaque 的屬性了冀泻。如果這個(gè)屬性為 NO常侣,GPU 將不會(huì)做任何合成,而是簡(jiǎn)單從這個(gè)層拷貝弹渔,不需要考慮它下方的任何東西(因?yàn)槎急凰趽踝×?
優(yōu)化點(diǎn):在多圖層疊加的情況下胳施,圖層不需要透明度時(shí),最好將opaque設(shè)置為NO
2.圖層對(duì)齊—為何圖片縮放會(huì)增加GPU工作量
簡(jiǎn)單的情況:
1.當(dāng)所有的像素是對(duì)齊的時(shí)候我們得到相對(duì)簡(jiǎn)單的計(jì)算公式肢专。每當(dāng) GPU 需要計(jì)算出屏幕上一個(gè)像素是什么顏色的時(shí)候舞肆,它只需要考慮在這個(gè)像素之上的所有 layer 中對(duì)應(yīng)的單個(gè)像素,并把這些像素合并到一起鸟召。
2.如果最頂層的紋理是不透明的(即圖層樹的最底層)胆绊,這時(shí)候 GPU 就可以簡(jiǎn)單的拷貝它的像素到屏幕上氨鹏。
當(dāng)一個(gè) layer 上所有的像素和屏幕上的像素完美的對(duì)應(yīng)整齊欧募,那這個(gè) layer 就是像素對(duì)齊的。主要有兩個(gè)原因可能會(huì)造成不對(duì)齊仆抵。
第一個(gè)便是滾動(dòng)跟继;當(dāng)一個(gè)紋理上下滾動(dòng)的時(shí)候,紋理的像素便不會(huì)和屏幕的像素排列對(duì)齊镣丑。
另一個(gè)原因便是當(dāng)紋理的起點(diǎn)不在一個(gè)像素的邊界上舔糖。
在這兩種情況下,GPU 需要再做額外的計(jì)算莺匠。它需要將源紋理上多個(gè)像素混合起來(lái)金吗,生成一個(gè)用來(lái)合成的值。
當(dāng)所有的像素都是對(duì)齊的時(shí)候趣竣,GPU 只剩下很少的工作要做摇庙。
離屏渲染
1.什么是離屏渲染
On-Screen Rendering
意為當(dāng)前屏幕渲染,指的是GPU的渲染操作是在當(dāng)前用于顯示的屏幕緩沖區(qū)中進(jìn)行遥缕。
Off-Screen Rendering
意為離屏渲染卫袒,指的是GPU在當(dāng)前屏幕緩沖區(qū)以外新開辟一個(gè)緩沖區(qū)進(jìn)行渲染操作。
當(dāng)圖層屬性的混合體被指定為在未預(yù)合成之前不能直接在屏幕中繪制時(shí)单匣,屏幕外渲染就被喚起了夕凝。屏幕外渲染并不意味著軟件繪制宝穗,但是它意味著圖層必須在被顯示之前在一個(gè)屏幕外上下文中被渲染(不論CPU還是GPU)
。
離屏渲染可以被 Core Animation 自動(dòng)觸發(fā)码秉,或者被應(yīng)用程序強(qiáng)制觸發(fā)逮矛。屏幕外的渲染會(huì)合并/渲染圖層樹的一部分到一個(gè)新的緩沖區(qū),然后該緩沖區(qū)被渲染到屏幕上转砖。
這里提到的offscreen rendering主要講的是通過(guò)GPU執(zhí)行的offscreen,事實(shí)上還有的offscreen rendering是通過(guò)CPU來(lái)執(zhí)行的橱鹏。
如果我們重寫了drawRect方法,并且使用任何Core Graphics的技術(shù)進(jìn)行了繪制操作堪藐,就涉及到了CPU渲染莉兰。整個(gè)渲染過(guò)程由CPU在App內(nèi)同步地完成,渲染得到的bitmap最后再交由GPU用于顯示礁竞。其它類似cornerRadios, masks, shadows等觸發(fā)的offscreen是基于GPU的糖荒。
2.為什么要謹(jǐn)慎避免離屏渲染
離屏渲染主要在兩個(gè)地方開銷較大:
創(chuàng)建新緩沖區(qū)
要想進(jìn)行離屏渲染,首先要?jiǎng)?chuàng)建一個(gè)新的緩沖區(qū)模捂。
上下文切換
離屏渲染的整個(gè)過(guò)程捶朵,需要多次切換上下文環(huán)境:先是從當(dāng)前屏幕(On-Screen)切換到離屏(Off-Screen);等到離屏渲染結(jié)束以后狂男,將離屏緩沖區(qū)的渲染結(jié)果顯示到屏幕上有需要將上下文環(huán)境從離屏切換到當(dāng)前屏幕综看。而上下文環(huán)境的切換是要付出很大代價(jià)的。
3.一些觸發(fā)離屏渲染的方式
設(shè)置了以下屬性時(shí)岖食,都會(huì)觸發(fā)離屏繪制:
shouldRasterize(光柵化)
masks(遮罩)
shadows(陰影)
edge antialiasing(抗鋸齒)
group opacity(不透明)
PS:光柵化
在CALayer中红碑,設(shè)置shouldRasterize = YES便會(huì)觸發(fā)光柵化,且會(huì)將光柵化后的內(nèi)容緩存起來(lái)泡垃。相當(dāng)于光柵化是把GPU的操作轉(zhuǎn)到CPU上了析珊,生成位圖緩存,直接讀取復(fù)用蔑穴。
因?yàn)殡x屏渲染本身開銷較大忠寻,所以對(duì)于是否需要光柵化,應(yīng)該因地制宜地使用存和。且系統(tǒng)設(shè)置了對(duì)這個(gè)光柵化的內(nèi)存使用限制奕剃,有兩點(diǎn)需要注意:
不要過(guò)度使用,系統(tǒng)限制了緩存的大小為2.5X Screen Size.如果過(guò)度使用,超出緩存之后,同樣會(huì)造成大量的offscreen渲染。
被光柵化的圖片如果超過(guò)100ms沒有被使用,則會(huì)被移除
因此我們應(yīng)該只對(duì)連續(xù)不斷使用的圖片進(jìn)行緩存捐腿。對(duì)于不常使用的圖片緩存是沒有意義,且耗費(fèi)資源的纵朋。
對(duì)于那些需要?jiǎng)赢嫸乙谄聊煌怃秩镜膱D層來(lái)說(shuō),你可以用CAShapeLayer叙量,contentsCenter或者shadowPath來(lái)獲得同樣的表現(xiàn)而且較少地影響到性能
小結(jié):
在上一章中倡蝙。我們已經(jīng)知道了影響界面卡頓的主要原因是在一個(gè) VSync 時(shí)間內(nèi),CPU 或者 GPU 沒有完成內(nèi)容提交绞佩,則那一幀就會(huì)被丟棄
寺鸥。所以猪钮,我們要盡可能的節(jié)約CPU,GPU的開銷胆建。在知道本文中講述的影響渲染性能的關(guān)鍵點(diǎn)以后烤低,開發(fā)中就可以注意使用相關(guān)功能來(lái)減少對(duì)CPU/GPU的開銷。