The one year game develpoment duel
Aug

25

2014

Memory Usage in Sprite Kit

Now that I’m receiving real art for my game, I thought it would be a good time to learn about memory usage and limits. This was also prompted by my current level running at 55-60MB before any gameplay action or characters loaded on screen. That memory usage seemed a bit high, so I decided this was an issue that I needed to delve into. In hindsight, most of this is basic to an experienced game developer, but I found the process to be quite fun. You can just read my lessons learned below if you don’t want to follow my detective work.

  • Product -> Clean in Xcode between each test.
  • Upper limits on memory usage.
  • Compress png’s to improve download size, not memory management.
  • PVR is a commonly recommend format.
  • Change build settings to RGBA4444 if your images still look good at that setting. I’m not sure if it is my art style, but I couldn’t get them to look right (even with dithering).
  • RGB565 seems to hold up quality, but does not support transparency. This would work well for my base background.
  • Breaking apart large images into tiles, and removing tiles that would be covered by other gameplay elements, shows minor gains.
  • Create a smaller file for smaller resolution devices as fallback.
  • Group files in texture atlases according to use.
  • Read official advice.

Of course, nothing is ever easy. Here are my unanswered questions:

  • Sprite Kit seems to convert atlases at build time, so setting one image to be RGB565 won’t work. It’s all or nothing. I tried adding my one image to images.xcassets, but memory usage increases since the benefits of atlases aren’t being used. I’m trying to find a way to set different compressions for different atlases.
  • I am not having any luck getting PVR files to work with Sprite Kit. Any tips?

Test 1: Remove GUI

Test 2

I began my tests by removing things piece by piece. The goal was to get down to 1 node and observe what happened along the way.

Memory: 52MB    CPU: 17%

Test 2: Remove Layers

foothills_base_01-compressor

My game is isometric, so there are a ton of layers to give depth and allow units to walk behind objects. I went ahead and removed all of those, which leaves a basic full size background image. This had hugely successful results mainly because anywhere a layer is sitting gone top of the background, the memory usage for that part of the screen is essentially doubled.

Memory: 25.5MB    CPU: 14%

Test 3: Compress Images

Test 4

Using compressor.io, I shrunk my background image from 1.93MB to 390KB. That’s a huge difference, and I was expecting huge results. But, I didn’t get the results I was looking for. Hmmm. At a minimum, this at least helps with the over-the-air download limit.

Memory: 25.5MB    CPU: 12%    Verdict: Helps download size, but not in game memory.

Test 4: Clean Up Atlas Folder

I was using my .atlas folders to hold generic items instead of specific collections. I removed everything not necessary to the scene, and noticed good results. It appears that if you touch anything inside of an atlas, the entire contents of the atlas are accessed in some fashion.

Memory: 20.9MB    CPU: 12%    Verdict: Important.

Test 5: New Swift / Sprite Kit Project

Making progress now, so a benchmark is needed. What does a brand new, empty project run at?

Memory: 8.1MB    CPU: 9%

Test 6: Clean Up images.xcassets

Since cleaning up the texture atlases helped, I decided to see if cleaning up images.xcassets would have a similar effect. No luck.

Memory: 20.9MB    CPU: 12%    Verdict: Not related.

Test 7: Stop All Code

I made sure that all of my code was no longer executing. The scene is unpacked, and then nothing else happens. No change.

Memory: 20.9MB    CPU: 12%

Test 8: Try A Smaller Background Image

Screen Shot 2014-08-23 at 12.01.36 PM

At this point, it became clear that file size alone is not the only indicator of memory. So, I tried an image of 1/4 the size. 75% savings, which proved that the remaining ~12MB between a base project and mine was due to the texture size of the background image. Turns out textures use memory based on their resolution and color depth.

Memory: 11.8MB    CPU: 11%    Verdict:  Huge. Memory is related to texture size.

Test 9: RGBA4444

foothills_bad

Using RGBA444 instead of RGBA8888 cuts the memory used in half. The problem is that it leaves artifacts all over the artwork, and in my case they are quite noticeable. I pointed out the water and snow as examples above.

