Thursday, October 31, 2019

graphics - Which image format is more memory-efficient: PNG, JPEG, or GIF?


Which image format is more efficient to save memory? PNG, JPEG, or GIF?



Answer



"Memory" and "efficiency" are commonly misused terms, so I'll give you an answer for four different elements that may affect the performance of your game.



I will be oversimplifying way too many things to keep it short and concise, but there are tons of inaccuracies in this text below, so take it with a pinch of salt. However, the main concepts should be understandable.


Storage


This is the size your images consume on your software distribution. The more space your resources consume, the longer the download (as in from your website) will be. If you're distributing on physical media, such as CDs or DVDs, you're probably going to have to do some serious optimizations in this front.


In general, JPEG compresses the best for photographs and images with no sharp borders. However, your images will have degraded quality because JPEG uses lossy compression (you can fine tune the compression level/degradation when exporting images as JPEG. Refer to your imaging software's documentation for more information on this).


However, as good as JPEG may be, it doesn't support transparency. This is crucial if you want to have images that show through others, or if you want images with irregular shapes. GIF is a good choice, but it has been largely superseded by PNG (there's only a few things GIF supports that PNG doesn't, but they're largely irrelevant in game programming).


PNG supports transparency (and semitransparency), compresses data without degradation in quality (i.e. it uses lossless compression), and compresses fairly well, but not as much as JPG.


The problem arises when you need good compression, as well as transparency. If you don't mind slightly degraded images, you can use PNG quantization programs such as pngquant, which you can test online at TinyPNG. Keep in mind that the image degradation performed by quantization on its own is different than that of JPEG (which includes quantization as well as other aggressive techniques), so be sure to try both, with a wide variety of settings.


If you want to aggressively minimize your distribution size, you could manually process every image like this:


if the image has transparency then
try pngquant on the image

if the results are not satisfactory then
revert to the non-quantized image
end
store as PNG
else
try storing it as JPG with different quality settings
if no single setting yields an image of an acceptable quality then
try pngquant on the image
if the results are not satisfactory then
revert to the non-quantized image

end
store as PNG
else
store as JPG
end
end

Tip: it is okay to store some images in one format, and others in another format.


There are other specialized formats such as DXT, ETC and PVRTC. They support compression, and can also be loaded compressed into memory, but they are only supported by specific GPUs, and most of these GPUs only support one of them, so unless you know the exact hardware specifications of your target hardware (a notable case is the iPhone/iPad, which supports PVRTC textures), you should avoid these formats.


Program Memory



I included it here, because this is what's commonly known by "memory". However, if your game uses graphics acceleration (and if you're making a game after 1998, you most likely are), then the only thing that will consume memory are your texture descriptors (just a few bytes per image), which is only effected by the amount of images, and not their size or format (this has a few caveats, but are mostly irrelevant).


If your platform does not have dedicated video memory, is not hardware accelerated, or other uncommon cases, then the next section regarding VRAM will happen completely or partially in RAM, but the main principles will be the same.


Video Memory


This is where your images will be stored once your program is running. In general, the format in which you stored them will make no difference here, as all images are decompressed before loading them into video memory.


Now, the VRAM consumed by your images will roughly be width * height * bitdepth for each image loaded in VRAM. There are a couple of things to notice here:




  1. The width and height in which your images are stored in VRAM will not necessarily match the ones of your original image. Some GPUs can only handle textures with sizes in powers of 2, so your 320x240 image may actually be stored in a 512x256 space in VRAM, with the unused memory effectively wasted. Some times you're not even allowed to load textures with sizes that are not powers of 2 (like in GLES 1.1).


    So if you want to minimize VRAM usage, you may want to considering atlasing your images, and sizing them with powers of 2, which will also have the advantage of fewer render state changes when rendering. More on this later.





  2. The bitdepth is very important. Usually textures are loaded into VRAM in 32-bit ARGB or 32-bit XRGB, but if your hardware can support 16-bit depths, and you don't mind having a lower bitdepth, you can half the amount of VRAM consumed by each image, which may be something interesting to consider.




  3. But no matter what you do, the most important factor when considering the amount of VRAM your game uses, is the amount of images you have in VRAM at a given time. This is the number you most likely want to keep as low as possible if you want a good performing game. Loading and unloading textures into VRAM is expensive, so you can't just load each image whenever you're going to use it. You must find a balance between preloading the images you will most likely use, and unload them when you're sure you're not going to use them anymore. Doing this right is not trivial, and you have to think of your own strategy for your particular game.




Execution speed


Even though not "memory", it is very related with performance in games. Drawing images is expensive, and you want to make sure your rendering runs as fast as possible. Of course, in here, format doesn't matter, but other things do:





  1. Image size (actually, it would be "sampling size"): the biggest the region of an image you're going to draw, the more time it will take to draw it. Rendering a huge image in a small section of the screen is not very effective, so there is a technique called mipmapping, which consists of trading VRAM for rendering speed, by storing your images several times at several resolutions and using the smallest one that can give you the required quality at any given time. Mipmapping can be performed when the images are loaded, which will impact loading speed and VRAM usage, or at preprocessing (by manually storing different versions of the same image, or using a format that natively supports mipmapping such as DDS), which will impact storage and VRAM usage, but will have little impact on loading speed.




  2. Render state changes. You will most likely want to draw several different images on the screen at the same time. However, the GPU can only use a single source image at any given time (this is not true, but please bear with me here). The currently used image for rendering is one of many render states, and it is expensive. So if you're going to use the same image several times (remember when I mentioned texture atlases?), you will notice a huge performance gain if you reuse an image as much as you can before you change the render state and start using a different image (there are other render states apart from this, and fine tuning the order in which you draw your stuff to minimize render state changes is a very common activity when enhancing the performance of a game)




However, image usage optimization is a very complex topic, and what I wrote here is a very broad and oversimplified overview of some of the factors you will have to consider when writing a game, so I think it's definitely best if you keep it simple, and only optimize whenever you really need to do so. Most of the times, prematurely optimizing is unnecessary (and sometimes even detrimental), so take it easy.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...