Moving owner of secondary command buffers 48/257248/2
authorDavid Steele <david.steele@samsung.com>
Wed, 21 Apr 2021 15:59:38 +0000 (16:59 +0100)
committerDavid Steele <david.steele@samsung.com>
Thu, 22 Apr 2021 12:20:19 +0000 (13:20 +0100)
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 <david.steele@samsung.com>
automated-tests/src/dali/dali-test-suite-utils/test-graphics-command-buffer.cpp
automated-tests/src/dali/dali-test-suite-utils/test-graphics-command-buffer.h
automated-tests/src/dali/dali-test-suite-utils/test-graphics-controller.cpp
automated-tests/src/dali/dali-test-suite-utils/test-graphics-controller.h
dali/graphics-api/graphics-command-buffer.h
dali/internal/render/common/render-algorithms.cpp
dali/internal/render/common/render-algorithms.h
dali/internal/render/common/render-list.h
dali/internal/render/renderers/render-renderer.cpp
dali/internal/render/renderers/render-renderer.h

index 78c6fbb..e34b852 100644 (file)
@@ -60,9 +60,9 @@ void TestGraphicsCommandBuffer::GetStateForDrawCall(int drawCallIndex)
   }
 }
 
-std::vector<Command*> TestGraphicsCommandBuffer::GetCommandsByType(CommandTypeMask mask)
+std::vector<const Command*> TestGraphicsCommandBuffer::GetCommandsByType(CommandTypeMask mask) const
 {
-  std::vector<Command*> mCommandStack{};
+  std::vector<const Command*> mCommandStack{};
   for(auto& cmd : mCommands)
   {
     if(uint32_t(cmd.type) == (mask & uint32_t(cmd.type)))
@@ -73,9 +73,9 @@ std::vector<Command*> TestGraphicsCommandBuffer::GetCommandsByType(CommandTypeMa
   return mCommandStack;
 }
 
-std::vector<Command*> TestGraphicsCommandBuffer::GetChildCommandsByType(CommandTypeMask mask)
+std::vector<const Command*> TestGraphicsCommandBuffer::GetChildCommandsByType(CommandTypeMask mask) const
 {
-  std::vector<Command*> mCommandStack{};
+  std::vector<const Command*> mCommandStack{};
   for(auto& cmd : mCommands)
   {
     if(uint32_t(cmd.type) == (mask & uint32_t(cmd.type)))
index bc1ea7b..a68cb7f 100644 (file)
@@ -477,7 +477,7 @@ struct Command
 
     struct
     {
-      std::vector<TestGraphicsCommandBuffer*> buffers;
+      std::vector<const TestGraphicsCommandBuffer*> buffers;
     } executeCommandBuffers;
 
   } data;
@@ -624,7 +624,7 @@ public:
     mCallStack.PushCall("EndRenderPass", "");
   }
 
-  void ExecuteCommandBuffers(std::vector<CommandBuffer*>&& commandBuffers) override
+  void ExecuteCommandBuffers(std::vector<const CommandBuffer*>&& 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<TestGraphicsCommandBuffer*>(item));
+      cmd.data.executeCommandBuffers.buffers.emplace_back(static_cast<const TestGraphicsCommandBuffer*>(item));
     }
     mCallStack.PushCall("ExecuteCommandBuffers", "");
   }
@@ -768,9 +768,9 @@ public:
   /**
    * Retrieves commands of specified type
    */
-  std::vector<Command*> GetCommandsByType(CommandTypeMask mask);
+  std::vector<const Command*> GetCommandsByType(CommandTypeMask mask) const;
 
-  std::vector<Command*> GetChildCommandsByType(CommandTypeMask mask);
+  std::vector<const Command*> GetChildCommandsByType(CommandTypeMask mask) const;
 
 private:
   TraceCallStack&    mCallStack;
index e398236..ddcf0f5 100644 (file)
@@ -543,21 +543,6 @@ void TestGraphicsController::ProcessCommandBuffer(TestGraphicsCommandBuffer& com
       case CommandType::BIND_PIPELINE:
       {
         currentPipeline = Uncast<TestGraphicsPipeline>(cmd.data.bindPipeline.pipeline);
-
-        // Bind framebuffer if different. @todo Move to RenderPass
-        auto framebuffer = currentPipeline->framebufferState.framebuffer;
-        if(framebuffer && framebuffer != currentFramebuffer)
-        {
-          auto graphicsFramebuffer = Uncast<TestGraphicsFramebuffer>(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<TestGraphicsCommandBuffer*>(buf));
+          ProcessCommandBuffer(*Uncast<TestGraphicsCommandBuffer>(buf));
         }
         break;
       }
index f777053..1d93bfa 100644 (file)
@@ -406,6 +406,10 @@ public:
   };
   std::vector<ProgramCache> mProgramCache;
 
