在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。
答案 0 :(得分:1)
我可以使用两个或更多PBO。将当前帧解码为一个PBO,然后渲染/显示前一帧的PBO,以利用写入PBO的异步性质?但是,这会增加额外的解码延迟帧,我非常希望避免这种情况。
这不是双缓冲区纹理上传应该如何工作: - )
当您使用两个PBO时,您将当前帧解码为一个PBO,然后将相同的PBO 渲染到显示器。这里发生的是渲染将在GPU上异步发生,并且当GPU仍然忙于最后一帧时,您可以开始将下一帧上传到另一个PBO。关键是你延迟重复使用另一帧缓冲区给GPU一个赶上的机会,这减少了后续帧的延迟,但不会影响当前帧的延迟。
这是一种非常典型的高性能纹理上传策略,就像您描述的那样。
相比之下,剩余的优化通常相当轻微。对片段和顶点着色器的更改,如果它们开始时相当合理,则不会导致任何显着差异。请注意,目前删除glClear()
通常不是优化。