(轉(zhuǎn))Advanced Graphics and Animations for iOS Apps

這篇文章主要是學(xué)習(xí)完Advanced Graphics and Animations for iOS Apps這個session后的總結(jié)和相應(yīng)細(xì)節(jié)的延伸和細(xì)化。主要內(nèi)容為圖形性能與測試工具這兩個章節(jié)。

目錄:

Core Animation Pipeline

UIBlurEffectView

圖形性能

測試工具

總結(jié)

Core Animation Pipeline

第一部分主要講解了Core Animation的工作流程和渲染過程。

CoreAnimation的渲染流程可以用下圖來概括:

在GPU的渲染過程中,我們能看到頂點(diǎn)著色器與像素著色器參與到圖像的處理。

在objc.io中有一篇文章進(jìn)一步地闡明了頂點(diǎn)著色器與像素著色器 (GPU 加速下的圖像處理)

UIBlurEffectView

第二部分主要講解了iOS8新引入的UIBlurEffectView,結(jié)合第一部分來闡述UIBlurEffectView是如何工作的,以及它們的性能。

事實(shí)上,個人覺得有一點(diǎn)很有趣。就是UIBlurEffectView為了優(yōu)化圖像處理效率,并不是用普通的模糊算法。雖然Session中沒提到模糊算法,我在這里簡單地介紹一下簡單的模糊算法。

最簡單模糊的過程即是用中心像素與其周圍像素的顏色算術(shù)平均值來代表模糊后的顏色值。我們可以在下面兩幅圖看到中心像素值的變化。

原始圖像

中心像素模糊化

那么蘋果是怎么做的呢?最有趣的一點(diǎn)是它并不是對原始圖像直接模糊,而是先將圖像縮放之后再進(jìn)行模糊。這樣的優(yōu)點(diǎn)就是模糊算法需要處理的像素點(diǎn)就減少了,處理的效率會更快。

看到這里的時候我是笑了的,哈哈,覺得很機(jī)智。適當(dāng)?shù)陌阉季S跳出來,"偷點(diǎn)懶",有時真的會取得很不錯的效果。學(xué)習(xí),學(xué)習(xí)。

其次就是水平模糊與豎直模糊后再合成,添加顏色。

最后需要關(guān)注和有趣的一點(diǎn)是:蘋果為我們提供了三個UIBlurEffect styles,

分別為Extra light, Light, Dark.但是三者的耗費(fèi)的資源各為不同。

Extra light耗費(fèi)最多資源, Light其次, Dark最少。

我在自己的個人項(xiàng)目里也有用到UIBlurEffectView來美化界面,優(yōu)化用戶體驗(yàn)。兩個項(xiàng)目都已經(jīng)上架,并完整開源。歡迎去看看。

TouchColor

主要使用在主菜單界面。

QR Catcher

用在了實(shí)時蒙版,即類似微信二維碼掃描框外的黑色半透明背景,在這里則是實(shí)時模糊,更美觀。

圖形性能

關(guān)于圖形性能在之前關(guān)注的不夠多,主要是用前人總結(jié)好的比較教條式的優(yōu)化方式。這次借這個Session的學(xué)習(xí),繼續(xù)往外擴(kuò)展閱讀學(xué)習(xí),好好梳理和學(xué)習(xí)遺漏點(diǎn),底層細(xì)節(jié),原理與性能優(yōu)化的工具

1. 關(guān)于CALayer的shouldRasterize(光柵化)

開啟shouldRasterize后,CALayer會被光柵化為bitmap,layer的陰影等效果也會被保存到bitmap中。

當(dāng)我們開啟光柵化后,需要注意三點(diǎn)問題。

如果我們更新已光柵化的layer,會造成大量的offscreen渲染。

因此CALayer的光柵化選項(xiàng)的開啟與否需要我們仔細(xì)衡量使用場景。只能用在圖像內(nèi)容不變的前提下的:

用于避免靜態(tài)內(nèi)容的復(fù)雜特效的重繪,例如前面講到的UIBlurEffect

用于避免多個View嵌套的復(fù)雜View的重繪。

而對于經(jīng)常變動的內(nèi)容,這個時候不要開啟,否則會造成性能的浪費(fèi)。

例如我們?nèi)粘探?jīng)常打交道的TableViewCell,因?yàn)門ableViewCell的重繪是很頻繁的(因?yàn)镃ell的復(fù)用),如果Cell的內(nèi)容不斷變化,則Cell需要不斷重繪,如果此時設(shè)置了cell.layer可光柵化。則會造成大量的offscreen渲染,降低圖形性能。

當(dāng)然,合理利用的話,是能夠得到不少性能的提高的,因?yàn)槭褂胹houldRasterize后layer會緩存為Bitmap位圖,對一些添加了shawdow等效果的耗費(fèi)資源較多的靜態(tài)內(nèi)容進(jìn)行緩存,能夠得到性能的提升。

不要過度使用,系統(tǒng)限制了緩存的大小為2.5X Screen Size.

如果過度使用,超出緩存之后,同樣會造成大量的offscreen渲染。

被光柵化的圖片如果超過100ms沒有被使用,則會被移除

因此我們應(yīng)該只對連續(xù)不斷使用的圖片進(jìn)行緩存。對于不常使用的圖片緩存是沒有意義,且耗費(fèi)資源的。

2. 關(guān)于offscreen rendering

注意到上面提到的offscreen rendering。我們需要注意shouldRasterize的地方就是會造成offscreen rendering的地方,那么為什么需要避免呢?

WWDC 2011 Understanding UIKit Rendering指出一般導(dǎo)致圖形性能的問題大部分都出在了offscreen rendering,因此如果我們發(fā)現(xiàn)列表滾動不流暢,動畫卡頓等問題,就可以想想和找出我們哪部分代碼導(dǎo)致了大量的offscreen 渲染。

首先,什么是offscreen rendering?

offscreen rendring指的是在圖像在繪制到當(dāng)前屏幕前,需要先進(jìn)行一次渲染,之后才繪制到當(dāng)前屏幕。

那么為什么offscreen渲染會耗費(fèi)大量資源呢?

原因是顯卡需要另外alloc一塊內(nèi)存來進(jìn)行渲染,渲染完畢后在繪制到當(dāng)前屏幕,而且對于顯卡來說,onscreen到offscreen的上下文環(huán)境切換是非常昂貴的(涉及到OpenGL的pipelines和barrier等),

備注:

這里提到的offscreen rendering主要講的是通過GPU執(zhí)行的offscreen,事實(shí)上還有的offscreen rendering是通過CPU來執(zhí)行的(例如使用Core Graphics, drawRect)。其它類似cornerRadios, masks, shadows等觸發(fā)的offscreen是基于GPU的。

許多人有誤區(qū),認(rèn)為offscreen rendering就是software rendering,只是純粹地靠CPU運(yùn)算。實(shí)際上并不是的,offscreen rendering是個比較復(fù)雜,涉及許多方面的內(nèi)容。

我們在開發(fā)應(yīng)用,提高性能通常要注意的是避免offscreen rendering。不需要糾結(jié)和拘泥于它的定義.

有興趣可以繼續(xù)閱讀Andy Matuschak, 前UIKit team成員關(guān)于offscreen rendering的評論

總之,我們通常需要避免大量的offscreen rendering.

會造成 offscreen rendering的原因有:

Any layer with a mask (layer.mask)

Any layer with layer.masksToBounds 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

因此,對于一些需要優(yōu)化圖像性能的場景,我們可以檢查我們是否觸發(fā)了offscreen rendering。 并用更高效的實(shí)現(xiàn)手段來替換。

例如:

陰影繪制:

