In the previous chapter, you saw how a few simple rules could improve your application’s ability to process vertex data. This chapter provides similar guidelines for textures.
The amount of memory your iOS application uses is critical. However, texture memory in OpenGL ES is even more constrained. iPhone or iPod touch devices that use the PowerVR MBX hardware have a limit on the total amount of memory they can use for textures and renderbuffers (described in “PowerVR MBX”). Where possible, your application should minimize the amount of memory it uses for textures.
Broadly, your application must balance the size of your texture data with the quality of the final image.
Texture compression is often the single largest source of memory savings. OpenGL ES for iOS supports the PowerVR Texture Compression (PVRTC) format by implementing the GL_IMG_texture_compression_pvrtc
extension. There are two levels of PVRTC compression, 4 bits per channel and 2 bits per channel, which offer a 8:1 and 16:1 compression ratio over the uncompressed 32-bit texture format respectively. A compressed PVRTC texture still provides a decent level of quality, particularly at the 4-bit level.
Important: Future Apple hardware may not support the PVRTC texture format. You must test for the existence of the GL_IMG_texture_compression_pvrtc
extension before loading a PVRTC compressed texture. For more information on how to check for extensions, see “Check for Extensions Before Using Them.” For maximum compatibility, your application may want to include uncompressed textures to use when this extension is not available.
For more information on compressing textures into PVRTC format, see “Using texturetool to Compress Textures.”
If your application cannot use compressed textures, you should consider using a smaller pixel format. A texture in RGB565, RGBA5551, or RGBA4444 format uses half the memory of a texture in RGBA8888 format. Use RGBA8888 only when your application needs that level of quality.
The images that an iOS-based device displays are very small. Your application does not need to provide large textures to present acceptable images to the screen. Halving both dimensions of a texture reduces the amount of memory needed for that texture to one-quarter that of the original texture.
Before shrinking your textures, you should first attempt to compress the texture or use a lower-precision color format. A texture compressed with the PVRTC format usually provides higher-quality output than shrinking the texture—and it uses less memory too!
Creating and loading textures is an expensive operation. For best results, avoid creating new textures while your application is running. Instead, create and load your texture data during initialization. Be sure to dispose of your original images once you’ve finished creating the texture.
Once your application creates a texture, avoid changing it except at the beginning or end of a frame. Currently, all iPhone and iPod touch devices uses a tile-based deferred renderer that makes calls to glTexSubImage
and glCopyTexSubImage
particularly expensive. See “Tile-Based Deferred Rendering” for more information.
Binding to a texture changes OpenGL ES state, which takes CPU time to process. Applications that reduce the number of changes they make to OpenGL ES state perform better. Reducing the number of state changes is discussed in detail in “Avoid Changing OpenGL ES State Unnecessarily.”
One way to avoid changing the texture is to combine multiple smaller textures into a single large texture, known as a texture atlas. Each model uses modified texture coordinates to select the desired texture from within the atlas. A texture atlas allows multiple models to be drawn without changing the bound texture and may also allow you to collapse multiple glDrawElements
calls into a single call.
Texture atlases have a few restrictions:
You cannot use a texture atlas if you are using the GL_REPEAT
texture wrap parameter.
Filtering may sometimes fetch texels outside the expected range. To use those textures in a texture atlas, you must place padding between the textures that make up the texture atlas.
Because the texture atlas is still a texture, it is subject to the limitations of the OpenGL ES implementation.
Your application should provide mipmaps for all textures except those being used to draw 2D unscaled images. Although mipmaps use additional memory, they prevent texturing artifacts and improve image quality. More importantly, when smaller mipmaps are sampled, fewer texels are fetched from texture memory, which reduces the memory bandwidth used by the hardware and significantly improves performance.
The GL_LINEAR_MIPMAP_LINEAR
filter mode provides the best quality when texturing but requires additional texels to be fetched from memory. Your application can trade some image quality for better performance by specifying the GL_LINEAR_MIPMAP_NEAREST
filter mode instead.
Many applications perform multiple passes on their geometry, altering the configuration of OpenGL as they need to in order to generate the final result. This is not only expensive due to the number of state changes required, but it also requires vertex information to be reprocessed for every pass, and pixel data to be read back from the framebuffer on later passes.
All OpenGL ES implementations support at least two texture units and your application can use these texture units to perform multiple steps of your algorithm in each pass. You can retrieve the number of texture units available to your application by calling glGetIntegerv
with GL_MAX_TEXTURE_UNITS
as the parameter.
If your application requires multiple passes to render a single object:
Ensure that the position data remains unchanged for every pass.
Use the GL_EQUAL
depth function to ensure that only pixels in your geometry are modified.
Last updated: 2010-07-09