I have a very strange problem when purging a cached instance of SKTextureAtlas
.
In short, before the game starts I preload the SKTextureAtlas
with textureAtlas.preload(completionHandler:)
and in the completion handler I store a reference to the atlas in a private property, in order to keep it in the memory for the later use.
After a while, this texture atlas is not needed anymore and I want to get rid of it, in order to maintain a clean and healthy memory.
Problem
The problem I'm having is that when I purge the texture atlas (by setting the property which was holding a reference to the atlas to nil
), the performance drops drastically for a few seconds. By drastically, I mean from a constant 60fps to 40fps.
Note that there is definitely no other reference to that atlas left at this point.
Interesting: part 1
Only happens on iOS 10. Runs perfectly on iOS 9, even on much older device.
Interesting: part 2
Happens only if the atlas is defined in form of .spriteatlas
inside the .xcassets
. but not if the atlas is an .atlas
folder outside of the .xcassets
.
I know I could just go ahead and use .atlas
instead of .spriteatlas
, but .spriteatlas
support app-thinning out of the box, which .atlas
doesn't.
Interesting: part 3
Purging works fine if the atlas has a small number of small textures. This indicates that there might be an internal issue related the total size of the atlas. But why?
Motivation
The motivation behind finding a solution/workaround to this problem is batching of bind/draw calls. Child nodes that use the same texture are all rendered in a single draw pass, which of course increases performance a lot.
With this problem, I'm basically forced to create multiple atlases which will in turn result in having multiple draw calls and therefore affect the overall performance.
Any help will be greatly appreciated.
Update
I've tried implementing a solution suggested by @Knight0fDragon, which suggest caching SKTexture
s from SKTextureAtlas
instead of SKTextureAtlas
itself, but that sadly didn't work in my case.
What I forgot to mention in the original question is that this problem happens most of the times, but not always. I get it to work perfectly fine every ~5th try.
Solution
Since I wasn't able to re-open my question after being wrongly marked as duplicate, I'll just post the solution I found here.
The problem was an animation running in the background. This animation was set up like this
let rotation = CABasicAnimation(keyPath: "transform.rotation")
rotation.fromValue = 0
rotation.toValue = 2 * M_PI
rotation.duration = 110
rotation.repeatCount = Float.infinity
and it was running on the layer of UIImageView
. This image view was located in the view controller that eventually presented another view controller which contains a game scene.
The transition between those two view controllers is custom, and since I forgot to add calls to beginAppearanceTransition
and endAppearanceTransition
in the custom transition animator, the view controller lifecycle methods (viewWillAppear:
, viewWillDisappear
) were not called, which is exactly where I had the logic for starting/stopping all animations. Therefore the animation kept on running in the background.
That said, even if the animation was running in the background, it should not interfere with something completely unrelated like clearing the cached atlas.
Note: the image in the image view with problematic animation was not defined in the problematic texture atlas.