onScreen和offScreen渲染(一)

版本記錄

版本號(hào) 時(shí)間
V1.0 2017.06.15

前言

我們要在界面上看到東西,是因?yàn)榭丶讳秩镜狡聊簧系脑?,說到渲染大家都知道包括當(dāng)屏渲染和離屏渲染,這里就和大家說一下onScreen和offScreen。

onScreen渲染

顧名思義,就是將視圖直接渲染到屏幕上。onscreen render指的是GPU在當(dāng)前用于顯示的屏幕緩沖區(qū)進(jìn)行渲染。


offScreen渲染

一、基本概念

offscreen rendring指的是在圖像在繪制到當(dāng)前屏幕前,需要先進(jìn)行一次渲染,之后才繪制到當(dāng)前屏幕。 那么為什么offscreen渲染會(huì)耗費(fèi)大量資源呢? 原因是顯卡需要另外alloc一塊內(nèi)存來進(jìn)行渲染,渲染完畢后在繪制到當(dāng)前屏幕,而且對(duì)于顯卡來說,onscreen到offscreen的上下文環(huán)境切換是非常昂貴的(涉及到OpenGL的pipelines和barrier等)。
??與當(dāng)前屏幕渲染相比,離屏渲染的代價(jià)是很高的,主要體現(xiàn)在兩個(gè)方面:

  • 創(chuàng)建新緩沖區(qū):要想進(jìn)行離屏渲染,首先要?jiǎng)?chuàng)建一個(gè)新的緩沖區(qū)。
  • 上下文切換:離屏渲染的整個(gè)過程,需要多次切換上下文環(huán)境。先是從當(dāng)前屏幕(On-Screen)切換到離屏(Off-Screen);等到離屏渲染結(jié)束以后,將離屏緩沖區(qū)的渲染結(jié)果顯示到屏幕上又需要將上下文環(huán)境從離屏切換到當(dāng)前屏幕。但是,上下文環(huán)境的切換是要付出很大代價(jià)的。

二、離屏渲染的幾種情況

  • 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.

下面我就給大家翻一下,英語不好,翻譯不對(duì)的地方請(qǐng)指出。

  • 帶有遮罩的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之后,會(huì)把被光柵化的圖層保存成位圖并緩存起來,其中圓角或者陰影之類的效果也是直接保存到位圖當(dāng)中,當(dāng)需要渲染到屏幕上的時(shí)候只需要到緩存中去取對(duì)應(yīng)的位圖進(jìn)行顯示就行了,加快了整個(gè)渲染過程??梢酝ㄟ^勾選instruments core animation中的Color Hits Green and Misses Red選項(xiàng)來查看圖層是否被緩存了,如果圖層顯示為綠色則表示已經(jīng)被緩存起來了,也就是這個(gè)緩沖區(qū)的內(nèi)容被復(fù)用了,不用在去重新創(chuàng)建緩沖區(qū),反之則是用紅色標(biāo)示。設(shè)置shouldRasterize之后,如果滑動(dòng)過程中發(fā)現(xiàn)都是紅色的證明就有問題了。也可以這么表述:如果在設(shè)置為YES后,在快速滾動(dòng)的時(shí)候,綠色較少,而紅色較多,說明并不可觀,表示不建議使用光柵化。相反,如果在快速滾動(dòng)的時(shí)候,基本都是綠色的,只有極少數(shù)偶爾出現(xiàn)紅色,那么使用光柵化來優(yōu)化是可以達(dá)到很好的效果的。
  • layer設(shè)置了 layer.cornerRadius, layer.edgeAntialiasingMask, layer.allowsEdgeAntialiasing Text(任何種類,包括UILabel、CATextLayer、CoreText等)
  • 在drawRect中利用上下文CGContext繪圖,即使什么都沒畫,只是空的實(shí)現(xiàn)也會(huì)導(dǎo)致離屏渲染。

