diff options
author | Paul Lemire <paul.lemire@kdab.com> | 2020-04-30 16:09:46 +0200 |
---|---|---|
committer | Paul Lemire <paul.lemire@kdab.com> | 2020-06-04 14:02:33 +0200 |
commit | 80e73b0a8537c794be187e8847f84d376c30bd66 (patch) | |
tree | a28c70f7985678880f16982a7da1260beebc0c06 /src | |
parent | 0c9c4e163458f99ac350aef4979754cbe147dac3 (diff) |
Leverage RV cache to reuse render commands when possible
- Only perform render command filtering when needed (when camera has moved usually)
- Keep RenderCommand around so that we only update them if needed:
-> We keep a double set of commands we per RV and switch in between every frame
- Introduce EntityRenderCommandDataView as a wrapper around RenderCommands
-> Use std::vector for RenderCommand storage (avoids hidden detachments)
-> Filter and sort indices into the RenderCommand vector rather than the commands
directly
- Cleanup RenderView
-> Remove InnerData field
-> Hide direct RenderCommand access
- Next steps
-> Only update uniforms that need to be updated
-> Most likely lights/standard uniforms won't all change every frame
-> Only update light sources for entity when needed
Change-Id: I153822a16c0989a8ac5b83d756385056c96572aa
Reviewed-by: Mike Krus <mike.krus@kdab.com>
(cherry picked from commit 59345bc1a733ee035b342feecadc923e062a850c)
Diffstat (limited to 'src')
12 files changed, 576 insertions, 430 deletions
diff --git a/src/plugins/renderers/opengl/debug/imguirenderer.cpp b/src/plugins/renderers/opengl/debug/imguirenderer.cpp index 0acee945a..f193d624e 100644 --- a/src/plugins/renderers/opengl/debug/imguirenderer.cpp +++ b/src/plugins/renderers/opengl/debug/imguirenderer.cpp @@ -201,19 +201,18 @@ void ImGuiRenderer::renderDebugOverlay(const QVector<RenderView *> &renderViews, QSet<HGeometryRenderer> inUseGeometries; QSet<Qt3DCore::QNodeId> inUseTextures; for (int j=0; j<renderViewsCount; j++) { - const auto &commands = renderViews.at(j)->commands(); - nCommands += commands.size(); - for (int k=0; k<commands.size(); k++) { - const RenderCommand &command = commands.at(k); + RenderView *rv = renderViews.at(j); + nCommands += rv->commandCount(); + rv->forEachCommand([&] (const RenderCommand &command) { if (command.m_type != RenderCommand::Draw) - continue; + return; nVertices += command.m_primitiveCount; nPrimitives += vertexToPrimitiveCount(command.m_primitiveType, command.m_primitiveCount); inUseGeometries.insert(command.m_geometryRenderer); const auto &textures = command.m_parameterPack.textures(); for (const auto &ns: textures) inUseTextures.insert(ns.nodeId); - } + }); } auto columnNumber = [](int i) { @@ -340,8 +339,8 @@ void ImGuiRenderer::showRenderDetails(const QVector<RenderView *> &renderViews) ImGui::Text("Clear Depth Value: %f", static_cast<double>(view->clearDepthValue())); ImGui::Text("Clear Stencil Value: %d", view->clearStencilValue()); int j = 1; - const auto commands = view->commands(); - for (const RenderCommand &command: commands) { + + view->forEachCommand([&] (const RenderCommand &command) { GeometryRenderer *rGeometryRenderer = m_renderer->nodeManagers()->data<GeometryRenderer, GeometryRendererManager>(command.m_geometryRenderer); QString label = QString(QLatin1String("Command %1 {%2}")).arg(QString::number(j++), QString::number(rGeometryRenderer->peerId().id())); if (ImGui::TreeNode(label.toLatin1().data())) { @@ -352,7 +351,7 @@ void ImGuiRenderer::showRenderDetails(const QVector<RenderView *> &renderViews) ImGui::Text("# Instances: %d", command.m_instanceCount); ImGui::TreePop(); } - } + }); ImGui::TreePop(); ImGui::Separator(); } diff --git a/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp b/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp index 9bbdf50fd..0c84fde9e 100644 --- a/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp +++ b/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp @@ -70,7 +70,7 @@ public: bool RenderViewCommandUpdaterJobPrivate::isRequired() const { Q_Q(const RenderViewCommandUpdaterJob); - return q->m_renderView && !q->m_renderView->noDraw() && q->m_count > 0; + return q->m_renderView && !q->m_renderView->noDraw() && q->m_renderablesSubView.count > 0; } void RenderViewCommandUpdaterJobPrivate::postFrame(Qt3DCore::QAspectManager *manager) @@ -83,11 +83,9 @@ void RenderViewCommandUpdaterJobPrivate::postFrame(Qt3DCore::QAspectManager *man RenderViewCommandUpdaterJob::RenderViewCommandUpdaterJob() : Qt3DCore::QAspectJob(*new RenderViewCommandUpdaterJobPrivate(this)) - , m_offset(0) - , m_count(0) , m_renderView(nullptr) , m_renderer(nullptr) - , m_renderables() + , m_renderablesSubView() { SET_JOB_RUN_STAT_TYPE(this, JobTypes::RenderCommandUpdater, renderViewInstanceCounter++) } @@ -97,10 +95,10 @@ void RenderViewCommandUpdaterJob::run() // Build RenderCommand should perform the culling as we have no way to determine // if a child has a mesh in the view frustum while its parent isn't contained in it. if (!m_renderView->noDraw()) { - if (m_count == 0) + if (m_renderablesSubView.count == 0) return; // Update Render Commands (Uniform Change, Depth Change) - m_renderView->updateRenderCommand(m_renderables.data(), m_offset, m_count); + m_renderView->updateRenderCommand(m_renderablesSubView); } } diff --git a/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob_p.h b/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob_p.h index 9c7e84edd..1ebeb0249 100644 --- a/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob_p.h +++ b/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob_p.h @@ -74,24 +74,20 @@ public: inline void setRenderView(RenderView *rv) Q_DECL_NOTHROW { m_renderView = rv; } inline void setRenderer(Renderer *renderer) Q_DECL_NOTHROW { m_renderer = renderer; } - inline void setRenderables(const EntityRenderCommandDataPtr &renderables, int offset, int count) Q_DECL_NOTHROW + inline void setRenderablesSubView(const EntityRenderCommandDataSubView &renderablesSubView) Q_DECL_NOTHROW { - m_offset = offset; - m_count = count; - m_renderables = renderables; + m_renderablesSubView = renderablesSubView; } - EntityRenderCommandDataPtr renderables() const { return m_renderables; } + EntityRenderCommandDataSubView renderablesSubView() const { return m_renderablesSubView; } inline void setRebuildFlags(RebuildFlagSet rebuildFlags) { m_rebuildFlags = rebuildFlags; } void run() final; private: - int m_offset; - int m_count; RebuildFlagSet m_rebuildFlags; RenderView *m_renderView; Renderer *m_renderer; - EntityRenderCommandDataPtr m_renderables; + EntityRenderCommandDataSubView m_renderablesSubView; Q_DECLARE_PRIVATE(RenderViewCommandUpdaterJob) }; diff --git a/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp b/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp index 8774ac368..eb1e3dd38 100644 --- a/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp +++ b/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp @@ -188,17 +188,13 @@ void setRenderViewConfigFromFrameGraphLeafNode(RenderView *rv, const FrameGraphN case FrameGraphNode::StateSet: { const Render::StateSetNode *rStateSet = static_cast<const Render::StateSetNode *>(node); - // Create global RenderStateSet for renderView if no stateSet was set before - RenderStateSet *stateSet = rv->stateSet(); - if (stateSet == nullptr && rStateSet->hasRenderStates()) { - stateSet = new RenderStateSet(); - rv->setStateSet(stateSet); - } - // Add states from new stateSet we might be missing // but don' t override existing states (lower StateSetNode always has priority) - if (rStateSet->hasRenderStates()) + if (rStateSet->hasRenderStates()) { + // Create global RenderStateSet for renderView if no stateSet was set before + RenderStateSet *stateSet = rv->getOrCreateStateSet(); addStatesToRenderStateSet(stateSet, rStateSet->renderStates(), manager->renderStateManager()); + } break; } diff --git a/src/plugins/renderers/opengl/renderer/commandexecuter.cpp b/src/plugins/renderers/opengl/renderer/commandexecuter.cpp index 03d5d5a7f..70fdbb943 100644 --- a/src/plugins/renderers/opengl/renderer/commandexecuter.cpp +++ b/src/plugins/renderers/opengl/renderer/commandexecuter.cpp @@ -346,7 +346,7 @@ void CommandExecuter::performAsynchronousCommandExecution(const QVector<Render:: viewObj.insert(QLatin1String("clearStencilValue"), v->clearStencilValue()); QJsonArray renderCommandsArray; - for (Render::OpenGL::RenderCommand &c : v->commands()) { + v->forEachCommand([&] (Render::OpenGL::RenderCommand &c) { QJsonObject commandObj; Render::NodeManagers *nodeManagers = m_renderer->nodeManagers(); commandObj.insert(QLatin1String("shader"), typeToJsonValue(QVariant::fromValue(c.m_shaderId))); @@ -357,7 +357,7 @@ void CommandExecuter::performAsynchronousCommandExecution(const QVector<Render:: commandObj.insert(QLatin1String("shaderParameterPack"), parameterPackToJson(c.m_parameterPack)); renderCommandsArray.push_back(commandObj); - } + }); viewObj.insert(QLatin1String("commands"), renderCommandsArray); viewArray.push_back(viewObj); } diff --git a/src/plugins/renderers/opengl/renderer/rendercommand_p.h b/src/plugins/renderers/opengl/renderer/rendercommand_p.h index ce81e041d..a2305ecdf 100644 --- a/src/plugins/renderers/opengl/renderer/rendercommand_p.h +++ b/src/plugins/renderers/opengl/renderer/rendercommand_p.h @@ -61,6 +61,7 @@ #include <QOpenGLShaderProgram> #include <QOpenGLTexture> #include <QMatrix4x4> +#include <vector> QT_BEGIN_NAMESPACE @@ -145,18 +146,18 @@ inline bool operator!=(const RenderCommand &lhs, const RenderCommand &rhs) noexc struct EntityRenderCommandData { - QVector<Entity *> entities; - QVector<RenderCommand> commands; - QVector<RenderPassParameterData> passesData; + std::vector<Entity *> entities; + std::vector<RenderCommand> commands; + std::vector<RenderPassParameterData> passesData; - void reserve(int size) + void reserve(size_t size) { entities.reserve(size); commands.reserve(size); passesData.reserve(size); } - inline int size() const { return entities.size(); } + inline size_t size() const { return entities.size(); } inline void push_back(Entity *e, const RenderCommand &c, const RenderPassParameterData &p) { @@ -174,15 +175,64 @@ struct EntityRenderCommandData EntityRenderCommandData &operator+=(EntityRenderCommandData &&t) { - entities += std::move(t.entities); - commands += std::move(t.commands); - passesData += std::move(t.passesData); + entities.insert(entities.cend(), + std::make_move_iterator(t.entities.begin()), + std::make_move_iterator(t.entities.end())); + commands.insert(commands.cend(), + std::make_move_iterator(t.commands.begin()), + std::make_move_iterator(t.commands.end())); + passesData.insert(passesData.cend(), + std::make_move_iterator(t.passesData.begin()), + std::make_move_iterator(t.passesData.end())); return *this; } }; -using EntityRenderCommandDataPtr = QSharedPointer<EntityRenderCommandData>; +struct EntityRenderCommandDataView +{ + EntityRenderCommandData data; + std::vector<size_t> indices; + + size_t size() const noexcept { return indices.size(); } + + template<typename F> + void forEachCommand(F func) + { + for (size_t idx : indices) + func(data.commands[idx]); + } +}; +using EntityRenderCommandDataViewPtr = QSharedPointer<EntityRenderCommandDataView>; + +struct EntityRenderCommandDataSubView +{ + EntityRenderCommandDataViewPtr view; + size_t offset; + size_t count; + + template<typename F> + void forEach(F func) + { + for (size_t i = 0, m = size_t(count); i < m; ++i) { + const size_t idx = view->indices[offset + i]; + func(view->data.entities[idx], + view->data.passesData[idx], + view->data.commands[idx]); + } + } + + template<typename F> + void forEach(F func) const + { + for (size_t i = 0, m = size_t(count); i < m; ++i) { + const size_t idx = view->indices[offset + i]; + func(view->data.entities[idx], + view->data.passesData[idx], + view->data.commands[idx]); + } + } +}; } // namespace OpenGL diff --git a/src/plugins/renderers/opengl/renderer/renderer.cpp b/src/plugins/renderers/opengl/renderer/renderer.cpp index a5d624c34..63e5fe6c3 100644 --- a/src/plugins/renderers/opengl/renderer/renderer.cpp +++ b/src/plugins/renderers/opengl/renderer/renderer.cpp @@ -938,8 +938,7 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView QHash<HVao, bool> updatedTable; for (RenderView *rv: renderViews) { - QVector<RenderCommand> &commands = rv->commands(); - for (RenderCommand &command : commands) { + rv->forEachCommand([&] (RenderCommand &command) { // Update/Create VAO if (command.m_type == RenderCommand::Draw) { Geometry *rGeometry = m_nodesManager->data<Geometry, GeometryManager>(command.m_geometry); @@ -998,7 +997,7 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView GLShader *shader = command.m_glShader; Q_ASSERT(shader); } - } + }); } // Make sure we leave nothing bound @@ -1525,7 +1524,7 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren for (int i = 0; i < renderViewsCount; ++i) { // Initialize GraphicsContext for drawing // If the RenderView has a RenderStateSet defined - const RenderView *renderView = renderViews.at(i); + RenderView *renderView = renderViews.at(i); if (renderView->shouldSkipSubmission()) continue; @@ -1982,7 +1981,7 @@ QAbstractFrameAdvanceService *Renderer::frameAdvanceService() const } // Called by executeCommands -void Renderer::performDraw(RenderCommand *command) +void Renderer::performDraw(const RenderCommand *command) { // Indirect Draw Calls if (command->m_drawIndirect) { @@ -2108,12 +2107,11 @@ void Renderer::createOrUpdateVAO(RenderCommand *command, // Called by RenderView->submit() in RenderThread context // Returns true, if all RenderCommands were sent to the GPU -bool Renderer::executeCommandsSubmission(const RenderView *rv) +bool Renderer::executeCommandsSubmission(RenderView *rv) { bool allCommandsIssued = true; // Render drawing commands - QVector<RenderCommand> commands = rv->commands(); // Use the graphicscontext to submit the commands to the underlying // graphics API (OpenGL) @@ -2122,7 +2120,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) RenderStateSet *globalState = m_submissionContext->currentStateSet(); OpenGLVertexArrayObject *vao = nullptr; - for (RenderCommand &command : commands) { + rv->forEachCommand([&] (RenderCommand &command) { if (command.m_type == RenderCommand::Compute) { // Compute Call performCompute(rv, &command); @@ -2130,7 +2128,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) // Check if we have a valid command that can be drawn if (!command.m_isValid) { allCommandsIssued = false; - continue; + return; } vao = m_glResourceManagers->vaoManager()->data(command.m_vao); @@ -2138,7 +2136,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) // something may have went wrong when initializing the VAO if (!vao->isSpecified()) { allCommandsIssued = false; - continue; + return; } { @@ -2147,7 +2145,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) GLShader *shader = command.m_glShader; if (!m_submissionContext->activateShader(shader)) { allCommandsIssued = false; - continue; + return; } } @@ -2164,7 +2162,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) 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 - continue; + return; } } @@ -2192,7 +2190,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) //// Draw Calls performDraw(&command); } - } // end of RenderCommands loop + }); // end of RenderCommands loop // We cache the VAO and release it only at the end of the exectute frame // We try to minimize VAO binding between RenderCommands diff --git a/src/plugins/renderers/opengl/renderer/renderer_p.h b/src/plugins/renderers/opengl/renderer/renderer_p.h index eff5e9d96..fddff18c8 100644 --- a/src/plugins/renderers/opengl/renderer/renderer_p.h +++ b/src/plugins/renderers/opengl/renderer/renderer_p.h @@ -254,7 +254,7 @@ public: GLuint defaultFramebuffer); void prepareCommandsSubmission(const QVector<RenderView *> &renderViews); - bool executeCommandsSubmission(const RenderView *rv); + bool executeCommandsSubmission(RenderView *rv); bool updateVAOWithAttributes(Geometry *geometry, const RenderCommand *command, GLShader *shader, @@ -366,7 +366,7 @@ private: QVector<Qt3DCore::QNodeId> m_pendingRenderCaptureSendRequests; - void performDraw(RenderCommand *command); + void performDraw(const RenderCommand *command); void performCompute(const RenderView *rv, RenderCommand *command); void createOrUpdateVAO(RenderCommand *command, HVao *previousVAOHandle, diff --git a/src/plugins/renderers/opengl/renderer/renderercache_p.h b/src/plugins/renderers/opengl/renderer/renderercache_p.h index 5e02df623..60f08bcbd 100644 --- a/src/plugins/renderers/opengl/renderer/renderercache_p.h +++ b/src/plugins/renderers/opengl/renderer/renderercache_p.h @@ -70,6 +70,7 @@ struct RendererCache { struct LeafNodeData { + Matrix4x4 viewProjectionMatrix; // Set by the FilterLayerJob // Contains all Entities that satisfy the layer filtering for the RV QVector<Entity *> filterEntitiesByLayer; @@ -78,11 +79,17 @@ struct RendererCache MaterialParameterGathererData materialParameterGatherer; // Set by the SyncRenderViewPreCommandUpdateJob - // Contains all Entities that are renderables (either compute or draw - // depending on the RV) and that satisfy the layer filtering. - QVector<Entity *> layeredFilteredRenderables; + // Contains caches of different filtering stages that can + // be cached across frame + QVector<Entity *> layeredFilteredRenderables; // Changes rarely + QVector<Entity *> filteredAndCulledRenderables; // Changes if camera is modified QVector<LightSource> layeredFilteredLightSources; - EntityRenderCommandData renderCommandData; + + // We keep two sets of filtered render commands + // and we flip in between them every frame + int viewIdx = 0; + bool requestFilteringAtNextFrame = false; + EntityRenderCommandDataViewPtr filteredRenderCommandDataViews[2]; }; // Variabled below are shared amongst all RV diff --git a/src/plugins/renderers/opengl/renderer/renderview.cpp b/src/plugins/renderers/opengl/renderer/renderview.cpp index 6707c6c2f..9e4bd9f46 100644 --- a/src/plugins/renderers/opengl/renderer/renderview.cpp +++ b/src/plugins/renderers/opengl/renderer/renderview.cpp @@ -156,34 +156,34 @@ UniformValue RenderView::standardUniformValue(RenderView::StandardUniform standa case ModelMatrix: return UniformValue(model); case ViewMatrix: - return UniformValue(m_data.m_viewMatrix); + return UniformValue(m_viewMatrix); case ProjectionMatrix: - return UniformValue(getProjectionMatrix(m_data.m_renderCameraLens)); + return UniformValue(getProjectionMatrix(m_renderCameraLens)); case ModelViewMatrix: - return UniformValue(m_data.m_viewMatrix * model); + return UniformValue(m_viewMatrix * model); case ViewProjectionMatrix: - return UniformValue(getProjectionMatrix(m_data.m_renderCameraLens) * m_data.m_viewMatrix); + return UniformValue(getProjectionMatrix(m_renderCameraLens) * m_viewMatrix); case ModelViewProjectionMatrix: - return UniformValue(m_data.m_viewProjectionMatrix * model); + return UniformValue(m_viewProjectionMatrix * model); case InverseModelMatrix: return UniformValue(model.inverted()); case InverseViewMatrix: - return UniformValue(m_data.m_viewMatrix.inverted()); + return UniformValue(m_viewMatrix.inverted()); case InverseProjectionMatrix: { - return UniformValue(getProjectionMatrix(m_data.m_renderCameraLens).inverted()); + return UniformValue(getProjectionMatrix(m_renderCameraLens).inverted()); } case InverseModelViewMatrix: - return UniformValue((m_data.m_viewMatrix * model).inverted()); + return UniformValue((m_viewMatrix * model).inverted()); case InverseViewProjectionMatrix: { - const Matrix4x4 viewProjectionMatrix = getProjectionMatrix(m_data.m_renderCameraLens) * m_data.m_viewMatrix; + const Matrix4x4 viewProjectionMatrix = getProjectionMatrix(m_renderCameraLens) * m_viewMatrix; return UniformValue(viewProjectionMatrix.inverted()); } case InverseModelViewProjectionMatrix: - return UniformValue((m_data.m_viewProjectionMatrix * model).inverted()); + return UniformValue((m_viewProjectionMatrix * model).inverted()); case ModelNormalMatrix: return UniformValue(convertToQMatrix4x4(model).normalMatrix()); case ModelViewNormalMatrix: - return UniformValue(convertToQMatrix4x4(m_data.m_viewMatrix * model).normalMatrix()); + return UniformValue(convertToQMatrix4x4(m_viewMatrix * model).normalMatrix()); case ViewportMatrix: { QMatrix4x4 viewportMatrix; // TO DO: Implement on Matrix4x4 @@ -199,13 +199,13 @@ UniformValue RenderView::standardUniformValue(RenderView::StandardUniform standa case AspectRatio: return float(m_surfaceSize.width()) / std::max(1.f, float(m_surfaceSize.height())); case Exposure: - return UniformValue(m_data.m_renderCameraLens ? m_data.m_renderCameraLens->exposure() : 0.0f); + return UniformValue(m_renderCameraLens ? m_renderCameraLens->exposure() : 0.0f); case Gamma: return UniformValue(m_gamma); case Time: return UniformValue(float(m_renderer->time() / 1000000000.0f)); case EyePosition: - return UniformValue(m_data.m_eyePos); + return UniformValue(m_eyePos); case SkinningPalette: { const Armature *armature = entity->renderComponent<Armature>(); if (!armature) { @@ -221,29 +221,7 @@ UniformValue RenderView::standardUniformValue(RenderView::StandardUniform standa } RenderView::RenderView() - : m_isDownloadBuffersEnable(false) - , m_hasBlitFramebufferInfo(false) - , m_renderer(nullptr) - , m_manager(nullptr) - , m_devicePixelRatio(1.) - , m_viewport(QRectF(0., 0., 1., 1.)) - , m_gamma(2.2f) - , m_surface(nullptr) - , m_clearBuffer(QClearBuffers::None) - , m_clearDepthValue(1.f) - , m_clearStencilValue(0) - , m_stateSet(nullptr) - , m_noDraw(false) - , m_compute(false) - , m_frustumCulling(false) - , m_showDebugOverlay(false) - , m_memoryBarrier(QMemoryBarrier::None) - , m_environmentLight(nullptr) { - m_workGroups[0] = 1; - m_workGroups[1] = 1; - m_workGroups[2] = 1; - if (Q_UNLIKELY(!wasInitialized.exchange(true))) { // Needed as we can control the init order of static/global variables across compile units // and this hash relies on the static StringToInt class @@ -254,7 +232,6 @@ RenderView::RenderView() RenderView::~RenderView() { - delete m_stateSet; } namespace { @@ -327,26 +304,31 @@ struct AdjacentSubRangeFinder<QSortPolicy::Texture> }; template<typename Predicate> -int advanceUntilNonAdjacent(const QVector<RenderCommand> &commands, - const int beg, const int end, Predicate pred) +int advanceUntilNonAdjacent(const EntityRenderCommandDataView *view, + const size_t beg, const size_t end, Predicate pred) { - int i = beg + 1; - while (i < end) { - if (!pred(*(commands.begin() + beg), *(commands.begin() + i))) - break; - ++i; + const std::vector<size_t> &commandIndices = view->indices; + const std::vector<RenderCommand> &commands = view->data.commands; + size_t i = beg + 1; + if (i < end) { + const size_t startIdx = commandIndices[beg]; + while (i < end) { + const size_t targetIdx = commandIndices[i]; + if (!pred(commands[startIdx], commands[targetIdx])) + break; + ++i; + } } return i; } -using CommandIt = QVector<RenderCommand>::iterator; - template<int SortType> struct SubRangeSorter { - static void sortSubRange(CommandIt begin, const CommandIt end) + static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) { + Q_UNUSED(view) Q_UNUSED(begin) Q_UNUSED(end) Q_UNREACHABLE(); @@ -356,9 +338,14 @@ struct SubRangeSorter template<> struct SubRangeSorter<QSortPolicy::StateChangeCost> { - static void sortSubRange(CommandIt begin, const CommandIt end) + static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) { - std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) { + std::vector<size_t> &commandIndices = view->indices; + const std::vector<RenderCommand> &commands = view->data.commands; + std::stable_sort(commandIndices.begin() + begin, commandIndices.begin() + end, + [&commands] (const size_t &iA, const size_t &iB) { + const RenderCommand &a = commands[iA]; + const RenderCommand &b = commands[iB]; return a.m_changeCost > b.m_changeCost; }); } @@ -367,9 +354,14 @@ struct SubRangeSorter<QSortPolicy::StateChangeCost> template<> struct SubRangeSorter<QSortPolicy::BackToFront> { - static void sortSubRange(CommandIt begin, const CommandIt end) + static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) { - std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) { + std::vector<size_t> &commandIndices = view->indices; + const std::vector<RenderCommand> &commands = view->data.commands; + std::stable_sort(commandIndices.begin() + begin, commandIndices.begin() + end, + [&commands] (const size_t &iA, const size_t &iB) { + const RenderCommand &a = commands[iA]; + const RenderCommand &b = commands[iB]; return a.m_depth > b.m_depth; }); } @@ -378,10 +370,14 @@ struct SubRangeSorter<QSortPolicy::BackToFront> template<> struct SubRangeSorter<QSortPolicy::Material> { - static void sortSubRange(CommandIt begin, const CommandIt end) + static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) { - // First we sort by shader - std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) { + std::vector<size_t> &commandIndices = view->indices; + const std::vector<RenderCommand> &commands = view->data.commands; + std::stable_sort(commandIndices.begin() + begin, commandIndices.begin() + end, + [&commands] (const size_t &iA, const size_t &iB) { + const RenderCommand &a = commands[iA]; + const RenderCommand &b = commands[iB]; return a.m_glShader > b.m_glShader; }); } @@ -390,9 +386,14 @@ struct SubRangeSorter<QSortPolicy::Material> template<> struct SubRangeSorter<QSortPolicy::FrontToBack> { - static void sortSubRange(CommandIt begin, const CommandIt end) + static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) { - std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) { + std::vector<size_t> &commandIndices = view->indices; + const std::vector<RenderCommand> &commands = view->data.commands; + std::stable_sort(commandIndices.begin() + begin, commandIndices.begin() + end, + [&commands] (const size_t &iA, const size_t &iB) { + const RenderCommand &a = commands[iA]; + const RenderCommand &b = commands[iB]; return a.m_depth < b.m_depth; }); } @@ -401,10 +402,15 @@ struct SubRangeSorter<QSortPolicy::FrontToBack> template<> struct SubRangeSorter<QSortPolicy::Texture> { - static void sortSubRange(CommandIt begin, const CommandIt end) + static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) { #ifndef Q_OS_WIN - std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) { + std::vector<size_t> &commandIndices = view->indices; + const std::vector<RenderCommand> &commands = view->data.commands; + std::stable_sort(commandIndices.begin() + begin, commandIndices.begin() + end, + [&commands] (const int &iA, const int &iB) { + const RenderCommand &a = commands[iA]; + const RenderCommand &b = commands[iB]; QVector<ShaderParameterPack::NamedResource> texturesA = a.m_parameterPack.textures(); QVector<ShaderParameterPack::NamedResource> texturesB = b.m_parameterPack.textures(); @@ -426,21 +432,21 @@ struct SubRangeSorter<QSortPolicy::Texture> } }; -int findSubRange(const QVector<RenderCommand> &commands, +int findSubRange(const EntityRenderCommandDataView *view, const int begin, const int end, const QSortPolicy::SortType sortType) { switch (sortType) { case QSortPolicy::StateChangeCost: - return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::StateChangeCost>::adjacentSubRange); + return advanceUntilNonAdjacent(view, begin, end, AdjacentSubRangeFinder<QSortPolicy::StateChangeCost>::adjacentSubRange); case QSortPolicy::BackToFront: - return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::BackToFront>::adjacentSubRange); + return advanceUntilNonAdjacent(view, begin, end, AdjacentSubRangeFinder<QSortPolicy::BackToFront>::adjacentSubRange); case QSortPolicy::Material: - return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange); + return advanceUntilNonAdjacent(view, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange); case QSortPolicy::FrontToBack: - return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::FrontToBack>::adjacentSubRange); + return advanceUntilNonAdjacent(view, begin, end, AdjacentSubRangeFinder<QSortPolicy::FrontToBack>::adjacentSubRange); case QSortPolicy::Texture: - return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::Texture>::adjacentSubRange); + return advanceUntilNonAdjacent(view, begin, end, AdjacentSubRangeFinder<QSortPolicy::Texture>::adjacentSubRange); case QSortPolicy::Uniform: return end; default: @@ -449,22 +455,27 @@ int findSubRange(const QVector<RenderCommand> &commands, } } -void sortByMaterial(QVector<RenderCommand> &commands, int begin, const int end) +void sortByMaterial(EntityRenderCommandDataView *view, 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); + std::vector<size_t> &commandIndices = view->indices; + const std::vector<RenderCommand> &commands = view->data.commands; + int rangeEnd = advanceUntilNonAdjacent(view, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange); while (begin != end) { if (begin + 1 < rangeEnd) { - std::stable_sort(commands.begin() + begin + 1, commands.begin() + rangeEnd, [] (const RenderCommand &a, const RenderCommand &b){ + std::stable_sort(commandIndices.begin() + begin + 1, commandIndices.begin() + rangeEnd, + [&commands] (const int &iA, const int &iB) { + const RenderCommand &a = commands[iA]; + const RenderCommand &b = commands[iB]; return a.m_material.handle() < b.m_material.handle(); }); } begin = rangeEnd; - rangeEnd = advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange); + rangeEnd = advanceUntilNonAdjacent(view, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange); } } -void sortCommandRange(QVector<RenderCommand> &commands, int begin, const int end, const int level, +void sortCommandRange(EntityRenderCommandDataView *view, int begin, int end, const int level, const QVector<Qt3DRender::QSortPolicy::SortType> &sortingTypes) { if (level >= sortingTypes.size()) @@ -472,22 +483,22 @@ void sortCommandRange(QVector<RenderCommand> &commands, int begin, const int end switch (sortingTypes.at(level)) { case QSortPolicy::StateChangeCost: - SubRangeSorter<QSortPolicy::StateChangeCost>::sortSubRange(commands.begin() + begin, commands.begin() + end); + SubRangeSorter<QSortPolicy::StateChangeCost>::sortSubRange(view, begin, end); break; case QSortPolicy::BackToFront: - SubRangeSorter<QSortPolicy::BackToFront>::sortSubRange(commands.begin() + begin, commands.begin() + end); + SubRangeSorter<QSortPolicy::BackToFront>::sortSubRange(view, begin, end); break; case QSortPolicy::Material: // Groups all same shader DNA together - SubRangeSorter<QSortPolicy::Material>::sortSubRange(commands.begin() + begin, commands.begin() + end); + SubRangeSorter<QSortPolicy::Material>::sortSubRange(view, begin, end); // Group all same material together (same parameters most likely) - sortByMaterial(commands, begin, end); + sortByMaterial(view, begin, end); break; case QSortPolicy::FrontToBack: - SubRangeSorter<QSortPolicy::FrontToBack>::sortSubRange(commands.begin() + begin, commands.begin() + end); + SubRangeSorter<QSortPolicy::FrontToBack>::sortSubRange(view, begin, end); break; case QSortPolicy::Texture: - SubRangeSorter<QSortPolicy::Texture>::sortSubRange(commands.begin() + begin, commands.begin() + end); + SubRangeSorter<QSortPolicy::Texture>::sortSubRange(view, begin, end); break; case QSortPolicy::Uniform: break; @@ -497,11 +508,11 @@ void sortCommandRange(QVector<RenderCommand> &commands, int begin, const int end // For all sub ranges of adjacent item for sortType[i] // Perform filtering with sortType[i + 1] - int rangeEnd = findSubRange(commands, begin, end, sortingTypes.at(level)); + int rangeEnd = findSubRange(view, begin, end, sortingTypes.at(level)); while (begin != end) { - sortCommandRange(commands, begin, rangeEnd, level + 1, sortingTypes); + sortCommandRange(view, begin, rangeEnd, level + 1, sortingTypes); begin = rangeEnd; - rangeEnd = findSubRange(commands, begin, end, sortingTypes.at(level)); + rangeEnd = findSubRange(view, begin, end, sortingTypes.at(level)); } } @@ -509,37 +520,41 @@ void sortCommandRange(QVector<RenderCommand> &commands, int begin, const int end void RenderView::sort() { + assert(m_renderCommandDataView); // Compares the bitsetKey of the RenderCommands // Key[Depth | StateCost | Shader] - sortCommandRange(m_commands, 0, m_commands.size(), 0, m_data.m_sortingTypes); + sortCommandRange(m_renderCommandDataView.data(), 0, m_renderCommandDataView->size(), 0, m_sortingTypes); // For RenderCommand with the same shader // We compute the adjacent change cost // Only perform uniform minimization if we explicitly asked for it - if (!m_data.m_sortingTypes.contains(QSortPolicy::Uniform)) + if (!m_sortingTypes.contains(QSortPolicy::Uniform)) return; // Minimize uniform changes int i = 0; - const int commandSize = m_commands.size(); + std::vector<RenderCommand> &commands = m_renderCommandDataView->data.commands; + const std::vector<size_t> &indices = m_renderCommandDataView->indices; + const size_t commandSize = indices.size(); + while (i < commandSize) { - int j = i; + size_t j = i; // Advance while commands share the same shader while (i < commandSize && - m_commands[j].m_glShader == m_commands[i].m_glShader) + commands[indices[j]].m_glShader == commands[indices[i]].m_glShader) ++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 = commands[indices[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[j].m_parameterPack.m_uniforms; + PackUniformHash &uniforms = commands[indices[j]].m_parameterPack.m_uniforms; - for (int u = 0; u < uniforms.keys.size();) { + for (size_t u = 0; u < uniforms.keys.size();) { // We are comparing the values: // - raw uniform values // - the texture Node id if the uniform represents a texture @@ -572,6 +587,13 @@ void RenderView::setRenderer(Renderer *renderer) m_manager = renderer->nodeManagers(); } +RenderStateSet *RenderView::getOrCreateStateSet() +{ + if (!m_stateSet) + m_stateSet.reset(new RenderStateSet()); + return m_stateSet.data(); +} + void RenderView::addClearBuffers(const ClearBuffers *cb) { QClearBuffers::BufferTypeFlags type = cb->type(); @@ -649,8 +671,8 @@ EntityRenderCommandData RenderView::buildDrawRenderCommands(const QVector<Entity if (pass->hasRenderStates()) { 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); + if (m_stateSet) + command.m_stateSet->merge(m_stateSet.data()); command.m_changeCost = m_renderer->defaultRenderState()->changeCost(command.m_stateSet.data()); } command.m_shaderId = pass->shaderProgram(); @@ -773,7 +795,7 @@ EntityRenderCommandData RenderView::buildComputeRenderCommands(const QVector<Ent // 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_stateSet->merge(m_stateSet.data()); command.m_changeCost = m_renderer->defaultRenderState()->changeCost(command.m_stateSet.data()); } command.m_shaderId = pass->shaderProgram(); @@ -800,9 +822,7 @@ EntityRenderCommandData RenderView::buildComputeRenderCommands(const QVector<Ent return commands; } -void RenderView::updateRenderCommand(EntityRenderCommandData *renderCommandData, - int offset, - int count) +void RenderView::updateRenderCommand(const EntityRenderCommandDataSubView &subView) { // Note: since many threads can be building render commands // we need to ensure that the UniformBlockValueBuilder they are using @@ -812,38 +832,14 @@ void RenderView::updateRenderCommand(EntityRenderCommandData *renderCommandData, builder->textureManager = m_manager->textureManager(); m_localData.setLocalData(builder); - for (int i = 0, m = count; i < m; ++i) { - const int idx = offset + i; - Entity *entity = renderCommandData->entities.at(idx); - const RenderPassParameterData passData = renderCommandData->passesData.at(idx); - RenderCommand &command = renderCommandData->commands[idx]; - - // 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; - + subView.forEach([this] (const Entity *entity, + const RenderPassParameterData &passData, + RenderCommand &command) { 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)); + command.m_depth = Vector3D::dotProduct(entity->worldBoundingVolume()->center() - m_eyePos, m_eyeViewDir); } else { // Compute // Note: if frameCount has reached 0 in the previous frame, isEnabled // would be false @@ -852,16 +848,12 @@ void RenderView::updateRenderCommand(EntityRenderCommandData *renderCommandData, 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); - } + passData.parameterInfo, + entity); + }); // We reset the local data once we are done with it m_localData.setLocalData(nullptr); @@ -869,11 +861,11 @@ void RenderView::updateRenderCommand(EntityRenderCommandData *renderCommandData, void RenderView::updateMatrices() { - if (m_data.m_renderCameraNode && m_data.m_renderCameraLens && m_data.m_renderCameraLens->isEnabled()) { - const Matrix4x4 cameraWorld = *(m_data.m_renderCameraNode->worldTransform()); - setViewMatrix(m_data.m_renderCameraLens->viewMatrix(cameraWorld)); + if (m_renderCameraNode && m_renderCameraLens && m_renderCameraLens->isEnabled()) { + const Matrix4x4 cameraWorld = *(m_renderCameraNode->worldTransform()); + setViewMatrix(m_renderCameraLens->viewMatrix(cameraWorld)); - setViewProjectionMatrix(m_data.m_renderCameraLens->projection() * viewMatrix()); + setViewProjectionMatrix(m_renderCameraLens->projection() * viewMatrix()); //To get the eyePosition of the camera, we need to use the inverse of the //camera's worldTransform matrix. const Matrix4x4 inverseWorldTransform = viewMatrix().inverted(); @@ -882,7 +874,7 @@ void RenderView::updateMatrices() // Get the viewing direction of the camera. Use the normal matrix to // ensure non-uniform scale works too. - const QMatrix3x3 normalMat = convertToQMatrix4x4(m_data.m_viewMatrix).normalMatrix(); + const QMatrix3x3 normalMat = convertToQMatrix4x4(m_viewMatrix).normalMatrix(); // dir = normalize(QVector3D(0, 0, -1) * normalMat) setEyeViewDirection(Vector3D(-normalMat(2, 0), -normalMat(2, 1), -normalMat(2, 2)).normalized()); } @@ -976,7 +968,7 @@ void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &unif builder->activeUniformNamesToValue.clear(); // Set the view matrix to be used to transform "Transformed" properties in the ShaderData - builder->viewMatrix = m_data.m_viewMatrix; + builder->viewMatrix = m_viewMatrix; // Force to update the whole block builder->updatedPropertiesOnly = false; // Retrieve names and description of each active uniforms in the uniform block @@ -1031,10 +1023,8 @@ void RenderView::applyParameter(const Parameter *param, void RenderView::setShaderAndUniforms(RenderCommand *command, - ParameterInfoList ¶meters, - Entity *entity, - const QVector<LightSource> &activeLightSources, - EnvironmentLight *environmentLight) const + const ParameterInfoList ¶meters, + const Entity *entity) const { // The VAO Handle is set directly in the renderer thread so as to avoid having to use a mutex here // Set shader, technique, and effect by basically doing : @@ -1047,8 +1037,16 @@ void RenderView::setShaderAndUniforms(RenderCommand *command, // Once that works, improve that to try and minimize QUniformPack updates GLShader *shader = command->m_glShader; - if (shader != nullptr && shader->isLoaded()) { + if (shader == nullptr || !shader->isLoaded()) + return; + // If we have already build the uniforms previously, we should + // only update values of uniforms that have changed + // If parameters add been added/removed, the command would have been rebuild + // and the parameter pack would be empty + const bool updateUniformsOnly = command->m_parameterPack.submissionUniformIndices().size() > 0; + + if (!updateUniformsOnly) { // 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 @@ -1075,112 +1073,141 @@ void RenderView::setShaderAndUniforms(RenderCommand *command, // We still need to process the uniforms as the command could be a compute command command->m_isValid = !command->m_activeAttributes.empty(); - if (shader->hasActiveVariables()) { + // Reserve amount of uniforms we are going to need + command->m_parameterPack.reserve(shader->parameterPackSize()); + } - // Reserve amount of uniforms we are going to need - command->m_parameterPack.reserve(shader->parameterPackSize()); + if (shader->hasActiveVariables()) { + const QVector<int> &standardUniformNamesIds = shader->standardUniformNameIds(); - const QVector<int> &standardUniformNamesIds = shader->standardUniformNameIds(); - for (const int uniformNameId : standardUniformNamesIds) - setStandardUniformValue(command->m_parameterPack, uniformNameId, entity); + // It only makes sense to update the standard uniforms if: + // - Camera changed + // - Entity transform changed + // - Viewport/Surface changed - ParameterInfoList::const_iterator it = parameters.cbegin(); - const ParameterInfoList::const_iterator parametersEnd = parameters.cend(); + for (const int uniformNameId : standardUniformNamesIds) + setStandardUniformValue(command->m_parameterPack, uniformNameId, entity); - while (it != parametersEnd) { - const Parameter *param = m_manager->data<Parameter, ParameterManager>(it->handle); - applyParameter(param, command, shader); - ++it; - } + ParameterInfoList::const_iterator it = parameters.cbegin(); + const ParameterInfoList::const_iterator parametersEnd = parameters.cend(); - // Lights - const QVector<int> &lightUniformNamesIds = shader->lightUniformsNamesIds(); - if (!lightUniformNamesIds.empty()) { - int lightIdx = 0; - for (const LightSource &lightSource : activeLightSources) { - if (lightIdx == MAX_LIGHTS) - break; - Entity *lightEntity = lightSource.entity; - const Matrix4x4 lightWorldTransform = *(lightEntity->worldTransform()); - const Vector3D worldPos = lightWorldTransform * Vector3D(0.0f, 0.0f, 0.0f); - 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; + while (it != parametersEnd) { + const Parameter *param = m_manager->data<Parameter, ParameterManager>(it->handle); + applyParameter(param, command, shader); + ++it; + } - // Note: implicit conversion of values to UniformValue - if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_NAMES[lightIdx])) { - setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_NAMES[lightIdx], worldPos); - setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_NAMES[lightIdx], int(QAbstractLight::PointLight)); - setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f)); - setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_NAMES[lightIdx], 0.5f); - } else if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx])) { - setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_UNROLL_NAMES[lightIdx], worldPos); - setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx], int(QAbstractLight::PointLight)); - setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_UNROLL_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f)); - setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_UNROLL_NAMES[lightIdx], 0.5f); - } + // Lights + updateLightUniforms(command, entity); + } - // 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); + // Prepare the ShaderParameterPack based on the active uniforms of the shader + if (!updateUniformsOnly) + shader->prepareUniforms(command->m_parameterPack); +} - setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, GLLights::LIGHT_STRUCT_NAMES[lightIdx]); - setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, GLLights::LIGHT_STRUCT_UNROLL_NAMES[lightIdx]); - ++lightIdx; - } - } +void RenderView::updateLightUniforms(RenderCommand *command, const Entity *entity) const +{ + GLShader *shader = command->m_glShader; + const QVector<int> &lightUniformNamesIds = shader->lightUniformsNamesIds(); + if (!lightUniformNamesIds.empty()) { + // 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; + }); + } + m_lightSources = lightSources.mid(0, std::min(lightSources.size(), MAX_LIGHTS)); + + int lightIdx = 0; + for (const LightSource &lightSource : qAsConst(m_lightSources)) { + if (lightIdx == MAX_LIGHTS) + break; + Entity *lightEntity = lightSource.entity; + const Matrix4x4 lightWorldTransform = *(lightEntity->worldTransform()); + const Vector3D worldPos = lightWorldTransform * Vector3D(0.0f, 0.0f, 0.0f); + for (Light *light : lightSource.lights) { + if (!light->isEnabled()) + continue; - setUniformValue(command->m_parameterPack, GLLights::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 - if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_NAMES[0])) { - setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f)); - setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_NAMES[0], int(QAbstractLight::PointLight)); - setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f)); - setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_NAMES[0], 0.5f); - } else if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx])) { - setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_UNROLL_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f)); - setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_UNROLL_NAMES[0], int(QAbstractLight::PointLight)); - setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_UNROLL_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f)); - setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_UNROLL_NAMES[0], 0.5f); - } + ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(light->shaderData()); + if (!shaderData) + continue; + + if (lightIdx == MAX_LIGHTS) + break; + + // Note: implicit conversion of values to UniformValue + if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_NAMES[lightIdx])) { + setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_NAMES[lightIdx], worldPos); + setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_NAMES[lightIdx], int(QAbstractLight::PointLight)); + setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f)); + setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_NAMES[lightIdx], 0.5f); + } else if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx])) { + setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_UNROLL_NAMES[lightIdx], worldPos); + setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx], int(QAbstractLight::PointLight)); + setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_UNROLL_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f)); + setUniformValue(command->m_parameterPack, GLLights::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, GLLights::LIGHT_STRUCT_NAMES[lightIdx]); + setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, GLLights::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()); + setUniformValue(command->m_parameterPack, GLLights::LIGHT_COUNT_NAME_ID, UniformValue(qMax((m_environmentLight ? 0 : 1), lightIdx))); + + // If no active light sources and no environment light, add a default light + if (m_lightSources.isEmpty() && !m_environmentLight) { + // Note: implicit conversion of values to UniformValue + if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_NAMES[0])) { + setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f)); + setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_NAMES[0], int(QAbstractLight::PointLight)); + setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f)); + setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_NAMES[0], 0.5f); + } else if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx])) { + setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_UNROLL_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f)); + setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_UNROLL_NAMES[0], int(QAbstractLight::PointLight)); + setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_UNROLL_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f)); + setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_UNROLL_NAMES[0], 0.5f); } - setUniformValue(command->m_parameterPack, StringToInt::lookupId(QStringLiteral("envLightCount")), envLightCount); } + } - // Prepare the ShaderParameterPack based on the active uniforms of the shader - shader->prepareUniforms(command->m_parameterPack); + // Environment Light + int envLightCount = 0; + if (m_environmentLight && m_environmentLight->isEnabled()) { + ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(m_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()); } + setUniformValue(command->m_parameterPack, StringToInt::lookupId(QStringLiteral("envLightCount")), envLightCount); } bool RenderView::hasBlitFramebufferInfo() const @@ -1195,7 +1222,7 @@ void RenderView::setHasBlitFramebufferInfo(bool hasBlitFramebufferInfo) bool RenderView::shouldSkipSubmission() const { - if (!m_commands.empty()) + if (commandCount() > 0) return false; if (m_hasBlitFramebufferInfo) diff --git a/src/plugins/renderers/opengl/renderer/renderview_p.h b/src/plugins/renderers/opengl/renderer/renderview_p.h index 85dafe3fd..53f76dbfe 100644 --- a/src/plugins/renderers/opengl/renderer/renderview_p.h +++ b/src/plugins/renderers/opengl/renderer/renderview_p.h @@ -141,29 +141,29 @@ public: inline void setDevicePixelRatio(qreal r) Q_DECL_NOTHROW { m_devicePixelRatio = r; } inline qreal devicePixelRatio() const Q_DECL_NOTHROW { return m_devicePixelRatio; } - inline void setRenderCameraLens(CameraLens *renderCameraLens) Q_DECL_NOTHROW { m_data.m_renderCameraLens = renderCameraLens; } - inline CameraLens *renderCameraLens() const Q_DECL_NOTHROW { return m_data.m_renderCameraLens; } + inline void setRenderCameraLens(CameraLens *renderCameraLens) Q_DECL_NOTHROW { m_renderCameraLens = renderCameraLens; } + inline CameraLens *renderCameraLens() const Q_DECL_NOTHROW { return m_renderCameraLens; } - inline void setRenderCameraEntity(Entity *renderCameraNode) Q_DECL_NOTHROW { m_data.m_renderCameraNode = renderCameraNode; } - inline Entity *renderCameraEntity() const Q_DECL_NOTHROW { return m_data.m_renderCameraNode; } + inline void setRenderCameraEntity(Entity *renderCameraNode) Q_DECL_NOTHROW { m_renderCameraNode = renderCameraNode; } + inline Entity *renderCameraEntity() const Q_DECL_NOTHROW { return m_renderCameraNode; } - inline void setViewMatrix(const Matrix4x4 &viewMatrix) Q_DECL_NOTHROW { m_data.m_viewMatrix = viewMatrix; } - inline Matrix4x4 viewMatrix() const Q_DECL_NOTHROW { return m_data.m_viewMatrix; } + inline void setViewMatrix(const Matrix4x4 &viewMatrix) Q_DECL_NOTHROW { m_viewMatrix = viewMatrix; } + inline Matrix4x4 viewMatrix() const Q_DECL_NOTHROW { return m_viewMatrix; } - inline void setViewProjectionMatrix(const Matrix4x4 &viewProjectionMatrix) Q_DECL_NOTHROW { m_data.m_viewProjectionMatrix = viewProjectionMatrix; } - inline Matrix4x4 viewProjectionMatrix() const Q_DECL_NOTHROW { return m_data.m_viewProjectionMatrix; } + inline void setViewProjectionMatrix(const Matrix4x4 &viewProjectionMatrix) Q_DECL_NOTHROW { m_viewProjectionMatrix = viewProjectionMatrix; } + inline Matrix4x4 viewProjectionMatrix() const Q_DECL_NOTHROW { return m_viewProjectionMatrix; } - inline void setEyePosition(const Vector3D &eyePos) Q_DECL_NOTHROW { m_data.m_eyePos = eyePos; } - inline Vector3D eyePosition() const Q_DECL_NOTHROW { return m_data.m_eyePos; } + inline void setEyePosition(const Vector3D &eyePos) Q_DECL_NOTHROW { m_eyePos = eyePos; } + inline Vector3D eyePosition() const Q_DECL_NOTHROW { return m_eyePos; } - inline void setEyeViewDirection(const Vector3D &dir) Q_DECL_NOTHROW { m_data.m_eyeViewDir = dir; } - inline Vector3D eyeViewDirection() const Q_DECL_NOTHROW { return m_data.m_eyeViewDir; } + inline void setEyeViewDirection(const Vector3D &dir) Q_DECL_NOTHROW { m_eyeViewDir = dir; } + inline Vector3D eyeViewDirection() const Q_DECL_NOTHROW { return m_eyeViewDir; } - inline void appendLayerFilter(const Qt3DCore::QNodeId layerFilterId) Q_DECL_NOTHROW { m_data.m_layerFilterIds.push_back(layerFilterId); } - inline Qt3DCore::QNodeIdVector layerFilters() const Q_DECL_NOTHROW { return m_data.m_layerFilterIds; } + inline void appendLayerFilter(const Qt3DCore::QNodeId layerFilterId) Q_DECL_NOTHROW { m_layerFilterIds.push_back(layerFilterId); } + inline Qt3DCore::QNodeIdVector layerFilters() const Q_DECL_NOTHROW { return m_layerFilterIds; } - inline void appendProximityFilterId(const Qt3DCore::QNodeId proximityFilterId) { m_data.m_proximityFilterIds.push_back(proximityFilterId); } - inline Qt3DCore::QNodeIdVector proximityFilterIds() const { return m_data.m_proximityFilterIds; } + inline void appendProximityFilterId(const Qt3DCore::QNodeId proximityFilterId) { m_proximityFilterIds.push_back(proximityFilterId); } + inline Qt3DCore::QNodeIdVector proximityFilterIds() const { return m_proximityFilterIds; } inline void appendInsertFenceId(const Qt3DCore::QNodeId setFenceId) { m_insertFenceIds.push_back(setFenceId); } // We prefix with get to avoid confusion when it is called @@ -172,14 +172,17 @@ public: inline void appendWaitFence(const QWaitFenceData &data) { m_waitFences.push_back(data); } inline QVector<QWaitFenceData> waitFences() const { return m_waitFences; } - inline void setRenderPassFilter(const RenderPassFilter *rpFilter) Q_DECL_NOTHROW { m_data.m_passFilter = rpFilter; } - inline const RenderPassFilter *renderPassFilter() const Q_DECL_NOTHROW { return m_data.m_passFilter; } + inline void setRenderPassFilter(const RenderPassFilter *rpFilter) Q_DECL_NOTHROW { m_passFilter = rpFilter; } + inline const RenderPassFilter *renderPassFilter() const Q_DECL_NOTHROW { return m_passFilter; } - inline void setTechniqueFilter(const TechniqueFilter *filter) Q_DECL_NOTHROW { m_data.m_techniqueFilter = filter; } - inline const TechniqueFilter *techniqueFilter() const Q_DECL_NOTHROW { return m_data.m_techniqueFilter; } + inline void setTechniqueFilter(const TechniqueFilter *filter) Q_DECL_NOTHROW { m_techniqueFilter = filter; } + inline const TechniqueFilter *techniqueFilter() const Q_DECL_NOTHROW { return m_techniqueFilter; } - inline RenderStateSet *stateSet() const Q_DECL_NOTHROW { return m_stateSet; } - void setStateSet(RenderStateSet *stateSet) Q_DECL_NOTHROW { m_stateSet = stateSet; } + inline void setRenderCommandDataView(const EntityRenderCommandDataViewPtr &renderCommandDataView) Q_DECL_NOTHROW { m_renderCommandDataView = renderCommandDataView; } + inline EntityRenderCommandDataViewPtr renderCommandDataView() const Q_DECL_NOTHROW { return m_renderCommandDataView; } + + RenderStateSet *getOrCreateStateSet(); + RenderStateSet *stateSet() const Q_DECL_NOTHROW { return m_stateSet.data(); } inline bool noDraw() const Q_DECL_NOTHROW { return m_noDraw; } void setNoDraw(bool noDraw) Q_DECL_NOTHROW { m_noDraw = noDraw; } @@ -224,14 +227,7 @@ public: EntityRenderCommandData buildComputeRenderCommands(const QVector<Entity *> &entities, int offset, int count) const; - - void updateRenderCommand(EntityRenderCommandData *renderCommandData, - int offset, int count); - - - 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 updateRenderCommand(const EntityRenderCommandDataSubView &subView); void setAttachmentPack(const AttachmentPack &pack) { m_attachmentPack = pack; } const AttachmentPack &attachmentPack() const { return m_attachmentPack; } @@ -239,7 +235,7 @@ public: void setRenderTargetId(Qt3DCore::QNodeId renderTargetId) Q_DECL_NOTHROW { m_renderTarget = renderTargetId; } Qt3DCore::QNodeId renderTargetId() const Q_DECL_NOTHROW { return m_renderTarget; } - void addSortType(const QVector<Qt3DRender::QSortPolicy::SortType> &sortTypes) { m_data.m_sortingTypes.append(sortTypes); } + void addSortType(const QVector<Qt3DRender::QSortPolicy::SortType> &sortTypes) { m_sortingTypes.append(sortTypes); } void setSurface(QSurface *surface) { m_surface = surface; } QSurface *surface() const { return m_surface; } @@ -257,30 +253,6 @@ public: void setMemoryBarrier(QMemoryBarrier::Operations barrier) Q_DECL_NOTHROW { m_memoryBarrier = barrier; } QMemoryBarrier::Operations memoryBarrier() const Q_DECL_NOTHROW { return m_memoryBarrier; } - // Helps making the size of RenderView smaller - // Contains all the data needed for the actual building of the RenderView - // But that aren't used later by the Renderer - struct InnerData { - InnerData() - : m_renderCameraLens(nullptr) - , m_renderCameraNode(nullptr) - , m_techniqueFilter(nullptr) - , m_passFilter(nullptr) - { - } - CameraLens *m_renderCameraLens; - Entity *m_renderCameraNode; - const TechniqueFilter *m_techniqueFilter; - const RenderPassFilter *m_passFilter; - Matrix4x4 m_viewMatrix; - Matrix4x4 m_viewProjectionMatrix; - Qt3DCore::QNodeIdVector m_layerFilterIds; - QVector<Qt3DRender::QSortPolicy::SortType> m_sortingTypes; - Vector3D m_eyePos; - Vector3D m_eyeViewDir; - Qt3DCore::QNodeIdVector m_proximityFilterIds; - }; - bool isDownloadBuffersEnable() const; void setIsDownloadBuffersEnable(bool isDownloadBuffersEnable); @@ -292,53 +264,83 @@ public: bool shouldSkipSubmission() const; + template<typename F> + inline void forEachCommand(F func) const + { + if (!m_renderCommandDataView) + return; + m_renderCommandDataView->forEachCommand(func); + } + + template<typename F> + inline void forEachCommand(F func) + { + if (!m_renderCommandDataView) + return; + m_renderCommandDataView->forEachCommand(func); + } + + inline int commandCount() const { return m_renderCommandDataView ? m_renderCommandDataView->size() : 0; } + private: void setShaderAndUniforms(RenderCommand *command, - ParameterInfoList ¶meters, - Entity *entity, - const QVector<LightSource> &activeLightSources, - EnvironmentLight *environmentLight) const; + const ParameterInfoList ¶meters, + const Entity *entity) const; + + void updateLightUniforms(RenderCommand *command, + const Entity *entity) const; + mutable QThreadStorage<UniformBlockValueBuilder*> m_localData; + Renderer *m_renderer = nullptr; + NodeManagers *m_manager = nullptr; + EntityRenderCommandDataViewPtr m_renderCommandDataView; + + QSize m_surfaceSize; + float m_devicePixelRatio = 1.0f; + QRectF m_viewport = QRectF(0.0f, 0.0f, 1.0f, 1.0f); + float m_gamma = 2.2f; + Qt3DCore::QNodeId m_renderCaptureNodeId; QRenderCaptureRequest m_renderCaptureRequest; - bool m_isDownloadBuffersEnable; + bool m_isDownloadBuffersEnable = false; - bool m_hasBlitFramebufferInfo; + bool m_hasBlitFramebufferInfo = false; BlitFramebufferInfo m_blitFrameBufferInfo; - Renderer *m_renderer; - NodeManagers *m_manager; - QSize m_surfaceSize; - qreal m_devicePixelRatio; - - InnerData m_data; - - QRectF m_viewport; - float m_gamma; + QSurface *m_surface = nullptr; Qt3DCore::QNodeId m_renderTarget; - QSurface *m_surface; AttachmentPack m_attachmentPack; - QClearBuffers::BufferTypeFlags m_clearBuffer; - float m_clearDepthValue; - int m_clearStencilValue; + QClearBuffers::BufferTypeFlags m_clearBuffer = QClearBuffers::None; + float m_clearDepthValue = 1.0f; + int m_clearStencilValue = 0; ClearBufferInfo m_globalClearColorBuffer; // global ClearColor QVector<ClearBufferInfo> m_specificClearColorBuffers; // different draw buffers with distinct colors - RenderStateSet *m_stateSet; - bool m_noDraw:1; - bool m_compute:1; - bool m_frustumCulling:1; - bool m_showDebugOverlay:1; - int m_workGroups[3]; - QMemoryBarrier::Operations m_memoryBarrier; + + QScopedPointer<RenderStateSet> m_stateSet; + CameraLens *m_renderCameraLens = nullptr; + Entity *m_renderCameraNode = nullptr; + const TechniqueFilter *m_techniqueFilter = nullptr; + const RenderPassFilter *m_passFilter = nullptr; + bool m_noDraw = false; + bool m_compute = false; + bool m_frustumCulling = false; + bool m_showDebugOverlay = false; + int m_workGroups[3] = { 1, 1, 1}; + QMemoryBarrier::Operations m_memoryBarrier = QMemoryBarrier::None; QVector<Qt3DCore::QNodeId> m_insertFenceIds; QVector<QWaitFenceData> m_waitFences; - - QVector<RenderCommand> m_commands; - mutable QVector<LightSource> m_lightSources; - EnvironmentLight *m_environmentLight; + QVector<Qt3DRender::QSortPolicy::SortType> m_sortingTypes; + Qt3DCore::QNodeIdVector m_proximityFilterIds; + Qt3DCore::QNodeIdVector m_layerFilterIds; + Matrix4x4 m_viewMatrix; + Matrix4x4 m_viewProjectionMatrix; + Vector3D m_eyePos; + Vector3D m_eyeViewDir; MaterialParameterGathererData m_parameters; + mutable QVector<LightSource> m_lightSources; + EnvironmentLight *m_environmentLight = nullptr; enum StandardUniform { diff --git a/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp b/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp index 6ba1ac361..e971c3f3c 100644 --- a/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp +++ b/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp @@ -114,10 +114,12 @@ class SyncRenderViewPostCommandUpdate public: explicit SyncRenderViewPostCommandUpdate(const RenderViewInitializerJobPtr &renderViewJob, const QVector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdateJobs, - Renderer *renderer) + Renderer *renderer, + FrameGraphNode *leafNode) : m_renderViewJob(renderViewJob) , m_renderViewCommandUpdaterJobs(renderViewCommandUpdateJobs) , m_renderer(renderer) + , m_leafNode(leafNode) {} void operator()() @@ -125,15 +127,30 @@ public: // Append all the commands and sort them RenderView *rv = m_renderViewJob->renderView(); - const EntityRenderCommandDataPtr commandData = m_renderViewCommandUpdaterJobs.first()->renderables(); - - if (commandData) { - const QVector<RenderCommand> commands = std::move(commandData->commands); - rv->setCommands(commands); + if (!rv->noDraw()) { + RendererCache *cache = m_renderer->cache(); + RendererCache::LeafNodeData &writableCacheForLeaf = cache->leafNodeCache[m_leafNode]; - // TO DO: Find way to store commands once or at least only when required - // Sort the commands + // Sort command on RenderView rv->sort(); + + // Flip between the 2 EntityRenderCommandDataView on the leaf node case + { + const int currentViewIdx = writableCacheForLeaf.viewIdx; + const int nextViewIdx = 1 - currentViewIdx; + EntityRenderCommandDataViewPtr currentDataView = writableCacheForLeaf.filteredRenderCommandDataViews[currentViewIdx]; + + // In case the next view has yet to be initialized, we make a copy of the current + // view + if (writableCacheForLeaf.filteredRenderCommandDataViews[nextViewIdx].isNull()) { + EntityRenderCommandDataViewPtr nextDataView = EntityRenderCommandDataViewPtr::create(); + nextDataView->data = currentDataView->data; + nextDataView->indices = currentDataView->indices; + writableCacheForLeaf.filteredRenderCommandDataViews[nextViewIdx] = nextDataView; + } + // Flip index for next frame + writableCacheForLeaf.viewIdx = nextViewIdx; + } } // TO DO: Record the commandData information with the idea of being to @@ -148,6 +165,7 @@ private: RenderViewInitializerJobPtr m_renderViewJob; QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs; Renderer *m_renderer; + FrameGraphNode *m_leafNode; }; class SyncPreFrustumCulling @@ -275,11 +293,27 @@ public: // no conflict const bool isDraw = !rv->isCompute(); - const RendererCache::LeafNodeData &dataCacheForLeaf = cache->leafNodeCache[m_leafNode]; + RendererCache::LeafNodeData &cacheForLeaf = cache->leafNodeCache[m_leafNode]; const bool fullRebuild = m_rebuildFlags.testFlag(RebuildFlag::FullCommandRebuild); const bool layerFilteringRebuild = m_rebuildFlags.testFlag(RebuildFlag::LayerCacheRebuild); const bool lightsCacheRebuild = m_rebuildFlags.testFlag(RebuildFlag::LightCacheRebuild); + const bool cameraDirty = cacheForLeaf.viewProjectionMatrix != rv->viewProjectionMatrix(); + const bool hasProximityFilter = !rv->proximityFilterIds().empty(); + const bool commandFilteringRequired = + fullRebuild || + layerFilteringRebuild || + lightsCacheRebuild || + cameraDirty || + hasProximityFilter || + cacheForLeaf.requestFilteringAtNextFrame; + + // Reset flag on leaf + cacheForLeaf.requestFilteringAtNextFrame = false; + + // If we have no filteredRenderCommandDataViews then we should have fullRebuild set to true + // otherwise something is wrong + Q_ASSERT(fullRebuild || cacheForLeaf.filteredRenderCommandDataViews[cacheForLeaf.viewIdx]); // Rebuild RenderCommands if required // This should happen fairly infrequently (FrameGraph Change, Geometry/Material change) @@ -297,92 +331,130 @@ public: } // Store new cache - RendererCache::LeafNodeData &writableCacheForLeaf = cache->leafNodeCache[m_leafNode]; - writableCacheForLeaf.renderCommandData = std::move(commandData); + EntityRenderCommandDataViewPtr dataView = EntityRenderCommandDataViewPtr::create(); + dataView->data = std::move(commandData); + // Store the update dataView + cacheForLeaf.filteredRenderCommandDataViews[cacheForLeaf.viewIdx] = dataView; + // Clear the other dataView + cacheForLeaf.filteredRenderCommandDataViews[1 - cacheForLeaf.viewIdx].clear(); } - const EntityRenderCommandData commandData = dataCacheForLeaf.renderCommandData; // Should be fairly infrequent if (layerFilteringRebuild || fullRebuild) { - RendererCache::LeafNodeData &writableCacheForLeaf = cache->leafNodeCache[m_leafNode]; - // Filter out renderable entities that weren't selected by the layer filters and store that in cache - writableCacheForLeaf.layeredFilteredRenderables = RenderViewBuilder::entitiesInSubset( + cacheForLeaf.layeredFilteredRenderables = RenderViewBuilder::entitiesInSubset( isDraw ? cache->renderableEntities : cache->computeEntities, - dataCacheForLeaf.filterEntitiesByLayer); + cacheForLeaf.filterEntitiesByLayer); + // Set default value for filteredAndCulledRenderables + if (isDraw) + cacheForLeaf.filteredAndCulledRenderables = cacheForLeaf.layeredFilteredRenderables; } + // Should be fairly infrequent if (lightsCacheRebuild) { // Filter out renderable entities that weren't selected by the // layer filters and store that in cache - const QVector<Entity *> &layeredFilteredEntities = dataCacheForLeaf.filterEntitiesByLayer; + const QVector<Entity *> &layeredFilteredEntities = cacheForLeaf.filterEntitiesByLayer; QVector<LightSource> filteredLightSources = cache->gatheredLights; for (int i = 0; i < filteredLightSources.count(); ++i) { if (!layeredFilteredEntities.contains(filteredLightSources[i].entity)) filteredLightSources.removeAt(i--); } - RendererCache::LeafNodeData &writableCacheForLeaf = cache->leafNodeCache[m_leafNode]; - writableCacheForLeaf.layeredFilteredLightSources = filteredLightSources; + cacheForLeaf.layeredFilteredLightSources = filteredLightSources; } - QVector<Entity *> renderableEntities = dataCacheForLeaf.layeredFilteredRenderables; - rv->setMaterialParameterTable(dataCacheForLeaf.materialParameterGatherer); + // This is likely very frequent + if (cameraDirty) { + // Record the updated viewProjectionMatrix in the cache to allow check to be performed + // next frame + cacheForLeaf.viewProjectionMatrix = rv->viewProjectionMatrix(); + + // Filter out frustum culled entity for drawable entities and store in cache + if (isDraw && rv->frustumCulling()) { + cacheForLeaf.filteredAndCulledRenderables = RenderViewBuilder::entitiesInSubset( + cacheForLeaf.layeredFilteredRenderables, + m_frustumCullingJob->visibleEntities()); + } + } + + rv->setMaterialParameterTable(cacheForLeaf.materialParameterGatherer); rv->setEnvironmentLight(cache->environmentLight); // Set the light sources, with layer filters applied. - rv->setLightSources(dataCacheForLeaf.layeredFilteredLightSources); + rv->setLightSources(cacheForLeaf.layeredFilteredLightSources); + + QVector<Entity *> renderableEntities = isDraw ? cacheForLeaf.filteredAndCulledRenderables : cacheForLeaf.layeredFilteredRenderables; + // TO DO: Find a way to do that only if proximity entities has changed if (isDraw) { - // Filter out frustum culled entity for drawable entities - if (rv->frustumCulling()) - renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities, m_frustumCullingJob->visibleEntities()); // Filter out entities which didn't satisfy proximity filtering - if (!rv->proximityFilterIds().empty()) - renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities, m_filterProximityJob->filteredEntities()); + if (hasProximityFilter) + renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities, + m_filterProximityJob->filteredEntities()); } + EntityRenderCommandDataViewPtr filteredCommandData = cacheForLeaf.filteredRenderCommandDataViews[cacheForLeaf.viewIdx]; + + // Set RenderCommandDataView on RV (will be used later on to sort commands ...) + rv->setRenderCommandDataView(filteredCommandData); + // Early return in case we have nothing to filter if (renderableEntities.size() == 0) return; // Filter out Render commands for which the Entity wasn't selected because // of frustum, proximity or layer filtering - EntityRenderCommandDataPtr filteredCommandData = EntityRenderCommandDataPtr::create(); - filteredCommandData->reserve(renderableEntities.size()); - // Because dataCacheForLeaf.renderableEntities or computeEntities are sorted - // What we get out of EntityRenderCommandData is also sorted by Entity - auto eIt = renderableEntities.cbegin(); - const auto eEnd = renderableEntities.cend(); - int cIt = 0; - const int cEnd = commandData.size(); - - 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 != cEnd && commandData.entities.at(cIt) < targetEntity) - ++cIt; - - // Push pointers to command data for all commands that match the - // entity - while (cIt != cEnd && commandData.entities.at(cIt) == targetEntity) { - filteredCommandData->push_back(commandData.entities.at(cIt), - commandData.commands.at(cIt), - commandData.passesData.at(cIt)); - ++cIt; + if (commandFilteringRequired) { + const std::vector<Entity *> &entities = filteredCommandData->data.entities; + // Because cacheForLeaf.renderableEntities or computeEntities are sorted + // What we get out of EntityRenderCommandData is also sorted by Entity + auto eIt = renderableEntities.cbegin(); + const auto eEnd = renderableEntities.cend(); + size_t cIt = 0; + const size_t cEnd = entities.size(); + + std::vector<size_t> filteredCommandIndices; + filteredCommandIndices.reserve(renderableEntities.size()); + + 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 != cEnd && entities[cIt] < targetEntity) + ++cIt; + + // Push pointers to command data for all commands that match the + // entity + while (cIt != cEnd && entities[cIt] == targetEntity) { + filteredCommandIndices.push_back(cIt); + ++cIt; + } + ++eIt; } - ++eIt; + + // Store result in cache + cacheForLeaf.filteredRenderCommandDataViews[cacheForLeaf.viewIdx]->indices = std::move(filteredCommandIndices); + + // Request filtering at next frame (indices for view0 and view1 + // could mistmatch if something is dirty for frame 0 and not at + // frame 1 (given we have 2 views we alternate with) + cacheForLeaf.requestFilteringAtNextFrame = true; } // Split among the number of command updaters const int jobCount = m_renderViewCommandUpdaterJobs.size(); - const int idealPacketSize = std::min(std::max(10, filteredCommandData->size() / jobCount), filteredCommandData->size()); - const int m = findIdealNumberOfWorkers(filteredCommandData->size(), idealPacketSize, jobCount); + const int commandCount = filteredCommandData->size(); + const int idealPacketSize = std::min(std::max(10, commandCount), commandCount); + const int m = findIdealNumberOfWorkers(commandCount, idealPacketSize, jobCount); for (int i = 0; i < m; ++i) { + // TO DO: Based on whether we had to update the commands + // we should be able to know what needs to be recomputed + // -> lights/standard uniforms ... might no have to be set over and over again + // if they are identical const RenderViewCommandUpdaterJobPtr &renderViewCommandUpdater = m_renderViewCommandUpdaterJobs.at(i); - const int count = (i == m - 1) ? filteredCommandData->size() - (i * idealPacketSize) : idealPacketSize; - renderViewCommandUpdater->setRenderables(filteredCommandData, i * idealPacketSize, count); + const size_t count = (i == m - 1) ? commandCount - (i * idealPacketSize) : idealPacketSize; + renderViewCommandUpdater->setRenderablesSubView({filteredCommandData, size_t(i * idealPacketSize), count}); } } } @@ -655,7 +727,8 @@ void RenderViewBuilder::prepareJobs() m_syncRenderViewPostCommandUpdateJob = CreateSynchronizerJobPtr(SyncRenderViewPostCommandUpdate(m_renderViewJob, m_renderViewCommandUpdaterJobs, - m_renderer), + m_renderer, + m_leafNode), JobTypes::SyncRenderViewPostCommandUpdate); m_syncRenderViewPostInitializationJob = CreateSynchronizerJobPtr(SyncRenderViewPostInitialization(m_renderViewJob, |