使用ShadowPath來替代shadowOffset等屬性的設(shè)置。

一個如圖的簡單tableView:

兩種不同方式來繪制陰影:

不使用shadowPath

CALayer *imageViewLayer = cell.imageView.layer;

imageViewLayer.shadowColor = [UIColor blackColor].CGColor;

imageViewLayer.shadowOpacity = 1.0;

imageViewLayer.shadowRadius = 2.0;

imageViewLayer.shadowOffset = CGSizeMake(1.0, 1.0);

使用shadowPath

imageViewLayer.shadowPath = CGPathCreateWithRect(imageRect, NULL);

我們可以在下圖看到兩種方式巨大的性能差別。

個人推測的shadowPath高效的原因是使用shadowPath避免了offscreen渲染,因?yàn)閮H需要直接繪制路徑即可,不需要提前讀取圖像去渲染。

裁剪圖片為圓:

如圖為例

使用CornerRadius:

CALayer *imageViewLayer = cell.imageView.layer;

imageViewLayer.cornerRadius = imageHeight / 2.0;

imageViewLayer.masksToBounds = YES;

利用一張中間為透明圓形的圖片來進(jìn)行遮蓋,雖然會引起blending,但性能仍然高于offerScreen。

根據(jù)蘋果測試,第二種方式比第一種方式更高效:

以上舉了兩個例子闡明了在避免大量的offerscreen渲染后,性能能夠得到非常直觀有效的提高。

3. 關(guān)于blending

前面提到了用透明圓形的圖片來進(jìn)行遮蓋,會引起blending。blending也會耗費(fèi)性能。

:) 笑。如果閱讀這篇文章的讀者看到這里,是不是覺得已經(jīng)無眼看下去了。哈哈,我自己學(xué)習(xí)總結(jié)到這里也是感受到了長路慢慢,但是我們?nèi)匀贿€是要不斷上下求索的。 :)

好了 接下來讓我們來認(rèn)識一下Blending.

什么是Blending?

在iOS的圖形處理中,blending主要指的是混合像素顏色的計算。最直觀的例子就是,我們把兩個圖層疊加在一起,如果第一個圖層的透明的,則最終像素的顏色計算需要將第二個圖層也考慮進(jìn)來。這一過程即為Blending。

會導(dǎo)致blending的原因:

layer(UIView)的Alpha < 1

UIImgaeView的image含有Alpha channel(即使UIImageView的alpha是1,但只要image含透明通道,則仍會導(dǎo)致Blending)

為什么Blending會導(dǎo)致性能的損失?

原因是很直觀的,如果一個圖層是不透明的,則系統(tǒng)直接顯示該圖層的顏色即可。而如果圖層是透明的,則會引入更多的計算,因?yàn)樾枰严旅娴膱D層也包括進(jìn)來,進(jìn)行混合后顏色的計算。

在了解完Blending之后,我們就知道為什么很多優(yōu)化準(zhǔn)則都需要我們盡量使用不透明圖層了。接下來就是在開發(fā)中留意和進(jìn)行優(yōu)化了。

測試工具

在出現(xiàn)圖像性能問題,滑動,動畫不夠流暢之后,我們首先要做的就是定位出問題的所在。而這個過程并不是只靠經(jīng)驗(yàn)和窮舉法探索,我們應(yīng)該用有脈絡(luò),有順序的科學(xué)的手段進(jìn)行探索。

首先,我們要有一個定位問題的模式。我們可以按照這樣的順序來逐步定位,發(fā)現(xiàn)問題。

定位幀率,為了給用戶流暢的感受,我們需要保持幀率在60幀左右。當(dāng)遇到問題后,我們首先檢查一下幀率是否保持在60幀。

定位瓶頸,究竟是CPU還是GPU。我們希望占用率越少越好,一是為了流暢性,二也節(jié)省了電力。

檢查有沒有做無必要的CPU渲染,例如有些地方我們重寫了drawRect,而其實(shí)是我們不需要也不應(yīng)該的。我們希望GPU負(fù)責(zé)更多的工作。

檢查有沒有過多的offscreen渲染,這會耗費(fèi)GPU的資源,像前面已經(jīng)分析的到的。offscreen 渲染會導(dǎo)致GPU需要不斷地onScreen和offscreen進(jìn)行上下文切換。我們希望有更少的offscreen渲染。

檢查我們有無過多的Blending,GPU渲染一個不透明的圖層更省資源。

檢查圖片的格式是否為常用格式,大小是否正常。如果一個圖片格式不被GPU所支持,則只能通過CPU來渲染。一般我們在iOS開發(fā)中都應(yīng)該用PNG格式,之前閱讀過的一些資料也有指出蘋果特意為PNG格式做了渲染和壓縮算法上的優(yōu)化。

檢查是否有耗費(fèi)資源多的View或效果。我們需要合理有節(jié)制的使用。像之前提到的UIBlurEffect就是一個例子。

最后,我們需要檢查在我們View層級中是否有不正確的地方。例如有時我們不斷的添加或移除View,有時就會在不經(jīng)意間導(dǎo)致bug的發(fā)生。像我之前就遇到過不斷添加View的一個低級錯誤。我們希望在View層級中只包含了我們想要的東西。

OK,當(dāng)我們有了一套模式之后,就可以使用蘋果為我們提供的優(yōu)秀測試工具來進(jìn)行測試了。

對于圖形性能問題的地位。一般我們有下列測試工具:

Instruments里的:

Core Animation instrument

OpenGL ES Driver instrument

模擬器中的:

Color debug options View debugging

還有Xcode的:

View debugging

然后我們來根據(jù)上面定位問題的模式來選擇相應(yīng)測試工具:

定位幀率

定位瓶頸

檢查有無必要的CPU渲染

以上三點(diǎn)我們可以使用CoreAnimation instrument來測試。

CoreAnimation instrument包含了兩個模塊。第一幅圖展示了檢測幀率。第二幅圖展示了檢測CPU調(diào)用。我們能夠通過它們來進(jìn)行上述三個問題的檢測。注意到第二幅圖左下角,那是CPU 的call stack.我們就是在這里檢測我們有沒有做無必要的drawRect,有沒有在主線程做太多事務(wù)導(dǎo)致阻塞了UI更新。

關(guān)于GPU的瓶頸問題,我們可以通過OpenGL ES Driver instrument來獲得更詳細(xì)的信息。例如GPU的占用率。可以看到下圖左下角有顯示Device utilization。

檢查有無過多offscreen渲染

檢查有無過多Blending

檢查有無不正確圖片格式,圖片是否被放縮,像素是否對齊。

檢查有無使用復(fù)雜的圖形效果。

以上這四點(diǎn)我們同樣使用CoreAnimation instrument來測試。

我們可以看到上圖右下角的Debug options有多個選項(xiàng)。我們通過勾選這些選項(xiàng)來觸發(fā)Color Debug。下面逐個對這些選項(xiàng)進(jìn)行分析。

Color Blended layers

如圖,勾選這個選項(xiàng)后,blended layer 就會被顯示為紅色,而不透明的layer則是綠色。我們希望越少紅色區(qū)域越好。

Color Hits Green and Misses Red

這個選項(xiàng)主要是檢測我們有無濫用或正確使用layer的shouldRasterize屬性.成功被緩存的layer會標(biāo)注為綠色,沒有成功緩存的會標(biāo)注為紅色。

在測試的過程中,第一次加載時,開啟光柵化的layer會顯示為紅色,這是很正常的,因?yàn)檫€沒有緩存成功。但是如果在接下來的測試,例如我們來回滾動TableView時,我們?nèi)匀话l(fā)現(xiàn)有許多紅色區(qū)域,那就需要謹(jǐn)慎對待了。因?yàn)橄裎覀兦懊嬗懻撨^的,這會引起offscreen rendering。

