将4k视频渲染为OpenGL ES纹理的最快方法?

时间:2017-03-10 17:36:57

标签: video opengl-es

在CPU上解码4k(3840x2160 @ 60hz)视频以渲染到OpenGL ES纹理的软件过程的最佳方法是什么?

我目前的做法如下:

创建像素缓冲区对象:

glGenBuffers(1, &pbo_id);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_id);

将3840x2160x4(宽x高x bpp)分配给PBO:

glBufferData(GL_PIXEL_UNPACK_BUFFER, size, NULL, GL_STREAM_DRAW);

将PBO映射到客户端的内存空间:

GLubyte *ptr = (GLubyte *)glMapBufferRange(
                               GL_PIXEL_UNPACK_BUFFER,
                               0,
                               size,
                               GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT | GL_MAP_UNSYNCHRONIZED_BIT);

直接解码到此内存并手动刷新:

glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, size);

看来我可以每秒约300帧的速度完成此操作。这看起来很漂亮 令人印象深刻的是,这是很多数据。

创建纹理:

glGenTextures(1, &texture_id);
glBindTexture(GL_TEXTURE_2D, texture_id);

将PBO复制到纹理中:

glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, 0);

这个阶段很慢,将性能限制在~60 fps。

渲染:

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

此阶段将性能进一步限制在~40 fps。

这是最好的方法吗?是否有更好的方法在PBO中获得一次像素?每秒40帧的速度还不够快。

注释:

  • 将内存映射到客户端的地址空间使得与视频解码的集成更加简单,并且转移到GPU不必等待整个帧被解码。

  • 我可以使用两个或更多PBO。将当前帧解码为一个PBO,然后渲染/显示前一帧的PBO,以利用写入PBO的异步性质?但是,这会增加额外的解码延迟帧,我非常希望避免这种情况。

  • 我的顶点和片段着色器现在是直接传递。我没有锁定/屏蔽atm,但显然稍后会要求它。

  • 我正在使用Linux,Wayland和EGL。直接使用DRM Dumb Buffers我可以达到~200 fps。

1 个答案:

答案 0 :(得分:1)

  

我可以使用两个或更多PBO。将当前帧解码为一个PBO,然后渲染/显示前一帧的PBO,以利用写入PBO的异步性质?但是,这会增加额外的解码延迟帧,我非常希望避免这种情况。

这不是双缓冲区纹理上传应该如何工作: - )

当您使用两个PBO时,您将当前帧解码为一个PBO,然后将相同的PBO 渲染到显示器。这里发生的是渲染将在GPU上异步发生,并且当GPU仍然忙于最后一帧时,您可以开始将下一帧上传到另一个PBO。关键是你延迟重复使用另一帧缓冲区给GPU一个赶上的机会,这减少了后续帧的延迟,但不会影响当前帧的延迟。

这是一种非常典型的高性能纹理上传策略,就像您描述的那样。

相比之下,剩余的优化通常相当轻微。对片段和顶点着色器的更改,如果它们开始时相当合理,则不会导致任何显着差异。请注意,目前删除glClear()通常不是优化。