UPDATE
Well, well, well, long time no see. 2020 has been a busy and creative year for me; I wrote two short films,
started a YouTube series, released assets onto the store, and found my workload at my day-job triple the moment we went into lockdown. Notice a pattern? None of that had anything to do with Ignite. I guess I was just burnt out (no pun intended) and couldn't face it.
One reason I lost interest in Ignite over the summer was my frustration with an early three-level build that refused to work as intended. I mean, if I couldn't get three simple levels to run correctly, what chance did I stand of getting six or seven levels done? I was also at a crossroads regarding whether I should port the game to Game Guru Max, in hindsight I'm glad I didn't.
Instead I spent a lot of time thinking about texture optimisation. It could be that one of the reasons Ignite wasn't running correctly was because I had made little effort to optimise it. I figured I'd have to eventually, I just didn't expect to start hitting memory issues after only three short levels. Still, whether or not lack of optimisation is the problem, it's still a good habit to get into.
What follows is a long and rambling post about my journey into the world of texture optimisation.
Texture optimisation
Optimisation is all about saving resources to improve performance. When it comes to textures, there are two basic attributes you can tweak to save memory; resolution (size), and compression (format). Let's talk resolution first.
Traditionally, assets bought made by third parties are never optimised by their creators - I'm guilty of this too! Why? Because we don't know what the user will want to use that asset for. It makes sense to provide textures in the highest quality and highest resolution you can to give the user more freedom. But most people won't notice the difference between a 4096 x 4096 (4K) texture and a 2048 x 2048 (2K) texture in-game, not unless they are either getting really close to an asset, or it's really big, or they are running a big high-resolution monitor. To test this out, let's look at this switch I made for Ignite ages ago. I originally mastered it with a 2K texture.
As you can see, the difference between 2K and 1K is almost negligible, and even at 512 x 512, the texture still holds up at a distance. Only at 256 x 256 does noticeable pixilation occur, but at this point your texture is 1/64th the size of the 2K original.
Every time you half the length of a side of your texture (e.g. 4096/2 = 2048, or 2048/2 = 1024), you divide its area by 4. This means a 1024 texture is a quarter the size and therefore takes up a quarter of the memory of a 2048 texture. A 512 x 512 texture takes up a quarter of that, so 1/16th the memory of a 2048 texture, often it's even less than that. Here's how those four examples seen above measure up (we're still working in PNG for now):
2048 x 2048 = 6.24MB
1024 x 1024 = 1.46MB
512 x 512 = 343Kb
256 x 256 = 79.7Kb
Now obviously I didn't just want to go around converting every texture to 256 x 256, it would look awful! But I spent a week going through my third party and self-made assets and, one-by-one, reducing the texture resolution down until it became unacceptable. Again, it's all about the amount of screen space the asset occupies. Here's a big wooden table, it takes up a lot of the screen because the player can get really close to it.
In the end, I still went with a 2K texture here because the player can see it up close so well. But that's still a quarter of the size it used to be. I always tried to go one step further than I thought I could, just to see if the texture quality fell apart.
Equally, there were other entities that are tiny in-game, yet had enormous textures. I'd made this combination lock to have a 2K texture because I felt it was important the player can read the numbers. But it turns out, you can still read them perfectly well at 256 x 256, because the entity is so small in-game, and there's enough contrast in the texture itself.
Of course, many assets for Game Guru now take advantage of PBR; they'll have not one texture map but usually three or four; _color, _normal, _gloss, _ao, _metalness, _illumination, or any combination of those. The saving in memory only multiplies with additional textures. 5 x 1024 textures make take up 10MB of memory, whereas 5 x 512 textures would take around 2.5MB (usually even less because _ao, _metalness, and _gloss are greyscale). Game Guru Classic is a little behind the times when it comes to texture stacking; in other engines, the _ao, _gloss, and _metalness maps, which are all greyscale, can be combined into a single texture, as in Max's use of '_surface' textures. Classic can't do that, but that does give you the freedom to resize the individual maps as you choose, potentially further saving memory.
The other tool at your disposal is format. PNG textures are popular because they offer less compression than JPG, and allow for transparency, AKA alpha channels. However, every PNG texture you use must be converted to DDS by your graphics card in order to display. This conversion happens behind the scenes in Game Guru and puts a little extra load on your graphics card. Some people will tell you Game Guru is limited by its memory cap, not its graphics, therefore the smaller filesize of a PNG is better than a bigger filesize of a DDS. This is rubbish, DDS comes in a multitude of flavours and can be compressed in a number of ways to make it both better for your GPU, and your RAM.
I don't know if this is just Game Guru, but another unintended up-shot of DDS textures over PNG is that they appear crisper in-game. Maybe this has something to do with your GPU not needing to convert them.
A good program for converting textures is
Paint.net. But it comes with a huge list of compression types to choose from, so how do you know which is best?
Some people swear by BC1 compression; it's been around the longest and is the most supported. However, I find the compression to be too harsh, and it also only features a 1-bit alpha channel, meaning your alpha is either on or off, opaque or transparent, there's no ability to do semi-transparent materials. Yes you save filesize, but at what cost?! The compression on BC1 is also quite noticeable. In order to save filesize, BC1 groups similar colours together into colour blocks, which often results in a subtle but noticeable shift in colours, and strange mottled patterns throughout the texture. These artefacts that are particularly noticeable in low contrast, low saturation areas like on metal but you'll see these purple and green blocks pop up on lots of DDS textures, including in stock Game Guru assets.
To save time, here's the compression formats I use now, having done lots of experimentation:
_color =
BC7 (little compression, 8-bit alpha channel allows for semi-transparent materials)
_normal =
BC3 (a little compression, but less than BC1. You could go BC1, but the green and purple colour-blocking you notice in the above example can mess with the subtleties of your normal map)
_ao =
BC4 (a greyscale format, discards all colour data, saving you about 50% filesize)
_metalness =
BC4 (see above)
_gloss =
BC4 (see above)
_illumination/_emissive =
BC7 if low compression is a priority,
BC1 if solid colours of illumination.
All of this combines to save some serious memory. I had a set of 4K PNG textures weighing in a 27.1MB. After resizing them to 2K, and saving them as DDS with the above compression, I got that down to 12MB, 44% of what it was originally. Every megabyte saved is a little less strain on the already inefficient memory management of Game Guru.
I've enjoyed doing these experiments, laborious though it has been. Now that winter approaches, I'm gradually beginning to pick up Ignite again, but from now on I'll be going through every asset to ensure it both looks great, and is optimised to ensure smooth, and hopefully stable, gameplay.
My fingers hurt now
AE