Qt/OpenGL:我是否正确使用PBO?
我正在开发一个项目,用OpenGL显示视频流,然后在外部屏幕上显示QOpenGLWidget
上的内容。所以我做的是在OpenGL小部件上显示流,然后使用glReadPixels
和两个像素包缓冲区对象来获取缓冲区并将其发送到另一个屏幕。问题是,与使用PBOs时相比,当我不是时,我会失去表现。Qt/OpenGL:我是否正确使用PBO?
这里是代码的有趣的部分:
的代码来创建将是外屏上发送帧:
screenBuffer
是存储从QOpenGLWidget
帧的内存缓冲区
在代码中的这一点上,PBO已经充满了数据从paintGL
功能
void GLWidget::videodisplay(unsigned char *copy){
update();
unsigned char* frame = publicCreateOutputVideoFrame();
if(pboIndex){
pbo1->bind();
pbo1->read(0, screenBuffer, vWidth*vHeight*3);
}else{
pbo2->bind();
pbo2->read(0, screenBuffer, vWidth*vHeight*3);
}
pboIndex = !pboIndex;
unsigned char* yuvFrame = convertRGBtoYUV(screenBuffer);
memcpy(frame, yuvFrame, vWidth*vHeight*2);
publicDisplayVideoFrameSync();
delete yuvFrame;
yuvFrame = NULL;
delete copy;
copy = NULL;
}
初始化的PBO的:
void GLWidget::InitializeGL(){
pbo1 = new QOpenGLBuffer(QOpenGLBuffer::PixelPackBuffer);
pbo1->create();
pbo1->bind();
pbo1->allocate(vWidth*vHeight*3);
pbo1->release();
pbo2 = new QOpenGLBuffer(QOpenGLBuffer::PixelPackBuffer);
pbo2->create();
pbo2->bind();
pbo2->allocate(vWidth*vHeight*3);
pbo2->release();
}
这里我用PBO与glReadPixels
void GLWidget::paintGL(){
glClear(GL_COLOR_BUFFER_BIT);
program->bind();
{
vao.bind();
glBindTexture(GL_TEXTURE_2D, tex);
glDrawArrays(GL_QUADS, 0, 4);
glBindTexture(GL_TEXTURE_2D, 0);
vao.release();
}
program->release();
if(!isZoomed){
programMouse->bind();
{
vaoMouse.bind();
glLineWidth(2.0);
glDrawArrays(GL_LINES, 0, 8);
vaoMouse.release();
}
programMouse->release();
}
if(pboIndex){
pbo2->bind();
}else{
pbo1->bind();
}
glReadPixels(0, 0, vWidth, vHeight, GL_RGB, GL_UNSIGNED_BYTE, 0);
if(pboIndex){
pbo2->release();
}else{
pbo1->release();
}
}
pboIndex
只是切换值的第一和第二PBO之间交替一个布尔值。
显然,由于我失去表现,我做错了什么?我是以错误的方式使用公益组织,或者我没有正确理解我应该使用它们的情况。
感谢
你的背后像素缓冲对象的目的,通过乒乓两个不同的缓冲区之间每一帧显示出一个大致的了解(我认为)。真正的问题是两个像素缓冲区可能不足以防止管道堵塞。
许多驱动程序配置为开箱即可排队三个帧的命令,如果您尝试在帧n+2
期间回读帧n
的结果,您已经有效地缩短了最大管道深度。框架n+2
的命令设置将不被允许继续,直到n
的结果完成并被回读。
驱动程序的命令排队行为远远超出了OpenGL的范围,您将永远无法知道驱动程序设置了多少帧以便继续工作。将回读间隔时间增加到3会有所帮助,但理想情况下,您要使用的是fence sync。
您可以在OpenGL中的命令流中插入一个同步对象,其唯一目的是一旦所有命令完成后都会发出信号。检查此对象的信号状态不会以任何方式拖延流水线,并且可以让您快速判断GPU上前一帧的命令何时完成,并且像素缓冲区的回读不会引入任何CPU/GPU同步问题。
当然,如果你天真地增加缓冲区长度,会引入额外的延迟。一些API(例如Direct3D)使用FIFO队列来呈现帧渲染,这为队列中的每个缓冲区添加了1帧的延迟(在移到下一帧之前,必须显示每帧)。绝对不要将此设计用于视频应用,请放下晚期帧。 –