版本記錄
版本號 | 時間 |
---|---|
V1.0 | 2017.06.15 |
前言
我們要在界面上看到東西,是因為控件被渲染到屏幕上的原因娩贷,說到渲染大家都知道包括當屏渲染和離屏渲染,這里就和大家說一下onScreen和offScreen。
onScreen渲染
顧名思義棍好,就是將視圖直接渲染到屏幕上。onscreen render指的是GPU在當前用于顯示的屏幕緩沖區(qū)進行渲染允耿。
offScreen渲染
一借笙、基本概念
offscreen rendring指的是在圖像在繪制到當前屏幕前,需要先進行一次渲染,之后才繪制到當前屏幕。 那么為什么offscreen渲染會耗費大量資源呢较锡? 原因是顯卡需要另外alloc一塊內(nèi)存來進行渲染业稼,渲染完畢后在繪制到當前屏幕,而且對于顯卡來說蚂蕴,onscreen到offscreen的上下文環(huán)境切換是非常昂貴的(涉及到OpenGL的pipelines和barrier等)低散。
??與當前屏幕渲染相比,離屏渲染的代價是很高的骡楼,主要體現(xiàn)在兩個方面:
- 創(chuàng)建新緩沖區(qū):要想進行離屏渲染熔号,首先要創(chuàng)建一個新的緩沖區(qū)。
- 上下文切換:離屏渲染的整個過程鸟整,需要多次切換上下文環(huán)境跨嘉。先是從當前屏幕(On-Screen)切換到離屏(Off-Screen);等到離屏渲染結(jié)束以后吃嘿,將離屏緩沖區(qū)的渲染結(jié)果顯示到屏幕上又需要將上下文環(huán)境從離屏切換到當前屏幕祠乃。但是,上下文環(huán)境的切換是要付出很大代價的兑燥。
二亮瓷、離屏渲染的幾種情況
- Any layer with a mask (layer.mask)
- Any layer with layer.masksToBounds / view.clipsToBounds being true
- Any layer with layer.allowsGroupOpacity set to YES and layer.opacity is less than 1.0
- Any layer with a drop shadow (layer.shadow*).
- Any layer with layer.shouldRasterize being true
- Any layer with layer.cornerRadius, layer.edgeAntialiasingMask, layer.allowsEdgeAntialiasing
Text (any kind, including UILabel, CATextLayer, Core Text, etc). - Most of the drawing you do with CGContext in drawRect:. Even an empty implementation will be rendered offscreen.
下面我就給大家翻一下,英語不好降瞳,翻譯不對的地方請指出嘱支。
- 帶有遮罩的layer
- layer.masksToBounds和view.clipsToBounds設(shè)置為YES
- layer.allowsGroupOpacity設(shè)置為YES或者layer.opacity<1.0
- layer帶有陰影shadow
- layer.shouldRasterize設(shè)置為YES
- layer設(shè)置shouldRasterize=Y(jié)ES之后蚓胸,會把被光柵化的圖層保存成位圖并緩存起來,其中圓角或者陰影之類的效果也是直接保存到位圖當中除师,當需要渲染到屏幕上的時候只需要到緩存中去取對應的位圖進行顯示就行了沛膳,加快了整個渲染過程⊙淳郏可以通過勾選instruments core animation中的Color Hits Green and Misses Red選項來查看圖層是否被緩存了锹安,如果圖層顯示為綠色則表示已經(jīng)被緩存起來了,也就是這個緩沖區(qū)的內(nèi)容被復用了倚舀,不用在去重新創(chuàng)建緩沖區(qū)叹哭,反之則是用紅色標示。設(shè)置shouldRasterize之后痕貌,如果滑動過程中發(fā)現(xiàn)都是紅色的證明就有問題了风罩。也可以這么表述:如果在設(shè)置為YES后,在快速滾動的時候舵稠,綠色較少超升,而紅色較多,說明并不可觀哺徊,表示不建議使用光柵化廓俭。相反,如果在快速滾動的時候唉工,基本都是綠色的研乒,只有極少數(shù)偶爾出現(xiàn)紅色,那么使用光柵化來優(yōu)化是可以達到很好的效果的淋硝。
- layer設(shè)置了 layer.cornerRadius, layer.edgeAntialiasingMask, layer.allowsEdgeAntialiasing Text(任何種類雹熬,包括UILabel、CATextLayer谣膳、CoreText等)
- 在drawRect中利用上下文CGContext繪圖竿报,即使什么都沒畫,只是空的實現(xiàn)也會導致離屏渲染继谚。
大家都知道離屏渲染多了烈菌,系統(tǒng)性能就會降低,變的很卡花履。所以要盡量避免以下幾點:
盡量將視圖背景色和superView的背景色一致/背景色不要為clearColor(會引起圖層混合)芽世。
假如最上層的view是不透明的,那直接使用這個view的對應顏色之就可以诡壁,但如果view是透明的济瓢,在計算像素的顏色值時就需要計算它下面圖層,透明的視圖越多妹卿,計算量就越大旺矾,因此也會對圖形的性能產(chǎn)生一定的影響蔑鹦,所以可以的話也盡量減少透明圖層的數(shù)目。
-
光柵化對于那些有很多子view嵌套在一起箕宙、view的層級復雜或者有很復雜特效效果的圖層有很明顯的提升嚎朽,因為這些內(nèi)容都被緩存到位圖當中了。但是使用光柵化需要注意一些內(nèi)容:
- 適用于內(nèi)容基本不變的圖層
假如圖層的內(nèi)容經(jīng)常變化柬帕,比如cell里面有涉及到動畫之類的哟忍,那么緩存的內(nèi)容就無效了,GPU需要重新創(chuàng)建緩存區(qū)雕崩,導致離屏渲染,這又涉及到OpenGL的上下文環(huán)境切換融撞,反而降低性能盼铁。 - 不要過度使用
緩存區(qū)的大小被設(shè)置為屏幕大小的2.5倍,假如過分使用同樣會導致大量的離屏渲染尝偎。 - 如果緩存的內(nèi)容超過100ms沒有被使用則會被回收饶火。
- 適用于內(nèi)容基本不變的圖層
避免離屏渲染。將圖層的alpha設(shè)為1(如何其不為1致扯,圖層合成過程中肤寝,將引起大量的計算),uiview的圓角抖僵,遮罩 也會引起離屏渲染鲤看。
減少不必要的drawrect()操作。
處理好UIView 與 CALayer的關(guān)系耍群。UIView層次不要太多义桂。因為這樣會加多合成。
將線程安全的操作挪到非主線程蹈垢。比如繪制圖片等慷吊。
三、界面卡頓的原因
可能大家有所疑惑曹抬,為什么有的時候說性能低界面卡溉瓶,到底是什么原因,其實可以從軟硬件結(jié)合的角度講谤民。
原因:GPU垂直同步信號(VSync堰酿,硬件產(chǎn)生的),固定1/60S發(fā)出一次张足。在此時間間隔內(nèi)胞锰,CPU、GPU需要準備好數(shù)據(jù)兢榨。在 VSync 信號到來后嗅榕,系統(tǒng)圖形服務會通過 CADisplayLink 等機制通知 App顺饮,App 主線程開始在 CPU 中計算顯示內(nèi)容,比如視圖的創(chuàng)建凌那、布局計算兼雄、圖片解碼、文本繪制等帽蝶。隨后 CPU 會將計算好的內(nèi)容提交到 GPU 去赦肋,由 GPU 進行變換、合成励稳、渲染佃乘。隨后 GPU 會把渲染結(jié)果提交到幀緩沖區(qū)去,等待下一次 VSync 信號到來時顯示到屏幕上驹尼。由于垂直同步的機制趣避,如果在一個 VSync 時間內(nèi),CPU 或者 GPU 沒有完成內(nèi)容提交新翎,則那一幀就會被丟棄程帕,等待下一次機會再顯示,而這時顯示屏會保留之前的內(nèi)容不變地啰。這就是界面卡頓的原因愁拭。即FPS降低。
- CPU做的工作:對象的創(chuàng)建亏吝;調(diào)整岭埠;layout;布局的計算蔚鸥;文本的計算枫攀,渲染;圖片的渲染株茶,解碼来涨,渲染等。
- GPU做的工作:紋理的渲染启盛,圖形混合蹦掐,圖形生成。(離屏渲染發(fā)生在GPU中)僵闯。
四卧抗、離屏渲染的分類
離屏渲染分為兩種:一種是CPU的渲染,另外一種就是GPU渲染鳖粟。
CPU離屏渲染
使用CPU來完成渲染操縱社裆,通常在你使用:
- drawRect (如果沒有自定義繪制的任務就不要在子類中寫一個空的drawRect方法,因為只要實現(xiàn)了該方法向图,就會為視圖分配一個寄宿圖泳秀,這個寄宿圖的像素尺寸等于視圖大小乘以 contentsScale的值标沪,造成資源浪費)。
- 使用Core Graphics嗜傅。
上面的兩種情況使用的就是CPU離屏渲染金句,首先分配一塊內(nèi)存,然后進行渲染操作生成一份bitmap位圖吕嘀,整個渲染過程會在你的應用中同步的進行违寞,接著再將位圖打包發(fā)送到iOS里一個單獨的進程--render server,理想情況下偶房,render server將內(nèi)容交給GPU直接顯示到屏幕上趁曼。
GPU離屏渲染
使用GPU在當前屏幕緩沖區(qū)以外開辟一個新的緩沖區(qū)進行繪制,通常發(fā)生的情況有:
- 設(shè)置cornerRadius, masks, shadows,edge antialiasing(抗鋸齒)等
- 設(shè)置layer.shouldRasterize = YES
具體的渲染過程如下圖所示棕洋。
通常大家說的離屏渲染指的是GPU這塊(當然CPU這塊也會有影響挡闰,也需要消耗一定的資源),比如修改了layer的陰影或者圓角拍冠,GPU需要做額外的渲染操作尿这。通常GPU在做渲染的時候是很快的簇抵,但是涉及到offscreen-render的時候情況就可能有些不同庆杜,因為需要額外開辟一個新的緩沖區(qū)進行渲染,然后繪制到當前屏幕的過程需要做onscreen跟offscreen上下文之間的切換碟摆,這個過程的消耗會比較昂貴晃财,涉及到OpenGL的pipeline跟barrier,而且offscreen-render在每一幀都會涉及到典蜕,因此處理不當肯定會對性能產(chǎn)生一定的影響断盛,所以可以的話盡量減少offscreen-render的圖層,查看哪些圖層需要離屏渲染可以用Instruments的Core Animation工具進行檢測愉舔,Color Offscreen-Rendered Yellow選項會將對應的圖層標記為黃色钢猛。
五、提高性能的幾種方式
- 對于圓角可以使用一張中間圓形透明的圖覆蓋在上面轩缤,雖然這會引入blending操作命迈,但是大部分情況下性能會比離屏渲染好。
- 讓你的view層次結(jié)構(gòu)平坦一些火的,因為OpenGL在渲染layer的時候壶愤,在碰到有子層級layer的時候可能需要停下來把兩者合成到一個buffer里再接著渲染。(When the OpenGL renderer goes to draw each layer, it may have to stop for some subhierarchies and composite them into a single buffer).
- 延遲加載圖片
有時候在邊滾動邊設(shè)置圖片的時候可能會有一定的影響馏鹤,因此可以在滾動的時候imageview不執(zhí)行setimage的操作征椒,滾動停止的時候才加載圖片,由于滾動的時候NSRunloop是處于UITrackingRunLoopMode模式下湃累,可以采用如下的方式勃救,將設(shè)置圖片放到NSDefaultRunLoopMode模式下才進行:
UIImage *downloadedImage = ...;
[self.avatarImageView performSelector:@selector(setImage:)
withObject:downloadedImage
afterDelay:0
inModes:@[NSDefaultRunLoopMode]];
圖片加載的極限優(yōu)化方式:FastImageCache
五碍讨、offScreen檢測
??利用instruments可以檢測offScreen,在Instruments->Core Animation下剪芥,如下圖垄开。
注意:要在真機上才可以。
參考文章
后記
未完税肪,待續(xù)溉躲,希望大家能喜歡~~~