

Layer Trees vs. Flat Drawing: Graphics Performance Across iOS Device Generations - floriankugler
http://floriankugler.com/blog/2013/5/24/layer-trees-vs-flat-drawing-graphics-performance-across-ios-device-generations

======
cclogg
I enjoyed this read especially! I am one of the creators of 'Stratosphere
Multiplayer Defense' (on iPad), and we did it entirely in UIKit. It was very
interesting to see what we could get away with, since Apple's OS is very
animation-friendly.

We used all UIKit elements like UIViews, UIImageViews, and just core-
animationed everything around. It's amazing because you can have 50 towers on
screen, all playing their firing animation, along with 100+ monsters walking
with their animations.. and it handles well.

The reason we actually chose UIKit (over Cocos2D or OpenGL etc) was (1) my
experience in it from app development, but (2) more importantly being able to
use Apple's UI widgets. Like scrollviews, gesture-recognizers, etc... rather
than try to write our own (I'm sure we've all played iOS games with weird UIs
that are kind of like Apple's but fall short). Also, (3) was the speed of
getting something working (kind of related to (1) for me).

Downsides are that there's no blending effects like 'additive' for
lights/effects, and also it's not trivial to pause a game when it's running
timers and core-animation separately (we do have a game loop for logic, but
the monsters them-self just move on GPU-based core animations). I'd like to
STRESS that using UIKit for most games is probably not a good idea.

Anyway, thought it might interest some people to hear this :)

Oh, and another benefit is no load screens.

------
andrewgleave
As an aside: If Apple do transition to a flatter UI (yes, I know it's all
rumor and speculation, but humor me), I'd expect to see a reasonable jump in
UI responsiveness of older iOS devices.

Not having to load many Retina-sized background images for UI elements would
significantly decrease memory pressure on the OS as well as increase
responsiveness.

NB. Assuming older devices will be able to upgrade and that texture-heavy UI
elements would be phased out.

Edit: Typo

~~~
mtgx
Only iPhone 4S and iPhone 5 will be upgraded, right? Those have pretty good
GPU's. It was iPhone 4 that had a pretty terrible one combined with the retina
display at the time.

The biggest impact would be seen on iPads, because iPad 3 actually took a step
backwards in terms in performance behind iPad 2, when it switched to retina,
and iPad 4 barely matches iPad 2 in performance (if we're comparing native
iPad 2 apps vs native "retina" iPad 4 apps).

It could also be the reason why the next iPad is rumored to not have a huge
battery anymore, like iPad 3 and iPad 4, and be much slimmer (the last 2 iPads
were thicker than iPad 2).

~~~
perardi
I think the iPhone 4 will get the update, as it's still being sold. (If I had
to guess, it's probably selling pretty darn well in the U.S. as it's Verizon's
first "free" iPhone.)

~~~
masklinn
The 4 will definitely get the update. The 3GS may or may not, though it's
already way outlived the support life of both the iPhone and the 3G.

------
rjohnk
I've delegated my iPhone 3GS to extra iPod Touch status. I must say it still
holds it's own with speed. Some entry level android phones still can't match
it's UI responsiveness. It's a great phone/device.

~~~
prawks
As someone who still continues to love his 3GS, I have to agree _for the most
part_. Some apps have me considering whether the developers know they run/load
that slowly, or whether it's because of my device.

For the most part it is very responsive, but it does get bogged down here and
there. For example if I get a text from someone while I'm typing a text to a
different person and click the notification ribbon, the phone will hang for a
second or two before switching to the other person's text. Is that common on
all iOS devices? I'd always imagined it should be much faster given that the
Message app is already loaded and active.

~~~
ryanpetrich
If it's been opened recently, Messages is likely running in the background,
but isn't always running like Mail and Phone are.

------
Zev
It wasn't mentioned in the article, but, it is very important to note:
CAGradientLayer is not GPU-backed.

In other words, the comparison is between the CPU redrawing a gradient 60x a
second, and, an image being moved around on screen. Not entirely surprising
that the one that does less rendering is going to be faster.

~~~
ryanpetrich
Yes, one should always set shouldRasterize on CAGradientLayers (or at least
those that aren't in a parent that has shouldRasterize)

~~~
Zev
Saying "Always rasterize" isn't necessarily the solution, either. Its actually
pretty bad advice to follow all the time ;)

