Wednesday, March 16, 2016

CAShapeLayer poor scrolling performance. Why?

Leave a Comment

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 proper rasterizationScale on all CAShapeLayer. It makes things worse.

  • Setting shouldRasterize with the proper rasterizationScale 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.

1 Answers

Answers 1

I suspect that it is caused by your zooming. Every pixel you scroll it will call your zooming calculation. To my experience, when I have a large picture zoom in a small scale of screen, it would be extremely slow when scrolling because the view has to redraw it every single pixel.

And if you don't need any animation attached to your CAShapeLayer, use CGLayer instead, it is faster when you are using scale function.

From Apple

Color Blended Layers. Shows blended view layers. Multiple view layers that are drawn on top of each other with blending enabled are highlighted in red. Reducing the amount of red in your app when this option is selected can dramatically improve your app’s performance. Blended view layers often cause slow table scrolling.

Bottlenecks for an OpenGL app are usually GPU or CPU bottlenecks. GPU bottlenecks occur when the GPU forces the CPU to wait for information because the GPU has too much information to process. CPU bottlenecks occur when the GPU has to wait for information from the CPU before the GPU can process it. CPU bottlenecks can often be fixed by changing the underlying logic of your app to create a better overall flow of information to the GPU. Common bottlenecks include: Geometry limited. If Tiler Utilization is high, examine your vertex shader processes. Pixel limited. If Rendered Utilization is high, examine your fragment shader processes. CPU limited. If Tiler Utilization and Rendered Utilization are both low, the performance bottleneck may not be in your OpenGL code. Examine your code’s overall logic.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment