Optimizing 2D Assets
Optimizing the images used by Texture components in your scene can substantially improve both the startup and runtime performance of your application, as well as the visual quality in certain situations.
Motivation
Unoptimized images can hurt the performance of your application in several ways:
- A large image takes more memory bandwidth when being traversed as a texture.
- PNG and JPG compressed images must be decompressed before they can be sent to the graphics system.
- Poorly sized images degrade performance and quality on startup
- Sending uncompressed images to the graphics system takes longer, and they take up more graphics memory when there.
Additionally, choosing an appropriate size for your images and using mipmaps can prevent aliasing issues if your image is going to be shown substantially smaller at some points.
Reducing Image Dimensions
Note: Make your image small, but with dimensions that are power of two.
The first thing you need to do is resize your image to be almost as small as possible. Smaller images can be loaded faster by the graphics system as they need less bandwidth and memory. Think about the largest size that your image will ever be seen at, and resize your image down to that size. If your texture will be tiling at about 32 pixels per repetition, don't save it at 1024×1024.
However, note that both the horizontal and vertical dimensions of your image must be POT(power of two) to be stored on the GPU. Most of modern GPUs support NPOT(non-power of two) textures but many investigations show that using NPOT textures hurts about 30% of the performance. If you need to use NPOT textures, we recommend to make the dimensions multiple of 4. It might help your GPU to use its memory and cache efficiently and finally increase performance.
Note: If you are using mipmaps, there are further constraints on image dimensions.
Using MipMaps
Note: Use mipmaps when your image may be seen smaller than the original size, including portions in perspective.
Enabling mipmaps creates many smaller copies of the image at an additional 1/3 memory usage. Each mipmap dimension is halved from the one preceding it, and is downsized in image editing tool with good image resampling. The result speeds up rendering, decreases the time that the GPU uses for texture lookup, and also reduces aliasing artifacts such as moiré effects or texture subsampling.
To provide mipmaps at build time, the texture needs to be in a container format like KTX, as standard image formats do not have the capability of storing mipmaps.
It is possible to generate mipmaps at runtime by setting the property Texture::generateMipmaps to true
.
Note: Mipmaps require specific image dimensions to work correctly at all levels, depending on which encoding is used.
MipMaps and Image Dimensions
If the original image dimensions are represented by width
, height
, and depth
, the number of mipmap levels will be 1 + floor(log2(max(width, height, depth)))
and each levels' dimensions will be max(1, width_prev >> 1)
, max(1, height_prev >> 1)
, and max(1, depth_prev >> 1)
.
Saving Alpha Channels
Most of the time when dealing with images with semi-transparent regions, it is easier to use image editing tool's transparent layers when editing such images. If you choose a compressed encoding that supports alpha, the transparent regions of your scene are properly used for the alpha information.
However, in certain cases you need to control the RGB values of fully transparent pixels. Specifically, you can see visual artifacts if:
- Any portion of your image will ever be seen at a size larger than saved.
- You have fully transparent pixels in your image next to rather opaque pixels.
In this case, the texture interpolation between a fully-transparent pixel and its neighboring somewhat-opaque pixel blends the RGB values between the two. If you use the image editing tool's transparent layers, the RGB values for certain transparent pixels are saved as white, and you will thus see white fringing at the edges of your transparent regions.
For such cases, instead of creating a semi-transparent layer in your image editing tool, create a layer with no transparency at all, setting the RGB value for every pixel you care about. Then, save the alpha information in a fourth channel.