openglx11framebuffertextureviewglx

Is there a better/more efficient way to capture composite X windows in Linux?


As per subject I have the following pseudo-code to setup window capture in X (Linux):

xdisplay = XOpenDisplay(NULL);
win_capture = ...find the window to capture...
XCompositeRedirectWindow(xdisplay, win_capture, CompositeRedirectAutomatic);
XGetWindowAttributes(xdisplay, win_capture, &win_attr); // attributes used later
GLXFBConfig *configs = glXChooseFBConfig(xdisplay, win_attr.root, config_attrs, &nelem);
// cycle through the configs to
// find a valid one
...
win_pixmap = XCompositeNameWindowPixmap(xdisplay, win_capture);
const int pixmap_attrs[] = {GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
                    GLX_TEXTURE_FORMAT_EXT,
                    GLX_TEXTURE_FORMAT_RGBA_EXT, None};
gl_pixmap = glXCreatePixmap(xdisplay, config, win_pixmap, pixmap_attrs);
gl_ctx = glXCreateNewContext(xdisplay, config, GLX_RGBA_TYPE, 0, 1);
glXMakeCurrent(xdisplay, gl_pixmap, gl_ctx);
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &gl_texmap);
glBindTexture(GL_TEXTURE_2D, gl_texmap);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, win_attr.width, win_attr.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

Then, much later on, this would be the loop to capture the frames:

glXMakeCurrent(xdisplay, gl_pixmap, gl_ctx);
glBindTexture(GL_TEXTURE_2D, gl_texmap);
glXBindTexImageEXT(xdisplay, gl_pixmap, GLX_FRONT_LEFT_EXT, NULL);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); // data is output RGBA buffer
glXReleaseTexImageEXT(xdisplay, gl_pixmap, GLX_FRONT_LEFT_EXT);

I basically do glXBindTexImageEXT -> glGetTexImage -> glXReleaseTexImageEXT so that I get an updated picture. It does work, but not sure I'm doing the right/optimal thing.

Is there a better/more optimized way to get such picture/context?


Solution

  • As of now I've found a slightly better way to implement fetching the composite window through OpenGL, via PBO; the advantages of this way is that you could initiate the command asynchronously and then retrieve the RGBA buffer from system memory, whilst the OpenGL driver does data transfer.

    Sample pseudocode:

    // setup a PBO
    GLuint cur_pbo;
    glGenBuffers(1, &cur_pbo);
    glBindBuffer(GL_PIXEL_PACK_BUFFER, cur_pbo);
    glBufferData(GL_PIXEL_PACK_BUFFER, size, NULL, GL_STREAM_READ);
    

    Then much later on

    glXMakeCurrent(xdisplay, gl_pixmap, gl_ctx);
    glBindTexture(GL_TEXTURE_2D, gl_texmap);
    glXBindTexImageEXT(xdisplay, gl_pixmap, GLX_FRONT_LEFT_EXT, NULL);
    glBindBuffer(GL_PIXEL_PACK_BUFFER, cur_pbo);
    // This will initiate the data transfer, the previous
    // buffer pointer is now an offset in the index bound by previous
    // glBufferData call
    glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
    // do something else
    ...
    ...
    ...
    // then later on when we _really_ need to get the data
    // perform this call which will make wait if the RGBA 
    // data is not avilable yet
    void* rgba_ptr = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
    // Then when finished to use rgba_ptr, release it
    glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
    glXReleaseTexImageEXT(xdisplay, gl_pixmap, GLX_FRONT_LEFT_EXT);
    

    This approach is definitely better than original approach (in the question) if you can use the CPU/same thread to do something between the calls to glGetTexImage and glMapBuffer.
    It's worth thinking it may be still better even if you perform these calls sequentially (instead of glGetTexImage without PBO) because the driver may still optimize the transfer and would manage the system memory buffer itself.