diff options
Diffstat (limited to 'src/render/renderers/opengl/renderer')
9 files changed, 679 insertions, 465 deletions
diff --git a/src/render/renderers/opengl/renderer/rendercommand.cpp b/src/render/renderers/opengl/renderer/rendercommand.cpp index ee0bf0aec..072127391 100644 --- a/src/render/renderers/opengl/renderer/rendercommand.cpp +++ b/src/render/renderers/opengl/renderer/rendercommand.cpp @@ -71,6 +71,20 @@ RenderCommand::RenderCommand() m_workGroups[2] = 0; } +bool operator==(const RenderCommand &a, const RenderCommand &b) noexcept +{ + return (a.m_vao == b.m_vao && a.m_shader == b.m_shader && a.m_material == b.m_material && + a.m_stateSet == b.m_stateSet && a.m_geometry == b.m_geometry && a.m_geometryRenderer == b.m_geometryRenderer && + a.m_indirectDrawBuffer == b.m_indirectDrawBuffer && a.m_activeAttributes == b.m_activeAttributes && + a.m_depth == b.m_depth && a.m_changeCost == b.m_changeCost && a.m_shaderDna == b.m_shaderDna && + a.m_workGroups[0] == b.m_workGroups[0] && a.m_workGroups[1] == b.m_workGroups[1] && a.m_workGroups[2] == b.m_workGroups[2] && + a.m_primitiveCount == b.m_primitiveCount && a.m_primitiveType == b.m_primitiveType && a.m_restartIndexValue == b.m_restartIndexValue && + a.m_firstInstance == b.m_firstInstance && a.m_firstVertex == b.m_firstVertex && a.m_verticesPerPatch == b.m_verticesPerPatch && + a.m_instanceCount == b.m_instanceCount && a.m_indexOffset == b.m_indexOffset && a.m_indexAttributeByteOffset == b.m_indexAttributeByteOffset && + a.m_drawIndexed == b.m_drawIndexed && a.m_drawIndirect == b.m_drawIndirect && a.m_primitiveRestartEnabled == b.m_primitiveRestartEnabled && + a.m_isValid == b.m_isValid && a.m_computeCommand == b.m_computeCommand); +} + } // namespace Render } // namespace Qt3DRender diff --git a/src/render/renderers/opengl/renderer/rendercommand_p.h b/src/render/renderers/opengl/renderer/rendercommand_p.h index 61cc6d17d..58ab0cadd 100644 --- a/src/render/renderers/opengl/renderer/rendercommand_p.h +++ b/src/render/renderers/opengl/renderer/rendercommand_p.h @@ -55,6 +55,7 @@ #include <qglobal.h> #include <Qt3DRender/private/shaderparameterpack_p.h> #include <Qt3DRender/private/handle_types_p.h> +#include <Qt3DRender/private/renderviewjobutils_p.h> #include <Qt3DRender/qgeometryrenderer.h> #include <QOpenGLShaderProgram> #include <QOpenGLTexture> @@ -69,6 +70,7 @@ namespace Qt3DRender { namespace Render { class RenderStateSet; +using RenderStateSetPtr = QSharedPointer<RenderStateSet>; class Q_AUTOTEST_EXPORT RenderCommand { @@ -80,12 +82,13 @@ public: HMaterial m_material; // Purely used to ease sorting (minimize stage changes, binding changes ....) ShaderParameterPack m_parameterPack; // Might need to be reworked so as to be able to destroy the // Texture while submission is happening. - RenderStateSet *m_stateSet; + RenderStateSetPtr m_stateSet; HGeometry m_geometry; HGeometryRenderer m_geometryRenderer; HBuffer m_indirectDrawBuffer; // Reference to indirect draw buffer (valid only m_drawIndirect == true) + HComputeCommand m_computeCommand; // A QAttribute pack might be interesting // This is a temporary fix in the meantime, to remove the hacked methods in Technique @@ -121,6 +124,18 @@ public: bool m_isValid; }; +Q_AUTOTEST_EXPORT bool operator==(const RenderCommand &a, const RenderCommand &b) noexcept; + +inline bool operator!=(const RenderCommand &lhs, const RenderCommand &rhs) noexcept +{ return !operator==(lhs, rhs); } + +struct EntityRenderCommandData +{ + Entity *entity; + RenderCommand command; + RenderPassParameterData passData; +}; + } // namespace Render } // namespace Qt3DRender diff --git a/src/render/renderers/opengl/renderer/renderer.cpp b/src/render/renderers/opengl/renderer/renderer.cpp index 121a6aa8f..f17e66108 100644 --- a/src/render/renderers/opengl/renderer/renderer.cpp +++ b/src/render/renderers/opengl/renderer/renderer.cpp @@ -432,6 +432,9 @@ void Renderer::initialize() m_waitForInitializationToBeCompleted.release(1); // Allow the aspect manager to proceed m_vsyncFrameAdvanceService->proceedToNextFrame(); + + // Force initial refresh + markDirty(AllDirty, nullptr); } /*! @@ -860,13 +863,13 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView QHash<HVao, bool> updatedTable; for (RenderView *rv: renderViews) { - const QVector<RenderCommand *> commands = rv->commands(); - for (RenderCommand *command : commands) { + QVector<RenderCommand> &commands = rv->commands(); + for (RenderCommand &command : commands) { // Update/Create VAO - if (command->m_type == RenderCommand::Draw) { - Geometry *rGeometry = m_nodesManager->data<Geometry, GeometryManager>(command->m_geometry); - GeometryRenderer *rGeometryRenderer = m_nodesManager->data<GeometryRenderer, GeometryRendererManager>(command->m_geometryRenderer); - Shader *shader = m_nodesManager->data<Shader, ShaderManager>(command->m_shader); + if (command.m_type == RenderCommand::Draw) { + Geometry *rGeometry = m_nodesManager->data<Geometry, GeometryManager>(command.m_geometry); + GeometryRenderer *rGeometryRenderer = m_nodesManager->data<GeometryRenderer, GeometryRendererManager>(command.m_geometryRenderer); + Shader *shader = m_nodesManager->data<Shader, ShaderManager>(command.m_shader); // We should never have inserted a command for which these are null // in the first place @@ -879,15 +882,15 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView // Create VAO or return already created instance associated with command shader/geometry // (VAO is emulated if not supported) - createOrUpdateVAO(command, &vaoHandle, &vao); - command->m_vao = vaoHandle; + createOrUpdateVAO(&command, &vaoHandle, &vao); + command.m_vao = vaoHandle; // Avoids redoing the same thing for the same VAO if (!updatedTable.contains(vaoHandle)) { updatedTable.insert(vaoHandle, true); // Do we have any attributes that are dirty ? - const bool requiresPartialVAOUpdate = requiresVAOAttributeUpdate(rGeometry, command); + const bool requiresPartialVAOUpdate = requiresVAOAttributeUpdate(rGeometry, &command); // If true, we need to reupload all attributes to set the VAO // Otherwise only dirty attributes will be updates @@ -898,7 +901,7 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView if (rGeometry->isDirty()) m_dirtyGeometry.push_back(rGeometry); - if (!command->m_activeAttributes.isEmpty() && (requiresFullVAOUpdate || requiresPartialVAOUpdate)) { + if (!command.m_activeAttributes.isEmpty() && (requiresFullVAOUpdate || requiresPartialVAOUpdate)) { Profiling::GLTimeRecorder recorder(Profiling::VAOUpload); // Activate shader m_submissionContext->activateShader(shader->dna()); @@ -906,7 +909,7 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView vao->bind(); // Update or set Attributes and Buffers for the given rGeometry and Command // Note: this fills m_dirtyAttributes as well - if (updateVAOWithAttributes(rGeometry, command, shader, requiresFullVAOUpdate)) + if (updateVAOWithAttributes(rGeometry, &command, shader, requiresFullVAOUpdate)) vao->setSpecified(true); } } @@ -918,11 +921,11 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView rGeometryRenderer->unsetDirty(); // Prepare the ShaderParameterPack based on the active uniforms of the shader - shader->prepareUniforms(command->m_parameterPack); + shader->prepareUniforms(command.m_parameterPack); { // Scoped to show extent - command->m_isValid = !command->m_activeAttributes.empty(); - if (!command->m_isValid) + command.m_isValid = !command.m_activeAttributes.empty(); + if (!command.m_isValid) continue; // Update the draw command with what's going to be needed for the drawing @@ -942,7 +945,7 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView indirectAttribute = attribute; break; case QAttribute::VertexAttribute: { - if (command->m_activeAttributes.contains(attribute->nameId())) + if (command.m_activeAttributes.contains(attribute->nameId())) estimatedCount = qMax(attribute->count(), estimatedCount); break; } @@ -952,20 +955,20 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView } } - command->m_drawIndexed = (indexAttribute != nullptr); - command->m_drawIndirect = (indirectAttribute != nullptr); + command.m_drawIndexed = (indexAttribute != nullptr); + command.m_drawIndirect = (indirectAttribute != nullptr); // Update the draw command with all the information required for the drawing - if (command->m_drawIndexed) { - command->m_indexAttributeDataType = GraphicsContext::glDataTypeFromAttributeDataType(indexAttribute->vertexBaseType()); - command->m_indexAttributeByteOffset = indexAttribute->byteOffset() + rGeometryRenderer->indexBufferByteOffset(); + if (command.m_drawIndexed) { + command.m_indexAttributeDataType = GraphicsContext::glDataTypeFromAttributeDataType(indexAttribute->vertexBaseType()); + command.m_indexAttributeByteOffset = indexAttribute->byteOffset() + rGeometryRenderer->indexBufferByteOffset(); } // Note: we only care about the primitiveCount when using direct draw calls // For indirect draw calls it is assumed the buffer was properly set already - if (command->m_drawIndirect) { - command->m_indirectAttributeByteOffset = indirectAttribute->byteOffset(); - command->m_indirectDrawBuffer = m_nodesManager->bufferManager()->lookupHandle(indirectAttribute->bufferId()); + if (command.m_drawIndirect) { + command.m_indirectAttributeByteOffset = indirectAttribute->byteOffset(); + command.m_indirectDrawBuffer = m_nodesManager->bufferManager()->lookupHandle(indirectAttribute->bufferId()); } else { // Use the count specified by the GeometryRender // If not specify use the indexAttribute count if present @@ -978,22 +981,22 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView } } - command->m_primitiveCount = primitiveCount; - command->m_primitiveType = rGeometryRenderer->primitiveType(); - command->m_primitiveRestartEnabled = rGeometryRenderer->primitiveRestartEnabled(); - command->m_restartIndexValue = rGeometryRenderer->restartIndexValue(); - command->m_firstInstance = rGeometryRenderer->firstInstance(); - command->m_instanceCount = rGeometryRenderer->instanceCount(); - command->m_firstVertex = rGeometryRenderer->firstVertex(); - command->m_indexOffset = rGeometryRenderer->indexOffset(); - command->m_verticesPerPatch = rGeometryRenderer->verticesPerPatch(); + command.m_primitiveCount = primitiveCount; + command.m_primitiveType = rGeometryRenderer->primitiveType(); + command.m_primitiveRestartEnabled = rGeometryRenderer->primitiveRestartEnabled(); + command.m_restartIndexValue = rGeometryRenderer->restartIndexValue(); + command.m_firstInstance = rGeometryRenderer->firstInstance(); + command.m_instanceCount = rGeometryRenderer->instanceCount(); + command.m_firstVertex = rGeometryRenderer->firstVertex(); + command.m_indexOffset = rGeometryRenderer->indexOffset(); + command.m_verticesPerPatch = rGeometryRenderer->verticesPerPatch(); } // scope - } else if (command->m_type == RenderCommand::Compute) { - Shader *shader = m_nodesManager->data<Shader, ShaderManager>(command->m_shader); + } else if (command.m_type == RenderCommand::Compute) { + Shader *shader = m_nodesManager->data<Shader, ShaderManager>(command.m_shader); Q_ASSERT(shader); // Prepare the ShaderParameterPack based on the active uniforms of the shader - shader->prepareUniforms(command->m_parameterPack); + shader->prepareUniforms(command.m_parameterPack); } } } @@ -1834,6 +1837,7 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() const bool computeableDirty = dirtyBitsForFrame & AbstractRenderer::ComputeDirty; const bool renderableDirty = dirtyBitsForFrame & AbstractRenderer::GeometryDirty; const bool materialCacheNeedsToBeRebuilt = shadersDirty || materialDirty || frameGraphDirty; + const bool renderCommandsDirty = materialCacheNeedsToBeRebuilt || renderableDirty || computeableDirty; // Rebuild Entity Layers list if layers are dirty if (layersDirty) @@ -1873,6 +1877,7 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() builder.setLightGathererCacheNeedsToBeRebuilt(lightsDirty); builder.setMaterialGathererCacheNeedsToBeRebuilt(materialCacheNeedsToBeRebuilt); builder.setLightGathererCacheNeedsToBeRebuilt(lightsDirty); + builder.setRenderCommandCacheNeedsToBeRebuilt(renderCommandsDirty); builder.prepareJobs(); renderBinJobs.append(builder.buildJobHierachy()); @@ -2075,7 +2080,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) bool allCommandsIssued = true; // Render drawing commands - const QVector<RenderCommand *> commands = rv->commands(); + QVector<RenderCommand> commands = rv->commands(); // Use the graphicscontext to submit the commands to the underlying // graphics API (OpenGL) @@ -2084,18 +2089,18 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) RenderStateSet *globalState = m_submissionContext->currentStateSet(); OpenGLVertexArrayObject *vao = nullptr; - for (RenderCommand *command : qAsConst(commands)) { + for (RenderCommand &command : commands) { - if (command->m_type == RenderCommand::Compute) { // Compute Call - performCompute(rv, command); + if (command.m_type == RenderCommand::Compute) { // Compute Call + performCompute(rv, &command); } else { // Draw Command // Check if we have a valid command that can be drawn - if (!command->m_isValid) { + if (!command.m_isValid) { allCommandsIssued = false; continue; } - vao = m_nodesManager->vaoManager()->data(command->m_vao); + vao = m_nodesManager->vaoManager()->data(command.m_vao); // something may have went wrong when initializing the VAO if (!vao->isSpecified()) { @@ -2106,7 +2111,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) { Profiling::GLTimeRecorder recorder(Profiling::ShaderUpdate); //// We activate the shader here - if (!m_submissionContext->activateShader(command->m_shaderDna)) { + if (!m_submissionContext->activateShader(command.m_shaderDna)) { allCommandsIssued = false; continue; } @@ -2121,7 +2126,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) { Profiling::GLTimeRecorder recorder(Profiling::UniformUpdate); //// Update program uniforms - if (!m_submissionContext->setParameters(command->m_parameterPack)) { + if (!m_submissionContext->setParameters(command.m_parameterPack)) { allCommandsIssued = false; // If we have failed to set uniform (e.g unable to bind a texture) // we won't perform the draw call which could show invalid content @@ -2132,7 +2137,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) //// OpenGL State // TO DO: Make states not dependendent on their backend node for this step // Set state - RenderStateSet *localState = command->m_stateSet; + RenderStateSet *localState = command.m_stateSet.data(); { @@ -2140,8 +2145,8 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) // Merge the RenderCommand state with the globalState of the RenderView // Or restore the globalState if no stateSet for the RenderCommand if (localState != nullptr) { - command->m_stateSet->merge(globalState); - m_submissionContext->setCurrentStateSet(command->m_stateSet); + command.m_stateSet->merge(globalState); + m_submissionContext->setCurrentStateSet(localState); } else { m_submissionContext->setCurrentStateSet(globalState); } @@ -2151,7 +2156,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) // at that point //// Draw Calls - performDraw(command); + performDraw(&command); } } // end of RenderCommands loop @@ -2167,7 +2172,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) } bool Renderer::updateVAOWithAttributes(Geometry *geometry, - RenderCommand *command, + const RenderCommand *command, Shader *shader, bool forceUpdate) { @@ -2227,7 +2232,7 @@ bool Renderer::updateVAOWithAttributes(Geometry *geometry, } bool Renderer::requiresVAOAttributeUpdate(Geometry *geometry, - RenderCommand *command) const + const RenderCommand *command) const { const auto attributeIds = geometry->attributes(); diff --git a/src/render/renderers/opengl/renderer/renderer_p.h b/src/render/renderers/opengl/renderer/renderer_p.h index b1cd4aac5..db63f31d0 100644 --- a/src/render/renderers/opengl/renderer/renderer_p.h +++ b/src/render/renderers/opengl/renderer/renderer_p.h @@ -254,12 +254,12 @@ public: void prepareCommandsSubmission(const QVector<RenderView *> &renderViews); bool executeCommandsSubmission(const RenderView *rv); bool updateVAOWithAttributes(Geometry *geometry, - RenderCommand *command, + const RenderCommand *command, Shader *shader, bool forceUpdate); bool requiresVAOAttributeUpdate(Geometry *geometry, - RenderCommand *command) const; + const RenderCommand *command) const; void setOpenGLContext(QOpenGLContext *context) override; const GraphicsApiFilterData *contextInfo() const; diff --git a/src/render/renderers/opengl/renderer/renderercache_p.h b/src/render/renderers/opengl/renderer/renderercache_p.h index 0e9c5d3cd..a1298f491 100644 --- a/src/render/renderers/opengl/renderer/renderercache_p.h +++ b/src/render/renderers/opengl/renderer/renderercache_p.h @@ -56,6 +56,7 @@ #include <Qt3DRender/private/entity_p.h> #include <Qt3DRender/private/renderviewjobutils_p.h> #include <Qt3DRender/private/lightsource_p.h> +#include <Qt3DRender/private/rendercommand_p.h> QT_BEGIN_NAMESPACE @@ -73,6 +74,7 @@ struct RendererCache QVector<Entity *> renderableEntities; QVector<Entity *> computeEntities; EnvironmentLight* environmentLight; + QVector<EntityRenderCommandData> renderCommandData; }; QHash<FrameGraphNode *, LeafNodeData> leafNodeCache; diff --git a/src/render/renderers/opengl/renderer/renderview.cpp b/src/render/renderers/opengl/renderer/renderview.cpp index 48f622c07..c38ffae80 100644 --- a/src/render/renderers/opengl/renderer/renderview.cpp +++ b/src/render/renderers/opengl/renderer/renderview.cpp @@ -282,11 +282,6 @@ RenderView::RenderView() RenderView::~RenderView() { - delete m_stateSet; - for (RenderCommand *command : qAsConst(m_commands)) { - delete command->m_stateSet; - delete command; - } } namespace { @@ -294,7 +289,7 @@ namespace { template<int SortType> struct AdjacentSubRangeFinder { - static bool adjacentSubRange(RenderCommand *, RenderCommand *) + static bool adjacentSubRange(const RenderCommand &, const RenderCommand &) { Q_UNREACHABLE(); return false; @@ -304,47 +299,47 @@ struct AdjacentSubRangeFinder template<> struct AdjacentSubRangeFinder<QSortPolicy::StateChangeCost> { - static bool adjacentSubRange(RenderCommand *a, RenderCommand *b) + static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) { - return a->m_changeCost == b->m_changeCost; + return a.m_changeCost == b.m_changeCost; } }; template<> struct AdjacentSubRangeFinder<QSortPolicy::BackToFront> { - static bool adjacentSubRange(RenderCommand *a, RenderCommand *b) + static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) { - return a->m_depth == b->m_depth; + return a.m_depth == b.m_depth; } }; template<> struct AdjacentSubRangeFinder<QSortPolicy::Material> { - static bool adjacentSubRange(RenderCommand *a, RenderCommand *b) + static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) { - return a->m_shaderDna == b->m_shaderDna; + return a.m_shaderDna == b.m_shaderDna; } }; template<> struct AdjacentSubRangeFinder<QSortPolicy::FrontToBack> { - static bool adjacentSubRange(RenderCommand *a, RenderCommand *b) + static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) { - return a->m_depth == b->m_depth; + return a.m_depth == b.m_depth; } }; template<> struct AdjacentSubRangeFinder<QSortPolicy::Texture> { - static bool adjacentSubRange(RenderCommand *a, RenderCommand *b) + static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) { // Two renderCommands are adjacent if one contains all the other command's textures - QVector<ShaderParameterPack::NamedResource> texturesA = a->m_parameterPack.textures(); - QVector<ShaderParameterPack::NamedResource> texturesB = b->m_parameterPack.textures(); + QVector<ShaderParameterPack::NamedResource> texturesA = a.m_parameterPack.textures(); + QVector<ShaderParameterPack::NamedResource> texturesB = b.m_parameterPack.textures(); if (texturesB.size() > texturesA.size()) qSwap(texturesA, texturesB); @@ -359,7 +354,7 @@ struct AdjacentSubRangeFinder<QSortPolicy::Texture> }; template<typename Predicate> -int advanceUntilNonAdjacent(const QVector<RenderCommand *> &commands, +int advanceUntilNonAdjacent(const QVector<RenderCommand> &commands, const int beg, const int end, Predicate pred) { int i = beg + 1; @@ -372,7 +367,7 @@ int advanceUntilNonAdjacent(const QVector<RenderCommand *> &commands, } -using CommandIt = QVector<RenderCommand *>::iterator; +using CommandIt = QVector<RenderCommand>::iterator; template<int SortType> struct SubRangeSorter @@ -390,8 +385,8 @@ struct SubRangeSorter<QSortPolicy::StateChangeCost> { static void sortSubRange(CommandIt begin, const CommandIt end) { - std::stable_sort(begin, end, [] (RenderCommand *a, RenderCommand *b) { - return a->m_changeCost > b->m_changeCost; + std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) { + return a.m_changeCost > b.m_changeCost; }); } }; @@ -401,8 +396,8 @@ struct SubRangeSorter<QSortPolicy::BackToFront> { static void sortSubRange(CommandIt begin, const CommandIt end) { - std::stable_sort(begin, end, [] (RenderCommand *a, RenderCommand *b) { - return a->m_depth > b->m_depth; + std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) { + return a.m_depth > b.m_depth; }); } }; @@ -413,8 +408,8 @@ struct SubRangeSorter<QSortPolicy::Material> static void sortSubRange(CommandIt begin, const CommandIt end) { // First we sort by shaderDNA - std::stable_sort(begin, end, [] (RenderCommand *a, RenderCommand *b) { - return a->m_shaderDna > b->m_shaderDna; + std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) { + return a.m_shaderDna > b.m_shaderDna; }); } }; @@ -424,8 +419,8 @@ struct SubRangeSorter<QSortPolicy::FrontToBack> { static void sortSubRange(CommandIt begin, const CommandIt end) { - std::stable_sort(begin, end, [] (RenderCommand *a, RenderCommand *b) { - return a->m_depth < b->m_depth; + std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) { + return a.m_depth < b.m_depth; }); } }; @@ -435,9 +430,9 @@ struct SubRangeSorter<QSortPolicy::Texture> { static void sortSubRange(CommandIt begin, const CommandIt end) { - std::stable_sort(begin, end, [] (RenderCommand *a, RenderCommand *b) { - QVector<ShaderParameterPack::NamedResource> texturesA = a->m_parameterPack.textures(); - QVector<ShaderParameterPack::NamedResource> texturesB = b->m_parameterPack.textures(); + std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) { + QVector<ShaderParameterPack::NamedResource> texturesA = a.m_parameterPack.textures(); + QVector<ShaderParameterPack::NamedResource> texturesB = b.m_parameterPack.textures(); const int originalTextureASize = texturesA.size(); @@ -456,7 +451,7 @@ struct SubRangeSorter<QSortPolicy::Texture> } }; -int findSubRange(const QVector<RenderCommand *> &commands, +int findSubRange(const QVector<RenderCommand> &commands, const int begin, const int end, const QSortPolicy::SortType sortType) { @@ -477,14 +472,14 @@ int findSubRange(const QVector<RenderCommand *> &commands, } } -void sortByMaterial(QVector<RenderCommand *> &commands, int begin, const int end) +void sortByMaterial(QVector<RenderCommand> &commands, int begin, const int end) { // We try to arrange elements so that their rendering cost is minimized for a given shader int rangeEnd = advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange); while (begin != end) { if (begin + 1 < rangeEnd) { - std::stable_sort(commands.begin() + begin + 1, commands.begin() + rangeEnd, [] (RenderCommand *a, RenderCommand *b){ - return a->m_material.handle() < b->m_material.handle(); + std::stable_sort(commands.begin() + begin + 1, commands.begin() + rangeEnd, [] (const RenderCommand &a, const RenderCommand &b){ + return a.m_material.handle() < b.m_material.handle(); }); } begin = rangeEnd; @@ -492,7 +487,7 @@ void sortByMaterial(QVector<RenderCommand *> &commands, int begin, const int end } } -void sortCommandRange(QVector<RenderCommand *> &commands, int begin, const int end, const int level, +void sortCommandRange(QVector<RenderCommand> &commands, int begin, const int end, const int level, const QVector<Qt3DRender::QSortPolicy::SortType> &sortingTypes) { if (level >= sortingTypes.size()) @@ -542,20 +537,21 @@ void RenderView::sort() // Minimize uniform changes int i = 0; - while (i < m_commands.size()) { + const int commandSize = m_commands.size(); + while (i < commandSize) { int j = i; // Advance while commands share the same shader - while (i < m_commands.size() && m_commands[j]->m_shaderDna == m_commands[i]->m_shaderDna) + while (i < commandSize && m_commands[j].m_shaderDna == m_commands[i].m_shaderDna) ++i; if (i - j > 0) { // Several commands have the same shader, so we minimize uniform changes - PackUniformHash cachedUniforms = m_commands[j++]->m_parameterPack.uniforms(); + PackUniformHash cachedUniforms = m_commands[j++].m_parameterPack.uniforms(); while (j < i) { // We need the reference here as we are modifying the original container // not the copy - PackUniformHash &uniforms = m_commands.at(j)->m_parameterPack.m_uniforms; + PackUniformHash &uniforms = m_commands[j].m_parameterPack.m_uniforms; PackUniformHash::iterator it = uniforms.begin(); const PackUniformHash::iterator end = uniforms.end(); @@ -571,6 +567,9 @@ void RenderView::sort() if (it.value() == refValue) { it = uniforms.erase(it); } else { + // Record updated value so that subsequent comparison + // for the next command will be made againts latest + // uniform value cachedUniforms.insert(it.key(), it.value()); ++it; } @@ -622,19 +621,12 @@ void RenderView::addClearBuffers(const ClearBuffers *cb) { } // If we are there, we know that entity had a GeometryRenderer + Material -QVector<RenderCommand *> RenderView::buildDrawRenderCommands(const QVector<Entity *> &entities) const +QVector<EntityRenderCommandData> RenderView::buildDrawRenderCommands(const QVector<Entity *> &entities) const { - // Note: since many threads can be building render commands - // we need to ensure that the UniformBlockValueBuilder they are using - // is only accessed from the same thread - UniformBlockValueBuilder *builder = new UniformBlockValueBuilder(); - builder->shaderDataManager = m_manager->shaderDataManager(); - builder->textureManager = m_manager->textureManager(); - m_localData.setLocalData(builder); - - QVector<RenderCommand *> commands; + QVector<EntityRenderCommandData> commands; commands.reserve(entities.size()); + for (Entity *entity : entities) { GeometryRenderer *geometryRenderer = nullptr; HGeometryRenderer geometryRendererHandle = entity->componentHandle<GeometryRenderer>(); @@ -651,84 +643,47 @@ QVector<RenderCommand *> RenderView::buildDrawRenderCommands(const QVector<Entit // 1 RenderCommand per RenderPass pass on an Entity with a Mesh for (const RenderPassParameterData &passData : renderPassData) { // Add the RenderPass Parameters - RenderCommand *command = new RenderCommand(); - command->m_geometryRenderer = geometryRendererHandle; - command->m_geometry = m_manager->geometryManager()->lookupHandle(geometryRenderer->geometryId()); - - // Project the camera-to-object-center vector onto the camera - // view vector. This gives a depth value suitable as the key - // for BackToFront sorting. - command->m_depth = Vector3D::dotProduct(entity->worldBoundingVolume()->center() - m_data.m_eyePos, m_data.m_eyeViewDir); - command->m_material = materialHandle; + RenderCommand command = {}; + command.m_geometryRenderer = geometryRendererHandle; + command.m_geometry = m_manager->geometryManager()->lookupHandle(geometryRenderer->geometryId()); + + command.m_material = materialHandle; // For RenderPass based states we use the globally set RenderState // if no renderstates are defined as part of the pass. That means: // RenderPass { renderStates: [] } will use the states defined by // StateSet in the FrameGraph RenderPass *pass = passData.pass; if (pass->hasRenderStates()) { - command->m_stateSet = new RenderStateSet(); - addStatesToRenderStateSet(command->m_stateSet, pass->renderStates(), m_manager->renderStateManager()); + command.m_stateSet = RenderStateSetPtr::create(); + addStatesToRenderStateSet(command.m_stateSet.data(), pass->renderStates(), m_manager->renderStateManager()); if (m_stateSet != nullptr) - command->m_stateSet->merge(m_stateSet); - command->m_changeCost = m_renderer->defaultRenderState()->changeCost(command->m_stateSet); + command.m_stateSet->merge(m_stateSet); + command.m_changeCost = m_renderer->defaultRenderState()->changeCost(command.m_stateSet.data()); } + command.m_shader = m_manager->lookupHandle<Shader, ShaderManager, HShader>(pass->shaderProgram()); - // Pick which lights to take in to account. - // For now decide based on the distance by taking the MAX_LIGHTS closest lights. - // Replace with more sophisticated mechanisms later. - // Copy vector so that we can sort it concurrently and we only want to sort the one for the current command - QVector<LightSource> lightSources = m_lightSources; - if (lightSources.size() > 1) { - const Vector3D entityCenter = entity->worldBoundingVolume()->center(); - std::sort(lightSources.begin(), lightSources.end(), - [&] (const LightSource &a, const LightSource &b) { - const float distA = entityCenter.distanceToPoint(a.entity->worldBoundingVolume()->center()); - const float distB = entityCenter.distanceToPoint(b.entity->worldBoundingVolume()->center()); - return distA < distB; - }); - } - - ParameterInfoList globalParameters = passData.parameterInfo; - // setShaderAndUniforms can initialize a localData - // make sure this is cleared before we leave this function - setShaderAndUniforms(command, - pass, - globalParameters, - entity, - lightSources.mid(0, std::max(lightSources.size(), MAX_LIGHTS)), - m_environmentLight); - - commands.append(command); + const EntityRenderCommandData commandData = { entity, command, passData }; + commands.append(commandData); } } } - // We reset the local data once we are done with it - m_localData.setLocalData(nullptr); - return commands; } -QVector<RenderCommand *> RenderView::buildComputeRenderCommands(const QVector<Entity *> &entities) const +QVector<EntityRenderCommandData> RenderView::buildComputeRenderCommands(const QVector<Entity *> &entities) const { - // Note: since many threads can be building render commands - // we need to ensure that the UniformBlockValueBuilder they are using - // is only accessed from the same thread - UniformBlockValueBuilder *builder = new UniformBlockValueBuilder(); - builder->shaderDataManager = m_manager->shaderDataManager(); - builder->textureManager = m_manager->textureManager(); - m_localData.setLocalData(builder); - // If the RenderView contains only a ComputeDispatch then it cares about // A ComputeDispatch is also implicitely a NoDraw operation // enabled flag // layer component // material/effect/technique/parameters/filters/ - QVector<RenderCommand *> commands; + QVector<EntityRenderCommandData> commands; commands.reserve(entities.size()); for (Entity *entity : entities) { ComputeCommand *computeJob = nullptr; - if ((computeJob = entity->renderComponent<ComputeCommand>()) != nullptr + HComputeCommand computeCommandHandle = entity->componentHandle<ComputeCommand>(); + if ((computeJob = nodeManagers()->computeJobManager()->data(computeCommandHandle)) != nullptr && computeJob->isEnabled()) { // Note: if frameCount has reached 0 in the previous frame, isEnabled @@ -742,41 +697,96 @@ QVector<RenderCommand *> RenderView::buildComputeRenderCommands(const QVector<En // 1 RenderCommand per RenderPass pass on an Entity with a Mesh for (const RenderPassParameterData &passData : renderPassData) { // Add the RenderPass Parameters - RenderCommand *command = new RenderCommand(); + RenderCommand command = {}; RenderPass *pass = passData.pass; if (pass->hasRenderStates()) { - command->m_stateSet = new RenderStateSet(); - addStatesToRenderStateSet(command->m_stateSet, pass->renderStates(), m_manager->renderStateManager()); + command.m_stateSet = RenderStateSetPtr::create(); + addStatesToRenderStateSet(command.m_stateSet.data(), pass->renderStates(), m_manager->renderStateManager()); // Merge per pass stateset with global stateset // so that the local stateset only overrides if (m_stateSet != nullptr) - command->m_stateSet->merge(m_stateSet); - command->m_changeCost = m_renderer->defaultRenderState()->changeCost(command->m_stateSet); + command.m_stateSet->merge(m_stateSet); + command.m_changeCost = m_renderer->defaultRenderState()->changeCost(command.m_stateSet.data()); } + command.m_shader = m_manager->lookupHandle<Shader, ShaderManager, HShader>(pass->shaderProgram()); + command.m_computeCommand = computeCommandHandle; + command.m_type = RenderCommand::Compute; + command.m_workGroups[0] = std::max(m_workGroups[0], computeJob->x()); + command.m_workGroups[1] = std::max(m_workGroups[1], computeJob->y()); + command.m_workGroups[2] = std::max(m_workGroups[2], computeJob->z()); + + const EntityRenderCommandData commandData = { entity, command, passData }; + commands.append(commandData); + } + } + } - command->m_type = RenderCommand::Compute; - command->m_workGroups[0] = std::max(m_workGroups[0], computeJob->x()); - command->m_workGroups[1] = std::max(m_workGroups[1], computeJob->y()); - command->m_workGroups[2] = std::max(m_workGroups[2], computeJob->z()); - - ParameterInfoList globalParameters = passData.parameterInfo; - setShaderAndUniforms(command, - pass, - globalParameters, - entity, - QVector<LightSource>(), - nullptr); - commands.append(command); + return commands; +} + +void RenderView::updateRenderCommand(QVector<EntityRenderCommandData *> &renderCommandData) +{ + // Note: since many threads can be building render commands + // we need to ensure that the UniformBlockValueBuilder they are using + // is only accessed from the same thread + UniformBlockValueBuilder *builder = new UniformBlockValueBuilder(); + builder->shaderDataManager = m_manager->shaderDataManager(); + builder->textureManager = m_manager->textureManager(); + m_localData.setLocalData(builder); + + for (EntityRenderCommandData *commandData : renderCommandData) { + Entity *entity = commandData->entity; + RenderPassParameterData passData = commandData->passData; + RenderCommand &command = commandData->command; + + // Pick which lights to take in to account. + // For now decide based on the distance by taking the MAX_LIGHTS closest lights. + // Replace with more sophisticated mechanisms later. + // Copy vector so that we can sort it concurrently and we only want to sort the one for the current command + QVector<LightSource> lightSources; + EnvironmentLight *environmentLight = nullptr; + + if (command.m_type == RenderCommand::Draw) { + // Project the camera-to-object-center vector onto the camera + // view vector. This gives a depth value suitable as the key + // for BackToFront sorting. + command.m_depth = Vector3D::dotProduct(entity->worldBoundingVolume()->center() - m_data.m_eyePos, m_data.m_eyeViewDir); + + environmentLight = m_environmentLight; + lightSources = m_lightSources; + + if (lightSources.size() > 1) { + const Vector3D entityCenter = entity->worldBoundingVolume()->center(); + std::sort(lightSources.begin(), lightSources.end(), + [&] (const LightSource &a, const LightSource &b) { + const float distA = entityCenter.distanceToPoint(a.entity->worldBoundingVolume()->center()); + const float distB = entityCenter.distanceToPoint(b.entity->worldBoundingVolume()->center()); + return distA < distB; + }); } + lightSources = lightSources.mid(0, std::max(lightSources.size(), MAX_LIGHTS)); + } else { // Compute + // Note: if frameCount has reached 0 in the previous frame, isEnabled + // would be false + ComputeCommand *computeJob = m_manager->computeJobManager()->data(command.m_computeCommand); + if (computeJob->runType() == QComputeCommand::Manual) + computeJob->updateFrameCount(); } + + ParameterInfoList globalParameters = passData.parameterInfo; + // setShaderAndUniforms can initialize a localData + // make sure this is cleared before we leave this function + setShaderAndUniforms(&command, + globalParameters, + entity, + lightSources, + environmentLight); } // We reset the local data once we are done with it m_localData.setLocalData(nullptr); - - return commands; } void RenderView::updateMatrices() @@ -911,7 +921,6 @@ void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &unif } void RenderView::setShaderAndUniforms(RenderCommand *command, - RenderPass *rPass, ParameterInfoList ¶meters, Entity *entity, const QVector<LightSource> &activeLightSources, @@ -927,163 +936,158 @@ void RenderView::setShaderAndUniforms(RenderCommand *command, // For each ParameterBinder in the RenderPass -> create a QUniformPack // Once that works, improve that to try and minimize QUniformPack updates - if (rPass != nullptr) { - // Index Shader by Shader UUID - command->m_shader = m_manager->lookupHandle<Shader, ShaderManager, HShader>(rPass->shaderProgram()); - Shader *shader = m_manager->data<Shader, ShaderManager>(command->m_shader); - if (shader != nullptr && shader->isLoaded()) { - command->m_shaderDna = shader->dna(); - - // Builds the QUniformPack, sets shader standard uniforms and store attributes name / glname bindings - // If a parameter is defined and not found in the bindings it is assumed to be a binding of Uniform type with the glsl name - // equals to the parameter name - const QVector<int> uniformNamesIds = shader->uniformsNamesIds(); - const QVector<int> uniformBlockNamesIds = shader->uniformBlockNamesIds(); - const QVector<int> shaderStorageBlockNamesIds = shader->storageBlockNamesIds(); - const QVector<int> attributeNamesIds = shader->attributeNamesIds(); - - // Set fragData Name and index - // Later on we might want to relink the shader if attachments have changed - // But for now we set them once and for all + // Index Shader by Shader UUID + Shader *shader = m_manager->data<Shader, ShaderManager>(command->m_shader); + if (shader != nullptr && shader->isLoaded()) { + command->m_shaderDna = shader->dna(); + + // Builds the QUniformPack, sets shader standard uniforms and store attributes name / glname bindings + // If a parameter is defined and not found in the bindings it is assumed to be a binding of Uniform type with the glsl name + // equals to the parameter name + const QVector<int> uniformNamesIds = shader->uniformsNamesIds(); + const QVector<int> uniformBlockNamesIds = shader->uniformBlockNamesIds(); + const QVector<int> shaderStorageBlockNamesIds = shader->storageBlockNamesIds(); + const QVector<int> attributeNamesIds = shader->attributeNamesIds(); + + // Set fragData Name and index + // Later on we might want to relink the shader if attachments have changed + // But for now we set them once and for all + if (!m_renderTarget.isNull() && !shader->isLoaded()) { QHash<QString, int> fragOutputs; - if (!m_renderTarget.isNull() && !shader->isLoaded()) { - const auto atts = m_attachmentPack.attachments(); - for (const Attachment &att : atts) { - if (att.m_point <= QRenderTargetOutput::Color15) - fragOutputs.insert(att.m_name, att.m_point); - } + const auto atts = m_attachmentPack.attachments(); + for (const Attachment &att : atts) { + if (att.m_point <= QRenderTargetOutput::Color15) + fragOutputs.insert(att.m_name, att.m_point); } + // Set frag outputs in the shaders if hash not empty + if (!fragOutputs.isEmpty()) + shader->setFragOutputs(fragOutputs); + } - if (!uniformNamesIds.isEmpty() || !attributeNamesIds.isEmpty() || - !shaderStorageBlockNamesIds.isEmpty() || !attributeNamesIds.isEmpty()) { + if (!uniformNamesIds.isEmpty() || !attributeNamesIds.isEmpty() || + !shaderStorageBlockNamesIds.isEmpty() || !attributeNamesIds.isEmpty()) { - // Set default standard uniforms without bindings - const Matrix4x4 worldTransform = *(entity->worldTransform()); - for (const int uniformNameId : uniformNamesIds) { - if (ms_standardUniformSetters.contains(uniformNameId)) - setStandardUniformValue(command->m_parameterPack, uniformNameId, uniformNameId, entity, worldTransform); - } + // Set default standard uniforms without bindings + const Matrix4x4 worldTransform = *(entity->worldTransform()); - // Set default attributes - for (const int attributeNameId : attributeNamesIds) - command->m_activeAttributes.push_back(attributeNameId); - - // Parameters remaining could be - // -> uniform scalar / vector - // -> uniform struct / arrays - // -> uniform block / array (4.3) - // -> ssbo block / array (4.3) - - ParameterInfoList::const_iterator it = parameters.cbegin(); - const ParameterInfoList::const_iterator parametersEnd = parameters.cend(); - - while (it != parametersEnd) { - Parameter *param = m_manager->data<Parameter, ParameterManager>(it->handle); - const UniformValue &uniformValue = param->uniformValue(); - if (uniformNamesIds.contains(it->nameId)) { // Parameter is a regular uniform - setUniformValue(command->m_parameterPack, it->nameId, uniformValue); - } else if (uniformBlockNamesIds.indexOf(it->nameId) != -1) { // Parameter is a uniform block - setUniformBlockValue(command->m_parameterPack, shader, shader->uniformBlockForBlockNameId(it->nameId), uniformValue); - } else if (shaderStorageBlockNamesIds.indexOf(it->nameId) != -1) { // Parameters is a SSBO - setShaderStorageValue(command->m_parameterPack, shader, shader->storageBlockForBlockNameId(it->nameId), uniformValue); - } else { // Parameter is a struct - ShaderData *shaderData = nullptr; - if (uniformValue.valueType() == UniformValue::NodeId && - (shaderData = m_manager->shaderDataManager()->lookupResource(*uniformValue.constData<Qt3DCore::QNodeId>())) != nullptr) { - // Try to check if we have a struct or array matching a QShaderData parameter - setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, StringToInt::lookupString(it->nameId)); - } - // Otherwise: param unused by current shader + for (const int uniformNameId : uniformNamesIds) { + if (ms_standardUniformSetters.contains(uniformNameId)) + setStandardUniformValue(command->m_parameterPack, uniformNameId, uniformNameId, entity, worldTransform); + } + + // Set default attributes + command->m_activeAttributes = attributeNamesIds; + + // Parameters remaining could be + // -> uniform scalar / vector + // -> uniform struct / arrays + // -> uniform block / array (4.3) + // -> ssbo block / array (4.3) + + ParameterInfoList::const_iterator it = parameters.cbegin(); + const ParameterInfoList::const_iterator parametersEnd = parameters.cend(); + + while (it != parametersEnd) { + Parameter *param = m_manager->data<Parameter, ParameterManager>(it->handle); + const UniformValue &uniformValue = param->uniformValue(); + if (uniformNamesIds.contains(it->nameId)) { // Parameter is a regular uniform + setUniformValue(command->m_parameterPack, it->nameId, uniformValue); + } else if (uniformBlockNamesIds.indexOf(it->nameId) != -1) { // Parameter is a uniform block + setUniformBlockValue(command->m_parameterPack, shader, shader->uniformBlockForBlockNameId(it->nameId), uniformValue); + } else if (shaderStorageBlockNamesIds.indexOf(it->nameId) != -1) { // Parameters is a SSBO + setShaderStorageValue(command->m_parameterPack, shader, shader->storageBlockForBlockNameId(it->nameId), uniformValue); + } else { // Parameter is a struct + ShaderData *shaderData = nullptr; + if (uniformValue.valueType() == UniformValue::NodeId && + (shaderData = m_manager->shaderDataManager()->lookupResource(*uniformValue.constData<Qt3DCore::QNodeId>())) != nullptr) { + // Try to check if we have a struct or array matching a QShaderData parameter + setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, StringToInt::lookupString(it->nameId)); } - ++it; + // Otherwise: param unused by current shader } + ++it; + } + + // Lights + + int lightIdx = 0; + for (const LightSource &lightSource : activeLightSources) { + if (lightIdx == MAX_LIGHTS) + break; + Entity *lightEntity = lightSource.entity; + const Vector3D worldPos = lightEntity->worldBoundingVolume()->center(); + for (Light *light : lightSource.lights) { + if (!light->isEnabled()) + continue; - // Lights + ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(light->shaderData()); + if (!shaderData) + continue; - int lightIdx = 0; - for (const LightSource &lightSource : activeLightSources) { if (lightIdx == MAX_LIGHTS) break; - Entity *lightEntity = lightSource.entity; - const Vector3D worldPos = lightEntity->worldBoundingVolume()->center(); - for (Light *light : lightSource.lights) { - if (!light->isEnabled()) - continue; - - ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(light->shaderData()); - if (!shaderData) - continue; - - if (lightIdx == MAX_LIGHTS) - break; - - // Note: implicit conversion of values to UniformValue - setUniformValue(command->m_parameterPack, LIGHT_POSITION_NAMES[lightIdx], worldPos); - setUniformValue(command->m_parameterPack, LIGHT_TYPE_NAMES[lightIdx], int(QAbstractLight::PointLight)); - setUniformValue(command->m_parameterPack, LIGHT_COLOR_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f)); - setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[lightIdx], 0.5f); - - setUniformValue(command->m_parameterPack, LIGHT_POSITION_UNROLL_NAMES[lightIdx], worldPos); - setUniformValue(command->m_parameterPack, LIGHT_TYPE_UNROLL_NAMES[lightIdx], int(QAbstractLight::PointLight)); - setUniformValue(command->m_parameterPack, LIGHT_COLOR_UNROLL_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f)); - setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_UNROLL_NAMES[lightIdx], 0.5f); - - // There is no risk in doing that even if multithreaded - // since we are sure that a shaderData is unique for a given light - // and won't ever be referenced as a Component either - Matrix4x4 *worldTransform = lightEntity->worldTransform(); - if (worldTransform) - shaderData->updateWorldTransform(*worldTransform); - - setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, LIGHT_STRUCT_NAMES[lightIdx]); - setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, LIGHT_STRUCT_UNROLL_NAMES[lightIdx]); - ++lightIdx; - } - } - if (uniformNamesIds.contains(LIGHT_COUNT_NAME_ID)) - setUniformValue(command->m_parameterPack, LIGHT_COUNT_NAME_ID, UniformValue(qMax((environmentLight ? 0 : 1), lightIdx))); - - // If no active light sources and no environment light, add a default light - if (activeLightSources.isEmpty() && !environmentLight) { // Note: implicit conversion of values to UniformValue - setUniformValue(command->m_parameterPack, LIGHT_POSITION_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f)); - setUniformValue(command->m_parameterPack, LIGHT_TYPE_NAMES[0], int(QAbstractLight::PointLight)); - setUniformValue(command->m_parameterPack, LIGHT_COLOR_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f)); - setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[0], 0.5f); - - setUniformValue(command->m_parameterPack, LIGHT_POSITION_UNROLL_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f)); - setUniformValue(command->m_parameterPack, LIGHT_TYPE_UNROLL_NAMES[0], int(QAbstractLight::PointLight)); - setUniformValue(command->m_parameterPack, LIGHT_COLOR_UNROLL_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f)); - setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_UNROLL_NAMES[0], 0.5f); + setUniformValue(command->m_parameterPack, LIGHT_POSITION_NAMES[lightIdx], worldPos); + setUniformValue(command->m_parameterPack, LIGHT_TYPE_NAMES[lightIdx], int(QAbstractLight::PointLight)); + setUniformValue(command->m_parameterPack, LIGHT_COLOR_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f)); + setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[lightIdx], 0.5f); + + setUniformValue(command->m_parameterPack, LIGHT_POSITION_UNROLL_NAMES[lightIdx], worldPos); + setUniformValue(command->m_parameterPack, LIGHT_TYPE_UNROLL_NAMES[lightIdx], int(QAbstractLight::PointLight)); + setUniformValue(command->m_parameterPack, LIGHT_COLOR_UNROLL_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f)); + setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_UNROLL_NAMES[lightIdx], 0.5f); + + + // There is no risk in doing that even if multithreaded + // since we are sure that a shaderData is unique for a given light + // and won't ever be referenced as a Component either + Matrix4x4 *worldTransform = lightEntity->worldTransform(); + if (worldTransform) + shaderData->updateWorldTransform(*worldTransform); + + setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, LIGHT_STRUCT_NAMES[lightIdx]); + setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, LIGHT_STRUCT_UNROLL_NAMES[lightIdx]); + ++lightIdx; } + } - // Environment Light - int envLightCount = 0; - if (environmentLight && environmentLight->isEnabled()) { - ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(environmentLight->shaderData()); - if (shaderData) { - setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, QStringLiteral("envLight")); - envLightCount = 1; - } - } else { - // with some drivers, samplers (like the envbox sampler) need to be bound even though - // they may not be actually used, otherwise draw calls can fail - static const int irradianceId = StringToInt::lookupId(QLatin1String("envLight.irradiance")); - static const int specularId = StringToInt::lookupId(QLatin1String("envLight.specular")); - setUniformValue(command->m_parameterPack, irradianceId, m_renderer->submissionContext()->maxTextureUnitsCount()); - setUniformValue(command->m_parameterPack, specularId, m_renderer->submissionContext()->maxTextureUnitsCount()); + if (uniformNamesIds.contains(LIGHT_COUNT_NAME_ID)) + setUniformValue(command->m_parameterPack, LIGHT_COUNT_NAME_ID, UniformValue(qMax((environmentLight ? 0 : 1), lightIdx))); + + // If no active light sources and no environment light, add a default light + if (activeLightSources.isEmpty() && !environmentLight) { + // Note: implicit conversion of values to UniformValue + setUniformValue(command->m_parameterPack, LIGHT_POSITION_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f)); + setUniformValue(command->m_parameterPack, LIGHT_TYPE_NAMES[0], int(QAbstractLight::PointLight)); + setUniformValue(command->m_parameterPack, LIGHT_COLOR_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f)); + setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[0], 0.5f); + + setUniformValue(command->m_parameterPack, LIGHT_POSITION_UNROLL_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f)); + setUniformValue(command->m_parameterPack, LIGHT_TYPE_UNROLL_NAMES[0], int(QAbstractLight::PointLight)); + setUniformValue(command->m_parameterPack, LIGHT_COLOR_UNROLL_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f)); + setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_UNROLL_NAMES[0], 0.5f); + } + + // Environment Light + int envLightCount = 0; + if (environmentLight && environmentLight->isEnabled()) { + ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(environmentLight->shaderData()); + if (shaderData) { + setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, QStringLiteral("envLight")); + envLightCount = 1; } - setUniformValue(command->m_parameterPack, StringToInt::lookupId(QStringLiteral("envLightCount")), envLightCount); + } else { + // with some drivers, samplers (like the envbox sampler) need to be bound even though + // they may not be actually used, otherwise draw calls can fail + static const int irradianceId = StringToInt::lookupId(QLatin1String("envLight.irradiance")); + static const int specularId = StringToInt::lookupId(QLatin1String("envLight.specular")); + setUniformValue(command->m_parameterPack, irradianceId, m_renderer->submissionContext()->maxTextureUnitsCount()); + setUniformValue(command->m_parameterPack, specularId, m_renderer->submissionContext()->maxTextureUnitsCount()); } - // Set frag outputs in the shaders if hash not empty - if (!fragOutputs.isEmpty()) - shader->setFragOutputs(fragOutputs); + setUniformValue(command->m_parameterPack, StringToInt::lookupId(QStringLiteral("envLightCount")), envLightCount); } } - else { - qCWarning(Render::Backend) << Q_FUNC_INFO << "Using default effect as none was provided"; - } } bool RenderView::hasBlitFramebufferInfo() const diff --git a/src/render/renderers/opengl/renderer/renderview_p.h b/src/render/renderers/opengl/renderer/renderview_p.h index 7ebcdb6bd..7d148118d 100644 --- a/src/render/renderers/opengl/renderer/renderview_p.h +++ b/src/render/renderers/opengl/renderer/renderview_p.h @@ -227,10 +227,16 @@ public: RenderPassList passesAndParameters(ParameterInfoList *parameter, Entity *node, bool useDefaultMaterials = true); - QVector<RenderCommand *> buildDrawRenderCommands(const QVector<Entity *> &entities) const; - QVector<RenderCommand *> buildComputeRenderCommands(const QVector<Entity *> &entities) const; - void setCommands(QVector<RenderCommand *> &commands) Q_DECL_NOTHROW { m_commands = commands; } - QVector<RenderCommand *> commands() const Q_DECL_NOTHROW { return m_commands; } + QVector<EntityRenderCommandData> buildDrawRenderCommands(const QVector<Entity *> &entities) const; + QVector<EntityRenderCommandData> buildComputeRenderCommands(const QVector<Entity *> &entities) const; + + + void updateRenderCommand(QVector<EntityRenderCommandData *> &renderCommandData); + + + void setCommands(const QVector<RenderCommand> &commands) Q_DECL_NOTHROW { m_commands = commands; } + QVector<RenderCommand> &commands() { return m_commands; } + QVector<RenderCommand> commands() const { return m_commands; } void setAttachmentPack(const AttachmentPack &pack) { m_attachmentPack = pack; } const AttachmentPack &attachmentPack() const { return m_attachmentPack; } @@ -291,7 +297,6 @@ public: private: void setShaderAndUniforms(RenderCommand *command, - RenderPass *pass, ParameterInfoList ¶meters, Entity *entity, const QVector<LightSource> &activeLightSources, @@ -331,10 +336,7 @@ private: QVector<Qt3DCore::QNodeId> m_insertFenceIds; QVector<QWaitFenceData> m_waitFences; - // We do not use pointers to RenderNodes or Drawable's here so that the - // render aspect is free to change the drawables on the next frame whilst - // the render thread is submitting these commands. - QVector<RenderCommand *> m_commands; + QVector<RenderCommand> m_commands; mutable QVector<LightSource> m_lightSources; EnvironmentLight *m_environmentLight; diff --git a/src/render/renderers/opengl/renderer/renderviewbuilder.cpp b/src/render/renderers/opengl/renderer/renderviewbuilder.cpp index 83fab301a..3c6521263 100644 --- a/src/render/renderers/opengl/renderer/renderviewbuilder.cpp +++ b/src/render/renderers/opengl/renderer/renderviewbuilder.cpp @@ -54,14 +54,60 @@ const int RenderViewBuilder::m_optimalParallelJobCount = std::max(std::min(4, QT namespace { -class SyncRenderViewCommandBuilders +class SyncPreCommandBuilding { public: - explicit SyncRenderViewCommandBuilders(const RenderViewInitializerJobPtr &renderViewJob, - const QVector<RenderViewBuilderJobPtr> &renderViewBuilderJobs, - Renderer *renderer) + explicit SyncPreCommandBuilding(RenderViewInitializerJobPtr renderViewInitializerJob, + const QVector<RenderViewCommandBuilderJobPtr> &renderViewCommandBuilderJobs, + Renderer *renderer, + FrameGraphNode *leafNode) + : m_renderViewInitializer(renderViewInitializerJob) + , m_renderViewCommandBuilderJobs(renderViewCommandBuilderJobs) + , m_renderer(renderer) + , m_leafNode(leafNode) + { + } + + void operator()() + { + // Split commands to build among jobs + QMutexLocker lock(m_renderer->cache()->mutex()); + // Rebuild RenderCommands for all entities in RV (ignoring filtering) + const RendererCache::LeafNodeData &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode]; + RenderView *rv = m_renderViewInitializer->renderView(); + const auto entities = !rv->isCompute() ? dataCacheForLeaf.renderableEntities : dataCacheForLeaf.computeEntities; + + rv->setMaterialParameterTable(dataCacheForLeaf.materialParameterGatherer); + + lock.unlock(); + + // Split among the number of command builders + int i = 0; + const int m = RenderViewBuilder::optimalJobCount() - 1; + const int packetSize = entities.size() / RenderViewBuilder::optimalJobCount(); + while (i < m) { + const RenderViewCommandBuilderJobPtr renderViewCommandBuilder = m_renderViewCommandBuilderJobs.at(i); + renderViewCommandBuilder->setEntities(entities.mid(i * packetSize, packetSize)); + ++i; + } + m_renderViewCommandBuilderJobs.at(i)->setEntities(entities.mid(i * packetSize, packetSize + entities.size() % (m + 1))); + } + +private: + RenderViewInitializerJobPtr m_renderViewInitializer; + QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs; + Renderer *m_renderer; + FrameGraphNode *m_leafNode; +}; + +class SyncRenderViewPostCommandUpdate +{ +public: + explicit SyncRenderViewPostCommandUpdate(const RenderViewInitializerJobPtr &renderViewJob, + const QVector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdateJobs, + Renderer *renderer) : m_renderViewJob(renderViewJob) - , m_renderViewBuilderJobs(renderViewBuilderJobs) + , m_renderViewCommandUpdaterJobs(renderViewCommandUpdateJobs) , m_renderer(renderer) {} @@ -71,17 +117,19 @@ public: RenderView *rv = m_renderViewJob->renderView(); int totalCommandCount = 0; - for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewCommandUpdaterJobs)) totalCommandCount += renderViewCommandBuilder->commands().size(); - QVector<RenderCommand *> commands; + QVector<RenderCommand> commands; commands.reserve(totalCommandCount); // Reduction - for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewCommandUpdaterJobs)) commands += std::move(renderViewCommandBuilder->commands()); + rv->setCommands(commands); + // TO DO: Find way to store commands once or at least only when required // Sort the commands rv->sort(); @@ -91,15 +139,15 @@ public: private: RenderViewInitializerJobPtr m_renderViewJob; - QVector<RenderViewBuilderJobPtr> m_renderViewBuilderJobs; + QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs; Renderer *m_renderer; }; -class SyncFrustumCulling +class SyncPreFrustumCulling { public: - explicit SyncFrustumCulling(const RenderViewInitializerJobPtr &renderViewJob, - const FrustumCullingJobPtr &frustumCulling) + explicit SyncPreFrustumCulling(const RenderViewInitializerJobPtr &renderViewJob, + const FrustumCullingJobPtr &frustumCulling) : m_renderViewJob(renderViewJob) , m_frustumCullingJob(frustumCulling) {} @@ -120,21 +168,23 @@ private: FrustumCullingJobPtr m_frustumCullingJob; }; -class SyncRenderViewInitialization +class SyncRenderViewPostInitialization { public: - explicit SyncRenderViewInitialization(const RenderViewInitializerJobPtr &renderViewJob, - const FrustumCullingJobPtr &frustumCullingJob, - const FilterLayerEntityJobPtr &filterEntityByLayerJob, - const FilterProximityDistanceJobPtr &filterProximityJob, - const QVector<MaterialParameterGathererJobPtr> &materialGathererJobs, - const QVector<RenderViewBuilderJobPtr> &renderViewBuilderJobs) + explicit SyncRenderViewPostInitialization(const RenderViewInitializerJobPtr &renderViewJob, + const FrustumCullingJobPtr &frustumCullingJob, + const FilterLayerEntityJobPtr &filterEntityByLayerJob, + const FilterProximityDistanceJobPtr &filterProximityJob, + const QVector<MaterialParameterGathererJobPtr> &materialGathererJobs, + const QVector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdaterJobs, + const QVector<RenderViewCommandBuilderJobPtr> &renderViewCommandBuilderJobs) : m_renderViewJob(renderViewJob) , m_frustumCullingJob(frustumCullingJob) , m_filterEntityByLayerJob(filterEntityByLayerJob) , m_filterProximityJob(filterProximityJob) , m_materialGathererJobs(materialGathererJobs) - , m_renderViewBuilderJobs(renderViewBuilderJobs) + , m_renderViewCommandUpdaterJobs(renderViewCommandUpdaterJobs) + , m_renderViewCommandBuilderJobs(renderViewCommandBuilderJobs) {} void operator()() @@ -154,8 +204,10 @@ public: materialGatherer->setTechniqueFilter(const_cast<TechniqueFilter *>(rv->techniqueFilter())); } - // Command builders - for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) + // Command builders and updates + for (const auto &renderViewCommandUpdater : qAsConst(m_renderViewCommandUpdaterJobs)) + renderViewCommandUpdater->setRenderView(rv); + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs)) renderViewCommandBuilder->setRenderView(rv); // Set whether frustum culling is enabled or not @@ -168,26 +220,31 @@ private: FilterLayerEntityJobPtr m_filterEntityByLayerJob; FilterProximityDistanceJobPtr m_filterProximityJob; QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs; - QVector<RenderViewBuilderJobPtr> m_renderViewBuilderJobs; + QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs; + QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs; }; -class SyncRenderCommandBuilding +class SyncRenderViewPreCommandUpdate { public: - explicit SyncRenderCommandBuilding(const RenderViewInitializerJobPtr &renderViewJob, - const FrustumCullingJobPtr &frustumCullingJob, - const FilterProximityDistanceJobPtr &filterProximityJob, - const QVector<MaterialParameterGathererJobPtr> &materialGathererJobs, - const QVector<RenderViewBuilderJobPtr> &renderViewBuilderJobs, - Renderer *renderer, - FrameGraphNode *leafNode) + explicit SyncRenderViewPreCommandUpdate(const RenderViewInitializerJobPtr &renderViewJob, + const FrustumCullingJobPtr &frustumCullingJob, + const FilterProximityDistanceJobPtr &filterProximityJob, + const QVector<MaterialParameterGathererJobPtr> &materialGathererJobs, + const QVector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdaterJobs, + const QVector<RenderViewCommandBuilderJobPtr> &renderViewCommandBuilderJobs, + Renderer *renderer, + FrameGraphNode *leafNode, + bool fullCommandRebuild) : m_renderViewJob(renderViewJob) , m_frustumCullingJob(frustumCullingJob) , m_filterProximityJob(filterProximityJob) , m_materialGathererJobs(materialGathererJobs) - , m_renderViewBuilderJobs(renderViewBuilderJobs) + , m_renderViewCommandUpdaterJobs(renderViewCommandUpdaterJobs) + , m_renderViewCommandBuilderJobs(renderViewCommandBuilderJobs) , m_renderer(renderer) , m_leafNode(leafNode) + , m_fullRebuild(fullCommandRebuild) {} void operator()() @@ -197,21 +254,45 @@ public: RenderView *rv = m_renderViewJob->renderView(); if (!rv->noDraw()) { - QVector<Entity *> renderableEntities; - const bool isDraw = !rv->isCompute(); + ///////// CACHE LOCKED //////////// + // Retrieve Data from Cache QMutexLocker lock(m_renderer->cache()->mutex()); - const auto& cacheData = m_renderer->cache()->leafNodeCache.value(m_leafNode); + Q_ASSERT(m_renderer->cache()->leafNodeCache.contains(m_leafNode)); - if (isDraw) - renderableEntities = cacheData.renderableEntities; - else - renderableEntities = cacheData.computeEntities; + const bool isDraw = !rv->isCompute(); + const RendererCache::LeafNodeData &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode]; + + // Rebuild RenderCommands if required + // This should happen fairly infrequently (FrameGraph Change, Geometry/Material change) + // and allow to skip that step most of the time + if (m_fullRebuild) { + // Clear previous cache + RendererCache::LeafNodeData &writableCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode]; + writableCacheForLeaf.renderCommandData.clear(); + + QVector<EntityRenderCommandData> commandData; + // Reduction + { + int totalCommandCount = 0; + for (const RenderViewCommandBuilderJobPtr &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs)) + totalCommandCount += renderViewCommandBuilder->commandData().size(); + commandData.reserve(totalCommandCount); + for (const RenderViewCommandBuilderJobPtr &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs)) + commandData += std::move(renderViewCommandBuilder->commandData()); + } + // Store in cache + writableCacheForLeaf.renderCommandData = std::move(commandData); + } + const QVector<EntityRenderCommandData> commandData = dataCacheForLeaf.renderCommandData; - const QVector<Entity *> filteredEntities = cacheData.filterEntitiesByLayer; - QVector<LightSource> lightSources = cacheData.gatheredLights; - rv->setEnvironmentLight(cacheData.environmentLight); + QVector<Entity *> renderableEntities = isDraw ? dataCacheForLeaf.renderableEntities : dataCacheForLeaf.computeEntities; + const QVector<Entity *> filteredEntities = dataCacheForLeaf.filterEntitiesByLayer; + QVector<LightSource> lightSources = dataCacheForLeaf.gatheredLights; + rv->setMaterialParameterTable(dataCacheForLeaf.materialParameterGatherer); + rv->setEnvironmentLight(dataCacheForLeaf.environmentLight); lock.unlock(); + ///////// END OF CACHE LOCKED //////////// // Filter out entities that weren't selected by the layer filters // Remove all entities from the compute and renderable vectors that aren't in the filtered layer vector @@ -232,20 +313,43 @@ public: renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities, m_filterProximityJob->filteredEntities()); } + // Filter out Render commands for which the Entity wasn't selected because + // of frustum, proximity or layer filtering + QVector<EntityRenderCommandData *> filteredCommandData; + filteredCommandData.reserve(renderableEntities.size()); + // Because dataCacheForLeaf.renderableEntities or computeEntities are sorted + // What we get out of EntityRenderCommandData is also sorted by Entity + auto eIt = std::cbegin(renderableEntities); + auto cIt = std::cbegin(commandData); + const auto eEnd = std::cend(renderableEntities); + const auto cEnd = std::cend(commandData); + + while (eIt != eEnd) { + const Entity *targetEntity = *eIt; + // Advance until we have commands whose Entity has a lower address + // than the selected filtered entity + while (cIt->entity < targetEntity && cIt != cEnd) + ++cIt; + + // Push pointers to command data for all commands that match the + // entity + while (cIt->entity == targetEntity && cIt != cEnd) { + filteredCommandData.push_back(const_cast<EntityRenderCommandData *>(&(*cIt))); + ++cIt; + } + ++eIt; + } + // Split among the number of command builders int i = 0; const int m = RenderViewBuilder::optimalJobCount() - 1; - const int packetSize = renderableEntities.size() / RenderViewBuilder::optimalJobCount(); + const int packetSize = filteredCommandData.size() / RenderViewBuilder::optimalJobCount(); while (i < m) { - const RenderViewBuilderJobPtr renderViewCommandBuilder = m_renderViewBuilderJobs.at(i); - renderViewCommandBuilder->setRenderables(renderableEntities.mid(i * packetSize, packetSize)); + const RenderViewCommandUpdaterJobPtr renderViewCommandBuilder = m_renderViewCommandUpdaterJobs.at(i); + renderViewCommandBuilder->setRenderables(filteredCommandData.mid(i * packetSize, packetSize)); ++i; } - m_renderViewBuilderJobs.at(i)->setRenderables(renderableEntities.mid(i * packetSize, packetSize + renderableEntities.size() % (m + 1))); - { - QMutexLocker rendererCacheLock(m_renderer->cache()->mutex()); - rv->setMaterialParameterTable(m_renderer->cache()->leafNodeCache.value(m_leafNode).materialParameterGatherer); - } + m_renderViewCommandUpdaterJobs.at(i)->setRenderables(filteredCommandData.mid(i * packetSize, packetSize + filteredCommandData.size() % (m + 1))); } } @@ -254,9 +358,11 @@ private: FrustumCullingJobPtr m_frustumCullingJob; FilterProximityDistanceJobPtr m_filterProximityJob; QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs; - QVector<RenderViewBuilderJobPtr> m_renderViewBuilderJobs; + QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs; + QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs; Renderer *m_renderer; FrameGraphNode *m_leafNode; + bool m_fullRebuild; }; class SetClearDrawBufferIndex @@ -365,8 +471,8 @@ class SyncRenderableEntities { public: explicit SyncRenderableEntities(RenderableEntityFilterPtr gatherJob, - Renderer *renderer, - FrameGraphNode *leafNode) + Renderer *renderer, + FrameGraphNode *leafNode) : m_gatherJob(gatherJob) , m_renderer(renderer) , m_leafNode(leafNode) @@ -375,10 +481,12 @@ public: void operator()() { + QVector<Entity *> selectedEntities = m_gatherJob->filteredEntities(); + std::sort(selectedEntities.begin(), selectedEntities.end()); + QMutexLocker lock(m_renderer->cache()->mutex()); RendererCache::LeafNodeData &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode]; - dataCacheForLeaf.renderableEntities = m_gatherJob->filteredEntities(); - std::sort(dataCacheForLeaf.renderableEntities.begin(), dataCacheForLeaf.renderableEntities.end()); + dataCacheForLeaf.renderableEntities = selectedEntities; } private: @@ -391,8 +499,8 @@ class SyncComputableEntities { public: explicit SyncComputableEntities(ComputableEntityFilterPtr gatherJob, - Renderer *renderer, - FrameGraphNode *leafNode) + Renderer *renderer, + FrameGraphNode *leafNode) : m_gatherJob(gatherJob) , m_renderer(renderer) , m_leafNode(leafNode) @@ -401,10 +509,12 @@ public: void operator()() { + QVector<Entity *> selectedEntities = m_gatherJob->filteredEntities(); + std::sort(selectedEntities.begin(), selectedEntities.end()); + QMutexLocker lock(m_renderer->cache()->mutex()); RendererCache::LeafNodeData &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode]; - dataCacheForLeaf.computeEntities = m_gatherJob->filteredEntities(); - std::sort(dataCacheForLeaf.computeEntities.begin(), dataCacheForLeaf.computeEntities.end()); + dataCacheForLeaf.computeEntities = selectedEntities; } private: @@ -424,10 +534,11 @@ RenderViewBuilder::RenderViewBuilder(Render::FrameGraphNode *leafNode, int rende , m_lightsCacheNeedsToBeRebuilt(false) , m_renderableCacheNeedsToBeRebuilt(false) , m_computableCacheNeedsToBeRebuilt(false) + , m_renderCommandCacheNeedsToBeRebuilt(false) , m_renderViewJob(RenderViewInitializerJobPtr::create()) , m_filterEntityByLayerJob() , m_frustumCullingJob(new Render::FrustumCullingJob()) - , m_syncFrustumCullingJob(SynchronizerJobPtr::create(SyncFrustumCulling(m_renderViewJob, m_frustumCullingJob), JobTypes::SyncFrustumCulling)) + , m_syncPreFrustumCullingJob(SynchronizerJobPtr::create(SyncPreFrustumCulling(m_renderViewJob, m_frustumCullingJob), JobTypes::SyncFrustumCulling)) , m_setClearDrawBufferIndexJob(SynchronizerJobPtr::create(SetClearDrawBufferIndex(m_renderViewJob), JobTypes::ClearBufferDrawIndex)) , m_syncFilterEntityByLayerJob() , m_filterProximityJob(Render::FilterProximityDistanceJobPtr::create()) @@ -464,9 +575,14 @@ FrustumCullingJobPtr RenderViewBuilder::frustumCullingJob() const return m_frustumCullingJob; } -QVector<RenderViewBuilderJobPtr> RenderViewBuilder::renderViewBuilderJobs() const +QVector<RenderViewCommandUpdaterJobPtr> RenderViewBuilder::renderViewCommandUpdaterJobs() const +{ + return m_renderViewCommandUpdaterJobs; +} + +QVector<RenderViewCommandBuilderJobPtr> RenderViewBuilder::renderViewCommandBuilderJobs() const { - return m_renderViewBuilderJobs; + return m_renderViewCommandBuilderJobs; } QVector<MaterialParameterGathererJobPtr> RenderViewBuilder::materialGathererJobs() const @@ -474,24 +590,29 @@ QVector<MaterialParameterGathererJobPtr> RenderViewBuilder::materialGathererJobs return m_materialGathererJobs; } -SynchronizerJobPtr RenderViewBuilder::syncRenderViewInitializationJob() const +SynchronizerJobPtr RenderViewBuilder::syncRenderViewPostInitializationJob() const +{ + return m_syncRenderViewPostInitializationJob; +} + +SynchronizerJobPtr RenderViewBuilder::syncPreFrustumCullingJob() const { - return m_syncRenderViewInitializationJob; + return m_syncPreFrustumCullingJob; } -SynchronizerJobPtr RenderViewBuilder::syncFrustumCullingJob() const +SynchronizerJobPtr RenderViewBuilder::syncRenderViewPreCommandBuildingJob() const { - return m_syncFrustumCullingJob; + return m_syncRenderViewPreCommandBuildingJob; } -SynchronizerJobPtr RenderViewBuilder::syncRenderCommandBuildingJob() const +SynchronizerJobPtr RenderViewBuilder::syncRenderViewPreCommandUpdateJob() const { - return m_syncRenderCommandBuildingJob; + return m_syncRenderViewPreCommandUpdateJob; } -SynchronizerJobPtr RenderViewBuilder::syncRenderViewCommandBuildersJob() const +SynchronizerJobPtr RenderViewBuilder::syncRenderViewPostCommandUpdateJob() const { - return m_syncRenderViewCommandBuildersJob; + return m_syncRenderViewPostCommandUpdateJob; } SynchronizerJobPtr RenderViewBuilder::setClearDrawBufferIndexJob() const @@ -526,7 +647,7 @@ void RenderViewBuilder::prepareJobs() m_lightGathererJob->setManager(entityManager); m_cacheLightsJob = SynchronizerJobPtr::create(SyncLightsGatherer(m_lightGathererJob, m_renderer, m_leafNode), - JobTypes::EntityComponentTypeFiltering); + JobTypes::EntityComponentTypeFiltering); m_cacheLightsJob->addDependency(m_lightGathererJob); } @@ -535,8 +656,8 @@ void RenderViewBuilder::prepareJobs() m_renderableEntityFilterJob->setManager(entityManager); m_cacheRenderableEntitiesJob = SynchronizerJobPtr::create( - SyncRenderableEntities(m_renderableEntityFilterJob, m_renderer, m_leafNode), - JobTypes::EntityComponentTypeFiltering); + SyncRenderableEntities(m_renderableEntityFilterJob, m_renderer, m_leafNode), + JobTypes::EntityComponentTypeFiltering); m_cacheRenderableEntitiesJob->addDependency(m_renderableEntityFilterJob); } @@ -545,23 +666,36 @@ void RenderViewBuilder::prepareJobs() m_computableEntityFilterJob->setManager(entityManager); m_cacheComputableEntitiesJob = SynchronizerJobPtr::create( - SyncComputableEntities(m_computableEntityFilterJob, m_renderer, m_leafNode), - JobTypes::EntityComponentTypeFiltering); + SyncComputableEntities(m_computableEntityFilterJob, m_renderer, m_leafNode), + JobTypes::EntityComponentTypeFiltering); m_cacheComputableEntitiesJob->addDependency(m_computableEntityFilterJob); } + if (m_renderCommandCacheNeedsToBeRebuilt) { + + m_renderViewCommandBuilderJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount); + for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) { + auto renderViewCommandBuilder = Render::RenderViewCommandBuilderJobPtr::create(); + m_renderViewCommandBuilderJobs.push_back(renderViewCommandBuilder); + } + m_syncRenderViewPreCommandBuildingJob = SynchronizerJobPtr::create(SyncPreCommandBuilding(m_renderViewJob, + m_renderViewCommandBuilderJobs, + m_renderer, + m_leafNode), + JobTypes::SyncRenderViewPreCommandBuilding); + } + m_renderViewJob->setRenderer(m_renderer); m_renderViewJob->setFrameGraphLeafNode(m_leafNode); m_renderViewJob->setSubmitOrderIndex(m_renderViewIndex); // RenderCommand building is the most consuming task -> split it // Estimate the number of jobs to create based on the number of entities - m_renderViewBuilderJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount); + m_renderViewCommandUpdaterJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount); for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) { - auto renderViewCommandBuilder = Render::RenderViewBuilderJobPtr::create(); - renderViewCommandBuilder->setIndex(m_renderViewIndex); - renderViewCommandBuilder->setRenderer(m_renderer); - m_renderViewBuilderJobs.push_back(renderViewCommandBuilder); + auto renderViewCommandUpdater = Render::RenderViewCommandUpdaterJobPtr::create(); + renderViewCommandUpdater->setRenderer(m_renderer); + m_renderViewCommandUpdaterJobs.push_back(renderViewCommandUpdater); } if (m_materialGathererCacheNeedsToBeRebuilt) { @@ -582,7 +716,7 @@ void RenderViewBuilder::prepareJobs() m_syncMaterialGathererJob = SynchronizerJobPtr::create(SyncMaterialParameterGatherer(m_materialGathererJobs, m_renderer, m_leafNode), - JobTypes::SyncMaterialGatherer); + JobTypes::SyncMaterialGatherer); } if (m_layerCacheNeedsToBeRebuilt) { @@ -594,34 +728,37 @@ void RenderViewBuilder::prepareJobs() JobTypes::SyncFilterEntityByLayer); } - m_syncRenderCommandBuildingJob = SynchronizerJobPtr::create(SyncRenderCommandBuilding(m_renderViewJob, - m_frustumCullingJob, - m_filterProximityJob, - m_materialGathererJobs, - m_renderViewBuilderJobs, - m_renderer, - m_leafNode), - JobTypes::SyncRenderViewCommandBuilding); - - m_syncRenderViewCommandBuildersJob = SynchronizerJobPtr::create(SyncRenderViewCommandBuilders(m_renderViewJob, - m_renderViewBuilderJobs, - m_renderer), - JobTypes::SyncRenderViewCommandBuilder); - - m_syncRenderViewInitializationJob = SynchronizerJobPtr::create(SyncRenderViewInitialization(m_renderViewJob, - m_frustumCullingJob, - m_filterEntityByLayerJob, - m_filterProximityJob, - m_materialGathererJobs, - m_renderViewBuilderJobs), - JobTypes::SyncRenderViewInitialization); + m_syncRenderViewPreCommandUpdateJob = SynchronizerJobPtr::create(SyncRenderViewPreCommandUpdate(m_renderViewJob, + m_frustumCullingJob, + m_filterProximityJob, + m_materialGathererJobs, + m_renderViewCommandUpdaterJobs, + m_renderViewCommandBuilderJobs, + m_renderer, + m_leafNode, + m_renderCommandCacheNeedsToBeRebuilt), + JobTypes::SyncRenderViewPreCommandUpdate); + + m_syncRenderViewPostCommandUpdateJob = SynchronizerJobPtr::create(SyncRenderViewPostCommandUpdate(m_renderViewJob, + m_renderViewCommandUpdaterJobs, + m_renderer), + JobTypes::SyncRenderViewPostCommandUpdate); + + m_syncRenderViewPostInitializationJob = SynchronizerJobPtr::create(SyncRenderViewPostInitialization(m_renderViewJob, + m_frustumCullingJob, + m_filterEntityByLayerJob, + m_filterProximityJob, + m_materialGathererJobs, + m_renderViewCommandUpdaterJobs, + m_renderViewCommandBuilderJobs), + JobTypes::SyncRenderViewInitialization); } QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const { QVector<Qt3DCore::QAspectJobPtr> jobs; - jobs.reserve(m_materialGathererJobs.size() + m_renderViewBuilderJobs.size() + 11); + jobs.reserve(m_materialGathererJobs.size() + m_renderViewCommandUpdaterJobs.size() + 11); // Set dependencies @@ -629,36 +766,36 @@ QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const // TODO: Maybe only update skinning palettes for non-culled entities m_renderViewJob->addDependency(m_renderer->updateSkinningPaletteJob()); - m_syncFrustumCullingJob->addDependency(m_renderer->updateWorldTransformJob()); - m_syncFrustumCullingJob->addDependency(m_renderer->updateShaderDataTransformJob()); - m_syncFrustumCullingJob->addDependency(m_syncRenderViewInitializationJob); + m_syncPreFrustumCullingJob->addDependency(m_renderer->updateWorldTransformJob()); + m_syncPreFrustumCullingJob->addDependency(m_renderer->updateShaderDataTransformJob()); + m_syncPreFrustumCullingJob->addDependency(m_syncRenderViewPostInitializationJob); m_frustumCullingJob->addDependency(m_renderer->expandBoundingVolumeJob()); - m_frustumCullingJob->addDependency(m_syncFrustumCullingJob); + m_frustumCullingJob->addDependency(m_syncPreFrustumCullingJob); - m_setClearDrawBufferIndexJob->addDependency(m_syncRenderViewInitializationJob); + m_setClearDrawBufferIndexJob->addDependency(m_syncRenderViewPostInitializationJob); - m_syncRenderViewInitializationJob->addDependency(m_renderViewJob); + m_syncRenderViewPostInitializationJob->addDependency(m_renderViewJob); m_filterProximityJob->addDependency(m_renderer->expandBoundingVolumeJob()); - m_filterProximityJob->addDependency(m_syncRenderViewInitializationJob); + m_filterProximityJob->addDependency(m_syncRenderViewPostInitializationJob); - m_syncRenderCommandBuildingJob->addDependency(m_syncRenderViewInitializationJob); - m_syncRenderCommandBuildingJob->addDependency(m_filterProximityJob); - m_syncRenderCommandBuildingJob->addDependency(m_frustumCullingJob); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_syncRenderViewPostInitializationJob); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_filterProximityJob); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_frustumCullingJob); // Ensure the RenderThread won't be able to process dirtyResources // before they have been completely gathered - m_syncRenderCommandBuildingJob->addDependency(m_renderer->introspectShadersJob()); - m_syncRenderCommandBuildingJob->addDependency(m_renderer->bufferGathererJob()); - m_syncRenderCommandBuildingJob->addDependency(m_renderer->textureGathererJob()); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->introspectShadersJob()); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->bufferGathererJob()); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->textureGathererJob()); - for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) { - renderViewCommandBuilder->addDependency(m_syncRenderCommandBuildingJob); - m_syncRenderViewCommandBuildersJob->addDependency(renderViewCommandBuilder); + for (const auto &renderViewCommandUpdater : qAsConst(m_renderViewCommandUpdaterJobs)) { + renderViewCommandUpdater->addDependency(m_syncRenderViewPreCommandUpdateJob); + m_syncRenderViewPostCommandUpdateJob->addDependency(renderViewCommandUpdater); } - m_renderer->frameCleanupJob()->addDependency(m_syncRenderViewCommandBuildersJob); + m_renderer->frameCleanupJob()->addDependency(m_syncRenderViewPostCommandUpdateJob); m_renderer->frameCleanupJob()->addDependency(m_setClearDrawBufferIndexJob); // Add jobs @@ -667,59 +804,75 @@ QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const if (m_lightsCacheNeedsToBeRebuilt) { jobs.push_back(m_lightGathererJob); // Step 1 jobs.push_back(m_cacheLightsJob); - m_syncRenderCommandBuildingJob->addDependency(m_cacheLightsJob); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_cacheLightsJob); } if (m_renderableCacheNeedsToBeRebuilt) { jobs.push_back(m_renderableEntityFilterJob); // Step 1 jobs.push_back(m_cacheRenderableEntitiesJob); - m_syncRenderCommandBuildingJob->addDependency(m_cacheRenderableEntitiesJob); } if (m_computableCacheNeedsToBeRebuilt) { // Note: do it only if OpenGL 4.3+ available jobs.push_back(m_computableEntityFilterJob); // Step 1 jobs.push_back(m_cacheComputableEntitiesJob); - m_syncRenderCommandBuildingJob->addDependency(m_cacheComputableEntitiesJob); } - jobs.push_back(m_syncRenderViewInitializationJob); // Step 2 + jobs.push_back(m_syncRenderViewPostInitializationJob); // Step 2 + + if (m_renderCommandCacheNeedsToBeRebuilt) { // Step 3 + m_syncRenderViewPreCommandBuildingJob->addDependency(m_cacheComputableEntitiesJob); + m_syncRenderViewPreCommandBuildingJob->addDependency(m_cacheRenderableEntitiesJob); + m_syncRenderViewPreCommandBuildingJob->addDependency(m_syncRenderViewPostInitializationJob); + + if (m_materialGathererCacheNeedsToBeRebuilt) + m_syncRenderViewPreCommandBuildingJob->addDependency(m_syncMaterialGathererJob); + + jobs.push_back(m_syncRenderViewPreCommandBuildingJob); + + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs)) { + renderViewCommandBuilder->addDependency(m_syncRenderViewPreCommandBuildingJob); + m_syncRenderViewPreCommandUpdateJob->addDependency(renderViewCommandBuilder); + jobs.push_back(renderViewCommandBuilder); + } + } if (m_layerCacheNeedsToBeRebuilt) { m_filterEntityByLayerJob->addDependency(m_renderer->updateEntityLayersJob()); - m_filterEntityByLayerJob->addDependency(m_syncRenderViewInitializationJob); + m_filterEntityByLayerJob->addDependency(m_syncRenderViewPostInitializationJob); m_filterEntityByLayerJob->addDependency(m_renderer->updateTreeEnabledJob()); m_syncFilterEntityByLayerJob->addDependency(m_filterEntityByLayerJob); - m_syncRenderCommandBuildingJob->addDependency(m_syncFilterEntityByLayerJob); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_syncFilterEntityByLayerJob); jobs.push_back(m_filterEntityByLayerJob); // Step 3 jobs.push_back(m_syncFilterEntityByLayerJob); // Step 4 } - jobs.push_back(m_syncFrustumCullingJob); // Step 3 + jobs.push_back(m_syncPreFrustumCullingJob); // Step 3 jobs.push_back(m_filterProximityJob); // Step 3 jobs.push_back(m_setClearDrawBufferIndexJob); // Step 3 if (m_materialGathererCacheNeedsToBeRebuilt) { for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) { - materialGatherer->addDependency(m_syncRenderViewInitializationJob); + materialGatherer->addDependency(m_syncRenderViewPostInitializationJob); materialGatherer->addDependency(m_renderer->introspectShadersJob()); materialGatherer->addDependency(m_renderer->filterCompatibleTechniqueJob()); jobs.push_back(materialGatherer); // Step3 m_syncMaterialGathererJob->addDependency(materialGatherer); } - m_syncRenderCommandBuildingJob->addDependency(m_syncMaterialGathererJob); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_syncMaterialGathererJob); jobs.push_back(m_syncMaterialGathererJob); // Step 3 } jobs.push_back(m_frustumCullingJob); // Step 4 - jobs.push_back(m_syncRenderCommandBuildingJob); // Step 5 + jobs.push_back(m_syncRenderViewPreCommandUpdateJob); // Step 5 - for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) // Step 6 + // Build RenderCommands or Update RenderCommand Uniforms + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewCommandUpdaterJobs)) // Step 6 jobs.push_back(renderViewCommandBuilder); - jobs.push_back(m_syncRenderViewCommandBuildersJob); // Step 7 + jobs.push_back(m_syncRenderViewPostCommandUpdateJob); // Step 7 return jobs; } @@ -784,6 +937,16 @@ bool RenderViewBuilder::lightGathererCacheNeedsToBeRebuilt() const return m_lightsCacheNeedsToBeRebuilt; } +void RenderViewBuilder::setRenderCommandCacheNeedsToBeRebuilt(bool needsToBeRebuilt) +{ + m_renderCommandCacheNeedsToBeRebuilt = needsToBeRebuilt; +} + +bool RenderViewBuilder::renderCommandCacheNeedsToBeRebuilt() const +{ + return m_renderCommandCacheNeedsToBeRebuilt; +} + int RenderViewBuilder::optimalJobCount() { return RenderViewBuilder::m_optimalParallelJobCount; diff --git a/src/render/renderers/opengl/renderer/renderviewbuilder_p.h b/src/render/renderers/opengl/renderer/renderviewbuilder_p.h index e223a5f1e..66344da11 100644 --- a/src/render/renderers/opengl/renderer/renderviewbuilder_p.h +++ b/src/render/renderers/opengl/renderer/renderviewbuilder_p.h @@ -58,7 +58,8 @@ #include <Qt3DRender/private/genericlambdajob_p.h> #include <Qt3DRender/private/materialparametergathererjob_p.h> #include <Qt3DRender/private/nodemanagers_p.h> -#include <Qt3DRender/private/renderviewbuilderjob_p.h> +#include <Qt3DRender/private/renderviewcommandbuilderjob_p.h> +#include <Qt3DRender/private/renderviewcommandupdaterjob_p.h> #include <Qt3DRender/private/renderview_p.h> #include <Qt3DRender/private/frustumcullingjob_p.h> #include <Qt3DRender/private/lightgatherer_p.h> @@ -87,12 +88,14 @@ public: RenderableEntityFilterPtr renderableEntityFilterJob() const; ComputableEntityFilterPtr computableEntityFilterJob() const; FrustumCullingJobPtr frustumCullingJob() const; - QVector<RenderViewBuilderJobPtr> renderViewBuilderJobs() const; + QVector<RenderViewCommandBuilderJobPtr> renderViewCommandBuilderJobs() const; + QVector<RenderViewCommandUpdaterJobPtr> renderViewCommandUpdaterJobs() const; QVector<MaterialParameterGathererJobPtr> materialGathererJobs() const; - SynchronizerJobPtr syncRenderViewInitializationJob() const; - SynchronizerJobPtr syncFrustumCullingJob() const; - SynchronizerJobPtr syncRenderCommandBuildingJob() const; - SynchronizerJobPtr syncRenderViewCommandBuildersJob() const; + SynchronizerJobPtr syncRenderViewPostInitializationJob() const; + SynchronizerJobPtr syncPreFrustumCullingJob() const; + SynchronizerJobPtr syncRenderViewPreCommandBuildingJob() const; + SynchronizerJobPtr syncRenderViewPreCommandUpdateJob() const; + SynchronizerJobPtr syncRenderViewPostCommandUpdateJob() const; SynchronizerJobPtr setClearDrawBufferIndexJob() const; SynchronizerJobPtr syncFilterEntityByLayerJob() const; FilterProximityDistanceJobPtr filterProximityJob() const; @@ -118,6 +121,9 @@ public: void setLightGathererCacheNeedsToBeRebuilt(bool needsToBeRebuilt); bool lightGathererCacheNeedsToBeRebuilt() const; + void setRenderCommandCacheNeedsToBeRebuilt(bool needsToBeRebuilt); + bool renderCommandCacheNeedsToBeRebuilt() const; + static int optimalJobCount(); static QVector<Entity *> entitiesInSubset(const QVector<Entity *> &entities, const QVector<Entity *> &subset); @@ -130,6 +136,7 @@ private: bool m_lightsCacheNeedsToBeRebuilt; bool m_renderableCacheNeedsToBeRebuilt; bool m_computableCacheNeedsToBeRebuilt; + bool m_renderCommandCacheNeedsToBeRebuilt; RenderViewInitializerJobPtr m_renderViewJob; FilterLayerEntityJobPtr m_filterEntityByLayerJob; @@ -137,13 +144,15 @@ private: RenderableEntityFilterPtr m_renderableEntityFilterJob; ComputableEntityFilterPtr m_computableEntityFilterJob; FrustumCullingJobPtr m_frustumCullingJob; - QVector<RenderViewBuilderJobPtr> m_renderViewBuilderJobs; + QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs; + QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs; QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs; - SynchronizerJobPtr m_syncRenderViewInitializationJob; - SynchronizerJobPtr m_syncFrustumCullingJob; - SynchronizerJobPtr m_syncRenderCommandBuildingJob; - SynchronizerJobPtr m_syncRenderViewCommandBuildersJob; + SynchronizerJobPtr m_syncRenderViewPostInitializationJob; + SynchronizerJobPtr m_syncPreFrustumCullingJob; + SynchronizerJobPtr m_syncRenderViewPreCommandBuildingJob; + SynchronizerJobPtr m_syncRenderViewPreCommandUpdateJob; + SynchronizerJobPtr m_syncRenderViewPostCommandUpdateJob; SynchronizerJobPtr m_setClearDrawBufferIndexJob; SynchronizerJobPtr m_syncFilterEntityByLayerJob; SynchronizerJobPtr m_syncMaterialGathererJob; |