iOS的視圖繪制通常有三種方式,CoreGraphics、Quartz2D和OpenGL ES,性能從低到高。
CoreGraphics主要是覆蓋UIView的drawRect的方式來實(shí)現(xiàn)視圖繪制,而UIView的下層繪制是由CALayer提供的,UIView只是在CALayer上添加了事件捕獲來響應(yīng)用戶交互操作。那么按道理說CoreGraphics與Quartz2D的性能差距應(yīng)該不大,但事實(shí)并不是這樣。
參考以下內(nèi)容:
http://bihongbo.com/2016/01/03/memoryGhostdrawRect/
http://bihongbo.com/2016/01/11/memoryGhostMore/
可見如果drawRect使用不當(dāng),會造成大量的內(nèi)存反復(fù)申請和釋放(用來構(gòu)建位圖和刷新),而當(dāng)該視圖在不斷變化,需要與屏幕有同樣刷新率的情況下,這個(gè)性能是遠(yuǎn)遠(yuǎn)不足以達(dá)到的。這時(shí)候可以使用Quartz2D的一系列CALayer子類來實(shí)現(xiàn)。
新的問題
剛開始使用CAShapeLayer繪制圖形的時(shí)候,還是蠻驚喜的,速度很快內(nèi)存也幾乎沒有變化。但是不久后就發(fā)現(xiàn)了一個(gè)新問題。那就是在低端一些的機(jī)型上(其實(shí)也不是很低端,iPhone6p就會出現(xiàn)問題。而且即使是SE、6S、7等機(jī)型,如果重繪面積大、路徑復(fù)雜,一樣會出現(xiàn)同樣的問題,只不過程度輕一些,應(yīng)該是因?yàn)镃PU性能的提升),如果視圖內(nèi)容是不斷變化的,并且想以屏幕刷新率60fps一樣的頻率刷新視圖內(nèi)容,則會出現(xiàn)明顯的丟幀卡頓現(xiàn)象,也就是說CAShapeLayer的重繪依然是一個(gè)很昂貴的操作,即使其矢量圖的繪制方式在內(nèi)存利用率上遠(yuǎn)遠(yuǎn)超過CoreGraphics的位圖實(shí)現(xiàn),但重繪機(jī)制依然是沒有辦法越過的障礙。
參考以下內(nèi)容:
https://stackoverflow.com/questions/2720804/how-much-more-complex-is-it-to-draw-simple-curves-lines-and-circles-in-opengl-e/
https://stackoverflow.com/questions/2720642/what-is-faster-drawing-or-compositing/
I assume the reason you want to switch to OpenGL is to accelerate the animation of your drawn elements. In that question, you were attempting to animate by redrawing your UIView's contents with Quartz every frame. That will be incredibly slow, because of the way that the iPhone's drawing system works. Every frame, your view's backing layer would need to be converted to a texture and re-uploaded to the GPU, a very slow set of operations.
提問者的做法是嘗試通過Quartz2D在每一幀重繪視圖內(nèi)容的方式來實(shí)現(xiàn)動畫。但受限于iPhone的繪制系統(tǒng)工作機(jī)制,這個(gè)行為會極其的緩慢。因?yàn)槊恳粠瑑?nèi)容都需要先被轉(zhuǎn)換成紋理然后再重新上傳GPU,這是一系列耗時(shí)操作。
Compositing is faster, by far. On the iPhone, content is drawn into a CALayer (or into a UIView's backing CALayer) using Quartz drawing calls or from a bitmapped image. This layer is then rasterized and effectively cached as an OpenGL texture on the GPU.
This drawing and caching operation is very expensive, but once the layer is on the GPU, it can be moved around, scaled, rotated, etc. in a hardware-accelerated manner. All the GPU has to do while the layer is animating is to composite it with the other onscreen layers every frame. This is why Core Animation can do 50 layers animating around the screen at 60 FPS on even the oldest iPhone models.
Layers and views only redraw themselves when prompted, or if resized when their needsDisplayOnBoundsChange property is set to YES. The drawing system is set up this way because of how expensive it is to redraw and recache the layer contents. If you can, avoid redrawing your layer content regularly, but instead split it into layers or views that you can animate around individually using Core Animation. If you need to animate a changing shape, look to CAShapeLayer, which makes this much more efficient than simply redrawing every frame.
目前混合比繪制快的多。在iPhone上,CALayer上的內(nèi)容是通過Quartz繪制函數(shù)的調(diào)用或者通過一個(gè)位圖圖片繪制的。然后layer被柵格化然后作為OpenGL的紋理被高效緩存到GPU上。
繪制和緩存操作非常昂貴,但是一旦layer發(fā)送到了GPU上,就可以通過硬件加速的方式進(jìn)行評議、縮放、旋轉(zhuǎn)等變化。GPU在layer動畫的過程中唯一需要做的就是在每一幀的時(shí)候把這個(gè)layer跟其他屏幕可見layer進(jìn)行混合。這就是為什么Core Animation即使在最古老的iPhone機(jī)型上也能做到以60FPS的刷新率同時(shí)動畫屏幕上的50個(gè)以上的layers。
layers和views只有接到通知的時(shí)候才進(jìn)行重繪,另外如果needsDisplayOnBoundsChange被設(shè)置成true那么尺寸變化的時(shí)候也會重繪。之所以設(shè)計(jì)這種繪制系統(tǒng)就是因?yàn)橹乩L和緩存layer的內(nèi)容十分消耗資源。應(yīng)該盡可能的避免頻繁重繪layer的內(nèi)容,而是應(yīng)該把內(nèi)容切分到不同的layers或者views里面,然后獨(dú)立對每個(gè)layer使用Core Animatio進(jìn)行動畫。如果需要?jiǎng)赢嬕粋€(gè)變化的圖形可以使用CAShapeLayer,這個(gè)比重繪每一幀高效的多。
結(jié)論
所以,當(dāng)使用CALayer繪制產(chǎn)生性能問題的時(shí)候,就只能采用OpenGL ES了??傮w來說的原則是,使用Quartz2D和Core Animation能解決需求并沒有性能問題的情況下,優(yōu)先使用它。若性能不滿足則換OpenGL ES,沒有什么理由來使用CoreGraphics,除非繪制任務(wù)很輕松沒有太大區(qū)別(只繪制一次不刷新面積也很小的情況下)。