Rendering depth to pc memory

Hi,

I’m trying to render the depth of a collada model to client memory, to perform some image processing in cpu. I’m using vbo for storing the vertices in gpu memory. So far the only option that has worked for me was a framebuffer object with a texture attached, but it is too slow for my requirements (~1ms/frame for rendering and ~3ms/frame for passing the texture to client memory, 640x480 float). I cannot use two buffers and get the previous frame while computing the current one; I must request a render and obtain the depth corresponding to that request immediately. I wonder:

  • what’s the best option for these requirements: texture+framebuffer? renderbuffer + framebuffer? pixelbuffer?
  • any code sample to see how to do it with pixelbuffer or rbo+fbo? i tried the later but i only got a black screen.

So far the only option that has worked for me was a framebuffer object with a texture attached, but it is too slow for my requirements (~1ms/frame for rendering and ~3ms/frame for passing the texture to client memory, 640x480 float).

A 640x480 image contains roughly 300,000 pixels. Each depth component is (presumably) 24-bits, which means each depth value fits in a 32-bit word. At 3 milliseconds per frame, you can get about 333 frames per second. Do the math, and you come up with a transfer speed of around 380 MB/sec.

This transfer speed could be entirely reasonable for your hardware. What hardware are you using?

Also, make sure that you aren’t doing anything more than DMA’ing the data. The read format should be GL_UNSIGNED_INT_24_8; anything else, and the CPU is probably doing some data conversion, thus slowing you down.

any code sample to see how to do it with pixelbuffer or rbo+fbo?

Pixel buffer objects are used to make data transfers asynchronous. It will not decrease the time it takes to actually transfer data.

My graphics card is a GeForce 9800 GT.

I was reading the data as GL_FLOAT, as that was the format I set for the texture. I can try reading GL_UNSIGNED_INT_24_8.

That was what I thought about PBOs; since I need synchronous rendering they wouldn’t be of much help. What about fbo+rbo vs fbo+texture?

GL_UNSIGNED_INT_24_8 is faster, but doesn’t show anything (so i’m not sure it’s faster because it’s not sending anything or because it actually saves conversion time). I guess it doesn’t make much sense to use GL_UNSIGNED_INT_24_8, since the texture has GL_FLOAT format:

glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, WIDTH, HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,GL_TEXTURE_2D, depthTextureId, 0);

So, isn’t the following a sensible way of reading the texture?(as said before, it works but it’s slow):

glGetTexImage( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,GL_FLOAT,maskBW.data);

Is there a preferred format for depth component textures?

I guess it doesn’t make much sense to use GL_UNSIGNED_INT_24_8, since the texture has GL_FLOAT format:

glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, WIDTH, HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);

This is a sadly common misunderstanding about these sorts of things. The last three parameters describe the format of the data you are initializing the texture with. If you’re not initializing the texture (by passing NULL, with no PBO bound to GL_PACK_BUFFER), then these parameters do nothing.

The actual format of the image is GL_DEPTH_COMPONENT (the third parameter. The internal format). Because you didn’t specify a size, the implementation probably choose GL_DEPTH_COMPONENT24 (note: always choose a size).

GL_UNSIGNED_INT_24_8 is faster, but doesn’t show anything

What didn’t show anything? What numbers are you getting back? Did you change the code that was processing them to use normalized integer data instead of floating-point values on [0, 1]? It can be a bit trickey to pull data from an UNSIGNED_INT_24_8; are you doing it correctly?

Good point. I tried to switch to any of the tastes of GL_DEPTH_COMPONENT (GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32) in both format, internal format and when reading but i get a GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT.

If I set format to GL_DEPTH_COMPONENT and internal format to GL_DEPTH_COMPONENT32 it works,
glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, mWidth, mHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);

but it requires the get function to request GL_DEPTH_COMPONENT in order to show something, and it’s equally slow.

And yes, I didn’t treat it properly. I should pull the data out of the 24-8 format, but I want to use it as float32 in the end, so it would be useful to make it work (fast) with GL_DEPTH_COMPONENT32.

I tried to switch to any of the tastes of GL_DEPTH_COMPONENT (GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32) in both format, internal format and when reading but i get a GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT.

