Luca
Luca

Reputation: 11016

OpenGL optimised representation of textures

I have read many places that one should avoid OpenGL textures that are 3 bytes long and should always use 4 bytes bus aligned representations. Keeping that in mind, I have a couple of questions about the glTextImage2D API.

From the documentation, the API signature is:

void glTexImage2D(GLenum target,  GLint level,  GLint internalformat,
                  GLsizei width,  GLsizei height,  GLint border,  
                  GLenum format,  GLenum type,  const GLvoid * data);

Am I correct in assuming that if I have an RGB image and I want a RGBA representation, it is suffice to specigy the internalformat parameter as GL_RGBA and the format parameter as GL_RGB? Would there be an internal conversion between the formats when generating the texture?

My second question is what if I have grayscale data (so just one channel). Is it ok to represent this as GL_RED or is it also better to have a 4 byte representation for this?

Upvotes: 0

Views: 2097

Answers (2)

Reto Koradi
Reto Koradi

Reputation: 54642

I disagree with the recommendation you found to avoid RGB formats. I can't think of a good reason to avoid RGB textures. Some GPUs support them natively, many other do not. You have two scenarios:

  1. The GPU does not support RGB textures. It will use a format that is largely equivalent to RGBA for storage, and ignore the A component during sampling.
  2. The GPU supports RGB textures.

In scenario 1, you end up with pretty much the same thing as you get when specifying RGBA as the internal format. In scenario 2, you save 25% of memory (and the corresponding bandwidth) by actually using a RGB internal format.

Therefore, using RGB for the internal format is never worse, and can be better on some systems.

What you have in your code fragment is completely legal in desktop OpenGL. The RGB data you provide will be expanded to RGBA by filling the A component with 1.0. This would not be the case in OpenGL ES, where you can only use a very controlled number of formats for each internal format, which mostly avoids format conversions during TexImage and TexSubImage operations.

It is generally beneficial to match the internal format and the format/type to avoid conversions. But even that is not as clear cut as it might seem. Say you compare loading RGB data or RGBA data into a texture with an internal RGBA format. Loading RGBA has a clear advantage by not using a format conversion. Since on the other hand the RGB data is smaller, loading it requires less memory bandwidth, and might cause less cache pollution.

Now, the memory bandwidth of modern computer systems is so high that you can't really saturate it with sequential access from a single core. So the option that avoids conversion is likely to be better. But it's going to be very platform and situation dependent. For example, if intermediate copies are needed, the smaller amount of data could win. Particularly if the actual RGB to RGBA expansion can be done as part of a copy performed by the GPU.

One thing I would definitely avoid is doing conversions in your own code. Say you get RGB data from somewhere, and you really do need to load it into a RGBA texture. Drivers have highly optimized code for these conversions, or they might even happen as part of a GPU blit. And it's always going to be better to perform the conversion as part of a copy, compared to your code creating another copy of the data.

Sounds confusing? These kinds of tradeoffs are very common when looking at performance. It's often the case that to get the optimal performance with OpenGL, you need to use a different code path for different GPUs/platforms.

Upvotes: 4

Andon M. Coleman
Andon M. Coleman

Reputation: 43369

Am I correct in assuming that if I have an RGB image and I want a RGBA representation, it is suffice to specigy the internalformat parameter as GL_RGBA and the format parameter as GL_RGB? Would there be an internal conversion between the formats when generating the texture?

This will work, and GL will assign a constant 1.0 to the alpha component for every texel. OpenGL is required to convert compatible image data into the GPU's native format during pixel transfer, and this includes things like adding extra image channels, converting from floating-point to fixed-point, swapping bytes for endian differences between CPU and GPU.

For best pixel transfer performance, you need to eliminate all of that extra work that GL would do. That means if you used GL_RGBA8 as your internal format your data type should be GL_UNSIGNED_BYTE and the pixels should be some variant of RGBA (often GL_BGRA is the fast path -- the data can be copied straight from CPU to GPU unaltered).

Storing only 3 components on the CPU side will obviously prevent a direct copy, and will make the driver do more work. Whether this matters really depends on how much and how frequently you transfer pixel data.

My second question is what if I have grayscale data (so just one channel). Is it ok to represent this as GL_RED or is it also better to have a 4 byte representation for this?

GL_RED is not a representation of your data. That only tells GL which channels the pixel transfer data contains, you have to combine it with a data type (e.g. GL_UNSIGNED_BYTE) to make any sense out of it.

GL_R8 would be a common internal format for a grayscale image, and that is perfectly fine. The rule of thumb you need to concern yourself with is actually that data sizes needs to be aligned to a power-of-two. So 1-, 2-, 4- and 8-byte image formats are fine. The oddball is 3, which happens when you try to use something like GL_RGB8 (the driver is going to have to pad that image format for alignment).

Unless you actually need 32-bits worth of gradiation (4.29 billion shades of gray!) in your grayscale, stick with either GL_R8 (256 shades) or GL_R16 (65536 shades) for internal format.

Upvotes: 1

Related Questions