简体   繁体   中英

Why SKTexture.preload raises memory issue on a real device?

I'm using SpriteKit for a 2D game and have an array of images for a simple animation.

  1. First, I'm creating an array with SKTextures to keep references to those images.
var hyperLeapArray: [SKTexture] = []
for i in 0 ..< 90 {
  hyperLeapArray.append(SKTexture(imageNamed: "hyper_leap_\(i)"))
}
  1. Then I create an animation action.
let animation = SKA.animate(with: hyperLeapArray, timePerFrame: 0.03)
  1. Finally, I run this action on my SKScene's node to play it on the screen.
self.animationHost.run(SKAction.repeatForever(animation)) // Plays the animaion on a loop.

The first time this is executed - FPS drops to ~20 and then smoothly stabilizes up to 60. To avoid this, I've decided to use SKTexture.preload method before playing the animation.

 Task.init {
    await SKTexture.preload(hyperLeapArray)
 }

And, on a simulator it worked fine, FPS are still dropping, but for acceptable > ~35 rate. But this solution caused another issue with memory on a real device. After investigating, I realized that calling SKTexture.preload - fills the memory up to ~1.5GB - which is enormous and keeps that memory in use before the animation is cashed. Original 90 images weight only 12Mb, I have no idea how this is growing to the enormous amount of space.

Space usage


What I've tried:

  1. To preload textures one by one, eg
   func preload(index: Int, array: [SKTexture]) {
     if (index < array.count) {
       array[index].preload {
         print("Preloaded \(index)")
         sleep(1)
         preload(index: index + 1, array: array)
       }
     } else {
       print("Done")
     }
   }
  1. To initialize UIImages first and then build SKTexutres(image: UIImage)
  2. Run animations in background without preloading for SpriteKit to cache that before actual use
  3. Used SKTextureAtlas - but it made it even worse, up to 4Gb of memory usage

But nothing helped so far.

Any ideas on

  1. Why does this happen?
  2. How to keep FPS on a high level at any time?
  3. Why SKTexture.preload reservice so much space in memory?

Here is full code snippet if you'd like to reproduce it locally:

final class TempScene: SKScene {
  private var animationHost: SKSpriteNode!
  private var hyperLeapAnimation: Animation!

  convenience init(test: Bool) {
    self.init(size: FULL_SCREEN_SIZE)
    let center = CGPoint(x: frame.width / 2, y: frame.height / 2)

    animationHost = SKSpriteNode(color: .clear, size: FULL_SCREEN_SIZE)
    animationHost.position = center
    animationHost.zPosition = 110
    addChild(animationHost)

    var hyperLeapTextures: [SKTexture] = []
    for i in 0 ..< 90 {
      // You can use any texture instead.
      hyperLeapTextures.append(SKTexture(imageNamed: "hyper_leap_\(i)"))
    }
    Task.init {
      await SKTexture.preload(hyperLeapTextures)
    }
    let animation = SKAction.animate(with: hyperLeapTextures, timePerFrame: 0.03)
    animationHost.run(SKAction.sequence([SKAction.wait(forDuration: 6),
                                         SKAction.repeatForever(animation)]))

  }
}

After investigating, I've realized that the reason was that no matter how much a picture file size is, the main thing that matters is picture resolution and in my case, a single image memory size was 32x1536×2048 / 8 / 1024 / 1024 = 12Mb . (Where 1536×2048 is the actual picture size). And I had at least 100 frames.

Unfortunately, it seems like there is no way to avoid memory overflow error in such cases :/

Frame animation should be used for small images, but for big ones the only acceptable way is to replace frame animation with a video file usingSKVideoNode .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM