Added draw command support to the Renderer. 26/238026/12
authoradam.b <adam.b@samsung.com>
Thu, 16 Jul 2020 11:30:56 +0000 (12:30 +0100)
committeradam.b <adam.b@samsung.com>
Fri, 24 Jul 2020 11:43:10 +0000 (12:43 +0100)
DrawCommand allows to execute multiple draw calls from a single renderer

Change-Id: I1eb872651543173e27564d375b477047e6fb253e

automated-tests/src/dali/utc-Dali-Renderer.cpp
dali/devel-api/file.list
dali/devel-api/rendering/renderer-devel.cpp [new file with mode: 0644]
dali/devel-api/rendering/renderer-devel.h
dali/internal/event/rendering/renderer-impl.cpp
dali/internal/event/rendering/renderer-impl.h
dali/internal/render/common/render-algorithms.cpp
dali/internal/render/renderers/render-renderer.cpp
dali/internal/render/renderers/render-renderer.h
dali/internal/update/rendering/scene-graph-renderer.cpp
dali/internal/update/rendering/scene-graph-renderer.h

index 77b86a7..09dd80d 100644 (file)
@@ -3108,3 +3108,64 @@ int UtcDaliRendererRegenerateUniformMap(void)
 
   END_TEST;
 }
+
+int UtcDaliRendererAddDrawCommands(void)
+{
+  TestApplication application;
+
+  tet_infoline("Test adding draw commands to the renderer");
+
+  TestGlAbstraction &glAbstraction = application.GetGlAbstraction();
+  glAbstraction.EnableEnableDisableCallTrace(true);
+
+  Geometry geometry = CreateQuadGeometry();
+  Shader   shader   = Shader::New("vertexSrc", "fragmentSrc");
+  Renderer renderer = Renderer::New(geometry, shader);
+
+  renderer.SetProperty( Renderer::Property::BLEND_MODE, Dali::BlendMode::ON );
+  Actor actor = Actor::New();
+  actor.AddRenderer(renderer);
+  actor.SetProperty(Actor::Property::SIZE, Vector2(400.0f, 400.0f));
+  actor.SetProperty(Actor::Property::COLOR, Vector4(1.0f, 0.0f, 1.0f, 1.0f));
+  application.GetScene().Add(actor);
+
+  // Expect delivering a single draw call
+  auto &drawTrace = glAbstraction.GetDrawTrace();
+  drawTrace.Reset();
+  drawTrace.Enable(true);
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( drawTrace.CountMethod("DrawElements"), 1, TEST_LOCATION );
+
+  auto drawCommand1 = DevelRenderer::DrawCommand{};
+  drawCommand1.drawType     = DevelRenderer::DrawType::INDEXED;
+  drawCommand1.firstIndex   = 0;
+  drawCommand1.elementCount = 2;
+  drawCommand1.queue        = DevelRenderer::RENDER_QUEUE_OPAQUE;
+
+  auto drawCommand2 = DevelRenderer::DrawCommand{};
+  drawCommand2.drawType     = DevelRenderer::DrawType::INDEXED;
+  drawCommand2.firstIndex   = 2;
+  drawCommand2.elementCount = 2;
+  drawCommand2.queue        = DevelRenderer::RENDER_QUEUE_TRANSPARENT;
+
+  auto drawCommand3 = DevelRenderer::DrawCommand{};
+  drawCommand3.drawType     = DevelRenderer::DrawType::ARRAY;
+  drawCommand3.firstIndex   = 2;
+  drawCommand3.elementCount = 2;
+  drawCommand3.queue        = DevelRenderer::RENDER_QUEUE_OPAQUE;
+
+  DevelRenderer::AddDrawCommand(renderer, drawCommand1);
+  DevelRenderer::AddDrawCommand(renderer, drawCommand2);
+  DevelRenderer::AddDrawCommand(renderer, drawCommand3);
+
+  drawTrace.Reset();
+  drawTrace.Enable(true);
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(drawTrace.CountMethod("DrawElements"), 3, TEST_LOCATION);
+
+  END_TEST;
+}
\ No newline at end of file
index b450186..2710954 100644 (file)
@@ -24,6 +24,7 @@ SET( devel_api_src_files
   ${devel_api_src_dir}/object/handle-devel.cpp
   ${devel_api_src_dir}/object/csharp-type-registry.cpp
   ${devel_api_src_dir}/rendering/frame-buffer-devel.cpp
+  ${devel_api_src_dir}/rendering/renderer-devel.cpp
   ${devel_api_src_dir}/scripting/scripting.cpp
   ${devel_api_src_dir}/signals/signal-delegate.cpp
   ${devel_api_src_dir}/threading/conditional-wait.cpp
diff --git a/dali/devel-api/rendering/renderer-devel.cpp b/dali/devel-api/rendering/renderer-devel.cpp
new file mode 100644 (file)
index 0000000..0d987cb
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali/internal/event/rendering/renderer-impl.h>
+
+namespace Dali
+{
+
+namespace DevelRenderer
+{
+
+void AddDrawCommand( Dali::Renderer renderer, const DrawCommand& drawCommand )
+{
+  auto& impl = GetImplementation( renderer );
+  impl.AddDrawCommand( drawCommand );
+}
+
+} // DevelRenderer
+} // Dali
\ No newline at end of file
index 02292ab..8fb2032 100644 (file)
@@ -27,6 +27,45 @@ namespace Dali
 namespace DevelRenderer
 {
 
+/**
+ * The index of render queue used by the DrawCommand
+ */
+using RenderQueueIndex = uint32_t;
+
+constexpr RenderQueueIndex RENDER_QUEUE_OPAQUE = 0; ///< Queue for opaque elements
+constexpr RenderQueueIndex RENDER_QUEUE_TRANSPARENT = 1; ///<Queue for transparent elements
+constexpr RenderQueueIndex RENDER_QUEUE_MAX = 2;
+
+/**
+ * Enum describing way of rendering the primitives (indexed draw, array draw)
+ */
+enum class DrawType
+{
+  INDEXED,
+  ARRAY,
+};
+
+/**
+ * Draw command can be attached to the Dali::Renderer and override the default
+ * rendering functionality. Renderer may have several DrawCommands attached to it
+ * and the will be executed sequentially in the order of the 'queue' index.
+ */
+struct DrawCommand
+{
+  DrawType         drawType; ///< Type of drawing (indexed, array)
+  uint32_t         firstIndex ; ///< First index into the geometry array
+  uint32_t         elementCount; ///< Number of elements to draw
+  RenderQueueIndex queue; ///< Queue index
+};
+
+/**
+ * @brief Adds a draw command to the renderer
+ * Once the draw command is added, the default Renderer's behaviour is overriden.
+ * @param[in] renderer a valid Renderer object
+ * @param[in] drawCommand Valid DrawCommand to add to the Renderer
+ */
+DALI_CORE_API void AddDrawCommand( Dali::Renderer renderer, const DrawCommand& drawCommand );
+
 namespace Property
 {
 
@@ -74,6 +113,8 @@ namespace Property
 namespace Rendering
 {
 
+
+
 /**
  * @brief Enumeration for the rendering behavior
  */
index 7071a67..c560bf4 100644 (file)
@@ -1095,6 +1095,24 @@ bool Renderer::GetCurrentPropertyValue( Property::Index index, Property::Value&
   return valueSet;
 }
 
+void Renderer::AddDrawCommand( const Dali::DevelRenderer::DrawCommand& command )
+{
+  if(!mDrawCommands.capacity())
+  {
+    mDrawCommands.reserve(8);
+  }
+
+  mDrawCommands.emplace_back( command );
+
+  Dali::Internal::SceneGraph::SetDrawCommandsMessage( GetEventThreadServices(),
+                                                      GetRendererSceneObject(),
+                                                      mDrawCommands.data(),
+                                                      uint32_t(mDrawCommands.size())
+
+  );
+}
+
+
 } // namespace Internal
 
 } // namespace Dali
index 3817bd4..3a36186 100755 (executable)
@@ -200,6 +200,14 @@ public: // Default property extensions from Object
    */
   virtual const PropertyInputImpl* GetSceneObjectInputProperty( Property::Index index ) const;
 
+  /**
+   * @brief Adds a draw command to the Renderer.
+   * DrawCommands override Renderer's default behaviour.
+   *
+   * @param[in] command Valid reference to a DrawCommand objects
+   */
+  void AddDrawCommand( const Dali::DevelRenderer::DrawCommand& command );
+
 private: // implementation
 
   /**
@@ -269,6 +277,8 @@ private: // data
   DepthTestMode::Type                 mDepthTestMode:3;            ///< Local copy of the depth test mode
   DevelRenderer::Rendering::Type      mRenderingBehavior:2;        ///< The rendering behavior
   bool                                mPremultipledAlphaEnabled:1; ///< Flag indicating whether the Pre-multiplied Alpha Blending is required
+
+  std::vector<Dali::DevelRenderer::DrawCommand> mDrawCommands;     ///< list of draw commands
 };
 
 } // namespace Internal
index cfd1f92..744ae86 100644 (file)
@@ -440,6 +440,7 @@ inline void RenderAlgorithms::ProcessRenderList( const RenderList& renderList,
   for( uint32_t index = 0u; index < count; ++index )
   {
     const RenderItem& item = renderList.GetItem( index );
+
     DALI_PRINT_RENDER_ITEM( item );
 
     // Set up clipping based on both the Renderer and Actor APIs.
@@ -458,9 +459,18 @@ inline void RenderAlgorithms::ProcessRenderList( const RenderList& renderList,
         SetupDepthBuffer( item, context, autoDepthTestMode, firstDepthBufferUse );
       }
 
-      // Render the item.
-      item.mRenderer->Render( context, bufferIndex, *item.mNode, item.mModelMatrix, item.mModelViewMatrix,
-                              viewMatrix, projectionMatrix, item.mSize, !item.mIsOpaque, boundTextures, instruction ); // Added instruction for reflection effect
+      // Depending on whether the renderer has draw commands attached or not the rendering process will
+      // iterate through all the render queues. If there are no draw commands attached, only one
+      // iteration must be done and the default behaviour of the renderer will be executed.
+      // The queues allow to iterate over the same renderer multiple times changing the state of the renderer.
+      // It is similar to the multi-pass rendering.
+      auto const MAX_QUEUE = item.mRenderer->GetDrawCommands().empty() ? 1 : DevelRenderer::RENDER_QUEUE_MAX;
+      for( auto queue = 0u; queue < MAX_QUEUE; ++queue )
+      {
+        // Render the item.
+        item.mRenderer->Render(context, bufferIndex, *item.mNode, item.mModelMatrix, item.mModelViewMatrix,
+                               viewMatrix, projectionMatrix, item.mSize, !item.mIsOpaque, boundTextures, instruction, queue);
+      }
     }
   }
 }
index 7456616..80449e8 100644 (file)
@@ -174,6 +174,11 @@ void Renderer::SetGeometry( Render::Geometry* geometry )
   mGeometry = geometry;
   mUpdateAttributesLocation = true;
 }
+void Renderer::SetDrawCommands( Dali::DevelRenderer::DrawCommand* pDrawCommands, uint32_t size )
+{
+  mDrawCommands.clear();
+  mDrawCommands.insert( mDrawCommands.end(), pDrawCommands, pDrawCommands+size );
+}
 
 void Renderer::SetBlending( Context& context, bool blend )
 {
@@ -550,8 +555,31 @@ void Renderer::Render( Context& context,
                        const Vector3& size,
                        bool blend,
                        Vector<GLuint>& boundTextures,
-                       const Dali::Internal::SceneGraph::RenderInstruction& instruction )
+                       const Dali::Internal::SceneGraph::RenderInstruction& instruction,
+                       uint32_t queueIndex )
 {
+  // Before doing anything test if the call happens in the right queue
+  if( mDrawCommands.empty() && queueIndex > 0 )
+  {
+    return;
+  }
+
+  // Prepare commands
+  std::vector<DevelRenderer::DrawCommand*> commands;
+  for( auto& cmd : mDrawCommands )
+  {
+    if(cmd.queue == queueIndex)
+    {
+      commands.emplace_back( &cmd );
+    }
+  }
+
+  // Have commands but nothing to be drawn - abort
+  if(!mDrawCommands.empty() && commands.empty())
+  {
+    return;
+  }
+
   // Get the program to use:
   Program* program = mRenderDataProvider->GetShader().GetProgram();
   if( !program )
@@ -589,9 +617,6 @@ void Renderer::Render( Context& context,
     context.CullFace( mFaceCullingMode );
   }
 
-  //Set blending mode
-  SetBlending( context, blend );
-
   // Take the program into use so we can send uniforms to it
   program->Use();
 
@@ -626,12 +651,29 @@ void Renderer::Render( Context& context,
       mUpdateAttributesLocation = false;
     }
 
-    mGeometry->Draw( context,
-                     bufferIndex,
-                     mAttributesLocation,
-                     mIndexedDrawFirstElement,
-                     mIndexedDrawElementsCount );
+    if(mDrawCommands.empty())
+    {
+      SetBlending( context, blend );
 
+      mGeometry->Draw( context,
+                       bufferIndex,
+                       mAttributesLocation,
+                       mIndexedDrawFirstElement,
+                       mIndexedDrawElementsCount );
+    }
+    else
+    {
+      for(auto& cmd : commands )
+      {
+        if(cmd->queue == queueIndex )
+        {
+          //Set blending mode
+          SetBlending(context, cmd->queue == DevelRenderer::RENDER_QUEUE_OPAQUE ? false : blend);
+          mGeometry->Draw(context, bufferIndex, mAttributesLocation,
+                          cmd->firstIndex, cmd->elementCount);
+        }
+      }
+    }
     mUpdated = false;
   }
 }
index fccf372..5f4bbfb 100755 (executable)
@@ -155,6 +155,17 @@ public:
    */
   void SetGeometry( Render::Geometry* geometry );
 
+  void SetDrawCommands( Dali::DevelRenderer::DrawCommand* pDrawCommands, uint32_t size );
+
+  /**
+   * @brief Returns a reference to an array of draw commands
+   * @return Valid array of draw commands (may be empty)
+   */
+  const std::vector<Dali::DevelRenderer::DrawCommand>& GetDrawCommands() const
+  {
+    return mDrawCommands;
+  }
+
   /**
    * Second-phase construction.
    * This is called when the renderer is inside render thread
@@ -366,8 +377,8 @@ public:
                const Vector3& size,
                bool blend,
                Vector<GLuint>& boundTextures,
-               const Dali::Internal::SceneGraph::RenderInstruction& instruction //for reflection effect
-               );
+               const Dali::Internal::SceneGraph::RenderInstruction& instruction,
+               uint32_t queueIndex );
 
   /**
    * Write the renderer's sort attributes to the passed in reference
@@ -471,6 +482,7 @@ private:
   bool                         mUpdated:1;
 
 
+  std::vector<Dali::DevelRenderer::DrawCommand> mDrawCommands; // Devel stuff
 };
 
 } // namespace SceneGraph
index ad0974b..ffb71a9 100644 (file)
@@ -114,6 +114,7 @@ enum Flags
   RESEND_STENCIL_OPERATION_ON_Z_PASS = 1 << 17,
   RESEND_WRITE_TO_COLOR_BUFFER       = 1 << 18,
   RESEND_SHADER                      = 1 << 19,
+  RESEND_DRAW_COMMANDS               = 1 << 20
 };
 
 } // Anonymous namespace
@@ -223,6 +224,13 @@ void Renderer::PrepareRender( BufferIndex updateBufferIndex )
       new (slot) DerivedType( mRenderer, &Render::Renderer::SetGeometry, mGeometry );
     }
 
+    if( mResendFlag & RESEND_DRAW_COMMANDS )
+    {
+      typedef MessageValue2< Render::Renderer, Dali::DevelRenderer::DrawCommand*, uint32_t > DerivedType;
+      uint32_t* slot = mSceneController->GetRenderQueue().ReserveMessageSlot( updateBufferIndex, sizeof( DerivedType ) );
+      new (slot) DerivedType( mRenderer, &Render::Renderer::SetDrawCommands, mDrawCommands.data(), mDrawCommands.size() );
+    }
+
     if( mResendFlag & RESEND_FACE_CULLING_MODE )
     {
       typedef MessageValue1< Render::Renderer, FaceCullingMode::Type > DerivedType;
@@ -763,6 +771,13 @@ void Renderer::ObservedObjectDestroyed(PropertyOwner& owner)
   }
 }
 
+void Renderer::SetDrawCommands( Dali::DevelRenderer::DrawCommand* pDrawCommands, uint32_t size )
+{
+  mDrawCommands.clear();
+  mDrawCommands.insert( mDrawCommands.end(), pDrawCommands, pDrawCommands+size );
+  mResendFlag |= RESEND_DRAW_COMMANDS;
+}
+
 } // namespace SceneGraph
 } // namespace Internal
 } // namespace Dali
index 508b4af..a9c1c1e 100755 (executable)
@@ -432,6 +432,8 @@ public: // From UniformMapDataProvider
    */
   virtual const CollectedUniformMap& GetUniformMap( BufferIndex bufferIndex ) const;
 
+  void SetDrawCommands( Dali::DevelRenderer::DrawCommand* pDrawCommands, uint32_t size );
+
 private:
 
   /**
@@ -473,6 +475,8 @@ private:
   bool                         mUniformMapChanged[2];             ///< Records if the uniform map has been altered this frame
   bool                         mPremultipledAlphaEnabled:1;       ///< Flag indicating whether the Pre-multiplied Alpha Blending is required
 
+  std::vector<Dali::DevelRenderer::DrawCommand> mDrawCommands;
+
 public:
 
   AnimatableProperty< float >  mOpacity;                          ///< The opacity value
@@ -726,6 +730,17 @@ inline void SetRenderingBehaviorMessage( EventThreadServices& eventThreadService
   new (slot) LocalType( &renderer, &Renderer::SetRenderingBehavior, renderingBehavior );
 }
 
+inline void SetDrawCommandsMessage( EventThreadServices& eventThreadServices, const Renderer& renderer, Dali::DevelRenderer::DrawCommand* pDrawCommands, uint32_t size )
+{
+  typedef MessageValue2< Renderer, Dali::DevelRenderer::DrawCommand*, uint32_t > LocalType;
+
+  // Reserve some memory inside the message queue
+  uint32_t* slot = eventThreadServices.ReserveMessageSlot( sizeof( LocalType ) );
+
+  new (slot) LocalType( &renderer, &Renderer::SetDrawCommands, pDrawCommands, size );
+}
+
+
 } // namespace SceneGraph
 } // namespace Internal
 } // namespace Dali