Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. vsync: missing rendered frames
QtWS25 Last Chance

vsync: missing rendered frames

Scheduled Pinned Locked Moved Solved General and Desktop
framebuffervsync
12 Posts 3 Posters 6.1k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • G Offline
    G Offline
    geissenpeter
    wrote on 16 May 2016, 11:26 last edited by geissenpeter
    #1

    Hello,
    for a scientific task, flickering areas with a stable frequency (max. 60 Hz), shall be displayed on the screen. I tried to achieve a stable stimulus visualization using Qt 5.6.

    According to this blog entry and many other online recommendations, I realized three different approaches: Inheriting from QWindow Class, QOpenGLWindow Class and QRasterWindow Class. I wanted to get the accurate advantage of vsync and avoid the usage of QTimer.

    The flickering area can be displayed. Also a stable time period between the frames has been measured with 16 up to 17 ms. But every few seconds some missed frames are spotted. It can be seen very clearly that there is no stable visualization of the stimulus. The same effect occurs on all three approaches.

    Have I done the implementation of my code properly or do better solutions exist? If the code is adequate for its purpose do I have to assume that it is a hardware problem? Could it be that difficult then, to display a simple flickering area?

    Thank you very much for helping me!

    As Example you can see my code for QWindow Class here:

    Window::Window(QWindow *parent)
    : m_context(0)
    , m_paintDevice(0)
    , m_bFlickerState(true){
    setSurfaceType(QSurface::OpenGLSurface);
    
    QSurfaceFormat format;
    format.setDepthBufferSize(24);
    format.setStencilBufferSize(8);
    format.setSwapInterval(1);
    this->setFormat(format);
    
    m_context.setFormat(format);
    m_context.create();
    }
    
    void Window::render(){
    
    //calculating elapsed time between frames
    m_t1 = QTime::currentTime();
    int curDelta = m_t0.msecsTo(m_t1);
    m_t0 = m_t1;
    qDebug()<< curDelta;
    
    m_context.makeCurrent(this);
    
    if (!m_paintDevice)
        m_paintDevice = new QOpenGLPaintDevice;
    if (m_paintDevice->size() != size())
        m_paintDevice->setSize(size());
    
    QPainter p(m_paintDevice);
    // draw using QPainter
    if(m_bFlickerState){
        p.setBrush(Qt::white);
        p.drawRect(0,0,this->width(),this->height());
    }
    p.end();
    m_bFlickerState = !m_bFlickerState;
    m_context.swapBuffers(this);
    
    // animate continuously: schedule an update
    QCoreApplication::postEvent( this, new QEvent(QEvent::UpdateRequest));
    }
    
    
    1 Reply Last reply
    0
    • K Offline
      K Offline
      kshegunov
      Moderators
      wrote on 16 May 2016, 11:58 last edited by kshegunov
      #2

      Hello,

      this->setFormat(format);
      

      this is the same as:

      setFormat(format)
      

      the context (this) is resolved automatically. This is not an error by any means, only a note.

      if (!m_paintDevice)
          m_paintDevice = new QOpenGLPaintDevice;
      

      this part I don't understand. Your QWindow (if Window is derived fromQWindow) is already a paint device, so why do you need another? Could you provide the class declaration as well? This also goes to Window::render, why do that. I'd derive from QOpenGLWindow and do the painting in a QOpenGLWindow::paintGL and implement the other needed protected methods as well - initializing the GL context and resize at least.

      QCoreApplication::postEvent( this, new QEvent(QEvent::UpdateRequest));
      

      This is the same as calling requestUpdate().

      Kind regards.

      Read and abide by the Qt Code of Conduct

      G 1 Reply Last reply 16 May 2016, 12:37
      0
      • K kshegunov
        16 May 2016, 11:58

        Hello,

        this->setFormat(format);
        

        this is the same as:

        setFormat(format)
        

        the context (this) is resolved automatically. This is not an error by any means, only a note.

        if (!m_paintDevice)
            m_paintDevice = new QOpenGLPaintDevice;
        

        this part I don't understand. Your QWindow (if Window is derived fromQWindow) is already a paint device, so why do you need another? Could you provide the class declaration as well? This also goes to Window::render, why do that. I'd derive from QOpenGLWindow and do the painting in a QOpenGLWindow::paintGL and implement the other needed protected methods as well - initializing the GL context and resize at least.

        QCoreApplication::postEvent( this, new QEvent(QEvent::UpdateRequest));
        

        This is the same as calling requestUpdate().

        Kind regards.

        G Offline
        G Offline
        geissenpeter
        wrote on 16 May 2016, 12:37 last edited by geissenpeter
        #3

        @kshegunov
        (I looked it up but I couldn't see the QWindow is a paint device but QOpenGLWindow does inherit from QPaintDeviceWindow class!)

        Thank you very much for the fast response!

        If you would choose QOpenGLWIndow, I provide you with the declaration and Implementation of my QOpenGLWindow approach. Like I said, I have the same problem here: frames are clearly missing!
        Also I can't display this class in Fullscreen-mode on another screen: This error message then is generated:

        QPainter::begin(): QOpenGLPaintDevice's context needs to be current
        QPainter::begin(): Returned false
        QPainter::end: Painter not active, aborted
        

        Here is now my full code:
        mainwindow.cpp:

        #include "mainwindow.h"
        #include "QPainter"
        
        #include <QOpenGLWindow>
        
        
        MainWindow::MainWindow(QOpenGLWindow *parent)
            : m_bFlickerState(true)
        {
            QSurfaceFormat format;
            format.setDepthBufferSize(24);
            format.setStencilBufferSize(8);
            format.setSwapInterval(1);
            setFormat(format);
            //continous update when frame was displayed
            connect(this, SIGNAL(frameSwapped()), this, SLOT(update()));
        }
        
        void MainWindow::resizeGL(int w, int h) {
        
        }
        
        void MainWindow::paintGL() {
            //calculating FPS
            m_t1 = QTime::currentTime();
            int curDelta = m_t0.msecsTo(m_t1);
            m_t0 = m_t1;
            qDebug()<< curDelta;
        //    QOpenGLFunctions *f = context()->functions();
        //    f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        // issue some native OpenGL commands
            QPainter p(this);
            // draw using QPainter
            if(m_bFlickerState){
                    //f->glClearColor(1,1,1,1);
                p.fillRect(0,0,100,100,Qt::white);
            }
            else{
                //f->glClearColor(0,0,0,0);
                p.fillRect(0,0,100,100,Qt::black);
        
            }
            p.end();
            m_bFlickerState = !m_bFlickerState;
        }
        
        

        mainwindow.h:

        #ifndef MAINWINDOW_H
        #define MAINWINDOW_H
        
        #include <QtCore>
        #include <QOpenGLPaintDevice>
        #include <QOpenGLFunctions>
        #include <QOpenGLWindow>
        
        class MainWindow : public QOpenGLWindow, protected QOpenGLFunctions
        {
            Q_OBJECT
        
        public:
            explicit MainWindow(QOpenGLWindow *parent = 0 );
            ~MainWindow();
        
        protected:
            void resizeGL(int w, int h);
            void paintGL();
        
        private:
            QTime   m_t0;
            QTime   m_t1;
            bool m_bFlickerState;
            QOpenGLPaintDevice *m_paintDevice;
            QTimer  timer;
        };
        
        #endif // MAINWINDOW_H
        

        main.cpp:

        #include "mainwindow.h"
        #include <QApplication>
        #include <QScreen>
        #include <QVBoxLayout>
        #include <QLayout>
        
        int main(int argc, char *argv[])
        {
            QApplication a(argc, argv);
        
            QScreen *screen = QGuiApplication::screens()[1];
        
            //setup the window
            MainWindow w;
            w.setScreen(screen);
            w.showFullScreen();
        
            return a.exec();
        }
        
        K 1 Reply Last reply 16 May 2016, 17:16
        0
        • G geissenpeter
          16 May 2016, 12:37

          @kshegunov
          (I looked it up but I couldn't see the QWindow is a paint device but QOpenGLWindow does inherit from QPaintDeviceWindow class!)

          Thank you very much for the fast response!

          If you would choose QOpenGLWIndow, I provide you with the declaration and Implementation of my QOpenGLWindow approach. Like I said, I have the same problem here: frames are clearly missing!
          Also I can't display this class in Fullscreen-mode on another screen: This error message then is generated:

          QPainter::begin(): QOpenGLPaintDevice's context needs to be current
          QPainter::begin(): Returned false
          QPainter::end: Painter not active, aborted
          

          Here is now my full code:
          mainwindow.cpp:

          #include "mainwindow.h"
          #include "QPainter"
          
          #include <QOpenGLWindow>
          
          
          MainWindow::MainWindow(QOpenGLWindow *parent)
              : m_bFlickerState(true)
          {
              QSurfaceFormat format;
              format.setDepthBufferSize(24);
              format.setStencilBufferSize(8);
              format.setSwapInterval(1);
              setFormat(format);
              //continous update when frame was displayed
              connect(this, SIGNAL(frameSwapped()), this, SLOT(update()));
          }
          
          void MainWindow::resizeGL(int w, int h) {
          
          }
          
          void MainWindow::paintGL() {
              //calculating FPS
              m_t1 = QTime::currentTime();
              int curDelta = m_t0.msecsTo(m_t1);
              m_t0 = m_t1;
              qDebug()<< curDelta;
          //    QOpenGLFunctions *f = context()->functions();
          //    f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
          // issue some native OpenGL commands
              QPainter p(this);
              // draw using QPainter
              if(m_bFlickerState){
                      //f->glClearColor(1,1,1,1);
                  p.fillRect(0,0,100,100,Qt::white);
              }
              else{
                  //f->glClearColor(0,0,0,0);
                  p.fillRect(0,0,100,100,Qt::black);
          
              }
              p.end();
              m_bFlickerState = !m_bFlickerState;
          }
          
          

          mainwindow.h:

          #ifndef MAINWINDOW_H
          #define MAINWINDOW_H
          
          #include <QtCore>
          #include <QOpenGLPaintDevice>
          #include <QOpenGLFunctions>
          #include <QOpenGLWindow>
          
          class MainWindow : public QOpenGLWindow, protected QOpenGLFunctions
          {
              Q_OBJECT
          
          public:
              explicit MainWindow(QOpenGLWindow *parent = 0 );
              ~MainWindow();
          
          protected:
              void resizeGL(int w, int h);
              void paintGL();
          
          private:
              QTime   m_t0;
              QTime   m_t1;
              bool m_bFlickerState;
              QOpenGLPaintDevice *m_paintDevice;
              QTimer  timer;
          };
          
          #endif // MAINWINDOW_H
          

          main.cpp:

          #include "mainwindow.h"
          #include <QApplication>
          #include <QScreen>
          #include <QVBoxLayout>
          #include <QLayout>
          
          int main(int argc, char *argv[])
          {
              QApplication a(argc, argv);
          
              QScreen *screen = QGuiApplication::screens()[1];
          
              //setup the window
              MainWindow w;
              w.setScreen(screen);
              w.showFullScreen();
          
              return a.exec();
          }
          
          K Offline
          K Offline
          kshegunov
          Moderators
          wrote on 16 May 2016, 17:16 last edited by
          #4

          @geissenpeter said:

          I looked it up but I couldn't see the QWindow is a paint device but QOpenGLWindow does inherit from QPaintDeviceWindow class!

          Yes indeed, that's a mistake on my part.

          I provide you with the declaration and Implementation of my QOpenGLWindow approach

          It looks good to me.

          Like I said, I have the same problem here: frames are clearly missing!

          The only thing I could think of is that the paint events might be getting compressed and some of the updates are skipped. Perhaps you could attach an additional receiver for the frameSwapped signal and see if that's the case - you'd expect to see update request and then a repaint and then another update request etc. If that's not the case, then this might very well be the reason.

          Kind regards.

          Read and abide by the Qt Code of Conduct

          G 1 Reply Last reply 16 May 2016, 20:26
          0
          • K kshegunov
            16 May 2016, 17:16

            @geissenpeter said:

            I looked it up but I couldn't see the QWindow is a paint device but QOpenGLWindow does inherit from QPaintDeviceWindow class!

            Yes indeed, that's a mistake on my part.

            I provide you with the declaration and Implementation of my QOpenGLWindow approach

            It looks good to me.

            Like I said, I have the same problem here: frames are clearly missing!

            The only thing I could think of is that the paint events might be getting compressed and some of the updates are skipped. Perhaps you could attach an additional receiver for the frameSwapped signal and see if that's the case - you'd expect to see update request and then a repaint and then another update request etc. If that's not the case, then this might very well be the reason.

            Kind regards.

            G Offline
            G Offline
            geissenpeter
            wrote on 16 May 2016, 20:26 last edited by
            #5

            @kshegunov said:

            some of the updates are skipped

            I think this can't be, because the time measured between the update calls is always about 16 to 18 ms. Could a problem with the frame buffer occur instead?

            Also I have a problem with the QPainter: When my class is displayed in Fullscreen mode (like shown in the code above) then this error is generated. I have looked it up, but haven't found a solution yet

            QPainter::begin(): QOpenGLPaintDevice's context needs to be current
            QPainter::begin(): Returned false
            QPainter::end: Painter not active, aborted
            

            thank you for your fast response!

            K 1 Reply Last reply 16 May 2016, 22:23
            0
            • G geissenpeter
              16 May 2016, 20:26

              @kshegunov said:

              some of the updates are skipped

              I think this can't be, because the time measured between the update calls is always about 16 to 18 ms. Could a problem with the frame buffer occur instead?

              Also I have a problem with the QPainter: When my class is displayed in Fullscreen mode (like shown in the code above) then this error is generated. I have looked it up, but haven't found a solution yet

              QPainter::begin(): QOpenGLPaintDevice's context needs to be current
              QPainter::begin(): Returned false
              QPainter::end: Painter not active, aborted
              

              thank you for your fast response!

              K Offline
              K Offline
              kshegunov
              Moderators
              wrote on 16 May 2016, 22:23 last edited by
              #6

              @geissenpeter

              I think this can't be, because the time measured between the update calls is always about 16 to 18 ms.

              This is of no consequence. Look here: http://doc.qt.io/qt-5/eventsandfilters.html#sending-events
              update() posts an event on the queue for later processing and this event is subject to being compressed on occasion. I don't know if you can force the repaint however.

              Could a problem with the frame buffer occur instead?

              I'm by no means an expert, but it seems doubtful.

              Also I have a problem with the QPainter: When my class is displayed in Fullscreen mode (like shown in the code above) then this error is generated. I have looked it up, but haven't found a solution yet

              Possibly you're getting a paint event when the windows is not exposed. But I'm just guessing here.

              Read and abide by the Qt Code of Conduct

              1 Reply Last reply
              1
              • G Offline
                G Offline
                geissenpeter
                wrote on 17 May 2016, 10:53 last edited by geissenpeter
                #7

                @kshegunov
                I realized your approach: Every frameSwapped() signal is following an update request. So this code seems to do its job. I don't know what possible errors I could look for anymore...
                Could other solutions exist for this (in my opinion) quite simple task?

                K 1 Reply Last reply 17 May 2016, 10:59
                0
                • G geissenpeter
                  17 May 2016, 10:53

                  @kshegunov
                  I realized your approach: Every frameSwapped() signal is following an update request. So this code seems to do its job. I don't know what possible errors I could look for anymore...
                  Could other solutions exist for this (in my opinion) quite simple task?

                  K Offline
                  K Offline
                  kshegunov
                  Moderators
                  wrote on 17 May 2016, 10:59 last edited by
                  #8

                  @geissenpeter

                  I don't know what possible errors I could look for anymore...

                  Sadly, I don't know either.

                  Could other solutions exist for this (in my opinion) quite simple task?

                  I'm sure there are other solutions, but I don't know of any better way. Perhaps one of our more GL-versed members, like @Chris-Kawa, might take a look and suggest something.

                  Read and abide by the Qt Code of Conduct

                  1 Reply Last reply
                  0
                  • Chris KawaC Online
                    Chris KawaC Online
                    Chris Kawa
                    Lifetime Qt Champion
                    wrote on 17 May 2016, 18:19 last edited by Chris Kawa
                    #9

                    V-sync is hard ;) Basically it's fighting with the inherent noisiness of the system. If the output shows 16-17 ms then that's the problem. 17 ms is too much. That's the skipping you see.

                    Couple of things to reduce that noise:

                    • Don't do I/O in the render loop! qDebug()is I/O and it can block on all kinds of buffering shenanigans.
                    • Testing V-sync under a debugger is useless. Debugging introduces all kinds of noise into your app. You should be testing v-sync in Release mode without debugger attached.
                    • try not to use signals/slots/events if you can help it. They can be noisy i.e. call update() manually at the end of paintGL. You skip some overhead this way (not much but every bit counts).
                    • If all you need is a flickering screen avoid QPainter. It's not exactly slow, but drop into the begin() method of it and see how much it actually does. OpenGL has fast, dedicated facilities to fill the buffer with a color. You might as well use it.

                    Not directly related, but it will make your code cleaner:

                    • Use QElapsedTimer instead of manually calculating time intervals. Why re-invent the wheel.

                    Applying these bits I was able to remove the skipping from your example. Note that the skipping will occur in some circumstances, e.g. when you move/resize the window or when OS/other apps are busy doing something . You have no control over that.

                    Here are the modified code bits:

                    //mainwindow.h
                        ...
                        QElapsedTimer  m_timer;
                    };
                    
                    //mainwindow.cpp
                    MainWindow::MainWindow(QOpenGLWindow *parent) : m_bFlickerState(true)
                    {
                        QSurfaceFormat format;
                        format.setDepthBufferSize(24);
                        format.setStencilBufferSize(8);
                        format.setSwapInterval(1);
                        setFormat(format);
                        m_timer.start(); //note that it doesn't really start any timers, just records current time
                    }
                    
                    void MainWindow::paintGL()
                    {
                        int ms_elapsed = timer.restart(); //you can store it somewhere to analyze later 
                    
                        auto f = context()->functions();
                        if(m_bFlickerState)
                            f->glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
                        else
                            f->glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
                    
                        f->glClear(GL_COLOR_BUFFER_BIT);
                    
                        m_bFlickerState = !m_bFlickerState;
                        update(); //schedules next update directly, without going through signal dispatching
                    }
                    
                    K 1 Reply Last reply 17 May 2016, 18:31
                    1
                    • Chris KawaC Chris Kawa
                      17 May 2016, 18:19

                      V-sync is hard ;) Basically it's fighting with the inherent noisiness of the system. If the output shows 16-17 ms then that's the problem. 17 ms is too much. That's the skipping you see.

                      Couple of things to reduce that noise:

                      • Don't do I/O in the render loop! qDebug()is I/O and it can block on all kinds of buffering shenanigans.
                      • Testing V-sync under a debugger is useless. Debugging introduces all kinds of noise into your app. You should be testing v-sync in Release mode without debugger attached.
                      • try not to use signals/slots/events if you can help it. They can be noisy i.e. call update() manually at the end of paintGL. You skip some overhead this way (not much but every bit counts).
                      • If all you need is a flickering screen avoid QPainter. It's not exactly slow, but drop into the begin() method of it and see how much it actually does. OpenGL has fast, dedicated facilities to fill the buffer with a color. You might as well use it.

                      Not directly related, but it will make your code cleaner:

                      • Use QElapsedTimer instead of manually calculating time intervals. Why re-invent the wheel.

                      Applying these bits I was able to remove the skipping from your example. Note that the skipping will occur in some circumstances, e.g. when you move/resize the window or when OS/other apps are busy doing something . You have no control over that.

                      Here are the modified code bits:

                      //mainwindow.h
                          ...
                          QElapsedTimer  m_timer;
                      };
                      
                      //mainwindow.cpp
                      MainWindow::MainWindow(QOpenGLWindow *parent) : m_bFlickerState(true)
                      {
                          QSurfaceFormat format;
                          format.setDepthBufferSize(24);
                          format.setStencilBufferSize(8);
                          format.setSwapInterval(1);
                          setFormat(format);
                          m_timer.start(); //note that it doesn't really start any timers, just records current time
                      }
                      
                      void MainWindow::paintGL()
                      {
                          int ms_elapsed = timer.restart(); //you can store it somewhere to analyze later 
                      
                          auto f = context()->functions();
                          if(m_bFlickerState)
                              f->glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
                          else
                              f->glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
                      
                          f->glClear(GL_COLOR_BUFFER_BIT);
                      
                          m_bFlickerState = !m_bFlickerState;
                          update(); //schedules next update directly, without going through signal dispatching
                      }
                      
                      K Offline
                      K Offline
                      kshegunov
                      Moderators
                      wrote on 17 May 2016, 18:31 last edited by
                      #10

                      @Chris-Kawa
                      Chris, would running the rendering in a dedicated thread without event loop that forces rendering on a regular interval (for example a QThread subclass) be beneficial in this case? Or are the waiting routines (i.e. QThread::msleep/QThread::usleep/QSemaphore::tryAcquire) not precise enough for such things?

                      Again I'm just curious, so don't answer if you don't have the time. :)

                      Read and abide by the Qt Code of Conduct

                      1 Reply Last reply
                      1
                      • Chris KawaC Online
                        Chris KawaC Online
                        Chris Kawa
                        Lifetime Qt Champion
                        wrote on 17 May 2016, 18:45 last edited by
                        #11

                        @kshegunov
                        Any kind of sleeping/timer is no good for frame-perfect rendering. It doesn't matter in which thread you do it. There's just no solid way to synchronize these with the refresh rate.
                        You can't for example calculate "oh I rendered in 7ms so I have ~9.66ms left till the v-sync so I'll sleep for 9ms". That will miss most of the time. If any of these primitives is late even a microsecond for the v-sync it's all lost.
                        The only way to have any control over it is start drawing as soon as the v-sync is done and wait for the next one. Basically QOpenGLContext::swapBuffers is your only tool for that (done implicitly when you exit paintGL).

                        K 1 Reply Last reply 17 May 2016, 18:47
                        1
                        • Chris KawaC Chris Kawa
                          17 May 2016, 18:45

                          @kshegunov
                          Any kind of sleeping/timer is no good for frame-perfect rendering. It doesn't matter in which thread you do it. There's just no solid way to synchronize these with the refresh rate.
                          You can't for example calculate "oh I rendered in 7ms so I have ~9.66ms left till the v-sync so I'll sleep for 9ms". That will miss most of the time. If any of these primitives is late even a microsecond for the v-sync it's all lost.
                          The only way to have any control over it is start drawing as soon as the v-sync is done and wait for the next one. Basically QOpenGLContext::swapBuffers is your only tool for that (done implicitly when you exit paintGL).

                          K Offline
                          K Offline
                          kshegunov
                          Moderators
                          wrote on 17 May 2016, 18:47 last edited by
                          #12

                          @Chris-Kawa
                          Fair enough. Thanks for taking the time.

                          Cheers!

                          Read and abide by the Qt Code of Conduct

                          1 Reply Last reply
                          0

                          1/12

                          16 May 2016, 11:26

                          • Login

                          • Login or register to search.
                          1 out of 12
                          • First post
                            1/12
                            Last post
                          0
                          • Categories
                          • Recent
                          • Tags
                          • Popular
                          • Users
                          • Groups
                          • Search
                          • Get Qt Extensions
                          • Unsolved