summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Lemire <paul.lemire@kdab.com>2020-07-15 16:17:10 +0200
committerPaul Lemire <paul.lemire@kdab.com>2020-07-29 08:03:55 +0200
commit136c016cbe99cd7b8c810c44724e5e3b1b6297e1 (patch)
tree81e05dbf0eddcc70ece85cf54292b3ba47110a59 /src
parentdb611272f595dbb9f381b86d23353dece35df021 (diff)
Refactor Scene3D to work with both RHI and GL Qt3D renderers
- Depending on whether we are using RHI or GL we need to either trigger the rendering after the beforeRendering or beforeRenderPassRecording have been fired -> beforeRendering The RHI command buffer is set up but nothing has been recorded yet. This is what we want for the RHI backend but we will need to make sure we don't call begin/endFrame nor use swap chains other than the one QtQuick is using. This means RenderSurfaceSelector won't be possible. -> beforeRenderPassRecording The RHI command for buffer uploads have been uploaded but the actual RenderPass draw calls have yet to be made. The screen has been cleared already, so this is the best place for the GL backend which expects the screen to have been cleared. - The GL backend can use a QOpenGLFrameBufferObject but that is not possible with the RHI backend. - The RHI backend uses a custom QRhiRenderTarget that takes care of blitting its color attachment into a QRhiTexture which is then bound to a QSGTexture The overall Scene3DItem/Scene3DRender architecture remains the same: - processChange - Render Qt3D content into Texture - Set texture on a custom QSGNode quad Change-Id: Id6c317342d0a227d5295cbfefefc3ed12da160d7 Reviewed-by: Mike Krus <mike.krus@kdab.com>
Diffstat (limited to 'src')
-rw-r--r--src/core/resources/qresourcemanager_p.h13
-rw-r--r--src/extras/defaults/qt3dwindow.cpp5
-rw-r--r--src/plugins/renderers/opengl/renderer/renderer.cpp10
-rw-r--r--src/plugins/renderers/opengl/renderer/renderer_p.h9
-rw-r--r--src/plugins/renderers/rhi/graphicshelpers/submissioncontext.cpp109
-rw-r--r--src/plugins/renderers/rhi/graphicshelpers/submissioncontext_p.h13
-rw-r--r--src/plugins/renderers/rhi/managers/rhiresourcemanagers.cpp3
-rw-r--r--src/plugins/renderers/rhi/renderer/renderer.cpp153
-rw-r--r--src/plugins/renderers/rhi/renderer/renderer_p.h15
-rw-r--r--src/quick3d/imports/scene3d/scene3ditem.cpp2
-rw-r--r--src/quick3d/imports/scene3d/scene3drenderer.cpp547
-rw-r--r--src/quick3d/imports/scene3d/scene3drenderer_p.h85
-rw-r--r--src/render/backend/abstractrenderer_p.h15
-rw-r--r--src/render/frontend/qrenderapi.h1
-rw-r--r--src/render/frontend/qrenderaspect.cpp23
-rw-r--r--src/render/frontend/qrenderaspect_p.h5
16 files changed, 706 insertions, 302 deletions
diff --git a/src/core/resources/qresourcemanager_p.h b/src/core/resources/qresourcemanager_p.h
index 308036fd9..82e8ad17f 100644
--- a/src/core/resources/qresourcemanager_p.h
+++ b/src/core/resources/qresourcemanager_p.h
@@ -441,6 +441,19 @@ public:
Allocator::releaseResource(handle);
}
+ // Releases all resources referenced by a key
+ // Resources allocated manually with just a handle aren't releases
+ void releaseAllResources()
+ {
+ typename LockingPolicy<QResourceManager>::WriteLocker lock(this);
+ // Make a copy as releaseResource removes the entry in m_activeHanldes
+ const std::vector<Handle> activeHandles = Allocator::activeHandles();
+ for (const Handle &h : activeHandles)
+ Allocator::releaseResource(h);
+ // Clear Key to Handle Map
+ m_keyToHandleMap.clear();
+ }
+
protected:
QHash<KeyType, Handle > m_keyToHandleMap;
diff --git a/src/extras/defaults/qt3dwindow.cpp b/src/extras/defaults/qt3dwindow.cpp
index 34264a89f..ffa8d3a4f 100644
--- a/src/extras/defaults/qt3dwindow.cpp
+++ b/src/extras/defaults/qt3dwindow.cpp
@@ -262,7 +262,11 @@ void setupWindowSurface(QWindow *window, Qt3DRender::API api) noexcept
api = Qt3DRender::API::DirectX;
} else if (userRequestedApi == QByteArrayLiteral("null")) {
api = Qt3DRender::API::Null;
+ } else if (userRequestedApi == QByteArrayLiteral("auto")) {
+ api = Qt3DRender::API::RHI;
}
+ } else {
+ api = Qt3DRender::API::RHI;
}
// We have to set the environment so that the backend is able to read it.
@@ -294,6 +298,7 @@ void setupWindowSurface(QWindow *window, Qt3DRender::API api) noexcept
break;
}
#endif
+ case Qt3DRender::API::RHI:
default:
break;
}
diff --git a/src/plugins/renderers/opengl/renderer/renderer.cpp b/src/plugins/renderers/opengl/renderer/renderer.cpp
index b90b08ac1..407d0d25c 100644
--- a/src/plugins/renderers/opengl/renderer/renderer.cpp
+++ b/src/plugins/renderers/opengl/renderer/renderer.cpp
@@ -315,6 +315,16 @@ void Renderer::dumpInfo() const
qDebug() << *textureImageManager;
}
+void Renderer::setRenderDriver(AbstractRenderer::RenderDriver driver)
+{
+ m_driver = driver;
+}
+
+AbstractRenderer::RenderDriver Renderer::renderDriver() const
+{
+ return m_driver;
+}
+
qint64 Renderer::time() const
{
return m_time;
diff --git a/src/plugins/renderers/opengl/renderer/renderer_p.h b/src/plugins/renderers/opengl/renderer/renderer_p.h
index 37be7642b..1d12e5039 100644
--- a/src/plugins/renderers/opengl/renderer/renderer_p.h
+++ b/src/plugins/renderers/opengl/renderer/renderer_p.h
@@ -172,6 +172,9 @@ public:
void dumpInfo() const override;
API api() const override { return Qt3DRender::API::OpenGL; }
+ void setRenderDriver(RenderDriver driver) override;
+ RenderDriver renderDriver() const override;
+
qint64 time() const override;
void setTime(qint64 time) override;
void setJobsInLastFrame(int jobsInLastFrame) override;
@@ -258,8 +261,11 @@ public:
bool requiresVAOAttributeUpdate(Geometry *geometry,
const RenderCommand *command) const;
- // For Scene2D rendering
+ // For Scene3D/Scene2D rendering
void setOpenGLContext(QOpenGLContext *context) override;
+ void setRHIContext(QRhi *) override {};
+ void setDefaultRHIRenderTarget(QRhiRenderTarget *) override {};
+ void setRHICommandBuffer(QRhiCommandBuffer *) override {};
bool accessOpenGLTexture(Qt3DCore::QNodeId nodeId,
QOpenGLTexture **texture,
QMutex **lock,
@@ -410,6 +416,7 @@ private:
QMetaObject::Connection m_contextConnection;
RendererCache<RenderCommand> m_cache;
bool m_shouldSwapBuffers;
+ RenderDriver m_driver = RenderDriver::Qt3D;
std::vector<FrameGraphNode *> m_frameGraphLeaves;
QScreen *m_screen = nullptr;
diff --git a/src/plugins/renderers/rhi/graphicshelpers/submissioncontext.cpp b/src/plugins/renderers/rhi/graphicshelpers/submissioncontext.cpp
index 276d71348..17119e504 100644
--- a/src/plugins/renderers/rhi/graphicshelpers/submissioncontext.cpp
+++ b/src/plugins/renderers/rhi/graphicshelpers/submissioncontext.cpp
@@ -499,7 +499,8 @@ static QShader::Stage rhiShaderStage(QShaderProgram::ShaderType type) noexcept
} // anonymous
SubmissionContext::SubmissionContext()
- : m_ownCurrent(true),
+ : m_ownsRhiCtx(false),
+ m_drivenExternally(false),
m_id(nextFreeContextId()),
m_surface(nullptr),
m_renderTargetFormat(QAbstractTexture::NoFormat),
@@ -509,7 +510,9 @@ SubmissionContext::SubmissionContext()
m_initialized(false),
m_rhi(nullptr),
m_currentSwapChain(nullptr),
- m_currentRenderPassDescriptor(nullptr)
+ m_currentRenderPassDescriptor(nullptr),
+ m_defaultRenderTarget(nullptr),
+ m_defaultCommandBuffer(nullptr)
#ifndef QT_NO_OPENGL
,
m_fallbackSurface(nullptr)
@@ -534,9 +537,15 @@ SubmissionContext::~SubmissionContext()
void SubmissionContext::initialize()
{
m_initialized = true;
- // m_textureContext.initialize(this);
- Qt3DRender::API requestedApi = Qt3DRender::API::OpenGL;
+ // If the RHI instance was already set (Scene3D)
+ // no point in continuing below;
+ if (m_rhi)
+ return;
+
+ m_ownsRhiCtx = true;
+
+ Qt3DRender::API requestedApi = Qt3DRender::API::RHI;
const auto userRequestedApi = qgetenv("QT3D_RHI_DEFAULT_API").toLower();
if (!userRequestedApi.isEmpty()) {
if (userRequestedApi == QByteArrayLiteral("opengl")) {
@@ -552,6 +561,19 @@ void SubmissionContext::initialize()
}
}
+ // If nothing specified, deduce best backend API based on platform
+ if (requestedApi == Qt3DRender::API::RHI) {
+#if defined(Q_OS_WIN)
+ requestedApi = Qt3DRender::API::DirectX;
+#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+ requestedApi = Qt3DRender::API::Metal;
+#elif QT_CONFIG(opengl)
+ requestedApi = Qt3DRender::API::OpenGL;
+#else
+ requestedApi = Qt3DRender::API::Vulkan;
+#endif
+ }
+
QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers;
#if QT_CONFIG(vulkan)
@@ -601,31 +623,15 @@ void SubmissionContext::initialize()
Q_ASSERT(m_rhi != nullptr);
}
-void SubmissionContext::resolveRenderTargetFormat()
+void SubmissionContext::setDrivenExternally(bool drivenExternally)
{
- RHI_UNIMPLEMENTED;
+ Q_ASSERT(!m_initialized);
+ m_drivenExternally = drivenExternally;
+}
- //* const QSurfaceFormat format = m_gl->format();
- //* const uint a = (format.alphaBufferSize() == -1) ? 0 : format.alphaBufferSize();
- //* const uint r = format.redBufferSize();
- //* const uint g = format.greenBufferSize();
- //* const uint b = format.blueBufferSize();
- //*
- //* #define RGBA_BITS(r,g,b,a) (r | (g << 6) | (b << 12) | (a << 18))
- //*
- //* const uint bits = RGBA_BITS(r,g,b,a);
- //* switch (bits) {
- //* case RGBA_BITS(8,8,8,8):
- //* m_renderTargetFormat = QAbstractTexture::RGBA8_UNorm;
- //* break;
- //* case RGBA_BITS(8,8,8,0):
- //* m_renderTargetFormat = QAbstractTexture::RGB8_UNorm;
- //* break;
- //* case RGBA_BITS(5,6,5,0):
- //* m_renderTargetFormat = QAbstractTexture::R5G6B5;
- //* break;
- //* }
- //* #undef RGBA_BITS
+bool SubmissionContext::drivenExternally() const
+{
+ return m_drivenExternally;
}
bool SubmissionContext::beginDrawing(QSurface *surface)
@@ -634,11 +640,15 @@ bool SubmissionContext::beginDrawing(QSurface *surface)
m_surface = surface;
- // TODO: cache surface format somewhere rather than doing this every time render surface changes
- resolveRenderTargetFormat();
-
Q_ASSERT(isInitialized());
+ // In the Scene3D case it does not make sense to create SwapChains as we
+ // can only record commands that will be used against QtQuick default's
+ // swap chain. Also when rendering through Scene3D, QtQuick takes care of
+ // beginning the frame
+ if (m_drivenExternally)
+ return true;
+
// Check if we have a swapchain for the Window, if not create one
SwapChainInfo *swapChainInfo = swapChainForSurface(surface);
QRhiSwapChain *swapChain = swapChainInfo->swapChain;
@@ -653,16 +663,17 @@ bool SubmissionContext::beginDrawing(QSurface *surface)
m_currentSwapChain = swapChain;
m_currentRenderPassDescriptor = swapChainInfo->renderPassDescriptor;
- // Begin Frame
const auto success = m_rhi->beginFrame(m_currentSwapChain);
-
return success == QRhi::FrameOpSuccess;
}
void SubmissionContext::endDrawing(bool swapBuffers)
{
Q_UNUSED(swapBuffers);
- m_rhi->endFrame(m_currentSwapChain, {});
+ const bool shouldEndFrame = !m_drivenExternally;
+ // When rendering through Scene3D, QtQuick takes care of ending the frame
+ if (shouldEndFrame)
+ m_rhi->endFrame(m_currentSwapChain, {});
}
QImage SubmissionContext::readFramebuffer(const QRect &rect)
@@ -836,7 +847,9 @@ void SubmissionContext::releaseResources()
it = m_swapChains.erase(it);
}
- delete m_rhi;
+ // Only destroy RHI context if we created it
+ if (m_ownsRhiCtx)
+ delete m_rhi;
m_rhi = nullptr;
#ifndef QT_NO_OPENGL
@@ -852,6 +865,24 @@ void SubmissionContext::releaseResources()
//* }
}
+// Called when Scene3D is used
+void SubmissionContext::setRHIContext(QRhi *ctx)
+{
+ m_rhi = ctx;
+}
+
+// Scene3D
+void SubmissionContext::setDefaultRenderTarget(QRhiRenderTarget *target)
+{
+ m_defaultRenderTarget = target;
+}
+
+// Scene3D
+void SubmissionContext::setCommandBuffer(QRhiCommandBuffer *commandBuffer)
+{
+ m_defaultCommandBuffer = commandBuffer;
+}
+
void SubmissionContext::bindFrameBufferAttachmentHelper(GLuint fboId,
const AttachmentPack &attachments)
{
@@ -1149,6 +1180,11 @@ SubmissionContext::SwapChainInfo *SubmissionContext::swapChainForSurface(QSurfac
QRhiCommandBuffer *SubmissionContext::currentFrameCommandBuffer() const
{
+ // When rendering with Scene3D, we have to use the Command Buffer provided by QtQuick
+ // When Qt3D renders on its own, we use our own Command Buffer which we can
+ // retrieve from the current Swap Chain
+ if (m_defaultCommandBuffer)
+ return m_defaultCommandBuffer;
return m_currentSwapChain->currentFrameCommandBuffer();
}
@@ -1157,6 +1193,11 @@ QRhiRenderTarget *SubmissionContext::currentFrameRenderTarget() const
return m_currentSwapChain->currentFrameRenderTarget();
}
+QRhiRenderTarget *SubmissionContext::defaultRenderTarget() const
+{
+ return m_defaultRenderTarget;
+}
+
QRhiSwapChain *SubmissionContext::currentSwapChain() const
{
return m_currentSwapChain;
diff --git a/src/plugins/renderers/rhi/graphicshelpers/submissioncontext_p.h b/src/plugins/renderers/rhi/graphicshelpers/submissioncontext_p.h
index 2d55ae382..e164564a4 100644
--- a/src/plugins/renderers/rhi/graphicshelpers/submissioncontext_p.h
+++ b/src/plugins/renderers/rhi/graphicshelpers/submissioncontext_p.h
@@ -124,10 +124,16 @@ public:
int id() const; // unique, small integer ID of this context
void setRenderer(Renderer *renderer) { m_renderer = renderer; }
+ void setDrivenExternally(bool drivenExternally);
+ bool drivenExternally() const;
+
bool beginDrawing(QSurface *surface);
void endDrawing(bool swapBuffers);
void releaseResources();
void setOpenGLContext(QOpenGLContext *ctx);
+ void setRHIContext(QRhi *ctx);
+ void setDefaultRenderTarget(QRhiRenderTarget *target);
+ void setCommandBuffer(QRhiCommandBuffer *commandBuffer);
bool isInitialized() const { return m_initialized; }
const GraphicsApiFilterData *contextInfo() const;
@@ -186,6 +192,7 @@ public:
QRhi *rhi() const { return m_rhi; }
QRhiCommandBuffer *currentFrameCommandBuffer() const;
QRhiRenderTarget *currentFrameRenderTarget() const;
+ QRhiRenderTarget *defaultRenderTarget() const;
QRhiRenderPassDescriptor *currentRenderPassDescriptor() const;
QRhiSwapChain *currentSwapChain() const;
QSurfaceFormat format() const noexcept;
@@ -194,7 +201,6 @@ private:
// FBO
void bindFrameBufferAttachmentHelper(GLuint fboId, const AttachmentPack &attachments);
void activateDrawBuffers(const AttachmentPack &attachments);
- void resolveRenderTargetFormat();
// Buffers
HRHIBuffer createRHIBufferFor(Buffer *buffer);
void uploadDataToRHIBuffer(Buffer *buffer, RHIBuffer *b);
@@ -204,7 +210,8 @@ private:
// States
void applyState(const StateVariant &state, QRhiGraphicsPipeline *graphicsPipeline);
- bool m_ownCurrent;
+ bool m_ownsRhiCtx;
+ bool m_drivenExternally;
const unsigned int m_id;
QSurface *m_surface;
QSize m_surfaceSize;
@@ -227,6 +234,8 @@ private:
QHash<QSurface *, SwapChainInfo> m_swapChains;
QRhiSwapChain *m_currentSwapChain;
QRhiRenderPassDescriptor *m_currentRenderPassDescriptor;
+ QRhiRenderTarget *m_defaultRenderTarget;
+ QRhiCommandBuffer *m_defaultCommandBuffer;
#ifndef QT_NO_OPENGL
QOffscreenSurface *m_fallbackSurface;
diff --git a/src/plugins/renderers/rhi/managers/rhiresourcemanagers.cpp b/src/plugins/renderers/rhi/managers/rhiresourcemanagers.cpp
index 385fa24b4..387464fe4 100644
--- a/src/plugins/renderers/rhi/managers/rhiresourcemanagers.cpp
+++ b/src/plugins/renderers/rhi/managers/rhiresourcemanagers.cpp
@@ -68,6 +68,9 @@ RHIResourceManagers::~RHIResourceManagers()
void RHIResourceManagers::releaseAllResources()
{
auto releaseAll = [](auto *manager) noexcept {
+ // Release all resources reference by a key
+ manager->releaseAllResources();
+ // Release remaining resources which were allocated manually but had no key
const auto handles = manager->activeHandles();
for (const auto &handle : handles) {
manager->release(handle);
diff --git a/src/plugins/renderers/rhi/renderer/renderer.cpp b/src/plugins/renderers/rhi/renderer/renderer.cpp
index 096994da1..a194c94ff 100644
--- a/src/plugins/renderers/rhi/renderer/renderer.cpp
+++ b/src/plugins/renderers/rhi/renderer/renderer.cpp
@@ -247,6 +247,7 @@ Renderer::Renderer()
m_exposed(0),
m_lastFrameCorrect(0),
m_glContext(nullptr),
+ m_rhiContext(nullptr),
m_time(0),
m_settings(nullptr),
m_updateShaderDataTransformJob(Render::UpdateShaderDataTransformJobPtr::create()),
@@ -265,7 +266,6 @@ Renderer::Renderer()
[this](Qt3DCore::QAspectManager *m) { sendShaderChangesToFrontend(m); },
JobTypes::DirtyShaderGathering)),
m_ownedContext(false),
- m_offscreenHelper(nullptr),
m_RHIResourceManagers(nullptr),
m_commandExecuter(new Qt3DRender::Debug::CommandExecuter(this)),
m_shouldSwapBuffers(true)
@@ -316,7 +316,24 @@ void Renderer::dumpInfo() const
API Renderer::api() const
{
- return API::OpenGL;
+ return API::RHI;
+}
+
+// When Scene3D is driving rendering:
+// - Don't create SwapChains
+// - Use the defaultRenderTarget if no renderTarget specified instead of the
+// swapChain
+// - Use the provided commandBuffer
+void Renderer::setRenderDriver(AbstractRenderer::RenderDriver driver)
+{
+ // This must be called before initialize which creates the submission context
+ Q_ASSERT(!m_submissionContext);
+ m_driver = driver;
+}
+
+AbstractRenderer::RenderDriver Renderer::renderDriver() const
+{
+ return m_driver;
}
qint64 Renderer::time() const
@@ -397,6 +414,30 @@ void Renderer::setOpenGLContext(QOpenGLContext *context)
m_glContext = context;
}
+void Renderer::setRHIContext(QRhi *ctx)
+{
+ m_rhiContext = ctx;
+}
+
+// Called by Scene3D
+void Renderer::setDefaultRHIRenderTarget(QRhiRenderTarget *defaultTarget)
+{
+ Q_ASSERT(m_submissionContext);
+ m_submissionContext->setDefaultRenderTarget(defaultTarget);
+
+ // When the defaultTarget changes, we have to recreate all QRhiGraphicsPipelines
+ // to ensure they match new DefaultRenderTargetLayout. This is potentially expensive
+ RHIGraphicsPipelineManager *pipelineManager = m_RHIResourceManagers->rhiGraphicsPipelineManager();
+ pipelineManager->releaseAllResources();
+}
+
+// Called by Scene3D
+void Renderer::setRHICommandBuffer(QRhiCommandBuffer *commandBuffer)
+{
+ Q_ASSERT(m_submissionContext);
+ m_submissionContext->setCommandBuffer(commandBuffer);
+}
+
void Renderer::setScreen(QScreen *scr)
{
m_screen = scr;
@@ -450,6 +491,11 @@ void Renderer::initialize()
m_submissionContext.reset(new SubmissionContext);
m_submissionContext->setRenderer(this);
+ if (m_driver == AbstractRenderer::Scene3D) {
+ m_submissionContext->setRHIContext(m_rhiContext);
+ m_submissionContext->setDrivenExternally(true);
+ }
+
// RHI initialization
{
qCDebug(Backend) << Q_FUNC_INFO << "Requesting renderer initialize";
@@ -534,53 +580,6 @@ void Renderer::releaseGraphicsResources()
// check that we haven't already cleaned up before going any further.
if (!m_submissionContext)
return;
-
- // Try to temporarily make the context current so we can free up any resources
- QMutexLocker locker(&m_offscreenSurfaceMutex);
- QOffscreenSurface *offscreenSurface = m_offscreenHelper->offscreenSurface();
- if (!offscreenSurface) {
- qCWarning(Backend) << "Failed to make context current: OpenGL resources will not be destroyed";
- // We still need to delete the submission context
- m_submissionContext.reset(nullptr);
- return;
- }
-
- //* QOpenGLContext *context = m_submissionContext->openGLContext();
- //* Q_ASSERT(context);
- //*
- //* if (context->thread() == QThread::currentThread() && context->makeCurrent(offscreenSurface))
- //{
- //*
- //* // Clean up the graphics context and any resources
- //* const std::vector<HRHITexture> activeTexturesHandles =
- //m_RHIResourceManagers->rhiTextureManager()->activeHandles();
- //* for (const HRHITexture &textureHandle : activeTexturesHandles) {
- //* RHITexture *tex = m_RHIResourceManagers->rhiTextureManager()->data(textureHandle);
- //* tex->destroy();
- //* }
- //*
- //* // Do the same thing with buffers
- //* const std::vector<HRHIBuffer> activeBuffers =
- //m_RHIResourceManagers->rhiBufferManager()->activeHandles();
- //* for (const HRHIBuffer &bufferHandle : activeBuffers) {
- //* RHIBuffer *buffer = m_RHIResourceManagers->rhiBufferManager()->data(bufferHandle);
- //* buffer->destroy(m_submissionContext.data());
- //* }
- //*
- //* // Do the same thing with shaders
- //* const std::vector<RHIShader *> shaders =
- //m_RHIResourceManagers->rhiShaderManager()->takeActiveResources();
- //* qDeleteAll(shaders);
- //*
- //*
- //* context->doneCurrent();
- //* } else {
- //* qCWarning(Backend) << "Failed to make context current: OpenGL resources will not be destroyed";
- //* }
- //*
- //* if (m_ownedContext)
- //* delete context;
-
m_submissionContext.reset(nullptr);
qCDebug(Backend) << Q_FUNC_INFO << "Renderer properly shutdown";
@@ -807,16 +806,6 @@ QVariant Renderer::executeCommand(const QStringList &args)
return m_commandExecuter->executeCommand(args);
}
-/*!
- \internal
- Called in the context of the aspect thread from QRenderAspect::onRegistered
-*/
-void Renderer::setOffscreenSurfaceHelper(OffscreenSurfaceHelper *helper)
-{
- QMutexLocker locker(&m_offscreenSurfaceMutex);
- m_offscreenHelper = helper;
-}
-
QSurfaceFormat Renderer::format()
{
return m_submissionContext->format();
@@ -908,8 +897,6 @@ void Renderer::updateGraphicsPipeline(RenderCommand &cmd, RenderView *rv,
if (auto& pipelines = m_rvToPipelines[rv]; !Qt3DCore::contains(pipelines, graphicsPipeline))
pipelines.push_back(graphicsPipeline);
- // TO DO: Record active pipelines in the RenderView
-
// Record RHIGraphicsPipeline into command for later use
cmd.pipeline = graphicsPipeline;
@@ -933,10 +920,18 @@ void Renderer::buildGraphicsPipelines(RHIGraphicsPipeline *graphicsPipeline,
// Note: we can rebuild add/remove things from the QRhiShaderResourceBindings after having
// created the pipeline and rebuild it. Changes should be picked up automatically
- const SubmissionContext::SwapChainInfo *swapchain =
- m_submissionContext->swapChainForSurface(rv->surface());
- if (!swapchain || !swapchain->swapChain || !swapchain->renderPassDescriptor)
- return;
+
+ // If a defaultRenderTarget was set (Scene3D) we don't bother retrieving the swapchain
+ // as we have to use the one provided by Scene3D
+ QRhiSwapChain *rhiSwapChain = nullptr;
+ if (!m_submissionContext->defaultRenderTarget()) {
+ const SubmissionContext::SwapChainInfo *swapchain = m_submissionContext->swapChainForSurface(rv->surface());
+ if (!swapchain || !swapchain->swapChain || !swapchain->renderPassDescriptor) {
+ qCWarning(Backend) << "Can't create pipeline, incomplete SwapChain and no default Render Target";
+ return;
+ }
+ rhiSwapChain = swapchain->swapChain;
+ }
auto onFailure = [&] {
qCWarning(Backend) << "Failed to build pipeline";
@@ -1019,7 +1014,7 @@ void Renderer::buildGraphicsPipelines(RHIGraphicsPipeline *graphicsPipeline,
m_submissionContext->applyStateSet(renderState, pipeline);
// Setup potential texture render target
- const bool renderTargetIsSet = setupRenderTarget(rv, graphicsPipeline, swapchain->swapChain);
+ const bool renderTargetIsSet = setupRenderTarget(rv, graphicsPipeline, rhiSwapChain);
if (!renderTargetIsSet)
return onFailure();
@@ -1133,7 +1128,9 @@ void Renderer::createRenderTarget(RenderView *rv, RHIRenderTarget *target)
target->depthStencilBuffer = ds;
}
-bool Renderer::setupRenderTarget(RenderView *rv, RHIGraphicsPipeline *graphicsPipeline, QRhiSwapChain *swapchain)
+bool Renderer::setupRenderTarget(RenderView *rv,
+ RHIGraphicsPipeline *graphicsPipeline,
+ QRhiSwapChain *swapchain)
{
QRhiGraphicsPipeline *rhiPipeline = graphicsPipeline->pipeline();
@@ -1163,13 +1160,18 @@ bool Renderer::setupRenderTarget(RenderView *rv, RHIGraphicsPipeline *graphicsPi
rhiRenderTargetManager->nodeIdForRHIRenderTarget.insert(rhiTarget, renderTargetId);
}
}
-
rhiPipeline->setRenderPassDescriptor(rhiTarget->renderPassDescriptor);
rhiPipeline->setSampleCount(rhiTarget->renderTarget->sampleCount());
-
+ return true;
+ } else if (m_submissionContext->defaultRenderTarget()) {
+ // Use default RenderTarget if set Default FBO set by Scene3D
+ QRhiRenderTarget *defaultTarget = m_submissionContext->defaultRenderTarget();;
+ rhiPipeline->setRenderPassDescriptor(defaultTarget->renderPassDescriptor());
+ rhiPipeline->setSampleCount(defaultTarget->sampleCount());
return true;
} else {
- // Render to the default framebuffer
+ Q_ASSERT(swapchain);
+ // Render to the default framebuffer on our swapchain
rhiPipeline->setRenderPassDescriptor(swapchain->renderPassDescriptor());
rhiPipeline->setSampleCount(swapchain->sampleCount());
return true;
@@ -1871,6 +1873,8 @@ Renderer::submitRenderViews(const std::vector<RHIPassInfo> &rhiPassesInfo)
const bool surfaceHasChanged = surface != previousSurface;
if (surfaceHasChanged && previousSurface) {
+ // TO DO: Warn that this likely won't work with Scene3D
+
// TODO what should be the swapBuffers condition for RHI ?
// lastRenderTarget == swapChain->renderTarget or something like that ?
const bool swapBuffers = surfaceLock.isSurfaceValid() && m_shouldSwapBuffers;
@@ -1879,6 +1883,8 @@ Renderer::submitRenderViews(const std::vector<RHIPassInfo> &rhiPassesInfo)
}
if (surfaceHasChanged) {
+ // TO DO: Warn that this likely won't work with Scene3D
+
// If we can't make the context current on the surface, skip to the
// next RenderView. We won't get the full frame but we may get something
if (!m_submissionContext->beginDrawing(surface)) {
@@ -2455,11 +2461,12 @@ bool Renderer::executeCommandsSubmission(const RHIPassInfo &passInfo)
auto &renderTargetManager = *managers.rhiRenderTargetManager();
auto *renderTarget = renderTargetManager.lookupResource(passInfo.renderTargetId);
- if (renderTarget) {
+ if (renderTarget)
rhiRenderTarget = renderTarget->renderTarget;
- } else {
+ else if (m_submissionContext->defaultRenderTarget())
+ rhiRenderTarget = m_submissionContext->defaultRenderTarget();
+ else
rhiRenderTarget = m_submissionContext->currentSwapChain()->currentFrameRenderTarget();
- }
}
// TO DO: should be moved elsewhere
diff --git a/src/plugins/renderers/rhi/renderer/renderer_p.h b/src/plugins/renderers/rhi/renderer/renderer_p.h
index 190fed178..5d89670b0 100644
--- a/src/plugins/renderers/rhi/renderer/renderer_p.h
+++ b/src/plugins/renderers/rhi/renderer/renderer_p.h
@@ -168,6 +168,9 @@ public:
void dumpInfo() const override;
API api() const override;
+ void setRenderDriver(AbstractRenderer::RenderDriver driver) override;
+ AbstractRenderer::RenderDriver renderDriver() const override;
+
qint64 time() const override;
void setTime(qint64 time) override;
void setJobsInLastFrame(int jobsInLastFrame) override;
@@ -265,8 +268,11 @@ public:
std::vector<RHIPassInfo> prepareCommandsSubmission(const std::vector<RenderView *> &renderViews);
bool executeCommandsSubmission(const RHIPassInfo &passInfo);
- // For Scene2D rendering
+ // For Scene3D/Scene2D rendering
void setOpenGLContext(QOpenGLContext *context) override;
+ void setRHIContext(QRhi *ctx) override;
+ void setDefaultRHIRenderTarget(QRhiRenderTarget *defaultTarget) override;
+ void setRHICommandBuffer(QRhiCommandBuffer *commandBuffer) override;
bool accessOpenGLTexture(Qt3DCore::QNodeId nodeId, QOpenGLTexture **texture, QMutex **lock,
bool readonly) override;
QSharedPointer<RenderBackendResourceAccessor> resourceAccessor() const override;
@@ -282,8 +288,8 @@ public:
void enqueueRenderView(RenderView *renderView, int submitOrder);
bool waitUntilReadyToSubmit();
- QVariant executeCommand(const QStringList &args) override;
- void setOffscreenSurfaceHelper(OffscreenSurfaceHelper *helper) override;
+ QVariant executeCommand(const QStringList &args);
+ void setOffscreenSurfaceHelper(OffscreenSurfaceHelper *) override {};
QSurfaceFormat format() override;
struct ViewSubmissionResultData
@@ -345,6 +351,7 @@ private:
QAtomicInt m_lastFrameCorrect;
QOpenGLContext *m_glContext;
+ QRhi *m_rhiContext;
qint64 m_time;
@@ -387,7 +394,6 @@ private:
bool m_ownedContext;
- OffscreenSurfaceHelper *m_offscreenHelper;
RHIResourceManagers *m_RHIResourceManagers;
QMutex m_offscreenSurfaceMutex;
@@ -405,6 +411,7 @@ private:
QScreen *m_screen = nullptr;
QSharedPointer<ResourceAccessor> m_scene2DResourceAccessor;
QHash<RenderView *, std::vector<RHIGraphicsPipeline *>> m_rvToPipelines;
+ RenderDriver m_driver = RenderDriver::Qt3D;
bool m_hasSwapChain = false;
diff --git a/src/quick3d/imports/scene3d/scene3ditem.cpp b/src/quick3d/imports/scene3d/scene3ditem.cpp
index 7b89ce91b..1932cd155 100644
--- a/src/quick3d/imports/scene3d/scene3ditem.cpp
+++ b/src/quick3d/imports/scene3d/scene3ditem.cpp
@@ -747,7 +747,7 @@ QSGNode *Scene3DItem::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNode
const bool hasScene3DViews = !m_views.empty();
Scene3DSGNode *fboNode = static_cast<Scene3DSGNode *>(node);
- // When usin Scene3DViews or Scene3D in Underlay mode
+ // When using Scene3DViews or Scene3D in Underlay mode
// we shouldn't be managing a Scene3DSGNode
if (!usesFBO || hasScene3DViews) {
if (fboNode != nullptr) {
diff --git a/src/quick3d/imports/scene3d/scene3drenderer.cpp b/src/quick3d/imports/scene3d/scene3drenderer.cpp
index e8ffdea60..e75dd9689 100644
--- a/src/quick3d/imports/scene3d/scene3drenderer.cpp
+++ b/src/quick3d/imports/scene3d/scene3drenderer.cpp
@@ -45,6 +45,7 @@
#include <qopenglcontext.h>
#include <qopenglframebufferobject.h>
#include <QtQuick/qquickwindow.h>
+#include <private/qrhi_p.h>
#include <Qt3DRender/private/qrenderaspect_p.h>
#include <Qt3DRender/private/abstractrenderer_p.h>
@@ -150,15 +151,9 @@ Scene3DRenderer::Scene3DRenderer()
, m_item(nullptr)
, m_aspectEngine(nullptr)
, m_renderAspect(nullptr)
- , m_multisampledFBO(nullptr)
- , m_finalFBO(nullptr)
- , m_texture(nullptr)
, m_node(nullptr)
, m_window(nullptr)
- , m_multisample(false) // this value is not used, will be synced from the Scene3DItem instead
- , m_lastMultisample(false)
, m_needsShutdown(true)
- , m_forceRecreate(false)
, m_shouldRender(false)
, m_dirtyViews(false)
, m_skipFrame(false)
@@ -180,20 +175,34 @@ void Scene3DRenderer::init(Scene3DItem *item, Qt3DCore::QAspectEngine *aspectEng
Q_CHECK_PTR(m_item->window());
m_window = m_item->window();
- QObject::connect(m_item->window(), &QQuickWindow::beforeSynchronizing, this, &Scene3DRenderer::beforeSynchronize, Qt::DirectConnection);
- QObject::connect(m_item->window(), &QQuickWindow::beforeRenderPassRecording, this, &Scene3DRenderer::render, Qt::DirectConnection);
- QObject::connect(m_item->window(), &QQuickWindow::sceneGraphInvalidated, this, &Scene3DRenderer::onSceneGraphInvalidated, Qt::DirectConnection);
+
+ // Detect which Rendering backend Qt3D is using
+ Qt3DRender::QRenderAspectPrivate *aspectPriv = static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect));
+ Qt3DRender::Render::AbstractRenderer *renderer = aspectPriv->m_renderer;
+
+ const bool isRHI = renderer->api() == API::RHI;
+ if (isRHI)
+ m_quickRenderer = new Scene3DRenderer::RHIRenderer;
+ else
+ m_quickRenderer = new Scene3DRenderer::GLRenderer;
+ m_quickRenderer->initialize(this, renderer);
+
+ QObject::connect(m_item->window(), &QQuickWindow::beforeSynchronizing,
+ this, [this] () { m_quickRenderer->beforeSynchronize(this); }, Qt::DirectConnection);
+ QObject::connect(m_item->window(), &QQuickWindow::beforeRendering, this,
+ [this] () { m_quickRenderer->beforeRendering(this); }, Qt::DirectConnection);
+ QObject::connect(m_item->window(), &QQuickWindow::beforeRenderPassRecording, this,
+ [this] () { m_quickRenderer->beforeRenderPassRecording(this); }, Qt::DirectConnection);
+ QObject::connect(m_item->window(), &QQuickWindow::sceneGraphInvalidated, this,
+ &Scene3DRenderer::onSceneGraphInvalidated, Qt::DirectConnection);
// So that we can schedule the cleanup
- QObject::connect(m_item, &QQuickItem::windowChanged, this, &Scene3DRenderer::onWindowChanged, Qt::QueuedConnection);
+ QObject::connect(m_item, &QQuickItem::windowChanged, this,
+ &Scene3DRenderer::onWindowChanged, Qt::QueuedConnection);
// Main thread -> updates the rendering window
QObject::connect(m_item, &QQuickItem::windowChanged, this, [this] (QQuickWindow *w) {
QMutexLocker l(&m_windowMutex);
m_window = w;
});
-
- Q_ASSERT(QOpenGLContext::currentContext());
- ContextSaver saver;
- static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect))->renderInitialize(saver.context());
scheduleRootEntityChange();
}
@@ -202,25 +211,6 @@ Scene3DRenderer::~Scene3DRenderer()
qCDebug(Scene3D) << Q_FUNC_INFO << QThread::currentThread();
}
-
-QOpenGLFramebufferObject *Scene3DRenderer::createMultisampledFramebufferObject(const QSize &size)
-{
- QOpenGLFramebufferObjectFormat format;
- format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
- int samples = QSurfaceFormat::defaultFormat().samples();
- if (samples == -1)
- samples = 4;
- format.setSamples(samples);
- return new QOpenGLFramebufferObject(size, format);
-}
-
-QOpenGLFramebufferObject *Scene3DRenderer::createFramebufferObject(const QSize &size)
-{
- QOpenGLFramebufferObjectFormat format;
- format.setAttachment(QOpenGLFramebufferObject::Depth);
- return new QOpenGLFramebufferObject(size, format);
-}
-
void Scene3DRenderer::scheduleRootEntityChange()
{
QMetaObject::invokeMethod(m_item, "applyRootEntityChange", Qt::QueuedConnection);
@@ -245,15 +235,13 @@ void Scene3DRenderer::shutdown()
engineD->exitSimulationLoop();
}
- // Shutdown the Renderer Aspect while the OpenGL context
- // is still valid
- if (m_renderAspect) {
- static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect))->renderShutdown();
- m_renderAspect = nullptr;
- }
+ m_quickRenderer->shutdown(this);
+
+ m_renderAspect = nullptr;
m_aspectEngine = nullptr;
- m_finalFBO.reset();
- m_multisampledFBO.reset();
+
+ delete m_quickRenderer;
+ m_quickRenderer = nullptr;
}
// QtQuick render thread (which may also be the gui/main thread with QQuickWidget / RenderControl)
@@ -277,102 +265,6 @@ void Scene3DRenderer::onWindowChanged(QQuickWindow *w)
}
}
-// Render Thread, GUI locked
-void Scene3DRenderer::beforeSynchronize()
-{
- if (m_item && m_window) {
-
- // Only render if we are sure aspectManager->processFrame was called prior
- // We could otherwise enter a deadlock state
- if (!m_allowRendering.tryAcquire(std::max(m_allowRendering.available(), 1)))
- return;
-
- // In the case of OnDemand rendering, we still need to get to this
- // point to ensure we have processed jobs for all aspects.
- // We also still need to call render() to allow proceeding with the
- // next frame. However it won't be performing any 3d rendering at all
- // so we do it here and return early. This prevents a costly QtQuick
- // SceneGraph update for nothing
- if (m_skipFrame) {
- m_skipFrame = false;
- ContextSaver saver;
- static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect))->render(false);
- return;
- }
-
- m_shouldRender = true;
-
- // Check size / multisampling
- m_multisample = m_item->multisample();
- 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;
- 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();
- setItemAreaAndDevicePixelRatio.invoke(m_item, Qt::QueuedConnection, Q_ARG(QSize, boundingRectSize),
- Q_ARG(qreal, m_window->effectiveDevicePixelRatio()));
- }
-
- // 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_textureId = m_finalFBO->texture();
- m_texture.reset(m_window->createTextureFromNativeObject(QQuickWindow::NativeObjectTexture, m_textureId,
- 0, m_finalFBO->size(), QQuickWindow::TextureHasAlphaChannel));
- }
-
- // We can render either the Scene3D or the Scene3DView but not both
- // at the same time
- Q_ASSERT((m_node == nullptr || m_views.empty()) ||
- (m_node != nullptr && m_views.empty()) ||
- (m_node == nullptr && !m_views.empty()));
-
- // Set texture on node
- if (m_node && (!m_node->texture() || generateNewTexture))
- m_node->setTexture(m_texture.data());
-
- // Set textures on Scene3DView
- if (m_dirtyViews || generateNewTexture) {
- for (Scene3DView *view : qAsConst(m_views))
- if (!view->texture() || generateNewTexture)
- view->setTexture(m_texture.data());
- m_dirtyViews = false;
- }
- }
-
- 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);
-
- for (Scene3DView *view : qAsConst(m_views))
- view->markSGNodeDirty();
-
- m_item->update();
- }
-}
-
void Scene3DRenderer::allowRender()
{
m_allowRendering.release(1);
@@ -400,19 +292,160 @@ void Scene3DRenderer::setSGNode(Scene3DSGNode *node)
m_node = node;
}
-// Render Thread, Main Thread is unlocked at this point
-void Scene3DRenderer::render()
+QOpenGLFramebufferObject *Scene3DRenderer::GLRenderer::createMultisampledFramebufferObject(const QSize &size)
{
- QMutexLocker l(&m_windowMutex);
- // Lock to ensure the window doesn't change while we are rendering
- if (!m_window || !m_shouldRender)
+ QOpenGLFramebufferObjectFormat format;
+ format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
+ int samples = QSurfaceFormat::defaultFormat().samples();
+ if (samples == -1)
+ samples = 4;
+ format.setSamples(samples);
+ return new QOpenGLFramebufferObject(size, format);
+}
+
+QOpenGLFramebufferObject *Scene3DRenderer::GLRenderer::createFramebufferObject(const QSize &size)
+{
+ QOpenGLFramebufferObjectFormat format;
+ format.setAttachment(QOpenGLFramebufferObject::Depth);
+ return new QOpenGLFramebufferObject(size, format);
+}
+
+void Scene3DRenderer::GLRenderer::initialize(Scene3DRenderer *scene3DRenderer,
+ Qt3DRender::Render::AbstractRenderer *renderer)
+{
+ Q_UNUSED(scene3DRenderer);
+ Q_ASSERT(QOpenGLContext::currentContext());
+ m_renderer = renderer;
+ ContextSaver saver;
+ Q_ASSERT(renderer->api() == API::OpenGL);
+
+ m_renderer->setRenderDriver(Qt3DRender::Render::AbstractRenderer::Scene3D);
+ m_renderer->setOpenGLContext(saver.context());
+ m_renderer->initialize();
+}
+
+void Scene3DRenderer::GLRenderer::beforeSynchronize(Scene3DRenderer *scene3DRenderer)
+{
+ // Check size / multisampling
+ Scene3DItem *item = scene3DRenderer->m_item;
+ QQuickWindow *window = scene3DRenderer->m_window;
+
+ if (!item || !window)
+ return;
+
+ // Only render if we are sure aspectManager->processFrame was called prior
+ // We could otherwise enter a deadlock state
+ if (!scene3DRenderer->m_allowRendering.tryAcquire(std::max(scene3DRenderer->m_allowRendering.available(), 1)))
return;
- m_shouldRender = false;
+ // In the case of OnDemand rendering, we still need to get to this
+ // point to ensure we have processed jobs for all aspects.
+ // We also still need to call render() to allow proceeding with the
+ // next frame. However it won't be performing any 3d rendering at all
+ // so we do it here and return early. This prevents a costly QtQuick
+ // SceneGraph update for nothing
+ if (scene3DRenderer->m_skipFrame) {
+ scene3DRenderer->m_skipFrame = false;
+ ContextSaver saver;
+ m_renderer->render(false);
+ return;
+ }
+
+ scene3DRenderer->m_shouldRender = true;
+
+ m_multisample = item->multisample();
+ const QSize boundingRectSize = item->boundingRect().size().toSize();
+ const QSize currentSize = boundingRectSize * window->effectiveDevicePixelRatio();
+ const bool sizeHasChanged = currentSize != m_lastSize;
+ const bool multisampleHasChanged = m_multisample != m_lastMultisample;
+ 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();
+ setItemAreaAndDevicePixelRatio.invoke(item, Qt::QueuedConnection, Q_ARG(QSize, boundingRectSize),
+ Q_ARG(qreal, window->effectiveDevicePixelRatio()));
+ }
+
+ // Rebuild FBO if size/multisampling has changed
+ const bool usesFBO = scene3DRenderer->m_compositingMode == Scene3DItem::FBO;
+ auto node = scene3DRenderer->m_node;
+ 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_textureId = m_finalFBO->texture();
+ m_texture.reset(window->createTextureFromNativeObject(QQuickWindow::NativeObjectTexture, m_textureId,
+ 0, m_finalFBO->size(), QQuickWindow::TextureHasAlphaChannel));
+ }
+
+ // We can render either the Scene3D or the Scene3DView but not both
+ // at the same time
+ const auto &views = scene3DRenderer->m_views;
+ Q_ASSERT((node == nullptr || views.empty()) ||
+ (node != nullptr && views.empty()) ||
+ (node == nullptr && !views.empty()));
+
+ // Set texture on node
+ if (node && (!node->texture() || generateNewTexture))
+ node->setTexture(m_texture.data());
+
+ // Set textures on Scene3DView
+ if (scene3DRenderer->m_dirtyViews || generateNewTexture) {
+ for (Scene3DView *view : qAsConst(views))
+ if (!view->texture() || generateNewTexture)
+ view->setTexture(m_texture.data());
+ scene3DRenderer->m_dirtyViews = false;
+ }
+ }
+
+ if (scene3DRenderer->m_aspectEngine->rootEntity() != item->entity())
+ scene3DRenderer->scheduleRootEntityChange();
+
+ // Mark SGNodes as dirty so that QQuick will trigger some rendering
+ if (node)
+ node->markDirty(QSGNode::DirtyMaterial);
+
+ for (Scene3DView *view : qAsConst(scene3DRenderer->m_views))
+ view->markSGNodeDirty();
+
+ item->update();
+}
+
+void Scene3DRenderer::GLRenderer::beforeRendering(Scene3DRenderer *scene3DRenderer)
+{
+ Q_UNUSED(scene3DRenderer);
+}
+
+void Scene3DRenderer::GLRenderer::beforeRenderPassRecording(Scene3DRenderer *scene3DRenderer)
+{
+ // When this is fired, beforeRendering was fired and the RenderPass for QtQuick was
+ // started -> meaning the window was cleared but actual draw commands have yet to be
+ // recorded.
+ // When using the GL Renderer and FBO, that's the state we want to be in.
+
+ QMutexLocker l(&scene3DRenderer->m_windowMutex);
+ // Lock to ensure the window doesn't change while we are rendering
+ if (!scene3DRenderer->m_window || !scene3DRenderer->m_shouldRender)
+ return;
+ // Reset flag for next frame
+ scene3DRenderer->m_shouldRender = false;
ContextSaver saver;
// Create and bind FBO if using the FBO compositing mode
- const bool usesFBO = m_compositingMode == Scene3DItem::FBO;
+ const bool usesFBO = scene3DRenderer->m_compositingMode == Scene3DItem::FBO;
if (usesFBO) {
// Bind FBO
if (m_multisample) //Only try to use MSAA when available
@@ -423,7 +456,7 @@ void Scene3DRenderer::render()
// Render Qt3D Scene
// Qt3D takes care of resetting the GL state to default values
- static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect))->render(usesFBO);
+ m_renderer->render(usesFBO);
// We may have called doneCurrent() so restore the context if the rendering surface was changed
// Note: keep in mind that the ContextSave also restores the surface when destroyed
@@ -448,11 +481,231 @@ void Scene3DRenderer::render()
// Only show the node once Qt3D has rendered to it
// Avoids showing garbage on the first frame
- if (m_node)
- m_node->show();
+ if (scene3DRenderer->m_node)
+ scene3DRenderer->m_node->show();
}
}
+void Scene3DRenderer::GLRenderer::shutdown(Scene3DRenderer *sceneRenderer)
+{
+ // Shutdown the Renderer Aspect while the OpenGL context
+ // is still valid
+ if (sceneRenderer->m_renderAspect)
+ m_renderer->shutdown();
+
+ m_finalFBO.reset();
+ m_multisampledFBO.reset();
+}
+
+void Scene3DRenderer::RHIRenderer::initialize(Scene3DRenderer *scene3DRenderer,
+ Qt3DRender::Render::AbstractRenderer *renderer)
+{
+ QQuickWindow *window = scene3DRenderer->m_window;
+ Q_ASSERT(window);
+ Q_ASSERT(renderer->api() == API::RHI);
+
+ // Retrieve RHI context
+ QSGRendererInterface *rif = window->rendererInterface();
+ const bool isRhi = QSGRendererInterface::isApiRhiBased(rif->graphicsApi());
+ Q_ASSERT(isRhi);
+ if (isRhi) {
+ m_rhi = static_cast<QRhi *>(rif->getResource(window, QSGRendererInterface::RhiResource));
+ if (!m_rhi)
+ qFatal("No QRhi from QQuickWindow, this cannot happen");
+ m_renderer = renderer;
+ m_renderer->setRenderDriver(Qt3DRender::Render::AbstractRenderer::Scene3D);
+ m_renderer->setRHIContext(m_rhi);
+ m_renderer->initialize();
+ }
+}
+
+void Scene3DRenderer::RHIRenderer::beforeSynchronize(Scene3DRenderer *scene3DRenderer)
+{
+ // Check size / multisampling
+ Scene3DItem *item = scene3DRenderer->m_item;
+ QQuickWindow *window = scene3DRenderer->m_window;
+
+ if (!item || !window)
+ return;
+
+ // Only render if we are sure aspectManager->processFrame was called prior
+ // We could otherwise enter a deadlock state
+ if (!scene3DRenderer->m_allowRendering.tryAcquire(std::max(scene3DRenderer->m_allowRendering.available(), 1)))
+ return;
+
+ // In the case of OnDemand rendering, we still need to get to this
+ // point to ensure we have processed jobs for all aspects.
+ // We also still need to call render() to allow proceeding with the
+ // next frame. However it won't be performing any 3d rendering at all
+ // so we do it here and return early. This prevents a costly QtQuick
+ // SceneGraph update for nothing
+ if (scene3DRenderer->m_skipFrame) {
+ scene3DRenderer->m_skipFrame = false;
+ m_renderer->render(false);
+ return;
+ }
+
+ scene3DRenderer->m_shouldRender = true;
+
+ const QSize boundingRectSize = item->boundingRect().size().toSize();
+ const QSize currentSize = boundingRectSize * window->effectiveDevicePixelRatio();
+ const bool sizeHasChanged = currentSize != m_lastSize;
+ const bool multisampleHasChanged = item->multisample() != m_lastMultisample;
+ // Store the current size and multisample as a comparison point for the next frame
+ m_lastMultisample = item->multisample();
+ m_lastSize = currentSize;
+
+ if (sizeHasChanged) {
+ static const QMetaMethod setItemAreaAndDevicePixelRatio = setItemAreaAndDevicePixelRatioMethod();
+ setItemAreaAndDevicePixelRatio.invoke(item, Qt::QueuedConnection, Q_ARG(QSize, boundingRectSize),
+ Q_ARG(qreal, window->effectiveDevicePixelRatio()));
+ }
+
+ const bool forceRecreate = sizeHasChanged || multisampleHasChanged;
+ const bool usesFBO = scene3DRenderer->m_compositingMode == Scene3DItem::FBO;
+ // Not sure how we could support underlay rendering properly given Qt3D RHI will render into its own
+ // RHI RenderPasses prior to QtQuick and beginning a new RenderPass clears the screen
+ Q_ASSERT(usesFBO);
+ auto node = scene3DRenderer->m_node;
+ const bool generateNewTexture = m_texture.isNull() || forceRecreate;
+ const int samples = item->multisample() ? 4 : 1;
+
+ if (generateNewTexture) {
+ releaseRHIResources();
+
+ // Depth / Stencil
+ m_rhiDepthRenderBuffer = m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, m_lastSize, samples);
+ m_rhiDepthRenderBuffer->create();
+
+ // Color Attachment or Color Resolutin texture (if using MSAA)
+ m_rhiTexture = m_rhi->newTexture(QRhiTexture::RGBA8, m_lastSize, 1, QRhiTexture::RenderTarget|QRhiTexture::UsedAsTransferSource);
+ m_rhiTexture->create();
+
+ // Color Attachment description
+ QRhiTextureRenderTargetDescription renderTargetDesc;
+ renderTargetDesc.setDepthStencilBuffer(m_rhiDepthRenderBuffer);
+ if (samples > 1) {
+ m_rhiMSAARenderBuffer = m_rhi->newRenderBuffer(QRhiRenderBuffer::Color, m_lastSize, samples, {}, m_rhiTexture->format());
+ m_rhiMSAARenderBuffer->create();
+ QRhiColorAttachment att;
+ att.setRenderBuffer(m_rhiMSAARenderBuffer);
+ att.setResolveTexture(m_rhiTexture);
+ renderTargetDesc.setColorAttachments({ att });
+ } else {
+ renderTargetDesc.setColorAttachments({ m_rhiTexture });
+ }
+
+ m_rhiRenderTarget = m_rhi->newTextureRenderTarget(renderTargetDesc);
+ m_rhiRenderTargetPassDescriptor = m_rhiRenderTarget->newCompatibleRenderPassDescriptor();
+ m_rhiRenderTarget->setRenderPassDescriptor(m_rhiRenderTargetPassDescriptor);
+ m_rhiRenderTarget->create();
+
+ // Create QSGTexture from QRhiTexture
+ m_texture.reset(window->createTextureFromNativeObject(QQuickWindow::NativeObjectTexture,
+ m_rhiTexture->nativeTexture().object,
+ 0, m_lastSize, QQuickWindow::TextureHasAlphaChannel));
+
+ // Set the Default RenderTarget to use on the RHI Renderer
+ // Note: this will release all pipelines using previousRenderTarget
+ m_renderer->setDefaultRHIRenderTarget(m_rhiRenderTarget);
+ }
+
+ // Set texture on node
+ if (node && (!node->texture() || generateNewTexture))
+ node->setTexture(m_texture.data());
+
+ // Mark SGNodes as dirty so that QQuick will trigger some rendering
+ if (node)
+ node->markDirty(QSGNode::DirtyMaterial);
+
+ if (scene3DRenderer->m_aspectEngine->rootEntity() != item->entity())
+ scene3DRenderer->scheduleRootEntityChange();
+
+ item->update();
+}
+
+void Scene3DRenderer::RHIRenderer::beforeRendering(Scene3DRenderer *scene3DRenderer)
+{
+ // This is fired before QtQuick starts its RenderPass but after a RHI Command Buffer
+ // has been created and after the RHI Frame has begun
+
+ // This means the swap chain has been set up and that we only need to record commands
+
+ // On the Qt3D side this also means we shouldn't be calling m_rhi->beginFrame/endFrame
+ // and we should only be rendering using the provided swap chain
+
+ // Therefore, if a QRenderSurfaceSelector is used in the FrameGraph to render to another surface,
+ // this will not be supported with Scene3D/RHI
+
+
+ QMutexLocker l(&scene3DRenderer->m_windowMutex);
+ // Lock to ensure the window doesn't change while we are rendering
+ if (!scene3DRenderer->m_window || !scene3DRenderer->m_shouldRender)
+ return;
+ // Reset flag for next frame
+ scene3DRenderer->m_shouldRender = false;
+
+ // TO DO: We need to check what we do about 3DViews and Composition Mode as those are not
+ // directly applicable with RHI
+
+ // Retrieve Command Buffer used by QtQuick
+ QRhiCommandBuffer *cb = nullptr;
+ QSGRendererInterface *rif = scene3DRenderer->m_window ->rendererInterface();
+ QRhiSwapChain *swapchain = static_cast<QRhiSwapChain *>(rif->getResource(scene3DRenderer->m_window, QSGRendererInterface::RhiSwapchainResource));
+ if (swapchain) {
+ cb = swapchain->currentFrameCommandBuffer();
+ } else {
+ cb = static_cast<QRhiCommandBuffer *>(rif->getResource(scene3DRenderer->m_window, QSGRendererInterface::RhiRedirectCommandBuffer));
+ }
+
+ Q_ASSERT(cb);
+ // Tell Qt3D renderer to use provided command buffer
+ m_renderer->setRHICommandBuffer(cb);
+ m_renderer->render(false);
+
+ // Only show the node once Qt3D has rendered to it
+ // Avoids showing garbage on the first frame
+ if (scene3DRenderer->m_node)
+ scene3DRenderer->m_node->show();
+}
+
+void Scene3DRenderer::RHIRenderer::beforeRenderPassRecording(Scene3DRenderer *scene3DRenderer)
+{
+ Q_UNUSED(scene3DRenderer);
+}
+
+void Scene3DRenderer::RHIRenderer::shutdown(Scene3DRenderer *scene3DRenderer)
+{
+ if (scene3DRenderer->m_renderAspect)
+ m_renderer->shutdown();
+
+ releaseRHIResources();
+}
+
+void Scene3DRenderer::RHIRenderer::releaseRHIResources()
+{
+ delete m_rhiRenderTarget;
+ m_rhiRenderTarget = nullptr;
+
+ delete m_rhiTexture;
+ m_rhiTexture = nullptr;
+
+ delete m_rhiDepthRenderBuffer;
+ m_rhiDepthRenderBuffer = nullptr;
+
+ delete m_rhiMSAARenderBuffer;
+ m_rhiMSAARenderBuffer = nullptr;
+
+ delete m_rhiColorRenderBuffer;
+ m_rhiColorRenderBuffer = nullptr;
+
+ delete m_rhiRenderTargetPassDescriptor;
+ m_rhiRenderTargetPassDescriptor = nullptr;
+}
+
+Scene3DRenderer::QuickRenderer::QuickRenderer() {}
+Scene3DRenderer::QuickRenderer::~QuickRenderer() {}
+
} // namespace Qt3DRender
QT_END_NAMESPACE
diff --git a/src/quick3d/imports/scene3d/scene3drenderer_p.h b/src/quick3d/imports/scene3d/scene3drenderer_p.h
index 5c57f71e3..0f20bb3eb 100644
--- a/src/quick3d/imports/scene3d/scene3drenderer_p.h
+++ b/src/quick3d/imports/scene3d/scene3drenderer_p.h
@@ -59,6 +59,11 @@
QT_BEGIN_NAMESPACE
+class QRhi;
+class QRhiTextureRenderTarget;
+class QRhiRenderBuffer;
+class QRhiTexture;
+class QRhiRenderPassDescriptor;
class QQuickWindow;
class QSGTexture;
class QOpenGLFramebufferObject;
@@ -74,6 +79,10 @@ class Scene3DCleaner;
class Scene3DSGNode;
class Scene3DViews;
+namespace Render {
+class AbstractRenderer;
+}
+
class Scene3DRenderer : public QObject
{
Q_OBJECT
@@ -94,31 +103,83 @@ public:
return m_renderAspect;
}
public Q_SLOTS:
- void render();
void shutdown();
void onSceneGraphInvalidated();
void onWindowChanged(QQuickWindow *w);
private:
- QOpenGLFramebufferObject *createMultisampledFramebufferObject(const QSize &size);
- QOpenGLFramebufferObject *createFramebufferObject(const QSize &size);
- void beforeSynchronize();
void scheduleRootEntityChange();
+ class QuickRenderer
+ {
+ public:
+ // ctor/dtor cannot be inlined because of QScopedPointer with forward
+ // declared class
+ QuickRenderer();
+ virtual ~QuickRenderer();
+ virtual void initialize(Scene3DRenderer *scene3DRenderer,
+ Qt3DRender::Render::AbstractRenderer *renderer) = 0;
+ virtual void beforeSynchronize(Scene3DRenderer *scene3DRenderer) = 0;
+ virtual void beforeRendering(Scene3DRenderer *scene3DRenderer) = 0;
+ virtual void beforeRenderPassRecording(Scene3DRenderer *scene3DRenderer) = 0;
+ virtual void shutdown(Scene3DRenderer *sceneRenderer) = 0;
+
+ protected:
+ bool m_lastMultisample = false;
+ QSize m_lastSize;
+ QScopedPointer<QSGTexture> m_texture;
+ Qt3DRender::Render::AbstractRenderer *m_renderer = nullptr;
+ };
+
+ class GLRenderer : public QuickRenderer
+ {
+ public:
+ QOpenGLFramebufferObject *createMultisampledFramebufferObject(const QSize &size);
+ QOpenGLFramebufferObject *createFramebufferObject(const QSize &size);
+
+ void initialize(Scene3DRenderer *scene3DRenderer,
+ Qt3DRender::Render::AbstractRenderer *renderer) override;
+ void beforeSynchronize(Scene3DRenderer *scene3DRenderer) override;
+ void beforeRendering(Scene3DRenderer *scene3DRenderer) override;
+ void beforeRenderPassRecording(Scene3DRenderer *scene3DRenderer) override;
+ void shutdown(Scene3DRenderer *sceneRenderer) override;
+
+ private:
+ QScopedPointer<QOpenGLFramebufferObject> m_multisampledFBO;
+ QScopedPointer<QOpenGLFramebufferObject> m_finalFBO;
+ bool m_multisample = false;
+ quint32 m_textureId = 0;
+ };
+
+ class RHIRenderer : public QuickRenderer
+ {
+ public:
+ void initialize(Scene3DRenderer *scene3DRenderer,
+ Qt3DRender::Render::AbstractRenderer *renderer) override;
+ void beforeSynchronize(Scene3DRenderer *scene3DRenderer) override;
+ void beforeRendering(Scene3DRenderer *scene3DRenderer) override;
+ void beforeRenderPassRecording(Scene3DRenderer *scene3DRenderer) override;
+ void shutdown(Scene3DRenderer *sceneRenderer) override;
+
+ private:
+ void releaseRHIResources();
+
+ QRhiTexture *m_rhiTexture = nullptr;
+ QRhiRenderBuffer *m_rhiColorRenderBuffer = nullptr;
+ QRhiRenderBuffer *m_rhiMSAARenderBuffer = nullptr;
+ QRhiRenderBuffer *m_rhiDepthRenderBuffer = nullptr;
+ QRhiTextureRenderTarget *m_rhiRenderTarget = nullptr;
+ QRhiRenderPassDescriptor *m_rhiRenderTargetPassDescriptor = nullptr;
+ QRhi *m_rhi = nullptr;
+ };
+
Scene3DItem *m_item; // Will be released by the QQuickWindow/QML Engine
Qt3DCore::QAspectEngine *m_aspectEngine; // Will be released by the Scene3DItem
QRenderAspect *m_renderAspect; // Will be released by the aspectEngine
- QScopedPointer<QOpenGLFramebufferObject> m_multisampledFBO;
- QScopedPointer<QOpenGLFramebufferObject> m_finalFBO;
- QScopedPointer<QSGTexture> m_texture;
Scene3DSGNode *m_node; // Will be released by the QtQuick SceneGraph
QQuickWindow *m_window;
QMutex m_windowMutex;
- QSize m_lastSize;
- bool m_multisample;
- bool m_lastMultisample;
bool m_needsShutdown;
- bool m_forceRecreate;
bool m_shouldRender;
bool m_dirtyViews;
bool m_skipFrame;
@@ -126,7 +187,7 @@ private:
Scene3DItem::CompositingMode m_compositingMode;
QList<Scene3DView *> m_views;
bool m_resetRequested = false;
- quint32 m_textureId;
+ QuickRenderer *m_quickRenderer = nullptr;
friend class Scene3DItem;
};
diff --git a/src/render/backend/abstractrenderer_p.h b/src/render/backend/abstractrenderer_p.h
index 6f849128a..2eec1bc1b 100644
--- a/src/render/backend/abstractrenderer_p.h
+++ b/src/render/backend/abstractrenderer_p.h
@@ -69,6 +69,9 @@ class QScreen;
class QOpenGLTexture;
class QMouseEvent;
class QKeyEvent;
+class QRhi;
+class QRhiRenderTarget;
+class QRhiCommandBuffer;
namespace Qt3DCore {
class QAbstractFrameAdvanceService;
@@ -118,9 +121,16 @@ public:
};
Q_DECLARE_FLAGS(BackendNodeDirtySet, BackendNodeDirtyFlag)
+ enum RenderDriver {
+ Qt3D,
+ Scene3D,
+ };
+
virtual void dumpInfo() const = 0;
virtual API api() const = 0;
+ virtual void setRenderDriver(RenderDriver driver) = 0;
+ virtual RenderDriver renderDriver() const = 0;
virtual qint64 time() const = 0;
virtual void setTime(qint64 time) = 0;
@@ -172,8 +182,11 @@ public:
virtual QVariant executeCommand(const QStringList &args) = 0;
- // For QtQuick rendering (Scene2D)
+ // For QtQuick rendering (Scene3D/2D)
virtual void setOpenGLContext(QOpenGLContext *ctx) = 0;
+ virtual void setRHIContext(QRhi *ctx) = 0;
+ virtual void setDefaultRHIRenderTarget(QRhiRenderTarget *defaultTarget) = 0;
+ virtual void setRHICommandBuffer(QRhiCommandBuffer *commandBuffer) = 0;
virtual void setScreen(QScreen *) {}
virtual QScreen *screen() const { return nullptr; }
virtual bool accessOpenGLTexture(Qt3DCore::QNodeId nodeId, QOpenGLTexture **texture, QMutex **lock, bool readonly) = 0;
diff --git a/src/render/frontend/qrenderapi.h b/src/render/frontend/qrenderapi.h
index fc046642a..df1a4a692 100644
--- a/src/render/frontend/qrenderapi.h
+++ b/src/render/frontend/qrenderapi.h
@@ -51,6 +51,7 @@ enum class API {
Vulkan,
DirectX,
Metal,
+ RHI,
Null
};
diff --git a/src/render/frontend/qrenderaspect.cpp b/src/render/frontend/qrenderaspect.cpp
index bd6d7d831..3c5c2c053 100644
--- a/src/render/frontend/qrenderaspect.cpp
+++ b/src/render/frontend/qrenderaspect.cpp
@@ -630,29 +630,6 @@ QRenderAspect::~QRenderAspect()
{
}
-// Called by Scene3DRenderer only
-void QRenderAspectPrivate::renderInitialize(QOpenGLContext *context)
-{
- if (m_renderer->api() == API::OpenGL)
- m_renderer->setOpenGLContext(context);
- m_renderer->initialize();
-}
-
-/*! \internal */
-void QRenderAspectPrivate::render(bool swapBuffers)
-{
- m_renderer->render(swapBuffers);
-}
-
-/*
- * \internal
- * Only called when rendering with QtQuick 2 and a Scene3D item
- */
-void QRenderAspectPrivate::renderShutdown()
-{
- m_renderer->shutdown();
-}
-
std::vector<Qt3DCore::QAspectJobPtr> QRenderAspect::jobsToExecute(qint64 time)
{
using namespace Render;
diff --git a/src/render/frontend/qrenderaspect_p.h b/src/render/frontend/qrenderaspect_p.h
index 71dcf0468..b1b7bedc3 100644
--- a/src/render/frontend/qrenderaspect_p.h
+++ b/src/render/frontend/qrenderaspect_p.h
@@ -71,7 +71,7 @@ QT_BEGIN_NAMESPACE
class QSurface;
class QScreen;
-
+class QRhi;
namespace Qt3DRender {
class QSceneImporter;
@@ -113,9 +113,6 @@ public:
void unregisterBackendTypes();
void loadSceneParsers();
void loadRenderPlugin(const QString &pluginName);
- void renderInitialize(QOpenGLContext *context);
- void render(bool swapBuffers = true);
- void renderShutdown();
void registerBackendType(const QMetaObject &, const Qt3DCore::QBackendNodeMapperPtr &functor);
std::vector<Qt3DCore::QAspectJobPtr> createGeometryRendererJobs() const;
std::vector<Qt3DCore::QAspectJobPtr> createPreRendererJobs() const;