The reason for this blog post is, should I say, very naive: I was pretty angry, to be honest, to see people on a video game enthusiast forum (NeoGAF for example, and some domestic forums) repeatedly over the course of multiple years, being so ignorant about compression and game sizes. They would routinely speak of Nintendo, the size of their games — which usually are smaller barring some notable exceptions like Xenoblade Chronicles series — and then criticize third party developers for ‘not compressing’ their games.
I do assume some of them are just Nintendo fanboys, and I have seen Nintendo fanboys that are really fanatical. But that’s beside the point. The point is that it is a good opportunity to finally talk about something I am very compassionate about. Although my recent field of work was mostly sidetracked to rendering, my real work experience lies in all low level work, the grunt work, those that users usually won’t notice but will surely complain if it fails — save/load, loading time optimizations, CPU optimizations, file size optimizations, etc. So I am pretty sure I have some related experience about the topic at hand.
This is a big topic, so I don’t expect to explain everything in detail here. But this topic also can be talked about in very non-technical terms, since every computer user will use compression software and/or algorithms in one way or another.
As a matter of fact, all modern games use compression in one way or another. But still, games with different design choices and content types will determine its sizes.
The problem is that AAA games usually have a lot of assets, and I really mean it when I say a lot. Hundreds of maps or massive open world, hundreds to thousands of PC/NPCs, voice over for all main casts, an hour or two of high quality FMVs. These are all compressed, but still sometimes more aggressive compression would be needed for them, at the cost of reducing asset quality. I read from somewhere that Nintendo still uses 32kHz audio in some strategic places. Therefore compression is always a game between content creators who want to preserve as much quality of their original creation as possible, and programmers who just wanted to put the game onto the storage medium.
This has been the hot new research area for video gaming ever since S3 (RIP, one of my favorites) created the S3TC compression, which is a kind of Block Truncation Coding . Most texture compression follows similar methodologies but with each progression there would be better result at the same or even smaller size. So we have the following families of encoding methods:
DXTC (successor of S3TC): This is the standard mostly used on PC and console gaming (since the 7th generation). It has 7 variants from BC1 – BC7. Each variant has its specific use (or obsolete). Nowdays developers mostly use BC5 for normal maps and BC7 for other textures. Normal maps are unique in that
a) normal maps usually tolerate even less compression artifacts and
b) only two channels are necessary for them. The other channel can be calculated on the fly.
The compression rate is usually as follows.
BC1: 3 x 8bit -> 4bit
BC3: 4 x 8bit -> 8bit
BC5: 2 x 16bit -> 8bit
BC7: 4 x 8bit -> 8 bit (but with much better result than BC3; BC3 suffers from block artifacts too much so most modern day use would be limited.)
PVRTC: This is the format used on PowerVR GPUs and became the de facto standard on iOS devices. It is the first format to provide much more aggressive levels of compression, where you can compress your textures to 1-2bit. However at that level, image quality is greatly sacrificed. It is understandable because on mobile devices the quality loss would be less visible.
ETC: This is the format we used the most, because every Android device will support it (at least the ETC1 variant), making it the de facto standard on Android and WebGL. The compression rate is the same as BC3, but ETC1 has no full level transparency support. We usually split a 32bit texture into two (RGB and alpha) and read them separately.
ASTC: This is an advanced texture format supported on Intel and Mali GPUs. It is also worth mentioning here because it is also supported by Tegra and consequently by Nintendo Switch. Therefore Switch games can and should benefit from it, seeing that usually a 5-6bit per pixel compression rate can match or even exceed the quality of BC3 compression.
This part in video gaming is pretty straightforward as most manufacturers use industry standards like H.264 or AAC to encode their video and audio. There are far too many articles that talk about this and are probably more detailed than I am. It is not like in the olden days like on Nintendo 64 where there is no video hardware and you need to write custom algorithms to encode your videos (ala Resident Evil 2).
Usually console game data nowadays will be compressed like a custom built zip file, even upon other forms of compression. You can have your texture compressed, and then still compress it with a zlib or lz4 compression. You can still net a 3:1 compression rate for BC3 textures, for example. The result of this varies across different kinds of data.
Some hardware even supports compressed file systems, and uses hardware to decode data. This would ensure maximum speed and lean file size at the same time.
Compression algorithms in this area are usually your zlib (deflate) or your LZMA algorithm. Zlib is more widely used seeing that LZMA memory usage and decode time would increase exponentially in regard to compression level and is ill fitted for gaming. Also there are variants like lz4 in order to reach a high level of decode speed with marginally worse levels of compression. There are also commercial middleware like Oodle from RAD Game Tools.
Other general and specific tricks
You may ask why I still talk about tricks even when other compression methods are present. Well most of the time it’s just removing stuff you don’t need, but still, this is where individual developers and their experience shines. Here are some of the examples I know of:
- When porting games, sometimes it is difficult to alter content structure in a packed file, even when you want to delete some of its data, because your file will be converted to a structure in C++ code on the fly and on site, and the structure is so complex as written by your predecessors who has left no document at all. But you can still remove the data by filling its space with zero. zlib/lz4/etc. will pick it up and compress it all.
- Big file pack level redundancy check: it will automatically remove redundancy without any human input, thus save development time.
- Use Integer polar coordinates so that the data type will be better fit for compression.
Of course the conflict here is the usual ‘dev-time vs result’ and the need to compress. If there is a hard limit that you would soon reach on your platform, then you will have to spend time on it.