diff options
17 files changed, 494 insertions, 178 deletions
diff --git a/examples/qt3d/compute-particles/ParticlesScene.qml b/examples/qt3d/compute-particles/ParticlesScene.qml index e63c6cb91..2c74f51b5 100644 --- a/examples/qt3d/compute-particles/ParticlesScene.qml +++ b/examples/qt3d/compute-particles/ParticlesScene.qml @@ -117,7 +117,6 @@ Entity { Buffer { id: particleBuffer - type: Buffer.VertexBuffer // struct ParticleData // { // vec3 position; // Aligned to 4 floats diff --git a/examples/qt3d/compute-particles/main.qml b/examples/qt3d/compute-particles/main.qml index de5666fad..1375b312a 100644 --- a/examples/qt3d/compute-particles/main.qml +++ b/examples/qt3d/compute-particles/main.qml @@ -50,7 +50,7 @@ import QtQuick 2.0 import QtQuick.Scene3D 2.0 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.0 import QtQuick.Layouts 1.1 Item { @@ -84,8 +84,8 @@ Item { height: 35 id: stepSlider Layout.fillWidth: true - minimumValue: 0.0 - maximumValue: 2 + from: 0.0 + to: 2 value: 0.4 } } @@ -98,8 +98,8 @@ Item { height: 35 id: collisionSlider Layout.fillWidth: true - minimumValue: 0.0 - maximumValue: 2 + from: 0.0 + to: 2 value: 0.2 } } @@ -114,31 +114,32 @@ Item { text: "Particles Shape:" color: "white" } - ExclusiveGroup { + ButtonGroup { + exclusive: true id: particlesTypeGroup } CheckBox { text: "Sphere" checked: true - exclusiveGroup: particlesTypeGroup + ButtonGroup.group: particlesTypeGroup onClicked: scene.particlesShape = scene._SPHERE } CheckBox { text: "Cube" checked: false - exclusiveGroup: particlesTypeGroup + ButtonGroup.group: particlesTypeGroup onClicked: scene.particlesShape = scene._CUBE } CheckBox { text: "Cylinder" checked: false - exclusiveGroup: particlesTypeGroup + ButtonGroup.group: particlesTypeGroup onClicked: scene.particlesShape = scene._CYLINDER } CheckBox { text: "Torus" checked: false - exclusiveGroup: particlesTypeGroup + ButtonGroup.group: particlesTypeGroup onClicked: scene.particlesShape = scene._TORUS } } diff --git a/src/plugins/renderers/rhi/graphicshelpers/submissioncontext.cpp b/src/plugins/renderers/rhi/graphicshelpers/submissioncontext.cpp index 17119e504..15add3310 100644 --- a/src/plugins/renderers/rhi/graphicshelpers/submissioncontext.cpp +++ b/src/plugins/renderers/rhi/graphicshelpers/submissioncontext.cpp @@ -1665,20 +1665,32 @@ SubmissionContext::ShaderCreationInfo SubmissionContext::createShaderProgram(RHI // Compile shaders const auto &shaderCode = shader->shaderCode(); QShaderBaker b; - b.setGeneratedShaders({ - { QShader::SpirvShader, 100 }, -#ifndef QT_NO_OPENGL - { QShader::GlslShader, glslVersionForFormat(format()) }, + QVector<QShaderBaker::GeneratedShader> generatedShaders; + +#if QT_FEATURE_vulkan + generatedShaders.push_back({ QShader::SpirvShader, 100 }); #endif - { QShader::HlslShader, QShaderVersion(50) }, - { QShader::MslShader, QShaderVersion(12) }, - }); - b.setGeneratedShaderVariants({ QShader::Variant {}, #ifndef QT_NO_OPENGL - QShader::Variant {}, + // GLES2 RHI backend does not seem to support compute + //if (shaderCode.at(QShaderProgram::Compute).isEmpty()) + { + generatedShaders.push_back({ QShader::GlslShader, glslVersionForFormat(format()) }); + } +#endif + +#if Q_OS_WIN + generatedShaders.push_back({ QShader::HlslShader, QShaderVersion(50) }); +#endif + +#if Q_OS_MACOS + generatedShaders.push_back({ QShader::MslShader, QShaderVersion(12) }); #endif - QShader::Variant {}, QShader::Variant {} }); + + QVector<QShader::Variant> generatedShaderVariants(generatedShaders.size()); + + b.setGeneratedShaders(generatedShaders); + b.setGeneratedShaderVariants(generatedShaderVariants); // TODO handle caching as QShader does not have a built-in mechanism for that QString logs; diff --git a/src/plugins/renderers/rhi/io/rhibuffer.cpp b/src/plugins/renderers/rhi/io/rhibuffer.cpp index 0f19bf250..fb1dd9f81 100644 --- a/src/plugins/renderers/rhi/io/rhibuffer.cpp +++ b/src/plugins/renderers/rhi/io/rhibuffer.cpp @@ -49,19 +49,23 @@ namespace Render { namespace Rhi { namespace { -QRhiBuffer::UsageFlag bufferTypeToRhi(RHIBuffer::Type t) +QRhiBuffer::UsageFlags bufferTypeToRhi(RHIBuffer::Type t) { - switch (t) { - case RHIBuffer::Type::ArrayBuffer: - return QRhiBuffer::VertexBuffer; - case RHIBuffer::Type::IndexBuffer: - return QRhiBuffer::IndexBuffer; - case RHIBuffer::Type::UniformBuffer: - return QRhiBuffer::UniformBuffer; - default: - RHI_UNIMPLEMENTED; - return QRhiBuffer::StorageBuffer; - } + QRhiBuffer::UsageFlags flag{}; + + if (t & RHIBuffer::Type::ArrayBuffer) + flag |= QRhiBuffer::VertexBuffer; + + if (t & RHIBuffer::Type::IndexBuffer) + flag |= QRhiBuffer::IndexBuffer; + + if (t & RHIBuffer::Type::UniformBuffer) + flag |= QRhiBuffer::UniformBuffer; + + if (t & RHIBuffer::Type::ShaderStorageBuffer) + flag |= QRhiBuffer::StorageBuffer; + + return flag; } } @@ -90,8 +94,11 @@ bool RHIBuffer::bind(SubmissionContext *ctx, Type t) const auto usage = bufferTypeToRhi(t); m_rhiBuffer = ctx->rhi()->newBuffer(kind, usage, m_allocSize); + assert(m_rhiBuffer); + if (!m_rhiBuffer->create()) return false; + #if defined(QT_DEBUG) { // for debug: we set the buffer to zero @@ -99,10 +106,15 @@ bool RHIBuffer::bind(SubmissionContext *ctx, Type t) (ctx->m_currentUpdates->*uploadMethod)(m_rhiBuffer, 0, m_allocSize, ptr); delete[] ptr; } -#endif } +#endif assert(m_rhiBuffer); +#if defined(QT_DEBUG) + // RHI does not seem to support using the same buffer with different types + assert(m_rhiBuffer->usage() == bufferTypeToRhi(t)); +#endif + for (const std::pair<QByteArray, int> &pair : this->m_datasToUpload) { const QByteArray &data = pair.first; int offset = pair.second; diff --git a/src/plugins/renderers/rhi/io/rhibuffer_p.h b/src/plugins/renderers/rhi/io/rhibuffer_p.h index 4d13836b7..7d4a3c54f 100644 --- a/src/plugins/renderers/rhi/io/rhibuffer_p.h +++ b/src/plugins/renderers/rhi/io/rhibuffer_p.h @@ -70,13 +70,13 @@ public: RHIBuffer(); enum Type { - ArrayBuffer = 0, - UniformBuffer, - IndexBuffer, - ShaderStorageBuffer, - PixelPackBuffer, - PixelUnpackBuffer, - DrawIndirectBuffer + ArrayBuffer = 1 << 0, + UniformBuffer = 1 << 1, + IndexBuffer = 1 << 2, + ShaderStorageBuffer = 1 << 3, + PixelPackBuffer = 1 << 4, + PixelUnpackBuffer = 1 << 5, + DrawIndirectBuffer = 1 << 6 }; bool bind(SubmissionContext *ctx, Type t); diff --git a/src/plugins/renderers/rhi/managers/rhihandle_types_p.h b/src/plugins/renderers/rhi/managers/rhihandle_types_p.h index 3a042a9be..8ae440e6d 100644 --- a/src/plugins/renderers/rhi/managers/rhihandle_types_p.h +++ b/src/plugins/renderers/rhi/managers/rhihandle_types_p.h @@ -64,11 +64,13 @@ namespace Rhi { class RHIBuffer; class RHITexture; class RHIGraphicsPipeline; +class RHIComputePipeline; struct RHIRenderTarget; typedef Qt3DCore::QHandle<RHIBuffer> HRHIBuffer; typedef Qt3DCore::QHandle<RHITexture> HRHITexture; typedef Qt3DCore::QHandle<RHIGraphicsPipeline> HRHIGraphicsPipeline; +typedef Qt3DCore::QHandle<RHIComputePipeline> HRHIComputePipeline; typedef Qt3DCore::QHandle<RHIRenderTarget> HRHIRenderTarget; } // namespace Rhi diff --git a/src/plugins/renderers/rhi/managers/rhiresourcemanagers.cpp b/src/plugins/renderers/rhi/managers/rhiresourcemanagers.cpp index 387464fe4..8fca77247 100644 --- a/src/plugins/renderers/rhi/managers/rhiresourcemanagers.cpp +++ b/src/plugins/renderers/rhi/managers/rhiresourcemanagers.cpp @@ -52,7 +52,8 @@ RHIResourceManagers::RHIResourceManagers() m_rhiShaderManager(new RHIShaderManager()), m_rhiTextureManager(new RHITextureManager()), m_rhiRenderTargetManager(new RHIRenderTargetManager()), - m_rhiGraphicsPipelineManager(new RHIGraphicsPipelineManager()) + m_rhiGraphicsPipelineManager(new RHIGraphicsPipelineManager()), + m_rhiComputePipelineManager(new RHIComputePipelineManager()) { } @@ -63,6 +64,7 @@ RHIResourceManagers::~RHIResourceManagers() delete m_rhiBufferManager; delete m_rhiRenderTargetManager; delete m_rhiGraphicsPipelineManager; + delete m_rhiComputePipelineManager; } void RHIResourceManagers::releaseAllResources() @@ -82,6 +84,7 @@ void RHIResourceManagers::releaseAllResources() // releaseAll(m_rhiShaderManager); releaseAll(m_rhiRenderTargetManager); releaseAll(m_rhiGraphicsPipelineManager); + releaseAll(m_rhiComputePipelineManager); } int RHIGraphicsPipelineManager::getIdForAttributeVec(const std::vector<AttributeInfo> &attributesInfo) diff --git a/src/plugins/renderers/rhi/managers/rhiresourcemanagers_p.h b/src/plugins/renderers/rhi/managers/rhiresourcemanagers_p.h index 48e9d8300..a323632fb 100644 --- a/src/plugins/renderers/rhi/managers/rhiresourcemanagers_p.h +++ b/src/plugins/renderers/rhi/managers/rhiresourcemanagers_p.h @@ -109,6 +109,14 @@ private: std::vector<AttributeInfoVec> m_attributesInfo; }; +class Q_AUTOTEST_EXPORT RHIComputePipelineManager + : public Qt3DCore::QResourceManager<RHIComputePipeline, ComputePipelineIdentifier, + Qt3DCore::NonLockingPolicy> +{ +public: + RHIComputePipelineManager() { } +}; + class Q_AUTOTEST_EXPORT RHIResourceManagers { public: @@ -123,6 +131,10 @@ public: { return m_rhiGraphicsPipelineManager; } + inline RHIComputePipelineManager *rhiComputePipelineManager() const noexcept + { + return m_rhiComputePipelineManager; + } void releaseAllResources(); @@ -132,6 +144,7 @@ private: RHITextureManager *m_rhiTextureManager; RHIRenderTargetManager *m_rhiRenderTargetManager; RHIGraphicsPipelineManager *m_rhiGraphicsPipelineManager; + RHIComputePipelineManager *m_rhiComputePipelineManager; }; inline uint qHash(const GraphicsPipelineIdentifier &key, uint seed = 0) @@ -152,6 +165,20 @@ inline bool operator==(const GraphicsPipelineIdentifier &a, const GraphicsPipeli a.renderViewIndex == b.renderViewIndex; } +inline uint qHash(const ComputePipelineIdentifier &key, uint seed = 0) +{ + using QT_PREPEND_NAMESPACE(qHash); + seed = qHash(key.shader, seed); + seed = qHash(key.renderViewIndex, seed); + return seed; +} + +inline bool operator==(const ComputePipelineIdentifier &a, const ComputePipelineIdentifier &b) +{ + return a.shader == b.shader && + a.renderViewIndex == b.renderViewIndex; +} + } // Rhi } // Render @@ -159,6 +186,7 @@ inline bool operator==(const GraphicsPipelineIdentifier &a, const GraphicsPipeli } // Qt3DRender Q_DECLARE_RESOURCE_INFO(Qt3DRender::Render::Rhi::RHIGraphicsPipeline, Q_REQUIRES_CLEANUP) +Q_DECLARE_RESOURCE_INFO(Qt3DRender::Render::Rhi::RHIComputePipeline, Q_REQUIRES_CLEANUP) Q_DECLARE_RESOURCE_INFO(Qt3DRender::Render::Rhi::RHITexture, Q_REQUIRES_CLEANUP) Q_DECLARE_RESOURCE_INFO(Qt3DRender::Render::Rhi::RHIBuffer, Q_REQUIRES_CLEANUP) Q_DECLARE_RESOURCE_INFO(Qt3DRender::Render::Rhi::RHIRenderTarget, Q_REQUIRES_CLEANUP) diff --git a/src/plugins/renderers/rhi/renderer/pipelineuboset_p.h b/src/plugins/renderers/rhi/renderer/pipelineuboset_p.h index 699ff0602..d56adab12 100644 --- a/src/plugins/renderers/rhi/renderer/pipelineuboset_p.h +++ b/src/plugins/renderers/rhi/renderer/pipelineuboset_p.h @@ -110,7 +110,8 @@ private: std::vector<const RenderCommand *> m_renderCommands; RHIResourceManagers *m_resourceManagers = nullptr; - friend class RHIGraphicsPipeline; + template<typename Pipeline, typename Key> + friend class RHIPipelineBase; }; diff --git a/src/plugins/renderers/rhi/renderer/rendercommand.cpp b/src/plugins/renderers/rhi/renderer/rendercommand.cpp index 9e82192f5..06fb3bec0 100644 --- a/src/plugins/renderers/rhi/renderer/rendercommand.cpp +++ b/src/plugins/renderers/rhi/renderer/rendercommand.cpp @@ -46,6 +46,22 @@ namespace Qt3DRender { namespace Render { namespace Rhi { +bool RenderCommand::Pipeline::isValid() const noexcept +{ + struct { + bool operator()(RHIGraphicsPipeline* pipeline) const noexcept { + return pipeline && pipeline->pipeline(); + } + bool operator()(RHIComputePipeline* pipeline) const noexcept { + return pipeline && pipeline->pipeline(); + } + bool operator()(std::monostate) const noexcept { + return false; + } + } visitor; + return this->visit(visitor); +} + RenderCommand::RenderCommand() : m_rhiShader(nullptr), m_stateSet(nullptr), @@ -71,7 +87,7 @@ RenderCommand::RenderCommand() indexAttribute(nullptr), indexBuffer(nullptr), m_commandUBO(), - pipeline(nullptr) + pipeline() { m_workGroups[0] = 0; @@ -81,7 +97,7 @@ RenderCommand::RenderCommand() bool RenderCommand::isValid() const noexcept { - return m_isValid && m_rhiShader && pipeline && pipeline->pipeline(); + return m_isValid && m_rhiShader && pipeline.isValid(); } bool operator==(const RenderCommand &a, const RenderCommand &b) noexcept diff --git a/src/plugins/renderers/rhi/renderer/rendercommand_p.h b/src/plugins/renderers/rhi/renderer/rendercommand_p.h index 6f0169acf..031b4fca2 100644 --- a/src/plugins/renderers/rhi/renderer/rendercommand_p.h +++ b/src/plugins/renderers/rhi/renderer/rendercommand_p.h @@ -64,6 +64,7 @@ #include <QMatrix4x4> #include <QtGui/private/qrhi_p.h> #include <Qt3DCore/qattribute.h> +#include <variant> QT_BEGIN_NAMESPACE class QRhiGraphicsPipeline; @@ -89,6 +90,7 @@ namespace Rhi { class RHIShader; class RHIGraphicsPipeline; +class RHIComputePipeline; struct CommandUBO { @@ -125,7 +127,7 @@ public: HMaterial m_material; // Purely used to ease sorting (minimize stage changes, binding changes // ....) - RHIShader *m_rhiShader; // GL Shader to be used at render time + RHIShader *m_rhiShader; // Shader to be used at render time Qt3DCore::QNodeId m_shaderId; // Shader for given pass and mesh ShaderParameterPack m_parameterPack; // Might need to be reworked so as to be able to destroy // the Texture while submission is happening. @@ -174,9 +176,35 @@ public: QRhiBuffer *indexBuffer {}; CommandUBO m_commandUBO; - RHIGraphicsPipeline *pipeline {}; QRhiShaderResourceBindings *shaderResourceBindings = nullptr; std::vector<QRhiShaderResourceBinding> resourcesBindings; + + struct Pipeline : std::variant<std::monostate, RHIGraphicsPipeline *, RHIComputePipeline*> + { + using variant::variant; + + bool isValid() const noexcept; + + RHIGraphicsPipeline* graphics() const noexcept + { + auto ptr = std::get_if<RHIGraphicsPipeline*>(this); + return ptr ? *ptr : nullptr; + } + + RHIComputePipeline* compute() const noexcept + { + auto ptr = std::get_if<RHIComputePipeline*>(this); + return ptr ? *ptr : nullptr; + } + + template<typename F> + auto visit(F&& f) const noexcept(noexcept(std::visit(f, (const variant&) *this))) + { + return std::visit(f, (const variant&) *this); + } + }; + + Pipeline pipeline {}; }; Q_AUTOTEST_EXPORT bool operator==(const RenderCommand &a, const RenderCommand &b) noexcept; diff --git a/src/plugins/renderers/rhi/renderer/renderer.cpp b/src/plugins/renderers/rhi/renderer/renderer.cpp index f33626b8f..73d15d51b 100644 --- a/src/plugins/renderers/rhi/renderer/renderer.cpp +++ b/src/plugins/renderers/rhi/renderer/renderer.cpp @@ -894,7 +894,7 @@ void Renderer::updateGraphicsPipeline(RenderCommand &cmd, RenderView *rv, graphicsPipeline->uboSet()->addRenderCommand(cmd); // Store association between RV and pipeline - if (auto& pipelines = m_rvToPipelines[rv]; !Qt3DCore::contains(pipelines, graphicsPipeline)) + if (auto& pipelines = m_rvToGraphicsPipelines[rv]; !Qt3DCore::contains(pipelines, graphicsPipeline)) pipelines.push_back(graphicsPipeline); // Record RHIGraphicsPipeline into command for later use @@ -1015,6 +1015,95 @@ void Renderer::buildGraphicsPipelines(RHIGraphicsPipeline *graphicsPipeline, return onFailure(); } +void Renderer::updateComputePipeline(RenderCommand &cmd, RenderView *rv, int renderViewIndex) +{ + if (!cmd.m_rhiShader) { + qDebug() << "Warning: command has no shader"; + return; + } + + // Try to retrieve existing pipeline + RHIComputePipelineManager *pipelineManager = m_RHIResourceManagers->rhiComputePipelineManager(); + const ComputePipelineIdentifier pipelineKey { cmd.m_shaderId, renderViewIndex }; + RHIComputePipeline *computePipeline = pipelineManager->lookupResource(pipelineKey); + if (computePipeline == nullptr) { + // Init UBOSet the first time we allocate a new pipeline + computePipeline = pipelineManager->getOrCreateResource(pipelineKey); + computePipeline->setKey(pipelineKey); + computePipeline->uboSet()->setResourceManager(m_RHIResourceManagers); + computePipeline->uboSet()->initializeLayout(m_submissionContext.data(), cmd.m_rhiShader); + } + + if (!computePipeline) { + qDebug() << "Warning : could not create a compute pipeline"; + return; + } + + // Increase score so that we know the pipeline was used for this frame and shouldn't be + // destroyed + computePipeline->increaseScore(); + + // Record command reference in UBOSet + computePipeline->uboSet()->addRenderCommand(cmd); + + // Store association between RV and pipeline + if (auto& pipelines = m_rvToComputePipelines[rv]; !Qt3DCore::contains(pipelines, computePipeline)) + pipelines.push_back(computePipeline); + + // Record RHIGraphicsPipeline into command for later use + cmd.pipeline = computePipeline; + + // TO DO: Set to true if geometry, shader or render state dirty + bool requiredRebuild = false; + + // Note: we can rebuild add/remove things from the QRhiShaderResourceBindings after having + // created the pipeline and rebuild it. Changes should be picked up automatically + + // Create pipeline if it doesn't exist or needs to be updated + if (computePipeline->pipeline() == nullptr || requiredRebuild) + buildComputePipelines(computePipeline, rv, cmd); +} + +void Renderer::buildComputePipelines(RHIComputePipeline *computePipeline, + RenderView *rv, + const RenderCommand &cmd) +{ + const auto bufManager = m_RHIResourceManagers->rhiBufferManager(); + auto onFailure = [&] { computePipeline->cleanup(); }; + + PipelineUBOSet *uboSet = computePipeline->uboSet(); + RHIShader *shader = cmd.m_rhiShader; + + // Setup shaders + const QShader& computeShader = cmd.m_rhiShader->shaderStage(QShader::ComputeStage); + if (!computeShader.isValid()) { + return onFailure(); + } + + // Set Resource Bindings + const std::vector<QRhiShaderResourceBinding> resourceBindings = uboSet->resourceLayout(shader); + QRhiShaderResourceBindings *shaderResourceBindings = + m_submissionContext->rhi()->newShaderResourceBindings(); + computePipeline->setShaderResourceBindings(shaderResourceBindings); + + shaderResourceBindings->setBindings(resourceBindings.cbegin(), resourceBindings.cend()); + if (!shaderResourceBindings->create()) { + return onFailure(); + } + + // Create pipeline + QRhiComputePipeline *pipeline = m_submissionContext->rhi()->newComputePipeline(); + computePipeline->setPipeline(pipeline); + + pipeline->setShaderStage(QRhiShaderStage{QRhiShaderStage::Compute, computeShader}); + pipeline->setShaderResourceBindings(shaderResourceBindings); + + // QRhiComputePiple has no render states + + if (!pipeline->create()) + return onFailure(); +} + void Renderer::createRenderTarget(RenderView *rv, RHIRenderTarget *target) { // Used in case of failure @@ -1182,18 +1271,25 @@ Renderer::prepareCommandsSubmission(const std::vector<RenderView *> &renderViews // -> The number of RenderCommands used by each pipeline // This will allows us to generate UBOs based on the number of commands/rv we have - RHIGraphicsPipelineManager *pipelineManager = - m_RHIResourceManagers->rhiGraphicsPipelineManager(); - const std::vector<HRHIGraphicsPipeline> &graphicsPipelinesHandles = pipelineManager->activeHandles(); + RHIGraphicsPipelineManager *graphicsPipelineManager = m_RHIResourceManagers->rhiGraphicsPipelineManager(); + const std::vector<HRHIGraphicsPipeline> &graphicsPipelinesHandles = graphicsPipelineManager->activeHandles(); for (HRHIGraphicsPipeline pipelineHandle : graphicsPipelinesHandles) { - RHIGraphicsPipeline *pipeline = pipelineManager->data(pipelineHandle); + RHIGraphicsPipeline *pipeline = graphicsPipelineManager->data(pipelineHandle); + // Reset PipelineUBOSet + pipeline->uboSet()->clear(); + } + RHIComputePipelineManager *computePipelineManager = m_RHIResourceManagers->rhiComputePipelineManager(); + const std::vector<HRHIComputePipeline> &computePipelinesHandles = computePipelineManager->activeHandles(); + for (HRHIComputePipeline pipelineHandle : computePipelinesHandles) { + RHIComputePipeline *pipeline = computePipelineManager->data(pipelineHandle); // Reset PipelineUBOSet pipeline->uboSet()->clear(); } // Clear any reference between RV and Pipelines we had // as we are about to rebuild these - m_rvToPipelines.clear(); + m_rvToGraphicsPipelines.clear(); + m_rvToComputePipelines.clear(); // We need to have a single RHI RenderPass per RenderTarget // as creating the pass clears the buffers @@ -1257,6 +1353,8 @@ Renderer::prepareCommandsSubmission(const std::vector<RenderView *> &renderViews // By this time shaders have been loaded RHIShader *shader = command.m_rhiShader; Q_ASSERT(shader); + + updateComputePipeline(command, rv, i); } }); } @@ -1265,10 +1363,13 @@ Renderer::prepareCommandsSubmission(const std::vector<RenderView *> &renderViews // has, we can allocate/reallocate UBOs with correct size for each pipelines for (RenderView *rv : renderViews) { // Allocate UBOs for pipelines used in current RV - const std::vector<RHIGraphicsPipeline *> &rvPipelines = m_rvToPipelines[rv]; - for (RHIGraphicsPipeline *pipeline : rvPipelines) { + const std::vector<RHIGraphicsPipeline *> &rvGraphicsPipelines = m_rvToGraphicsPipelines[rv]; + for (RHIGraphicsPipeline *pipeline : rvGraphicsPipelines) + pipeline->uboSet()->allocateUBOs(m_submissionContext.data()); + // Allocate UBOs for pipelines used in current RV + const std::vector<RHIComputePipeline *> &rvComputePipelines = m_rvToComputePipelines[rv]; + for (RHIComputePipeline *pipeline : rvComputePipelines) pipeline->uboSet()->allocateUBOs(m_submissionContext.data()); - } } // Unset dirtiness on Geometry and Attributes @@ -2234,9 +2335,17 @@ void Renderer::performDraw(RenderCommand *command) // m_submissionContext->disablePrimitiveRestart(); } -void Renderer::performCompute(const RenderView *, RenderCommand *command) +bool Renderer::performCompute(QRhiCommandBuffer *cb, const RenderCommand &command) { - RHI_UNIMPLEMENTED; + RHIComputePipeline *pipeline = command.pipeline.compute(); + if (!pipeline) + return true; + cb->setComputePipeline(pipeline->pipeline()); + cb->setShaderResources(); + + cb->dispatch(command.m_workGroups[0], command.m_workGroups[1], command.m_workGroups[2]); + + Q_UNUSED(command); //* { //* RHIShader *shader = @@ -2251,14 +2360,15 @@ void Renderer::performCompute(const RenderView *, RenderCommand *command) //* command->m_workGroups[1], //* command->m_workGroups[2]); //* } - //* // HACK: Reset the compute flag to dirty - //* m_dirtyBits.marked |= AbstractRenderer::ComputeDirty; + // HACK: Reset the compute flag to dirty + m_dirtyBits.marked |= AbstractRenderer::ComputeDirty; //* #if defined(QT3D_RENDER_ASPECT_RHI_DEBUG) //* int err = m_submissionContext->openGLContext()->functions()->glGetError(); //* if (err) //* qCWarning(Rendering) << "GL error after drawing mesh:" << QString::number(err, 16); //* #endif + return true; } static auto rhiIndexFormat(QAttribute::VertexBaseType type) @@ -2276,12 +2386,48 @@ static auto rhiIndexFormat(QAttribute::VertexBaseType type) bool Renderer::uploadBuffersForCommand(QRhiCommandBuffer *cb, const RenderView *rv, RenderCommand &command) { - Q_UNUSED(cb); - Q_UNUSED(rv); - RHIGraphicsPipeline *graphicsPipeline = command.pipeline; - if (!graphicsPipeline) - return true; + struct + { + Renderer &self; + RenderCommand &command; + bool operator()(RHIGraphicsPipeline* pipeline) const noexcept { + if (!pipeline) + return true; + + return self.uploadBuffersForCommand(pipeline, command); + } + bool operator()(RHIComputePipeline* pipeline) const noexcept { + if (!pipeline) + return true; + + return self.uploadBuffersForCommand(pipeline, command); + } + bool operator()(std::monostate) { + return false; + } + } vis{*this, command}; + + if (!command.pipeline.visit(vis)) + return false; + for (const BlockToUBO &pack : command.m_parameterPack.uniformBuffers()) { + Buffer *cpuBuffer = nodeManagers()->bufferManager()->lookupResource(pack.m_bufferID); + RHIBuffer *ubo = m_submissionContext->rhiBufferForRenderBuffer(cpuBuffer); + if (!ubo->bind(&*m_submissionContext, RHIBuffer::UniformBuffer)) + return false; + } + for (const BlockToSSBO &pack : command.m_parameterPack.shaderStorageBuffers()) { + Buffer *cpuBuffer = nodeManagers()->bufferManager()->lookupResource(pack.m_bufferID); + RHIBuffer *ubo = m_submissionContext->rhiBufferForRenderBuffer(cpuBuffer); + if (!ubo->bind(&*m_submissionContext, RHIBuffer::ShaderStorageBuffer)) + return false; + } + + return true; +} + +bool Renderer::uploadBuffersForCommand(RHIGraphicsPipeline* graphicsPipeline, RenderCommand &command) +{ // Create the vertex input description // Note: we have to bind the buffers here -> which will trigger the actual @@ -2297,11 +2443,10 @@ bool Renderer::uploadBuffersForCommand(QRhiCommandBuffer *cb, const RenderView * // TODO isn't there a more efficient way than doing three hash lookups ? Attribute *attrib = m_nodesManager->attributeManager()->lookupResource(attribute_id); Buffer *buffer = m_nodesManager->bufferManager()->lookupResource(attrib->bufferId()); - RHIBuffer *hbuf = - m_RHIResourceManagers->rhiBufferManager()->lookupResource(buffer->peerId()); + RHIBuffer *hbuf = m_RHIResourceManagers->rhiBufferManager()->lookupResource(buffer->peerId()); switch (attrib->attributeType()) { case QAttribute::VertexAttribute: { - if (!hbuf->bind(&*m_submissionContext, RHIBuffer::Type::ArrayBuffer)) + if (!hbuf->bind(&*m_submissionContext, RHIBuffer::Type((int)RHIBuffer::Type::ArrayBuffer | (int)RHIBuffer::Type::ShaderStorageBuffer))) return false; assert(hbuf->rhiBuffer()); // Find Binding for Attribute @@ -2328,20 +2473,18 @@ bool Renderer::uploadBuffersForCommand(QRhiCommandBuffer *cb, const RenderView * } } - for (const BlockToUBO &pack : command.m_parameterPack.uniformBuffers()) { - Buffer *cpuBuffer = nodeManagers()->bufferManager()->lookupResource(pack.m_bufferID); - RHIBuffer *ubo = m_submissionContext->rhiBufferForRenderBuffer(cpuBuffer); - if (!ubo->bind(&*m_submissionContext, RHIBuffer::UniformBuffer)) - return false; - } + return true; +} +bool Renderer::uploadBuffersForCommand(RHIComputePipeline* computePipeline, RenderCommand &command) +{ return true; } bool Renderer::performDraw(QRhiCommandBuffer *cb, const QRhiViewport &vp, const QRhiScissor *scissor, RenderCommand &command) { - RHIGraphicsPipeline *pipeline = command.pipeline; + RHIGraphicsPipeline *pipeline = command.pipeline.graphics(); if (!pipeline) return true; @@ -2413,16 +2556,27 @@ bool Renderer::executeCommandsSubmission(const RHIPassInfo &passInfo) // Render drawing commands // Upload UBOs for pipelines used in current RV - const std::vector<RHIGraphicsPipeline *> &rvPipelines = m_rvToPipelines[rv]; - for (RHIGraphicsPipeline *pipeline : rvPipelines) { + const std::vector<RHIGraphicsPipeline *> &rvGraphicsPipelines = m_rvToGraphicsPipelines[rv]; + for (RHIGraphicsPipeline *pipeline : rvGraphicsPipelines) + pipeline->uboSet()->uploadUBOs(m_submissionContext.data(), rv); + + const std::vector<RHIComputePipeline *> &rvComputePipelines = m_rvToComputePipelines[rv]; + for (RHIComputePipeline *pipeline : rvComputePipelines) pipeline->uboSet()->uploadUBOs(m_submissionContext.data(), rv); - } // Upload Buffers for Commands rv->forEachCommand([&] (RenderCommand &command) { if (Q_UNLIKELY(!command.isValid())) return; + if (!uploadBuffersForCommand(cb, rv, command)) { + // Something went wrong trying to upload buffers + // -> likely that frontend buffer has no initial data + qCWarning(Backend) << "Failed to upload buffers"; + // Prevents further processing which could be catastrophic + command.m_isValid = false; + } + if (command.m_type == RenderCommand::Draw) { if (!uploadBuffersForCommand(cb, rv, command)) { // Something went wrong trying to upload buffers @@ -2459,26 +2613,7 @@ bool Renderer::executeCommandsSubmission(const RHIPassInfo &passInfo) rhiRenderTarget = m_submissionContext->currentSwapChain()->currentFrameRenderTarget(); } - // TO DO: should be moved elsewhere - // Perform compute actions - // cb->beginComputePass(m_submissionContext->m_currentUpdates); - // for (RenderCommand &command : commands) { - // if (command.m_type == RenderCommand::Compute) { - // performCompute(rv, &command); - // } - // } - // cb->endComputePass(); - // m_submissionContext->m_currentUpdates = - // m_submissionContext->rhi()->nextResourceUpdateBatch(); - - // Draw the commands - - // Begin pass - cb->beginPass(rhiRenderTarget, clearColor, clearDepthStencil, - m_submissionContext->m_currentUpdates); - - // Per Pass Global States - for (RenderView *rv : renderViews) { + auto executeDrawRenderView = [&] (RenderView* rv) { // Viewport QRhiViewport vp; QRhiScissor scissor; @@ -2514,13 +2649,64 @@ bool Renderer::executeCommandsSubmission(const RHIPassInfo &passInfo) if (Q_UNLIKELY(!command.isValid())) return; - if (command.m_type == RenderCommand::Draw) { - performDraw(cb, vp, hasScissor ? &scissor : nullptr, command); - } + Q_ASSERT (command.m_type == RenderCommand::Draw); + performDraw(cb, vp, hasScissor ? &scissor : nullptr, command); + }); + }; + + auto executeComputeRenderView = [&] (RenderView* rv) { + rv->forEachCommand([&] (const RenderCommand &command) { + if (Q_UNLIKELY(!command.isValid())) + return; + + Q_ASSERT (command.m_type == RenderCommand::Compute); + performCompute(cb, command); }); + }; + + bool inCompute = false; + bool inDraw = false; + + // Per Pass Global States + for (RenderView *rv : renderViews) { + if (rv->isCompute()) { + // If we were running draw calls we stop the draw pass + if (inDraw) { + cb->endPass(); + m_submissionContext->m_currentUpdates = m_submissionContext->rhi()->nextResourceUpdateBatch(); + inDraw = false; + } + + // There is also the possibility where we weren't either in a compute or draw pass (for the first RV) + // hence why these conditions are like this + if (!inCompute) { + cb->beginComputePass(m_submissionContext->m_currentUpdates); + inCompute = true; + } + + executeComputeRenderView(rv); + } else { + // Same logic than above but reversed + if (inCompute) { + cb->endComputePass(); + m_submissionContext->m_currentUpdates = m_submissionContext->rhi()->nextResourceUpdateBatch(); + inCompute = false; + } + + if (!inDraw) { + cb->beginPass(rhiRenderTarget, clearColor, clearDepthStencil, m_submissionContext->m_currentUpdates); + inDraw = true; + } + + executeDrawRenderView(rv); + } } - cb->endPass(); + if (Q_LIKELY(inDraw)) + cb->endPass(); + else if (inCompute) + cb->endComputePass(); + m_submissionContext->m_currentUpdates = m_submissionContext->rhi()->nextResourceUpdateBatch(); return allCommandsIssued; diff --git a/src/plugins/renderers/rhi/renderer/renderer_p.h b/src/plugins/renderers/rhi/renderer/renderer_p.h index 5d89670b0..122847bc5 100644 --- a/src/plugins/renderers/rhi/renderer/renderer_p.h +++ b/src/plugins/renderers/rhi/renderer/renderer_p.h @@ -158,6 +158,8 @@ struct RHIRenderTarget; class RHIShader; class RHIResourceManagers; class RenderView; +class RHIGraphicsPipeline; +class RHIComputePipeline; class Q_AUTOTEST_EXPORT Renderer : public AbstractRenderer { @@ -368,8 +370,6 @@ private: std::vector<Qt3DCore::QNodeId> m_pendingRenderCaptureSendRequests; void performDraw(RenderCommand *command); - void performCompute(const RenderView *rv, RenderCommand *command); - SynchronizerJobPtr m_bufferGathererJob; SynchronizerJobPtr m_textureGathererJob; SynchronizerPostFramePtr m_introspectShaderJob; @@ -410,7 +410,8 @@ private: std::vector<FrameGraphNode *> m_frameGraphLeaves; QScreen *m_screen = nullptr; QSharedPointer<ResourceAccessor> m_scene2DResourceAccessor; - QHash<RenderView *, std::vector<RHIGraphicsPipeline *>> m_rvToPipelines; + QHash<RenderView *, std::vector<RHIGraphicsPipeline *>> m_rvToGraphicsPipelines; + QHash<RenderView *, std::vector<RHIComputePipeline *>> m_rvToComputePipelines; RenderDriver m_driver = RenderDriver::Qt3D; bool m_hasSwapChain = false; @@ -426,11 +427,17 @@ private: void updateGraphicsPipeline(RenderCommand &command, RenderView *rv, int renderViewIndex); + void updateComputePipeline(RenderCommand &cmd, RenderView *rv, + int renderViewIndex); void buildGraphicsPipelines(RHIGraphicsPipeline *graphicsPipeline, RenderView *rv, const RenderCommand &command); + void buildComputePipelines(RHIComputePipeline *computePipeline, + RenderView *rv, + const RenderCommand &command); + void cleanupRenderTarget(const RenderTarget *renderTarget); void createRenderTarget(RenderView* rv, RHIRenderTarget *); @@ -438,6 +445,12 @@ private: bool uploadBuffersForCommand(QRhiCommandBuffer *cb, const RenderView *rv, RenderCommand &command); + + bool uploadBuffersForCommand(RHIComputePipeline* compute, RenderCommand &command); + bool uploadBuffersForCommand(RHIGraphicsPipeline* graphics, RenderCommand &command); + bool uploadUBOsForCommand(QRhiCommandBuffer *cb, const RenderView *rv, + const RenderCommand &command); + bool performCompute(QRhiCommandBuffer *cb, const RenderCommand &command); bool performDraw(QRhiCommandBuffer *cb, const QRhiViewport &vp, const QRhiScissor *scissor, RenderCommand &command); }; diff --git a/src/plugins/renderers/rhi/renderer/renderview.cpp b/src/plugins/renderers/rhi/renderer/renderview.cpp index c0d6d0214..8c5bbcb9b 100644 --- a/src/plugins/renderers/rhi/renderer/renderview.cpp +++ b/src/plugins/renderers/rhi/renderer/renderview.cpp @@ -922,7 +922,7 @@ EntityRenderCommandData RenderView::buildDrawRenderCommands(const Entity **entit command.m_drawIndirect = (indirectAttribute != nullptr); command.indexAttribute = nullptr; command.indexBuffer = nullptr; - command.pipeline = nullptr; + command.pipeline = std::monostate{}; // Update the draw command with all the information required for the drawing if (command.m_drawIndexed) { @@ -1385,9 +1385,7 @@ void RenderView::setShaderAndUniforms(RenderCommand *command, ParameterInfoList // At this point we know whether the command is a valid draw command or not // We still need to process the uniforms as the command could be a compute command - command->m_isValid = !command->m_activeAttributes.empty(); - - + command->m_isValid = (!command->m_activeAttributes.empty()) || (command->m_type == RenderCommand::CommandType::Compute); } if (shader->hasActiveVariables()) { diff --git a/src/plugins/renderers/rhi/renderer/rhigraphicspipeline.cpp b/src/plugins/renderers/rhi/renderer/rhigraphicspipeline.cpp index cc22c8856..824609b7f 100644 --- a/src/plugins/renderers/rhi/renderer/rhigraphicspipeline.cpp +++ b/src/plugins/renderers/rhi/renderer/rhigraphicspipeline.cpp @@ -47,30 +47,6 @@ namespace Render { namespace Rhi { -RHIGraphicsPipeline::RHIGraphicsPipeline() - : m_pipeline(nullptr) - , m_shaderResourceBindings(nullptr) - , m_score(5) -{ -} - -RHIGraphicsPipeline::~RHIGraphicsPipeline() { } - -void RHIGraphicsPipeline::cleanup() -{ - delete m_shaderResourceBindings; - if (m_pipeline) - m_pipeline->destroy(); - delete m_pipeline; - m_pipeline = nullptr; - m_shaderResourceBindings = nullptr; - m_attributeNameIdToBindingIndex.clear(); - m_uboSet.releaseResources(); - m_uboSet.clear(); - m_key = {}; - m_score = 5; -} - } // Rhi } // Render diff --git a/src/plugins/renderers/rhi/renderer/rhigraphicspipeline_p.h b/src/plugins/renderers/rhi/renderer/rhigraphicspipeline_p.h index 68242b525..574b7d342 100644 --- a/src/plugins/renderers/rhi/renderer/rhigraphicspipeline_p.h +++ b/src/plugins/renderers/rhi/renderer/rhigraphicspipeline_p.h @@ -76,26 +76,59 @@ struct GraphicsPipelineIdentifier int renderViewIndex = 0; }; -class RHIGraphicsPipeline +struct ComputePipelineIdentifier +{ + Qt3DCore::QNodeId shader; + int renderViewIndex; +}; + +template<typename Pipeline, typename Key> +class RHIPipelineBase { public: - RHIGraphicsPipeline(); - ~RHIGraphicsPipeline(); + virtual ~RHIPipelineBase() {} PipelineUBOSet* uboSet() { return &m_uboSet; } - QRhiGraphicsPipeline *pipeline() const { return m_pipeline; } + + Pipeline *pipeline() const { return m_pipeline; } + void setPipeline(Pipeline *pipeline) { m_pipeline = pipeline; } + + void setKey(const Key &key) { m_key = key; } + Key key() const { return m_key; } + + void setShaderResourceBindings(QRhiShaderResourceBindings *shaderResourceBindings) { m_shaderResourceBindings = shaderResourceBindings; } QRhiShaderResourceBindings *shaderResourceBindings() const { return m_shaderResourceBindings; } - int score() const { return m_score; } - void setKey(const GraphicsPipelineIdentifier &key) { m_key = key; } - GraphicsPipelineIdentifier key() const { return m_key; } - void setPipeline(QRhiGraphicsPipeline *pipeline) { m_pipeline = pipeline; } + int score() const { return m_score; } + void increaseScore() { m_score += 2; } + void decreaseScore() { --m_score; } - void setShaderResourceBindings(QRhiShaderResourceBindings *shaderResourceBindings) + virtual void cleanup() { - m_shaderResourceBindings = shaderResourceBindings; + delete m_shaderResourceBindings; + delete m_pipeline; + m_pipeline = nullptr; + m_shaderResourceBindings = nullptr; + m_uboSet.releaseResources(); + m_uboSet.clear(); + m_key = {}; + m_score = 5; } +protected: + RHIPipelineBase() {} + + Pipeline *m_pipeline = nullptr; + QRhiShaderResourceBindings *m_shaderResourceBindings = nullptr; + // For user defined uniforms + PipelineUBOSet m_uboSet; + Key m_key = {}; + int m_score = 5; +}; + +class RHIGraphicsPipeline : public RHIPipelineBase<QRhiGraphicsPipeline, GraphicsPipelineIdentifier> +{ +public: void setAttributesToBindingHash(const QHash<int, int> &attributeNameToBinding) { m_attributeNameIdToBindingIndex = attributeNameToBinding; @@ -106,19 +139,19 @@ public: return m_attributeNameIdToBindingIndex.value(attributeNameId, -1); } - void increaseScore() { m_score += 2; } - void decreaseScore() { --m_score; } - - void cleanup(); + virtual void cleanup() + { + RHIPipelineBase<QRhiGraphicsPipeline, GraphicsPipelineIdentifier>::cleanup(); + m_attributeNameIdToBindingIndex.clear(); + } private: - QRhiGraphicsPipeline *m_pipeline; - QRhiShaderResourceBindings *m_shaderResourceBindings; // For user defined uniforms QHash<int, int> m_attributeNameIdToBindingIndex; - PipelineUBOSet m_uboSet; - GraphicsPipelineIdentifier m_key; - int m_score; +}; + +class RHIComputePipeline : public RHIPipelineBase<QRhiComputePipeline, ComputePipelineIdentifier> +{ }; } // Rhi diff --git a/src/plugins/renderers/rhi/renderer/rhishader.cpp b/src/plugins/renderers/rhi/renderer/rhishader.cpp index 9d957984f..3281cb640 100644 --- a/src/plugins/renderers/rhi/renderer/rhishader.cpp +++ b/src/plugins/renderers/rhi/renderer/rhishader.cpp @@ -408,37 +408,45 @@ void RHIShader::introspect() std::vector<ShaderAttribute> samplers; std::vector<ShaderAttribute> images; - // Introspect shader vertex input - if (m_stages[QShader::VertexStage].isValid()) { - const QShaderDescription &vtx = m_stages[QShader::VertexStage].description(); - - for (const QShaderDescription::InOutVariable &input : vtx.inputVariables()) { - attributes.push_back(ShaderAttribute { input.name, StringToInt::lookupId(input.name), - input.type, rhiTypeSize(input.type), - input.location }); - } + if (m_stages[QShader::ComputeStage].isValid()) { + const QShaderDescription &comp = m_stages[QShader::ComputeStage].description(); - Qt3DCore::append(rhiUBO, vtx.uniformBlocks()); - Qt3DCore::append(rhiSSBO, vtx.storageBlocks()); + Qt3DCore::append(rhiUBO, comp.uniformBlocks()); + Qt3DCore::append(rhiSSBO, comp.storageBlocks()); } + else + { + // Introspect shader vertex input + if (m_stages[QShader::VertexStage].isValid()) { + const QShaderDescription &vtx = m_stages[QShader::VertexStage].description(); + + for (const QShaderDescription::InOutVariable &input : vtx.inputVariables()) { + attributes.push_back(ShaderAttribute { input.name, StringToInt::lookupId(input.name), + input.type, rhiTypeSize(input.type), + input.location }); + } - // Introspect shader uniforms - - if (m_stages[QShader::FragmentStage].isValid()) { - const QShaderDescription &frag = m_stages[QShader::FragmentStage].description(); - for (const QShaderDescription::InOutVariable &sampler : frag.combinedImageSamplers()) { - samplers.push_back(ShaderAttribute { sampler.name, StringToInt::lookupId(sampler.name), - sampler.type, rhiTypeSize(sampler.type), - sampler.binding }); - } - for (const QShaderDescription::InOutVariable &image : frag.storageImages()) { - images.push_back(ShaderAttribute { image.name, StringToInt::lookupId(image.name), - image.type, rhiTypeSize(image.type), - image.binding }); + Qt3DCore::append(rhiUBO, vtx.uniformBlocks()); + Qt3DCore::append(rhiSSBO, vtx.storageBlocks()); } - Qt3DCore::append(rhiUBO, frag.uniformBlocks()); - Qt3DCore::append(rhiSSBO, frag.storageBlocks()); + // Introspect shader uniforms + if (m_stages[QShader::FragmentStage].isValid()) { + const QShaderDescription &frag = m_stages[QShader::FragmentStage].description(); + for (const QShaderDescription::InOutVariable &sampler : frag.combinedImageSamplers()) { + samplers.push_back(ShaderAttribute { sampler.name, StringToInt::lookupId(sampler.name), + sampler.type, rhiTypeSize(sampler.type), + sampler.binding }); + } + for (const QShaderDescription::InOutVariable &image : frag.storageImages()) { + images.push_back(ShaderAttribute { image.name, StringToInt::lookupId(image.name), + image.type, rhiTypeSize(image.type), + image.binding }); + } + + Qt3DCore::append(rhiUBO, frag.uniformBlocks()); + Qt3DCore::append(rhiSSBO, frag.storageBlocks()); + } } rhiUBO = stableRemoveDuplicates(rhiUBO, |