Texture Storage

From OpenGL.org
Revision as of 17:28, 8 October 2012 by Alfonse (Talk | contribs) (Overview)

Jump to: navigation, search

Texture objects contain 3 kinds of information. One of these kinds of information is Texture Storage: the actual pixel data stored in the texture. This page describes the many ways to create storage for textures, modify the contents of storage, and otherwise manipulate the storage of a texture object.


There are three kinds of storage for textures: mutable storage, immutable storage, and buffer storage. Only Buffer Textures can use buffer storage, where the texture gets its storage from a Buffer Object. And similarly, buffer textures cannot use mutable or immutable storage. As such, buffer storage is a special case. Any other kind of texture can use either mutable or immutable storage.

The difference between mutable storage and immutable storage is this: immutable storage allocates all of the images for the texture all at once. Every mipmap level, array layers, cubemap face, etc is all allocated with a single call, giving it a specific Image Format. It is called "immutable" because one the storage is allocated, the storage cannot be changed. The texture can be deleted as normal, but the storage cannot be altered. A 256x256 2D texture with 5 mipmap layers that uses the GL_RGBA8 image format will *always* be a 256x256 2D texture with 5 mipmap layers that uses the GL_RGBA8 image format.

Note that what immutable storage refers to is the allocation of the memory, not the contents of that memory. You can upload different pixel data to immutable storage all you want. You simply can't change a 256x256 texture into a 1024x1024 texture the way you can with mutable storage.

Recommendation: If your implementation supports creating textures with immutable storage, you should use it wherever possible. It will save you from innumerable mistakes.

Immutable storage

Immutable Storage
Core in version 4.5
Core since version 4.2, 4.3
Core ARB extension ARB_texture_storage, ARB_texture_storage_multisample

Texture views

Texture View
Core in version 4.5
Core since version 4.3
Core ARB extension ARB_texture_view

Mutable storage

OpenGL functions of the form gl*TexImage*​ are used to create mutable storage for images within a texture. Calling any of these on a texture that had immutable storage created for it is an error.

The immutable storage calls are the equivalent of a C malloc​: they allocate memory, but they don't put anything in it. All of the mutable storage calls are capable of both allocating memory and transferring pixel data into that memory.

Texture completeness

These functions allocate one mipmap layer of the texture at a time (and in some cases, only part of a mipmap layer at a time). As such, if you intend to use mipmap filtering, you must allocate all of the mipmap layers you intend to use, then set texture parameters that confine the mipmap range to the range you have allocated. Only after you have done this can you actually read from the texture in a shader.

Also, you will notice that you can technically allocate different mipmap levels with different internal format. Yes, you can do that, but you shouldn't. Each mipmap layer should use the exact same internal format.

Failing to follow the above results in a texture that is not "complete" by the rules of the standard. You cannot attempt to sample from such a texture, and it is a very good idea to make sure that a texture is always complete after creation. Make it complete initially, and leave it that way. That's one of the reasons why immutable storage is nice: such textures are always complete.

Note: This is a very abbreviated discussion of texture completeness rules. It is possible to not set the mipmap range and still allow the texture to be complete by turning off mipmap sampling or using a Sampler Object that doesn't do mipmap-based sampling. However, it is never wrong to set the mipmap range correctly, and it is a good idea to get into the habit of always creating textures that are complete rather than not doing so.

Direct creation

There are several ways to allocate mutable storage; the differences are based on where they get their pixel data from. Since mutable storage creation also uploads data, there are many different places the user can get data from.

The only difference between these groups of functions is where they get the pixel data to initialize their images from.

The most direct method performs a regular Pixel Transfer operation from either client memory or a buffer object. These are the functions that allocate and upload in this way:

 void glTexImage1D( GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, void *data );
 void glTexImage2D( GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, void *data );
 void glTexImage3D( GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, void *data );
 void glTexImage2DMultisample( GLenum target, GLsizei samples, GLint internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations );
 void glTexImage3DMultisample( GLenum target, GLsizei samples, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations );

These functions allocate an image from the texture bound to target​ (note: cubemaps use the target very differently). The level​ parameter specifies the mipmap level for the image to be allocated. You can only allocate one mipmap level at a time.

With the exception of cubemaps, target​s for these functions work as they did for the analogous immutable storage functions. So glTexImage2D is used to allocate a mipmap level of a 1D array texture, where the height​ is the number of elements in the array. Multisample texture types must use the multisample allocation functions. And so forth.

Some of these functions have a border​ parameter. This was old functionality that is no longer supported (and really, never was); always set it to 0.

Warning: Do not forget to make sure that all mipmaps and images in the same texture are allocated with the same internalformat​.

The format​, type​, and data​ parameters are used for performing a Pixel Transfer operation. This allows one to create a texture and fill it with some data in one call. As with any pixel transfer operation, Pixel Buffer Objects can be used to feed OpenGL the data.

You do not need to fill in the texture's data in the same call that you create it in. If data​ is NULL, no pixel transfer will be done, and the texture's data is undefined.

Note: Even if data is NULL, the format and type fields must be legal fields, or the entire call will fail with a GL_INVALID_ENUM error.

The multisample versions of these functions do not offer pixel transfer. This is because the image data of multisample textures cannot be updated from client data. It can only be filled in as a render target, and it can only be sourced as a texture from GLSL.

Compressed format creation

Textures that use compressed image formats need special care. It is perfectly legal to pass an appropriate compressed format to any of the prior functions (except for the multisample ones). However, the Pixel Transfer parameters pose a problem. They are designed for regular image data where each pixel is specified individually.

Most compressed formats store pixels in specially formatted blocks. As such, you cannot perform a direct pixel transfer of previously compressed data. If you use a compressed internalformat​ with a regular pixel transfer call, you are telling OpenGL to take uncompressed data and compress it manually.

There are a number of special functions for allocating images with compressed formats and simultaneously filling them with compressed data:

 void glCompressedTexImage1D( GLenum target​, GLint level​, GLenum internalformat​, GLsizei width​, GLint border​, GLsizei imageSize​, void *data​ );
 void glCompressedTexImage2D( GLenum target​, GLint level​, GLenum internalformat​, GLsizei width​, GLsizei height​, GLint border​, GLsizei imageSize​, void *data​ );
 void glCompressedTexImage3D( GLenum target​, GLint level​, GLenum internalformat​, GLsizei width​, GLsizei height​, GLsizei depth​, GLint border​, GLsizei imageSize​, void *data​ );

With the exception of the last two parameters, these functions work identically to their glTexImage*​ counterparts. They allocate mutable storage of the given size for the texture bound to the given target.

Where they differ is in how they transfer pixel data. OpenGL assumes that the data you are passing has been properly formatted according to whatever internalformat​ the image uses. Therefore, it is just going to copy the data verbatim from your data​. The imageSize​ must match with what OpenGL would compute based on the dimensions of the image and the internalformat​. If it doesn't, you get an GL_INVALID_VALUE error.

Again, Pixel Buffer Objects work with such transfers. The data​ parameter must thus be a byte-offset from the front of the buffer bound to GL_UNPACK_BUFFER.

Warning: data​ cannot be NULL here; if you didn't want to transfer pixel data, you should have used glTexImage*​.

internalformat​ must not be a generic compressed format. It must be a specific compressed format (such as GL_COMPRESSED_RG_RGTC1​ or GL_COMPRESSED_RGB_S3TC_DXT1_EXT​.

Framebuffer copy creation

Storage modification

Buffer storage