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
utc-Dali-Internal-FixedSizeMemoryPool.cpp
utc-Dali-Internal-MemoryPoolObjectAllocator.cpp
utc-Dali-Internal-FrustumCulling.cpp
+ utc-Dali-Internal-GeometryBatcher.cpp
)
LIST(APPEND TC_SOURCES
--- /dev/null
+#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;
+}
#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>
mNotificationManager(NULL),
mImageFactory(NULL),
mShaderFactory(NULL),
+ mGeometryBatcher( NULL ),
mIsActive(true),
mProcessingEvent(false)
{
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();
renderController,
*mRenderManager,
renderQueue,
- *mTextureCacheDispatcher );
+ *mTextureCacheDispatcher,
+ *mGeometryBatcher );
mRenderManager->SetShaderSaver( *mUpdateManager );
delete mTextureCacheDispatcher;
delete mUpdateManager;
delete mRenderManager;
+ delete mGeometryBatcher;
delete mTextureUploadedQueue;
}
class RenderManager;
class DiscardQueue;
class TextureCacheDispatcher;
+class GeometryBatcher;
}
/**
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()
{
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]);
+ }
+}
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
*/
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
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
mInheritScale( true ),
mDrawMode( DrawMode::NORMAL ),
mPositionInheritanceMode( Node::DEFAULT_POSITION_INHERITANCE_MODE ),
- mColorMode( Node::DEFAULT_COLOR_MODE )
+ mColorMode( Node::DEFAULT_COLOR_MODE ),
+ mIsBatchParent( false )
{
}
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
break;
}
+ case Dali::Actor::Property::BATCH_PARENT:
+ {
+ value = mIsBatchParent;
+ break;
+ }
+
default:
{
DALI_ASSERT_ALWAYS( false && "Actor Property index invalid" ); // should not come here
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
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:
return mPremultipledAlphaEnabled;
}
+bool Renderer::IsBatchingEnabled() const
+{
+ return mBatchingEnabled;
+}
+
SceneGraph::Renderer* Renderer::GetRendererSceneObject()
{
return mSceneObject;
}
break;
}
+ case Dali::Renderer::Property::BATCHING_ENABLED:
+ {
+ bool enabled;
+ if( propertyValue.Get( enabled ) )
+ {
+ if( mBatchingEnabled != enabled )
+ {
+ mBatchingEnabled = enabled;
+ SetBatchingEnabledMessage( GetEventThreadServices(), *mSceneObject, mBatchingEnabled );
+ }
+ }
+ break;
+ }
}
}
value = mDepthWriteMode;
break;
}
+ case Dali::Renderer::Property::BATCHING_ENABLED:
+ {
+ value = mBatchingEnabled;
+ break;
+ }
case Dali::Renderer::Property::DEPTH_FUNCTION:
{
value = mDepthFunction;
mDepthWriteMode( DepthWriteMode::AUTO ),
mDepthTestMode( DepthTestMode::AUTO ),
mWriteToColorBuffer( true ),
- mPremultipledAlphaEnabled( false )
+ mPremultipledAlphaEnabled( false ),
+ mBatchingEnabled( false )
{
}
*/
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
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
$(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 \
#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
{
* @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,
SceneGraph::Shader& defaultShader,
BufferIndex bufferIndex,
const Matrix& viewMatrix,
- const Matrix& projectionMatrix )
+ const Matrix& projectionMatrix,
+ GeometryBatcher* geometryBatcher )
{
DALI_PRINT_RENDER_LIST( renderList );
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
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 );
}
}
}
Context& context,
SceneGraph::TextureCache& textureCache,
SceneGraph::Shader& defaultShader,
+ GeometryBatcher& geometryBatcher,
BufferIndex bufferIndex )
{
DALI_PRINT_RENDER_INSTRUCTION( instruction, bufferIndex );
if( renderList &&
!renderList->IsEmpty() )
{
- ProcessRenderList( *renderList, context, textureCache, defaultShader, bufferIndex, *viewMatrix, *projectionMatrix );
+ ProcessRenderList( *renderList, context, textureCache, defaultShader, bufferIndex, *viewMatrix, *projectionMatrix, &geometryBatcher );
}
}
}
class RenderInstruction;
class Shader;
class TextureCache;
+class GeometryBatcher;
}
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
mSize(),
mRenderer( NULL ),
mNode( NULL ),
+ mBatchRenderGeometry( NULL ),
mDepthIndex( 0 ),
- mIsOpaque( true )
+ mIsOpaque( true ),
+ mBatched( false )
{
}
namespace Render
{
class Renderer;
+class RenderGeometry;
}
namespace SceneGraph
*/
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:
Impl( Integration::GlAbstraction& glAbstraction,
Integration::GlSyncAbstraction& glSyncAbstraction,
LockedResourceQueue& textureUploadedQ,
- TextureUploadedDispatcher& postProcessDispatcher )
+ TextureUploadedDispatcher& postProcessDispatcher,
+ GeometryBatcher& geometryBatcher )
: context( glAbstraction ),
glSyncAbstraction( glSyncAbstraction ),
renderQueue(),
renderersAdded( false ),
firstRenderCompleted( false ),
defaultShader( NULL ),
- programController( glAbstraction )
+ programController( glAbstraction ),
+ geometryBatcher( geometryBatcher )
{
}
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;
}
mImpl->context,
mImpl->textureCache,
defaultShader,
+ mImpl->geometryBatcher,
mImpl->renderBufferIndex );
if(instruction.mOffscreenTextureId != 0)
class RenderInstructionContainer;
class Shader;
class PropertyBufferDataProvider;
+class GeometryBatcher;
/**
* RenderManager is responsible for rendering the result of the previous "update", which
* 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 );
/**
{
}
-void Geometry::AddPropertyBuffer( Render::PropertyBuffer* propertyBuffer )
+void Geometry::AddPropertyBuffer( Render::PropertyBuffer* propertyBuffer)
{
mVertexBuffers.PushBack( propertyBuffer );
mAttributesChanged = true;
mIndicesChanged = true;
}
+const Dali::Vector<unsigned short>* Geometry::GetIndexBuffer() const
+{
+ return &mIndices;
+}
+
void Geometry::RemovePropertyBuffer( const Render::PropertyBuffer* propertyBuffer )
{
size_t bufferCount = mVertexBuffers.Size();
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();
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 )
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
bool mIndicesChanged : 1;
bool mHasBeenUpdated : 1;
bool mAttributesChanged : 1;
-
};
} // namespace Render
mDataChanged = true;
}
+void PropertyBuffer::UpdateData()
+{
+ mDataChanged = true;
+}
+
bool PropertyBuffer::Update( Context& context )
{
if( !mData || !mFormat || !mSize )
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
*/
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
mDepthTestMode( depthTestMode ),
mWriteToColorBuffer( writeToColorBuffer ),
mUpdateAttributesLocation( true ),
- mPremultipledAlphaEnabled( preMultipliedAlphaEnabled )
+ mPremultipledAlphaEnabled( preMultipliedAlphaEnabled ),
+ mBatchingEnabled( false )
{
if( blendingBitmask != 0u )
{
return mWriteToColorBuffer;
}
+void Renderer::SetBatchingEnabled( bool batchingEnabled )
+{
+ mBatchingEnabled = batchingEnabled;
+}
+
void Renderer::Render( Context& context,
SceneGraph::TextureCache& textureCache,
BufferIndex bufferIndex,
const Matrix& viewMatrix,
const Matrix& projectionMatrix,
const Vector3& size,
+ Render::Geometry* externalGeometry,
bool blend )
{
// Get the program to use:
}
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 );
}
}
* @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 );
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
* @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,
const Matrix& viewMatrix,
const Matrix& projectionMatrix,
const Vector3& size,
+ Render::Geometry* externalGeometry,
bool blend);
/**
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
#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
{
// 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 )
// 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 )
// 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
namespace SceneGraph
{
+class GeometryBatcher;
class RenderQueue;
class Shader;
class Camera;
*/
void Clear( BufferIndex updateBufferIndex );
+ /**
+ * Sets pointer to the GeometryBatcher instance
+ * @param[in] geometryBatcher Instance of the GeometryBatcher
+ */
+ void SetGeometryBatcher( GeometryBatcher* geometryBatcher );
+
private:
// Undefined
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
--- /dev/null
+/*
+ * 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
--- /dev/null
+#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
+
#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
{
* @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
*/
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 )
{
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 );
}
}
* 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
*/
RenderableContainer& renderers,
const Matrix& viewMatrix,
SceneGraph::Camera& camera,
+ GeometryBatcher* geometryBatcher,
bool isLayer3d,
bool cull)
{
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 );
}
}
* @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
*/
bool stencilRenderablesExist,
RenderInstruction& instruction,
RendererSortingHelper& sortingHelper,
+ GeometryBatcher& geometryBatcher,
bool tryReuseRenderList,
bool cull)
{
}
}
- 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.
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 );
}
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,
RenderTask& renderTask,
RendererSortingHelper& sortingHelper,
bool cull,
- RenderInstructionContainer& instructions )
+ RenderInstructionContainer& instructions,
+ GeometryBatcher& geometryBatcher )
{
// Retrieve the RenderInstruction buffer from the RenderInstructionContainer
// then populate with instructions.
stencilRenderablesExist,
instruction,
sortingHelper,
+ geometryBatcher,
tryReuseRenderList,
cull );
}
class RenderTracker;
struct RenderItem;
class Shader;
+class GeometryBatcher;
/**
* Structure to store information for sorting the renderers.
RenderTask& renderTask,
RendererSortingHelper& sortingHelper,
bool cull,
- RenderInstructionContainer& instructions );
+ RenderInstructionContainer& instructions,
+ GeometryBatcher& geometryBatcher );
} // namespace SceneGraph
}
}
-
-
// Recurse children
NodeContainer& children = node.GetChildren();
const NodeIter endIter = children.End();
Layer& rootNode,
SortedLayerPointers& sortedLayers,
RendererSortingHelper& sortingHelper,
+ GeometryBatcher& geometryBatcher,
RenderInstructionContainer& instructions )
{
RenderTaskList::RenderTaskContainer& taskContainer = renderTasks.GetTasks();
renderTask,
sortingHelper,
renderTask.GetCullMode(),
- instructions );
+ instructions,
+ geometryBatcher );
}
else
{
renderTask,
sortingHelper,
renderTask.GetCullMode(),
- instructions );
+ instructions,
+ geometryBatcher );
}
renderTask.SetResourcesFinished( resourcesFinished );
* @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,
Layer& rootNode,
SortedLayerPointers& sortedLayers,
RendererSortingHelper& sortingHelper,
+ GeometryBatcher& geometryBatcher,
RenderInstructionContainer& instructions );
} // namespace SceneGraph
#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
RenderController& renderController,
RenderManager& renderManager,
RenderQueue& renderQueue,
- SceneGraphBuffers& sceneGraphBuffers )
+ SceneGraphBuffers& sceneGraphBuffers,
+ GeometryBatcher& geometryBatcher )
: renderMessageDispatcher( renderManager, renderQueue, sceneGraphBuffers ),
notificationManager( notificationManager ),
transformManager(),
renderManager( renderManager ),
renderQueue( renderQueue ),
renderInstructions( renderManager.GetRenderInstructionContainer() ),
+ geometryBatcher( geometryBatcher ),
backgroundColor( Dali::Stage::DEFAULT_BACKGROUND_COLOR ),
taskList( renderMessageDispatcher, resourceManager ),
systemLevelTaskList( renderMessageDispatcher, resourceManager ),
renderers.SetSceneController( *sceneController );
+ discardQueue.SetGeometryBatcher( &geometryBatcher );
+
// create first 'dummy' node
nodes.PushBack(0u);
}
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.
RenderController& controller,
RenderManager& renderManager,
RenderQueue& renderQueue,
- TextureCacheDispatcher& textureCacheDispatcher )
+ TextureCacheDispatcher& textureCacheDispatcher,
+ GeometryBatcher& geometryBatcher )
: mImpl(NULL)
{
mImpl = new Impl( notificationManager,
controller,
renderManager,
renderQueue,
- mSceneGraphBuffers );
+ mSceneGraphBuffers,
+ geometryBatcher );
textureCacheDispatcher.SetBufferIndices( &mSceneGraphBuffers );
+ mImpl->geometryBatcher.SetUpdateManager( this );
}
UpdateManager::~UpdateManager()
{
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);
if(node > (*iter))
{
mImpl->nodes.Insert((iter+1), node);
- node->CreateTransform( &mImpl->transformManager);
+ node->CreateTransform( &mImpl->transformManager );
+ node->mGeometryBatcher = &mImpl->geometryBatcher;
break;
}
}
//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,
*mImpl->root,
mImpl->sortedLayers,
mImpl->renderSortingHelper,
+ mImpl->geometryBatcher,
mImpl->renderInstructions );
// Process the system-level RenderTasks last
*mImpl->systemLevelRoot,
mImpl->systemLevelSortedLayers,
mImpl->renderSortingHelper,
+ mImpl->geometryBatcher,
mImpl->renderInstructions );
}
}
* @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,
Integration::RenderController& controller,
RenderManager& renderManager,
RenderQueue& renderQueue,
- TextureCacheDispatcher& textureCacheDispatcher );
+ TextureCacheDispatcher& textureCacheDispatcher,
+ GeometryBatcher& geometryBatcher );
/**
* Destructor.
#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>
}
Node::Node()
-: mTransformManager(0),
+: mTransformManager( NULL ),
mTransformId( INVALID_TRANSFORM_ID ),
mParentOrigin( TRANSFORM_PROPERTY_PARENT_ORIGIN ),
mAnchorPoint( TRANSFORM_PROPERTY_ANCHOR_POINT ),
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 ),
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() );
mDirtyFlags = NothingFlag;
}
-void Node::SetParent(Node& parentNode)
+void Node::SetParent( Node& parentNode )
{
DALI_ASSERT_ALWAYS(this != &parentNode);
DALI_ASSERT_ALWAYS(!mIsRoot);
}
}
+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);
class Layer;
class RenderTask;
class UpdateManager;
+class GeometryBatcher;
/**
* Flag whether property has changed, during the Update phase.
* 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
* Get the renderer at the given index
* @param[in] index
*/
- Renderer* GetRendererAt( unsigned int index )
+ Renderer* GetRendererAt( unsigned int index ) const
{
return mRenderer[index];
}
(mTransformManager->IsLocalMatrixDirty( mTransformId ));
}
-
/**
* Retrieve the cached world-matrix of a node.
* @param[in] bufferIndex The buffer to read from.
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
* 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()
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
// 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
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
mResourcesReady( false ),
mFinishedResourceAcquisition( false ),
mPremultipledAlphaEnabled( false ),
+ mBatchingEnabled( false ),
mDepthIndex( 0 )
{
mUniformMapChanged[0] = false;
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;
}
}
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 )
{
*/
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
* Get the shader used by this renderer
* @return the shader this renderer uses
*/
- Shader& GetShader()
+ const Shader& GetShader() const
{
return *mShader;
}
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
*/
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.
public:
+ bool mBatchingEnabled : 1; ///< Flag indicating whether the render supports batching
int mDepthIndex; ///< Used only in PrepareRenderInstructions
};
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
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
};
};
* @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
};
};