[SOLVED] QOpenGLWidget 's surface not written by QOpenGLContext living in another thread



  • Hi all!
    I am writing a multi-threaded QtGUI application making use of a QOpenGLWidget derived class. My GL widget is used to show resources (VBOs and shaders) allocated in additional QOpenGLContexts (representing distinct "views"), shared with the widget's context(). Each time the user chooses a specific view from a combo list, I want the widget to make the appropriate context current in its own worker thread and draw the widget's surface from there.
    I attach some relevant portions of my classes here, where for testing purposes I am using only one shared context and triggering widget updates for clearing the background with another color on mouse move.

    class glview : public QOpenGLWidget
    {
        Q_OBJECT
    public:
        explicit glview();
        ...
        void initializeGL();
        void paintEvent(QPaintEvent* e);
        void mouseMoveEvent(QMouseEvent *e);
        QOpenGLContext *m_context;
        QSurface *m_surf;
        bool m_isrendering;
    signals:
        void requestDraw();
    public slots:
        void provideUpdate();
    };
    
    glview::glview()
    {
        setMouseTracking(true);
        m_isrendering = false;
    }
    
    void glview::initializeGL()
    {
        m_context = context()->currentContext();
        m_surf = context()->currentContext()->surface();
        QOpenGLFunctions f;
        f.initializeOpenGLFunctions();
        f.glClearColor(0.0,0.0,1.0,1.0);
    }
    
    void glview::paintEvent(QPaintEvent *e)
    {
    }
    
    void glview::mouseMoveEvent(QMouseEvent *e)
    {
        if (!m_isrendering)
        {
            m_context->doneCurrent();
            m_isrendering = true;
            emit requestDraw();
        }
    }
    
    void glview::provideUpdate()
    {
        m_isrendering = false;
        context()->makeCurrent(m_surf);
        emit update();
    }
    
    class objview : public QObject
    {
        Q_OBJECT
    public:
        objview(QOpenGLContext* c, QSurface* s, QSurfaceFormat format);
        ...
        QOpenGLContext* m_glcontext;
        QSurface* m_surf;
    public slots:
        void provideDraw();
    private:
        QSurfaceFormat m_format;
    signals:
        void requestUpdate();
    };
    
    objview::objview(QOpenGLContext* c, QSurface* s, QSurfaceFormat format)
        : m_format(format)
        , m_surf(s)
    {
        m_glcontext = new QOpenGLContext(this);
        m_glcontext->setShareContext(c);
        m_glcontext->setFormat(m_format);
        if (m_glcontext->create())
            qDebug() << "shared glcontext correcty created";
    }
    
    void objview::provideDraw()
    {
        if (!m_glcontext->makeCurrent(m_surf))
            return;
        QOpenGLFunctions f;
        f.initializeOpenGLFunctions();
        f.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
        f.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        m_glcontext->doneCurrent();
        emit requestUpdate();
    }
    

    To facilitate things, I create a glcontroller class endowing a QThread and a QOpenGLContext member variables. After creating the shared context in the main thread, I use the function moveToThread() to move the controller's context in the other thread event loop.

    class glcontroller : public QObject
    {
        Q_OBJECT
    public:
        glcontroller(QOpenGLContext* c, QSurface* s, QSurfaceFormat sf);
    ...    
        QThread m_thread;
        objview *m_ov;
    signals:
        void requestUpdate();
        void requestDraw();
    };
    
    glcontroller::glcontroller(QOpenGLContext* c, QSurface* s, QSurfaceFormat sf)
    {
        m_ov = new objview(c,s,sf);    
        m_ov->moveToThread(&m_thread);    
        emit m_thread.start();
        connect(this,&glcontroller::requestDraw,
                m_ov,&objview::provideDraw);
        connect(m_ov,&objview::requestUpdate,
                this,&glcontroller::requestUpdate);
    }
    

    I define, implement and connect what I believe is an appropriate set of Signals and Slots to sync the worker thread and the main thread. Anyway, it seems that my widget is always showing a plain fill based on the glClearColor() I used in initializedGL().

    Originally, I thought the problem was caused by the use of paintGL in my main thread calling makeCurrent() asynchronously while my worker thread was still rendering the frame buffer. Therefore, as suggested on the QOpenGLWidget's reference page, I reimplemented paintEvent(QPaintEvent*) to do nothing and now the debugger shows a correct processing sequence of my update and rendering slots.
    Anyway, the widget's framebuffer doesn't seem to be touched in anyway by my worker thread. Could it be that I have to bind a QOffscreeenSurface and the perform some kind of swapBuffer (although I read that the new QOpenGL* classes do not require nor allow framebuffer swapping)?

    I have been struggling for several days on this problem and I would be very happy if anyone could explain me why my code is not working! Thank you very much! :-)

    Mario



  • Ok after several readings and tests, I found a solution myself. I simply had to bind the widget's framebuffer's when repainting from the worker thread and call glFlush().

    I hope it helps others dealing with the same or similar issues.

    Mario



  • I have followed your solution but still have the same problem. I am binding the defaultFrameBufferObject() accessed via the shared context in my rendering class but it's still not drawing on-screen. Could you please update the code to include your implementation of the binding operation?


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.