檢查一下是否有濫用該屬性,因?yàn)橄到y(tǒng)規(guī)定的緩存大小是屏幕大小的2.5倍,如果使用過度,超出了緩存大小,會引起offscreen rendering。檢測layer是否內(nèi)容不斷更新,內(nèi)容的更新會導(dǎo)致緩存失效和大量的offscreen rendering.

Color copied images

這個選項(xiàng)主要檢查我們有無使用不正確圖片格式,若是GPU不支持的色彩格式的圖片則會標(biāo)記為青色,則只能由CPU來進(jìn)行處理。我們不希望在滾動視圖的時候,CPU實(shí)時來進(jìn)行處理,因?yàn)橛锌赡軙枞骶€程。

Color misaligned images

這個選項(xiàng)檢查了圖片是否被放縮,像素是否對齊。被放縮的圖片會被標(biāo)記為黃色,像素不對齊則會標(biāo)注為紫色。

Color offscreen-rendered yellow

這個選項(xiàng)將需要offscreen渲染的的layer標(biāo)記為黃色。

以上圖為例子,NavigationBar和ToolBar被標(biāo)記為黃色。因?yàn)樗鼈冃枰:澈蟮膬?nèi)容,這需要offscreen渲染。但是這是我們需要的。而圖片也是被標(biāo)記為黃色,那是因?yàn)殛幱暗木壒省N仪懊嬉呀?jīng)提到了這一點(diǎn),如果此時我們用shadowPath來替代的話,就能夠避免offscreen渲染帶來的巨大開銷。

Color OpenGL fast path blue

這個選項(xiàng)勾選后,由OpenGL compositor進(jìn)行繪制的圖層會標(biāo)記為藍(lán)色。這是一個好的結(jié)果。

Flash updated regions

會標(biāo)記屏幕上被快速更新的部分為黃色,我們希望只是更新的部分被標(biāo)記完黃色。

好啦,終于完整介紹完這些調(diào)試選項(xiàng)了,我們總結(jié)一下。

我們需要重點(diǎn)注意的是

1.Color Blended layers

2.Color Hits Green and Misses Red

3.Color offscreen-rendered yellow這三個選項(xiàng)。

因?yàn)檫@三個部分對性能的影響最大。

檢查View層級是否正確。

我們可以在上圖清楚地看到View的層級關(guān)系。可以檢查View的層級是否正確。

小提示(應(yīng)用運(yùn)行后,在這里打開):從左往右第七個圖標(biāo)

總結(jié)

關(guān)于圖形性能還有許多細(xì)節(jié)和底層可以深入,不過經(jīng)過這一次總結(jié)與學(xué)習(xí),基本把握了iOS圖形性能的優(yōu)化細(xì)節(jié)和工具。希望也能夠?qū)δ阌幸稽c(diǎn)幫助。

在學(xué)習(xí)和探索的過程中,個人感受最深的是兩點(diǎn)。

一類事情背后都會有一定的原理,弄清楚了原理就能更好地把握這一類事務(wù)。

在之前的iOS開發(fā)中,對圖形界面的優(yōu)化主要處于用前人總結(jié)的教條來優(yōu)化。而經(jīng)過這次學(xué)習(xí)之后,明白這些教條背后的原理,像最影響性能的offscreen rendering和blending。更能有針對性的優(yōu)化和分析。

檢測問題不應(yīng)該是盲目的,有一定的模式和工具會更清晰。

像對圖形性能的問題定位,我們不應(yīng)該一上來就開始找問題,看代碼。而是應(yīng)該逐步定位。而是像前面總結(jié)的一樣,定位幀率,摸清瓶頸,逐個問題擊破。再配合合適的工具進(jìn)行測試和定位,一定能夠提升效率和準(zhǔn)確度

原文出處:100mango github 地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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