Geometry Batching 59/80559/20
authoradam.b <adam.b@samsung.com>
Tue, 26 Jul 2016 17:33:21 +0000 (18:33 +0100)
committerTom Robinson <tom.robinson@samsung.com>
Wed, 3 Aug 2016 15:09:52 +0000 (08:09 -0700)
DALi support to batch geometry under certain conditions ( like using same texture atlas, shaders ).

Known limitations:
- Batching, once it's on, is immutable
- Batches update only, when something changes in the hierarchy
- Actors within a batch share the same uniforms set ( share values like opacity, color etc. ).
- Transformations applied to actors within a batch won't have any visual effect.

Change-Id: Id48aa47397b8ff9169dc044f37e4de2341a6b169

39 files changed:
automated-tests/src/dali-internal/CMakeLists.txt
automated-tests/src/dali-internal/utc-Dali-Internal-GeometryBatcher.cpp [new file with mode: 0644]
dali/internal/common/core-impl.cpp
dali/internal/common/core-impl.h
dali/internal/common/math.cpp
dali/internal/common/math.h
dali/internal/event/actors/actor-impl.cpp
dali/internal/event/actors/actor-impl.h
dali/internal/event/rendering/renderer-impl.cpp
dali/internal/event/rendering/renderer-impl.h
dali/internal/file.list
dali/internal/render/common/render-algorithms.cpp
dali/internal/render/common/render-algorithms.h
dali/internal/render/common/render-item.cpp
dali/internal/render/common/render-item.h
dali/internal/render/common/render-manager.cpp
dali/internal/render/common/render-manager.h
dali/internal/render/renderers/render-geometry.cpp
dali/internal/render/renderers/render-geometry.h
dali/internal/render/renderers/render-property-buffer.cpp
dali/internal/render/renderers/render-property-buffer.h
dali/internal/render/renderers/render-renderer.cpp
dali/internal/render/renderers/render-renderer.h
dali/internal/update/common/discard-queue.cpp
dali/internal/update/common/discard-queue.h
dali/internal/update/manager/geometry-batcher.cpp [new file with mode: 0644]
dali/internal/update/manager/geometry-batcher.h [new file with mode: 0644]
dali/internal/update/manager/prepare-render-instructions.cpp
dali/internal/update/manager/prepare-render-instructions.h
dali/internal/update/manager/process-render-tasks.cpp
dali/internal/update/manager/process-render-tasks.h
dali/internal/update/manager/update-manager.cpp
dali/internal/update/manager/update-manager.h
dali/internal/update/nodes/node.cpp
dali/internal/update/nodes/node.h
dali/internal/update/rendering/scene-graph-renderer.cpp
dali/internal/update/rendering/scene-graph-renderer.h
dali/public-api/actors/actor.h
dali/public-api/rendering/renderer.h

index 4bc9f10..afedb0d 100644 (file)
@@ -12,6 +12,7 @@ SET(TC_SOURCES
         utc-Dali-Internal-FixedSizeMemoryPool.cpp
         utc-Dali-Internal-MemoryPoolObjectAllocator.cpp
         utc-Dali-Internal-FrustumCulling.cpp
+        utc-Dali-Internal-GeometryBatcher.cpp
 )
 
 LIST(APPEND TC_SOURCES
diff --git a/automated-tests/src/dali-internal/utc-Dali-Internal-GeometryBatcher.cpp b/automated-tests/src/dali-internal/utc-Dali-Internal-GeometryBatcher.cpp
new file mode 100644 (file)
index 0000000..f2c679f
--- /dev/null
@@ -0,0 +1,403 @@
+#include <dali/public-api/dali-core.h>
+#include <dali/internal/event/actors/actor-impl.h>
+#include <dali/internal/update/manager/geometry-batcher.h>
+
+#include <dali-test-suite-utils.h>
+
+namespace
+{
+
+class MockActor : public Dali::Internal::Actor
+{
+public:
+  inline const Dali::Internal::SceneGraph::Node* GetNode()
+  {
+    return mNode;
+  }
+
+  inline const bool IsNodeBatchParent()
+  {
+    return mNode->mIsBatchParent;
+  }
+};
+
+Geometry CreateBatchQuadGeometryVector2( Vector4 texCoords )
+{
+  const float halfWidth = 0.5f;
+  const float halfHeight = 0.5f;
+  struct QuadVertex {
+    QuadVertex( const Vector2& vertexPosition, const Vector2& vertexTexCoords )
+      : position( vertexPosition ),
+        texCoords( vertexTexCoords )
+    {}
+    Vector2 position;
+    Vector2 texCoords;
+  };
+
+  // special case, when texture takes whole space
+  if( texCoords == Vector4::ZERO )
+  {
+    texCoords = Vector4(0.0f, 0.0f, 1.0f, 1.0f);
+  }
+
+  QuadVertex quadVertexData[6] =
+  {
+    QuadVertex( Vector2(-halfWidth,   -halfHeight ),   Vector2(texCoords.x, texCoords.y) ),
+    QuadVertex( Vector2( halfWidth,   -halfHeight ),   Vector2(texCoords.z, texCoords.y) ),
+    QuadVertex( Vector2(-halfWidth,    halfHeight ),   Vector2(texCoords.x, texCoords.w) ),
+    QuadVertex( Vector2( halfWidth,   -halfHeight ),   Vector2(texCoords.z, texCoords.y) ),
+    QuadVertex( Vector2(-halfWidth,    halfHeight ),   Vector2(texCoords.x, texCoords.w) ),
+    QuadVertex( Vector2( halfWidth,    halfHeight ),   Vector2(texCoords.z, texCoords.w) ),
+  };
+
+  Property::Map vertexFormat;
+  vertexFormat[ "aPosition" ] = Property::VECTOR2;
+  vertexFormat[ "aTexCoord" ] = Property::VECTOR2;
+  PropertyBuffer vertexBuffer = PropertyBuffer::New( vertexFormat );
+  vertexBuffer.SetData( quadVertexData, 6 );
+
+  // create geometry as normal, single quad
+  Geometry geometry = Geometry::New();
+  geometry.AddVertexBuffer( vertexBuffer );
+  geometry.SetType( Geometry::TRIANGLES );
+
+  return geometry;
+}
+
+Geometry CreateBatchQuadGeometryVector3( Vector4 texCoords )
+{
+  const float halfWidth = 0.5f;
+  const float halfHeight = 0.5f;
+  struct QuadVertex {
+    QuadVertex( const Vector3& vertexPosition, const Vector2& vertexTexCoords )
+      : position( vertexPosition ),
+        texCoords( vertexTexCoords )
+    {}
+    Vector3 position;
+    Vector2 texCoords;
+  };
+
+  // special case, when texture takes whole space
+  if( texCoords == Vector4::ZERO )
+  {
+    texCoords = Vector4(0.0f, 0.0f, 1.0f, 1.0f);
+  }
+
+  QuadVertex quadVertexData[6] =
+  {
+    QuadVertex( Vector3(-halfWidth,   -halfHeight, 0.0f ),   Vector2(texCoords.x, texCoords.y) ),
+    QuadVertex( Vector3( halfWidth,   -halfHeight, 0.0f ),   Vector2(texCoords.z, texCoords.y) ),
+    QuadVertex( Vector3(-halfWidth,    halfHeight, 0.0f ),   Vector2(texCoords.x, texCoords.w) ),
+    QuadVertex( Vector3( halfWidth,   -halfHeight, 0.0f ),   Vector2(texCoords.z, texCoords.y) ),
+    QuadVertex( Vector3(-halfWidth,    halfHeight, 0.0f ),   Vector2(texCoords.x, texCoords.w) ),
+    QuadVertex( Vector3( halfWidth,    halfHeight, 0.0f ),   Vector2(texCoords.z, texCoords.w) ),
+  };
+
+  Property::Map vertexFormat;
+  vertexFormat[ "aPosition" ] = Property::VECTOR3;
+  vertexFormat[ "aTexCoord" ] = Property::VECTOR2;
+  PropertyBuffer vertexBuffer = PropertyBuffer::New( vertexFormat );
+  vertexBuffer.SetData( quadVertexData, 6 );
+
+  // create geometry as normal, single quad
+  Geometry geometry = Geometry::New();
+  geometry.AddVertexBuffer( vertexBuffer );
+  geometry.SetType( Geometry::TRIANGLES );
+  return geometry;
+}
+
+Geometry CreateBatchQuadGeometryVector4( Vector4 texCoords )
+{
+  const float halfWidth = 0.5f;
+  const float halfHeight = 0.5f;
+  struct QuadVertex {
+    QuadVertex( const Vector4& vertexPosition, const Vector2& vertexTexCoords )
+      : position( vertexPosition ),
+        texCoords( vertexTexCoords )
+    {}
+    Vector4 position;
+    Vector2 texCoords;
+  };
+
+  // special case, when texture takes whole space
+  if( texCoords == Vector4::ZERO )
+  {
+    texCoords = Vector4(0.0f, 0.0f, 1.0f, 1.0f);
+  }
+
+  QuadVertex quadVertexData[6] =
+  {
+    QuadVertex( Vector4(-halfWidth,   -halfHeight, 0.0f, 1.0f ),   Vector2(texCoords.x, texCoords.y) ),
+    QuadVertex( Vector4( halfWidth,   -halfHeight, 0.0f, 1.0f ),   Vector2(texCoords.z, texCoords.y) ),
+    QuadVertex( Vector4(-halfWidth,    halfHeight, 0.0f, 1.0f ),   Vector2(texCoords.x, texCoords.w) ),
+    QuadVertex( Vector4( halfWidth,   -halfHeight, 0.0f, 1.0f ),   Vector2(texCoords.z, texCoords.y) ),
+    QuadVertex( Vector4(-halfWidth,    halfHeight, 0.0f, 1.0f ),   Vector2(texCoords.x, texCoords.w) ),
+    QuadVertex( Vector4( halfWidth,    halfHeight, 0.0f, 1.0f ),   Vector2(texCoords.z, texCoords.w) ),
+  };
+
+  Property::Map vertexFormat;
+  vertexFormat[ "aPosition" ] = Property::VECTOR4;
+  vertexFormat[ "aTexCoord" ] = Property::VECTOR2;
+  PropertyBuffer vertexBuffer = PropertyBuffer::New( vertexFormat );
+  vertexBuffer.SetData( quadVertexData, 6 );
+
+  // create geometry as normal, single quad
+  Geometry geometry = Geometry::New();
+  geometry.AddVertexBuffer( vertexBuffer );
+  geometry.SetType( Geometry::TRIANGLES );
+  return geometry;
+}
+
+
+void CreateShadersAndTextureSets( Shader* shaders, size_t shaderCount, TextureSet* textureSets, size_t textureSetCount )
+{
+  for( size_t i = 0; i < shaderCount; ++i )
+  {
+    shaders[i] = Shader::New( "", "" );
+  }
+
+  for( size_t i = 0; i < textureSetCount; ++i )
+  {
+    textureSets[i] = TextureSet::New();
+  }
+}
+
+Vector4 WHOLE_IMAGE( 0.0f, 0.0f, 1.0f, 1.0f );
+
+Actor CreateActor( Actor& parent, Shader& shader, TextureSet& textureSet, Vector3 position, Vector4 texCoords, Geometry(*geomfunc)(Vector4) = CreateBatchQuadGeometryVector2 )
+{
+  Geometry geometry = geomfunc( texCoords );
+  Renderer renderer = Renderer::New( geometry, shader );
+
+  renderer.SetTextures( textureSet );
+  renderer.SetProperty( Dali::Renderer::Property::BATCHING_ENABLED, true );
+
+  Actor actor = Actor::New();
+  actor.SetPosition( position );
+  parent.Add( actor );
+  actor.AddRenderer( renderer );
+  return actor;
+}
+
+Actor CreateActor( Vector3 position )
+{
+  Actor actor = Actor::New();
+  actor.SetPosition( position );
+  return actor;
+}
+
+
+Actor CreateBatchParent( Vector3 pos )
+{
+  Actor actor = Actor::New();
+  actor.SetProperty( Actor::Property::BATCH_PARENT, true );
+  actor.SetPosition( pos );
+  Stage::GetCurrent().Add( actor );
+  return actor;
+}
+
+void AddChildren( Actor parent, Actor* children, size_t size )
+{
+  for( size_t i = 0; i < size; ++i )
+  {
+    parent.Add( children[i] );
+  }
+}
+
+}
+
+int UtcDaliGeometryBatcherBatchLevel0(void)
+{
+  TestApplication app;
+  TestGlAbstraction& glAbstraction = app.GetGlAbstraction();
+  glAbstraction.EnableDrawCallTrace( true );
+  TraceCallStack& drawTrace = glAbstraction.GetDrawTrace();
+
+  Shader shaders[1];
+  TextureSet textureSets[1];
+
+  CreateShadersAndTextureSets( shaders, 1, textureSets, 1 );
+  Actor batchParent = CreateBatchParent( Vector3::ZERO );
+  batchParent.SetSize( Stage::GetCurrent().GetSize() );
+
+  Actor children[] = {
+    CreateActor( batchParent, shaders[0], textureSets[0], Vector3( 0.0f, 0.0f, 0.0f ), WHOLE_IMAGE ),
+    CreateActor( batchParent, shaders[0], textureSets[0], Vector3( 10.0f, 0.0f, 0.0f ), WHOLE_IMAGE ),
+    CreateActor( batchParent, shaders[0], textureSets[0], Vector3( 20.0f, 0.0f, 0.0f ), WHOLE_IMAGE ),
+    CreateActor( batchParent, shaders[0], textureSets[0], Vector3( 30.0f, 0.0f, 0.0f ), WHOLE_IMAGE )
+  };
+
+  app.SendNotification();
+  app.Render( 16 );
+  app.SendNotification();
+  app.Render( 16 );
+
+  // should be 1 draw call
+  {
+    int result = drawTrace.CountMethod( "DrawElements");
+    DALI_TEST_CHECK( result == 1 );
+  }
+
+  // remove actor
+  batchParent.Remove( children[0] );
+  children[0].Reset();
+
+  // update
+  app.SendNotification();
+  app.Render( 16 );
+
+  // test geometry for that batch, 1 batch, 3 children, 18 elements in the buffer
+  //Dali::Internal::Actor& actor = GetImplementation( children[1] );
+  MockActor* mockActor = static_cast<MockActor*>( &GetImplementation( children[1] ) );
+  Dali::Internal::SceneGraph::GeometryBatcher* geometryBatcher = mockActor->GetNode()->mGeometryBatcher;
+  DALI_TEST_CHECK( geometryBatcher ); // must not be NULL
+
+  Dali::Internal::Render::Geometry* geometry = geometryBatcher->GetGeometry( 0 );
+  int elementCount = geometry->GetPropertyBuffer(0)->GetElementCount();
+  DALI_TEST_CHECK( elementCount == 18 );
+
+  // delete batch parent
+  Stage::GetCurrent().Remove( batchParent );
+  batchParent.Reset();
+
+  // update
+  app.SendNotification();
+  app.Render( 16 );
+
+  END_TEST;
+}
+
+int UtcDaliGeometryBatcherBatchMultipleTextureSet(void)
+{
+  TestApplication app;
+  TestGlAbstraction& glAbstraction = app.GetGlAbstraction();
+  glAbstraction.EnableDrawCallTrace( true );
+  TraceCallStack& drawTrace = glAbstraction.GetDrawTrace();
+
+  Shader shaders[1];
+  TextureSet textureSets[3];
+  CreateShadersAndTextureSets( shaders, 1, textureSets, 3 );
+
+  Actor batchParent = CreateBatchParent( Vector3::ZERO );
+  batchParent.SetSize( Stage::GetCurrent().GetSize() );
+
+  Actor children[] = {
+    CreateActor( batchParent, shaders[0], textureSets[0], Vector3( 0.0f, 0.0f, 0.0f ), WHOLE_IMAGE ),
+    CreateActor( batchParent, shaders[0], textureSets[0], Vector3( 10.0f, 0.0f, 0.0f ), WHOLE_IMAGE ),
+    CreateActor( batchParent, shaders[0], textureSets[0], Vector3( 20.0f, 0.0f, 0.0f ), WHOLE_IMAGE ),
+    CreateActor( batchParent, shaders[0], textureSets[0], Vector3( 30.0f, 0.0f, 0.0f ), WHOLE_IMAGE ),
+    CreateActor( batchParent, shaders[0], textureSets[1], Vector3( 0.0f, 0.0f, 0.0f ), WHOLE_IMAGE ),
+    CreateActor( batchParent, shaders[0], textureSets[2], Vector3( 10.0f, 0.0f, 0.0f ), WHOLE_IMAGE ),
+    CreateActor( batchParent, shaders[0], textureSets[2], Vector3( 20.0f, 0.0f, 0.0f ), WHOLE_IMAGE ),
+    CreateActor( batchParent, shaders[0], textureSets[1], Vector3( 30.0f, 0.0f, 0.0f ), WHOLE_IMAGE ),
+    CreateActor( batchParent, shaders[0], textureSets[1], Vector3( 0.0f, 0.0f, 0.0f ), WHOLE_IMAGE ),
+    CreateActor( batchParent, shaders[0], textureSets[1], Vector3( 10.0f, 0.0f, 0.0f ), WHOLE_IMAGE ),
+    CreateActor( batchParent, shaders[0], textureSets[2], Vector3( 20.0f, 0.0f, 0.0f ), WHOLE_IMAGE ),
+    CreateActor( batchParent, shaders[0], textureSets[2], Vector3( 30.0f, 0.0f, 0.0f ), WHOLE_IMAGE )
+  };
+
+  // must update twice
+  app.SendNotification();
+  app.Render( 16 );
+  app.SendNotification();
+  app.Render( 16 );
+
+  // should be 3 draw calls here
+  {
+    int result = drawTrace.CountMethod( "DrawElements");
+    DALI_TEST_CHECK( result == 3 );
+  }
+
+  // test assigned indices
+  {
+    bool indicesTest( true );
+    for( size_t i = 0; i < 12; ++i )
+    {
+      MockActor* mockActor = static_cast<MockActor*>( &GetImplementation( children[1] ) );
+      if( mockActor->GetNode()->mBatchIndex == BATCH_NULL_HANDLE )
+      {
+        indicesTest = false;
+      }
+    }
+    DALI_TEST_CHECK( indicesTest );
+  }
+
+  END_TEST;
+}
+
+int UtcDaliGeometryBatcherSettingBatchParent(void)
+{
+  TestApplication app;
+
+  Shader shaders[1];
+  TextureSet textureSets[1];
+  CreateShadersAndTextureSets( shaders, 1, textureSets, 1 );
+
+  Actor batchParent = CreateBatchParent( Vector3::ZERO );
+  batchParent.SetSize( Stage::GetCurrent().GetSize() );
+  app.SendNotification();
+  app.Render( 16 );
+
+  MockActor* mockActor = static_cast<MockActor*>( &GetImplementation( batchParent ) );
+  DALI_TEST_CHECK( mockActor->IsNodeBatchParent() );
+
+  END_TEST;
+}
+
+int UtcDaliGeometryBatcherBatchMultipleParents(void)
+{
+  TestApplication app;
+  TestGlAbstraction& glAbstraction = app.GetGlAbstraction();
+  glAbstraction.EnableDrawCallTrace( true );
+  TraceCallStack& drawTrace = glAbstraction.GetDrawTrace();
+
+  Shader shaders[2];
+  TextureSet textureSets[2];
+  CreateShadersAndTextureSets( shaders, 2, textureSets, 2 );
+
+  Actor batchParent0 = CreateBatchParent( Vector3::ZERO ); // Vector2
+  batchParent0.SetSize( Stage::GetCurrent().GetSize() );
+  Actor batchParent1 = CreateBatchParent( Vector3::ZERO ); // Vector3
+  batchParent1.SetSize( Stage::GetCurrent().GetSize() );
+  Actor batchParent2 = CreateBatchParent( Vector3::ZERO ); // Vector4
+  batchParent2.SetSize( Stage::GetCurrent().GetSize() );
+
+  CreateActor( batchParent0, shaders[0], textureSets[0], Vector3( 0.0f, 0.0f, 0.0f ), WHOLE_IMAGE );
+  CreateActor( batchParent0, shaders[0], textureSets[0], Vector3( 10.0f, 0.0f, 0.0f ), WHOLE_IMAGE );
+  CreateActor( batchParent0, shaders[0], textureSets[0], Vector3( 20.0f, 0.0f, 0.0f ), WHOLE_IMAGE );
+  CreateActor( batchParent0, shaders[0], textureSets[0], Vector3( 30.0f, 0.0f, 0.0f ), WHOLE_IMAGE );
+  CreateActor( batchParent0, shaders[0], textureSets[0], Vector3( 0.0f, 0.0f, 0.0f ), WHOLE_IMAGE );
+  CreateActor( batchParent1, shaders[1], textureSets[1], Vector3( 0.0f, 0.0f, 0.0f ), WHOLE_IMAGE,  CreateBatchQuadGeometryVector3 );
+  CreateActor( batchParent1, shaders[1], textureSets[1], Vector3( 10.0f, 0.0f, 0.0f ), WHOLE_IMAGE, CreateBatchQuadGeometryVector3 );
+  CreateActor( batchParent1, shaders[1], textureSets[1], Vector3( 20.0f, 0.0f, 0.0f ), WHOLE_IMAGE, CreateBatchQuadGeometryVector3 );
+  CreateActor( batchParent2, shaders[0], textureSets[1], Vector3( 30.0f, 0.0f, 0.0f ), WHOLE_IMAGE, CreateBatchQuadGeometryVector4 );
+  CreateActor( batchParent2, shaders[0], textureSets[1], Vector3( 0.0f, 0.0f, 0.0f ), WHOLE_IMAGE,  CreateBatchQuadGeometryVector4 );
+  CreateActor( batchParent2, shaders[0], textureSets[1], Vector3( 30.0f, 0.0f, 0.0f ), WHOLE_IMAGE, CreateBatchQuadGeometryVector4 );
+  CreateActor( batchParent2, shaders[0], textureSets[1], Vector3( 0.0f, 0.0f, 0.0f ), WHOLE_IMAGE,  CreateBatchQuadGeometryVector4 );
+
+  // must update twice
+  app.SendNotification();
+  app.Render( 16 );
+  app.SendNotification();
+  app.Render( 16 );
+
+  // should be 3 draw calls here
+  {
+    int result = drawTrace.CountMethod( "DrawElements");
+    DALI_TEST_EQUALS( result, 3, TEST_LOCATION );
+  }
+
+  // delete batch parent
+  Stage::GetCurrent().Remove( batchParent1 );
+  batchParent1.Reset();
+  drawTrace.Reset();
+  app.SendNotification();
+  app.Render( 16 );
+  // should be 2 draw calls here
+  {
+    int result = drawTrace.CountMethod( "DrawElements");
+    DALI_TEST_EQUALS( result, 2, TEST_LOCATION );
+  }
+
+  END_TEST;
+}
index 313fa72..b415482 100644 (file)
@@ -44,6 +44,7 @@
 #include <dali/internal/update/common/discard-queue.h>
 #include <dali/internal/update/common/texture-cache-dispatcher.h>
 #include <dali/internal/update/manager/update-manager.h>
+#include <dali/internal/update/manager/geometry-batcher.h>
 #include <dali/internal/update/resources/resource-manager.h>
 
 #include <dali/internal/render/common/performance-monitor.h>
@@ -97,6 +98,7 @@ Core::Core( RenderController& renderController, PlatformAbstraction& platform,
   mNotificationManager(NULL),
   mImageFactory(NULL),
   mShaderFactory(NULL),
+  mGeometryBatcher( NULL ),
   mIsActive(true),
   mProcessingEvent(false)
 {
@@ -114,7 +116,9 @@ Core::Core( RenderController& renderController, PlatformAbstraction& platform,
 
   mTextureUploadedQueue = new LockedResourceQueue;
 
-  mRenderManager = RenderManager::New( glAbstraction, glSyncAbstraction, *mTextureUploadedQueue );
+  mGeometryBatcher = new SceneGraph::GeometryBatcher();
+
+  mRenderManager = RenderManager::New( glAbstraction, glSyncAbstraction, *mGeometryBatcher, *mTextureUploadedQueue );
 
   RenderQueue& renderQueue = mRenderManager->GetRenderQueue();
   TextureCache& textureCache = mRenderManager->GetTextureCache();
@@ -145,7 +149,8 @@ Core::Core( RenderController& renderController, PlatformAbstraction& platform,
                                        renderController,
                                       *mRenderManager,
                                        renderQueue,
-                                      *mTextureCacheDispatcher );
+                                      *mTextureCacheDispatcher,
+                                      *mGeometryBatcher );
 
   mRenderManager->SetShaderSaver( *mUpdateManager );
 
@@ -210,6 +215,7 @@ Core::~Core()
   delete mTextureCacheDispatcher;
   delete mUpdateManager;
   delete mRenderManager;
+  delete mGeometryBatcher;
   delete mTextureUploadedQueue;
 }
 
index b9472cd..d40f827 100644 (file)
@@ -66,6 +66,7 @@ class UpdateManager;
 class RenderManager;
 class DiscardQueue;
 class TextureCacheDispatcher;
+class GeometryBatcher;
 }
 
 /**
@@ -294,7 +295,7 @@ private:
   ResourceClient*                           mResourceClient;              ///< Asynchronous Resource Loading
   ResourceManager*                          mResourceManager;             ///< Asynchronous Resource Loading
   IntrusivePtr< RelayoutController >        mRelayoutController;          ///< Size negotiation relayout controller
-
+  SceneGraph::GeometryBatcher*              mGeometryBatcher;             ///< Instance of the geometry batcher
   bool                                      mIsActive         : 1;        ///< Whether Core is active or suspended
   bool                                      mProcessingEvent  : 1;        ///< True during ProcessEvents()
 
index 1e3c051..3b0baf9 100644 (file)
@@ -57,3 +57,80 @@ float Dali::Internal::Length( const Vec3 v )
 {
   return sqrtf(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
 }
+
+void Dali::Internal::MultiplyVectorBySize( Vec2& result, const Vec2 v, const Size3 s )
+{
+  result[0] = v[0] * s[0];
+  result[1] = v[1] * s[1];
+}
+
+void Dali::Internal::MultiplyVectorBySize( Vec3& result, const Vec3 v, const Size3 s )
+{
+  result[0] = v[0] * s[0];
+  result[1] = v[1] * s[1];
+  result[2] = v[2] * s[2];
+}
+
+void Dali::Internal::MultiplyVectorBySize( Vec4& result, const Vec4 v, const Size3 s )
+{
+  result[0] = v[0] * s[0];
+  result[1] = v[1] * s[1];
+  result[2] = v[2] * s[2];
+  result[3] = 1.0f;
+}
+
+void Dali::Internal::MultiplyVectorByMatrix4( Vec2& result, const Mat4 m, const Vec2 v )
+{
+  result[0] = v[0] * m[0] + v[1] * m[4] + m[12];
+  result[1] = v[0] * m[1] + v[1] * m[5] + m[13];
+}
+
+void Dali::Internal::MultiplyVectorByMatrix4( Vec3& result, const Mat4 m, const Vec3 v )
+{
+  result[0] = v[0] * m[0] + v[1] * m[4] + v[2] * m[8]  +  m[12];
+  result[1] = v[0] * m[1] + v[1] * m[5] + v[2] * m[9]  +  m[13];
+  result[2] = v[0] * m[2] + v[1] * m[6] + v[2] * m[10] +  m[14];
+}
+
+void Dali::Internal::MultiplyVectorByMatrix4( Vec4& result, const Mat4 m, const Vec4 rhs )
+{
+  result[0] = rhs[0] * m[0] + rhs[1] * m[4] + rhs[2] * m[8]  +  rhs[3] * m[12];
+  result[1] = rhs[0] * m[1] + rhs[1] * m[5] + rhs[2] * m[9]  +  rhs[3] * m[13];
+  result[2] = rhs[0] * m[2] + rhs[1] * m[6] + rhs[2] * m[10] +  rhs[3] * m[14];
+  result[3] = rhs[0] * m[3] + rhs[1] * m[7] + rhs[2] * m[11] +  rhs[3] * m[15];
+}
+
+void Dali::Internal::MultiplyMatrices( float* result, const Mat4 lhs, const Mat4 rhs )
+{
+  for( int i=0; i < 4; i++ )
+  {
+    // i<<2 gives the first vector / column
+    int loc = i<<2;
+    int loc1 = loc + 1;
+    int loc2 = loc + 2;
+    int loc3 = loc + 3;
+    float value0 = lhs[loc];
+    float value1 = lhs[loc1];
+    float value2 = lhs[loc2];
+    float value3 = lhs[loc3];
+    result[loc]  = (value0 * rhs[0]) +
+                   (value1 * rhs[4]) +
+                   (value2 * rhs[8]) +
+                   (value3 * rhs[12]);
+
+    result[loc1] = (value0 * rhs[1]) +
+                   (value1 * rhs[5]) +
+                   (value2 * rhs[9]) +
+                   (value3 * rhs[13]);
+
+    result[loc2] = (value0 * rhs[2]) +
+                   (value1 * rhs[6]) +
+                   (value2 * rhs[10])+
+                   (value3 * rhs[14]);
+
+    result[loc3] = (value0 * rhs[3]) +
+                   (value1 * rhs[7]) +
+                   (value2 * rhs[11])+
+                   (value3 * rhs[15]);
+  }
+}
index ebb6291..4fce869 100644 (file)
@@ -24,9 +24,11 @@ namespace Dali
 namespace Internal
 {
 
+typedef float Vec2[2];
 typedef float Vec3[3];
 typedef float Vec4[4];
 typedef float Mat4[16];
+typedef Vec3  Size3;
 
 /**
  * @brief Applies a transformation matrix to a vector
@@ -45,6 +47,70 @@ void TransformVector3( Vec3 result, const Mat4 m, const Vec3 v );
  */
 float Length( const Vec3 v );
 
+/**
+ * @brief Transforms 2D vector by the Size3 and stores value in Vec2
+ *
+ * @param[out] result Vec2 type to store final value
+ * @param[in] v Vector to transform
+ * @param[in] s Size to transform with
+ */
+void MultiplyVectorBySize( Vec2& result, const Vec2 v, const Size3 s );
+
+/**
+ * @brief Transforms 3D vector by the Size3 and stores value in Vec3
+ *
+ * @param[out] result Vec3 type to store final value
+ * @param[in] v Vector to transform
+ * @param[in] s Size to transform with
+ */
+void MultiplyVectorBySize( Vec3& result, const Vec3 v, const Size3 s );
+
+/**
+ * @brief Transforms 4D vector by the Size3 and stores value in Vec4
+ *
+ * @param[out] result Vec4 type to store final value
+ * @param[in] v Vector to transform
+ * @param[in] s Size to transform with
+ */
+void MultiplyVectorBySize( Vec4& result, const Vec4 v, const Size3 s );
+
+/**
+ * @brief Multiplies 2D vector by the matrix
+ *
+ * @param[out] result Result of the multiplication
+ * @param[in] m Matrix to use
+ * @param[in] v Vector to multiply
+ */
+void MultiplyVectorByMatrix4( Vec2& result, const Mat4 m, const Vec2 v );
+
+/**
+ * @brief Multiplies 3D vector by the matrix
+ *
+ * @param[out] result Result of the multiplication
+ * @param[in] m Matrix to use
+ * @param[in] v Vector to multiply
+ */
+void MultiplyVectorByMatrix4( Vec3& result, const Mat4 m, const Vec3 v );
+
+/**
+ * @brief Multiplies 4D vector by the matrix
+ *
+ * @param[out] result Result of the multiplication
+ * @param[in] m Matrix to use
+ * @param[in] v Vector to multiply
+ */
+void MultiplyVectorByMatrix4( Vec4& result, const Mat4 m, const Vec4 v );
+
+/**
+ * @brief Multiplies two Mat4 matrices
+ *
+ * @param[out] result Result of multiplication
+ * @param[in] lhs Left-hand-side matrix to use
+ * @param[in] rhs Right-hand-side matrix to use
+ */
+void MultiplyMatrices( float* result, const Mat4 lhs, const Mat4 rhs );
+
+
 } // namespace Internal
 
 } // namespace Dali
index 5df7b5c..7b19757 100644 (file)
@@ -200,6 +200,7 @@ DALI_PROPERTY( "padding",           VECTOR4,  true,  false, false, Dali::Actor::
 DALI_PROPERTY( "minimumSize",       VECTOR2,  true,  false, false, Dali::Actor::Property::MINIMUM_SIZE )
 DALI_PROPERTY( "maximumSize",       VECTOR2,  true,  false, false, Dali::Actor::Property::MAXIMUM_SIZE )
 DALI_PROPERTY( "inheritPosition",   BOOLEAN,  true,  false, false, Dali::Actor::Property::INHERIT_POSITION )
+DALI_PROPERTY( "batchParent",          BOOLEAN,  true,  false, false, Dali::Actor::Property::BATCH_PARENT )
 DALI_PROPERTY_TABLE_END( DEFAULT_ACTOR_PROPERTY_START_INDEX )
 
 // Signals
@@ -1974,7 +1975,8 @@ Actor::Actor( DerivedType derivedType )
   mInheritScale( true ),
   mDrawMode( DrawMode::NORMAL ),
   mPositionInheritanceMode( Node::DEFAULT_POSITION_INHERITANCE_MODE ),
-  mColorMode( Node::DEFAULT_COLOR_MODE )
+  mColorMode( Node::DEFAULT_COLOR_MODE ),
+  mIsBatchParent( false )
 {
 }
 
@@ -2648,6 +2650,20 @@ void Actor::SetDefaultProperty( Property::Index index, const Property::Value& pr
       break;
     }
 
+    case Dali::Actor::Property::BATCH_PARENT:
+    {
+      bool value;
+
+      if( property.Get( value ) )
+      {
+        if( value != mIsBatchParent )
+        {
+          mIsBatchParent = value;
+          SetIsBatchParentMessage( GetEventThreadServices(), *mNode, mIsBatchParent );
+        }
+      }
+      break;
+    }
     default:
     {
       // this can happen in the case of a non-animatable default property so just do nothing
@@ -3139,6 +3155,12 @@ Property::Value Actor::GetDefaultProperty( Property::Index index ) const
       break;
     }
 
+    case Dali::Actor::Property::BATCH_PARENT:
+    {
+      value = mIsBatchParent;
+      break;
+    }
+
     default:
     {
       DALI_ASSERT_ALWAYS( false && "Actor Property index invalid" ); // should not come here
index d38493b..d40bde1 100644 (file)
@@ -1833,6 +1833,8 @@ private:
   static ActorContainer mNullChildren;  ///< Empty container (shared by all actors, returned by GetChildren() const)
   static unsigned int mActorCounter;    ///< A counter to track the actor instance creation
 
+  bool mIsBatchParent : 1;              ///< Flag indicating that the actor is a batch parent
+
 };
 
 } // namespace Internal
index 7ee14a0..0b89b26 100644 (file)
@@ -65,6 +65,7 @@ DALI_PROPERTY( "stencilOperationOnFail",          INTEGER,   true, false,  false
 DALI_PROPERTY( "stencilOperationOnZFail",         INTEGER,   true, false,  false, Dali::Renderer::Property::STENCIL_OPERATION_ON_Z_FAIL )
 DALI_PROPERTY( "stencilOperationOnZPass",         INTEGER,   true, false,  false, Dali::Renderer::Property::STENCIL_OPERATION_ON_Z_PASS )
 DALI_PROPERTY( "writeToColorBuffer",              BOOLEAN,   true, false,  false, Dali::Renderer::Property::WRITE_TO_COLOR_BUFFER )
+DALI_PROPERTY( "batchingEnabled",                 BOOLEAN,   true, false,  false, Dali::Renderer::Property::BATCHING_ENABLED )
 DALI_PROPERTY_TABLE_END( DEFAULT_RENDERER_PROPERTY_START_INDEX )
 
 // Property string to enumeration tables:
@@ -347,6 +348,11 @@ bool Renderer::IsPreMultipliedAlphaEnabled() const
   return mPremultipledAlphaEnabled;
 }
 
+bool Renderer::IsBatchingEnabled() const
+{
+  return mBatchingEnabled;
+}
+
 SceneGraph::Renderer* Renderer::GetRendererSceneObject()
 {
   return mSceneObject;
@@ -657,6 +663,19 @@ void Renderer::SetDefaultProperty( Property::Index index,
       }
       break;
     }
+    case Dali::Renderer::Property::BATCHING_ENABLED:
+    {
+      bool enabled;
+      if( propertyValue.Get( enabled ) )
+      {
+        if( mBatchingEnabled != enabled )
+        {
+          mBatchingEnabled = enabled;
+          SetBatchingEnabledMessage( GetEventThreadServices(), *mSceneObject, mBatchingEnabled );
+        }
+      }
+      break;
+    }
   }
 }
 
@@ -770,6 +789,11 @@ Property::Value Renderer::GetDefaultProperty( Property::Index index ) const
       value = mDepthWriteMode;
       break;
     }
+    case Dali::Renderer::Property::BATCHING_ENABLED:
+    {
+      value = mBatchingEnabled;
+      break;
+    }
     case Dali::Renderer::Property::DEPTH_FUNCTION:
     {
       value = mDepthFunction;
@@ -916,7 +940,8 @@ Renderer::Renderer()
   mDepthWriteMode( DepthWriteMode::AUTO ),
   mDepthTestMode( DepthTestMode::AUTO ),
   mWriteToColorBuffer( true ),
-  mPremultipledAlphaEnabled( false )
+  mPremultipledAlphaEnabled( false ),
+  mBatchingEnabled( false )
 {
 }
 
index 745159b..14a2b79 100644 (file)
@@ -171,12 +171,18 @@ public:
     */
    bool IsPreMultipliedAlphaEnabled() const;
 
-  /**
-   * @brief Get the scene graph object
-   *
-   * @return the scene object
-   */
-  SceneGraph::Renderer* GetRendererSceneObject();
+   /**
+    * Returns state of batching mode
+    * @return batching mode state ( true if enabled )
+    */
+   bool IsBatchingEnabled() const;
+
+   /**
+    * @brief Get the scene graph object
+    *
+    * @return the scene object
+    */
+   SceneGraph::Renderer* GetRendererSceneObject();
 
 public: // Default property extensions from Object
 
@@ -314,6 +320,7 @@ private: // data
   DepthTestMode::Type          mDepthTestMode:2;              ///< Local copy of the depth test mode
   bool                         mWriteToColorBuffer:1;         ///< Local copy of the write to color buffer flag
   bool                         mPremultipledAlphaEnabled:1;   ///< Flag indicating whether the Pre-multiplied Alpha Blending is required
+  bool                         mBatchingEnabled : 1;          ///< Flag indicating whether render is batchable or not
 };
 
 } // namespace Internal
index 92a2e1f..e90d723 100644 (file)
@@ -147,6 +147,7 @@ internal_src_files = \
   $(internal_src_dir)/update/gestures/scene-graph-pan-gesture.cpp \
   $(internal_src_dir)/update/queue/update-message-queue.cpp \
   $(internal_src_dir)/update/manager/prepare-render-instructions.cpp \
+  $(internal_src_dir)/update/manager/geometry-batcher.cpp \
   $(internal_src_dir)/update/manager/process-render-tasks.cpp \
   $(internal_src_dir)/update/manager/transform-manager.cpp \
   $(internal_src_dir)/update/manager/update-algorithms.cpp \
index 95c0cae..02353f3 100644 (file)
 #include <dali/internal/render/gl-resources/context.h>
 #include <dali/internal/render/renderers/render-renderer.h>
 #include <dali/internal/update/nodes/scene-graph-layer.h>
+#include <dali/internal/update/manager/geometry-batcher.h>
 
 using Dali::Internal::SceneGraph::RenderItem;
 using Dali::Internal::SceneGraph::RenderList;
 using Dali::Internal::SceneGraph::RenderListContainer;
 using Dali::Internal::SceneGraph::RenderInstruction;
+using Dali::Internal::SceneGraph::GeometryBatcher;
 
 namespace Dali
 {
@@ -215,6 +217,7 @@ inline void SetupDepthBuffer( const RenderItem& item, Context& context, bool isL
  * @param[in] buffer The current render buffer index (previous update buffer)
  * @param[in] viewMatrix The view matrix from the appropriate camera.
  * @param[in] projectionMatrix The projection matrix from the appropriate camera.
+ * @param[in] geometryBatcher The instance of the geometry batcher
  */
 inline void ProcessRenderList(
   const RenderList& renderList,
@@ -223,7 +226,8 @@ inline void ProcessRenderList(
   SceneGraph::Shader& defaultShader,
   BufferIndex bufferIndex,
   const Matrix& viewMatrix,
-  const Matrix& projectionMatrix )
+  const Matrix& projectionMatrix,
+  GeometryBatcher* geometryBatcher )
 {
   DALI_PRINT_RENDER_LIST( renderList );
 
@@ -241,14 +245,34 @@ inline void ProcessRenderList(
   if( DALI_LIKELY( !renderList.HasColorRenderItems() || !depthTestEnabled ) )
   {
     size_t count = renderList.Count();
+    bool skip( false );
     for ( size_t index = 0; index < count; ++index )
     {
       const RenderItem& item = renderList.GetItem( index );
       DALI_PRINT_RENDER_ITEM( item );
 
       SetupPerRendererFlags( item, context, usedStencilBuffer, stencilManagedByDrawMode );
-      item.mRenderer->Render( context, textureCache, bufferIndex, *item.mNode, defaultShader,
-                              item.mModelMatrix, item.mModelViewMatrix, viewMatrix, projectionMatrix, item.mSize, !item.mIsOpaque );
+
+      // Check if the node has a valid batch index value ( set previously by
+      // GeometryBatcher ). If so, then it queries the geometry object for this particular batch.
+      // If not, it still checks if the batch parent is set as it is possible, batching may
+      // fail ( for example if vertex format or buffers are not set ). In that case we need
+      // to skip rendering, otherwise unwanted GPU buffers will get uploaded. This is very rare case.
+      uint32_t batchIndex = item.mNode->mBatchIndex;
+      if( batchIndex != BATCH_NULL_HANDLE )
+      {
+        item.mBatchRenderGeometry = geometryBatcher->GetGeometry( batchIndex );
+      }
+      else
+      {
+        skip = item.mNode->GetBatchParent();
+        item.mBatchRenderGeometry = NULL;
+      }
+      if( !skip )
+      {
+        item.mRenderer->Render( context, textureCache, bufferIndex, *item.mNode, defaultShader,
+                              item.mModelMatrix, item.mModelViewMatrix, viewMatrix, projectionMatrix, item.mSize, item.mBatchRenderGeometry, !item.mIsOpaque );
+      }
     }
   }
   else
@@ -264,7 +288,7 @@ inline void ProcessRenderList(
       SetupPerRendererFlags( item, context, usedStencilBuffer, stencilManagedByDrawMode );
 
       item.mRenderer->Render( context, textureCache, bufferIndex, *item.mNode, defaultShader,
-                              item.mModelMatrix, item.mModelViewMatrix, viewMatrix, projectionMatrix, item.mSize, !item.mIsOpaque );
+                              item.mModelMatrix, item.mModelViewMatrix, viewMatrix, projectionMatrix, item.mSize, item.mBatchRenderGeometry, !item.mIsOpaque );
     }
   }
 }
@@ -273,6 +297,7 @@ void ProcessRenderInstruction( const RenderInstruction& instruction,
                                Context& context,
                                SceneGraph::TextureCache& textureCache,
                                SceneGraph::Shader& defaultShader,
+                               GeometryBatcher& geometryBatcher,
                                BufferIndex bufferIndex )
 {
   DALI_PRINT_RENDER_INSTRUCTION( instruction, bufferIndex );
@@ -297,7 +322,7 @@ void ProcessRenderInstruction( const RenderInstruction& instruction,
       if(  renderList &&
           !renderList->IsEmpty() )
       {
-        ProcessRenderList( *renderList, context, textureCache, defaultShader, bufferIndex, *viewMatrix, *projectionMatrix );
+        ProcessRenderList( *renderList, context, textureCache, defaultShader, bufferIndex, *viewMatrix, *projectionMatrix, &geometryBatcher );
       }
     }
   }
index a2e4c57..1bf2dd0 100644 (file)
@@ -32,6 +32,7 @@ namespace SceneGraph
 class RenderInstruction;
 class Shader;
 class TextureCache;
+class GeometryBatcher;
 }
 
 namespace Render
@@ -43,12 +44,14 @@ namespace Render
  * @param[in] context The GL context.
  * @param[in] textureCache The texture cache used to get textures.
  * @param[in] defaultShader The default shader.
+ * @param[in] geometryBatcher The instace of geometry batcher.
  * @param[in] bufferIndex The current render buffer index (previous update buffer)
  */
 void ProcessRenderInstruction( const SceneGraph::RenderInstruction& instruction,
                                Context& context,
                                SceneGraph::TextureCache& textureCache,
                                SceneGraph::Shader& defaultShader,
+                               SceneGraph::GeometryBatcher& geometryBatcher,
                                BufferIndex bufferIndex );
 
 } // namespace Render
index d81f068..d7fd016 100644 (file)
@@ -47,8 +47,10 @@ RenderItem::RenderItem()
   mSize(),
   mRenderer( NULL ),
   mNode( NULL ),
+  mBatchRenderGeometry( NULL ),
   mDepthIndex( 0 ),
-  mIsOpaque( true )
+  mIsOpaque( true ),
+  mBatched( false )
 {
 }
 
index d429452..bd37bda 100644 (file)
@@ -33,6 +33,7 @@ namespace Internal
 namespace Render
 {
 class Renderer;
+class RenderGeometry;
 }
 
 namespace SceneGraph
@@ -59,14 +60,18 @@ struct RenderItem
    */
   void operator delete( void* ptr );
 
-
   Matrix            mModelMatrix;
   Matrix            mModelViewMatrix;
   Vector3           mSize;
   Render::Renderer* mRenderer;
   Node*             mNode;
+
+  mutable Render::Geometry* mBatchRenderGeometry;
+
   int               mDepthIndex;
   bool              mIsOpaque:1;
+  bool              mBatched:1;
+
 
 private:
 
index a4281eb..497b0e5 100644 (file)
@@ -81,7 +81,8 @@ struct RenderManager::Impl
   Impl( Integration::GlAbstraction& glAbstraction,
         Integration::GlSyncAbstraction& glSyncAbstraction,
         LockedResourceQueue& textureUploadedQ,
-        TextureUploadedDispatcher& postProcessDispatcher )
+        TextureUploadedDispatcher& postProcessDispatcher,
+        GeometryBatcher& geometryBatcher )
   : context( glAbstraction ),
     glSyncAbstraction( glSyncAbstraction ),
     renderQueue(),
@@ -99,7 +100,8 @@ struct RenderManager::Impl
     renderersAdded( false ),
     firstRenderCompleted( false ),
     defaultShader( NULL ),
-    programController( glAbstraction )
+    programController( glAbstraction ),
+    geometryBatcher( geometryBatcher )
   {
   }
 
@@ -168,14 +170,17 @@ struct RenderManager::Impl
   bool                          firstRenderCompleted;     ///< False until the first render is done
   Shader*                       defaultShader;            ///< Default shader to use
   ProgramController             programController;        ///< Owner of the GL programs
+
+  SceneGraph::GeometryBatcher&  geometryBatcher;          ///< Instance of geometry batcher
 };
 
 RenderManager* RenderManager::New( Integration::GlAbstraction& glAbstraction,
                                    Integration::GlSyncAbstraction& glSyncAbstraction,
+                                   SceneGraph::GeometryBatcher& geometryBatcher,
                                    LockedResourceQueue& textureUploadedQ )
 {
   RenderManager* manager = new RenderManager;
-  manager->mImpl = new Impl( glAbstraction, glSyncAbstraction, textureUploadedQ, *manager );
+  manager->mImpl = new Impl( glAbstraction, glSyncAbstraction, textureUploadedQ, *manager, geometryBatcher );
   return manager;
 }
 
@@ -685,6 +690,7 @@ void RenderManager::DoRender( RenderInstruction& instruction, Shader& defaultSha
                                     mImpl->context,
                                     mImpl->textureCache,
                                     defaultShader,
+                                    mImpl->geometryBatcher,
                                     mImpl->renderBufferIndex );
 
   if(instruction.mOffscreenTextureId != 0)
index 93b9661..40cc7c1 100644 (file)
@@ -63,6 +63,7 @@ class RenderInstruction;
 class RenderInstructionContainer;
 class Shader;
 class PropertyBufferDataProvider;
+class GeometryBatcher;
 
 /**
  * RenderManager is responsible for rendering the result of the previous "update", which
@@ -76,10 +77,12 @@ public:
    * Construct a new RenderManager.
    * @param[in]  glAbstraction The GL abstraction used for rendering.
    * @param[in]  glSyncAbstraction The GL sync abstraction used fence sync creation/deletion.
-   * @param[out] resourcePostProcessQueue A queue for sending rendered texture ids to the update-thread.
+   * @param[in]  geometryBatcher The geometry batcher instance
+   * @param[out] resourcePostProcessQueue A queue for sending rendered texture ids to the update-thread.*
    */
   static RenderManager* New( Integration::GlAbstraction& glAbstraction,
                              Integration::GlSyncAbstraction& glSyncAbstraction,
+                             SceneGraph::GeometryBatcher& geometryBatcher,
                              LockedResourceQueue& resourcePostProcessQueue );
 
   /**
index 803b6eb..0c3bd0a 100644 (file)
@@ -50,7 +50,7 @@ void Geometry::GlContextDestroyed()
 {
 }
 
-void Geometry::AddPropertyBuffer( Render::PropertyBuffer* propertyBuffer )
+void Geometry::AddPropertyBuffer( Render::PropertyBuffer* propertyBuffer)
 {
   mVertexBuffers.PushBack( propertyBuffer );
   mAttributesChanged = true;
@@ -62,6 +62,11 @@ void Geometry::SetIndexBuffer( Dali::Vector<unsigned short>& indices )
   mIndicesChanged = true;
 }
 
+const Dali::Vector<unsigned short>* Geometry::GetIndexBuffer() const
+{
+  return &mIndices;
+}
+
 void Geometry::RemovePropertyBuffer( const Render::PropertyBuffer* propertyBuffer )
 {
   size_t bufferCount = mVertexBuffers.Size();
@@ -70,13 +75,22 @@ void Geometry::RemovePropertyBuffer( const Render::PropertyBuffer* propertyBuffe
     if( propertyBuffer == mVertexBuffers[i] )
     {
       //This will delete the gpu buffer associated to the RenderPropertyBuffer if there is one
-      mVertexBuffers.Remove( mVertexBuffers.Begin()+i);
+      mVertexBuffers.Remove( mVertexBuffers.Begin()+i );
       mAttributesChanged = true;
       break;
     }
   }
 }
 
+const Render::PropertyBuffer* Geometry::GetPropertyBuffer( size_t index ) const
+{
+  if( index < mVertexBuffers.Size() )
+  {
+    return mVertexBuffers[ index ];
+  }
+  return NULL;
+}
+
 void Geometry::GetAttributeLocationFromProgram( Vector<GLint>& attributeLocation, Program& program, BufferIndex bufferIndex ) const
 {
   attributeLocation.Clear();
@@ -136,8 +150,10 @@ void Geometry::UploadAndDraw(
       mIndicesChanged = false;
     }
 
-    for( unsigned int i = 0; i < mVertexBuffers.Count(); ++i )
+    size_t count = mVertexBuffers.Count();
+    for( unsigned int i = 0; i < count; ++i )
     {
+
       if( !mVertexBuffers[i]->Update( context ) )
       {
         //Vertex buffer is not ready ( Size, data or format has not been specified yet )
index 205e0f1..6075858 100644 (file)
@@ -78,12 +78,25 @@ public:
   void SetIndexBuffer( Dali::Vector<unsigned short>& indices );
 
   /**
+   * Obtains pointer to the storage of indexed elements
+   * @return Pointer to the index buffer
+   */
+  const Dali::Vector<unsigned short>* GetIndexBuffer() const;
+
+  /**
    * Removes a PropertyBuffer from the geometry
    * @param[in] propertyBuffer The property buffer to be removed
    */
   void RemovePropertyBuffer(  const Render::PropertyBuffer* propertyBuffer );
 
   /**
+   * Returns property buffer at specified index
+   * @param[in] index of the property buffer
+   * @return pointer to the property buffer or NULL
+   */
+  const Render::PropertyBuffer* GetPropertyBuffer( size_t index ) const;
+
+  /**
    * Gets the attribute locations on the shader for the attributes defined in the geometry RenderBuffers
    * @param[out] attributeLocation The vector where the attributes locations will be stored
    * @param[in] program The program
@@ -142,7 +155,6 @@ private:
   bool mIndicesChanged : 1;
   bool mHasBeenUpdated : 1;
   bool mAttributesChanged : 1;
-
 };
 
 } // namespace Render
index 6be4623..9664318 100644 (file)
@@ -125,6 +125,11 @@ void PropertyBuffer::SetData( Dali::Vector<char>* data, size_t size )
   mDataChanged = true;
 }
 
+void PropertyBuffer::UpdateData()
+{
+  mDataChanged = true;
+}
+
 bool PropertyBuffer::Update( Context& context )
 {
   if( !mData || !mFormat || !mSize )
index ed1e0b2..47748a1 100644 (file)
@@ -80,6 +80,12 @@ public:
   void SetData( Dali::Vector<char>* data, size_t size );
 
   /**
+   * @brief Sets flag to update data in the buffer when next PropertyBuffer::Update()
+   * is called.
+   */
+  void UpdateData();
+
+  /**
    * @brief Set the number of elements
    * @param[in] size The number of elements
    */
@@ -156,14 +162,39 @@ public:
     return mSize;
   }
 
+  /**
+   * Retrieve reference to the data storage vector
+   * @return Reference to the data storage
+   */
+  inline const Dali::Vector< char >& GetData() const
+  {
+    return *mData.Get();
+  }
+
+  /**
+   * Retrieve data writeable pointer ( direct access to the buffer data )
+   * @return Pointer to data converted to requested type
+   */
+  template <typename T>
+  inline T* GetDataTypedPtr()
+  {
+    Dali::Vector< char >* data = mData.Release();
+    mData = data;
+    return reinterpret_cast<T*>( &data->operator[]( 0 ) );
+  }
+
+  inline const PropertyBuffer::Format* GetFormat() const
+  {
+    return mFormat.Get();
+  }
+
 private:
-  OwnerPointer< PropertyBuffer::Format >  mFormat;  ///< Format of the buffer
-  OwnerPointer< Dali::Vector< char > >    mData;    ///< Data
-  OwnerPointer< GpuBuffer > mGpuBuffer;               ///< Pointer to the GpuBuffer associated with this RenderPropertyBuffer
+  OwnerPointer< PropertyBuffer::Format >  mFormat;    ///< Format of the buffer
+  OwnerPointer< Dali::Vector< char > >    mData;      ///< Data
+  OwnerPointer< GpuBuffer >               mGpuBuffer; ///< Pointer to the GpuBuffer associated with this RenderPropertyBuffer
 
   size_t mSize;       ///< Number of Elements in the buffer
   bool mDataChanged;  ///< Flag to know if data has changed in a frame
-
 };
 
 } // namespace Render
index cbad8bb..cfaa2be 100644 (file)
@@ -154,7 +154,8 @@ Renderer::Renderer( SceneGraph::RenderDataProvider* dataProvider,
   mDepthTestMode( depthTestMode ),
   mWriteToColorBuffer( writeToColorBuffer ),
   mUpdateAttributesLocation( true ),
-  mPremultipledAlphaEnabled( preMultipliedAlphaEnabled )
+  mPremultipledAlphaEnabled( preMultipliedAlphaEnabled ),
+  mBatchingEnabled( false )
 {
   if(  blendingBitmask != 0u )
   {
@@ -572,6 +573,11 @@ bool Renderer::GetWriteToColorBuffer() const
   return mWriteToColorBuffer;
 }
 
+void Renderer::SetBatchingEnabled( bool batchingEnabled )
+{
+  mBatchingEnabled = batchingEnabled;
+}
+
 void Renderer::Render( Context& context,
                        SceneGraph::TextureCache& textureCache,
                        BufferIndex bufferIndex,
@@ -582,6 +588,7 @@ void Renderer::Render( Context& context,
                        const Matrix& viewMatrix,
                        const Matrix& projectionMatrix,
                        const Vector3& size,
+                       Render::Geometry* externalGeometry,
                        bool blend )
 {
   // Get the program to use:
@@ -630,14 +637,15 @@ void Renderer::Render( Context& context,
     }
 
     SetUniforms( bufferIndex, node, size, *program );
+    Render::Geometry* geometry = externalGeometry ? externalGeometry : mGeometry;
 
-    if( mUpdateAttributesLocation || mGeometry->AttributesChanged() )
+    if( mUpdateAttributesLocation || geometry->AttributesChanged() )
     {
-      mGeometry->GetAttributeLocationFromProgram( mAttributesLocation, *program, bufferIndex );
+      geometry->GetAttributeLocationFromProgram( mAttributesLocation, *program, bufferIndex );
       mUpdateAttributesLocation = false;
     }
 
-    mGeometry->UploadAndDraw( context, bufferIndex, mAttributesLocation, mIndexedDrawFirstElement, mIndexedDrawElementsCount );
+    geometry->UploadAndDraw( context, bufferIndex, mAttributesLocation, mIndexedDrawFirstElement, mIndexedDrawElementsCount );
   }
 }
 
index d7e6575..8011c31 100644 (file)
@@ -166,12 +166,22 @@ public:
    * @param[in] geometry The new geometry
    */
   void SetGeometry( Render::Geometry* geometry );
+
+  /**
+   * Retrieves the geometry used by the renderer
+   * @return The geometry used by the renderer
+   */
+  Render::Geometry* GetGeometry() const
+  {
+    return mGeometry;
+  }
+
   /**
    * Second-phase construction.
    * This is called when the renderer is inside render thread
-   * @param[in] context to use
-   * @param[in] textureCache to use
-   * @param[in] uniformNameCache to use
+   * @param[in] context Context used by the renderer
+   * @param[in] textureCache The texture cache to use
+   * @param[in] uniformNameCache Cache of uniform names to use
    */
   void Initialize( Context& context, SceneGraph::TextureCache& textureCache, Render::UniformNameCache& uniformNameCache );
 
@@ -362,6 +372,12 @@ public:
   bool GetWriteToColorBuffer() const;
 
   /**
+   * Sets batching mode on the renderer
+   * @param[in] batchingEnabled batching state
+   */
+  void SetBatchingEnabled( bool batchingEnabled );
+
+  /**
    * Called to render during RenderManager::Render().
    * @param[in] context The context used for rendering
    * @param[in] textureCache The texture cache used to get textures
@@ -371,6 +387,9 @@ public:
    * @param[in] modelViewMatrix The model-view matrix.
    * @param[in] viewMatrix The view matrix.
    * @param[in] projectionMatrix The projection matrix.
+   * @param[in] size Size of the render item
+   * @param[in] externalGeometry Optional external geometry, if set the original geometry is ignored. If NULL, original geometry will be drawn as normal.
+   * @param[in] blend If true, blending is enabled
    */
   void Render( Context& context,
                SceneGraph::TextureCache& textureCache,
@@ -382,6 +401,7 @@ public:
                const Matrix& viewMatrix,
                const Matrix& projectionMatrix,
                const Vector3& size,
+               Render::Geometry* externalGeometry,
                bool blend);
 
   /**
@@ -468,6 +488,7 @@ private:
   bool                         mWriteToColorBuffer:1;       ///< True if we are writing to the color buffer
   bool                         mUpdateAttributesLocation:1; ///< Indicates attribute locations have changed
   bool                         mPremultipledAlphaEnabled:1; ///< Flag indicating whether the Pre-multiplied Alpha Blending is required
+  bool mBatchingEnabled:1;                ///< Flag indicating if the renderer is batchable
 };
 
 } // namespace SceneGraph
index e0114b4..e89c964 100644 (file)
@@ -26,6 +26,7 @@
 #include <dali/internal/render/renderers/render-renderer.h>
 #include <dali/internal/render/shaders/scene-graph-shader.h>
 #include <dali/internal/update/render-tasks/scene-graph-camera.h>
+#include <dali/internal/update/manager/geometry-batcher.h>
 
 namespace Dali
 {
@@ -52,14 +53,21 @@ void DiscardQueue::Add( BufferIndex updateBufferIndex, Node* node )
   // The GL resources will now be freed in frame N
   // The Update for frame N+1 may occur in parallel with the rendering of frame N
   // Queue the node for destruction in frame N+2
-  if ( 0u == updateBufferIndex )
+  mNodeQueue[ updateBufferIndex ].PushBack( node );
+
+  // If batching, then mark corresponding batch to be destroyed too
+  if( node->GetIsBatchParent() )
   {
-    mNodeQueue0.PushBack( node );
+    mGeometryBatcher->RemoveBatchParent( node );
   }
-  else
+  else if( node->GetBatchParent() )
   {
-    mNodeQueue1.PushBack( node );
+    if( node->mBatchIndex != BATCH_NULL_HANDLE )
+    {
+      mGeometryBatcher->RemoveNode( node );
+    }
   }
+
 }
 
 void DiscardQueue::Add( BufferIndex updateBufferIndex, Shader* shader )
@@ -71,14 +79,7 @@ void DiscardQueue::Add( BufferIndex updateBufferIndex, Shader* shader )
   // The GL resources will now be freed in frame N
   // The Update for frame N+1 may occur in parallel with the rendering of frame N
   // Queue the node for destruction in frame N+2
-  if ( 0u == updateBufferIndex )
-  {
-    mShaderQueue0.PushBack( shader );
-  }
-  else
-  {
-    mShaderQueue1.PushBack( shader );
-  }
+  mShaderQueue[ updateBufferIndex ].PushBack( shader );
 }
 
 void DiscardQueue::Add( BufferIndex updateBufferIndex, Renderer* renderer )
@@ -88,48 +89,28 @@ void DiscardQueue::Add( BufferIndex updateBufferIndex, Renderer* renderer )
   // The GL resources will now be freed in frame N
   // The Update for frame N+1 may occur in parallel with the rendering of frame N
   // Queue the node for destruction in frame N+2
-  if ( 0u == updateBufferIndex )
-  {
-    mRendererQueue0.PushBack( renderer );
-  }
-  else
-  {
-    mRendererQueue1.PushBack( renderer );
-  }
+  mRendererQueue[ updateBufferIndex ].PushBack( renderer );
 }
 
 void DiscardQueue::Add( BufferIndex updateBufferIndex, Camera* camera )
 {
   DALI_ASSERT_DEBUG( NULL != camera );
 
-  if ( 0u == updateBufferIndex )
-  {
-    mCameraQueue0.PushBack( camera );
-  }
-  else
-  {
-    mCameraQueue1.PushBack( camera );
-  }
+  mCameraQueue[ updateBufferIndex ].PushBack( camera );
 }
 
 void DiscardQueue::Clear( BufferIndex updateBufferIndex )
 {
   // Destroy some discarded objects; these should no longer own any GL resources
+  mNodeQueue[ updateBufferIndex ].Clear();
+  mShaderQueue[ updateBufferIndex ].Clear();
+  mRendererQueue[ updateBufferIndex ].Clear();
+  mCameraQueue[ updateBufferIndex ].Clear();
+}
 
-  if ( 0u == updateBufferIndex )
-  {
-    mNodeQueue0.Clear();
-    mShaderQueue0.Clear();
-    mRendererQueue0.Clear();
-    mCameraQueue0.Clear();
-  }
-  else
-  {
-    mNodeQueue1.Clear();
-    mShaderQueue1.Clear();
-    mRendererQueue1.Clear();
-    mCameraQueue1.Clear();
-  }
+void DiscardQueue::SetGeometryBatcher( GeometryBatcher* geometryBatcher )
+{
+  mGeometryBatcher = geometryBatcher;
 }
 
 } // namespace SceneGraph
index a8a35ef..899d856 100644 (file)
@@ -35,6 +35,7 @@ namespace Internal
 namespace SceneGraph
 {
 
+class GeometryBatcher;
 class RenderQueue;
 class Shader;
 class Camera;
@@ -107,6 +108,12 @@ public:
    */
   void Clear( BufferIndex updateBufferIndex );
 
+  /**
+   * Sets pointer to the GeometryBatcher instance
+   * @param[in] geometryBatcher Instance of the GeometryBatcher
+   */
+  void SetGeometryBatcher( GeometryBatcher* geometryBatcher );
+
 private:
 
   // Undefined
@@ -119,17 +126,14 @@ private:
 
   RenderQueue& mRenderQueue; ///< Used to send GL clean-up messages for the next Render.
 
-  // Messages are queued here when the update buffer index == 0
-  NodeOwnerContainer           mNodeQueue0;
-  ShaderQueue                  mShaderQueue0;
-  RendererQueue                mRendererQueue0;
-  CameraQueue                  mCameraQueue0;
-
-  // Messages are queued here when the update buffer index == 1
-  NodeOwnerContainer           mNodeQueue1;
-  ShaderQueue                  mShaderQueue1;
-  RendererQueue                mRendererQueue1;
-  CameraQueue                  mCameraQueue1;
+  // Messages are queued here following the current update buffer number
+  NodeOwnerContainer           mNodeQueue[2];
+  ShaderQueue                  mShaderQueue[2];
+  RendererQueue                mRendererQueue[2];
+  CameraQueue                  mCameraQueue[2];
+
+
+  GeometryBatcher*             mGeometryBatcher; ///< Geometry batcher needed to clean up batches upon node deletion
 };
 
 } // namespace SceneGraph
diff --git a/dali/internal/update/manager/geometry-batcher.cpp b/dali/internal/update/manager/geometry-batcher.cpp
new file mode 100644 (file)
index 0000000..58a9dbb
--- /dev/null
@@ -0,0 +1,581 @@
+/*
+ * Copyright (c) 2016 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.
+ *
+ */
+
+// CLASS HEADER
+#include <dali/internal/update/manager/geometry-batcher.h>
+
+// INTERNAL INCLUDES
+#include <dali/internal/render/common/render-item.h>
+#include <dali/internal/render/common/render-tracker.h>
+#include <dali/internal/render/common/render-instruction.h>
+#include <dali/internal/render/common/render-instruction-container.h>
+#include <dali/internal/render/shaders/scene-graph-shader.h>
+#include <dali/internal/render/renderers/render-renderer.h>
+#include <dali/internal/render/renderers/render-property-buffer.h>
+#include <dali/internal/render/renderers/render-geometry.h>
+#include <dali/internal/update/rendering/scene-graph-renderer.h>
+#include <dali/internal/update/controllers/scene-controller.h>
+#include <dali/internal/update/manager/update-manager.h>
+#include <dali/internal/common/math.h>
+
+namespace
+{
+
+// helper macros to deal with handles
+#define BATCH_LOCAL_INDEX(x) (x&0xFFFF)
+#define BATCH_PARENT_INDEX(x) ((x>>16)&0xFFFF)
+#define BATCH_INDEX_CREATE( batchParentIndex, batchIndex ) ( ( ( (batchParentIndex)&0xFFFF ) << 16 ) | ( (batchIndex)&0xFFFF ) )
+
+/**
+ * The TransformVertexBufferInfo struct
+ * Must be filled before transforming vertices
+ */
+struct TransformVertexBufferData
+{
+  void*                         destinationPtr;       ///< pointer to the destination vertex buffer
+  const void*                   sourcePtr;            ///< pointer to the source vertex buffer
+  float*                        transform;            ///< transform relative to batch parent
+  const float*                  worldMatrix;          ///< model/world matrix of node being batched
+  const float*                  parentInvWorldMatrix; ///< inv world matrix of batch parent
+  unsigned                      componentSize;        ///< size of component
+  unsigned                      vertexCount;          ///< number of vertices to process
+  const float*                  size;                 ///< size of render item
+};
+
+/**
+ * @brief function transforms vertices from 'source' and writes into 'destination'
+ * @param[in,out] data Filled TransformVertexBufferInfo arguments structure
+ */
+template <typename PositionType >
+void TransformVertexBuffer( TransformVertexBufferData& data )
+{
+  const PositionType* source = reinterpret_cast<const PositionType*>( data.sourcePtr );
+  PositionType* destination = reinterpret_cast<PositionType*>( data.destinationPtr );
+
+  size_t componentSize = data.componentSize ? data.componentSize : sizeof( PositionType );
+  const void* sourceEnd = (reinterpret_cast<const char*>( source ) + ( data.vertexCount*componentSize ));
+  for( ; source < sourceEnd;
+       *(reinterpret_cast<char**>( &destination )) += componentSize,
+       *(reinterpret_cast<const char**>( &source )) += componentSize
+       )
+  {
+    Dali::Internal::MultiplyVectorBySize( *destination, *source, data.size );
+    Dali::Internal::MultiplyVectorByMatrix4( *destination, data.transform, *destination );
+  }
+}
+
+} //Unnamed namespace
+
+namespace Dali
+{
+
+namespace Internal
+{
+
+namespace SceneGraph
+{
+
+/**
+ * @brief The VertexDescriptor struct
+ * Holds details of vertex format used for batching
+ */
+struct VertexDescriptor
+{
+  VertexDescriptor()
+    : mVertexComponentSize( 0 ),
+      mVertexPositionType( Dali::Property::NONE ),
+      mVertexFormat( NULL )
+  {
+  }
+
+  unsigned int mVertexComponentSize; ///< Vertex component size in bytes
+  Dali::Property::Type mVertexPositionType;  ///< Vertex position type ( may be Vector2, Vector3, Vector4 )
+  Dali::Internal::Render::PropertyBuffer::Format* mVertexFormat; ///< Vertex format cloned from the very first batched item
+};
+
+struct BatchKey
+{
+  BatchKey()
+  : batchParentNode( NULL ),
+    shader( NULL ),
+    textureSet( NULL ),
+    depthIndex( 0 )
+  {
+  }
+
+  ~BatchKey()
+  {
+  }
+
+  BatchKey( Node* node )
+  {
+    MakeFromNode( node );
+  }
+
+  inline void MakeFromNode( Node* node )
+  {
+    Renderer* renderer = node->GetRendererAt( 0 );
+    batchParentNode = node->GetBatchParent();
+    shader = &renderer->GetShader();
+    textureSet = renderer->GetTextures();
+    depthIndex = renderer->GetDepthIndex();
+  }
+
+  inline bool operator==( const BatchKey& key )
+  {
+    return batchParentNode == key.batchParentNode && shader == key.shader && textureSet == key.textureSet && depthIndex == key.depthIndex;
+  }
+
+  inline bool operator!=( const BatchKey& key )
+  {
+    return !( *this == key );
+  }
+
+  const Node*           batchParentNode;    /// batch parent node that owns batch
+  const Shader*         shader;             /// shader associated with the batch
+  const TextureSet*     textureSet;         /// texture set used by the batch
+  int                   depthIndex;         /// depth index of the batch
+};
+
+/**
+ * @brief The Batch struct
+ * Stores details of single batch
+ */
+struct Batch
+{
+  Batch( const BatchKey& key, Render::Geometry* batchGeometry = NULL )
+   : batchKey( key ),
+     geometry( batchGeometry ),
+     renderedFrame( 0 ),
+     dirty( true )
+  {}
+
+
+  BatchKey                batchKey;       /// Unique batch key
+  Vector<unsigned short>  indices;        /// index buffer per batch
+  Render::Geometry*       geometry;       /// Batch geometry
+  size_t                  renderedFrame;  /// Flag used to determine if batch has already rendered during a frame
+  bool                    dirty;          /// 'dirty' flag per batch
+};
+
+typedef std::vector<Batch> BatchList;
+
+/**
+ * @brief The BatchParent struct
+ * Stores list of children of single batch parent
+ */
+struct BatchParent
+{
+  Node*                   node;             /// Pointer to a node which is a parent for batch(es)
+  Vector<Node*>           batchedChildren;  /// List of batchable children
+  BatchList               batches;          /// List of batches which belong to this parent
+  Render::PropertyBuffer* vertexBuffer;     /// Vertex buffer shared by all batches for this parent
+  bool                    needsUpdate;      /// Flag indicating if batches should be updated
+};
+
+struct Impl
+{
+  Impl() :
+    currentFrame( 0 )
+  {}
+
+  int GetBatchKeyIndex( size_t batchParentIndex, const BatchKey& key )
+  {
+    BatchParent& batchParent = batchParents[ batchParentIndex ];
+    for( size_t j = 0; j < batchParent.batches.size(); ++j )
+    {
+      if( batchParent.batches[j].batchKey == key )
+      {
+        return BATCH_INDEX_CREATE( batchParentIndex, j );
+      }
+    }
+    return -1;
+  }
+
+  std::vector<BatchParent>          batchParents; /// non-trivial type, hence std::vector
+  UpdateManager*                    updateManager;
+  size_t                            currentFrame;
+};
+
+GeometryBatcher::GeometryBatcher() :
+  mImpl( NULL )
+{
+  mImpl = new Impl();
+}
+
+void GeometryBatcher::SetUpdateManager( UpdateManager* updateManager )
+{
+  mImpl->updateManager = updateManager;
+}
+
+GeometryBatcher::~GeometryBatcher()
+{
+  delete mImpl;
+}
+
+bool GeometryBatcher::CloneVertexFormat( const Render::Geometry* sourceGeometry, VertexDescriptor& vertexDescriptor )
+{
+  const Render::Geometry* geometry = sourceGeometry;
+  const Render::PropertyBuffer::Format* format = geometry->GetPropertyBuffer( 0 )->GetFormat();
+
+  if( !format )
+  {
+    return false;
+  }
+
+  Render::PropertyBuffer::Format* clonedVertexFormat = new Render::PropertyBuffer::Format( *format );
+  Render::PropertyBuffer::Component& firstComponent = clonedVertexFormat->components[0];
+
+  vertexDescriptor.mVertexPositionType = firstComponent.type;
+  vertexDescriptor.mVertexComponentSize = clonedVertexFormat->size;
+  vertexDescriptor.mVertexFormat = clonedVertexFormat;
+
+  return true;
+}
+
+void GeometryBatcher::Update( BufferIndex bufferIndex )
+{
+  if( !mImpl->batchParents.empty() )
+  {
+    std::vector<BatchParent>::iterator iter = mImpl->batchParents.begin();
+    std::vector<BatchParent>::iterator end = mImpl->batchParents.end();
+
+    // for each Batch Parent
+    for( size_t batchParentIndex = 0; iter != end; ++iter, ++batchParentIndex )
+    {
+      BatchParent& batchParentData = *iter;
+      // Skip update if batch parent doesn't need it
+      if( !batchParentData.needsUpdate )
+      {
+        continue;
+      }
+
+      Node* batchParentNode = batchParentData.node;
+
+      // Skip if batch parent doesn't have batched children
+      size_t size = batchParentData.batchedChildren.Size();
+      if( !size )
+      {
+        batchParentData.needsUpdate = false;
+        continue;
+      }
+
+      bool batchingFailed( false );
+      uint32_t batchKeyIndex( BATCH_NULL_HANDLE );
+
+      BatchKey oldKey;
+      BatchKey key;
+      VertexDescriptor vertexDescriptor;
+
+      // Destination vertex buffer per batch parent
+      Vector<char>& vertexBufferDest = *( new Vector<char>() );
+      Render::PropertyBuffer* batchVertexBuffer = new Render::PropertyBuffer();
+
+      size_t currentElementIndex = 0;
+
+      Matrix invWorldMatrix( batchParentNode->GetWorldMatrix( bufferIndex ) );
+      invWorldMatrix.Invert();
+
+      // For each batched child of this batch parent...
+      for( size_t i = 0; i < size; ++i )
+      {
+        Node* node = batchParentData.batchedChildren[i];
+
+        const SceneGraph::Renderer* renderer = node->GetRendererAt( 0 );
+
+        // Geometry
+        const Render::Geometry* geometry = &renderer->GetGeometry();
+
+        // Generate batch key
+        key.MakeFromNode( node );
+
+        // format of first property buffer
+        const Render::PropertyBuffer* vertexBuffer = geometry->GetPropertyBuffer( 0 );
+
+        // Geometry of the node may not be ready, in that case we discard whole batch
+        if( !vertexBuffer || ( !vertexDescriptor.mVertexFormat && !CloneVertexFormat( geometry, vertexDescriptor ) ) )
+        {
+          batchingFailed = true;
+          break;
+        }
+
+        // Instantiate new batch
+        if( oldKey != key )
+        {
+          oldKey = key;
+          batchKeyIndex = mImpl->GetBatchKeyIndex( batchParentIndex, key );
+
+          if( batchKeyIndex == BATCH_NULL_HANDLE )
+          {
+            // Create new batch geometry
+            Render::Geometry* newGeometry = new Render::Geometry();
+
+            Batch batch( key, newGeometry );
+
+            // push new batch
+            batchParentData.batches.push_back( batch );
+
+            // rebuild handle
+            batchKeyIndex = BATCH_INDEX_CREATE( batchParentIndex, batchParentData.batches.size()-1 );
+
+            // Vertex buffer may be set before it's filled with data
+            newGeometry->AddPropertyBuffer( batchVertexBuffer );
+
+            // Register geometry with update manager
+            mImpl->updateManager->AddGeometry( newGeometry );
+          }
+        }
+
+        // Tell node which batch it belongs to
+        node->mBatchIndex = batchKeyIndex;
+
+        uint32_t localIndex = BATCH_LOCAL_INDEX( batchKeyIndex );
+
+        if( !batchParentData.batches[ localIndex ].dirty )
+        {
+          continue;
+        }
+
+        const uint32_t vertexBufferSize = vertexBuffer->GetDataSize();
+        const char* vertexDataSource = &vertexBuffer->GetData()[ 0 ];
+
+        uint32_t currentSize = vertexBufferDest.Size();
+        vertexBufferDest.Resize( currentSize + vertexBufferSize );
+        char* vertexDataDest = &vertexBufferDest[ currentSize ];
+
+        // copy data as they are
+        std::copy( vertexDataSource, vertexDataSource + vertexBufferSize, vertexDataDest );
+
+        // transform node
+        const Matrix& worldMatrix = node->GetWorldMatrix( bufferIndex );
+
+        // vertex count
+        const unsigned int sourceVertexCount = vertexBufferSize / vertexDescriptor.mVertexComponentSize;
+
+        // compute transform for the node
+        TransformVertexBufferData transformParameters;
+        transformParameters.destinationPtr = vertexDataDest;
+        transformParameters.sourcePtr = vertexDataSource;
+
+        // perform transformation
+        Matrix transformMatrix;
+        Dali::Internal::MultiplyMatrices( transformMatrix.AsFloat(), worldMatrix.AsFloat(), invWorldMatrix.AsFloat() );
+        transformParameters.transform = transformMatrix.AsFloat();
+        transformParameters.componentSize = vertexDescriptor.mVertexComponentSize;
+        transformParameters.vertexCount = sourceVertexCount;
+        transformParameters.size = node->GetSize( bufferIndex ).AsFloat();
+
+        // Perform vertex transform based on the vertex format
+        switch( vertexDescriptor.mVertexPositionType )
+        {
+          case Dali::Property::VECTOR2:
+          {
+            TransformVertexBuffer<Vec2>( transformParameters );
+            break;
+          }
+          case Dali::Property::VECTOR3:
+          {
+            TransformVertexBuffer<Vec3>( transformParameters );
+            break;
+          }
+          case Dali::Property::VECTOR4:
+          {
+            TransformVertexBuffer<Vec4>( transformParameters );
+            break;
+          }
+          default:
+          {
+            DALI_ASSERT_ALWAYS( true && "Incorrect vertex format! Use Vector2, Vector3 or Vector4 as position!" );
+          }
+        }
+
+        // update index buffer
+        Batch& batch = batchParentData.batches[ localIndex ];
+        uint32_t currentIndexOffset = batch.indices.Size();
+        batch.indices.Resize( batch.indices.Size() + sourceVertexCount );
+        for( size_t k = 0; k < sourceVertexCount; ++k )
+        {
+          size_t index = currentElementIndex + k;
+          batch.indices[k + currentIndexOffset] = (unsigned short)index;
+        }
+
+        currentElementIndex += sourceVertexCount;
+      }
+
+      if( batchingFailed )
+      {
+        delete &vertexBufferDest;
+        delete batchVertexBuffer;
+        continue;
+      }
+
+      // Add shared property buffer
+      mImpl->updateManager->AddPropertyBuffer( batchVertexBuffer );
+      batchVertexBuffer->SetFormat( vertexDescriptor.mVertexFormat );
+      batchVertexBuffer->SetData( &vertexBufferDest, vertexBufferDest.Size()/vertexDescriptor.mVertexComponentSize );
+
+      batchParentData.needsUpdate = false;
+      batchParentData.vertexBuffer = batchVertexBuffer;
+
+      // Update index buffers for all batches own by that batch parent
+      std::vector<Batch>::iterator iter = batchParentData.batches.begin();
+      std::vector<Batch>::iterator end = batchParentData.batches.end();
+      for( ; iter != end; ++iter )
+      {
+        Batch& batch = (*iter);
+        batch.geometry->SetIndexBuffer( batch.indices );
+        batch.dirty = false;
+      }
+    }
+  }
+  ++mImpl->currentFrame;
+}
+
+void GeometryBatcher::AddBatchParent( Node* node )
+{
+  BatchParent batchParent;
+  batchParent.node = node;
+  batchParent.needsUpdate = true;
+  batchParent.batchedChildren.Clear();
+
+  mImpl->batchParents.push_back( batchParent );
+}
+
+void GeometryBatcher::RemoveBatchParent( Node* node )
+{
+  for( size_t i = 0; i < mImpl->batchParents.size(); ++i )
+  {
+    BatchParent& batchParent = mImpl->batchParents[i];
+    if( node == batchParent.node )
+    {
+      // tell children they're not batched anymore
+      Vector<Node*>::Iterator iter = batchParent.batchedChildren.Begin();
+      Vector<Node*>::Iterator end = batchParent.batchedChildren.End();
+      for( ; iter != end; ++iter )
+      {
+        Node* child = *iter;
+        child->mBatchIndex = BATCH_NULL_HANDLE;
+      }
+
+      // delete all resources that belongs to the batch parent
+      for( size_t j = 0; j < batchParent.batches.size(); ++j )
+      {
+        Batch& batch = batchParent.batches[j];
+        mImpl->updateManager->RemoveGeometry( batch.geometry );
+      }
+
+      // delete main vertex buffer
+      mImpl->updateManager->RemovePropertyBuffer( batchParent.vertexBuffer );
+
+      return;
+    }
+  }
+}
+
+void GeometryBatcher::AddNode( Node* node )
+{
+  // look for batch parent
+  Node* currentNode = node->GetParent();
+  Node* batchParent = NULL;
+  while( currentNode )
+  {
+    if( currentNode->mIsBatchParent )
+    {
+      batchParent = currentNode;
+    }
+    currentNode = currentNode->GetParent();
+  }
+
+  if( batchParent )
+  {
+    // find batch parent
+    for( size_t i = 0; i < mImpl->batchParents.size(); ++i )
+    {
+      if( mImpl->batchParents[i].node == batchParent )
+      {
+        mImpl->batchParents[i].batchedChildren.PushBack( node );
+        node->SetBatchParent( batchParent );
+        mImpl->batchParents[i].needsUpdate = true;
+        break;
+      }
+    }
+  }
+}
+
+void GeometryBatcher::RemoveNode( Node* node )
+{
+  if( node->mBatchIndex == BATCH_NULL_HANDLE )
+  {
+    return;
+  }
+
+  uint32_t parentIndex = BATCH_PARENT_INDEX( node->mBatchIndex );
+
+  BatchParent& batchParent = mImpl->batchParents[ parentIndex ];
+
+  // delete all batches from batch parent
+  for( size_t i = 0; i < batchParent.batches.size(); ++i )
+  {
+    Batch& batch = batchParent.batches[ i ];
+
+    // delete geometry
+    mImpl->updateManager->RemoveGeometry( batch.geometry );
+  }
+
+  batchParent.batches.clear();
+
+  // for all children reset batch index to BATCH_NULL_HANDLE
+  for( size_t i = 0; i < batchParent.batchedChildren.Size(); )
+  {
+    Node* child = batchParent.batchedChildren[i];
+
+    if( node == child )
+    {
+      batchParent.batchedChildren.Erase( batchParent.batchedChildren.Begin() + i );
+    }
+    else
+    {
+      child->mBatchIndex = BATCH_NULL_HANDLE;
+      ++i;
+    }
+  }
+
+  mImpl->updateManager->RemovePropertyBuffer( batchParent.vertexBuffer );
+  batchParent.needsUpdate = true;
+}
+
+bool GeometryBatcher::HasRendered( uint32_t batchIndex )
+{
+  return mImpl->batchParents[ BATCH_PARENT_INDEX( batchIndex ) ].batches[ BATCH_LOCAL_INDEX( batchIndex ) ].renderedFrame == mImpl->currentFrame;
+}
+
+void GeometryBatcher::SetRendered( uint32_t batchIndex )
+{
+  mImpl->batchParents[ BATCH_PARENT_INDEX( batchIndex ) ].batches[ BATCH_LOCAL_INDEX( batchIndex ) ].renderedFrame = mImpl->currentFrame;
+}
+
+Render::Geometry* GeometryBatcher::GetGeometry( uint32_t batchIndex )
+{
+  return mImpl->batchParents[ BATCH_PARENT_INDEX( batchIndex) ].batches[ BATCH_LOCAL_INDEX( batchIndex ) ].geometry;
+}
+
+} // namespace SceneGraph
+
+} // namespace Internal
+
+
+} // namespace Dali
diff --git a/dali/internal/update/manager/geometry-batcher.h b/dali/internal/update/manager/geometry-batcher.h
new file mode 100644 (file)
index 0000000..c19e034
--- /dev/null
@@ -0,0 +1,146 @@
+#ifndef DALI_INTERNAL_SCENE_GRAPH_GEOMETRY_BATCHER_H
+#define DALI_INTERNAL_SCENE_GRAPH_GEOMETRY_BATCHER_H
+
+/*
+ * Copyright (c) 2016 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/render/renderers/render-property-buffer.h>
+#include <dali/internal/update/rendering/scene-graph-texture-set.h>
+#include <dali/internal/render/common/render-list.h>
+
+// PUBLIC INCLUDES
+#include <dali/public-api/common/dali-vector.h>
+
+/// Value used by a node to indicate the batch index as null or invalid
+#define BATCH_NULL_HANDLE 0xFFFFFFFF
+
+namespace Dali
+{
+
+namespace Internal
+{
+
+namespace Render
+{
+class Geometry;
+}
+
+namespace SceneGraph
+{
+struct BatchInfo;
+struct BatchStreamMarker;
+
+class Node;
+class RenderInstructionContainer;
+
+class GeometryBatcher
+{
+
+public:
+
+  /**
+   * The constructor of GeometryBatcher
+   */
+  GeometryBatcher();
+
+  /**
+   * The destructor of GeometryBatcher
+   */
+  ~GeometryBatcher();
+
+  /**
+   * Assigns the update manager object
+   * @param[in] updateManager Pointer to instance of update manager
+   */
+  void SetUpdateManager( UpdateManager* updateManager );
+
+  /**
+   * Updates all batches
+   * @param[in] index Update buffer index
+   */
+  void Update( BufferIndex index );
+
+  /**
+   * @brief Add node to batch parents list
+   * @param[in] node instance of a node to be added
+   */
+  void AddBatchParent( Node* node );
+
+  /**
+   * @brief Remove node from list of batch parents
+   * @param[in] node valid instance of node to be removed
+   */
+  void RemoveBatchParent( Node* node );
+
+  /**
+   * @brief Add batchable node
+   * @param[in] node valid instance of the node to be added
+   */
+  void AddNode( Node* node );
+
+  /**
+   * @brief Remove batchable node
+   * @param[in] node valid instance of the node to be removed
+   */
+  void RemoveNode( Node* node );
+
+  /**
+   * @brief Return the geometry object associated with specified batch index
+   * @param[in] batchIndex VALID index of the batch
+   * @return instance of the batched geometry
+   */
+  Render::Geometry* GetGeometry( uint32_t batchIndex );
+
+  /**
+   * @brief Query if a batch at given index has been already rendered
+   * @param batchIndex VALID index of a batch to query
+   * @return true if batch has rendered, false otherwise
+   */
+  bool HasRendered( uint32_t batchIndex );
+
+  /**
+   * @brief Sets a batch at given index as rendered
+   * @param batchIndex VALID index of a batch
+   */
+  void SetRendered( uint32_t batchIndex );
+
+private:
+
+  /**
+   * @brief Clones vertex format of source geometry and stores generated data in the batchInfo object
+   * @param[in] sourceGeometry Geometry of the very first batched item
+   * @param[out] batchInfo Batch info object used to store cloned vertex format
+   * @return true on success, false otherwise
+   */
+  bool CloneVertexFormat( const Render::Geometry* sourceGeometry, struct VertexDescriptor& batchInfo );
+
+private:
+
+  struct Impl* mImpl; ///< Pointer to an instance of geometry batcher implementation
+
+};
+
+} // SceneGraph
+
+} // Internal
+
+} // Dali
+
+
+#endif //DALI_INTERNAL_SCENE_GRAPH_GEOMETRY_BATCHER_H
+
index 41616a1..350d66e 100644 (file)
@@ -34,6 +34,8 @@
 #include <dali/internal/render/common/render-instruction-container.h>
 #include <dali/internal/render/shaders/scene-graph-shader.h>
 #include <dali/internal/render/renderers/render-renderer.h>
