Search code examples
opengltexturespbo

copying texture from GPU to CPU using pixel buffer objects


I am new to OpenGL and have currently copying the contents of a 2D texture back to CPU memory as follows:

std::vector<unsigned char> frame-data(width * height * 4);
glBindTexture(GL_TEXTURE_2D, tid);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, &frame_data[0]);                 

I noticed this is quite sluggish and I started reading about PBOs and thought of trying to see if that makes things a bit smoother. So I create the PBO as follows:

GLuint  pbo;
glGenBuffers(1, &pbo);
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
glBufferData(GL_PIXEL_PACK_BUFFER, width*height* 4, NULL, GL_STREAM_READ); 

I think the above lines need to be just executed once to initialize the PBO.

My question is now how do I actually map the data from texture to this PBO object? I know at one point after that I will need to call glMapBuffer but I am not sure how to attach this texture to my PBO.


Solution

  • When a buffer is bound to GL_PIXEL_PACK_BUFFER, texture download functions will interpret their last data argument not as a pointer in client memory, but as an offset in bytes from the beginning of the currently bound pixel pack buffer.

    In other words, assuming an already created texture and a suitably sized buffer already created:

    // bind PBO
    glBindBuffer(GL_PIXEL_PACK_BUFFER, buffer); 
    
    // bind texture
    glBindTexture(GL_TEXTURE_2D, texture);
    
    // transfer texture into PBO
    glGetTexImage(GL_TEXTURE_2D,                 
                  level,
                  pixelformat,
                  pixeltype,
                  (GLvoid*)0 // offset in bytes into "buffer",
                             // not pointer to client memory!
                 ); 
    

    After the transfer is done, you can read your texture data contained in your buffer, for instance by mapping it.

    Note that if you map the buffer right away you're not going to get any benefit from the PBO. The whole point of this approach is to let the transfer happen asynchronously while you keep the GPU busy by submitting other commands.

    You can know when the transfer is finished by using synchronization calls, such as sync objects inserted after the transfer call.