Есть ли лучший / более эффективный способ захвата составных X-окон в Linux?

По теме у меня есть следующий псевдокод для настройки захвата окна в 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);

Затем, намного позже, это будет цикл для захвата кадров:

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);

Я в основном делаю glXBindTexImageEXT -> glGetTexImage -> glXReleaseTexImageEXT, чтобы получить обновленное изображение. Это действительно работает, но я не уверен, что поступаю правильно / оптимально.

Есть ли лучший / более оптимизированный способ получить такое изображение / контекст?


person Emanuele    schedule 05.05.2020    source источник
comment
Использование для этого всего конвейера GLX лишь для того, чтобы снова прочитать данные текстуры в основную память, кажется довольно непростым делом. Есть ли конкретная причина, по которой вам нужен OpenGL?   -  person Florian Echtler    schedule 08.05.2020
comment
Я также разработал версию, в которой я использую PBO для передачи данных и управления буферами памяти. Вы не поверите, но на некотором оборудовании многие операции должны быть DMA-подобными, что принесет 2 преимущества: стабильность с точки зрения отсутствия мерцания и истинный захват. Если вы сделаете то же самое с каким-либо другим API (например, x11grab в libavcodec), вы получите прерывистые кадры: с этим захватом качество намного лучше, потому что конвейер GL участвует в захвате.   -  person Emanuele    schedule 09.05.2020


Ответы (1)


На данный момент я нашел немного лучший способ реализовать выборку составного окна через OpenGL, через PBO; Преимущество этого способа состоит в том, что вы можете запускать команду асинхронно, а затем извлекать буфер RGBA из системной памяти, в то время как драйвер OpenGL выполняет передачу данных.

Пример псевдокода:

// 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);

Потом намного позже

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);

Этот подход определенно лучше, чем оригинальный подход (в вопросе), если вы можете использовать ЦП / тот же поток, чтобы что-то делать между вызовами glGetTexImage и glMapBuffer.
Стоит подумать, что может все еще лучше, даже если вы выполняете эти вызовы последовательно (вместо glGetTexImage без PBO), потому что драйвер все еще может оптимизировать передачу и сам управлять буфером системной памяти.

person Emanuele    schedule 14.05.2020
comment
Пока голосую за свой ответ, как только будет что-то получше, поменяю голосование. - person Emanuele; 17.05.2020