From: adam.b Date: Tue, 26 Jul 2016 17:33:21 +0000 (+0100) Subject: Geometry Batching X-Git-Tag: dali_1.2.0~5 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;ds=sidebyside;h=449cde6cfe99f1e9297bec8cdb5cedff37502afb;p=platform%2Fcore%2Fuifw%2Fdali-core.git Geometry Batching 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 --- diff --git a/automated-tests/src/dali-internal/CMakeLists.txt b/automated-tests/src/dali-internal/CMakeLists.txt index 4bc9f10..afedb0d 100644 --- a/automated-tests/src/dali-internal/CMakeLists.txt +++ b/automated-tests/src/dali-internal/CMakeLists.txt @@ -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 index 0000000..f2c679f --- /dev/null +++ b/automated-tests/src/dali-internal/utc-Dali-Internal-GeometryBatcher.cpp @@ -0,0 +1,403 @@ +#include +#include +#include + +#include + +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( &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( &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( &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; +} diff --git a/dali/internal/common/core-impl.cpp b/dali/internal/common/core-impl.cpp index 313fa72..b415482 100644 --- a/dali/internal/common/core-impl.cpp +++ b/dali/internal/common/core-impl.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -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; } diff --git a/dali/internal/common/core-impl.h b/dali/internal/common/core-impl.h index b9472cd..d40f827 100644 --- a/dali/internal/common/core-impl.h +++ b/dali/internal/common/core-impl.h @@ -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() diff --git a/dali/internal/common/math.cpp b/dali/internal/common/math.cpp index 1e3c051..3b0baf9 100644 --- a/dali/internal/common/math.cpp +++ b/dali/internal/common/math.cpp @@ -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]); + } +} diff --git a/dali/internal/common/math.h b/dali/internal/common/math.h index ebb6291..4fce869 100644 --- a/dali/internal/common/math.h +++ b/dali/internal/common/math.h @@ -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 diff --git a/dali/internal/event/actors/actor-impl.cpp b/dali/internal/event/actors/actor-impl.cpp index 5df7b5c..7b19757 100644 --- a/dali/internal/event/actors/actor-impl.cpp +++ b/dali/internal/event/actors/actor-impl.cpp @@ -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 diff --git a/dali/internal/event/actors/actor-impl.h b/dali/internal/event/actors/actor-impl.h index d38493b..d40bde1 100644 --- a/dali/internal/event/actors/actor-impl.h +++ b/dali/internal/event/actors/actor-impl.h @@ -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 diff --git a/dali/internal/event/rendering/renderer-impl.cpp b/dali/internal/event/rendering/renderer-impl.cpp index 7ee14a0..0b89b26 100644 --- a/dali/internal/event/rendering/renderer-impl.cpp +++ b/dali/internal/event/rendering/renderer-impl.cpp @@ -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 ) { } diff --git a/dali/internal/event/rendering/renderer-impl.h b/dali/internal/event/rendering/renderer-impl.h index 745159b..14a2b79 100644 --- a/dali/internal/event/rendering/renderer-impl.h +++ b/dali/internal/event/rendering/renderer-impl.h @@ -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 diff --git a/dali/internal/file.list b/dali/internal/file.list index 92a2e1f..e90d723 100644 --- a/dali/internal/file.list +++ b/dali/internal/file.list @@ -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 \ diff --git a/dali/internal/render/common/render-algorithms.cpp b/dali/internal/render/common/render-algorithms.cpp index 95c0cae..02353f3 100644 --- a/dali/internal/render/common/render-algorithms.cpp +++ b/dali/internal/render/common/render-algorithms.cpp @@ -25,11 +25,13 @@ #include #include #include +#include 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 ); } } } diff --git a/dali/internal/render/common/render-algorithms.h b/dali/internal/render/common/render-algorithms.h index a2e4c57..1bf2dd0 100644 --- a/dali/internal/render/common/render-algorithms.h +++ b/dali/internal/render/common/render-algorithms.h @@ -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 diff --git a/dali/internal/render/common/render-item.cpp b/dali/internal/render/common/render-item.cpp index d81f068..d7fd016 100644 --- a/dali/internal/render/common/render-item.cpp +++ b/dali/internal/render/common/render-item.cpp @@ -47,8 +47,10 @@ RenderItem::RenderItem() mSize(), mRenderer( NULL ), mNode( NULL ), + mBatchRenderGeometry( NULL ), mDepthIndex( 0 ), - mIsOpaque( true ) + mIsOpaque( true ), + mBatched( false ) { } diff --git a/dali/internal/render/common/render-item.h b/dali/internal/render/common/render-item.h index d429452..bd37bda 100644 --- a/dali/internal/render/common/render-item.h +++ b/dali/internal/render/common/render-item.h @@ -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: diff --git a/dali/internal/render/common/render-manager.cpp b/dali/internal/render/common/render-manager.cpp index a4281eb..497b0e5 100644 --- a/dali/internal/render/common/render-manager.cpp +++ b/dali/internal/render/common/render-manager.cpp @@ -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) diff --git a/dali/internal/render/common/render-manager.h b/dali/internal/render/common/render-manager.h index 93b9661..40cc7c1 100644 --- a/dali/internal/render/common/render-manager.h +++ b/dali/internal/render/common/render-manager.h @@ -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 ); /** diff --git a/dali/internal/render/renderers/render-geometry.cpp b/dali/internal/render/renderers/render-geometry.cpp index 803b6eb..0c3bd0a 100644 --- a/dali/internal/render/renderers/render-geometry.cpp +++ b/dali/internal/render/renderers/render-geometry.cpp @@ -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& indices ) mIndicesChanged = true; } +const Dali::Vector* 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& 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 ) diff --git a/dali/internal/render/renderers/render-geometry.h b/dali/internal/render/renderers/render-geometry.h index 205e0f1..6075858 100644 --- a/dali/internal/render/renderers/render-geometry.h +++ b/dali/internal/render/renderers/render-geometry.h @@ -78,12 +78,25 @@ public: void SetIndexBuffer( Dali::Vector& indices ); /** + * Obtains pointer to the storage of indexed elements + * @return Pointer to the index buffer + */ + const Dali::Vector* 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 diff --git a/dali/internal/render/renderers/render-property-buffer.cpp b/dali/internal/render/renderers/render-property-buffer.cpp index 6be4623..9664318 100644 --- a/dali/internal/render/renderers/render-property-buffer.cpp +++ b/dali/internal/render/renderers/render-property-buffer.cpp @@ -125,6 +125,11 @@ void PropertyBuffer::SetData( Dali::Vector* data, size_t size ) mDataChanged = true; } +void PropertyBuffer::UpdateData() +{ + mDataChanged = true; +} + bool PropertyBuffer::Update( Context& context ) { if( !mData || !mFormat || !mSize ) diff --git a/dali/internal/render/renderers/render-property-buffer.h b/dali/internal/render/renderers/render-property-buffer.h index ed1e0b2..47748a1 100644 --- a/dali/internal/render/renderers/render-property-buffer.h +++ b/dali/internal/render/renderers/render-property-buffer.h @@ -80,6 +80,12 @@ public: void SetData( Dali::Vector* 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 + inline T* GetDataTypedPtr() + { + Dali::Vector< char >* data = mData.Release(); + mData = data; + return reinterpret_cast( &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 diff --git a/dali/internal/render/renderers/render-renderer.cpp b/dali/internal/render/renderers/render-renderer.cpp index cbad8bb..cfaa2be 100644 --- a/dali/internal/render/renderers/render-renderer.cpp +++ b/dali/internal/render/renderers/render-renderer.cpp @@ -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 ); } } diff --git a/dali/internal/render/renderers/render-renderer.h b/dali/internal/render/renderers/render-renderer.h index d7e6575..8011c31 100644 --- a/dali/internal/render/renderers/render-renderer.h +++ b/dali/internal/render/renderers/render-renderer.h @@ -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 diff --git a/dali/internal/update/common/discard-queue.cpp b/dali/internal/update/common/discard-queue.cpp index e0114b4..e89c964 100644 --- a/dali/internal/update/common/discard-queue.cpp +++ b/dali/internal/update/common/discard-queue.cpp @@ -26,6 +26,7 @@ #include #include #include +#include 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 diff --git a/dali/internal/update/common/discard-queue.h b/dali/internal/update/common/discard-queue.h index a8a35ef..899d856 100644 --- a/dali/internal/update/common/discard-queue.h +++ b/dali/internal/update/common/discard-queue.h @@ -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 index 0000000..58a9dbb --- /dev/null +++ b/dali/internal/update/manager/geometry-batcher.cpp @@ -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 + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +void TransformVertexBuffer( TransformVertexBufferData& data ) +{ + const PositionType* source = reinterpret_cast( data.sourcePtr ); + PositionType* destination = reinterpret_cast( data.destinationPtr ); + + size_t componentSize = data.componentSize ? data.componentSize : sizeof( PositionType ); + const void* sourceEnd = (reinterpret_cast( source ) + ( data.vertexCount*componentSize )); + for( ; source < sourceEnd; + *(reinterpret_cast( &destination )) += componentSize, + *(reinterpret_cast( &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 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 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 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 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::iterator iter = mImpl->batchParents.begin(); + std::vector::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& vertexBufferDest = *( new Vector() ); + 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( transformParameters ); + break; + } + case Dali::Property::VECTOR3: + { + TransformVertexBuffer( transformParameters ); + break; + } + case Dali::Property::VECTOR4: + { + TransformVertexBuffer( 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::iterator iter = batchParentData.batches.begin(); + std::vector::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::Iterator iter = batchParent.batchedChildren.Begin(); + Vector::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 index 0000000..c19e034 --- /dev/null +++ b/dali/internal/update/manager/geometry-batcher.h @@ -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 +#include +#include + +// PUBLIC INCLUDES +#include + +/// 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 + diff --git a/dali/internal/update/manager/prepare-render-instructions.cpp b/dali/internal/update/manager/prepare-render-instructions.cpp index 41616a1..350d66e 100644 --- a/dali/internal/update/manager/prepare-render-instructions.cpp +++ b/dali/internal/update/manager/prepare-render-instructions.cpp @@ -34,6 +34,8 @@ #include #include #include +#include +#include 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( 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 #include #include +#include +#include #include #include #include #include #include -#include -#include -#include // 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 ); } } diff --git a/dali/internal/update/manager/update-manager.h b/dali/internal/update/manager/update-manager.h index e90ab9d..7f15563 100644 --- a/dali/internal/update/manager/update-manager.h +++ b/dali/internal/update/manager/update-manager.h @@ -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. diff --git a/dali/internal/update/nodes/node.cpp b/dali/internal/update/nodes/node.cpp index b3a9767..0a7dc5a 100644 --- a/dali/internal/update/nodes/node.cpp +++ b/dali/internal/update/nodes/node.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -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); imBatchingEnabled ) + { + 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); diff --git a/dali/internal/update/nodes/node.h b/dali/internal/update/nodes/node.h index 4846122..3f8bbac 100644 --- a/dali/internal/update/nodes/node.h +++ b/dali/internal/update/nodes/node.h @@ -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); iIsLocalMatrixDirty( 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 diff --git a/dali/internal/update/rendering/scene-graph-renderer.cpp b/dali/internal/update/rendering/scene-graph-renderer.cpp index 17b402f..c28a236 100644 --- a/dali/internal/update/rendering/scene-graph-renderer.cpp +++ b/dali/internal/update/rendering/scene-graph-renderer.cpp @@ -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 ) { diff --git a/dali/internal/update/rendering/scene-graph-renderer.h b/dali/internal/update/rendering/scene-graph-renderer.h index 4249b0f..1a86cb9 100644 --- a/dali/internal/update/rendering/scene-graph-renderer.h +++ b/dali/internal/update/rendering/scene-graph-renderer.h @@ -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 diff --git a/dali/public-api/actors/actor.h b/dali/public-api/actors/actor.h index b01ff3d..fda367b 100644 --- a/dali/public-api/actors/actor.h +++ b/dali/public-api/actors/actor.h @@ -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 }; }; diff --git a/dali/public-api/rendering/renderer.h b/dali/public-api/rendering/renderer.h index 9c06ea3..353dd28 100644 --- a/dali/public-api/rendering/renderer.h +++ b/dali/public-api/rendering/renderer.h @@ -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 }; };