This post is for beginners of mobile game dev and aim to inform about the different areas of how to work with textures in defold. A lot of the number presented in this post are ballpark numbers and should only be seen as a recommendations, not law or fact.
Makeshift Table of Content
-
This post
- Texture profiles
- Atlases
- Hardware compression
- Texture size
- Common problems
- Atlas properties
- Resizing Images
- Optimizing
Texture Profiles
The texture profile is only used when you bundle your package, if you are using “build and run” it will not compress your textures, however if there is an error in the profile it may make the build unable to run, or fail to load that atlas. This is also true if you use anything else than your local computer as a target.
Profiles
Texture profiles are covered very thoroughly in the documentation so if you want all information read about it here http://www.defold.com/manuals/texture-profiles/
Platform
Dictates which platform the profile should be used for it is quite self explanatory but it is worth mentioning that the OS_ID_GENERIC is used on all platforms, this means that if you uses OS_ID_IOS and OS_ID_GENERIC your build on IOS will now contain both OS_ID_GENERIC and OS_ID_IOS. Remember that you platform you pick should support the format, OS_ID_GENERIC with a hardware format will not work.
Formats
Dictates which channels should be included and if you are using an hardware compression, it is essentially how much memory the images will consume. RGB means that the alpha will be discarded, if your images uses alpha and you use this compression where there was alpha before there will now be black. If you images have alpha you want something that have RGBA.
LUMINANCE is a grey scale image so it only uses one channel, if you put in an image with RGB values in an atlas with this format the color of each pixel will be calculated with (something along the line of) 0.3R+0.59G+0.11*B this is to represent the colors in a way that takes how the eye works in account.
PVRTC is a hardware compression format for PowerVR GPUs, main usage is for iOS. There are two alternatives for it 2BPPV1 (2 bit mode) or 4BPPV1 (4 bit mode).
ETC1 is a part of the OpenGL ES graphics standard extensions and is mainly used for Android. Note that there are no support for alpha with the ETC1 format.
Compression Level
This is the quality you want the texture to be at. The higher the better, the only trade of I can think of here is build time, I have noticed this especially on hardware compression. I have seen changing the compression_level on hardware compression texture from NORMAL to HIGH makes the build go up from 8 minutes to 30 minutes. HIGH compression level isn’t supported with WEBP_LOSSY either.
Compression Type
Decides what compression we want, this is can give a good impact on your package size. You only have 3 choices, DEFAULT, WEPB and WEBP_LOSSY. WEPB is a texture format like JPEG or PNG, it is developed by google and are in my mind vastly superior to both PNG and JPEG. WEB is the equivalent to PNG and WEBP_LOSSY that of a JPEG, but a WEBP texture is a lot smaller on disk than JPEG or PNG. The DEFAULT uses a LZ4 compression, and there is (as far as I have seen) never a good reason to use that over WEBP as WEBP is smaller on disk and the quality is so similar I can’t really notice any difference. Here is a good comparison of WEBP and PNG/JPEG.
Mipmaps
If this is true the engine will embed mipmaps into your atlases if you don’t have problems with your textures you can turn it off and save some space on disk.
Max Size
Scales your texture down if it the atlas size goes over this value. If your atlas is 4k and this value is 2k it will be scaled down to fit it. But it does nothing if your atlas is smaller than the value set here. This is a good way to make sure your textures doesn’t go over your maximum supported texture size.
Path Settings
There is not much to say about the path settings, it uses glob paths and the atlases gets compressed in the order they are set in. Meaning that if you have a generic glob that hits every atlas then that will compress it with that profile even if you further down have specified a setting that will hit that atlas again.
My Recommendation
If you want to keep it simple use a profile with OS_ID_GENERIC and cap your textures to 2048x2048, use RGBA with WEBP at BEST, if you notice slow build times set it to NORMAL, if you want a smaller package use WEBP_LOSSY at HIGH. Set mipmap to false to save in some package size. Use a path that hits every atlas in your project.
game.texture_profile
path_settings {
path: "/**/*.atlas"
profile: "atlas_profile"
}
profiles {
name: "atlas_profile"
platforms {
os: OS_ID_GENERIC
formats {
format: TEXTURE_FORMAT_RGBA
compression_level: BEST
compression_type: COMPRESSION_TYPE_WEBP
}
mipmaps: false
max_texture_size: 2048
}
}
Atlases
In Defold we use atlases also know as sprite sheets, it is preferable to have an understanding of why we use them so we can work with them in a way that is optimized and works with us not against. If you want to learn more in depth about the theory I recommend you to read “Improved Batching via Texture Atlases”, available from NVIDIA’s developer site. For a brief overview I recommend Code and Webs video on it.
The basic theory is that we combine a lot of different textures into one big texture this greatly reduce the amount of calls that is made to the renderer, if we have anywhere from 2 to 200 different textures in a single atlas. This means that you have 2 to 200 less texture states changes per frame. With mobile gaming we have some very harsh restrictions on us from the hardware¹, one of them is that textures needs² to be power of two (8, 16, 32, 64, 128, 256 etc). If we have a sprite that is 140 x 140 pixels we need to fill up the space until it is the closest higher power of two. Meaning until it is 256 x 256 and we will have to do this for all images. Creating a lot of “dead space” used for nothing but taking up space and memory. Hence we pack them together into a big texture atlas.
Packing
As you now know texture atlases are used mainly for performance gain, to reduce the amount of batch/draw calls needed. We should therefore create atlases of textures that are used together. With that in mind the “in game hud”, “pre level hud” and “game tiles” should each be one atlas. We do this because we don’t want to read textures from the “game tiles” atlas when we are on the map, even if we only use a texture that is 1 pixel from the “game tiles” atlas while we are on the map we need to have the whole texture atlas in memory. This can make it useful to sometimes have the same texture in more than one atlas, for instance if you use the same button in the level and outside, placing that one button in both atlases will make us able to only load one of the atlases.
Another gain with packing files together that are used for the same purpose is that we may want to compress them the same way. For instance maybe we want the HUD in a fairly good quality because we know the user will focus on it a lot, but also know that the backgrounds the user will mostly ignore so we can have a harder compression on them. So we do not want to group backgrounds and the HUD for this reason. If we have a lot of textures that doesn’t use alpha we may want to group them together also, because then we don’t need to include the alpha channel in the texture and thus save some space and memory.
Resolution
Max size of the atlas is decided mostly by what device you want supported. The limit on the older iThouches, Original iPhone and iPhone3G is 1024x1024. Anything above that (iPhone 3GS+) supports 2048x2048, newer devices supports higher resolutions.
The hard-cap for the minimum size is 64x64 but remember the reason to use atlases in the first place, that small texture could probably be placed into a different atlas.
Memory
The cap for a the amount of texture atlases is almost more dependent on the package size than the memory – presuming that the atlases is used the right way and you don’t have the whole game loaded into memory at all times. Because we preferably want to support older iOS devices the game will need to run on a device with 512 MB memory (iPhone 4). The “safe value”³ for how much memory our app can take seems to be about 45-50% of the memory of the device, that makes it about 200 MB on the low-end iOS devices, remember that the memory can spike if you for example use a lot of popups and such, making you consume more memory than you think. If you notice you are memory capped⁴ on iOS look towards either reshuffle your images to more effectively use your atlases or consider using PVRTC as they are easier on the memory.
Hardware compression
I have mentioned hardware compression a few times now and if you have not heard it before you are probably wondering what this refers to. A JPEG or PNG uses a compression that compresses the images depending on how it looks so it have a varying size, a hardware compression always uses the same amount of bytes for images of the same size. To put it short (and probably not clear anything up) a hardware compression is a compression that uses a fixed-ration block based scheme for compression, making it easier for random access of a pixel. This Stackexchange post explains it fairly well. The thing to remember is that a hardware compression is a lot smaller in memory, but it is (probably, depends on the image compressed) bigger on disk than other none hardware formats.
Texture Size
When deciding the target resolution of your game you should take into account which is your lowest supported device, do you value disk space or graphical fidelity. Generally I personally want to make it look good on a iPad but this doesn’t mean that I make my textures “pixel perfect”, the user often doesn’t notice if textures are not 100% crisp. The best way to find a good number is to just make an crisp image of let us say 960×640 (resolution of the iPhone 4S) and see how it looks on different devices, if you are not happy with the result than increase it. Of course you should test this out before you start implementing all your art, if you don’t have a lot of devices to test on and want to support iPads and the like I would say go for a bit bigger size maybe something like 1280x720. But the best way is definitely to try it out and see what works for your games art style.
Hope it was a good read, let me know if there are any mistakes or if you have questions.
¹ This is not the case for ALL hardware but because some of them have it and the game needs to run on that particular device that makes it a restriction we always have to follow.
² This is not always true for Android but it is for iOS.
³ This is just a pall park value, the game is a lot more than textures. Sounds, scripts, the actual engine and a lot more. I have heard of some games putting restraints on themselves not to use more than 100 MB for texture memory.
⁴ This is easy to notice as iOS shuts down you app if you run out of memory.