The Texture Storage is the part of Texture objects that contains the actual pixel data stored in the texture. This article describes the layout of a texture's storage, the many ways of managing the allocation and pixel contents of a texture's storage.
Anatomy of storage
A texture's image storage contains one or more images of a certain dimensionality. Each kind of texture has a specific arrangement of images in its storage. Textures can have mipmaps, which are smaller versions of the same image used to aid in texture sampling and filtering. Each mipmap level has a separate set of images.
Since a texture stores multiple images, it is important to be able to identify a specific image in a texture. Each image in a texture can be uniquely identified by the following numbers, depending on the texture type:
- For textures that can have mipmaps, the mipmap level that contains the image.
- For Array Textures, the array layer that contains the image.
- For Cubemap Textures, the face within that array layer and mipmap level. Note that for cubemap array textures, the layer and face are combined into layer-faces.
Therefore, a texture can be thought of as a three-dimensional array of images. The first index is the mipmap level, the second is the array layer, and the third is the cube map face. Another way to think of it is that a texture has a number of mipmap levels. Each mipmap can have a number of array layers. And each array layer has a number of faces. Every image of a texture can therefore be uniquely referenced by the face, layer, and level indices.
Here is a table describing which texture types may have which values (mipmaps, array layers, and faces):
Note that virtually every function in OpenGL that deals with a texture's storage assumes that the texture may have mipmaps. So almost all of them take a level parameter. When using such functions for textures that cannot be mipmapped, the value for level must always be 0. Similarly, when dealing with functions that ask for a number of mipmap levels (such as the function to create storage for a texture), you must use 1 for non-mipmapped texture types.
Each texture type represents images of a certain dimensionality. As such, it is important to know the size of the individual images within a texture. This is easy enough.
All images that have the same mipmap level (ie: all array layers and/or cube map faces in a mipmap) in a texture will have the same size (note that there are ways to try to break this rule; they will only lead to a non-functional texture). That size depends on the size of the base mipmap level of the texture: level 0. The size of level 0 images defines the texture's effective size.
For every mipmap level past level 0, the size decreases in half, rounded down. So if you have a 67x67 base mipmap level for a texture with two-dimensional images, the images in mipmap level 1 will be 33x33 in size. For level 2, they will be 16x16. And so forth.
The mipmap chain stops when all dimensions are 1; that is the maximum mipmap level. "Maximum" in level index value, because the base level starts at 0.
The number of array layers and cube map faces do not change with the mipmap level. If a texture has 3 array layers, every mipmap will have 3 array layers. This is important to remember when allocating texture storage and uploading pixel data.
Kinds of storage
The above describes the way the storage exists within a texture. How to create that storage is another matter.
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. Any other kind of texture can use either mutable or immutable storage (well, the buffer object's storage can be immutable, but that's the buffer object's storage, not the texture's storage). Because of this, the discussion below, with the exception of one section, will focus on mutable and immutable storage.
One of the big differences between mutable storage allocation and immutable storage allocation is this: immutable storage allocates all of the images for the texture all at once. Every mipmap level, array layer, and cube map face is all allocated with a single call, giving all of these images a specific Image Format. It is called "immutable" because once 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. With mutable storage, you can re-vamp the storage of a texture object entirely, changing a 256x256 texture into a 1024x1024 texture.
Immutable storage can be analogized to the C++ concept of "pointer const":
char * const pData = new char;
The value of the pData pointer cannot change. You can't store a new pointer in pData. But, because the contents of pData are not const, it's perfectly valid to modify it as you wish:
char * const pData = new char; std::fill(pData, pData+500, 0); //Legal std::strcpy(pData, "A String Literal"); //Also legal. delete pData; //Yes, this is legal too. You can delete pointer consts. pData = new char; //NOT LEGAL. pData = "Foo"; //Also not legal.
|Core in version||4.6|
|Core since version||4.2, 4.3|
|Core ARB extension||ARB_texture_storage, ARB_texture_storage_multisample|
Allocating immutable storage for a texture requires binding the texture to its target, then calling a function of the form glTexStorage*. Which function you call depends on which texture type you are trying to allocate storage for. Each function only works on a specific set of targets.
- Valid target: GL_TEXTURE_1D
- Valid targets: GL_TEXTURE_2D, GL_TEXTURE_RECTANGE, GL_TEXTURE_CUBE_MAP, or GL_TEXTURE_1D_ARRAY.
- For 1D array textures, the number of array layers in each mipmap level is the height value. For rectangle textures, the number of mipmap levels must be 1.
- Valid targets: GL_TEXTURE_3D, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_CUBE_MAP_ARRAY (this requires GL 4.0 or ARB_texture_cube_map_array)
- For 2D array textures, the number of array layers in each mipmap level is the depth value.
- For 2D cubemap array textures, the number of cubemap layer-faces is the depth, which must be a multiple of 6. Therefore, the number of individual cubemaps in the array is given by depth / 6.
- Valid targets: GL_TEXTURE_2D_MULTISAMPLE
- Valid targets: GL_TEXTURE_2D_MULTISAMPLE_ARRAY
These functions allocate images with the given size (width, height, and depth, where appropriate), with the number of mipmaps given by levels. The storage is created here, but the contents of that storage is undefined. It's a lot like calling malloc; you get memory, but there's nothing in it yet.
The internalformat parameter defines the Image Format to use for the texture. For the most part, any texture type can use any image format. Including the compressed formats. Note that these functions explicitly require the use of sized image formats. So GL_RGBA is not sufficient; you have to ask for a size, like GL_RGBA8.
For the multisample functions, samples defines the number of samples that will be used per-texel in the texture. If you set fixedsamplelocations is GL_TRUE, then the following is assured:
- The texels in the image will all use the same sample locations.
- The texels in the image will all use the same number of sample locations (normally, the implementation could give some texels fewer than samples, while other texels get more).
- All textures with fixed sample locations will use the same set of sample locations, regardless of Image Format.
|Core in version||4.6|
|Core since version||4.3|
|Core ARB extension||ARB_texture_view|
Besides the infinitely cleaner texture specification syntax and the general reduction in the chance for mistakes, creating immutable storage for textures has one other advantage: immutable storage can be shared between texture objects.
Mutable storage is bound to a single texture object. Immutable storage can be shared among several objects, such that they are all referring to the same memory. Think of it like passing a reference-counted smart pointer around. Each object has its own smart pointer, and the memory doesn't go away until all objects that reference the shared memory are destroyed.
The glTexStorage* functions all create new immutable storage, ala malloc. In order to share previously-created immutable storage, we must use a different function:
This function takes two textures. origtexture is the texture that currently has immutable storage. texture is a new texture that doesn't have immutable storage. target is the type of texture. When this function completes, it will share immutable storage with origtexture. This is called a "view texture", because the new texture represents a "view" into the original texture's storage.
The view texture does not have to look at the exact same size of storage. It can reference only a portion of the original texture. For example, if you have an immutable texture with 6 mipmap levels, you can create a view that only uses 3 mipmap levels.
This is the responsibility of the minlevel and numlevels parameters. The minlevel specifies the mipmap level in the origtexture that will become the base level of the view texture. numlevels specifies how many mipmaps are to be viewed. If the origtexture is not a texture type that has mipmaps (multisample or rectangle textures), then minlevel must be 0 and numlevels must be 1. For textures that could have mipmaps, then minlevel and numlevels will be clamped to the actual available number of mipmaps in the source texture (though it is an error if minlevel is outside of the range of mipmaps).
For textures that have layers or faces (GL_TEXTURE_1D_ARRAY, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_CUBE_MAP, or GL_TEXTURE_CUBE_MAP_ARRAY), a range of layers to take can be specified with minlayer and numlayers. As with the mipmap level range parameters, the layer ranges are clamped to the available range of layers, and minlayer must be an available layer in the image. Cube maps are treated as an array texture with 6 layers. Cube map array layers here are layer-faces.
View texture aliases
There are two special tricks you can play with view textures. The texture type of origtexture does not have to match the target. For example, you can have a 1D array texture and create a view of it as a 1D texture, which represents a specific array layer of the original texture. To do this, you must use minlayer to define the layer you want to select, and pass 1 to numlayers.
You can only perform this kind of conversion between very specific sets of texture types. Here is a table defining where the conversion is allowed:
|Original Target||Compatible New Targets|
|GL_TEXTURE_CUBE_MAP||GL_TEXTURE_CUBE_MAP, GL_TEXTURE_2D, GL_TEXTURE_2D_ARRAY, GL_TEXTUER_CUBE_MAP_ARRAY|
|GL_TEXTURE_BUFFER||none. Cannot be used with this function.|
|GL_TEXTURE_2D_ARRAY||GL_TEXTURE_2D, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_2D_ARRAY, GL_TEXTUER_CUBE_MAP_ARRAY|
|GL_TEXTURE_CUBE_MAP_ARRAY||GL_TEXTURE_CUBE_MAP, GL_TEXTURE_2D, GL_TEXTURE_2D_ARRAY, GL_TEXTUER_CUBE_MAP_ARRAY|
The number of mipmaps levels and array layers you fetch are not allowed to violate the constraints of the destination target. So if you want to get a 2D array texture view of a 2D non-array texture, the new 2D array texture will only have one array layer. Similarly, if you want to create a Cubemap Array Texture from a regular 2D array texture, the number of layer-faces in the cubemap array must be a multiple of 6 (though the initial layer taken from the 2D array does not need to be a multiple of 6).
The other trick you can do with view textures is change the Image Format. internalformat is not restricted to the exact image format that origtexture uses. It only has to be compatible with it. All formats that share the same "class" are compatible with each other. Here are the various classes and their compatible formats:
|128-bit||GL_RGBA32F, GL_RGBA32UI, GL_RGBA32I|
|96-bit||GL_RGB32F, GL_RGB32UI, GL_RGB32I|
|64-bit||GL_RGBA16F, GL_RG32F, GL_RGBA16UI, GL_RG32UI, GL_RGBA16I, GL_RG32I, GL_RGBA16, GL_RGBA16_SNORM|
|48-bit||GL_RGB16, GL_RGB16_SNORM, GL_RGB16F, GL_RGB16UI, GL_RGB16I|
|32-bit||GL_RG16F, GL_R11F_G11F_B10F, GL_R32F, GL_RGB10_A2UI, GL_RGBA8UI, GL_RG16UI, GL_R32UI, GL_RGBA8I, GL_RG16I, GL_R32I, GL_RGB10_A2, GL_RGBA8, GL_RG16, GL_RGBA8_SNORM, GL_RG16_SNORM, GL_SRGB8_ALPHA8, GL_RGB9_E5|
|24-bit||GL_RGB8, GL_RGB8_SNORM, GL_SRGB8, GL_RGB8UI, GL_RGB8I|
|16-bit||GL_R16F, GL_RG8UI, GL_R16UI, GL_RG8I, GL_R16I, GL_RG8, GL_R16, GL_RG8_SNORM, GL_R16_SNORM|
|8-bit||GL_R8UI, GL_R8I, GL_R8, GL_R8_SNORM|
Any formats not on this chart are only compatible with themselves; you cannot create a view with a different format.
Because view textures reference immutable storage, this also means that view textures can be used as origtexture. So you can create a view of a view.
The mipmap levels, number and indices of layers, base level texture size, and similar parameters for a view are defined by the particular view, not the original block of storage. As an example, let's say we create a 2D array texture with immutable storage as follows:
glBindTexture(GL_TEXTURE_2D_ARRAY, tex); glTexStorage3D(GL_TEXTURE_2D_ARRAY, 10, GL_RGBA8, 1024, 1024, 6);
We can create a view of tex:
glTextureView(texView1, GL_TEXTURE_2D_ARRAY, tex, GL_RGBA8, 2, 5, 1, 3);
texView1 is a 2D array texture. As far as texView1 is concerned, the size of its base level is 256x256, because it starts with the third mipmap of tex. It has only 5 mipmaps. And though it is an array, it has only 3 array layers.
We can create view from the new texture:
glTextureView(texView2, GL_TEXTURE_2D, texView1, GL_RGBA8, 2, 1, 1, 1);
texView2 is a 2D texture. It has 1 mipmap level, and the size of that level is 64x64, because it picked the third mipmap from texView1. It has 1 array layer, which is taken from the second layer in texView1.
What is texView2 in relation to tex? Exactly what it sounds like. texView2 takes the fifth mipmap level and the third array layer from tex.
A view texture cannot view more mipmap levels and/or array layers than the origtexture advertises, even if the original texture is a view texture and those extra levels/layers exist. Views can only view the same information that the original does or a subset of it. If you want to view more of the storage, you need to use a texture that can access that storage.
Thus, it is technically possible to completely lose access to some levels/layers of a texture, if you delete the original texture created with glTexStorage*.
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.
These functions allocate one mipmap level of the texture at a time (and in some cases, only part of a mipmap level at a time). Because each mipmap level is created individually, there are many points of failure when creating mipmapped textures in this way. You must be sure to:
- Use the exact same internal format for each mipmap level.
- Use the correct size for the mipmap level. The width/height/depth of a mipmap level is the width/height/depth of the base level / 2k, where k is the mipmap level (remember: 0 is the base level). And remember to round down.
- 1D Array Textures have a width and height. But the height specifies the number of array layers in the array of 1D textures. Therefore, the height does not change with mipmap levels. Each mipmap level uses the same height. The same goes for 2D Array textures and Cubemap Array textures with the depth parameter.
- Allocate all of the mipmap levels you intend to use (using a call to a gl*TexImage* function for each level), then set the base/max levels to match this range.
- For cube maps (but not cube map arrays), allocate each face within a mipmap level with individual function calls, with each face being given the same (square) size.
Failing to follow the above rules results in a texture that is not "complete" by the rules of the standard. You cannot attempt to sample from such a texture. The best way to avoid completeness problems is to make sure that the texture is always complete; 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.
Cube map storage
Regular GL_TEXTURE_CUBE_MAPs (and only them. These rules do not apply to GL_TEXTURE_CUBE_MAP_ARRAY) are handled in an unusual way. Normally, each of the functions below will allocate a full mipmap level. All array layers and faces (of cube map arrays) of that mipmap are allocated at once. Not so for non-array cube maps.
For them, you must allocate each face within a mipmap level individually. This is done by playing with the texture target field.
While cube map textures are bound to the GL_TEXTURE_CUBE_MAP target, to reference a specific cube map face within the texture, you use a special target while the cube map is bound to GL_TEXTURE_CUBE_MAP. These targets are:
Each one names a specific face in the cube. Each one is considered a 2D image, so you call the "2D" version of the functions listed below. Note that this is how you interact with (non-array) cube maps for both creating mutable storage and modifying the contents of that storage.
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.
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 ﬁxedsamplelocations );
void glTexImage3DMultisample( GLenum target, GLsizei samples, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean ﬁxedsamplelocations );
These functions allocate an image from the texture bound to target (with the previously mentioned cube map target differences). 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, targets 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.
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.
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 or via some other form of in-OpenGL writing operation (Image Load Store, for example).
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.
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
When creating storage for a texture, you can also get the pixel data from the Framebuffer currently bound to the GL_READ_FRAMEBUFFER target. It will use the current read buffer of that framebuffer (for color reads), so make sure to use glReadBuffer to set it properly beforehand. Also, the framebuffer must be complete.
These functions act like a combination of glReadPixels followed by glTexImage* for the appropriate type. Since framebuffers are two-dimensional, only 1D and 2D copy creation are allowed:
void glCopyTexImage1D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border);
void glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);
The width and height define the size of the given mipmap level. They also define the size of the region taken from the framebuffer. x and y define the bottom-left corner where the read starts (remember: OpenGL puts the origin of the framebuffer at the bottom-left).
Which buffer is copied from depends on the type of internalformat. If internalformat is a color format, then the current read buffer specified by glReadBuffer is used. If internalformat has a depth component, the depth buffer is used (an error occurs if there is no depth buffer). If it has a stencil component, the stencil buffer is used (an error occurs if there is no stencil buffer). If internalformat has both depth and stencil, then both are used as the source.
Once the storage has been defined with one of the above functions, the contents of the storage (the actual pixel data) can be modified and access via various functions.
Automatic mipmap generation
It is often useful to auto-generate a mipmap set from just the base mipmap level in the previously defined range. How exactly the implementation does filtering for mipmap generation is implementation-dependent.
Before calling this function, the base mipmap level must be established. It must be allocated (either with mutable or immutable storage). For cube maps, this also means that each of the 6 faces of the base mipmap level must be allocated.
Once this is done, call this function:
This will cause the texture bound to target to have its mipmap levels below the base level auto-generated. Note that the GL_TEXTURE_BASE_LEVEL and GL_TEXTURE_MAX_LEVEL range are observed. If the max level specifies mipmap levels that cannot exist (for example, the base level is 16x16 in size, and the max level is 20. A 16x16 texture only has 5 mipmap levels), then the effective max level is clamped to the final size 1 image.
The current base level will not be changed; only all of the other levels will be generated. Any mipmap levels that are outside of the base/max range will not be changed by this call.
If the clamped max level specifies mipmap levels that have not yet been allocated, then they will be allocated. Recall that in an immutable storage texture, GL_TEXTURE_MAX_LEVEL will always specify an allocated mipmap level (and trying to set it outside of that range results in an error), so it isn't possible for this to happen for immutable storage textures.
Part or all of a mipmap level can have its pixels replaced via a Pixel Transfer operation. This can be initiated with these functions:
void glTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid * data);
void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid * data);
void glTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid * data);
These functions work like their glTexImage* analogs, with two exceptions. First, they can upload to a sub-section of the mipmap level (hence the name "SubImage"). The sub-section is defined by the xoffset, yoffset, zoffset, width, height, and depth parameters. The offsets define the pixel offsets from the bottom-left of the particular mipmap level. The sizes define both the size of the data being transferred and the pixel size of the data to be overwritten.
The second difference is of course that they do not reallocate the texture's storage. They only upload data to the image(s).
Compressed pixel upload
|TODO: This section needs to be filled in.|
|Core in version||4.6|
|Core since version||4.4|
|Core ARB extension||ARB_clear_texture|
Once storage for images in textures has been allocated, that storage can be cleared to a particular color. This can be done with one of the following functions:
void glClearTexImage(GLuint texture, GLint level, GLenum format, GLenum type, const void * data);void glClearTexSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * data);
The first function clears all images in the given mipmap level. The second function only clears the portion of the image specified by the xoffset, yoffset, zoffset, and width, height, depth.
Neither function takes the texture's type. It will detect the type from the texture itself. If the texture's type doesn't have a particular dimension, then you should set the offset for that dimension to 0 and the size parameter to 1.
Note that GL_TEXTURE_CUBE_MAP textures work differently here. This function treats GL_TEXTURE_CUBE_MAP as basically a GL_TEXTURE_2D_ARRAY texture with 6 layers. So you can clear all of the faces from one cubemap by using a zoffset of 0 and a depth of 6. The order of the faces in the array is the same as for layered rendering to a cube map.
The format, type, and data values work like the standard Pixel Transfer parameters, with a few exceptions. First, data only points to a single pixel's worth of data. Second, these functions do not use asynchronous pixel transfer mechanisms, so you cannot put the single pixel of data in a buffer object. Third, data can be NULL; if it is, then the texture will be cleared to 0.
|Core in version||4.6|
|Core since version||4.3|
|Core ARB extension||ARB_copy_image|
Image data can be copied between images in textures. To do this, use this function:
The srcName and dstName are the textures to copy from and to, respectively. They can be the same if you wish to copy data between mipmap levels of the same texture.
The srcX, srcY, and srcZ define the offset from the bottom-left of the source mipmap level srcLevel to begin copying. The srcWidth, srcHeight, and srcDepth define the size to be copied. And dstX, GLint dstY, and dstZ specify the offset in the destination mipmap level dstLevel.
Note that GL_TEXTURE_CUBE_MAP textures work differently here. This function treats GL_TEXTURE_CUBE_MAP as basically a GL_TEXTURE_2D_ARRAY texture with 6 layers. So you can copy all of the faces from one cubemap to another by using a srcDepth of 6. The order of the faces in the array is the same as for layered rendering to a cube map.
This function copies pixel data directly as is; no filtering or anything of that nature can take place. It also doesn't do color conversion, so copying from linear RGB to sRGB will not convert the colors. Think of it as memcpy for textures.
This copy operation doesn't do any kind of format conversions either. This means that you can only copy between certain Image Formats. It can copy between image formats that are compatible for texture views. Also, the sample count for the two images must be the same.
It can also copy between certain compressed and uncompressed formats, but only where the block size of the compressed format is equal to the pixel size of the uncompressed format. This does not perform compression or decompression; it simply copies the data directly. This allows you to implement compression and decompression algorithms.
For example, you can generate compressed blocks of data into GL_RGBA16UI texture (perhaps through a Compute Shader and Image Load Store). Then you copy the image into a texture whose format is GL_COMPRESSED_RED_RGTC1. Each "pixel" of the source texture represents a compressed block in the destination. So the source area is actually 4x smaller than the overwritten area in the destination image. In terms of pixels of course; in terms of memory, it's still just a memcpy).
You can copy image data from the current GL_READ_FRAMEBUFFER into existing texture storage with these functions. It will use the current read buffer of that framebuffer (for copying into textures that have color image formats), so make sure to use glReadBuffer to set it properly beforehand:
void glCopyTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width);
void glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
void glCopyTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);
These functions work similarly to the corresponding glCopyTexImage* functions.
The xoffset, yoffset, and zoffset parameters represent the offset into the destination mipmap level of where to copy the image data. The x and y parameters are the offset into the framebuffer image being copied from. The width and height define the size of the rectangle to copy from the framebuffer.
Images from textures can be attached to framebuffer objects, where they can be used as render targets.
Images from textures in framebuffers can also be used as destinations for blit operations.
|Core in version||4.6|
|Core since version||4.3|
|Core ARB extension||ARB_invalidate_subdata|
It is possible to tell OpenGL to "invalidate" images stored in a texture. From an outside perspective, this means that the contents of the selected images become undefined; the pixel values can be anything.
However, from a Synchronization perspective, this can be a valuable tool. If you invalidate the storage for a texture, then the OpenGL implementation can simply allocate new storage for it internally. This means that operations on this new storage don't have to wait for operations on the old (now orphaned) storage to complete.
This is perhaps most useful with multisample textures, since these are often used as render targets. Once you've rendered to them and either blitted from them or otherwise resolved them to the screen or some other image, they aren't needed anymore. You still need the texture object, but you can orphan the storage by invalidating it, which lets OpenGL create new storage that you can immediately (hopefully) start rendering into.
To invalidate an entire mipmap level of a texture, you use this function:
After calling this function, the contents of every image in level become undefined. This includes all cube map faces, all array layers, and all cube map array layer-faces.
It is possible to invalidate sub-regions of a mipmap level. This is possibly less useful than invalidating the entire mipmap level, but it could gain some performance in some circumstances.
The offsets define the position within the given texture to invalidate, and the sizes define the region to invalidate. For textures lacking those dimensions, the offset should be 0 and the missing sizes should be 1. Cube maps are again treated as array textures with 6 layers, so the zoffset is the face index of the cube map.
You can retrieve image data from a texture with this function:
This function performs a Pixel Transfer pack (ie: read) operation into img. It will retrieve the entire mipmap level of the texture, except for cube maps. For them, you must use the individual face targets.
This decompresses any compressed textures. To read compressed data as compressed data, you must use this function:
You are expected to know how big the img data storage ought to be.
The storage for buffer textures come from Buffer Objects directly. Buffer textures are one-dimensional. Their "width" is the size of the buffer object range that is attached to the texture with glTexBuffer or glTexBufferRange (requires 4.3 or ARB_texture_buffer_range).