使用OpenGL

时间:2018-01-22 13:09:42

标签: c++ multithreading opengl video-streaming

我尝试使用OpenGL进行高吞吐量视频流传输。我以为我用我的天才编程架构想出了这一切,但是 - 惊讶的是 - 在做更严肃的测试时,我已经遇到了性能问题。

故事是这样的:

  1. 这一切都始于预留一堆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后,它将返回到堆栈。

  2. 我还预先为每个单独的视频流保留纹理。我保留了三个纹理(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队列中读取帧。

  3. 渲染的关键步骤是将数据从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
    
  4. 最后,使用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合作。

0 个答案:

没有答案