+#include <dali/internal/render/renderers/render-property-buffer.h>
+#include <dali/internal/update/manager/geometry-batcher.h>
 
 namespace
 {
@@ -58,6 +60,7 @@ namespace SceneGraph
  * @param renderable Node-Renderer pair
  * @param viewMatrix used to calculate modelview matrix for the item
  * @param camera The camera used to render
+ * @param geometryBatcher The instance of the geometry batcher
  * @param isLayer3d Whether we are processing a 3D layer or not
  * @param cull Whether frustum culling is enabled or not
  */
@@ -66,19 +69,37 @@ inline void AddRendererToRenderList( BufferIndex updateBufferIndex,
                                      Renderable& renderable,
                                      const Matrix& viewMatrix,
                                      SceneGraph::Camera& camera,
+                                     GeometryBatcher& geometryBatcher,
                                      bool isLayer3d,
                                      bool cull )
 {
+  // discard renderable early if it belongs to the batch which has been consumed in during frame
+  Node* renderableNode = renderable.mNode;
+  const bool batchingValid( renderable.mRenderer->IsBatchingEnabled() && renderableNode->mBatchIndex != BATCH_NULL_HANDLE );
+  if( batchingValid && geometryBatcher.HasRendered( renderableNode->mBatchIndex ) )
+  {
+    return;
+  }
+
   bool inside( true );
+  const Node* batchParentNode = renderable.mNode->GetBatchParent();
+  const Node* node = ( renderable.mRenderer->IsBatchingEnabled() && batchParentNode ) ?
+        batchParentNode : renderableNode;
+
   if ( cull && !renderable.mRenderer->GetShader().HintEnabled( Dali::Shader::Hint::MODIFIES_GEOMETRY ) )
   {
-    const Vector4& boundingSphere = renderable.mNode->GetBoundingSphere();
+    const Vector4& boundingSphere = node->GetBoundingSphere();
     inside = (boundingSphere.w > Math::MACHINE_EPSILON_1000) &&
              (camera.CheckSphereInFrustum( updateBufferIndex, Vector3(boundingSphere), boundingSphere.w ) );
   }
 
-  if ( inside )
+  if( inside )
   {
+    if( batchingValid )
+    {
+      geometryBatcher.SetRendered( renderableNode->mBatchIndex );
+    }
+
     Renderer::Opacity opacity = renderable.mRenderer->GetOpacity( updateBufferIndex, *renderable.mNode );
     if( opacity != Renderer::TRANSPARENT )
     {
@@ -97,7 +118,7 @@ inline void AddRendererToRenderList( BufferIndex updateBufferIndex,
         item.mDepthIndex = renderable.mRenderer->GetDepthIndex() + static_cast<int>( renderable.mNode->GetDepth() ) * Dali::Layer::TREE_DEPTH_MULTIPLIER;
       }
       // save MV matrix onto the item
-      renderable.mNode->GetWorldMatrixAndSize( item.mModelMatrix, item.mSize );
+      node->GetWorldMatrixAndSize( item.mModelMatrix, item.mSize );
       Matrix::Multiply( item.mModelViewMatrix, item.mModelMatrix, viewMatrix );
     }
   }
@@ -111,6 +132,7 @@ inline void AddRendererToRenderList( BufferIndex updateBufferIndex,
  * NodeRendererContainer Node-Renderer pairs
  * @param viewMatrix used to calculate modelview matrix for the items
  * @param camera The camera used to render
+ * @param geometryBatcher The instance of the geometry batcher
  * @param isLayer3d Whether we are processing a 3D layer or not
  * @param cull Whether frustum culling is enabled or not
  */
@@ -119,6 +141,7 @@ inline void AddRenderersToRenderList( BufferIndex updateBufferIndex,
                                       RenderableContainer& renderers,
                                       const Matrix& viewMatrix,
                                       SceneGraph::Camera& camera,
+                                      GeometryBatcher* geometryBatcher,
                                       bool isLayer3d,
                                       bool cull)
 {
@@ -127,7 +150,7 @@ inline void AddRenderersToRenderList( BufferIndex updateBufferIndex,
   unsigned int rendererCount( renderers.Size() );
   for( unsigned int i(0); i<rendererCount; ++i )
   {
-    AddRendererToRenderList( updateBufferIndex, renderList, renderers[i], viewMatrix, camera, isLayer3d, cull );
+    AddRendererToRenderList( updateBufferIndex, renderList, renderers[i], viewMatrix, camera, *geometryBatcher, isLayer3d, cull );
   }
 }
 
@@ -330,6 +353,7 @@ inline void SortRenderItems( BufferIndex bufferIndex, RenderList& renderList, La
  * @param stencilRenderablesExist is true if there are stencil renderers on this layer
  * @param instruction to fill in
  * @param sortingHelper to use for sorting the renderitems (to avoid reallocating)
+ * @param geometryBatcher the instance of the geometry batcher
  * @param tryReuseRenderList whether to try to reuse the cached items from the instruction
  * @param cull Whether frustum culling is enabled or not
  */
@@ -340,6 +364,7 @@ inline void AddColorRenderers( BufferIndex updateBufferIndex,
                                bool stencilRenderablesExist,
                                RenderInstruction& instruction,
                                RendererSortingHelper& sortingHelper,
+                               GeometryBatcher& geometryBatcher,
                                bool tryReuseRenderList,
                                bool cull)
 {
@@ -357,7 +382,7 @@ inline void AddColorRenderers( BufferIndex updateBufferIndex,
     }
   }
 
-  AddRenderersToRenderList( updateBufferIndex, renderList, layer.colorRenderables, viewMatrix, camera, layer.GetBehavior() == Dali::Layer::LAYER_3D, cull );
+  AddRenderersToRenderList( updateBufferIndex, renderList, layer.colorRenderables, viewMatrix, camera, &geometryBatcher, layer.GetBehavior() == Dali::Layer::LAYER_3D, cull );
   SortRenderItems( updateBufferIndex, renderList, layer, sortingHelper );
 
   // Setup the render flags for stencil.
@@ -410,7 +435,7 @@ inline void AddOverlayRenderers( BufferIndex updateBufferIndex,
       return;
     }
   }
-  AddRenderersToRenderList( updateBufferIndex, overlayRenderList, layer.overlayRenderables, viewMatrix, camera, layer.GetBehavior() == Dali::Layer::LAYER_3D, cull );
+  AddRenderersToRenderList( updateBufferIndex, overlayRenderList, layer.overlayRenderables, viewMatrix, camera, NULL, layer.GetBehavior() == Dali::Layer::LAYER_3D, cull );
   SortRenderItems( updateBufferIndex, overlayRenderList, layer, sortingHelper );
 }
 
@@ -449,7 +474,7 @@ inline void AddStencilRenderers( BufferIndex updateBufferIndex,
       return;
     }
   }
-  AddRenderersToRenderList( updateBufferIndex, stencilRenderList, layer.stencilRenderables, viewMatrix, camera, layer.GetBehavior() == Dali::Layer::LAYER_3D, cull );
+  AddRenderersToRenderList( updateBufferIndex, stencilRenderList, layer.stencilRenderables, viewMatrix, camera, NULL, layer.GetBehavior() == Dali::Layer::LAYER_3D, cull );
 }
 
 void PrepareRenderInstruction( BufferIndex updateBufferIndex,
@@ -457,7 +482,8 @@ void PrepareRenderInstruction( BufferIndex updateBufferIndex,
                                RenderTask& renderTask,
                                RendererSortingHelper& sortingHelper,
                                bool cull,
-                               RenderInstructionContainer& instructions )
+                               RenderInstructionContainer& instructions,
+                               GeometryBatcher& geometryBatcher )
 {
   // Retrieve the RenderInstruction buffer from the RenderInstructionContainer
   // then populate with instructions.
@@ -494,6 +520,7 @@ void PrepareRenderInstruction( BufferIndex updateBufferIndex,
                          stencilRenderablesExist,
                          instruction,
                          sortingHelper,
+                         geometryBatcher,
                          tryReuseRenderList,
                          cull );
     }
index 0abbc2b..bd16804 100644 (file)
@@ -39,6 +39,7 @@ namespace SceneGraph
 class RenderTracker;
 struct RenderItem;
 class Shader;
+class GeometryBatcher;
 
 /**
  * Structure to store information for sorting the renderers.
@@ -89,7 +90,8 @@ void PrepareRenderInstruction( BufferIndex updateBufferIndex,
                                RenderTask& renderTask,
                                RendererSortingHelper& sortingHelper,
                                bool cull,
-                               RenderInstructionContainer& instructions );
+                               RenderInstructionContainer& instructions,
+                               GeometryBatcher& geometryBatcher );
 
 } // namespace SceneGraph
 
index 4ab67b4..b04a108 100644 (file)
@@ -163,8 +163,6 @@ bool AddRenderablesForTask( BufferIndex updateBufferIndex,
     }
   }
 
-
-
   // Recurse children
   NodeContainer& children = node.GetChildren();
   const NodeIter endIter = children.End();
@@ -184,6 +182,7 @@ void ProcessRenderTasks( BufferIndex updateBufferIndex,
                          Layer& rootNode,
                          SortedLayerPointers& sortedLayers,
                          RendererSortingHelper& sortingHelper,
+                         GeometryBatcher& geometryBatcher,
                          RenderInstructionContainer& instructions )
 {
   RenderTaskList::RenderTaskContainer& taskContainer = renderTasks.GetTasks();
@@ -258,7 +257,8 @@ void ProcessRenderTasks( BufferIndex updateBufferIndex,
                                 renderTask,
                                 sortingHelper,
                                 renderTask.GetCullMode(),
-                                instructions );
+                                instructions,
+                                geometryBatcher );
     }
     else
     {
@@ -321,7 +321,8 @@ void ProcessRenderTasks( BufferIndex updateBufferIndex,
                                 renderTask,
                                 sortingHelper,
                                 renderTask.GetCullMode(),
-                                instructions );
+                                instructions,
+                                geometryBatcher );
     }
 
     renderTask.SetResourcesFinished( resourcesFinished );
index 24df0ea..3758b02 100644 (file)
@@ -41,6 +41,7 @@ class RenderTaskList;
  * @param[in] rootNode The root node of the scene-graph.
  * @param[in] sortedLayers The layers containing lists of opaque/transparent renderables.
  * @param[in] sortingHelper Helper container for sorting transparent renderables.
+ * @param[in] geometryBatcher The instance of the geometry batcher
  * @param[out] instructions The instructions for rendering the next frame.
  */
 void ProcessRenderTasks( BufferIndex updateBufferIndex,
@@ -48,6 +49,7 @@ void ProcessRenderTasks( BufferIndex updateBufferIndex,
                          Layer& rootNode,
                          SortedLayerPointers& sortedLayers,
                          RendererSortingHelper& sortingHelper,
+                         GeometryBatcher& geometryBatcher,
                          RenderInstructionContainer& instructions );
 
 } // namespace SceneGraph
index b2a52d9..ee79039 100644 (file)
 #include <dali/internal/update/render-tasks/scene-graph-render-task-list.h>
 #include <dali/internal/update/rendering/scene-graph-texture-set.h>
 #include <dali/internal/update/resources/resource-manager.h>
+#include <dali/internal/update/manager/geometry-batcher.h>
+#include <dali/internal/update/render-tasks/scene-graph-camera.h>
 
 #include <dali/internal/render/common/render-instruction-container.h>
 #include <dali/internal/render/common/render-manager.h>
 #include <dali/internal/render/queue/render-queue.h>
 #include <dali/internal/render/gl-resources/texture-cache.h>
 #include <dali/internal/render/shaders/scene-graph-shader.h>
-#include <dali/internal/render/renderers/render-frame-buffer.h>
-#include <dali/internal/render/renderers/render-sampler.h>
-#include <dali/internal/update/render-tasks/scene-graph-camera.h>
 
 // Un-comment to enable node tree debug logging
 //#define NODE_TREE_LOGGING 1
@@ -131,7 +130,8 @@ struct UpdateManager::Impl
         RenderController& renderController,
         RenderManager& renderManager,
         RenderQueue& renderQueue,
-        SceneGraphBuffers& sceneGraphBuffers )
+        SceneGraphBuffers& sceneGraphBuffers,
+        GeometryBatcher& geometryBatcher )
   : renderMessageDispatcher( renderManager, renderQueue, sceneGraphBuffers ),
     notificationManager( notificationManager ),
     transformManager(),
