From 7c590d4036b30198e2fe0f14e7ec71a179ab75ec Mon Sep 17 00:00:00 2001 From: Nick Holland Date: Fri, 27 Feb 2015 16:02:53 +0000 Subject: [PATCH] Performance enhancement, cache glClear state DALi will perform glClear operations regardless of current state of the frame buffer. For example, dali-demo performs 1 redundant clear of the depth & stencil buffer per frame. Change-Id: I4877001f36468dc00380f6d459d3c8c20206ba30 --- .../dali-test-suite-utils/test-gl-abstraction.cpp | 2 + .../dali-test-suite-utils/test-gl-abstraction.h | 11 + .../src/dali/utc-Dali-RenderableActor.cpp | 181 +++++++++++++++++ dali/internal/file.list | 1 + dali/internal/render/common/render-algorithms.cpp | 19 +- dali/internal/render/common/render-debug.cpp | 4 +- dali/internal/render/common/render-list.h | 20 +- dali/internal/render/common/render-manager.cpp | 4 +- dali/internal/render/gl-resources/context.cpp | 14 +- dali/internal/render/gl-resources/context.h | 80 ++++++-- .../gl-resources/frame-buffer-state-cache.cpp | 224 +++++++++++++++++++++ .../render/gl-resources/frame-buffer-state-cache.h | 165 +++++++++++++++ .../update/manager/prepare-render-instructions.cpp | 12 +- 13 files changed, 693 insertions(+), 44 deletions(-) create mode 100644 dali/internal/render/gl-resources/frame-buffer-state-cache.cpp create mode 100644 dali/internal/render/gl-resources/frame-buffer-state-cache.h diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.cpp b/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.cpp index 23e05eb..dfc4346 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.cpp +++ b/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.cpp @@ -57,6 +57,8 @@ void TestGlAbstraction::Initialize() mLastProgramIdUsed = 0; mLastUniformIdUsed = 0; mLastShaderCompiled = 0; + mLastClearBitMask = 0; + mClearCount = 0; mLastBlendEquationRgb = 0; mLastBlendEquationAlpha = 0; diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.h b/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.h index 0d8e934..67170e3 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.h +++ b/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.h @@ -226,6 +226,8 @@ public: inline void Clear(GLbitfield mask) { + mClearCount++; + mLastClearBitMask = mask; } inline void ClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) @@ -1659,6 +1661,11 @@ public: // TEST FUNCTIONS return mLastProgramIdUsed; } + inline GLbitfield GetLastClearMask() const + { + return mLastClearBitMask; + } + enum AttribType { ATTRIB_UNKNOWN = -1, @@ -1686,6 +1693,8 @@ public: // TEST FUNCTIONS inline bool GetProgramBinaryCalled() const { return mGetProgramBinaryCalled; } + inline unsigned int GetClearCountCalled() const { return mClearCount; } + private: GLuint mCurrentProgram; GLuint mCompileStatus; @@ -1711,6 +1720,8 @@ private: typedef std::map< GLuint, std::string> ShaderSourceMap; ShaderSourceMap mShaderSources; GLuint mLastShaderCompiled; + GLbitfield mLastClearBitMask; + unsigned int mClearCount; Vector4 mLastBlendColor; GLenum mLastBlendEquationRgb; diff --git a/automated-tests/src/dali/utc-Dali-RenderableActor.cpp b/automated-tests/src/dali/utc-Dali-RenderableActor.cpp index b8c0412..f188683 100644 --- a/automated-tests/src/dali/utc-Dali-RenderableActor.cpp +++ b/automated-tests/src/dali/utc-Dali-RenderableActor.cpp @@ -899,3 +899,184 @@ int UtcDaliSetShaderEffectRecursively(void) END_TEST; } +int UtcDaliRenderableActorTestClearCache01(void) +{ + // Testing the framebuffer state caching in frame-buffer-state-caching.cpp + TestApplication application; + + tet_infoline("Testing Dali::RenderableActor::ClearCache01()"); + + BufferImage img = BufferImage::New( 1,1 ); + ImageActor actor = ImageActor::New( img ); + + actor.SetParentOrigin(ParentOrigin::CENTER); + actor.SetAnchorPoint(AnchorPoint::CENTER); + + Stage::GetCurrent().Add(actor); + + /**************************************************************/ + // Flush the queue and render once + application.SendNotification(); + application.Render(); + + // There should be a single call to Clear + DALI_TEST_EQUALS( application.GetGlAbstraction().GetClearCountCalled() , 1u, TEST_LOCATION ); + + // the last set clear mask should be COLOR, DEPTH & STENCIL which occurs at the start of each frame + GLbitfield mask = application.GetGlAbstraction().GetLastClearMask(); + DALI_TEST_CHECK( mask & GL_DEPTH_BUFFER_BIT ); + DALI_TEST_CHECK( mask & GL_STENCIL_BUFFER_BIT ); + DALI_TEST_CHECK( mask & GL_COLOR_BUFFER_BIT ); + + END_TEST; +} + +int UtcDaliRenderableActorTestClearCache02(void) +{ + // Testing the framebuffer state caching in frame-buffer-state-caching.cpp + TestApplication application; + + tet_infoline("Testing Dali::RenderableActor::ClearCache02()"); + + // use RGB so alpha is disabled and the actors are drawn opaque + BufferImage img = BufferImage::New( 10,10 ,Pixel::RGB888 ); + + // Without caching DALi perform clears in the following places + // Root + // | glClear #1 ( everything at start of frame ) + // | + // | + // | glClear #2 ( start of layer with opaque actors ) + // | ----> Layer1 + // | -> Actor 1 ( opaque ) + // | -> Actor 2 ( opaque ) + // | + // | + // | glClear #3 ( start of layer with opaque actors ) + // |----> Layer 2 + // | -> Actor 3 ( opaque ) + // | -> Actor 4 ( opaque ) + // + // With caching enabled glClear should only be called twice, at points #1 and #3. + // At #1 with depth, color and stencil cleared + // At #3 with depth cleared + // #2 is not required because the buffer has already been cleared at #1 + + Layer layer1 = Layer::New(); + layer1.Add( ImageActor::New( img ) ); + layer1.Add( ImageActor::New( img ) ); + + Layer layer2 = Layer::New(); + layer2.Add( ImageActor::New( img ) ); + layer2.Add( ImageActor::New( img ) ); + + Stage::GetCurrent().Add( layer1 ); + Stage::GetCurrent().Add( layer2 ); + + /**************************************************************/ + + // Flush the queue and render once + application.SendNotification(); + application.Render(); + + // There should be a 2 calls to Clear + DALI_TEST_EQUALS( application.GetGlAbstraction().GetClearCountCalled() , 2u, TEST_LOCATION ); + + // the last set clear mask should be DEPTH & STENCIL & COLOR + GLbitfield mask = application.GetGlAbstraction().GetLastClearMask(); + + tet_printf(" clear count = %d \n", application.GetGlAbstraction().GetClearCountCalled() ); + + // The last clear should just be DEPTH BUFFER, not color and stencil which were cleared at the start of the frame + DALI_TEST_CHECK( mask & GL_DEPTH_BUFFER_BIT ); + DALI_TEST_CHECK( ! ( mask & GL_COLOR_BUFFER_BIT ) ); + DALI_TEST_CHECK( ! ( mask & GL_STENCIL_BUFFER_BIT ) ); + + END_TEST; +} + +int UtcDaliRenderableActorTestClearCache03(void) +{ + // Testing the framebuffer state caching in frame-buffer-state-caching.cpp + TestApplication application; + + tet_infoline("Testing Dali::RenderableActor::ClearCache03()"); + + // use RGB so alpha is disabled and the actors are drawn opaque + BufferImage img = BufferImage::New( 10,10 ,Pixel::RGB888 ); + + // Without caching DALi perform clears in the following places + // Root + // | 1-## glClear ( COLOR, DEPTH, STENCIL ) + // | + // | ----> Layer1 + // | 2-## glClear ( STENCIL ) + // | -> Actor 1 ( stencil ) + // | 3-## glClear ( DEPTH ) + // | -> Actor 2 ( opaque ) // need 2 opaque actors to bypass optimisation of turning off depth test + // | -> Actor 3 ( opaque ) + // | + // | + // |----> Layer 2 + // | 4-## glClear ( STENCIL ) + // | -> Actor 4 ( stencil ) + // | 5-## glClear ( DEPTH ) + // | -> Actor 5 ( opaque ) // need 2 opaque actors to bypass optimisation of turning off depth test + // | -> Actor 6 ( opaque ) + // + // With caching enabled glClear will not be called at ## 2 and ## 3 ( because those buffers are already clear). + // + // @TODO Add further optimisation to look-ahead in the render-list to see if + // When performing STENCIL clear, check if there another layer after it. + // If there is, combine the STENCIL with a DEPTH clear. + // + + Layer layer1 = Layer::New(); + ImageActor actor1 = ImageActor::New( img ); + ImageActor actor2 = ImageActor::New( img ); + ImageActor actor3 = ImageActor::New( img ); + + actor2.SetDrawMode( DrawMode::STENCIL ); + + layer1.Add( actor1 ); + layer1.Add( actor2 ); + layer1.Add( actor3 ); + + Layer layer2 = Layer::New(); + ImageActor actor4 = ImageActor::New( img ); + ImageActor actor5 = ImageActor::New( img ); + ImageActor actor6 = ImageActor::New( img ); + + actor4.SetDrawMode( DrawMode::STENCIL ); + + layer2.Add( actor4 ); + layer2.Add( actor5 ); + layer2.Add( actor6 ); + + Stage::GetCurrent().Add( layer1 ); + Stage::GetCurrent().Add( layer2 ); + + + /**************************************************************/ + + // Flush the queue and render once + application.SendNotification(); + application.Render(); + + // There should be a 3 calls to Clear ( one for everything, one for stencil, one for depth buffer). + DALI_TEST_EQUALS( application.GetGlAbstraction().GetClearCountCalled() , 3u, TEST_LOCATION ); + + // the last set clear mask should be DEPTH & STENCIL & COLOR + GLbitfield mask = application.GetGlAbstraction().GetLastClearMask(); + + tet_printf(" clear count = %d \n", application.GetGlAbstraction().GetClearCountCalled() ); + tet_printf(" clear mask = %x \n", mask); + + // The last clear should just be DEPTH BUFFER and stencil + DALI_TEST_CHECK( !( mask & GL_COLOR_BUFFER_BIT ) ); + DALI_TEST_CHECK( !( mask & GL_STENCIL_BUFFER_BIT ) ); + DALI_TEST_CHECK( mask & GL_DEPTH_BUFFER_BIT ); + + + END_TEST; +} diff --git a/dali/internal/file.list b/dali/internal/file.list index db5bde7..7e22718 100644 --- a/dali/internal/file.list +++ b/dali/internal/file.list @@ -129,6 +129,7 @@ internal_src_files = \ $(internal_src_dir)/render/common/texture-cache-dispatcher.cpp \ $(internal_src_dir)/render/gl-resources/bitmap-texture.cpp \ $(internal_src_dir)/render/gl-resources/context.cpp \ + $(internal_src_dir)/render/gl-resources/frame-buffer-state-cache.cpp \ $(internal_src_dir)/render/gl-resources/compressed-bitmap-texture.cpp \ $(internal_src_dir)/render/gl-resources/frame-buffer-texture.cpp \ $(internal_src_dir)/render/gl-resources/gl-call-debug.cpp \ diff --git a/dali/internal/render/common/render-algorithms.cpp b/dali/internal/render/common/render-algorithms.cpp index a04d234..27abfb6 100644 --- a/dali/internal/render/common/render-algorithms.cpp +++ b/dali/internal/render/common/render-algorithms.cpp @@ -76,21 +76,21 @@ inline void ProcessRenderList( const RenderList& renderList, const unsigned int renderFlags = renderList.GetFlags(); - bool setDepthTest = ( ( renderFlags & RenderList::DEPTH_TEST ) != 0u ); - bool depthMask = ( ( renderFlags & RenderList::DEPTH_WRITE ) != 0u ); + bool enableDepthBuffer = ( ( renderFlags & RenderList::DEPTH_BUFFER_ENABLED ) != 0u ); + bool depthMask = ( ( renderFlags & RenderList::DEPTH_WRITE ) != 0u ); - GLbitfield clearMask = ( renderFlags & RenderList::DEPTH_CLEAR ) ? GL_DEPTH_BUFFER_BIT : 0u; + GLbitfield clearMask = ( renderFlags & RenderList::DEPTH_CLEAR ) ? GL_DEPTH_BUFFER_BIT : 0u; - context.SetDepthTest( setDepthTest ); + context.EnableDepthBuffer( enableDepthBuffer ); context.DepthMask( depthMask ); - // Stencil testing, writing, and clearing... - const bool enableStencilTest( renderFlags & RenderList::STENCIL_TEST ); + // Stencil enabled, writing, and clearing... + const bool enableStencilBuffer( renderFlags & RenderList::STENCIL_BUFFER_ENABLED ); const bool enableStencilWrite( renderFlags & RenderList::STENCIL_WRITE ); - context.SetStencilTest( enableStencilTest ); + context.EnableStencilBuffer( enableStencilBuffer ); - if( enableStencilTest ) + if( enableStencilBuffer ) { context.StencilFunc( (enableStencilWrite ? GL_ALWAYS : GL_EQUAL), 1, 0xFF ); context.StencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); @@ -105,7 +105,8 @@ inline void ProcessRenderList( const RenderList& renderList, // Clear depth and/or stencil buffer. if( clearMask ) { - context.Clear( clearMask ); + // only clear if the depth and/or stencil buffer have been written to after a previous clear + context.Clear( clearMask, Context::CHECK_CACHED_VALUES ); } size_t count = renderList.Count(); diff --git a/dali/internal/render/common/render-debug.cpp b/dali/internal/render/common/render-debug.cpp index 6a512bd..9e43d44 100644 --- a/dali/internal/render/common/render-debug.cpp +++ b/dali/internal/render/common/render-debug.cpp @@ -80,7 +80,7 @@ void PrintRenderList( const RenderList& list ) { debugStream << " with:"; - if( flags & RenderList::DEPTH_TEST ) + if( flags & RenderList::DEPTH_BUFFER_ENABLED ) { debugStream << " DEPTH_TEST"; } @@ -95,7 +95,7 @@ void PrintRenderList( const RenderList& list ) debugStream << " DEPTH_CLEAR"; } - if( flags & RenderList::STENCIL_TEST ) + if( flags & RenderList::STENCIL_BUFFER_ENABLED ) { debugStream << " STENCIL_TEST"; } diff --git a/dali/internal/render/common/render-list.h b/dali/internal/render/common/render-list.h index 8b0e420..f435628 100644 --- a/dali/internal/render/common/render-list.h +++ b/dali/internal/render/common/render-list.h @@ -52,16 +52,22 @@ struct RenderList public: /** - * The RenderFlags describe how the objects are rendered using the depth buffer. + * The RenderFlags describe how the objects are rendered using the depth and stencil buffer. + * + * The flags which relate to GL_DEPTH_TEST and GL_STENCIL_TEST are called + * DEPTH_BUFFER_ENABLED and STENCIL_BUFFER_ENABLED to avoid any confusion. + * E.g. if GL_DEPTH_TEST is not enabled you can't write to the depth buffer, which can cause confusion. + * */ enum RenderFlags { - DEPTH_TEST = 0x01, ///< If depth testing should be used - DEPTH_WRITE = 0x02, ///< If the depth buffer is writable - DEPTH_CLEAR = 0x04, ///< If the depth buffer should first be cleared - STENCIL_TEST = 0x08, ///< If stencil testing should be used - STENCIL_WRITE = 0x10, ///< If the stencil buffer is writable - STENCIL_CLEAR = 0x20 ///< If the stencil buffer should first be cleared + DEPTH_BUFFER_ENABLED = 1 << 0, ///< If depth buffer should be used for writing / test operations + DEPTH_WRITE = 1 << 1, ///< If the depth buffer is writable + DEPTH_CLEAR = 1 << 2, ///< If the depth buffer should first be cleared + STENCIL_BUFFER_ENABLED = 1 << 3, ///< If stencil buffer should be used for writing / test operation + STENCIL_WRITE = 1 << 4, ///< If the stencil buffer is writable + STENCIL_CLEAR = 1 << 5, ///< If the stencil buffer should first be cleared + }; /** diff --git a/dali/internal/render/common/render-manager.cpp b/dali/internal/render/common/render-manager.cpp index e450c23..2f7b1aa 100644 --- a/dali/internal/render/common/render-manager.cpp +++ b/dali/internal/render/common/render-manager.cpp @@ -378,7 +378,7 @@ bool RenderManager::Render( Integration::RenderStatus& status ) mImpl->context.ColorMask( true ); mImpl->context.DepthMask( true ); mImpl->context.StencilMask( 0xFF ); // 8 bit stencil mask, all 1's - mImpl->context.Clear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ); + mImpl->context.Clear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, Context::FORCE_CLEAR ); // reset the program matrices for all programs once per frame // this ensures we will set view and projection matrix once per program per camera @@ -506,7 +506,7 @@ void RenderManager::DoRender( RenderInstruction& instruction, Shader& defaultSha mImpl->context.SetScissorTest( true ); mImpl->context.Scissor( viewportRect.x, viewportRect.y, viewportRect.width, viewportRect.height ); mImpl->context.ColorMask( true ); - mImpl->context.Clear( GL_COLOR_BUFFER_BIT ); + mImpl->context.Clear( GL_COLOR_BUFFER_BIT , Context::CHECK_CACHED_VALUES ); mImpl->context.SetScissorTest( false ); } diff --git a/dali/internal/render/gl-resources/context.cpp b/dali/internal/render/gl-resources/context.cpp index ace0e95..2acd79d 100644 --- a/dali/internal/render/gl-resources/context.cpp +++ b/dali/internal/render/gl-resources/context.cpp @@ -68,14 +68,14 @@ Context::Context(Integration::GlAbstraction& glAbstraction) mColorMask(true), mStencilMask(0xFF), mBlendEnabled(false), - mDepthTestEnabled(false), + mDepthBufferEnabled(false), mDepthMaskEnabled(false), mDitherEnabled(true), // This the only GL capability which defaults to true mPolygonOffsetFillEnabled(false), mSampleAlphaToCoverageEnabled(false), mSampleCoverageEnabled(false), mScissorTestEnabled(false), - mStencilTestEnabled(false), + mStencilBufferEnabled(false), mClearColorSet(false), mBoundArrayBufferId(0), mBoundElementArrayBufferId(0), @@ -225,7 +225,7 @@ void Context::ResetGlState() mBlendEnabled = false; mGlAbstraction.Disable(GL_BLEND); - mDepthTestEnabled = false; + mDepthBufferEnabled = false; mGlAbstraction.Disable(GL_DEPTH_TEST); mDepthMaskEnabled = false; @@ -246,7 +246,7 @@ void Context::ResetGlState() mScissorTestEnabled = false; mGlAbstraction.Disable(GL_SCISSOR_TEST); - mStencilTestEnabled = false; + mStencilBufferEnabled = false; mGlAbstraction.Disable(GL_STENCIL_TEST); mBoundArrayBufferId = 0; @@ -301,6 +301,8 @@ void Context::ResetGlState() mViewPort.x = mViewPort.y = mViewPort.width = mViewPort.height = 0; ResetVertexAttributeState(); + + mFrameBufferStateCache.Reset(); } #ifdef DEBUG_ENABLED @@ -323,14 +325,14 @@ void Context::PrintCurrentState() "----------------- Context State END -----------------\n", mBlendEnabled ? "Enabled" : "Disabled", cullFaceModes[ mCullFaceMode ], - mDepthTestEnabled ? "Enabled" : "Disabled", + mDepthBufferEnabled ? "Enabled" : "Disabled", mDepthMaskEnabled ? "Enabled" : "Disabled", mDitherEnabled ? "Enabled" : "Disabled", mPolygonOffsetFillEnabled ? "Enabled" : "Disabled", mSampleAlphaToCoverageEnabled ? "Enabled" : "Disabled", mSampleCoverageEnabled ? "Enabled" : "Disabled", mScissorTestEnabled ? "Enabled" : "Disabled", - mStencilTestEnabled ? "Enabled" : "Disabled"); + mStencilBufferEnabled ? "Enabled" : "Disabled"); } #endif // DALI_CONTEXT_LOGGING diff --git a/dali/internal/render/gl-resources/context.h b/dali/internal/render/gl-resources/context.h index 303b394..1ae8caa 100644 --- a/dali/internal/render/gl-resources/context.h +++ b/dali/internal/render/gl-resources/context.h @@ -29,6 +29,7 @@ #include #include #include +#include #include namespace Dali @@ -44,6 +45,16 @@ namespace Internal class Context { public: + + /** + * FrameBuffer Clear mode + */ + enum ClearMode + { + FORCE_CLEAR, ///< always perform the glClear regardless of current state + CHECK_CACHED_VALUES ///< check the Frame buffers cached state to see if a clear is required + }; + /** * Size of the VertexAttributeArray enables * GLES specification states that there's minimum of 8 @@ -218,6 +229,8 @@ public: */ void BindFramebuffer(GLenum target, GLuint framebuffer) { + mFrameBufferStateCache.SetCurrentFrameBuffer( framebuffer ); + LOG_GL("BindFramebuffer %d %d\n", target, framebuffer); CHECK_GL( mGlAbstraction, mGlAbstraction.BindFramebuffer(target, framebuffer) ); } @@ -388,10 +401,16 @@ public: /** * Wrapper for OpenGL ES 2.0 glClear() */ - void Clear(GLbitfield mask) + void Clear(GLbitfield mask, ClearMode mode ) { - LOG_GL("Clear %d\n", mask); - CHECK_GL( mGlAbstraction, mGlAbstraction.Clear(mask) ); + bool forceClear = (mode == FORCE_CLEAR ); + mask = mFrameBufferStateCache.GetClearMask( mask, forceClear , mScissorTestEnabled ); + + if( mask > 0 ) + { + LOG_GL("Clear %d\n", mask); + CHECK_GL( mGlAbstraction, mGlAbstraction.Clear( mask ) ); + } } /** @@ -590,6 +609,8 @@ public: */ void DeleteFramebuffers(GLsizei n, const GLuint* framebuffers) { + mFrameBufferStateCache.FrameBuffersDeleted( n, framebuffers ); + LOG_GL("DeleteFramebuffers %d %p\n", n, framebuffers); CHECK_GL( mGlAbstraction, mGlAbstraction.DeleteFramebuffers(n, framebuffers) ); } @@ -679,6 +700,7 @@ public: */ void DrawArrays(GLenum mode, GLint first, GLsizei count) { + mFrameBufferStateCache.DrawOperation( mColorMask, DepthBufferWriteEnabled(), StencilBufferWriteEnabled() ); FlushVertexAttributeLocations(); LOG_GL("DrawArrays %x %d %d\n", mode, first, count); @@ -690,6 +712,7 @@ public: */ void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instanceCount) { + mFrameBufferStateCache.DrawOperation( mColorMask, DepthBufferWriteEnabled(), StencilBufferWriteEnabled() ); FlushVertexAttributeLocations(); LOG_GL("DrawArraysInstanced %x %d %d %d\n", mode, first, count, instanceCount); @@ -701,6 +724,7 @@ public: */ void DrawBuffers(GLsizei n, const GLenum* bufs) { + mFrameBufferStateCache.DrawOperation( mColorMask, DepthBufferWriteEnabled(), StencilBufferWriteEnabled() ); LOG_GL("DrawBuffers %d %p\n", n, bufs); CHECK_GL( mGlAbstraction, mGlAbstraction.DrawBuffers(n, bufs) ); } @@ -710,6 +734,8 @@ public: */ void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices) { + mFrameBufferStateCache.DrawOperation( mColorMask, DepthBufferWriteEnabled(), StencilBufferWriteEnabled() ); + FlushVertexAttributeLocations(); LOG_GL("DrawElements %x %d %d %p\n", mode, count, type, indices); @@ -721,6 +747,8 @@ public: */ void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instanceCount) { + mFrameBufferStateCache.DrawOperation( mColorMask, DepthBufferWriteEnabled(), StencilBufferWriteEnabled() ); + FlushVertexAttributeLocations(); LOG_GL("DrawElementsInstanced %x %d %d %p %d\n", mode, count, type, indices, instanceCount); @@ -732,6 +760,7 @@ public: */ void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices) { + mFrameBufferStateCache.DrawOperation( mColorMask, DepthBufferWriteEnabled(), StencilBufferWriteEnabled() ); FlushVertexAttributeLocations(); LOG_GL("DrawRangeElements %x %u %u %d %d %p\n", mode, start, end, count, type, indices); @@ -855,14 +884,18 @@ public: /** * This method replaces glEnable(GL_DEPTH_TEST) and glDisable(GL_DEPTH_TEST). + * Note GL_DEPTH_TEST means enable the depth buffer for writing and or testing. + * glDepthMask is used to enable / disable writing to depth buffer. + * glDepthFunc us used to control if testing is enabled and how it is performed ( default GL_LESS) + * * @param[in] enable True if GL_DEPTH_TEST should be enabled. */ - void SetDepthTest(bool enable) + void EnableDepthBuffer( bool enable ) { // Avoid unecessary calls to glEnable/glDisable - if (enable != mDepthTestEnabled) + if( enable != mDepthBufferEnabled ) { - mDepthTestEnabled = enable; + mDepthBufferEnabled = enable; if (enable) { @@ -999,14 +1032,17 @@ public: /** * This method replaces glEnable(GL_STENCIL_TEST) and glDisable(GL_STENCIL_TEST). + * Note GL_STENCIL_TEST means enable the stencil buffer for writing and or testing. + * glStencilMask is used to control how bits are written to the stencil buffer. + * glStencilFunc is used to control if testing is enabled and how it is performed ( default GL_ALWAYS ) * @param[in] enable True if GL_STENCIL_TEST should be enabled. */ - void SetStencilTest(bool enable) + void EnableStencilBuffer(bool enable) { // Avoid unecessary calls to glEnable/glDisable - if (enable != mStencilTestEnabled) + if( enable != mStencilBufferEnabled ) { - mStencilTestEnabled = enable; + mStencilBufferEnabled = enable; if (enable) { @@ -1118,6 +1154,8 @@ public: { LOG_GL("GenFramebuffers %d %p\n", n, framebuffers); CHECK_GL( mGlAbstraction, mGlAbstraction.GenFramebuffers(n, framebuffers) ); + + mFrameBufferStateCache.FrameBuffersCreated( n, framebuffers ); } /** @@ -1430,6 +1468,8 @@ public: */ void StencilFunc(GLenum func, GLint ref, GLuint mask) { + + LOG_GL("StencilFunc %x %d %d\n", func, ref, mask); CHECK_GL( mGlAbstraction, mGlAbstraction.StencilFunc(func, ref, mask) ); } @@ -1665,10 +1705,25 @@ public: return mRendererCount; } - private: // Implementation /** + * @return true if next draw operation will write to depth buffer + */ + bool DepthBufferWriteEnabled() const + { + return mDepthBufferEnabled && mDepthMaskEnabled; + } + + /** + * @return true if next draw operation will write to stencil buffer + */ + bool StencilBufferWriteEnabled() const + { + return mStencilBufferEnabled && ( mStencilMask > 0 ); + } + + /** * Flushes vertex attribute location changes to the driver */ void FlushVertexAttributeLocations(); @@ -1701,14 +1756,14 @@ private: // Data bool mColorMask; GLuint mStencilMask; bool mBlendEnabled; - bool mDepthTestEnabled; + bool mDepthBufferEnabled; bool mDepthMaskEnabled; bool mDitherEnabled; bool mPolygonOffsetFillEnabled; bool mSampleAlphaToCoverageEnabled; bool mSampleCoverageEnabled; bool mScissorTestEnabled; - bool mStencilTestEnabled; + bool mStencilBufferEnabled; bool mClearColorSet; // glBindBuffer() state @@ -1749,6 +1804,7 @@ private: // Data unsigned int mFrameCount; ///< Number of render frames unsigned int mCulledCount; ///< Number of culled renderers per frame unsigned int mRendererCount; ///< Number of image renderers per frame + FrameBufferStateCache mFrameBufferStateCache; ///< frame buffer state cache }; } // namespace Internal diff --git a/dali/internal/render/gl-resources/frame-buffer-state-cache.cpp b/dali/internal/render/gl-resources/frame-buffer-state-cache.cpp new file mode 100644 index 0000000..0ba565f --- /dev/null +++ b/dali/internal/render/gl-resources/frame-buffer-state-cache.cpp @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2015 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 "frame-buffer-state-cache.h" + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Internal +{ + +FrameBufferStateCache::FrameBufferStateCache() +:mCurrentFrameBufferId(0) +{ +} + +FrameBufferStateCache::~FrameBufferStateCache() +{ +} + +GLbitfield FrameBufferStateCache::GetClearMask( GLbitfield mask, bool forceClear, bool scissorTestEnabled ) +{ + if( scissorTestEnabled ) + { + // don't do anything if scissor test is enabled, in the future we could + // potentially keep track of frame buffer size vs scissor test size to see if the entire + // buffer is cleared or not. + return mask; + } + FrameBufferState* state = GetFrameBufferState( mCurrentFrameBufferId ); + if( !state ) + { + DALI_LOG_ERROR("FrameBuffer not found %d \n", mCurrentFrameBufferId); + return mask; + } + + // if we are forcing the clear operation, then just update the internal cached values + if( forceClear ) + { + SetClearState( state, mask ); + return mask; + } + + // use the cached values + if( mask & GL_COLOR_BUFFER_BIT) + { + // check if color buffer is currently clean + if( state->mState & COLOR_BUFFER_CLEAN ) + { + // remove clear color buffer flag from bitmask, no need to clear twice + mask&= ~GL_COLOR_BUFFER_BIT; + } + } + if( mask & GL_DEPTH_BUFFER_BIT) + { + // check if depth buffer is currently clean + if( state->mState & DEPTH_BUFFER_CLEAN ) + { + // remove clear depth buffer flag from bitmask, no need to clear twice + mask&= ~GL_DEPTH_BUFFER_BIT; + } + } + if( mask & GL_STENCIL_BUFFER_BIT) + { + // check if stencil buffer is currently clean + if( state->mState & STENCIL_BUFFER_CLEAN ) + { + // remove clear stencil buffer flag from bitmask, no need to clear twice + + mask&= ~GL_STENCIL_BUFFER_BIT; + } + } + + // set the clear state based, what's about to be cleared + SetClearState( state, mask ); + + return mask; +} + +void FrameBufferStateCache::SetCurrentFrameBuffer( GLuint frameBufferId ) +{ + mCurrentFrameBufferId = frameBufferId; +} + +void FrameBufferStateCache::FrameBuffersDeleted( GLsizei count, const GLuint* const frameBuffers ) +{ + for( GLsizei i = 0; i < count; ++i ) + { + DeleteFrameBuffer( frameBuffers[i] ); + } +} +void FrameBufferStateCache::FrameBuffersCreated( GLsizei count, const GLuint* const frameBuffers ) +{ + for( GLsizei i = 0; i < count; ++i ) + { + // check the frame buffer doesn't exist already + GLuint id = frameBuffers[i]; + + FrameBufferState* state = GetFrameBufferState( id ); + if( state ) + { + DALI_LOG_ERROR("FrameBuffer already exists%d \n", id ); + // reset its state + state->mState = GetInitialFrameBufferState(); + continue; + } + + FrameBufferState newFrameBuffer( frameBuffers[i], GetInitialFrameBufferState() ); + mFrameBufferStates.PushBack( newFrameBuffer ); + } +} + +void FrameBufferStateCache::DrawOperation( bool colorBuffer, bool depthBuffer, bool stencilBuffer ) +{ + FrameBufferState* state = GetFrameBufferState( mCurrentFrameBufferId ); + if( !state ) + { + // an error will have already been logged by the clear operation + return; + } + + if( colorBuffer ) + { + // un-set the clean bit + state->mState &= ~COLOR_BUFFER_CLEAN; + } + if( depthBuffer ) + { + // un-set the clean bit + state->mState &= ~DEPTH_BUFFER_CLEAN; + } + if( stencilBuffer ) + { + // un-set the clean bit + state->mState &= ~STENCIL_BUFFER_CLEAN; + } + +} + +void FrameBufferStateCache::Reset() +{ + mFrameBufferStates.Clear(); + + // create the default frame buffer + GLuint id = 0; // 0 == default frame buffer id + FrameBuffersCreated( 1, &id ); +} + +void FrameBufferStateCache::SetClearState( FrameBufferState* state, GLbitfield mask ) +{ + if( mask & GL_COLOR_BUFFER_BIT) + { + // set the color buffer to clean + state->mState |= COLOR_BUFFER_CLEAN; + } + if( mask & GL_DEPTH_BUFFER_BIT) + { + // set the depth buffer to clean + state->mState |= DEPTH_BUFFER_CLEAN; + } + if( mask & GL_STENCIL_BUFFER_BIT) + { + // set the stencil buffer to clean + state->mState |= STENCIL_BUFFER_CLEAN; + } +} + +FrameBufferStateCache::FrameBufferState* FrameBufferStateCache::GetFrameBufferState( GLuint frameBufferId ) +{ + for( FrameBufferStateVector::SizeType i = 0; i < mFrameBufferStates.Count(); ++i ) + { + FrameBufferState& state = mFrameBufferStates[i]; + if( state.mId == frameBufferId ) + { + return &state; + } + } + return NULL; +} + +void FrameBufferStateCache::DeleteFrameBuffer( GLuint frameBufferId ) +{ + FrameBufferStateVector::Iterator iter = mFrameBufferStates.Begin(); + FrameBufferStateVector::Iterator endIter = mFrameBufferStates.End(); + + for( ; iter != endIter ; ++iter ) + { + if( (*iter).mId == frameBufferId ) + { + mFrameBufferStates.Erase( iter); + return; + } + } + DALI_LOG_ERROR("FrameBuffer not found %d \n", frameBufferId); +} + +unsigned int FrameBufferStateCache::GetInitialFrameBufferState() +{ + return COLOR_BUFFER_CLEAN | DEPTH_BUFFER_CLEAN | STENCIL_BUFFER_CLEAN; +} + + +} // namespace Internal + +} // namespace Dali diff --git a/dali/internal/render/gl-resources/frame-buffer-state-cache.h b/dali/internal/render/gl-resources/frame-buffer-state-cache.h new file mode 100644 index 0000000..2594aa9 --- /dev/null +++ b/dali/internal/render/gl-resources/frame-buffer-state-cache.h @@ -0,0 +1,165 @@ +#ifndef __DALI_INTERNAL_CONTEXT_FRAME_BUFFER_STATE_CACHE_H__ +#define __DALI_INTERNAL_CONTEXT_FRAME_BUFFER_STATE_CACHE_H__ + +/* + * Copyright (c) 2015 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 + +namespace Dali +{ + +namespace Internal +{ + +/** + * @brief Keeps track of color, depth and stencil buffer state within each frame buffer. + * Used to avoid redundant glClear calls. + * + */ +class FrameBufferStateCache +{ +public: + + + /** + * @brief Constructor + */ + FrameBufferStateCache(); + + /** + * @brief non-virtual destructor + */ + ~FrameBufferStateCache(); + + /** + * @brief Get the bitmask to pass to glClear based on the mask requested + * and the current state of the frame buffer + * @param[in] mask glClear bit mask + * @param[in] forceClear whether to force the clear ( ignore cached state) + * @param[in] scissorTestEnabled whether scissor test is enabled + * @return new bitmask to pass to glClear + */ + GLbitfield GetClearMask( GLbitfield mask, bool forceClear, bool scissorTestEnabled ); + + /** + * @brief Set the current bound frame buffer + * @param[in] frameBufferId frame buffer id + */ + void SetCurrentFrameBuffer( GLuint frameBufferId ); + + /** + * @brief Called when frame buffers are deleted + * @param[in] count number of frame buffers + * @param[in] framebuffers array of frame buffer ids + */ + void FrameBuffersDeleted( GLsizei count, const GLuint* const frameBuffers ); + + /** + * @brief Called when frame buffers are created + * @param[in] count number of frame buffers + * @param[in] framebuffers array of frame buffer ids + */ + void FrameBuffersCreated( GLsizei count, const GLuint* const frameBuffers ); + + /** + * @brief Draw operation performed on the current frame buffer + * @param[in] colorBufferUsed whether the color buffer is being written to (glColorMask ) + * @param[in] depthBufferUsed whether the depth buffer is being written to (glDepthMask ) + * @param[in] stencilBufferUsed whether the stencil buffer is being written to (glStencilMask ) + */ + void DrawOperation( bool colorBufferUsed, bool depthBufferUsed, bool stencilBufferUsed ); + + /** + * Reset the cache + */ + void Reset(); + +private: + + /** + * Current status of the frame buffer + */ + enum FrameBufferStatus + { + COLOR_BUFFER_CLEAN = 1 << 0, ///< color buffer clean + DEPTH_BUFFER_CLEAN = 1 << 1, ///< depth buffer clean + STENCIL_BUFFER_CLEAN = 1 << 2, ///< stencil buffer clean + }; + + /** + * POD to store the status of frame buffer regarding color,depth and stencil buffers + */ + struct FrameBufferState + { + /** + * Constructor + */ + FrameBufferState( GLuint id, unsigned int state) + :mId( id ), + mState( state ) + { + } + GLuint mId; ///< Frame buffer id + unsigned int mState; ///< State, bitmask of FrameBufferStatus flags + }; + + typedef Dali::Vector< FrameBufferState > FrameBufferStateVector; + + /** + * @brief Set the clear state + * @param[in] pointer to frame buffer state object + * @param[in] mask clear mask + */ + void SetClearState( FrameBufferState* state, GLbitfield mask ); + + /** + * @brief Helper + * @param[in] frameBufferId frame buffer id + * @return pointer to frame buffer state object ( NULL if it doesn't exist) + */ + FrameBufferState* GetFrameBufferState( GLuint frameBufferId ); + + /** + * @brief Helper to delete a frame buffer state object + * @param[in] frameBufferId frame buffer id + */ + void DeleteFrameBuffer( GLuint frameBufferId ); + + /** + * @brief Get the default state of a frame buffer, before it's used + * @return initial state + */ + unsigned int GetInitialFrameBufferState(); + + FrameBufferStateCache( const FrameBufferStateCache& ); ///< undefined copy constructor + + FrameBufferStateCache& operator=( const FrameBufferStateCache& ); ///< undefined assignment operator + +private: // data + + FrameBufferStateVector mFrameBufferStates; ///< state of the frame buffers + GLuint mCurrentFrameBufferId; ///< currently bound frame buffer +}; + +} // namespace Internal + +} // namespace Dali + +#endif // __DALI_INTERNAL_CONTEXT_FRAME_BUFFER_STATE_CACHE_H__ diff --git a/dali/internal/update/manager/prepare-render-instructions.cpp b/dali/internal/update/manager/prepare-render-instructions.cpp index c793019..9481cb7 100644 --- a/dali/internal/update/manager/prepare-render-instructions.cpp +++ b/dali/internal/update/manager/prepare-render-instructions.cpp @@ -67,7 +67,7 @@ inline void SetOpaqueRenderFlags( RenderList& renderList, bool transparentRender else { // Prepare for rendering multiple opaque objects - unsigned int flags = RenderList::DEPTH_TEST | RenderList::DEPTH_WRITE | RenderList::DEPTH_CLEAR; // clear depth buffer, draw over the previously rendered layers; + unsigned int flags = RenderList::DEPTH_BUFFER_ENABLED | RenderList::DEPTH_WRITE | RenderList::DEPTH_CLEAR; // clear depth buffer, draw over the previously rendered layers; renderList.ClearFlags(); renderList.SetFlags(flags); @@ -75,7 +75,7 @@ inline void SetOpaqueRenderFlags( RenderList& renderList, bool transparentRender if( stencilRenderablesExist ) { - renderList.SetFlags( RenderList::STENCIL_TEST ); + renderList.SetFlags( RenderList::STENCIL_BUFFER_ENABLED ); } } @@ -98,12 +98,12 @@ inline void SetTransparentRenderFlags( RenderList& renderList, bool opaqueRender // objects should be rendered with depth test on to avoid background objects // appearing in front of opaque foreground objects. - renderList.SetFlags( RenderList::DEPTH_TEST ); + renderList.SetFlags( RenderList::DEPTH_BUFFER_ENABLED ); } if( stencilRenderablesExist ) { - renderList.SetFlags( RenderList::STENCIL_TEST ); + renderList.SetFlags( RenderList::STENCIL_BUFFER_ENABLED ); } } @@ -117,7 +117,7 @@ inline void SetOverlayRenderFlags( RenderList& renderList, bool stencilRenderabl { if(stencilRenderablesExist) { - renderList.SetFlags(RenderList::STENCIL_TEST); + renderList.SetFlags(RenderList::STENCIL_BUFFER_ENABLED); } } @@ -128,7 +128,7 @@ inline void SetOverlayRenderFlags( RenderList& renderList, bool stencilRenderabl inline void SetStencilRenderFlags( RenderList& renderList ) { renderList.ClearFlags(); - renderList.SetFlags(RenderList::STENCIL_CLEAR | RenderList::STENCIL_WRITE | RenderList::STENCIL_TEST); + renderList.SetFlags(RenderList::STENCIL_CLEAR | RenderList::STENCIL_WRITE | RenderList::STENCIL_BUFFER_ENABLED ); } /** -- 2.7.4