Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QWindow on a non-GUI thread



  • Hi guys,

    I have a project where there is a subclass of QWindow rendering content on a thread. It runs well other than the assert error I get when rendering.:

    Program: ..._0_MSVC2017_64bit-Debug\source\GLRenderer\debug\Qt5Cored.dll
    Module: 5.12.0
    File: kernel\qcoreapplication.cpp
    Line: 578
    
    ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread 0x0x28a6e997e10. Receiver '' (of type 'OpenGLRenderWindow') was created in thread 0x0x28a7bc0d190", file kernel\qcoreapplication.cpp, line 578
    

    Header file:

    #ifndef OPENGLRENDERWINDOW_H
    #define OPENGLRENDERWINDOW_H
    
    #include <QWindow>
    #include <QOpenGLContext>
    
    class OpenGLRenderWindow : public QWindow
    {
    public:
        OpenGLRenderWindow(QScreen* outputScreen,
    const QSurfaceFormat& surfaceFormat,
    QOpenGLContext* sharedContext);
    
        virtual ~OpenGLRenderWindow();
    
        bool event(QEvent *event) override;
        void exposeEvent(QExposeEvent* event) override;
    
        void resizeEvent(QResizeEvent *event) override;
    
        const QSurfaceFormat& getOpenGLFormat();
        QOpenGLContext* getOpenGLContext();
    
    protected:
        void swapSurfaceBuffers();
    
        bool makeContextCurrent();
        void doneContextCurrent();
    
        QSurfaceFormat openGLFormat;
        QOpenGLContext* openGLContext;
    
    public slots:
        void renderFrame();
    };
    
    #endif // OPENGLRENDERWINDOW_H
    

    CPP:

    #include "openglrenderwindow.h"
    
    OpenGLRenderWindow::OpenGLRenderWindow(
    QScreen *outputScreen,
    const QSurfaceFormat &surfaceFormat,
    QOpenGLContext *sharedContext) :
        QWindow(outputScreen),
        openGLFormat(surfaceFormat),
        openGLContext(nullptr)
    {
        //Create surface
        setSurfaceType(QWindow::OpenGLSurface);
        setFormat(openGLFormat);
    
        //Allocate memory for the context object and prepare to create it
        openGLContext = new QOpenGLContext(this);
        openGLContext->setFormat(openGLFormat);
        if(sharedContext)
            openGLContext->setShareContext(sharedContext);
    
        //Make sure the context is created & is sharing resources with the shared context
        bool contextCreated = openGLContext->create();
        assert(contextCreated);
    
        if(sharedContext)
        {
            bool sharing = QOpenGLContext::areSharing(openGLContext,sharedContext);
            assert(sharing);
        }
    }
    
    OpenGLRenderWindow::~OpenGLRenderWindow()
    {
    }
    
    bool OpenGLRenderWindow::event(QEvent *event)
    {
        switch (event->type())
        {
            case QEvent::UpdateRequest:
            renderFrame();
            return true;
            default:
            return QWindow::event(event);
        }
    }
    
    void OpenGLRenderWindow::exposeEvent(QExposeEvent *event)
    {
        if(isExposed())
            renderFrame();
    }
    
    void OpenGLRenderWindow::resizeEvent(QResizeEvent *event)
    {
        if(!makeContextCurrent())
            return;
    
        unsigned int w = event->size().width();
        unsigned int h = event->size().height();
    
        QWindow::resize(QSize(w,h));
    
        swapSurfaceBuffers();
        doneContextCurrent();
    }
    
    const QSurfaceFormat &OpenGLRenderWindow::getOpenGLFormat()
    {
        return openGLFormat;
    }
    
    QOpenGLContext *OpenGLRenderWindow::getOpenGLContext()
    {
        return openGLContext;
    }
    
    void OpenGLRenderWindow::swapSurfaceBuffers()
    {
        openGLContext->swapBuffers(this);
    }
    
    bool OpenGLRenderWindow::makeContextCurrent()
    {
        //Initialize OpenGL context & other resources
        return openGLContext->makeCurrent(this);
    }
    
    void OpenGLRenderWindow::doneContextCurrent()
    {
        openGLContext->doneCurrent();
    }
    
    void OpenGLRenderWindow::renderFrame()
    {
        if(!isExposed())
            return;
    
        if(!makeContextCurrent())
            return;
    
        //Rendering code here
    
        swapSurfaceBuffers();
        doneContextCurrent();
    }
    
    
    displayWindow = new OpenGLRenderWindow(mainOutputScreen,surfaceFormat,nullptr);
    displayWindow->show();
    displayWindow->moveToThread(thread);
    

    I have this exact same setup for an offscreen renderer subclassing QOffscreenSurface which works without issues (difference being the I call create() in the constructor of that class).

    It seems like there is no way around getting resize and expose events for a QWindow, but I don't quite know how to fix this.

    Is there something wrong with the order I create and move the resources?
    Is there a way to disable events to this window? It renders pretty well despite of the pop-up error.


  • Lifetime Qt Champion

    Hi,

    IIRC, you can move your QOpenGLContext to a different thread but not the QWindow.



  • @SGaist Thanks for replying.

    Are you aware of any alternatives to QWindow where using a thread is possible?

    I do most of my rendering on a QOffscreenSurface subclass and the QWindow is only displaying the result. But since it sits on the GUI thread, any events there cause it to drop frames and stall.


  • Lifetime Qt Champion

    Maybe the QQuickRenderControl example might help.



  • @SGaist Thanks! I will play around with that example