@@ -145,6 +145,7 @@ struct UpdateManager::Impl
     renderManager( renderManager ),
     renderQueue( renderQueue ),
     renderInstructions( renderManager.GetRenderInstructionContainer() ),
+    geometryBatcher( geometryBatcher ),
     backgroundColor( Dali::Stage::DEFAULT_BACKGROUND_COLOR ),
     taskList( renderMessageDispatcher, resourceManager ),
     systemLevelTaskList( renderMessageDispatcher, resourceManager ),
@@ -165,6 +166,8 @@ struct UpdateManager::Impl
 
     renderers.SetSceneController( *sceneController );
 
+    discardQueue.SetGeometryBatcher( &geometryBatcher );
+
     // create first 'dummy' node
     nodes.PushBack(0u);
   }
@@ -227,6 +230,7 @@ struct UpdateManager::Impl
   RenderManager&                      renderManager;                 ///< This is responsible for rendering the results of each "update"
   RenderQueue&                        renderQueue;                   ///< Used to queue messages for the next render
   RenderInstructionContainer&         renderInstructions;            ///< Used to prepare the render instructions
+  GeometryBatcher&                    geometryBatcher;               ///< An instance of the GeometryBatcher
 
   Vector4                             backgroundColor;               ///< The glClear color used at the beginning of each frame.
 
@@ -278,7 +282,8 @@ UpdateManager::UpdateManager( NotificationManager& notificationManager,
                               RenderController& controller,
                               RenderManager& renderManager,
                               RenderQueue& renderQueue,
-                              TextureCacheDispatcher& textureCacheDispatcher )
+                              TextureCacheDispatcher& textureCacheDispatcher,
+                              GeometryBatcher& geometryBatcher )
   : mImpl(NULL)
 {
   mImpl = new Impl( notificationManager,
@@ -289,9 +294,11 @@ UpdateManager::UpdateManager( NotificationManager& notificationManager,
                     controller,
                     renderManager,
                     renderQueue,
-                    mSceneGraphBuffers );
+                    mSceneGraphBuffers,
+                    geometryBatcher );
 
   textureCacheDispatcher.SetBufferIndices( &mSceneGraphBuffers );
+  mImpl->geometryBatcher.SetUpdateManager( this );
 }
 
 UpdateManager::~UpdateManager()
@@ -308,13 +315,13 @@ void UpdateManager::InstallRoot( SceneGraph::Layer* layer, bool systemLevel )
   {
     DALI_ASSERT_DEBUG( mImpl->root == NULL && "Root Node already installed" );
     mImpl->root = layer;
-    mImpl->root->CreateTransform( &mImpl->transformManager);
+    mImpl->root->CreateTransform( &mImpl->transformManager );
   }
   else
   {
     DALI_ASSERT_DEBUG( mImpl->systemLevelRoot == NULL && "System-level Root Node already installed" );
     mImpl->systemLevelRoot = layer;
-    mImpl->systemLevelRoot->CreateTransform( &mImpl->transformManager);
+    mImpl->systemLevelRoot->CreateTransform( &mImpl->transformManager );
   }
 
   layer->SetRoot(true);
@@ -332,7 +339,8 @@ void UpdateManager::AddNode( Node* node )
     if(node > (*iter))
     {
       mImpl->nodes.Insert((iter+1), node);
-      node->CreateTransform( &mImpl->transformManager);
+      node->CreateTransform( &mImpl->transformManager );
+      node->mGeometryBatcher = &mImpl->geometryBatcher;
       break;
     }
   }
@@ -979,6 +987,9 @@ unsigned int UpdateManager::Update( float elapsedSeconds,
     //Process Property Notifications
     ProcessPropertyNotifications( bufferIndex );
 
+    //Update geometry batcher
+    mImpl->geometryBatcher.Update( bufferIndex );
+
     //Process the RenderTasks; this creates the instructions for rendering the next frame.
     //reset the update buffer index and make sure there is enough room in the instruction container
     mImpl->renderInstructions.ResetAndReserve( bufferIndex,
@@ -991,6 +1002,7 @@ unsigned int UpdateManager::Update( float elapsedSeconds,
                            *mImpl->root,
                            mImpl->sortedLayers,
                            mImpl->renderSortingHelper,
+                           mImpl->geometryBatcher,
                            mImpl->renderInstructions );
 
       // Process the system-level RenderTasks last
@@ -1001,6 +1013,7 @@ unsigned int UpdateManager::Update( float elapsedSeconds,
                              *mImpl->systemLevelRoot,
                              mImpl->systemLevelSortedLayers,
                              mImpl->renderSortingHelper,
+                             mImpl->geometryBatcher,
                              mImpl->renderInstructions );
       }
     }
index e90ab9d..7f15563 100644 (file)
@@ -104,6 +104,7 @@ public:
    * @param[in] renderManager This is responsible for rendering the results of each "update".
    * @param[in] renderQueue Used to queue messages for the next render.
    * @param[in] textureCacheDispatcher Used for sending messages to texture cache.
