【发布时间】:2014-02-05 12:57:23
【问题描述】:
我想使用 OpenGL 渲染多个视频流。目前我正在使用 JOGL 提供的 glTexImage2D 执行并在 Swing 窗口上渲染。 为了更新每个视频帧的纹理内容,我调用 glTexImage2D。我想知道有没有更快的方法来更新纹理而不为每一帧调用 glTexImage2D。
【问题讨论】:
我想使用 OpenGL 渲染多个视频流。目前我正在使用 JOGL 提供的 glTexImage2D 执行并在 Swing 窗口上渲染。 为了更新每个视频帧的纹理内容,我调用 glTexImage2D。我想知道有没有更快的方法来更新纹理而不为每一帧调用 glTexImage2D。
【问题讨论】:
您将始终使用glTexImage2D,但不同之处在于数据来自缓冲区对象 (what is this?),而不是来自指针。
更新纹理的慢点不是更新纹理,而是与当前绘制操作和 PCIe 传输同步(阻塞)。当您调用glTexImage 时,OpenGL 必须等到它完成绘制最后一帧,在此期间它仍在读取纹理。在此期间,您的应用程序被阻塞并且什么都不做(这是必要的,因为否则您可以在 OpenGL 复制它之前修改或释放指向的内存!)。然后它必须复制数据并将其传输到显卡,然后您的应用程序才能继续运行。
虽然不能使该过程更快,但可以使其异步运行,因此这种延迟几乎消失了。
对于视频帧,最简单的方法是 create a buffer name,绑定它,然后 reserve-initialize 它一次。
然后,在每个后续帧上,通过使用空数据指针调用 glBufferData 来丢弃初始化它,并使用 non-reserving 调用或 mapping the buffer's complete range 填充它。
你想要做这种奇怪的舞蹈而不是简单地覆盖缓冲区的原因是它不会阻塞。 OpenGL 将同步对缓冲区对象的访问,因此您在读取数据时不会覆盖数据。 glBufferData 带有一个空数据指针是一种告诉 OpenGL 你并不真正关心缓冲区并且你不需要 same 缓冲区的方法。所以它只会分配另一个并给你那个,继续阅读旧的,并在完成后秘密交换它们。
由于已经使用了“同步”这个词,我将在上面的链接中解释我对glMapBufferRange 的选择,实际上你想要映射整个缓冲区,而不是某个范围。为什么要这样?
即使 OpenGL 在使用上述丢弃技术时大部分情况下都可以避免同步,但有时它仍可能不得不这样做。
此外,它仍然需要运行某种内存分配算法来管理缓冲区,这需要驱动程序时间。 glMapBufferRange 允许您指定额外的标志,特别是(在更高的 OpenGL 版本中)表示“不同步”的标志。这允许一种更复杂但仍然更快的方法,在这种方法中,您创建一个两倍于您需要的大小的单个缓冲区,然后继续映射/写入下半部分或上半部分,告诉 OpenGL 根本不同步。然后,您有责任知道何时安全(可能通过使用栅栏对象),但您应尽可能避免所有开销。
【讨论】:
glTexImage2D 会阻塞直到 glDrawXX 完成?我在opengl文档中找不到这方面的知识。
不更新纹理就无法更新纹理。
另外,我不认为对glTexImage 的一次调用可能是一个真正的性能问题。如果您对此非常担心,请创建两个纹理并在使用另一个进行绘图时将其中一个映射为写入,然后交换(就像双缓冲工作一样)。
如果您可以将处理转移到 GPU,则根本不必调用该函数,这大约是 100% 的加速。
【讨论】: