From f9306d34bde648975b2a3e24eea01aaa4b6bd679 Mon Sep 17 00:00:00 2001 From: Paul Lemire Date: Thu, 8 Aug 2019 14:55:52 +0200 Subject: Refactor Scene3D internals prior to Scene3DView introduction Change-Id: I6c9313616587c7930560a5abd962ad73d0bfff70 Reviewed-by: Mike Krus --- src/quick3d/imports/scene3d/scene3ditem.cpp | 51 ++++++++++-------- src/quick3d/imports/scene3d/scene3drenderer.cpp | 70 ++++++++++++++----------- src/quick3d/imports/scene3d/scene3drenderer_p.h | 2 +- 3 files changed, 68 insertions(+), 55 deletions(-) diff --git a/src/quick3d/imports/scene3d/scene3ditem.cpp b/src/quick3d/imports/scene3d/scene3ditem.cpp index 54f1554fd..8f3d0b9a6 100644 --- a/src/quick3d/imports/scene3d/scene3ditem.cpp +++ b/src/quick3d/imports/scene3d/scene3ditem.cpp @@ -365,12 +365,15 @@ bool Scene3DItem::needsRender() // The QtQuick SG proceeds like indicated below: // afterAnimating (Main Thread) // beforeSynchronizing (SG Thread and MainThread locked) -// afterSynchronizing (SG Thread) +// afterSynchronizing (SG Thread and MainThread locked) // beforeRendering (SG Thread) // Note: we connect to afterAnimating rather than beforeSynchronizing as a // direct connection on beforeSynchronizing is executed within the SG Render -// Thread context +// Thread context. This is executed before the RenderThread is asked to +// synchronize and render +// Note: we might still not be done rendering when this is called but +// processFrame will block and wait for renderer to have been finished void Scene3DItem::onBeforeSync() { // Has anything in the 3D scene actually changed that requires us to render? @@ -381,9 +384,13 @@ void Scene3DItem::onBeforeSync() // Since we are in manual mode, trigger jobs for the next frame Qt3DCore::QAspectEnginePrivate *aspectEnginePriv = static_cast(QObjectPrivate::get(m_aspectEngine)); - if (!aspectEnginePriv->m_initialized) + if (!aspectEnginePriv->m_initialized || !m_renderer) return; + // Set compositing mode on renderer + m_renderer->setCompositingMode(m_compositingMode); + const bool usesFBO = m_compositingMode == FBO; + Q_ASSERT(m_aspectEngine->runMode() == Qt3DCore::QAspectEngine::Manual); m_aspectEngine->processFrame(); // The above essentially sets the number of RV for the RenderQueue and @@ -399,14 +406,13 @@ void Scene3DItem::onBeforeSync() // start rendering before this function has been called // We add in a safety to skip such frames as this could otherwise // make Qt3D enter a locked state - if (m_renderer) - m_renderer->allowRender(); + m_renderer->allowRender(); // Request refresh for next frame // When using the FBO mode, only the QQuickItem needs to be updated // When using the Underlay mode, the whole windows needs updating - if (m_compositingMode == FBO) + if (usesFBO) QQuickItem::update(); else window()->update(); @@ -532,24 +538,12 @@ QSGNode *Scene3DItem::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNode m_renderer = new Scene3DRenderer(this, m_aspectEngine, m_renderAspect); m_renderer->setCleanerHelper(m_rendererCleaner); } - m_renderer->setCompositingMode(m_compositingMode); - - Scene3DSGNode *fboNode = static_cast(node); const bool usesFBO = m_compositingMode == FBO; - if (usesFBO) { - if (fboNode == nullptr) { - fboNode = new Scene3DSGNode(); - m_renderer->setSGNode(fboNode); - } - fboNode->setRect(boundingRect()); + Scene3DSGNode *fboNode = static_cast(node); - // Reset clear flag if we've set it to false it's still set to that - if (m_disableClearWindow && !window()->clearBeforeRendering()) - window()->setClearBeforeRendering(m_clearsWindowByDefault); - m_disableClearWindow = false; - } else { - // In FBOLess node the Scene3DItem doesn't have any QSGNode to actually - // manager + // When usin Scene3DViews or Scene3D in Underlay mode + // we shouldn't be managing a Scene3DSGNode + if (!usesFBO) { if (fboNode != nullptr) { delete fboNode; fboNode = nullptr; @@ -560,6 +554,19 @@ QSGNode *Scene3DItem::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNode m_disableClearWindow = true; if (m_clearsWindowByDefault) window()->setClearBeforeRendering(false); + } else { + // Regular Scene3D only case + // Create SGNode if using FBO and no Scene3DViews + if (fboNode == nullptr) { + fboNode = new Scene3DSGNode(); + m_renderer->setSGNode(fboNode); + } + fboNode->setRect(boundingRect()); + + // Reset clear flag if we've set it to false it's still set to that + if (m_disableClearWindow && !window()->clearBeforeRendering()) + window()->setClearBeforeRendering(m_clearsWindowByDefault); + m_disableClearWindow = false; } return fboNode; diff --git a/src/quick3d/imports/scene3d/scene3drenderer.cpp b/src/quick3d/imports/scene3d/scene3drenderer.cpp index da63c32d8..bc75e0861 100644 --- a/src/quick3d/imports/scene3d/scene3drenderer.cpp +++ b/src/quick3d/imports/scene3d/scene3drenderer.cpp @@ -165,7 +165,7 @@ Scene3DRenderer::Scene3DRenderer(Scene3DItem *item, Qt3DCore::QAspectEngine *asp Q_CHECK_PTR(m_item->window()); m_window = m_item->window(); - QObject::connect(m_item->window(), &QQuickWindow::afterSynchronizing, this, &Scene3DRenderer::synchronize, Qt::DirectConnection); + QObject::connect(m_item->window(), &QQuickWindow::beforeSynchronizing, this, &Scene3DRenderer::beforeSynchronize, Qt::DirectConnection); QObject::connect(m_item->window(), &QQuickWindow::beforeRendering, this, &Scene3DRenderer::render, Qt::DirectConnection); QObject::connect(m_item->window(), &QQuickWindow::sceneGraphInvalidated, this, &Scene3DRenderer::onSceneGraphInvalidated, Qt::DirectConnection); // So that we can schedule the cleanup @@ -266,7 +266,8 @@ void Scene3DRenderer::onWindowChanged(QQuickWindow *w) } } -void Scene3DRenderer::synchronize() +// Render Thread, GUI locked +void Scene3DRenderer::beforeSynchronize() { if (m_item && m_window) { @@ -274,20 +275,19 @@ void Scene3DRenderer::synchronize() // We could otherwise enter a deadlock state if (!m_allowRendering.tryAcquire(std::max(m_allowRendering.available(), 1))) return; - m_shouldRender = true; + // Check size / multisampling m_multisample = m_item->multisample(); - - if (m_aspectEngine->rootEntity() != m_item->entity()) { - scheduleRootEntityChange(); - } - const QSize boundingRectSize = m_item->boundingRect().size().toSize(); const QSize currentSize = boundingRectSize * m_window->effectiveDevicePixelRatio(); const bool sizeHasChanged = currentSize != m_lastSize; const bool multisampleHasChanged = m_multisample != m_lastMultisample; - m_forceRecreate |= sizeHasChanged || multisampleHasChanged; + const bool forceRecreate = sizeHasChanged || multisampleHasChanged; + // Store the current size as a comparison + // point for the next frame + m_lastSize = currentSize; + m_lastMultisample = m_multisample; if (sizeHasChanged) { static const QMetaMethod setItemAreaAndDevicePixelRatio = setItemAreaAndDevicePixelRatioMethod(); @@ -295,13 +295,37 @@ void Scene3DRenderer::synchronize() Q_ARG(qreal, m_window->effectiveDevicePixelRatio())); } - // Store the current size as a comparison - // point for the next frame - m_lastSize = currentSize; - m_lastMultisample = m_multisample; + // Rebuild FBO if size/multisampling has changed + const bool usesFBO = m_compositingMode == Scene3DItem::FBO; + if (usesFBO) { + // Rebuild FBO and textures if never created or a resize has occurred + if ((m_multisampledFBO.isNull() || forceRecreate) && m_multisample) { + m_multisampledFBO.reset(createMultisampledFramebufferObject(m_lastSize)); + if (m_multisampledFBO->format().samples() == 0 || !QOpenGLFramebufferObject::hasOpenGLFramebufferBlit()) { + m_multisample = false; + m_multisampledFBO.reset(nullptr); + } + } + + const bool generateNewTexture = m_finalFBO.isNull() || forceRecreate; + if (generateNewTexture) { + m_finalFBO.reset(createFramebufferObject(m_lastSize)); + m_texture.reset(m_window->createTextureFromId(m_finalFBO->texture(), m_finalFBO->size(), QQuickWindow::TextureHasAlphaChannel)); + } + + // Set texture on node + if (m_node && (!m_node->texture() || generateNewTexture)) + m_node->setTexture(m_texture.data()); + } + + if (m_aspectEngine->rootEntity() != m_item->entity()) { + scheduleRootEntityChange(); + } + // Mark SGNodes as dirty so that QQuick will trigger some rendering if (m_node) m_node->markDirty(QSGNode::DirtyMaterial); + m_item->update(); } } @@ -321,13 +345,13 @@ void Scene3DRenderer::setSGNode(Scene3DSGNode *node) m_node = node; } +// Render Thread, Main Thread is unlocked at this point void Scene3DRenderer::render() { QMutexLocker l(&m_windowMutex); // Lock to ensure the window doesn't change while we are rendering if (!m_window || !m_shouldRender) return; - m_shouldRender = false; ContextSaver saver; @@ -339,24 +363,6 @@ void Scene3DRenderer::render() // Create and bind FBO if using the FBO compositing mode const bool usesFBO = m_compositingMode == Scene3DItem::FBO; if (usesFBO) { - // Rebuild FBO and textures if never created or a resize has occurred - if ((m_multisampledFBO.isNull() || m_forceRecreate) && m_multisample) { - m_multisampledFBO.reset(createMultisampledFramebufferObject(m_lastSize)); - if (m_multisampledFBO->format().samples() == 0 || !QOpenGLFramebufferObject::hasOpenGLFramebufferBlit()) { - m_multisample = false; - m_multisampledFBO.reset(nullptr); - } - } - - if (m_finalFBO.isNull() || m_forceRecreate) { - m_finalFBO.reset(createFramebufferObject(m_lastSize)); - m_texture.reset(m_window->createTextureFromId(m_finalFBO->texture(), m_finalFBO->size(), QQuickWindow::TextureHasAlphaChannel)); - m_node->setTexture(m_texture.data()); - } - - - m_forceRecreate = false; - // Bind FBO if (m_multisample) //Only try to use MSAA when available m_multisampledFBO->bind(); diff --git a/src/quick3d/imports/scene3d/scene3drenderer_p.h b/src/quick3d/imports/scene3d/scene3drenderer_p.h index a2deed61e..11dfef77d 100644 --- a/src/quick3d/imports/scene3d/scene3drenderer_p.h +++ b/src/quick3d/imports/scene3d/scene3drenderer_p.h @@ -96,7 +96,7 @@ public Q_SLOTS: private: QOpenGLFramebufferObject *createMultisampledFramebufferObject(const QSize &size); QOpenGLFramebufferObject *createFramebufferObject(const QSize &size); - void synchronize(); + void beforeSynchronize(); void scheduleRootEntityChange(); Scene3DItem *m_item; // Will be released by the QQuickWindow/QML Engine -- cgit v1.2.3