I suspect (but can't confirm without the source code to the demo) that
shouldRasterize would actually hurt performance in one of the two cases in the
article. Specifically, I imagine that it would make the score for adding and
removing views from screen even worse than it already is.

Yes, it renders into a bitmap, and reduces CPU usage, once you've paid the
upfront rendering cost. But, it doesn't rasterize opacity, and, will use up a
_ton_ of memory (because it doesn't result in stretchable images).

------
monkey_slap
This is a wonderful article. I've been swapping different methods for
performance here and there, but not really settled on a concrete strategy. It
looks like drawing definitely has its edge with animations, but for static
content CALayers are king. On my weekend project
(<https://github.com/rnystrom/RNRippleTableView>) I found CALayers, especially
CAShapeLayers, to be incredibly efficient even during animation. Overriding
-drawRect can be fun and rewarding, but its starting to seem like its less and
less useful.

I'd love to hear others thoughts on how they use -drawRect nowadays.

~~~
nglevin
I use -drawRect any time I need a UIView's CGContext. For everything else, I
use -layoutSubviews.

When you do anything in Core Animation in -drawRect, that throws all drawing
on CPU, instead of dividing the work between the CPU and GPU. Which, in one
project where I was doing Core Animation alongside OpenGL, that had some very
bad consequences.

Core Animation is pretty interesting. Internally, it does some drawing with
Core Graphics, but that's only if you use certain features (i.e., use of
CAShapeLayers, anything involving shadows)... and even then, CG is pretty
darned fast.

I've found CAShapeLayers can be a bit of a resource hog, but since those
resources tend to be thrown behind 60 fps animation, that might not be such a
problem.

The CAShapeLayer issues only came up when I tried scrolling within a scroll
view that contained a CAShapeLayer with a CGPath that had 20 points defining
lines. Setting the CAShapeLayer to -shouldRasterize:YES after the animation
completed "fixed" the issue, as long as you didn't need to scroll while the
CAShapeLayer was animating.

(Don't even bother animating a CAShapeLayer while -shouldRasterize: is set to
YES. That just causes even more fun problems with dropped frames.)

~~~
monkey_slap
Ya setting shouldRasterize = YES would be bassackwards for animating
CAShapeLayer. I actually found animating the path attribute incredibly easy
and performant with CAKeyframeAnimations.

Another note with -drawRect and animations, I messed around with CADisplayLink
and -drawRect to make animations. Beware of doing this, it was a performance
hog. It was a simple animation in a large rect, and by the time I had
optimized the drawing area, I could have finished the entire project.

I wonder, would CAShapeLayer become more of a resource hog with more points in
the path (or more complex arcs), or does it happen with the overall size of
the path?

------
eliperkins
Great write-up. I've started recently to compose a lot of table view cells
just using subviews, not thinking back to the days where I had to drawRect the
subviews. Performance has seemed to catch up, as this article notes. Thanks,
Florian!

------
ocrickard
Awesome article. Very evenly balanced in its conclusions, though it avoided
the subject of asynchronous rendering/layout. I would add that it is possible
to achieve the 1/60th sec. setup time for more complex drawing
(NSAttributedStrings, composited views) through the use of background
rendering/compositing into CALayers that are delivered to the cells only when
complete, though it gets complex very fast, and as noted the performance in
newer devices is rapidly getting to the point that I don't have to think so
much about this anymore. Now if only we could get everyone to drop the iPhone
4... (just kidding).

~~~
monkey_slap
This man speaks the truth. Though I think the advice of "stick to the highest
level API for your needs" holds true here as well.

(Hi oc, its Ryan!)

~~~
ocrickard
Hahaha, Hey Ryan. Totally agree. If you don't need to do this, DON'T.

------
zmitri
I've suspected this was the case but never quantified it, so very cool.

A couple of things I've found work very well are:

1) not creating deep view hierarchies in your table cells - ie. keep those
very flat.'

2) Make your images as small as possible in terms of file size. Do background
processing to make them smaller and lower the quality if you don't need it.
This is by far the easiest way to increase speed if you aren't doing it
already.

~~~
Zev
Re: 2:

While its great to have a small image that doesn't require much I/O to read,
"small" shouldn't only refer to file size.

Instead, a "small" image should be one that has a small resolution, and is
stretchable. This lets the GPU tile the portions of the image that are being
stretched and reuse GPU memory, instead of having to waste space in ram to
hold duplicate pixel data.

(And when something's as small as it can be, it doesn't minify very well
anymore -- not much left to strip from the PNG.)

~~~
zmitri
I was referring to actual images used or featured in the table cell, not tiled
backgrounds via UIColor colorWithPatternImage like you mentioning. But yes, it
is important to do that as well.

------
babesh
Should you not be drawing the bitmap in a background thread so that it isn't
bounded by the UI thread?

------
dannowatts
i love the breakdown of the benchmarks plotted to graphs. i'm a visual person,
so seeing those really helps solidify the information for me.

thanks for posting, florian!

------
seivan
Good stuff! Thanks for posting.

