Optimizing with texture compression

To optimize your OpenGL ES application, you can compress the textures that you use. Texture compression is a specialized form of image compression that's designed for storing texture maps. Support for texture compression formats varies by GPU and you can see a list of BlackBerry 10 devices and their corresponding GPUs in Capabilities by graphics platform. The following sections describe the benefits of compressing textures, the texture compression formats that are available on BlackBerry 10 devices, and the texture compression pipeline.

Benefits of texture compression

  • Decreases memory size and bandwidth requirements

    Compressed texture data uses significantly less memory than uncompressed texture data. Compressing textures greatly reduces the memory and bus bandwidth needed to read textures.

  • Increases performance

    Because compressed textures have reduced bandwidth requirements, there is a performance improvement when transferring data using the Accelerated Graphics Port and from the default frame buffer.

  • Supports larger textures and more textures

    Larger textures typically result in more surface detail and a smoother look. Because texture sizes are smaller, you can store more textures as well.

  • Allows for mipmapping

    The extra memory you gain from texture compression allows you to use mipmaps, which help reduce aliasing artifacts on textured surfaces. Mipmaps require extra memory, but texture compression uses the finite system memory more efficiently.

Texture compression formats

If you use glGetString() to query the extensions that BlackBerry 10 supports, you can obtain a list of the texture compression formats that are available.

3Dc texture compression

BlackBerry 10 devices that use the Qualcomm Adreno GPU support the 3Dc texture compression format. The OpenGL ES extension is GL_AMD_compressed_3DC_texture. If this extension exists, you can use 3Dc compression. The following formats are supported:
  • 3DC_X_AMD
  • 3DC_XY_AMD
To compress your textures, you can use the Compressonator tool.

ATITC texture compression

BlackBerry 10 devices that use the Qualcomm Adreno GPU support the ATITC (ATC) texture compression format. The OpenGL ES extension is GL_AMD_compressed_ATC_texture. If this extension exists, you can use ATC compression. The following formats are supported:
  • ATC_RGB_AMD
  • ATC_RGBA_EXPLICIT_ALPHA_AMD
  • ATC_RGBA_INTERPOLATED_ALPHA_AMD
To compress your textures, you can use the Compressonator tool.

Ericsson texture compression

BlackBerry 10 devices that use the Qualcomm Adreno or PowerVR GPU support the Ericsson texture compression (ETC1) format. The OpenGL ES extension is GL_OES_compressed_ETC1_RGB8_texture. ETC1 doesn't support an alpha channel, so you use fully opaque textures only. To compress your textures, you can use the etcpack tool.

PowerVR texture compression

BlackBerry 10 devices that use the PowerVR GPU support the PowerVR texture compression (PVRTC) format. The OpenGL ES extension is GL_IMG_texture_compression_pvrtc. If this extension exists, you can use the PVRTC format. The extension provides additional functionality that's specific to the PVRTC format, but it supports precompressed images only. The following formats are supported:
  • GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG
  • GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG
  • GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG
  • GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG
To precompress images, you can use the Imagination Technologies PVRTexTool.

Texture compression pipeline

In this section, we discuss the texture compression pipeline and how to use texture compression in your application. First, we check which formats are available, and then compress the textures with a tool. Finally, we use the compressed textures in your code. This section outlines how to use the ETC1 compression format, but you can apply this approach to other formats as well.

Check which formats are available

We can check which formats are available by retrieving the number of formats that are supported, and then retrieving all the available formats. Take a look at the following code as an example:

GLint num_formats;
GLenum *compress_formats = NULL;
glGetInteger( GL_NUM_COMPRESSED_TEXTURE_FORMATS, &num_formats);

compress_formats = malloc( num_formats * sizeof(GLenum) );
glGetIntegerv(  GL_COMPRESSED_TEXTURE_FORMATS,
    (GLint *) compress_formats );

Now we have a list of symbolic constants that texture compression formats are available for. We can loop through the list and check for the format we're looking for.

int i;
for( i=0; i<num_formats; i++ ){
	if(compress_formats[i] == 
        GL_OES_compressed_ETC1_RGB8_texture ){

In this case, we're looking for ETC1, and if we make it inside this if statement, we can use this compression format.

Compress your image

You can use a tool like etcpack to compress textures, but the image must be in PPM format. The etcpack tool converts .ppm image files to .ktx or .pkm files, which are compressed using the ETC1 format. To convert other image files to .ppm files, we can use a tool like XnConvert.

To use XnConvert, we navigate to the folder that contains nconvert.exe, and then run nconvert.exe. Here is an example of using nconvert.exe to convert a .png file to a .ppm file:

nconvert -out ppm example.png

To use the etcpack tool, we navigate to the folder that contains etcpack.exe, and then run etcpack.exe. Here is an example of converting a .ppm file to a .ktx file using the ETC1 compression format:

etcpack example.ppm example.ktx

Use the compressed texture

First, we need to enable some OpenGL ES features to use textures and textures compression. We start by turning on the ability to use 2-D images by calling glEnable(GL_TEXTURE_2D). Generally, you can call this once at initialization and continue, because drawing without creating a texture first is still possible. We enable blending by calling glEnable(GL_BLEND), which lets us combine images in different ways. Then, we specify the type of blending to use. For example, if we set the source to GL_ZERO, each color channel is multiplied by 0.0 which makes them fully transparent. The parameter GL_SRC_COLOR specifies that we want to use the color from the source image. For more information about these constants, see the glBlendFunc() manual page on the OpenGL ES website.

glEnable(GL_TEXTURE_2D); 
glEnable(GL_BLEND);
glBlendFunc(GL_ZERO, GL_SRC_COLOR);

Next, we create the texture by declaring a GLuint array and specifying the number of textures we want. Then, we generate the textures using glGenTextures().

GLuint textures[1];
glGenTextures(1, textures);

We also need to bind the texture before we add the image data, so we call glBindTexture(). Binding a texture makes a particular texture active and only one can be active at a time. We pass the GL_TEXTURE_2D argument to indicate that we're using a 2-D image for our texture.

glBindTexture(GL_TEXTURE_2D, textures[0]);

We set any texture parameters we need. For example, here we specify a 2-D texture and a magnification filter constant to use when a pixel maps to an area less than or equal to one texture element. We also specify GL_LINEAR to indicate that we want to use a weighted average of the four surrounding texture elements.

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

Now we load the image data. If we use image data that is uncompressed, it might look like this:

glTexImage2D(GL_TEXTURE_2D, 0, format, tex_width, tex_height, 0, 
    format, GL_UNSIGNED_BYTE, image_data);

Or, we can load a compressed ETC texture:

glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_OES_compressed_ETC1_RGB8_texture, 
    tex_width, tex_height, 0, image_data_size, image_data); 

Last modified: 2014-11-17



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus