使用OpenGL和x264更快地编码实时3D图形

4
我正在处理一个系统,该系统会在服务器端渲染3D图形,并将压缩后的视频发送给客户端。我已经编写了可行的代码,但是我感觉它可以更快(因为它已经成为系统的瓶颈)。
以下是我的操作步骤:
首先,我获取帧缓冲区。
glReadBuffer( GL_FRONT );
glReadPixels( 0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer ); 

然后我翻转帧缓冲,因为在使用 swsScale 进行色彩空间转换时遇到了一个奇怪的 bug,它会在转换图像时垂直翻转。我提前进行翻转,没有任何花哨的操作。

void VerticalFlip(int width, int height, byte* pixelData, int bitsPerPixel)
{
byte* temp = new byte[width*bitsPerPixel];
height--; //remember height array ends at height-1


for (int y = 0; y < (height+1)/2; y++) 
{
    memcpy(temp,&pixelData[y*width*bitsPerPixel],width*bitsPerPixel);
    memcpy(&pixelData[y*width*bitsPerPixel],&pixelData[(height-y)*width*bitsPerPixel],width*bitsPerPixel);
    memcpy(&pixelData[(height-y)*width*bitsPerPixel],temp,width*bitsPerPixel);
}
delete[] temp;
}

然后我将其转换为YUV420p格式。

convertCtx = sws_getContext(width, height, PIX_FMT_RGB24, width, height, PIX_FMT_YUV420P, SWS_FAST_BILINEAR, NULL, NULL, NULL);
uint8_t *src[3]= {buffer, NULL, NULL}; 

sws_scale(convertCtx, src, &srcstride, 0, height, pic_in.img.plane, pic_in.img.i_stride);

然后我基本上只是调用了x264编码器。我已经使用了zerolatency预设。

int frame_size = x264_encoder_encode(_encoder, &nals, &i_nals, _inputPicture, &pic_out);

我的猜测是,应该有一种更快的方法来做这件事。捕获帧并将其转换为YUV420p。最好在GPU中将其转换为YUV420p,然后再将其复制到系统内存中,希望有一种不需要翻转即可进行颜色转换的方法。
如果没有更好的方法,至少这个问题可以帮助那些试图以我所做的方式进行操作的人。

1
有几个方法可以使这个程序“更快”。你确实可以将一些计算卸载到GPU上,但如果你的GPU已经成为瓶颈,那么这并没有太大帮助。你还可以通过使用PBO来改进代码,以便不会阻塞GPU管道。在这种情况下,“更快”是相对的。首先测量一下问题所在。 - KillianDS
1
此外,您只是想在这里进行屏幕抓取,还是可以将内容呈现到FBO中?这会对您进行优化方面的操作产生很大的影响。我已经经历了整个过程,它可能会非常棘手。 - KillianDS
抱歉。通过readpixels将framebuffer复制到内存、翻转和整体转换是瓶颈。复制后,一切都在主内存和CPU中完成。 我正在服务器上渲染到屏幕,然后进行捕获。这可能很傻,如果使用FBO可以避免去显示器并同时提高性能,那就太好了。 我不太熟悉Frame buffer objects,但我可以试试。我想我可以修改应用程序,以便它不再渲染到后备缓冲区,而是渲染到FBO。这有道理吗? - cloudraven
前缓冲区可能是您可以读取的最慢的源。即使后备缓冲区更快,但理想情况下,您需要一个实际的纹理(FBO)。 - ssube
然后我翻转了帧缓冲,因为swsScale存在奇怪的bug... 我认为图像被翻转是因为OpenGL的坐标系统:(0,0)位于屏幕左下角。 - sonofrage
1个回答

2
首先,使用PBOs进行异步纹理读取。这里有一个示例链接:example。它通过使用2个PBOs进行异步工作,而不像直接使用readPixels时会停止管道,从而加速读取。在我的应用程序中,当切换到PBOs时,性能提升了80%。 此外,在某些GPU上,glGetTexImage()的速度比glReadPixels()更快,可以尝试一下。
但是,如果您真的想将视频编码提升到下一个级别,可以使用CUDA和Nvidia Codec Library。最近我问了同样的问题,所以这个可能会有所帮助。

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