Reputation: 239
I'm working on a project to display a video stream with OpenGL then display what's on the QOpenGLWidget
on an external screen. So what I did is display the stream on an OpenGL widget then I use glReadPixels
with two Pixel Pack Buffer Object to get the buffer and send it to the other screen. The problem is, I'm losing performance when using the PBOs compared to when I'm not.
Here is the interesting part of the code :
The code to create the frame that's going to be send on the external screen :
screenBuffer
is a memory buffer to store the frame from QOpenGLWidget
At this point in the code, the PBO have already been filled with data from the paintGL
function
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;
}
Initialization of the 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();
}
Here I use PBO with 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
is just a boolean that switches value to alternate between the first and second PBO.
So apparently, since I'm losing performance, I'm doing something wrong ? I'm Either using PBOs the wrong way, or I didn't understand properly the situation where I should use them.
Thanks
Upvotes: 1
Views: 836
Reputation: 43369
You have a general understanding (I think) of the purpose behind Pixel Buffer Objects, demonstrated by the ping-pong each frame between two different buffers. The real problem is that two pixel buffers likely is not enough to prevent stalling the pipeline.
Many drivers are configured out of the box to queue up commands for three frames, if you try to read-back the results of frame n
during frame n+2
you have effectively shortened the maximum pipeline depth. Commands setup for frame n+2
will not be allowed to proceed until the results from n
finish and are read-back.
The command queuing behavior of drivers is well beyond the scope of OpenGL, you will never be able to know how many frames the driver is setup to work ahead. Increasing the length of time between read-backs to 3 would help, but ideally what you want to use is a fence sync.
You can insert a sync object into the command stream in OpenGL, whose only purpose is to be signaled once all commands up to a certain point are finished. Checking the signaled state of this object is not going to stall the pipeline in any way and allows you to quickly tell when the commands from a previous frame have finished on the GPU and a read-back of your pixel buffer will not introduce any CPU/GPU synchronization problems.
Upvotes: 2