大家都知道離屏渲染多了,系統(tǒng)性能就會(huì)降低,變的很卡。所以要盡量避免以下幾點(diǎn):

  • 盡量將視圖背景色和superView的背景色一致/背景色不要為clearColor(會(huì)引起圖層混合)。

  • 假如最上層的view是不透明的,那直接使用這個(gè)view的對(duì)應(yīng)顏色之就可以,但如果view是透明的,在計(jì)算像素的顏色值時(shí)就需要計(jì)算它下面圖層,透明的視圖越多,計(jì)算量就越大,因此也會(huì)對(duì)圖形的性能產(chǎn)生一定的影響,所以可以的話也盡量減少透明圖層的數(shù)目。

  • 光柵化對(duì)于那些有很多子view嵌套在一起、view的層級(jí)復(fù)雜或者有很復(fù)雜特效效果的圖層有很明顯的提升,因?yàn)檫@些內(nèi)容都被緩存到位圖當(dāng)中了。但是使用光柵化需要注意一些內(nèi)容:

    • 適用于內(nèi)容基本不變的圖層
      假如圖層的內(nèi)容經(jīng)常變化,比如cell里面有涉及到動(dòng)畫之類的,那么緩存的內(nèi)容就無效了,GPU需要重新創(chuàng)建緩存區(qū),導(dǎo)致離屏渲染,這又涉及到OpenGL的上下文環(huán)境切換,反而降低性能。
    • 不要過度使用
      緩存區(qū)的大小被設(shè)置為屏幕大小的2.5倍,假如過分使用同樣會(huì)導(dǎo)致大量的離屏渲染。
    • 如果緩存的內(nèi)容超過100ms沒有被使用則會(huì)被回收。
  • 避免離屏渲染。將圖層的alpha設(shè)為1(如何其不為1,圖層合成過程中,將引起大量的計(jì)算),uiview的圓角,遮罩 也會(huì)引起離屏渲染。

  • 減少不必要的drawrect()操作。

  • 處理好UIView 與 CALayer的關(guān)系。UIView層次不要太多。因?yàn)檫@樣會(huì)加多合成。

  • 將線程安全的操作挪到非主線程。比如繪制圖片等。

三、界面卡頓的原因

可能大家有所疑惑,為什么有的時(shí)候說性能低界面卡,到底是什么原因,其實(shí)可以從軟硬件結(jié)合的角度講。

原因:GPU垂直同步信號(hào)(VSync,硬件產(chǎn)生的),固定1/60S發(fā)出一次。在此時(shí)間間隔內(nèi),CPU、GPU需要準(zhǔn)備好數(shù)據(jù)。在 VSync 信號(hào)到來后,系統(tǒng)圖形服務(wù)會(huì)通過 CADisplayLink 等機(jī)制通知 App,App 主線程開始在 CPU 中計(jì)算顯示內(nèi)容,比如視圖的創(chuàng)建、布局計(jì)算、圖片解碼、文本繪制等。隨后 CPU 會(huì)將計(jì)算好的內(nèi)容提交到 GPU 去,由 GPU 進(jìn)行變換、合成、渲染。隨后 GPU 會(huì)把渲染結(jié)果提交到幀緩沖區(qū)去,等待下一次 VSync 信號(hào)到來時(shí)顯示到屏幕上。由于垂直同步的機(jī)制,如果在一個(gè) VSync 時(shí)間內(nèi),CPU 或者 GPU 沒有完成內(nèi)容提交,則那一幀就會(huì)被丟棄,等待下一次機(jī)會(huì)再顯示,而這時(shí)顯示屏?xí)A糁暗膬?nèi)容不變。這就是界面卡頓的原因。即FPS降低。

  • CPU做的工作:對(duì)象的創(chuàng)建;調(diào)整;layout;布局的計(jì)算;文本的計(jì)算,渲染;圖片的渲染,解碼,渲染等。
  • GPU做的工作:紋理的渲染,圖形混合,圖形生成。(離屏渲染發(fā)生在GPU中)。

四、離屏渲染的分類

離屏渲染分為兩種:一種是CPU的渲染,另外一種就是GPU渲染。

CPU離屏渲染

使用CPU來完成渲染操縱,通常在你使用:

  • drawRect (如果沒有自定義繪制的任務(wù)就不要在子類中寫一個(gè)空的drawRect方法,因?yàn)橹灰獙?shí)現(xiàn)了該方法,就會(huì)為視圖分配一個(gè)寄宿圖,這個(gè)寄宿圖的像素尺寸等于視圖大小乘以 contentsScale的值,造成資源浪費(fèi))。
  • 使用Core Graphics。

上面的兩種情況使用的就是CPU離屏渲染,首先分配一塊內(nèi)存,然后進(jìn)行渲染操作生成一份bitmap位圖,整個(gè)渲染過程會(huì)在你的應(yīng)用中同步的進(jìn)行,接著再將位圖打包發(fā)送到iOS里一個(gè)單獨(dú)的進(jìn)程--render server,理想情況下,render server將內(nèi)容交給GPU直接顯示到屏幕上。

GPU離屏渲染

