From 2942817beb1ac543abd7bd66428ed1f42bc07723 Mon Sep 17 00:00:00 2001 From: David Steele Date: Wed, 21 Apr 2021 16:59:38 +0100 Subject: [PATCH] Moving owner of secondary command buffers Current owner is renderer, but this is problematic because a single renderer can be rendered more than once per frame, e.g. because it's in more than one RenderTask, or referenced by more than one node, or it has both opaque and transparent external draw commands. Changed owner to RenderList. Changed management of Pipeline to have one handler per renderer per node per instruction per blend to ensure that owner is unique per render (as ptr to Pipeline is shallow copied in BindPipeline). Change-Id: I39cf2e991ba3179fb6ea6c9b80ff26d8bf7b0a95 Signed-off-by: David Steele --- .../test-graphics-command-buffer.cpp | 8 +- .../test-graphics-command-buffer.h | 10 +- .../test-graphics-controller.cpp | 17 +--- .../test-graphics-controller.h | 4 + dali/graphics-api/graphics-command-buffer.h | 4 +- dali/internal/render/common/render-algorithms.cpp | 78 ++++++++------- dali/internal/render/common/render-algorithms.h | 15 ++- dali/internal/render/common/render-list.h | 18 ++++ dali/internal/render/renderers/render-renderer.cpp | 105 ++++++++++----------- dali/internal/render/renderers/render-renderer.h | 32 +++++-- 10 files changed, 162 insertions(+), 129 deletions(-) diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-command-buffer.cpp b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-command-buffer.cpp index 78c6fbb..e34b852 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-command-buffer.cpp +++ b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-command-buffer.cpp @@ -60,9 +60,9 @@ void TestGraphicsCommandBuffer::GetStateForDrawCall(int drawCallIndex) } } -std::vector TestGraphicsCommandBuffer::GetCommandsByType(CommandTypeMask mask) +std::vector TestGraphicsCommandBuffer::GetCommandsByType(CommandTypeMask mask) const { - std::vector mCommandStack{}; + std::vector mCommandStack{}; for(auto& cmd : mCommands) { if(uint32_t(cmd.type) == (mask & uint32_t(cmd.type))) @@ -73,9 +73,9 @@ std::vector TestGraphicsCommandBuffer::GetCommandsByType(CommandTypeMa return mCommandStack; } -std::vector TestGraphicsCommandBuffer::GetChildCommandsByType(CommandTypeMask mask) +std::vector TestGraphicsCommandBuffer::GetChildCommandsByType(CommandTypeMask mask) const { - std::vector mCommandStack{}; + std::vector mCommandStack{}; for(auto& cmd : mCommands) { if(uint32_t(cmd.type) == (mask & uint32_t(cmd.type))) diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-command-buffer.h b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-command-buffer.h index bc1ea7b..a68cb7f 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-command-buffer.h +++ b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-command-buffer.h @@ -477,7 +477,7 @@ struct Command struct { - std::vector buffers; + std::vector buffers; } executeCommandBuffers; } data; @@ -624,7 +624,7 @@ public: mCallStack.PushCall("EndRenderPass", ""); } - void ExecuteCommandBuffers(std::vector&& commandBuffers) override + void ExecuteCommandBuffers(std::vector&& commandBuffers) override { mCommands.emplace_back(); auto& cmd = mCommands.back(); @@ -632,7 +632,7 @@ public: cmd.data.executeCommandBuffers.buffers.reserve(commandBuffers.size()); for(auto&& item : commandBuffers) { - cmd.data.executeCommandBuffers.buffers.emplace_back(static_cast(item)); + cmd.data.executeCommandBuffers.buffers.emplace_back(static_cast(item)); } mCallStack.PushCall("ExecuteCommandBuffers", ""); } @@ -768,9 +768,9 @@ public: /** * Retrieves commands of specified type */ - std::vector GetCommandsByType(CommandTypeMask mask); + std::vector GetCommandsByType(CommandTypeMask mask) const; - std::vector GetChildCommandsByType(CommandTypeMask mask); + std::vector GetChildCommandsByType(CommandTypeMask mask) const; private: TraceCallStack& mCallStack; diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-controller.cpp b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-controller.cpp index e398236..ddcf0f5 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-controller.cpp +++ b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-controller.cpp @@ -543,21 +543,6 @@ void TestGraphicsController::ProcessCommandBuffer(TestGraphicsCommandBuffer& com case CommandType::BIND_PIPELINE: { currentPipeline = Uncast(cmd.data.bindPipeline.pipeline); - - // Bind framebuffer if different. @todo Move to RenderPass - auto framebuffer = currentPipeline->framebufferState.framebuffer; - if(framebuffer && framebuffer != currentFramebuffer) - { - auto graphicsFramebuffer = Uncast(framebuffer); - graphicsFramebuffer->Bind(); - } - else - { - if(currentFramebuffer) - currentFramebuffer->Bind(); - else - mGl.BindFramebuffer(GL_FRAMEBUFFER, 0); - } BindPipeline(currentPipeline); break; } @@ -622,7 +607,7 @@ void TestGraphicsController::ProcessCommandBuffer(TestGraphicsCommandBuffer& com // Process secondary command buffers for(auto& buf : cmd.data.executeCommandBuffers.buffers) { - ProcessCommandBuffer(*static_cast(buf)); + ProcessCommandBuffer(*Uncast(buf)); } break; } diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-controller.h b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-controller.h index f777053..1d93bfa 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-controller.h +++ b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-controller.h @@ -406,6 +406,10 @@ public: }; std::vector mProgramCache; + struct PipelineCache + { + }; + std::vector mCustomUniforms; }; diff --git a/dali/graphics-api/graphics-command-buffer.h b/dali/graphics-api/graphics-command-buffer.h index 881543b..dd89295 100644 --- a/dali/graphics-api/graphics-command-buffer.h +++ b/dali/graphics-api/graphics-command-buffer.h @@ -206,7 +206,7 @@ public: * * @param[in] commandBuffers List of buffers to execute */ - virtual void ExecuteCommandBuffers( std::vector&& commandBuffers ) = 0; + virtual void ExecuteCommandBuffers(std::vector&& commandBuffers) = 0; /** * @brief Draw primitives @@ -304,6 +304,6 @@ protected: CommandBuffer(CommandBuffer&&) = default; CommandBuffer& operator=(CommandBuffer&&) = default; }; -} // Namespace Dali +} // namespace Dali::Graphics #endif diff --git a/dali/internal/render/common/render-algorithms.cpp b/dali/internal/render/common/render-algorithms.cpp index 1b871a6..280ff60 100644 --- a/dali/internal/render/common/render-algorithms.cpp +++ b/dali/internal/render/common/render-algorithms.cpp @@ -286,10 +286,15 @@ inline void SetupDepthBuffer(const RenderItem& item, Context& context, bool dept * A stack of scissor clips at each depth of clipping is maintained, so it can be applied and unapplied. * As the clips are hierarchical, this RenderItems AABB is clipped against the current "active" scissor bounds via an intersection operation. * @param[in] item The current RenderItem about to be rendered + * @param[in,out] commandBuffer The command buffer to write into * @param[in] context The context * @param[in] instruction The render-instruction to process. */ -inline void RenderAlgorithms::SetupScissorClipping(const RenderItem& item, Context& context, const RenderInstruction& instruction) +inline void RenderAlgorithms::SetupScissorClipping( + const RenderItem& item, + Graphics::CommandBuffer& commandBuffer, + Context& context, + const RenderInstruction& instruction) { // Get the number of child scissors in the stack (do not include layer or root box). size_t childStackDepth = mScissorStack.size() - 1u; @@ -346,7 +351,7 @@ inline void RenderAlgorithms::SetupScissorClipping(const RenderItem& item, Conte // Enable the scissor test based on the above calculation if(scissorEnabled) { - mGraphicsCommandBuffer->SetScissorTestEnable( scissorEnabled ); + commandBuffer.SetScissorTestEnable(scissorEnabled); } // If scissor is enabled, we use the calculated screen-space coordinates (now in the stack). @@ -358,16 +363,15 @@ inline void RenderAlgorithms::SetupScissorClipping(const RenderItem& item, Conte { useScissorBox.y = (instruction.mFrameBuffer->GetHeight() - useScissorBox.height) - useScissorBox.y; } - Graphics::Rect2D scissorBox = { - useScissorBox.x, useScissorBox.y, - uint32_t(useScissorBox.width), uint32_t(useScissorBox.height) - }; - mGraphicsCommandBuffer->SetScissor( scissorBox ); + Graphics::Rect2D scissorBox = { + useScissorBox.x, useScissorBox.y, uint32_t(useScissorBox.width), uint32_t(useScissorBox.height)}; + commandBuffer.SetScissor(scissorBox); } } } inline void RenderAlgorithms::SetupClipping(const RenderItem& item, + Graphics::CommandBuffer& commandBuffer, Context& context, bool& usedStencilBuffer, uint32_t& lastClippingDepth, @@ -395,7 +399,7 @@ inline void RenderAlgorithms::SetupClipping(const RenderItem& // As both scissor and stencil clips can be nested, we may be simultaneously traversing up the scissor tree, requiring a scissor to be un-done. Whilst simultaneously adding a new stencil clip. // We process both based on our current and old clipping depths for each mode. // Both methods with return rapidly if there is nothing to be done for that type of clipping. - SetupScissorClipping(item, context, instruction); + SetupScissorClipping(item, commandBuffer, context, instruction); if(stencilBufferAvailable == Integration::StencilBufferAvailable::TRUE) { @@ -481,7 +485,12 @@ inline void RenderAlgorithms::ProcessRenderList(const RenderList& bool firstDepthBufferUse(true); mViewportRectangle = viewport; - mGraphicsCommandBuffer->SetViewport(ViewportFromClippingBox(mViewportRectangle, orientation)); + + auto* mutableRenderList = const_cast(&renderList); + auto& secondaryCommandBuffer = mutableRenderList->GetCommandBuffer(mGraphicsController); + secondaryCommandBuffer.Reset(); + + secondaryCommandBuffer.SetViewport(ViewportFromClippingBox(mViewportRectangle, orientation)); mHasLayerScissor = false; // Setup Scissor testing (for both viewport and per-node scissor) @@ -492,29 +501,28 @@ inline void RenderAlgorithms::ProcessRenderList(const RenderList& if(!rootClippingRect.IsEmpty()) { Graphics::Viewport graphicsViewport = ViewportFromClippingBox(mViewportRectangle, 0); - mGraphicsCommandBuffer->SetScissorTestEnable(true); - mGraphicsCommandBuffer->SetScissor(Rect2DFromRect(rootClippingRect, orientation, graphicsViewport)); + secondaryCommandBuffer.SetScissorTestEnable(true); + secondaryCommandBuffer.SetScissor(Rect2DFromRect(rootClippingRect, orientation, graphicsViewport)); mScissorStack.push_back(rootClippingRect); } // We are not performing a layer clip and no clipping rect set. Add the viewport as the root scissor rectangle. else if(!renderList.IsClipping()) { - mGraphicsCommandBuffer->SetScissorTestEnable(false); + secondaryCommandBuffer.SetScissorTestEnable(false); mScissorStack.push_back(mViewportRectangle); } if(renderList.IsClipping()) { Graphics::Viewport graphicsViewport = ViewportFromClippingBox(mViewportRectangle, 0); - mGraphicsCommandBuffer->SetScissorTestEnable(true); + secondaryCommandBuffer.SetScissorTestEnable(true); const ClippingBox& layerScissorBox = renderList.GetClippingBox(); - mGraphicsCommandBuffer->SetScissor(Rect2DFromClippingBox(layerScissorBox, orientation, graphicsViewport)); + secondaryCommandBuffer.SetScissor(Rect2DFromClippingBox(layerScissorBox, orientation, graphicsViewport)); mScissorStack.push_back(layerScissorBox); mHasLayerScissor = true; } - mGraphicsRenderItemCommandBuffers.clear(); - // Loop through all RenderList in the RenderList, set up any prerequisites to render them, then perform the render. + // Loop through all RenderItems in the RenderList, set up any prerequisites to render them, then perform the render. for(uint32_t index = 0u; index < count; ++index) { const RenderItem& item = renderList.GetItem(index); @@ -539,7 +547,7 @@ inline void RenderAlgorithms::ProcessRenderList(const RenderList& // Set up clipping based on both the Renderer and Actor APIs. // The Renderer API will be used if specified. If AUTO, the Actors automatic clipping feature will be used. - SetupClipping(item, context, usedStencilBuffer, lastClippingDepth, lastClippingId, stencilBufferAvailable, instruction); + SetupClipping(item, secondaryCommandBuffer, context, usedStencilBuffer, lastClippingDepth, lastClippingId, stencilBufferAvailable, instruction); if(DALI_LIKELY(item.mRenderer)) { @@ -563,21 +571,12 @@ inline void RenderAlgorithms::ProcessRenderList(const RenderList& auto const MAX_QUEUE = item.mRenderer->GetDrawCommands().empty() ? 1 : DevelRenderer::RENDER_QUEUE_MAX; for(auto queue = 0u; queue < MAX_QUEUE; ++queue) { - // Render the item. If rendered, add its command buffer into the list - if( item.mRenderer->Render(context, bufferIndex, *item.mNode, item.mModelMatrix, item.mModelViewMatrix, viewMatrix, projectionMatrix, item.mSize, !item.mIsOpaque, boundTextures, instruction, queue) ) - { - mGraphicsRenderItemCommandBuffers.emplace_back( item.mRenderer->GetGraphicsCommandBuffer() ); - } + // Render the item. It will write into the command buffer everything it has to render + item.mRenderer->Render(secondaryCommandBuffer, bufferIndex, *item.mNode, item.mModelMatrix, item.mModelViewMatrix, viewMatrix, projectionMatrix, item.mSize, !item.mIsOpaque, boundTextures, instruction, queue); } } } } - - // Execute command buffers - if(!mGraphicsRenderItemCommandBuffers.empty()) - { - mGraphicsCommandBuffer->ExecuteCommandBuffers( std::move(mGraphicsRenderItemCommandBuffers) ); - } } RenderAlgorithms::RenderAlgorithms(Graphics::Controller& graphicsController) @@ -594,25 +593,22 @@ void RenderAlgorithms::ResetCommandBuffer() { mGraphicsCommandBuffer = mGraphicsController.CreateCommandBuffer( Graphics::CommandBufferCreateInfo() - .SetLevel(Graphics::CommandBufferLevel::SECONDARY), + .SetLevel(Graphics::CommandBufferLevel::PRIMARY), nullptr); } else { mGraphicsCommandBuffer->Reset(); } - - // Reset list of secondary buffers to submit - mGraphicsRenderItemCommandBuffers.clear(); } void RenderAlgorithms::SubmitCommandBuffer() { // Submit main command buffer Graphics::SubmitInfo submitInfo; - submitInfo.cmdBuffer.push_back( mGraphicsCommandBuffer.get() ); + submitInfo.cmdBuffer.push_back(mGraphicsCommandBuffer.get()); submitInfo.flags = 0 | Graphics::SubmitFlagBits::FLUSH; - mGraphicsController.SubmitCommandBuffers( submitInfo ); + mGraphicsController.SubmitCommandBuffers(submitInfo); } void RenderAlgorithms::ProcessRenderInstruction(const RenderInstruction& instruction, @@ -635,7 +631,8 @@ void RenderAlgorithms::ProcessRenderInstruction(const RenderInstruction& if(viewMatrix && projectionMatrix) { - const RenderListContainer::SizeType count = instruction.RenderListCount(); + std::vector buffers; + const RenderListContainer::SizeType count = instruction.RenderListCount(); // Iterate through each render list in order. If a pair of render lists // are marked as interleaved, then process them together. @@ -657,8 +654,19 @@ void RenderAlgorithms::ProcessRenderInstruction(const RenderInstruction& viewport, rootClippingRect, orientation); + + // Execute command buffer + auto* commandBuffer = renderList->GetCommandBuffer(); + if(commandBuffer) + { + buffers.push_back(commandBuffer); + } } } + if(buffers.size() > 0) + { + mGraphicsCommandBuffer->ExecuteCommandBuffers(std::move(buffers)); + } } } diff --git a/dali/internal/render/common/render-algorithms.h b/dali/internal/render/common/render-algorithms.h index 4654bf7..90b7146 100644 --- a/dali/internal/render/common/render-algorithms.h +++ b/dali/internal/render/common/render-algorithms.h @@ -112,16 +112,22 @@ private: * - If the node is a clipping node, apply the nodes clip intersected with the current/parent scissor clip. * - If we have gone up the scissor hierarchy, and need to un-apply a scissor clip. * - Disable scissor clipping completely if it is not needed - * @param[in] item The current RenderItem (about to be rendered) - * @param[in] context The current Context - * @param[in] instruction The render-instruction to process. + * @param[in] item The current RenderItem (about to be rendered) + * @param[in] commandBuffer The command buffer to write into + * @param[in] context The current Context + * @param[in] instruction The render-instruction to process. */ - inline void SetupScissorClipping(const Dali::Internal::SceneGraph::RenderItem& item, Context& context, const Dali::Internal::SceneGraph::RenderInstruction& instruction); + inline void SetupScissorClipping( + const Dali::Internal::SceneGraph::RenderItem& item, + Graphics::CommandBuffer& commandBuffer, + Context& context, + const Dali::Internal::SceneGraph::RenderInstruction& instruction); /** * @brief Set up the clipping based on the specified clipping settings. * @param[in] item The current RenderItem (about to be rendered) * @param[in] context The context + * @param[in,out] commandBuffer The command buffer to write commands to * @param[in/out] usedStencilBuffer True if the stencil buffer has been used so far within this RenderList. Used by StencilMode::ON. * @param[in/out] lastClippingDepth The stencil depth of the last renderer drawn. Used by the clipping feature. * @param[in/out] lastClippingId The clipping ID of the last renderer drawn. Used by the clipping feature. @@ -129,6 +135,7 @@ private: * @param[in] instruction The render-instruction to process. */ inline void SetupClipping(const Dali::Internal::SceneGraph::RenderItem& item, + Graphics::CommandBuffer& commandBuffer, Context& context, bool& usedStencilBuffer, uint32_t& lastClippingDepth, diff --git a/dali/internal/render/common/render-list.h b/dali/internal/render/common/render-list.h index e373c35..c0b7026 100644 --- a/dali/internal/render/common/render-list.h +++ b/dali/internal/render/common/render-list.h @@ -23,6 +23,7 @@ // INTERNAL INCLUDES #include +#include #include #include @@ -255,6 +256,21 @@ public: return mHasColorRenderItems; } + Graphics::CommandBuffer& GetCommandBuffer(Graphics::Controller& controller) + { + if(!mGraphicsCommandBuffer) + { + mGraphicsCommandBuffer = controller.CreateCommandBuffer( + Graphics::CommandBufferCreateInfo().SetLevel(Graphics::CommandBufferLevel::SECONDARY), nullptr); + } + return *mGraphicsCommandBuffer.get(); + } + + const Graphics::CommandBuffer* GetCommandBuffer() const + { + return mGraphicsCommandBuffer.get(); + } + private: /* * Copy constructor and assignment operator not defined @@ -265,6 +281,8 @@ private: RenderItemContainer mItems; ///< Each item is a renderer and matrix pair uint32_t mNextFree; ///< index for the next free item to use + mutable Graphics::UniquePtr mGraphicsCommandBuffer{nullptr}; + ClippingBox* mClippingBox; ///< The clipping box, in window coordinates, when clipping is enabled Layer* mSourceLayer; ///< The originating layer where the renderers are from bool mHasColorRenderItems : 1; ///< True if list contains color render items diff --git a/dali/internal/render/renderers/render-renderer.cpp b/dali/internal/render/renderers/render-renderer.cpp index 8ad31bb..03a8d98 100644 --- a/dali/internal/render/renderers/render-renderer.cpp +++ b/dali/internal/render/renderers/render-renderer.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -488,7 +487,7 @@ void Renderer::Upload() mGeometry->Upload(*mGraphicsController); } -bool Renderer::Render(Context& context, +bool Renderer::Render(Graphics::CommandBuffer& commandBuffer, BufferIndex bufferIndex, const SceneGraph::NodeDataProvider& node, const Matrix& modelMatrix, @@ -523,21 +522,6 @@ bool Renderer::Render(Context& conte return false; } - // Create command buffer if not present - if(!mGraphicsCommandBuffer) - { - mGraphicsCommandBuffer = mGraphicsController->CreateCommandBuffer( - Graphics::CommandBufferCreateInfo() - .SetLevel(Graphics::CommandBufferLevel::SECONDARY), - nullptr); - } - else - { - mGraphicsCommandBuffer->Reset(); - } - - auto& commandBuffer = mGraphicsCommandBuffer; - // Set blending mode if(!mDrawCommands.empty()) { @@ -582,48 +566,32 @@ bool Renderer::Render(Context& conte return false; } - // Temporarily create a pipeline here - this will be used for transporting - // topology, vertex format, attrs, rasterization state - mGraphicsPipeline = PrepareGraphicsPipeline(*program, instruction, blend, std::move(mGraphicsPipeline)); + // Prepare the graphics pipeline. This may either re-use an existing pipeline or create a new one. + auto& pipeline = PrepareGraphicsPipeline(*program, instruction, node, blend); - commandBuffer->BindPipeline(*mGraphicsPipeline.get()); + commandBuffer.BindPipeline(pipeline); - BindTextures(*commandBuffer.get(), boundTextures); + BindTextures(commandBuffer, boundTextures); BuildUniformIndexMap(bufferIndex, node, size, *program); - WriteUniformBuffer(bufferIndex, *commandBuffer.get(), program, instruction, node, modelMatrix, modelViewMatrix, viewMatrix, projectionMatrix, size); + WriteUniformBuffer(bufferIndex, commandBuffer, program, instruction, node, modelMatrix, modelViewMatrix, viewMatrix, projectionMatrix, size); bool drawn = false; // Draw can fail if there are no vertex buffers or they haven't been uploaded yet // @todo We should detect this case much earlier to prevent unnecessary work - //@todo manage mDrawCommands in the same way as above command buffer?! if(mDrawCommands.empty()) { - drawn = mGeometry->Draw(*mGraphicsController, *commandBuffer.get(), mIndexedDrawFirstElement, mIndexedDrawElementsCount); + drawn = mGeometry->Draw(*mGraphicsController, commandBuffer, mIndexedDrawFirstElement, mIndexedDrawElementsCount); } else { for(auto& cmd : commands) { - // @todo This should generate a command buffer per cmd - // Tests WILL fail. (Temporarily commented out) - mGeometry->Draw(*mGraphicsController, *commandBuffer.get(), cmd->firstIndex, cmd->elementCount); + mGeometry->Draw(*mGraphicsController, commandBuffer, cmd->firstIndex, cmd->elementCount); } } - // Command buffer contains Texture bindings, vertex bindings, index buffer binding, pipeline(vertex format) - // @todo We should return the command buffer(s) and let the calling method submit - // If not drawn, then don't add command buffer to submit info, and if empty, don't - // submit. - /* - if(drawn) - { - Graphics::SubmitInfo submitInfo{{}, 0 | Graphics::SubmitFlagBits::FLUSH}; - submitInfo.cmdBuffer.push_back(commandBuffer.get()); - mGraphicsController->SubmitCommandBuffers(submitInfo); - } - */ mUpdated = false; return drawn; } @@ -996,11 +964,11 @@ bool Renderer::Updated(BufferIndex bufferIndex, const SceneGraph::NodeDataProvid return false; } -Graphics::UniquePtr Renderer::PrepareGraphicsPipeline( +Graphics::Pipeline& Renderer::PrepareGraphicsPipeline( Program& program, const Dali::Internal::SceneGraph::RenderInstruction& instruction, - bool blend, - Graphics::UniquePtr&& oldPipeline) + const SceneGraph::NodeDataProvider& node, + bool blend) { Graphics::InputAssemblyState inputAssemblyState{}; Graphics::VertexInputState vertexInputState{}; @@ -1153,17 +1121,46 @@ Graphics::UniquePtr Renderer::PrepareGraphicsPipeline( mUpdated = true; - // Create a new pipeline - // @todo Passed as pointers - shallow copy will break. Implementation MUST deep copy. - return mGraphicsController->CreatePipeline( - Graphics::PipelineCreateInfo() - .SetInputAssemblyState(&inputAssemblyState) - .SetVertexInputState(&vertexInputState) - .SetRasterizationState(&rasterizationState) - .SetColorBlendState(&colorBlendState) - .SetProgramState(&programState) - .SetNextExtension(&mLegacyProgram), - std::move(oldPipeline)); + // Create the pipeline + Graphics::PipelineCreateInfo createInfo; + createInfo + .SetInputAssemblyState(&inputAssemblyState) + .SetVertexInputState(&vertexInputState) + .SetRasterizationState(&rasterizationState) + .SetColorBlendState(&colorBlendState) + .SetProgramState(&programState) + .SetNextExtension(&mLegacyProgram); + + // Store a pipeline per renderer per render (renderer can be owned by multiple nodes, + // and re-drawn in multiple instructions). + // @todo This is only needed because ColorBlend state can change. Fixme! + // This is ameliorated by the fact that implementation caches pipelines, and we're only storing + // handles. + auto hash = HashedPipeline::GetHash(&node, &instruction, blend); + HashedPipeline* hashedPipeline = nullptr; + for(auto& element : mGraphicsPipelines) + { + if(element.mHash == hash) + { + hashedPipeline = &element; + break; + } + } + + if(hashedPipeline != nullptr) + { + hashedPipeline->mGraphicsPipeline = mGraphicsController->CreatePipeline( + createInfo, + std::move(hashedPipeline->mGraphicsPipeline)); + } + else + { + mGraphicsPipelines.emplace_back(); + mGraphicsPipelines.back().mHash = hash; + mGraphicsPipelines.back().mGraphicsPipeline = mGraphicsController->CreatePipeline(createInfo, nullptr); + hashedPipeline = &mGraphicsPipelines.back(); + } + return *hashedPipeline->mGraphicsPipeline.get(); } } // namespace Render diff --git a/dali/internal/render/renderers/render-renderer.h b/dali/internal/render/renderers/render-renderer.h index 149cd84..05e1971 100644 --- a/dali/internal/render/renderers/render-renderer.h +++ b/dali/internal/render/renderers/render-renderer.h @@ -355,7 +355,7 @@ public: /** * Called to render during RenderManager::Render(). - * @param[in] context The context used for rendering + * @param[in,out] commandBuffer The command buffer to write into * @param[in] bufferIndex The index of the previous update buffer. * @param[in] node The node using this renderer * @param[in] modelViewMatrix The model-view matrix. @@ -368,7 +368,7 @@ public: * * @return True if the content has been rendered, false if skipped. */ - bool Render(Context& context, + bool Render(Graphics::CommandBuffer& commandBuffer, BufferIndex bufferIndex, const SceneGraph::NodeDataProvider& node, const Matrix& modelMatrix, @@ -468,13 +468,18 @@ private: void BindTextures(Graphics::CommandBuffer& commandBuffer, Vector& boundTextures); /** - * Prepare a pipeline for this renderer + * Prepare a pipeline for this renderer. + * + * As a renderer can be re-used in a single frame (e.g. being used by multiple nodes, or + * by non-exclusive render tasks), we store a pipeline per node/instruction. + * In practice, the implementation will cached pipelines, so we normally only have + * multiple handles. */ - Graphics::UniquePtr PrepareGraphicsPipeline( + Graphics::Pipeline& PrepareGraphicsPipeline( Program& program, const Dali::Internal::SceneGraph::RenderInstruction& instruction, - bool blend, - Graphics::UniquePtr&& oldPipeline); + const SceneGraph::NodeDataProvider& node, + bool blend); /** * Setup and write data to the uniform buffer @@ -531,8 +536,7 @@ private: Render::UniformBufferManager* mUniformBufferManager{}; std::vector mUniformBufferBindings{}; - Graphics::UniquePtr mGraphicsPipeline{}; ///< The graphics pipeline. (Cached implementation) - std::vector mShaderStates{}; + std::vector mShaderStates{}; using Hash = unsigned long; struct UniformIndexMap @@ -549,8 +553,18 @@ private: UniformIndexMappings mUniformIndexMap; Vector mAttributeLocations; + uint64_t mUniformsHash; - uint64_t mUniformsHash; + struct HashedPipeline + { + uint64_t mHash{0u}; + Graphics::UniquePtr mGraphicsPipeline{nullptr}; + inline static uint64_t GetHash(const void* node, const void* instruction, bool blend) + { + return (reinterpret_cast(node) << 32) | ((reinterpret_cast(instruction) & 0xFFFFFFF) << 1) | blend; + } + }; + std::vector mGraphicsPipelines{}; StencilParameters mStencilParameters; ///< Struct containing all stencil related options BlendingOptions mBlendingOptions; ///< Blending options including blend color, blend func and blend equation -- 2.7.4