我尝试使用OpenGL进行高吞吐量视频流传输。我以为我用我的天才编程架构想出了这一切,但是 - 惊讶的是 - 在做更严肃的测试时,我已经遇到了性能问题。
故事是这样的:
这一切都始于预留一堆PBO(比如说,一百多个):
glGenBuffers(1, &index);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, index);
glBufferData(GL_PIXEL_UNPACK_BUFFER, size, 0, GL_STREAM_DRAW); // reserve n_payload bytes to index/handle pbo_id
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); // unbind (not mandatory)
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, index); // rebind (not mandatory)
payload = (GLubyte*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); // release pointer to mapping buffer ** MANDATORY **
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); // unbind ** MANDATORY **
YUV像素数据由单独的解码器/上传器线程复制到PBO中,这些线程使用公共堆栈的可用PBO。 "有效载荷"您在上面看到的指针,可以从这些线程中访问,并且数据被复制(使用memcpy)"直接"到了gpu。使用PBO后,它将返回到堆栈。
我还预先为每个单独的视频流保留纹理。我保留了三个纹理(y,u和v),如下所示:
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &index);
glBindTexture(GL_TEXTURE_2D, index);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, format, GL_UNSIGNED_BYTE, 0); // no upload, just reserve
glBindTexture(GL_TEXTURE_2D, 0); // unbind
渲染是在"主线程" (记住,解码器/上传器线程是单独的动物)从FIFO队列中读取帧。
渲染的关键步骤是将数据从PBO复制到纹理(tex->格式为GL_RED):
// y
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo->y_index);
glBindTexture(GL_TEXTURE_2D, tex->y_index); // this is the texture we will manipulate
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex->w, tex->h, tex->format, GL_UNSIGNED_BYTE, 0); // copy from pbo to texture
// u
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo->u_index);
glBindTexture(GL_TEXTURE_2D, tex->u_index); // this is the texture we will manipulate
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex->w/2, tex->h/2, tex->format, GL_UNSIGNED_BYTE, 0); // copy from pbo to texture
// v
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo->v_index);
glBindTexture(GL_TEXTURE_2D, tex->v_index); // this is the texture we will manipulate
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex->w/2, tex->h/2, tex->format, GL_UNSIGNED_BYTE, 0); // copy from pbo to texture
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); // unbind // important!
glBindTexture(GL_TEXTURE_2D, 0); // unbind
最后,使用OpenGL着色语言(另一个故事)绘制图像。
问题:您是否在此处看到任何OpenGL性能瓶颈?
步骤(3)似乎是一个瓶颈,因为它开始消耗太多时间(最多10+毫秒)!当我试图用几台相机做这件事时。
当然,这可能是由于其他东西堵塞了OpenGL管道 - 但其他一切(glDrawElements等)似乎需要最多。 1毫秒。
我一直在阅读人们对glTexSubImage2D所遇到的问题,但在我的情况下,我只是填充了公益组织的纹理。这应该是闪电般快 - 对吧? GL_RED格式是否因驱动程序不是最佳而造成问题?
另一件事:我没有在这里进行de / reallocating(我正在使用相同的预先保留的PBO堆栈),但是重新分配似乎也很快......如果我理解的话这个正确吗??
https://www.khronos.org/opengl/wiki/Buffer_Object_Streaming
任何见解都非常感谢..!
P上。 S.完整的项目在这里:https://github.com/elsampsa/valkka-core
编辑1:
我做了一些分析:在流式传输期间,PBO =>纹理加载(如代码片段中所示)和glXMakeCurrent都完全疯了,它们都消耗10+毫秒(!)零星。我尝试在每次PBO =>纹理加载后添加一些glFinish调用,但收效甚微(似乎有点稳定了一些......但实际上我并不确定)
编辑2:
我正慢慢地到达那里。在一些测试中,我(a)用PBO上传到GPU然后(b)从PBO复制到纹理(就像在那个示例代码中)。速度似乎取决于" glTexImage2D"中的纹理格式。我尝试匹配纹理的格式和OpenGL内部格式,分别将它们设置为GL_RED和GL_RED(或GL_R8)。但那很慢。相反,如果我同时使用GL_RGBA,则PBO => TEX快速闪电...快100倍!
下面:
https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml
它说
GL_RED:每个元素都是一个红色成分。 GL将其转换为浮点并通过将0连接到绿色和蓝色以及将1连接到alpha来将其组装成RGBA元素。每个组件都被钳位到[0,1]范围内。
..但我不想让OpenGL这样做!我怎么能告诉它它只是简单的LUMA,即每像素一个字节而不需要转换/填充它,因为我只会在着色器程序中使用它。
也许这是不可能的,我应该使用缓冲区纹理(如评论中所建议的那样)..?缓冲区纹理不会尝试转换任何内容......它们只是将其作为原始有效负载处理,对吧?
编辑3:
我试图让dma到纹理缓冲区对象:
// let's reserve a TBO
glGenBuffers(1, &tbo_index); // a buffer
glBindBuffer(GL_TEXTURE_BUFFER, tbo_index); // .. what is it
glBufferData(GL_TEXTURE_BUFFER, size, 0, GL_STREAM_DRAW); // .. how much
std::cout << "tbo " << tbo_index << std::endl;
glBindBuffer(GL_TEXTURE_BUFFER, 0); // unbind
// generate a texture
glGenTextures(1, &tex_index);
std::cout << "texture " << tex_index << std::endl;
// let's try to get dma to the texture buffer
glBindBuffer(GL_TEXTURE_BUFFER, tbo_index); // bind
payload = (GLubyte*)glMapBuffer(GL_TEXTURE_BUFFER, GL_WRITE_ONLY); // ** TODO: doesn't work
glUnmapBuffer(GL_TEXTURE_BUFFER); // release pointer to mapping buffer
glBindBuffer(GL_TEXTURE_BUFFER, 0); // unbind
std::cout << "tbo " << tbo_index << " at " << (long unsigned int)payload << std::endl;
不能工作..有效负载始终是空指针。 glMapBuffer可以和PBO一起使用。它也应该与TBO合作。