Performance enhancement, cache glClear state 37/35137/4
authorNick Holland <nick.holland@partner.samsung.com>
Fri, 27 Feb 2015 16:02:53 +0000 (16:02 +0000)
committerNick Holland <nick.holland@partner.samsung.com>
Mon, 2 Mar 2015 15:08:16 +0000 (15:08 +0000)
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

13 files changed:
automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.cpp
automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.h
automated-tests/src/dali/utc-Dali-RenderableActor.cpp
dali/internal/file.list
dali/internal/render/common/render-algorithms.cpp
dali/internal/render/common/render-debug.cpp
dali/internal/render/common/render-list.h
dali/internal/render/common/render-manager.cpp
dali/internal/render/gl-resources/context.cpp
dali/internal/render/gl-resources/context.h
dali/internal/render/gl-resources/frame-buffer-state-cache.cpp [new file with mode: 0644]
dali/internal/render/gl-resources/frame-buffer-state-cache.h [new file with mode: 0644]
dali/internal/update/manager/prepare-render-instructions.cpp

index 23e05eb..dfc4346 100644 (file)
@@ -57,6 +57,8 @@ void TestGlAbstraction::Initialize()
   mLastProgramIdUsed = 0;
   mLastUniformIdUsed = 0;
   mLastShaderCompiled = 0;
+  mLastClearBitMask = 0;
+  mClearCount = 0;
 
   mLastBlendEquationRgb   = 0;
   mLastBlendEquationAlpha = 0;
index 0d8e934..67170e3 100644 (file)
@@ -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;
index b8c0412..f188683 100644 (file)
@@ -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;
+}
index db5bde7..7e22718 100644 (file)
@@ -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 \
index a04d234..27abfb6 100644 (file)
@@ -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();
index 6a512bd..9e43d44 100644 (file)
@@ -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";
     }
index 8b0e420..f435628 100644 (file)
@@ -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
+
   };
 
   /**
index e450c23..2f7b1aa 100644 (file)
@@ -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 );
   }
 
index ace0e95..2acd79d 100644 (file)
@@ -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
index 303b394..1ae8caa 100644 (file)
@@ -29,6 +29,7 @@
 #include <dali/integration-api/gl-defines.h>
 #include <dali/internal/render/common/performance-monitor.h>
 #include <dali/internal/render/gl-resources/texture-units.h>
+#include <dali/internal/render/gl-resources/frame-buffer-state-cache.h>
 #include <dali/internal/render/gl-resources/gl-call-debug.h>
 
 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 (file)
index 0000000..0ba565f
--- /dev/null
@@ -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 <dali/integration-api/debug.h>
+#include <dali/integration-api/gl-defines.h>
+
+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 (file)
index 0000000..2594aa9
--- /dev/null
@@ -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 <dali/integration-api/gl-abstraction.h>
+#include <dali/public-api/common/dali-vector.h>
+
+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__
index c793019..9481cb7 100644 (file)
@@ -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 );
 }
 
 /**