Texture internal format, Cuda-Opengl interoperability

Hi all,

I am confused about the storing mechanism of textures. Here is my understanding, please tell me if I am correct.

According to the documentation http://docs.gl/gl4/glTexImage2D


glTexImage2D() stores each element as four floats. For example, I first load a single-channel image in cpu memory:

uint16_t* pGrayscale = new uint16_t[nPixels];
fread_s((void*)pGrayscale , nPixels * sizeof(uint16_t), sizeof(uint16_t), nPixels , datafile);

then write it to a texture:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_UNSIGNED_SHORT, (void*)pGrayscale );

and it should be the same as

glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, width, height, 0, GL_RED, GL_UNSIGNED_SHORT, (void*)pGrayscale );

then Opengl allocates a gpu memory with the size 4 * nPixels * sizeof (float). However, I followed the Cuda-Opengl interoperatibility guide https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#opengl-interoperability, and got a cudaArray pointer to this gpu memory, I FAILED to copy this from device to host using cudaMemcpyFromArray as usual. So I think my understanding may be wrong.

Best regards

1 Like

No. Pixel transfer operations are specified in terms of taking whatever external format you use, converting it to four floats, then converting that to whatever internal format you use. That’s unlikely to be how the transfer process is actually implemented, it’s just simpler to specify it that way.

An unsized internal format (such as GL_RED) has an unspecified internal format (only the number of channels is specified, not their representation). An internal format of GL_RED will probably be equivalent to GL_R8, i.e. an 8-bit unsigned normalised value, but ultimately it’s up to the implementation. If you’re going to be accessing the internal representation directly, you need to used a sized internal format such as GL_R32F.

Thank you @GClements!

After reading your reply, I did some tests with different internal formats, now I still have some questions to ask.

  1. the result of the glGetError() told that only GL_RGB would work, why?

  2. I continued to use the Cuda-Opengl interop function to get the cudaArray pointer to this gpu memory, and copy it back to cpu memory, of course I followed your guide to treat it as 8-bit type. However, the result seems to show that 3-channel input data is stored as a 4-channel data2, with the 4th component set 0, when the internal format was ‘RGB’ NOT ‘RGBA’.

Could you please explain more, thank you!

Best regards

If you use an integral internal format (as opposed to a normalized integer format), then you must use matching pixel transfer parameters. Namely, the format must be an _INTEGER format.

Yes, that’s to be expected. Hardware doesn’t like the number 3; it prefers powers of two. And it likes pixels to be aligned to powers-of-two as well. So if you get an 8-bit-per-channel format, any “3-component” formats will almost certainly use 4 components, since that gives each pixel 32-bits instead of 24.

The others are all unsigned integer formats. As Alfonse says, you have to use an _INTEGER external format (e.g. GL_RED_INTEGER) with these. For an unsigned normalised format, use GL_R8 or GL_R16 (there aren’t any 32-bit normalised formats).

A format without a suffix is an unsigned normalised format, a _SNORM suffix indicates a signed normalised format, ‘F’ a floating-point format, ‘I’ a signed integer format, and ‘UI’ an unsigned integer format.

Ok, now I understand. Thank you for your help! @Alfonse_Reinheart @GClements