7

For an algorithm of mine I need to be able to access the depth buffer. I have no problem at all doing this using glReadPixels, but reading an 800x600 window is extremely slow (From 300 fps to 20 fps)

I'm reading a lot about this and I think dumping the depth buffer to a texture would be faster. I know how to create a texture, but how do I get the depth out?

Creating an FBO and creating the texture from there might be even faster, at the moment I am using an FBO (but still in combination with glReadPixels).

So what is the fastest way to do this?

(I'm probably not able to use GLSL because I don't know anything about it and I don't have much time left to learn, deadlines!)

edit: Would a PBO work? As described here: http://www.songho.ca/opengl/gl_pbo.html it can go a lot faster but I can not change buffers all the time as in the example.

Edit2: How would I go about putting the depth data in the PBO? At the moment I do:

glGenBuffersARB(1, &pboId);
glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboId);
glBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, 800*600*sizeof(GLfloat),0,     GL_STREAM_READ_ARB);

and right before my readpixels i call glBindbuffer again. The effect is that I read nothing at all. If I disable the PBO's it all works.

Final edit: I guess I solved it, I had to use:

glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboId);
glReadPixels( 0, 0,Engine::fWidth, Engine::fHeight, GL_DEPTH_COMPONENT,GL_FLOAT, BUFFER_OFFSET(0));

GLuint *pixels = (GLuint*)glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY);

This gave me a 20 FPS increase. It's not that much but it's something.

So, I used 2 PBO's but I'm still encountering a problem: My code only gets executed once.

glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboIds[index]);  
std::cout << "Reading pixels" << std::endl;
glReadPixels( 0, 0,Engine::fWidth, Engine::fHeight, GL_DEPTH_COMPONENT,GL_FLOAT, BUFFER_OFFSET(0));
std::cout << "Getting pixels" << std::endl;
//  glBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, 800*600*sizeof(GLfloat), 0, GL_STREAM_DRAW_ARB);
GLfloat *pixels = (GLfloat*)glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY);
int count = 0;
for(int i = 0; i != 800*600; ++i){
    std::cout << pixels[i] << std::endl;
}

The last line executes once, but only once, after that it keeps on calling the method (which is normal) but stops at the call to pixels.


I apparently forgot to load glUnMapBuffers, that kinda solved it, though my framerate is slower again..


I decided giving FBO's a go, but I stumbled across a problem: Initialising FBO:

glGenFramebuffersEXT(1, framebuffers);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffers[0]);
std::cout << "framebuffer generated, id: " << framebuffers[0] << std::endl;
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);

glGenRenderbuffersEXT(1,renderbuffers);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffers[0]);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 800, 600);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, renderbuffers[0]);
bool status = checkFramebufferStatus();
    if(!status)
        std::cout << "Could not initialise FBO" << std::endl;
    else
        std::cout << "FBO ready!" << std::endl;

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

My drawing loop:

GLenum errCode; const GLubyte *errString;

if ((errCode = glGetError()) != GL_NO_ERROR) {
    errString = gluErrorString(errCode);
   fprintf (stderr, "OpenGL Error: %s\n", errString);
}

++frameCount;
// -----------  First pass to fill the depth buffer  -------------------
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffers[0]);

std::cout << "FBO bound" << std::endl;
//Enable depth testing
glEnable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glDepthMask( GL_TRUE );
//Disable stencil test, we don't need that for this pass
glClearStencil(0);
glEnable(GL_STENCIL_TEST);

//Disable drawing to the color buffer
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

//We clear all buffers and reset the modelview matrix
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glLoadIdentity();

//We set our viewpoint
gluLookAt(eyePoint[0],eyePoint[1], eyePoint[2], 0.0,0.0,0.0,0.0,1.0,0.0);
//std::cout << angle << std::endl;
std::cout << "Writing to FBO depth" << std::endl;
//Draw the VBO's, this does not draw anything to the screen, we are just filling the depth buffer
glDrawElements(GL_TRIANGLES, 120, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0));

After this I call a function that calls glReadPixels() The function does not even get called. The loop restarts at the function call.


Apparently I solved this as well: I had to use

glReadPixels( 0, 0,Engine::fWidth, Engine::fHeight, GL_DEPTH_COMPONENT,GL_UNSIGNED_SHORT, pixels);

