Monday, September 22, 2014

Improving performance when dealing with layer properties or Core Animation

Prior or subsequent to this post I recommend you guys to take a look in to one of my another post related to enhancing performance and memory management of an iOS app here, http://just-works.blogspot.in/2014/05/how-to-manage-memory-and-enhance.html
Before going in to depth there is a quick snapshot below for those who don’t want to dig in but just want a quick instant solution w.r.t improving performance when dealing with layer properties of UIView,
1)   Use Opaque Layers Whenever Possible. i.e; set opaque property to YES, whenever it is possible.
2)   Use Simpler Paths for CAShapeLayer Objects i.e; While drawing shape layers it is recommended to break up complex shapes into simpler shapes. 
3)   Specify a Shadow Path when adding a shadow to your view’s layer. i.e; make use of layers shadowPath property when you are dealing with layers shadow effect. 
4)   Use Asynchronous Layer rendering whenever required (if and only if really required). i.e; set drawsAsynchronously boolean property to YES whenever required, so as to process your drawing commands asynchronously in a background thread. 
5)   Use  shouldRasterize boolean property, If you have a complex view (i.e. relatively expensive to re-render) that you are animating, but for which the animated view is not itself changing, rasterizing the layer can improve the performance by not re-rendering the layer all the time.
shouldRasterize will consume memory for saving a rasterized image in memory, so use this whenever it is really required.
Bit more description/explanation on above points,
1) Use Opaque Layers Whenever Possible. i.e; set opaque property to YES, whenever it is possible.
- A Boolean value indicating whether the layer contains completely opaque content.
- The default value of this property is NO.
- Declare Views as Opaque Whenever Possible
Setting the opaque property of your layer to YES lets Core Animation know that it does not need to maintain an alpha channel for the layer. Not having an alpha channel means that the compositor does not need to blend the contents of your layer with its background content, which saves time during rendering.
Setting the value of this property to YES for a custom view tells UIKit that it does not need to render any content behind your view. Less rendering can lead to increased performance for your drawing code and is generally encouraged. Of course, if you set the opaque property to YES, your view must fills its bounds rectangle completely with fully opaque content.
The iPhone GPU is a tile-based renderer. If an overlaying layer is completely opaque over an entire tile, the GPU can ignore setting up and processing any graphics commands related to the layer underneath for that particular tile, in addition to not having to do compositing of the pixels in that tile.
2) One way to minimize drawing time for shape layers is to break up complex shapes into simpler shapes. Using simpler paths and layering multiple CAShapeLayer objects on top of one another in the compositor can be much faster than drawing one large complex path. That is because the drawing operations happen on the CPU whereas compositing takes place on the GPU. 
3) shadowPath - The shape of the layer’s shadow.
when ever we are using CALayer animation or for shadow effects, we usually see a jerk in rendering those views if the transition view consists of huge graphics or utilizing more layer properties of view, especially in lower versions of iOS devices.
So as to avoid this jerk on rendering and optimizing performance we have to set shadowPath property when we are setting layers shadow effects.
- The default value of shadowPath property is nil, which causes the layer to use a standard shadow shape. If you specify a value for this property, the layer creates its shadow using the specified path instead of the layer’s composited alpha channel.
- Specifying an explicit path usually improves rendering performance. 
- The value of this property is retained using the Core Foundation retain/release semantics. 
- When you specify a path object for this property, Core Animation uses that shape to draw and cache the shadow effect.
- For layers whose shape never changes or rarely changes, this greatly improves performance by reducing the amount of rendering done by Core Animation. 
Below code snippet is an example of setting shadow path for a view's layer which is having rounded corners,
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(10.0, 10.0)];
view.layer.shadowPath = path.CGPath;
Note: Your code will vary depending on the actual shape of your view. UIBezierPath has many convenience methods, you can get more info on this over here, https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIBezierPath_class/
- You will need to update the layer’s shadowPath each time the bounds of your view change. And if you’re animating a change to bounds, then you will also need to animate the change to the layer’s shadowPath to match. 
- This property will drastically improves view rendering performance.
4) drawsAsynchronously - Use Asynchronous Layer rendering whenever required.
- drawsAsynchronously is a boolean indicating whether drawing commands are deferred and processed asynchronously in a background thread. 
- The default value for this property is NO. 
- Any drawing that you do in your delegate’s drawLayer:inContext: method or your view’s drawRect: method normally occurs synchronously on your app’s main thread. 
- In some situations, though, drawing your content synchronously might not offer the best performance. If you notice that your animations are not performing well, you might try enabling the drawsAsynchronously property on your layer to move those operations to a background thread. 
- Make sure your drawing code is thread safe whenever you are using drawsAsynchronously property. 
Note: Always, you should always measure the performance of drawing asynchronously before putting it into your code. 
5) shouldRasterize a boolean that indicates whether the layer is rendered as a bitmap before compositing 
- The default value of this property is NO. 
- When animating a complex set of layers that, themselves, are not changing, you can set shouldRasterize to YES, do the animation, and then turn off shouldRasterize. 
- If you have a complex view i.e; views expensive to re-render, that you are animating, but for which the animated view is not itself changing, rasterizing the layer can improve the performance by not re-rendering the layer all the time. 
- When the value of this property is YES, the layer is rendered as a bitmap in its local coordinate space and then composited to the destination with any other content. 
- Shadow effects and any filters in the filters property are rasterized and included in the bitmap. 
Note: shouldRasterize will consume memory for saving a rasterized image in memory, so use this whenever it is really required. 
Hope this post is helpful, any comments or suggestions are acceptable.