0

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 SKTextures 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.

damirstuhec
  • 6,069
  • 1
  • 22
  • 39

0 Answers0