With GL_UNSIGNED_SHORT instead of GL_FLOAT (or any other format for that matter)

Kevin
  • 765
  • 1
  • 12
  • 27
  • 1
    Have you made two PBO which you alternate between? – Andreas Brinck Aug 23 '11 at 19:10
  • Hmm, that's an idea, Ill check it out. – Kevin Aug 23 '11 at 19:45
  • I still have a problem, see my edit – Kevin Aug 23 '11 at 20:02
  • You won't gain anything by swithching over to FBO, the driver will still stall when you call glReadPixels. The reason for using async PBO is that this enables the driver to return immediately from glReadPixels. – Andreas Brinck Aug 25 '11 at 12:46
  • But I need something to do while it is reading, no? I mean, I am using 2 PBO's. I actually have 4 fps more when using FBO's. It might be that I am using the PBO"s wrong, but I did not find any information that pointed me in that direction. I took a look at your link and implemented it that way. – Kevin Aug 25 '11 at 12:49
  • Your PBO code is wrong. You should bind PBO1, glReadPixels, bind PBO2, glMapBufferARB and so forth. Right now your doing bind PBO1, glReadPixels, glMabBuffer which completely defies the point. – Andreas Brinck Aug 25 '11 at 13:03
  • (And yes this means you will lag one frame) – Andreas Brinck Aug 25 '11 at 13:05

2 Answers2

4

The fastest way of doing this is using asynchronous pixel buffer objects, there's a good explanation here:

http://www.songho.ca/opengl/gl_pbo.html

Andreas Brinck
  • 47,252
  • 14
  • 79
  • 112
  • Ah, did not see your reply when I made an edit, I will try this immediately! – Kevin Aug 23 '11 at 17:21
  • OKay, you are right. The first time I did PBO's I used 2 but I was not actually doing any asynchronous operations, hence I did not win any speed. I do now, a staggering 20fps. It's still slow, but already better. Using more PBO's, would that increase even more? – Kevin Aug 25 '11 at 14:54
  • You might, I would try and see. – Andreas Brinck Aug 25 '11 at 15:24
  • Hmm, okay, I tried with four and it seemed to go a bit slower (fps wise, measured with GLUT), so I'll keep it at two then. Thanks for your help! (I actually got at 30+ FPS after removing some debug output) – Kevin Aug 25 '11 at 16:34
1

I would render to a FBO and read its depth buffer after the frame has been rendered. PBOs are outdated technology.

karx11erx
  • 1,934
  • 2
  • 19
  • 32
  • And that should go faster than just reading from the default buffer? I will try it at any rate! – Kevin Aug 25 '11 at 10:53
  • I seem to be having a problem, I initialise my FBO, no errors reported, I bind my fbo, I write my depth to it, but glreadpixels does not work. It does not get called. – Kevin Aug 25 '11 at 12:13
  • You're just wrong. PBO and FBO are orthogonal concepts, if you don't use asyncronous PBO your graphic driver will stall when you call glReadPixels until the frame has been transfered over the bus. – Andreas Brinck Aug 25 '11 at 12:44
  • If rendering to an FBO, you can read its depth buffer at any time - even before swapping or flushing your buffers. I am doing it all the time, and there are no stalls (or my soft particle renderer wouldn't work), because you can treat the depth buffer as a texture. PBOs were clumsy to handle, and FBOs are there to replace them. – karx11erx Aug 25 '11 at 18:44
  • What are you talking about, PBO and FBO are completely different things and have different purposes. Are you maybe missundetstanding the OP (your soft particle remark indicates this, you're using the depth buffer as a texture, right?) He wants to transfer the depth buffer from the GPU into ordinary ram. – Andreas Brinck Aug 25 '11 at 19:51
  • (I'm not talking about pbuffers, http://www.opengl.org/registry/specs/ARB/wgl_pbuffer.txt, but pixel buffer objects, http://www.opengl.org/registry/specs/ARB/pixel_buffer_object.txt) – Andreas Brinck Aug 25 '11 at 20:12
  • No problem, the names sure are confusing, "pixel buffer" vs "pixel buffer objekt" – Andreas Brinck Aug 25 '11 at 20:42