版本記錄
版本號 | 時間 |
---|---|
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設置為YES
- layer.allowsGroupOpacity設置為YES或者layer.opacity<1.0
- layer帶有陰影shadow
- layer.shouldRasterize設置為YES
- layer設置shouldRasterize=Y(jié)ES之后,會把被光柵化的圖層保存成位圖并緩存起來,其中圓角或者陰影之類的效果也是直接保存到位圖當中,當需要渲染到屏幕上的時候只需要到緩存中去取對應的位圖進行顯示就行了,加快了整個渲染過程。可以通過勾選instruments core animation中的Color Hits Green and Misses Red選項來查看圖層是否被緩存了,如果圖層顯示為綠色則表示已經(jīng)被緩存起來了,也就是這個緩沖區(qū)的內(nèi)容被復用了,不用在去重新創(chuàng)建緩沖區(qū),反之則是用紅色標示。設置shouldRasterize之后,如果滑動過程中發(fā)現(xiàn)都是紅色的證明就有問題了。也可以這么表述:如果在設置為YES后,在快速滾動的時候,綠色較少,而紅色較多,說明并不可觀,表示不建議使用光柵化。相反,如果在快速滾動的時候,基本都是綠色的,只有極少數(shù)偶爾出現(xiàn)紅色,那么使用光柵化來優(yōu)化是可以達到很好的效果的。
- layer設置了 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ū)的大小被設置為屏幕大小的2.5倍,假如過分使用同樣會導致大量的離屏渲染。 - 如果緩存的內(nèi)容超過100ms沒有被使用則會被回收。
- 適用于內(nèi)容基本不變的圖層
避免離屏渲染。將圖層的alpha設為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ā)生的情況有:
- 設置cornerRadius, masks, shadows,edge antialiasing(抗鋸齒)等
- 設置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).
- 延遲加載圖片
有時候在邊滾動邊設置圖片的時候可能會有一定的影響,因此可以在滾動的時候imageview不執(zhí)行setimage的操作,滾動停止的時候才加載圖片,由于滾動的時候NSRunloop是處于UITrackingRunLoopMode模式下,可以采用如下的方式,將設置圖片放到NSDefaultRunLoopMode模式下才進行:
UIImage *downloadedImage = ...;
[self.avatarImageView performSelector:@selector(setImage:)
withObject:downloadedImage
afterDelay:0
inModes:@[NSDefaultRunLoopMode]];
圖片加載的極限優(yōu)化方式:FastImageCache
五、offScreen檢測
??利用instruments可以檢測offScreen,在Instruments->Core Animation下,如下圖。
注意:要在真機上才可以。
參考文章
后記
未完,待續(xù),希望大家能喜歡~~~