Memory: 14.9MB    CPU: 11%    Verdict: Huge. But, image quality suffers.

Test 10: Break Apart Into Tiles

Screen Shot 2014-08-24 at 10.45.14 AM

I wanted to see what would happen if the background was tiled instead of one giant image. Then, I could remove tiles that would be hidden behind a z-index overlay. I thought this would be significant, but I only saw minor gains.

Memory: Saved 1.8MB    CPU: 11%    Verdict: Minimal gains. Last resort.

7 of you did not hold your tongue!
  1. First of all, I want to congratulate both you and your brother for this awesome ideea and intiative.
    I think this blog is a great resource of usefull things to take into consideration when venturing into the indie gamedev world. I’ve read all your blog posts (and some posts written by Chris) – mainly because I’m an iOS developer and in my spare time I fiddle around with SpriteKit too. I’m from Romania (east Europe) and I don’t currently have the resources to start such a project on my own, but some day I hope to do something like this.

    I also apreciate the honesty of your thought process. I watched all your youtube videos and I can’t wait to see what’s next. It’s fascinating to see all this progress.

    There are developers who are shy or take too much pride in their work and do not want to share any WIP (being afraid of criticism). I also tend to fall in this category of people (sadly) and I am truly jelous of your openess. Maybe I sound stalky but I am curious to know more about your background.

    I think the youtube initiative is really awesome and I would encourage your brother to do the same thing (even though you have only a few views for the moment). I really have a sense of how your game takes shape compared to Chris’ game.

    Keep up the good work!

    P.S.: Sorry to be so off-topic, but I didn’t really know where to post this (I guess not many people go to the very first post).

    • Thanks for the comments — it’s great to hear when people enjoy the site / info. Right now, everything I do is for documentation. It will be neat to look back at the past year+ when this is finished. Also, I haven’t found any consolidated info that sets real expectations for indies, so hoefully we’ll have a wealth of knowledge to share at the end. I can already tell this process is / will continue to be much harder than I expected. Putting myself out there isn’t much of an issue because in my experience, most devs struggle with similar things at some point in time. It’s more about learning, and getting things done. The smartest guy in the room isn’t always the best project lead.

      If you have any specific questions, you can reach me on twitter, or through the contact from (link at top of page).

  2. Awesome document that every developer using SpriteKit should take into account.

    “I’m trying to find a way to set different compressions for different atlases.”

    Atlases are generated at build time, but you can add a post-action script in the Build configuration of the XCode scheme you are using to actually look for final atlases and compress them.

    Also for performance it’s good to remember that SpriteKit uses BatchNodes for textures within the same atlas, reducing drastically drawing time, but all children of a given node (that you want to be batch rendered) need to be using textures of that same atlas (this rules out LabelNodes, ShapeNodes or other things).

    my 2 cents 🙂

    • Great tips, thanks! I’ll see what post action scripts I can come up with. Let me know if you have a particular reference. As for batch nodes, the official Apple video covers that well. I’ll dig up the link and leave it here for future readers.

  3. Ryan,

    Try and keep your images in multiple of 2 so 256, 512, 768, 1024 etc. the “actual file size” has nothing to do with how OpenGL caches your file in memory but if you have an image that is 1025 it is going to move up to the next power resulting in more memory.

    Using the textureTool is fine but will not give you the kind of performance gains you are looking for.

    Spritekit will help with a lot of your resources, try and get as many things as you can in a 1024/1024 sheet because you only have to load it the one time and will not take another memory hit for loading images multiple times from the sheet.

    The biggest mistake I see when people make games (in any platform) is not understanding the power of 2 when it comes to resources, if you stick to this you will see your memory drop tremendously.

    I normally use a 2048/1536 background image and then put every other resource (“that i can”) inside of sprite sheets.

    Christopher

    • Good point, and thanks for the detailed post. In my tiled example, I could have shaved a few more mbs by sticking with powers of 2. In general though, your last paragraph sums up exactly what I have to do.


Speak freely