-1

I am developing on RedHat Linux, cat /etc/redhat-release:

    Red Hat Enterprise Linux Workstation release 7.2 (Maipo)

I am using Qt Creator 4.3.1:

    Based on Qt 5.9.1 (GCC 5.3.1 20160406 (Red Hat 5.3.1-6), 64 bit)

The project I'm developing is using Qt 5.6.2 GCC 64bit, the project has been developed with graphical objects derived from QWidget, this includes a live video stream.

Unfortunately we have experienced tearing in the video whilst it is playing back and this is also evident in other widgets displayed around the video, I believe this is because the video is not using vsync.

I believe using openGL will rectify this situation, the aim is to rewrite the widgets including the video playback using openGL. I've spent several days trying to find complete and working solutions but so far failed to find a complete and working solution.

I've been looking at using QOpenGLWidget, in a widget I am using to test:

    class clsElevStrip : public QOpenGLWidget, protected QOpenGLFunctions {
    Q_OBJECT

In the constructor, I set-up the format for offscreen rendering:

    //Create surface format for rendering offscreen
    mobjFormat.setDepthBufferSize(24);
    mobjFormat.setSamples(4);
    mobjFormat.setVersion(3, 0);
    mobjFormat.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
    setFormat(mobjFormat);

In the paintGL method:

    QOpenGLContext* pobjContext = context();
    QSurface* pobjSurface = pobjContext->surface();
    assert(pobjSurface != NULL);

    int intSB1 = pobjSurface->format().swapBehavior();
    qDebug() << (QString("paintGL:format: ")
               + QString::number(intSB1));
    pobjContext->makeCurrent(pobjSurface);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glBegin(GL_TRIANGLES);
    glColor3f(1.0, 0.0, 0.0);
    glVertex3f(-0.5, -0.5, 0);
    glColor3f(0.0, 1.0, 0.0);
    glVertex3f( 0.5, -0.5, 0);
    glColor3f(0.0, 0.0, 1.0);
    glVertex3f( 0.0,  0.5, 0);
    glEnd();

    pobjContext->swapBuffers(pobjSurface);

Nothing is visible on the main display, the debug statement shows the format as 2 (DoubleBuffering).

If I comment out the line in the constructor:

    setFormat(mobjFormat);

The debug statement shows the format as 0 (DefaultSwapBehavior). And the graphics are visible, what have I missed?

SPlatten
  • 5,334
  • 11
  • 57
  • 128
  • To save you later trouble when googling things: What you aim to do there is *double buffered* rendering, not off-screen rendering. Double buffering is, when you prepare an image in a back buffer and swap the back buffer to the screen scanout front buffer in the vertical retrace interval. *Off screen* rendering is, if you're rendering to a buffer that's not going to be shown directly on a display device, i.e. you intend to save the rendered image to a file without showing, or generating intermediary images that are post processed before shown. – datenwolf Aug 09 '17 at 11:18
  • @datenwolf, thank you, I've used offscreen rendering before to prepare graphics and then blit to the visible display (not using Qt or openGL), for our requirements the aim is to remove tearing in the video and graphics to give a smooth appearance. – SPlatten Aug 09 '17 at 11:20
  • Yes, that much has been clear. Using OpenGL and its swap interval extension (https://www.khronos.org/opengl/wiki/Swap_Interval) is certainly a viable way to get V-sync correct display updates (be wary of compositing WMs, though). However if your goal is displaying video, then one of the dedicated video presentation APIs of your platform might be a proper choice either. On X11 that'd be one of Xv or XVVA(VAAPI) or VDPAU. All of them have vsync control. – datenwolf Aug 09 '17 at 12:39
  • @datenwolf, thank you, can you please clarify with an example what you mean, I am using Qt and openGL on RedHat, I'm not sure what you mean by "On X11 that'd be one of Xv or XVVA(VAAPI) or VDPAU" ? At the moment, I don't have any visible video when DoubleBuffering is specified. – SPlatten Aug 09 '17 at 12:48
  • These are just alternative APIs, that you could use instead of OpenGL. – datenwolf Aug 09 '17 at 13:50
  • @datenwolf, thank you, but I'm sure there is a way to get the openGL wokring, I just need a good working example to look at, problem is I can't find one, one that is based on QOpenGLWidget, not Window and performs DoubleBuffering. – SPlatten Aug 09 '17 at 13:52

1 Answers1

0

The solution for your problem is simple:

Just do not all that QOpenGLGLContext jugging. The whole point of paintGL is, that this particular function is called inside a wrapper that already does all that context juggling for you. **There is no need to call makeCurrent or swapBuffers. Qt already does that for you!

From the Qt documentation

void QOpenGLWidget::paintGL()

This virtual function is called whenever the widget needs to be painted. Reimplement it in a subclass.

There is no need to call makeCurrent() because this has already been done when this function is called.

Before invoking this function, the context and the framebuffer are bound, and the viewport is set up by a call to glViewport(). No other state is set and no clearing or drawing is performed by the framework.

If you have just this as your paintGL it will show something, iff you have either a compatibility profile >=OpenGL-3.x context OR if you're using a <=OpenGL-2.x context. You're using the legacy fixed function pipeline there, which will not work with OpenGL-3.x core profile contexts!

void glwidget::paintGL(){
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glBegin(GL_TRIANGLES);
    glColor3f(1.0, 0.0, 0.0);
    glVertex3f(-0.5, -0.5, 0);
    glColor3f(0.0, 1.0, 0.0);
    glVertex3f( 0.5, -0.5, 0);
    glColor3f(0.0, 0.0, 1.0);
    glVertex3f( 0.0,  0.5, 0);
    glEnd();
}
datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • I thought the call to context() returns the context of the QOpenGLWidget class that paintGL is part of ? – SPlatten Aug 09 '17 at 13:59
  • According to http://doc.qt.io/qt-5/qopenglwidget.html#context, calling context returns a pointer to the context in the widget...not a new context. – SPlatten Aug 09 '17 at 14:01
  • I know this is true, because the context in paintGL has the same format as set in the contructor. – SPlatten Aug 09 '17 at 14:02
  • I've commented out both pobjContext->makeCurrent(pobjSurface); and pobjContext->swapBuffers(pobjSurface); but still nothing is displayed. So now my paintGL matches yours, but still nothing. – SPlatten Aug 09 '17 at 14:13
  • I'm not sure why but editing the constructor and changing the format settings to just: mobjFormat.setSamples(16); mobjFormat.setSwapBehavior(QSurfaceFormat::DoubleBuffer); and now it displays. – SPlatten Aug 09 '17 at 14:41