OpenGL PBO 用于纹理上传,无法理解一件事

3
好的,我阅读了有关PBO的所有内容:http://www.opengl.org/wiki/Pixel_Buffer_Objecthttp://www.songho.ca/opengl/gl_pbo.html,但我仍然有一个问题,我不知道在我的情况下使用PBO是否会有任何好处:
我正在进行视频流传输,目前我有一个函数将我的数据缓冲区复制到3个不同的纹理中,然后我在片段着色器中执行一些数学运算并显示纹理。
我认为PBO可以增加CPU->GPU的上传时间,但是在这里,让我们假设我们从上面第二个链接中获取了这个示例。
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pboIds[nextIndex]);

        // map the buffer object into client's memory
        // Note that glMapBufferARB() causes sync issue.
        // If GPU is working with this buffer, glMapBufferARB() will wait(stall)
        // for GPU to finish its job. To avoid waiting (stall), you can call
        // first glBufferDataARB() with NULL pointer before glMapBufferARB().
        // If you do that, the previous data in PBO will be discarded and
        // glMapBufferARB() returns a new allocated pointer immediately
        // even if GPU is still working with the previous data.
        glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, DATA_SIZE, 0, GL_STREAM_DRAW_ARB);
        GLubyte* ptr = (GLubyte*)glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY_ARB);
        if(ptr)
        {
            // update data directly on the mapped buffer
            updatePixels(ptr, DATA_SIZE);
            glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB); // release pointer to mapping buffer
        }

        // measure the time modifying the mapped buffer
        t1.stop();
        updateTime = t1.getElapsedTimeInMilliSec();
        ///////////////////////////////////////////////////

        // it is good idea to release PBOs with ID 0 after use.
        // Once bound with 0, all pixel operations behave normal ways.
        glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);

无论updatePixels函数的行为如何,它仍然使用CPU周期将数据复制到映射缓冲区,对吗?
那么假设我想以这样的方式使用PBO,即在函数中将我的帧像素更新到PBO中,然后在display函数中调用glTexSubImage2D(应立即返回)...从性能方面看,我会看到任何速度提升吗? 我不明白为什么它会更快...好吧,我们在glTex*调用期间不再等待,但是在上传帧到PBO的函数期间,我们不是正在等待吗?
有人能为我澄清一下吗?
谢谢
1个回答

5

缓冲对象的重点在于它们可以异步使用。您可以映射一个BO,然后让程序的其他部分更新它(考虑线程,考虑异步IO),同时您可以继续发出OpenGL命令。使用三重缓冲PBO的典型使用场景可能如下:

wait_for_video_frame_load_complete(buffer[k-2])

glUnmapBuffer buffer[k-2]

glTexSubImage2D from buffer[k-2]

buffer[k] = glMapBuffer

start_load_next_video_frame(buffer[k]);

draw_texture

SwapBuffers

这使得你的程序能够在用于渲染时执行有用的工作,甚至上传数据到OpenGL。

问题:为什么在这里要使用三重缓冲?是为了让两个load_next_video_frame调用并行工作吗? - axxel
@axxel:为了允许同步延迟。典型的设置是在SwapBuffers之前进行glMapBuffer,这样填充线程就可以工作,而OpenGL线程则会在V-Sync处阻塞。然后,在显示函数的开头立即执行glUnmapBuffer / glTexSubImage。现在,如果您引用使用该缓冲区填充的纹理,则会插入同步点并可能增加明显的延迟。但是,通过提前为此帧执行此操作,这将异步发生到当前呈现的帧。 - datenwolf
我明白了,你基本上想要一个三阶段的流水线,每个阶段都在并行运行:1)缓冲填充器(这里是video_frame)2)缓冲上传器(驱动程序内部的一些假定的并行/DMA魔法)3)使用缓冲区的GPU上的实际渲染命令队列。对吗? 我之前进行了一些基准测试,并发现至少我的特定NVIDIA移动芯片(记不清是哪个了)无法在命令处理期间并行进行DMA。你有关于哪些硬件能够做到这一点的见解吗? - axxel
@axxel:是的,那正是我们的想法。关于NVidia移动GPU的行为,我既不能确认也不能反驳。移动GPU最大的瓶颈是内存带宽,因此他们无法在渲染几何图形时进行DMA到系统内存,这符合情况。 - datenwolf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接