From 21aa22ba3fe2f6424742ba0a8fbf39937b81ad13 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Thu, 7 Mar 2013 10:20:19 +0100 Subject: [PATCH] Create BG context and fake surface on GUI thread MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This makes the example more messy and also makes it very hard to avoid the initial black frame, but it is not compliant with the limitations we experience on Windows. Task-number: QTBUG-30077 Change-Id: If5a6da16f8fc1269cd20d2aa1190588025203d4f Reviewed-by: Friedemann Kleint Reviewed-by: Samuel Rødal --- examples/quick/scenegraph/textureinthread/main.cpp | 32 ++++++++-- .../scenegraph/textureinthread/threadrenderer.cpp | 69 +++++++++++++++----- .../scenegraph/textureinthread/threadrenderer.h | 6 ++ 3 files changed, 85 insertions(+), 22 deletions(-) diff --git a/examples/quick/scenegraph/textureinthread/main.cpp b/examples/quick/scenegraph/textureinthread/main.cpp index e415d25..3286055 100644 --- a/examples/quick/scenegraph/textureinthread/main.cpp +++ b/examples/quick/scenegraph/textureinthread/main.cpp @@ -38,6 +38,8 @@ ** ****************************************************************************/ +#include + #include #include @@ -49,11 +51,31 @@ int main(int argc, char **argv) QGuiApplication app(argc, argv); qmlRegisterType("SceneGraphRendering", 1, 0, "Renderer"); + int execReturn = 0; + + { + QQuickView view; + + // Rendering in a thread introduces a slightly more complicated cleanup + // so we ensure that no cleanup of graphics resources happen until the + // application is shutting down. + view.setPersistentOpenGLContext(true); + view.setPersistentSceneGraph(true); + + view.setResizeMode(QQuickView::SizeRootObjectToView); + view.setSource(QUrl("qrc:///scenegraph/textureinsgnode/main.qml")); + view.show(); + + execReturn = app.exec(); + } - QQuickView view; - view.setResizeMode(QQuickView::SizeRootObjectToView); - view.setSource(QUrl("qrc:///scenegraph/textureinsgnode/main.qml")); - view.show(); + // As the render threads make use of our QGuiApplication object + // to clean up gracefully, wait for them to finish before + // QGuiApp is taken off the heap. + foreach (QThread *t, ThreadRenderer::threads) { + t->wait(); + delete t; + } - return app.exec(); + return execReturn; } diff --git a/examples/quick/scenegraph/textureinthread/threadrenderer.cpp b/examples/quick/scenegraph/textureinthread/threadrenderer.cpp index 90b6b49..7de1d29 100644 --- a/examples/quick/scenegraph/textureinthread/threadrenderer.cpp +++ b/examples/quick/scenegraph/textureinthread/threadrenderer.cpp @@ -46,10 +46,13 @@ #include #include +#include +#include #include #include +QList ThreadRenderer::threads; /* * The render thread shares a context with the scene graph and will @@ -60,37 +63,39 @@ class RenderThread : public QThread { Q_OBJECT public: - RenderThread(const QSize &size) + RenderThread(const QSize &size, QOpenGLContext *context) : m_renderFbo(0) , m_displayFbo(0) , m_logoRenderer(0) + , m_fakeSurface(0) , m_size(size) { - // Since we're using queued connections, we need affinity to the rendering thread. - moveToThread(this); + ThreadRenderer::threads << this; // Set up the QOpenGLContext to use for rendering in this thread. It is sharing // memory space with the GL context of the scene graph. This constructor is called // during updatePaintNode, so we are currently on the scene graph thread with the // scene graph's OpenGL context current. - QOpenGLContext *current = QOpenGLContext::currentContext(); m_context = new QOpenGLContext(); - m_context->setShareContext(current); - m_context->setFormat(current->format()); - m_context->create(); + m_context->setShareContext(context); + m_context->setFormat(context->format()); m_context->moveToThread(this); - // We need a non-visible surface to make current... - m_fakeSurface = new QWindow(); - m_fakeSurface->setGeometry(0, 0, 64, 64); - m_fakeSurface->setSurfaceType(QWindow::OpenGLSurface); - m_fakeSurface->setFormat(current->format()); + // We need a non-visible surface to make current in the other thread + // and QWindows must be created and managed on the GUI thread. + m_fakeSurface = new QOffscreenSurface(); + m_fakeSurface->setFormat(context->format()); m_fakeSurface->create(); } + void setSurface(QOffscreenSurface *surface) { m_fakeSurface = surface; } + public slots: void renderNext() { + if (!m_context->isValid()) + m_context->create(); + m_context->makeCurrent(m_fakeSurface); if (!m_renderFbo) { @@ -119,6 +124,23 @@ public slots: emit textureReady(m_displayFbo->texture(), m_size); } + void shutDown() + { + m_context->makeCurrent(m_fakeSurface); + delete m_renderFbo; + delete m_displayFbo; + delete m_logoRenderer; + m_context->doneCurrent(); + delete m_context; + + // schedule this to be deleted only after we're done cleaning up + m_fakeSurface->deleteLater(); + + // Stop event processing, move the thread to GUI and make sure it is deleted. + exit(); + moveToThread(QGuiApplication::instance()->thread()); + } + signals: void textureReady(int id, const QSize &size); @@ -128,7 +150,7 @@ private: LogoRenderer *m_logoRenderer; - QWindow *m_fakeSurface; + QOffscreenSurface *m_fakeSurface; QOpenGLContext *m_context; QSize m_size; }; @@ -209,19 +231,35 @@ private: ThreadRenderer::ThreadRenderer() + : m_renderThread(0) { setFlag(ItemHasContents, true); + polish(); } +void ThreadRenderer::updatePolish() +{ + if (!window() || !window()->openglContext()) + return; + m_renderThread = new RenderThread(QSize(512, 512), window()->openglContext()); + m_renderThread->moveToThread(m_renderThread); + m_renderThread->start(); + connect(window(), SIGNAL(sceneGraphInvalidated()), m_renderThread, SLOT(shutDown()), Qt::QueuedConnection); +} QSGNode *ThreadRenderer::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) { + if (!m_renderThread) { + polish(); + update(); + return 0; + } + TextureNode *node = static_cast(oldNode); if (!node) { node = new TextureNode(window()); - m_renderThread = new RenderThread(QSize(512, 512)); /* Set up connections to get the production of FBO textures in sync with vsync on the * rendering thread. @@ -242,9 +280,6 @@ QSGNode *ThreadRenderer::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * connect(window(), SIGNAL(beforeRendering()), node, SLOT(prepareNode()), Qt::DirectConnection); connect(node, SIGNAL(textureInUse()), m_renderThread, SLOT(renderNext()), Qt::QueuedConnection); - // Start the render thread and enter let it process events. - m_renderThread->start(); - // Get the production of FBO textures started.. QMetaObject::invokeMethod(m_renderThread, "renderNext", Qt::QueuedConnection); } diff --git a/examples/quick/scenegraph/textureinthread/threadrenderer.h b/examples/quick/scenegraph/textureinthread/threadrenderer.h index f12e640..8c68e27 100644 --- a/examples/quick/scenegraph/textureinthread/threadrenderer.h +++ b/examples/quick/scenegraph/textureinthread/threadrenderer.h @@ -52,9 +52,15 @@ class ThreadRenderer : public QQuickItem public: ThreadRenderer(); + static QList threads; + +public slots: + void updatePolish(); + protected: QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + private: RenderThread *m_renderThread; }; -- 1.7.2.5