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. QOpenGLWidget and Threads
Qt 6.11 is out! See what's new in the release blog

QOpenGLWidget and Threads

Scheduled Pinned Locked Moved Unsolved General and Desktop
13 Posts 4 Posters 4.0k Views 1 Watching
  • 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.
  • R rtavakko

    @Kent-Dorfman Thanks for your reply! That does make sense and looking at the QOpenGLWidget threaded example it looks like that is what they're doing there using a worker thread. I think another alternative is to have multiple contexts on different threads sharign textures. Do you know by any chance how that could be done?

    Cheers!

    Kent-DorfmanK Offline
    Kent-DorfmanK Offline
    Kent-Dorfman
    wrote on last edited by
    #4

    @rtavakko Unfortunately my opengl experience is old school immediate mode programming. I have yet to do anything real using the modern shaper/pipeline architecture. I think it's a given that the framework properly handles multiple QOpenGLWidgets, but as you've discovered, when you try to offload to worker threads there are some gotchas: the big one being that you now have to explicitly give the correct worker thread the context (and it probably cannot be simultaneously shared). I suspect that the modern pipeline architecture cannot handle multiple threads using the pipeline concurrently.

    The dystopian literature that served as a warning in my youth has become an instruction manual in my elder years.

    1 Reply Last reply
    0
    • W Offline
      W Offline
      wesblake
      wrote on last edited by
      #5

      I've got a similar question. I am trying to get our current (threaded) QGLWidget migrated to a QOpenGLWidget. So far based on an example it's successfully working meaning it runs, the glClear is working from the other thread, as is the "WTF" message so I know it's rendering at X fps.
      What I can't figure out is how to get a QPainter to work!! I've tried everything. Our current QGLWidget based class renders QIMages at about 60 fps threaded, np using QPainter. I've read many things that seem to indicate I can still use QPainter in a separate thread as we are doing with QGLWidget, but I just can't get it to work. No matter how/where I create QPainter, it crashes. (cpp line 197)
      Am I forced to use opengl directly? How would I draw a QImage like that? Thanks!

      #ifndef GLWIDGET_H
      #define GLWIDGET_H
      
      #include "framequeue.h"
      
      #include <QOpenGLWidget>
      #include <QOpenGLContext>
      #include <QImage>
      #include <QThread>
      #include <QMutex>
      #include <QList>
      #include <QWaitCondition>
      
      class GLWidget;
      
      class Renderer : public QObject
      {
          Q_OBJECT
      
      public:
          Renderer(GLWidget *);
          ~Renderer();
          void lockRenderer() { m_renderMutex.lock(); }
          void unlockRenderer() { m_renderMutex.unlock(); }
          QMutex *grabMutex() { return &m_grabMutex; }
          QWaitCondition *grabCond() { return &m_grabCond; }
          void prepareExit() { m_exiting = true; m_grabCond.wakeAll(); }
      
          FrameQueue *selfView;
          QList<FrameQueue*> remoteViewList;
      
      signals:
          void contextWanted();
      
      public slots:
          void start();
          void stop();
          void pause();
          void resume();
          void resumeToClose();
          void render();
      
      private:
          static const int animationFPS = 30;
          static const int pipWidth = 160;
          static const int pipHeight = 120;
      
          bool m_inited;
          GLWidget *m_glwidget;
          QMutex m_renderMutex;
          QMutex m_grabMutex;
          QWaitCondition m_grabCond;
          bool m_exiting;
      
          bool pauseRendering;
      };
      
      class GLWidget : public QOpenGLWidget
      {
          Q_OBJECT
      public:
          GLWidget(QWidget *parent);
          ~GLWidget();
          void stopRendering();
          void startRendering();
          void pauseRendering();
          void resumeRendering();
          void resumeForClose();
          bool isStarted();
      
      protected:
          void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE { }
      
      signals:
          void renderRequested();
      
      public slots:
          void grabContext();
      
      private slots:
          void onAboutToCompose();
          void onFrameSwapped();
          void onAboutToResize();
          void onResized();
      
      private:
          QThread *m_thread;
          Renderer *m_renderer;
          bool m_isStarted;
      };
      
      #endif // GLWIDGET_H
      
      
      #include "glwidget.h"
      
      #include <QGuiApplication>
      #include <QPainter>
      #include <QDebug>
      
      //Leak detection
      #ifdef Q_OS_WIN32
      #ifdef QT_DEBUG
      #include "debugtools/reportinghook.h"
      #include "debugtools/setdebugnew.h"
      #define new DEBUG_NEW
      #endif
      #endif
      
      GLWidget::GLWidget(QWidget *parent)
          : QOpenGLWidget(parent)
          , m_isStarted(false)
      {
          connect(this, &QOpenGLWidget::aboutToCompose, this, &GLWidget::onAboutToCompose);
          connect(this, &QOpenGLWidget::frameSwapped, this, &GLWidget::onFrameSwapped);
          connect(this, &QOpenGLWidget::aboutToResize, this, &GLWidget::onAboutToResize);
          connect(this, &QOpenGLWidget::resized, this, &GLWidget::onResized);
      
          m_thread = new QThread;
          m_renderer = new Renderer(this);
          m_renderer->moveToThread(m_thread);
          connect(m_thread, &QThread::finished, m_renderer, &QObject::deleteLater);
      
          connect(this, &GLWidget::renderRequested, m_renderer, &Renderer::render);
          connect(m_renderer, &Renderer::contextWanted, this, &GLWidget::grabContext);
      }
      
      GLWidget::~GLWidget()
      {
      //    stopRendering();
      }
      
      void GLWidget::stopRendering()
      {
          m_renderer->prepareExit();
          m_thread->quit();
          m_thread->wait();
          delete m_thread;
      }
      
      void GLWidget::startRendering()
      {
          m_thread->start();
          m_isStarted = true;
          QMetaObject::invokeMethod(m_renderer, "start");
      }
      
      void GLWidget::pauseRendering()
      {
          QMetaObject::invokeMethod(m_renderer, "pause");
      }
      
      void GLWidget::resumeRendering()
      {
          QMetaObject::invokeMethod(m_renderer, "resume");
      }
      
      void GLWidget::resumeForClose()
      {
          QMetaObject::invokeMethod(m_renderer, "resumeToClose");
      }
      
      bool GLWidget::isStarted()
      {
          return m_isStarted;
      }
      
      void GLWidget::onAboutToCompose()
      {
          //We are on the gui thread here. Composition is about to begin, wait until the render thread finishes.
          m_renderer->lockRenderer();
      }
      
      void GLWidget::onFrameSwapped()
      {
          m_renderer->unlockRenderer();
          //Assuming a blocking swap, our animation is driven purely by the vsync in this example.
          emit renderRequested();
      }
      
      void GLWidget::onAboutToResize()
      {
          m_renderer->lockRenderer();
      }
      
      void GLWidget::onResized()
      {
          m_renderer->unlockRenderer();
      }
      
      void GLWidget::grabContext()
      {
          m_renderer->lockRenderer();
          QMutexLocker lock(m_renderer->grabMutex());
          context()->moveToThread(m_thread);
          m_renderer->grabCond()->wakeAll();
          m_renderer->unlockRenderer();
      }
      
      Renderer::Renderer(GLWidget *w)
          : m_inited(false),
            m_glwidget(w),
            m_exiting(false),
            pauseRendering(false)
      {
          selfView = new FrameQueue("selfView");
          long lineCount = 4;
          int iRet = HAPIGetPropertyInt(catPhone, propPhoneCallsLimit, &lineCount);
          QString remoteName;
          for(int i=0; i<lineCount; i++){
              remoteViewList.append(new FrameQueue(remoteName.sprintf("remoteView%i",i)));
          }
      }
      
      Renderer::~Renderer()
      {
          if(selfView)
              delete selfView;
          for(int i=0; i<remoteViewList.size(); i++){
              FrameQueue *curQueue = remoteViewList.at(i);
              if(curQueue)
                  delete curQueue;
          }
      }
      
      void Renderer::start()
      {
          HAPIRegisterSelfView(selfView);
      }
      
      void Renderer::stop()
      {
          HAPIUnregisterSelfView(selfView);
      }
      
      void Renderer::pause()
      {
          pauseRendering = true;
          HAPIUnregisterSelfView(selfView);
      }
      
      void Renderer::resume()
      {
          pauseRendering = false;
          HAPIRegisterSelfView(selfView);
      }
      
      void Renderer::resumeToClose()
      {
          pauseRendering = false;
      }
      
      void Renderer::render()
      {
          if(m_exiting)
              return;
      
          QOpenGLContext *ctx = m_glwidget->context();
          if(!ctx)//QOpenGLWidget not yet initialized
              return;
      
          //Grab the context.
          m_grabMutex.lock();
          emit contextWanted();
          m_grabCond.wait(&m_grabMutex);
          QMutexLocker lock(&m_renderMutex);
          m_grabMutex.unlock();
      
          if(m_exiting)
              return;
      
          Q_ASSERT(ctx->thread() == QThread::currentThread());
      
          //Make the context (and an offscreen surface) current for this thread.
          //The QOpenGLWidget's fbo is bound in the context.
          m_glwidget->makeCurrent();
      
          if(!m_inited) {
              m_inited = true;
      //        initializeOpenGLFunctions();
          }
      
          glClearColor(0.27f, 0.26f, 0.26f, 1.0f);
          glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
      
          if(pauseRendering){
              return;
          } else {
              //TODO HERE
              //How do I get QPainter here?! The context is moved yet I'm getting errors!
      //            QPainter p(m_glwidget);
      
              qDebug() << "WTF";
          }
      
          //Make no context current on this thread and move the QOpenGLWidget's context back to the gui thread.
          m_glwidget->doneCurrent();
          ctx->moveToThread(qGuiApp->thread());
      
          //Schedule composition. Note that this will use QueuedConnection, meaning that update() will be invoked on the gui thread.
          QMetaObject::invokeMethod(m_glwidget, "update");
      }
      
      
      1 Reply Last reply
      0
      • W Offline
        W Offline
        wesblake
        wrote on last edited by
        #6

        Bump. I've done everything and can't figure this out. I'm about to try a non-threaded QOpenGLWidget because it's my only choice at this point but I'm afraid it will impact performance too much.

        R 1 Reply Last reply
        0
        • W wesblake

          Bump. I've done everything and can't figure this out. I'm about to try a non-threaded QOpenGLWidget because it's my only choice at this point but I'm afraid it will impact performance too much.

          R Offline
          R Offline
          rtavakko
          wrote on last edited by
          #7

          @wesblake The topics are not directly related so it would've probably helped to start a new one.

          What error are you getting?

          I'm coming more and more to the conclusion that a single thread is the way to go most of the time.

          W 1 Reply Last reply
          0
          • R rtavakko

            @wesblake The topics are not directly related so it would've probably helped to start a new one.

            What error are you getting?

            I'm coming more and more to the conclusion that a single thread is the way to go most of the time.

            W Offline
            W Offline
            wesblake
            wrote on last edited by
            #8

            @rtavakko said in QOpenGLWidget and Threads:

            @wesblake The topics are not directly related so it would've probably helped to start a new one.

            What error are you getting?

            I'm coming more and more to the conclusion that a single thread is the way to go most of the time.

            Sorry, seemed similar but I can move it. Here is the error:
            QGL-Thread-error.png

            1 Reply Last reply
            0
            • R Offline
              R Offline
              rtavakko
              wrote on last edited by
              #9

              @wesblake said in QOpenGLWidget and Threads:

              QPainter p(m_glwidget);

              Try replacing:

              QPainter p(m_glwidget);

              with:

              QPainter p(this);

              1 Reply Last reply
              0
              • W Offline
                W Offline
                wesblake
                wrote on last edited by
                #10

                Thanks. I found a known bug in QOpenGLWidget now preventing me from using it. :( So, starting over. New trial, a regular QWidget, worker thread to resize and composite frames first.

                R 1 Reply Last reply
                0
                • W wesblake

                  Thanks. I found a known bug in QOpenGLWidget now preventing me from using it. :( So, starting over. New trial, a regular QWidget, worker thread to resize and composite frames first.

                  R Offline
                  R Offline
                  rtavakko
                  wrote on last edited by
                  #11

                  @wesblake Hmm. Which bug is this?

                  1 Reply Last reply
                  0
                  • W Offline
                    W Offline
                    wesblake
                    wrote on last edited by
                    #12

                    https://bugreports.qt.io/browse/QTBUG-49657
                    Duplicated ticket says fixed but it's not. It occurs exactly as described.

                    1 Reply Last reply
                    0
                    • Christian EhrlicherC Offline
                      Christian EhrlicherC Offline
                      Christian Ehrlicher
                      Lifetime Qt Champion
                      wrote on last edited by
                      #13

                      I'm pretty sure this is not your bug as long as you don't use qt5.6 and fullscreen mode. Please provide a compilable, minimal example to show your problem.

                      Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                      Visit the Qt Academy at https://academy.qt.io/catalog

                      1 Reply Last reply
                      1

                      • Login

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