summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
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;