+   * @param[in] geometryBatcher Used when geometry batching is enabled.
    */
   UpdateManager( NotificationManager& notificationManager,
                  CompleteNotificationInterface& animationFinishedNotifier,
@@ -113,7 +114,8 @@ public:
                  Integration::RenderController& controller,
                  RenderManager& renderManager,
                  RenderQueue& renderQueue,
-                 TextureCacheDispatcher& textureCacheDispatcher );
+                 TextureCacheDispatcher& textureCacheDispatcher,
+                 GeometryBatcher& geometryBatcher );
 
   /**
    * Destructor.
index b3a9767..0a7dc5a 100644 (file)
@@ -22,6 +22,7 @@
 #include <dali/internal/common/internal-constants.h>
 #include <dali/internal/common/memory-pool-object-allocator.h>
 #include <dali/internal/update/common/discard-queue.h>
+#include <dali/internal/update/manager/geometry-batcher.h>
 #include <dali/public-api/common/dali-common.h>
 #include <dali/public-api/common/constants.h>
 
@@ -49,7 +50,7 @@ Node* Node::New()
 }
 
 Node::Node()
-: mTransformManager(0),
+: mTransformManager( NULL ),
   mTransformId( INVALID_TRANSFORM_ID ),
   mParentOrigin( TRANSFORM_PROPERTY_PARENT_ORIGIN ),
   mAnchorPoint( TRANSFORM_PROPERTY_ANCHOR_POINT ),
@@ -64,7 +65,11 @@ Node::Node()
   mWorldOrientation(), // initialized to identity by default
   mWorldMatrix(),
   mWorldColor( Color::WHITE ),
+  mGeometryBatcher( NULL ),
+  mBatchIndex( BATCH_NULL_HANDLE ),
+  mIsBatchParent( false ),
   mParent( NULL ),
+  mBatchParent( NULL ),
   mExclusiveRenderTask( NULL ),
   mChildren(),
   mRegenerateUniformMap( 0 ),
@@ -211,6 +216,35 @@ void Node::DisconnectChild( BufferIndex updateBufferIndex, Node& childNode )
   found->RecursiveDisconnectFromSceneGraph( updateBufferIndex );
 }
 
+void Node::AddRenderer( Renderer* renderer )
+{
+  //Check that it has not been already added
+  unsigned int rendererCount( mRenderer.Size() );
+  for( unsigned int i(0); i<rendererCount; ++i )
+  {
+    if( mRenderer[i] == renderer )
+    {
+      //Renderer already in the list
+      return;
+    }
+  }
+
+  //If it is the first renderer added, make sure the world transform will be calculated
+  //in the next update as world transform is not computed if node has no renderers
+  if( rendererCount == 0 )
+  {
+    mDirtyFlags |= TransformFlag;
+  }
+
+  mRenderer.PushBack( renderer );
+
+  // Tell geometry batcher if should batch the child
+  if( mRenderer.Size() == 1 && mRenderer[0]->mBatchingEnabled )
+  {
+    mGeometryBatcher->AddNode( this );
+  }
+}
+
 void Node::RemoveRenderer( Renderer* renderer )
 {
   unsigned int rendererCount( mRenderer.Size() );
@@ -252,7 +286,7 @@ void Node::ResetDefaultProperties( BufferIndex updateBufferIndex )
   mDirtyFlags = NothingFlag;
 }
 
-void Node::SetParent(Node& parentNode)
+void Node::SetParent( Node& parentNode )
 {
   DALI_ASSERT_ALWAYS(this != &parentNode);
   DALI_ASSERT_ALWAYS(!mIsRoot);
@@ -267,6 +301,29 @@ void Node::SetParent(Node& parentNode)
   }
 }
 
+void Node::SetBatchParent( Node* batchParentNode )
+{
+  DALI_ASSERT_ALWAYS(!mIsRoot);
+  mBatchParent = batchParentNode;
+}
+
+void Node::SetIsBatchParent( bool enabled )
+{
+  if( mIsBatchParent != enabled )
+  {
+    mIsBatchParent = enabled;
+
+    if( enabled )
+    {
+      mGeometryBatcher->AddBatchParent( this );
+    }
+    else
+    {
+      mGeometryBatcher->RemoveBatchParent( this );
+    }
+  }
+}
+
 void Node::RecursiveDisconnectFromSceneGraph( BufferIndex updateBufferIndex )
 {
   DALI_ASSERT_ALWAYS(!mIsRoot);
index 4846122..3f8bbac 100644 (file)
@@ -56,6 +56,7 @@ class DiscardQueue;
 class Layer;
 class RenderTask;
 class UpdateManager;
+class GeometryBatcher;
 
 /**
  * Flag whether property has changed, during the Update phase.
@@ -145,28 +146,7 @@ public:
    * Add a renderer to the node
    * @param[in] renderer The renderer added to the node
    */
-  void AddRenderer( Renderer* renderer )
-  {
-    //Check that it has not been already added
-    unsigned int rendererCount( mRenderer.Size() );
-    for( unsigned int i(0); i<rendererCount; ++i )
-    {
-      if( mRenderer[i] == renderer )
-      {
-        //Renderer already in the list
-        return;
-      }
-    }
-
-    //If it is the first renderer added, make sure the world transform will be calculated
-    //in the next update as world transform is not computed if node has no renderers
-    if( rendererCount == 0 )
-    {
-      mDirtyFlags |= TransformFlag;
-    }
-
-    mRenderer.PushBack( renderer );
-  }
+  void AddRenderer( Renderer* renderer );
 
   /**
    * Remove a renderer from the node
@@ -178,7 +158,7 @@ public:
    * Get the renderer at the given index
    * @param[in] index
    */
-  Renderer* GetRendererAt( unsigned int index )
+  Renderer* GetRendererAt( unsigned int index ) const
   {
     return mRenderer[index];
   }
@@ -611,7 +591,6 @@ public:
            (mTransformManager->IsLocalMatrixDirty( mTransformId ));
   }
 
-
   /**
    * Retrieve the cached world-matrix of a node.
    * @param[in] bufferIndex The buffer to read from.
@@ -685,6 +664,36 @@ public:
     return mDepth;
   }
 
+  /**
+   * @brief Turns on or off being a batch parent for the node
+   * @param[in] enabled If true the node becomes a parent for batch of its children
+   */
+  void SetIsBatchParent( bool enabled );
+
+  /**
+   * @brief Tells if the node is a batch parent
+   * @return True if node is a batch parent, false otherwise.
+   */
+  inline bool GetIsBatchParent()
+  {
+    return mIsBatchParent;
+  }
+
+  /**
+   * Set the batch parent of a Node.
+   * @param[in] batchParentNode The new batch parent.
+   */
+  void SetBatchParent( Node* batchParentNode );
+
+  /**
+   * Retrieve the batch parent of a Node.
+   * @return The batch parent node, or NULL if the Node has not been added to the scene-graph.
+   */
+  Node* GetBatchParent() const
+  {
+    return mBatchParent;
+  }
+
 public:
   /**
    * @copydoc UniformMap::Add
@@ -717,7 +726,9 @@ protected:
    * Set the parent of a Node.
    * @param[in] parentNode the new parent.
    */
-  void SetParent(Node& parentNode);
+  void SetParent( Node& parentNode );
+
+protected:
 
   /**
    * Protected constructor; See also Node::New()
@@ -799,11 +810,16 @@ public: // Default properties
   TransformManagerVector3Input    mWorldScale;
   TransformManagerQuaternionInput mWorldOrientation;  ///< Full inherited orientation
   TransformManagerMatrixInput     mWorldMatrix;       ///< Full inherited world matrix
-  InheritedColor      mWorldColor;        ///< Full inherited color
+  InheritedColor                  mWorldColor;        ///< Full inherited color
+
+  GeometryBatcher*                mGeometryBatcher;  ///< A pointer to an instance of geometry batcher
+  uint32_t                        mBatchIndex;       ///< Batch 32bit handle, BATCH_NULL_HANDLE by default
+  bool                            mIsBatchParent:1;  ///< Marks node as a batch parent
 
 protected:
 
   Node*               mParent;                       ///< Pointer to parent node (a child is owned by its parent)
+  Node*               mBatchParent;                  ///< Pointer to batch parent node
   RenderTask*         mExclusiveRenderTask;          ///< Nodes can be marked as exclusive to a single RenderTask
 
   RendererContainer   mRenderer;                     ///< Container of renderers; not owned
@@ -927,6 +943,19 @@ inline void RemoveRendererMessage( EventThreadServices& eventThreadServices, con
   // Construct message in the message queue memory; note that delete should not be called on the return value
   new (slot) LocalType( &node, &Node::RemoveRenderer, renderer );
 }
+
+inline void SetIsBatchParentMessage( EventThreadServices& eventThreadServices, const Node& node, bool isBatchParent )
+{
+  typedef MessageValue1< Node, bool > LocalType;
+
+  // Reserve some memory inside the message queue
+  unsigned int* slot = eventThreadServices.ReserveMessageSlot( sizeof( LocalType ) );
+
+  // Construct message in the message queue memory; note that delete should not be called on the return value
+  new (slot) LocalType( &node, &Node::SetIsBatchParent, isBatchParent );
+}
+
+
 } // namespace SceneGraph
 
 } // namespace Internal
index 17b402f..c28a236 100644 (file)
@@ -107,7 +107,8 @@ enum Flags
   RESEND_STENCIL_OPERATION_ON_FAIL   = 1 << 16,
   RESEND_STENCIL_OPERATION_ON_Z_FAIL = 1 << 17,
   RESEND_STENCIL_OPERATION_ON_Z_PASS = 1 << 18,
-  RESEND_WRITE_TO_COLOR_BUFFER       = 1 << 19
+  RESEND_WRITE_TO_COLOR_BUFFER       = 1 << 19,
+  RESEND_BATCHING_MODE               = 1 << 20,
 };
 
 } // Anonymous namespace
@@ -146,6 +147,7 @@ Renderer::Renderer()
   mResourcesReady( false ),
   mFinishedResourceAcquisition( false ),
   mPremultipledAlphaEnabled( false ),
+  mBatchingEnabled( false ),
   mDepthIndex( 0 )
 {
   mUniformMapChanged[0] = false;
@@ -376,6 +378,13 @@ void Renderer::PrepareRender( BufferIndex updateBufferIndex )
       new (slot) DerivedType( mRenderer, &Render::Renderer::SetWriteToColorBuffer, mWriteToColorBuffer );
     }
 
+    if( mResendFlag & RESEND_BATCHING_MODE )
+    {
+      typedef MessageValue1< Render::Renderer, bool > DerivedType;
+      unsigned int* slot = mSceneController->GetRenderQueue().ReserveMessageSlot( updateBufferIndex, sizeof( DerivedType ) );
+      new (slot) DerivedType( mRenderer, &Render::Renderer::SetBatchingEnabled, mBatchingEnabled );
+    }
+
     mResendFlag = 0;
   }
 }
@@ -550,6 +559,12 @@ void Renderer::SetWriteToColorBuffer( bool writeToColorBuffer )
   mResendFlag |= RESEND_WRITE_TO_COLOR_BUFFER;
 }
 
+void Renderer::SetBatchingEnabled( bool batchingEnabled )
+{
+  mBatchingEnabled = batchingEnabled;
+  mResendFlag |= RESEND_BATCHING_MODE;
+}
+
 //Called when SceneGraph::Renderer is added to update manager ( that happens when an "event-thread renderer" is created )
 void Renderer::ConnectToSceneGraph( SceneController& sceneController, BufferIndex bufferIndex )
 {
index 4249b0f..1a86cb9 100644 (file)
@@ -87,6 +87,14 @@ public:
    */
   void SetTextures( TextureSet* textureSet );
 
+  /**
+   * Returns current texture set object
+   * @return Pointer to the texture set
+   */
+  const TextureSet* GetTextures() const
+  {
+    return mTextureSet;
+  }
 
   /**
    * Set the shader for the renderer
@@ -98,7 +106,7 @@ public:
    * Get the shader used by this renderer
    * @return the shader this renderer uses
    */
-  Shader& GetShader()
+  const Shader& GetShader() const
   {
     return *mShader;
   }
@@ -110,6 +118,15 @@ public:
   void SetGeometry( Render::Geometry* geometry );
 
   /**
+   * Get the geometry of this renderer
+   * @return the geometry this renderer uses
+   */
+  const Render::Geometry& GetGeometry() const
+  {
+    return *mGeometry;
+  }
+
+  /**
    * Set the depth index
    * @param[in] depthIndex the new depth index to use
    */
@@ -239,6 +256,21 @@ public:
   void SetWriteToColorBuffer( bool writeToColorBuffer );
 
   /**
+   * Turns on batching feature for the renderer
+   * @param[in] batchingEnabled if true, enables the batching mode for the renderer
+   */
+  void SetBatchingEnabled( bool batchingEnabled );
+
+  /**
+   * Tests whether batching feature is enabled for this renderer
+   * @return batching state
+   */
+  bool IsBatchingEnabled() const
+  {
+    return mBatchingEnabled;
+  }
+
+  /**
    * Prepare the object for rendering.
    * This is called by the UpdateManager when an object is due to be rendered in the current frame.
    * @param[in] updateBufferIndex The current update buffer index.
@@ -390,6 +422,7 @@ private:
 
 public:
 
+  bool                         mBatchingEnabled : 1;              ///< Flag indicating whether the render supports batching
   int                          mDepthIndex;                       ///< Used only in PrepareRenderInstructions
 };
 
@@ -629,6 +662,16 @@ inline void SetWriteToColorBufferMessage( EventThreadServices& eventThreadServic
   new (slot) LocalType( &renderer, &Renderer::SetWriteToColorBuffer, writeToColorBuffer );
 }
 
+inline void SetBatchingEnabledMessage( EventThreadServices& eventThreadServices, const Renderer& renderer, bool batchable )
+{
+  typedef MessageValue1< Renderer, bool > LocalType;
+
+  // Reserve some memory inside the message queue
+  unsigned int* slot = eventThreadServices.ReserveMessageSlot( sizeof( LocalType ) );
+
+  new (slot) LocalType( &renderer, &Renderer::SetBatchingEnabled, batchable );
+}
+
 } // namespace SceneGraph
 } // namespace Internal
 } // namespace Dali
index b01ff3d..fda367b 100644 (file)
@@ -307,7 +307,8 @@ public:
       PADDING,                                            ///< name "padding",               type Vector4 @SINCE_1_0.0
       MINIMUM_SIZE,                                       ///< name "minimumSize",           type Vector2 @SINCE_1_0.0
       MAXIMUM_SIZE,                                       ///< name "maximumSize",           type Vector2 @SINCE_1_0.0
-      INHERIT_POSITION,                                   ///< name "inheritPosition", type bool @SINCE_1_1.24
+      INHERIT_POSITION,                                   ///< name "inheritPosition",       type bool @SINCE_1_1.24
+      BATCH_PARENT,                                       ///< name "batchParent",           type bool
     };
   };
 
index 9c06ea3..353dd28 100644 (file)
@@ -420,7 +420,14 @@ public:
        * @note The default value is True
        * @SINCE_1_1.43
        */
-      WRITE_TO_COLOR_BUFFER
+      WRITE_TO_COLOR_BUFFER,
+
+      /**
+       * @brief name "batchingEnabled", type BOOLEAN
+       * @see Batching
+       * @note The default value is 'false'
+       */
+      BATCHING_ENABLED
     };
   };