使用GPU在當(dāng)前屏幕緩沖區(qū)以外開辟一個(gè)新的緩沖區(qū)進(jìn)行繪制,通常發(fā)生的情況有:

  • 設(shè)置cornerRadius, masks, shadows,edge antialiasing(抗鋸齒)等
  • 設(shè)置layer.shouldRasterize = YES

具體的渲染過程如下圖所示。

渲染過程圖

通常大家說的離屏渲染指的是GPU這塊(當(dāng)然CPU這塊也會(huì)有影響,也需要消耗一定的資源),比如修改了layer的陰影或者圓角,GPU需要做額外的渲染操作。通常GPU在做渲染的時(shí)候是很快的,但是涉及到offscreen-render的時(shí)候情況就可能有些不同,因?yàn)樾枰~外開辟一個(gè)新的緩沖區(qū)進(jìn)行渲染,然后繪制到當(dāng)前屏幕的過程需要做onscreen跟offscreen上下文之間的切換,這個(gè)過程的消耗會(huì)比較昂貴,涉及到OpenGL的pipeline跟barrier,而且offscreen-render在每一幀都會(huì)涉及到,因此處理不當(dāng)肯定會(huì)對(duì)性能產(chǎn)生一定的影響,所以可以的話盡量減少offscreen-render的圖層,查看哪些圖層需要離屏渲染可以用Instruments的Core Animation工具進(jìn)行檢測,Color Offscreen-Rendered Yellow選項(xiàng)會(huì)將對(duì)應(yīng)的圖層標(biāo)記為黃色。

五、提高性能的幾種方式

  • 對(duì)于圓角可以使用一張中間圓形透明的圖覆蓋在上面,雖然這會(huì)引入blending操作,但是大部分情況下性能會(huì)比離屏渲染好。
  • 讓你的view層次結(jié)構(gòu)平坦一些,因?yàn)镺penGL在渲染layer的時(shí)候,在碰到有子層級(jí)layer的時(shí)候可能需要停下來把兩者合成到一個(gè)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í)候在邊滾動(dòng)邊設(shè)置圖片的時(shí)候可能會(huì)有一定的影響,因此可以在滾動(dòng)的時(shí)候imageview不執(zhí)行setimage的操作,滾動(dòng)停止的時(shí)候才加載圖片,由于滾動(dòng)的時(shí)候NSRunloop是處于UITrackingRunLoopMode模式下,可以采用如下的方式,將設(shè)置圖片放到NSDefaultRunLoopMode模式下才進(jìn)行:
UIImage *downloadedImage = ...;
  [self.avatarImageView performSelector:@selector(setImage:)
                             withObject:downloadedImage
                             afterDelay:0
                                inModes:@[NSDefaultRunLoopMode]];

圖片加載的極限優(yōu)化方式:FastImageCache

五、offScreen檢測

??利用instruments可以檢測offScreen,在Instruments->Core Animation下,如下圖。

offScreen檢測

注意:要在真機(jī)上才可以。

參考文章

1.iOS app性能優(yōu)化的那些事(二)

后記

未完,待續(xù),希望大家能喜歡~~~

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

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

  • 相比于當(dāng)前屏幕渲染,離屏渲染的代價(jià)是很高的,這也是iOS移動(dòng)端優(yōu)化的必要部分。 OpenGL中,GPU屏幕渲染有以...
    一個(gè)人在路上走下去閱讀 9,116評(píng)論 0 74
  • 一、概述 OpenGL ES是一套多功能開放標(biāo)準(zhǔn)的用于嵌入系統(tǒng)的C-based的圖形庫,用于2D和3D數(shù)據(jù)的可視化...
    灣里有桃樹閱讀 598評(píng)論 0 0
  • 離屏渲染 通常來說,計(jì)算機(jī)系統(tǒng)中 CPU、GPU、顯示器是以上面這種方式協(xié)同工作的。CPU 計(jì)算好顯示內(nèi)容提交到 ...
    Colleny_Z閱讀 1,100評(píng)論 0 2
  • CALayer與UIView iOS界面中,看到的界面元素基本都是UIView,例如按鈕,文本,圖片等都是集成自U...
    conowen閱讀 1,461評(píng)論 0 2
  • 卷首語 歡迎來到 objc.io 的第三期! 這一期都是關(guān)于視圖層的。當(dāng)然視圖層有很多方面,我們需要把它們縮小到幾...
    評(píng)評(píng)分分閱讀 1,930評(píng)論 0 18

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