Framebuffer completeness represents a programmatic error (unless its UNSUPPORTED). This likely means that you’re doing something wrong in your code. GL_DEPTH_COMPONENT24 is a required format by OpenGL 3.0 and above, and it is required to work for render targets.

There’s no way to know what’s wrong without seeing your binding code.

but I want to use it as float32 in the end, so it would be useful to make it work (fast) with GL_DEPTH_COMPONENT32.

GL_DEPTH_COMPONENT32 is a normalized integer format. GL_DEPTH_COMPONENT32F is a floating-point format.

The code that generates the texture it’s pretty simple, you can find it below. Changing to 32F gives the same error. My OpenGL version is 3.3 according glxinfo:
OpenGL version string: 3.3.0 NVIDIA 256.53

=============================================================
[b]
RenderTexture(const GLsizei &pWidth, const GLsizei &pHeight, bool pWindowRender=false):
mWidth(pWidth),mHeight(pHeight),mWindowRender(pWindowRender)
{
// Try to use a texture depth component
glGenTextures(1, &mTexId);
glBindTexture(GL_TEXTURE_2D, mTexId);

  // GL_LINEAR does not make sense for depth texture. However, next tutorial shows usage of GL_LINEAR and PCF
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

  glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, mWidth, mHeight, 0, GL_DEPTH_COMPONENT32F, GL_FLOAT, 0);
  glBindTexture(GL_TEXTURE_2D, 0);

  // create a framebuffer object
  glGenFramebuffers(1, &mFboId);
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFboId);

  // Instruct openGL that we won't bind a color texture with the currently binded FBO
  glDrawBuffer(GL_NONE);
  glReadBuffer(GL_NONE);

  // attach the texture to FBO depth attachment point
  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,GL_TEXTURE_2D, mTexId, 0);

  // check FBO status
  GLenum FBOstatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  if(FBOstatus != GL_FRAMEBUFFER_COMPLETE)
  {
    std::cout << "The framebuffer is not complete" << std::endl;

}

  // switch back to window-system-provided framebuffer
  glBindFramebuffer(GL_FRAMEBUFFER, 0);

}

[/b]

glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, mWidth, mHeight, 0, GL_DEPTH_COMPONENT32F, GL_FLOAT, 0);

The last three parameters describe the pixel data. I mentioned that they don’t matter when you pass NULL, but that was a misstatement; they don’t cause anything special to happen, but they can still break your code.

The first of these parameters, the format, specifies what components of data you are sending. GL_DEPTH_COMPONENT32F is a image format. It defines the format of the image as OpenGL stores it. It does not define what components of data you are sending OpenGL. It is not a valid value for the format argument.

Therefore, this code will give you a GL_INVALID_ENUM error, and no texture storage will be created. Hence your framebuffer incomplete message: the texture doesn’t contain anything.

The proper value should be GL_DEPTH_COMPONENT. As a format rather than internalformat, it says that you’re passing depth data. Even if you’re not passing any, you still need to give the right arguments.

Yes, this is really silly.

I see.
OK, I tried generating the texture as:

glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, mWidth, mHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);

Unfortunately, reading with GL_DEPTH_COMPONENT32F gives me all zeros:

glGetTexImage( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, GL_FLOAT, img);

but with GL_DEPTH_COMPONENT the image is properly read, and i would say with a small speed increase, but still slow(takes ~2ms to send the data).

So, I guess that by setting the format in glTexImage2D to GL_DEPTH_COMPONENT (and not GL_DEPTH_COMPONENT32F because… its an image format?) it manages to read the data natively in 32F.
When i use other formats (GL_DEPTH_COMPONENT32F) or other types (GL_UNSIGNED_24_8) it is faster: should i assumed that they are better or that actually nothing is sent? (i didn’t extract the data according to 24-8, so I don’t know if anything was sent at all)

As a guess, you could try replacing GL_FLOAT with…
GL_FLOAT_32_UNSIGNED_INT_24_8_REV

Unfortunately, reading with GL_DEPTH_COMPONENT32F gives me all zeros:

glGetTexImage( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, GL_FLOAT, img);

Of course it does. It throws a GL_INVALID_ENUM error because GL_DEPTH_COMPONENT32F is not a valid format. The format parameter that glGetTexImage takes is the same kind of format that glTexImage takes.

A quick look at the man pages for this function would have told you this.