+  struct PipelineCache
+  {
+  };
+
   std::vector<UniformData> mCustomUniforms;
 };
 
index 881543b..dd89295 100644 (file)
@@ -206,7 +206,7 @@ public:
    *
    * @param[in] commandBuffers List of buffers to execute
    */
-  virtual void ExecuteCommandBuffers( std::vector<CommandBuffer*>&& commandBuffers ) = 0;
+  virtual void ExecuteCommandBuffers(std::vector<const CommandBuffer*>&& commandBuffers) = 0;
 
   /**
    * @brief Draw primitives
@@ -304,6 +304,6 @@ protected:
   CommandBuffer(CommandBuffer&&) = default;
   CommandBuffer& operator=(CommandBuffer&&) = default;
 };
-} // Namespace Dali
+} // namespace Dali::Graphics
 
 #endif
index 1b871a6..280ff60 100644 (file)
@@ -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*>(&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<const Graphics::CommandBuffer*> 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));
+    }
   }
 }
 
index 4654bf7..90b7146 100644 (file)
@@ -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,
index e373c35..c0b7026 100644 (file)
@@ -23,6 +23,7 @@
 
 // INTERNAL INCLUDES
 #include <dali/devel-api/common/owner-container.h>
+#include <dali/graphics-api/graphics-controller.h>
 #include <dali/internal/render/common/render-item.h>
 #include <dali/public-api/math/rect.h>
 
@@ -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<Graphics::CommandBuffer> 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
index 8ad31bb..03a8d98 100644 (file)
@@ -26,7 +26,6 @@
 #include <dali/internal/render/common/render-instruction.h>
 #include <dali/internal/render/data-providers/node-data-provider.h>
 #include <dali/internal/render/data-providers/uniform-map-data-provider.h>
-#include <dali/internal/render/gl-resources/context.h>
 #include <dali/internal/render/renderers/render-sampler.h>
 #include <dali/internal/render/renderers/render-texture.h>
 #include <dali/internal/render/renderers/render-vertex-buffer.h>
@@ -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<Graphics::Pipeline> Renderer::PrepareGraphicsPipeline(
+Graphics::Pipeline& Renderer::PrepareGraphicsPipeline(
   Program&                                             program,
   const Dali::Internal::SceneGraph::RenderInstruction& instruction,
-  bool                                                 blend,
-  Graphics::UniquePtr<Graphics::Pipeline>&&            oldPipeline)
+  const SceneGraph::NodeDataProvider&                  node,
+  bool                                                 blend)
 {
   Graphics::InputAssemblyState inputAssemblyState{};
   Graphics::VertexInputState   vertexInputState{};
@@ -1153,17 +1121,46 @@ Graphics::UniquePtr<Graphics::Pipeline> 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
index 149cd84..05e1971 100644 (file)
@@ -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<Graphics::Texture*>& 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<Graphics::Pipeline> PrepareGraphicsPipeline(
+  Graphics::Pipeline& PrepareGraphicsPipeline(
     Program&                                             program,
     const Dali::Internal::SceneGraph::RenderInstruction& instruction,
-    bool                                                 blend,
-    Graphics::UniquePtr<Graphics::Pipeline>&&            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<Graphics::UniformBufferBinding> mUniformBufferBindings{};
 
-  Graphics::UniquePtr<Graphics::Pipeline> mGraphicsPipeline{}; ///< The graphics pipeline. (Cached implementation)
-  std::vector<Graphics::ShaderState>      mShaderStates{};
+  std::vector<Graphics::ShaderState> mShaderStates{};
 
   using Hash = unsigned long;
   struct UniformIndexMap
@@ -549,8 +553,18 @@ private:
 
   UniformIndexMappings mUniformIndexMap;
   Vector<int32_t>      mAttributeLocations;
+  uint64_t             mUniformsHash;
 
-  uint64_t mUniformsHash;
+  struct HashedPipeline
+  {
+    uint64_t                                mHash{0u};
+    Graphics::UniquePtr<Graphics::Pipeline> mGraphicsPipeline{nullptr};
+    inline static uint64_t                  GetHash(const void* node, const void* instruction, bool blend)
+    {
+      return (reinterpret_cast<uint64_t>(node) << 32) | ((reinterpret_cast<uint64_t>(instruction) & 0xFFFFFFF) << 1) | blend;
+    }
+  };
+  std::vector<HashedPipeline> mGraphicsPipelines{};
 
   StencilParameters mStencilParameters; ///< Struct containing all stencil related options
   BlendingOptions   mBlendingOptions;   ///< Blending options including blend color, blend func and blend equation