iOS的視圖繪制通常有三種方式,CoreGraphics、Quartz2D和OpenGL ES,性能從低到高。
CoreGraphics主要是覆蓋UIView的drawRect的方式來實現視圖繪制,而UIView的下層繪制是由CALayer提供的,UIView只是在CALayer上添加了事件捕獲來響應用戶交互操作。那么按道理說CoreGraphics與Quartz2D的性能差距應該不大,但事實并不是這樣。
參考以下內容:
http://bihongbo.com/2016/01/03/memoryGhostdrawRect/
http://bihongbo.com/2016/01/11/memoryGhostMore/
可見如果drawRect使用不當,會造成大量的內存反復申請和釋放(用來構建位圖和刷新),而當該視圖在不斷變化,需要與屏幕有同樣刷新率的情況下,這個性能是遠遠不足以達到的。這時候可以使用Quartz2D的一系列CALayer子類來實現。
新的問題
剛開始使用CAShapeLayer繪制圖形的時候,還是蠻驚喜的,速度很快內存也幾乎沒有變化。但是不久后就發現了一個新問題。那就是在低端一些的機型上(其實也不是很低端,iPhone6p就會出現問題。而且即使是SE、6S、7等機型,如果重繪面積大、路徑復雜,一樣會出現同樣的問題,只不過程度輕一些,應該是因為CPU性能的提升),如果視圖內容是不斷變化的,并且想以屏幕刷新率60fps一樣的頻率刷新視圖內容,則會出現明顯的丟幀卡頓現象,也就是說CAShapeLayer的重繪依然是一個很昂貴的操作,即使其矢量圖的繪制方式在內存利用率上遠遠超過CoreGraphics的位圖實現,但重繪機制依然是沒有辦法越過的障礙。
參考以下內容:
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在每一幀重繪視圖內容的方式來實現動畫。但受限于iPhone的繪制系統工作機制,這個行為會極其的緩慢。因為每一幀內容都需要先被轉換成紋理然后再重新上傳GPU,這是一系列耗時操作。
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上的內容是通過Quartz繪制函數的調用或者通過一個位圖圖片繪制的。然后layer被柵格化然后作為OpenGL的紋理被高效緩存到GPU上。
繪制和緩存操作非常昂貴,但是一旦layer發送到了GPU上,就可以通過硬件加速的方式進行評議、縮放、旋轉等變化。GPU在layer動畫的過程中唯一需要做的就是在每一幀的時候把這個layer跟其他屏幕可見layer進行混合。這就是為什么Core Animation即使在最古老的iPhone機型上也能做到以60FPS的刷新率同時動畫屏幕上的50個以上的layers。
layers和views只有接到通知的時候才進行重繪,另外如果needsDisplayOnBoundsChange被設置成true那么尺寸變化的時候也會重繪。之所以設計這種繪制系統就是因為重繪和緩存layer的內容十分消耗資源。應該盡可能的避免頻繁重繪layer的內容,而是應該把內容切分到不同的layers或者views里面,然后獨立對每個layer使用Core Animatio進行動畫。如果需要動畫一個變化的圖形可以使用CAShapeLayer,這個比重繪每一幀高效的多。
結論
所以,當使用CALayer繪制產生性能問題的時候,就只能采用OpenGL ES了。總體來說的原則是,使用Quartz2D和Core Animation能解決需求并沒有性能問題的情況下,優先使用它。若性能不滿足則換OpenGL ES,沒有什么理由來使用CoreGraphics,除非繪制任務很輕松沒有太大區別(只繪制一次不刷新面積也很小的情況下)。