I have a single container view inside a UIScrollView
, and that container view has several CAShapeLayer
based subviews (about 200). Each CAShapeLayer
contains a very simple CGPath
(a filled polygon of about 10 points). You can see it as a sort of map.
The container view itself is big (about 1000x2500 points) but I have implemented zooming using the transform
property (I'm not using UIScrollView
implementation), so at small scales it's entirely visible.
At high scales (only a part of the container view is visible on screen) this works great and scrolls smoothly, even on old hardware.
However, at small scales (when most of the container view is visible), this results in very bad scrolling performance on old hardware (40 fps on an iPhone 4S). And if I go over 200 subviews (which is something I would like to do), this is much worse (down to 15fps) even on newer hardware.
I have done some profiling but I can't find the bottleneck. During scrolling I make the following measurements (on average) :
Activity monitor instrument :
CPU : 8%
GPU driver instrument :
Device utilization : 40%
Renderer utilization : 35%
Tiler utilisation : 8%
FPS : 40
Here is what I have tried :
Setting
shouldRasterize
with the properrasterizationScale
on allCAShapeLayer
. It makes things worse.Setting
shouldRasterize
with the properrasterizationScale
on the layer of the container view. This improves scrolling for very small scales (when the entire container is visible inside the scrollView), but makes it much worse for bigger scales. Activating rasterization only for for small scales leaves a gap (between 0.5 an 2.0 approximately) where both options result in lost frames.Using layers only instead of views. Doesn't improve anything.
Using
CATiledLayer
for the container view layer. Doesn't improve anything.
Any ideas ? If there was a limitation of the hardware shouldn't I see a 100% somewhere ? What else should I be profiling ?
At this point the main thing that I'm asking is help on how to profile my app and understand what is causing lost frames. Then maybe, depending on what is happening and what is doable, I will try to improve things. I'm not asking for every single possible tweak in the book that could maybe improve things a little if i'm lucky.
EDIT
Just found out that although my app is running at 8% CPU, backboardd
runs at 100% CPU during scrolling. So the bottleneck is in the Core Animation render server !
Now I just have to figure out why. Maybe there are just too many layers... Anyone knows what is the maximum number of layers the render server can process every 16ms ? I can't find any official documentation about that.
EDIT 2
So after some more profiling it looks like it has nothing to do with the number of layers. If I combine all the shapes in a small number of layers (around 10) by combining some of the paths (and as a result using more complex paths), the result is approximately the same. backboardd
still runs at 100% CPU during scrolling.
So I guess that it's the paths processing/drawing/something that takes time, regardless of wether or not they are split in smaller/simpler paths. Maybe it's simply bound to the number of points. I'm starting to think that there is nothing that I can do...