Multi-level context caching 47/258847/7
authorRichard Huang <r.huang@samsung.com>
Tue, 25 May 2021 16:50:28 +0000 (17:50 +0100)
committerRichard Huang <r.huang@samsung.com>
Wed, 2 Jun 2021 15:22:32 +0000 (16:22 +0100)
Change-Id: I31f298c50e584df98f722cf01fd6eb6d4517d90b

17 files changed:
dali/internal/adaptor/common/combined-update-render-controller.cpp
dali/internal/graphics/gles-impl/egl-graphics-controller.cpp
dali/internal/graphics/gles-impl/egl-graphics-controller.h
dali/internal/graphics/gles-impl/file.list
dali/internal/graphics/gles-impl/gles-context-state-cache.h [new file with mode: 0644]
dali/internal/graphics/gles-impl/gles-context.cpp
dali/internal/graphics/gles-impl/gles-context.h
dali/internal/graphics/gles-impl/gles-framebuffer-state-cache.cpp [new file with mode: 0644]
dali/internal/graphics/gles-impl/gles-framebuffer-state-cache.h [new file with mode: 0644]
dali/internal/graphics/gles-impl/gles-graphics-buffer.cpp
dali/internal/graphics/gles-impl/gles-graphics-framebuffer.cpp
dali/internal/graphics/gles-impl/gles-graphics-pipeline.cpp
dali/internal/graphics/gles-impl/gles-graphics-pipeline.h
dali/internal/graphics/gles-impl/gles-graphics-texture.cpp
dali/internal/graphics/gles-impl/gles-graphics-texture.h
dali/internal/graphics/gles-impl/gles-graphics-types.h
dali/internal/graphics/gles/egl-graphics.cpp

index a8a1401..e26e549 100644 (file)
@@ -690,14 +690,14 @@ void CombinedUpdateRenderController::UpdateRenderThread()
           sceneSurfaceResized = scene.IsSurfaceRectChanged();
           windowSurface->SetIsResizing(sceneSurfaceResized);
 
-          windowSurface->InitializeGraphics();
-
           // clear previous frame damaged render items rects, buffer history is tracked on surface level
           mDamagedRects.clear();
 
           // Collect damage rects
           mCore.PreRender(scene, mDamagedRects);
 
+          graphics.ActivateSurfaceContext(windowSurface);
+
           // Render off-screen frame buffers first if any
           mCore.RenderScene(windowRenderStatus, scene, true);
 
index 772d4ef..545c257 100644 (file)
@@ -288,6 +288,7 @@ void EglGraphicsController::DeleteSurfaceContext(Dali::RenderSurfaceInterface* s
 void EglGraphicsController::ActivateResourceContext()
 {
   mCurrentContext = mContext.get();
+  mCurrentContext->GlContextCreated();
 }
 
 void EglGraphicsController::ActivateSurfaceContext(Dali::RenderSurfaceInterface* surface)
@@ -301,6 +302,7 @@ void EglGraphicsController::ActivateSurfaceContext(Dali::RenderSurfaceInterface*
     if(iter != mSurfaceContexts.end())
     {
       mCurrentContext = iter->second.get();
+      mCurrentContext->GlContextCreated();
     }
   }
 }
@@ -335,7 +337,7 @@ void EglGraphicsController::ProcessDiscardQueues()
   ProcessDiscardQueue<GLES::Framebuffer>(mDiscardFramebufferQueue);
 
   // Process pipelines
-  ProcessDiscardQueue<GLES::Pipeline>(mDiscardPipelineQueue);
+  ProcessDiscardQueue(mDiscardPipelineQueue);
 
   // Process programs
   ProcessDiscardQueue<GLES::Program>(mDiscardProgramQueue);
@@ -428,14 +430,7 @@ void EglGraphicsController::ProcessCommandBuffer(const GLES::CommandBuffer& comm
       }
       case GLES::CommandType::SET_SCISSOR_TEST:
       {
-        if(cmd.scissorTest.enable)
-        {
-          mGlAbstraction->Enable(GL_SCISSOR_TEST);
-        }
-        else
-        {
-          mGlAbstraction->Disable(GL_SCISSOR_TEST);
-        }
+        mCurrentContext->SetScissorTestEnabled(cmd.scissorTest.enable);
         break;
       }
       case GLES::CommandType::SET_VIEWPORT: // @todo Consider correcting for orientation here?
@@ -627,7 +622,7 @@ void EglGraphicsController::ProcessTextureUpdateQueue()
       }
 
       mGlAbstraction->PixelStorei(GL_UNPACK_ALIGNMENT, 1);
-      mGlAbstraction->BindTexture(bindTarget, texture->GetGLTexture());
+      mCurrentContext->BindTexture(bindTarget, texture->GetTextureTypeId(), texture->GetGLTexture());
 
       if(!isSubImage)
       {
@@ -751,8 +746,8 @@ void EglGraphicsController::ProcessTextureMipmapGenerationQueue()
   {
     auto* texture = mTextureMipmapGenerationRequests.front();
 
-    mGlAbstraction->BindTexture(texture->GetGlTarget(), texture->GetGLTexture());
-    mGlAbstraction->GenerateMipmap(texture->GetGlTarget());
+    mCurrentContext->BindTexture(texture->GetGlTarget(), texture->GetTextureTypeId(), texture->GetGLTexture());
+    mCurrentContext->GenerateMipmap(texture->GetGlTarget());
 
     mTextureMipmapGenerationRequests.pop();
   }
index 673e5a9..be883bf 100644 (file)
@@ -135,6 +135,19 @@ public:
   {
     // Final flush
     Flush();
+
+    if(mContext)
+    {
+      mContext->GlContextDestroyed();
+    }
+
+    for(auto&& context : mSurfaceContexts)
+    {
+      if(context.second)
+      {
+        context.second->GlContextDestroyed();
+      }
+    }
   }
 
   /**
@@ -448,6 +461,12 @@ public:
     // Process main command queue
     ProcessCommandQueues();
 
+    // Reset texture cache in the contexts while destroying textures
+    ResetTextureCache();
+
+    // Reset buffer cache in the contexts while destroying buffers
+    ResetBufferCache();
+
     // Process discards
     ProcessDiscardQueues();
 
@@ -483,9 +502,9 @@ public:
   }
 
   /**
-   * @brief Processes a create queue for type specified
+   * @brief Processes a discard queue for type specified
    *
-   * @param[in,out] queue Reference to the create queue
+   * @param[in,out] queue Reference to the discard queue
    */
   template<class U, class T>
   void ProcessDiscardQueue(T& queue)
@@ -516,6 +535,53 @@ public:
   }
 
   /**
+   * @brief Processes a discard queue for pipeline
+   *
+   * @param[in,out] queue Reference to the create queue
+   */
+  void ProcessDiscardQueue(std::queue<GLES::Pipeline*>& queue)
+  {
+    while(!queue.empty())
+    {
+      auto* object = const_cast<GLES::Pipeline*>(queue.front());
+
+      // Inform the contexts to invalidate the pipeline if cached
+      if(mContext)
+      {
+        mContext->InvalidateCachedPipeline(object);
+      }
+
+      for(auto&& context : mSurfaceContexts)
+      {
+        if(context.second)
+        {
+          context.second->InvalidateCachedPipeline(object);
+        }
+      }
+
+      // Destroy
+      object->DestroyResource();
+
+      // Free
+      auto* clbk = object->GetCreateInfo().allocationCallbacks;
+      if(clbk)
+      {
+        // Call destructor
+        using GLESPipeline = GLES::Pipeline;
+        object->~GLESPipeline();
+
+        // Free memory
+        clbk->freeCallback(object, clbk->userData);
+      }
+      else
+      {
+        delete object;
+      }
+      queue.pop();
+    }
+  }
+
+  /**
    * @brief Processes all resource create queues
    */
   void ProcessCreateQueues();
@@ -580,6 +646,44 @@ public:
     return mIsShuttingDown;
   }
 
+  /**
+   * @brief Reset texture cache in the contexts
+   */
+  void ResetTextureCache()
+  {
+    if(mContext)
+    {
+      mContext->GetGLStateCache().ResetTextureCache();
+    }
+
+    for(auto& context : mSurfaceContexts)
+    {
+      if(context.second)
+      {
+        context.second->GetGLStateCache().ResetTextureCache();
+      }
+    }
+  }
+
+  /**
+   * @brief Reset buffer cache in the contexts
+   */
+  void ResetBufferCache()
+  {
+    if(mContext)
+    {
+      mContext->GetGLStateCache().ResetBufferCache();
+    }
+
+    for(auto& context : mSurfaceContexts)
+    {
+      if(context.second)
+      {
+        context.second->GetGLStateCache().ResetBufferCache();
+      }
+    }
+  }
+
   void ProcessCommandBuffer(const GLES::CommandBuffer& commandBuffer);
 
   // Resolves presentation
@@ -611,6 +715,16 @@ public:
    */
   void ActivateSurfaceContext(Dali::RenderSurfaceInterface* surface);
 
+  /**
+   * @brief Returns the current context
+   *
+   * @return the current context
+   */
+  GLES::Context* GetCurrentContext() const
+  {
+    return mCurrentContext;
+  }
+
 private:
   Integration::GlAbstraction*              mGlAbstraction{nullptr};
   Integration::GlContextHelperAbstraction* mGlContextHelperAbstraction{nullptr};
index 84481fd..1b0af65 100644 (file)
@@ -21,4 +21,5 @@ SET( adaptor_graphics_gles_src_files ${adaptor_graphics_gles_src_files}
     ${adaptor_graphics_dir}/gles-impl/gles-graphics-pipeline-cache.cpp
     ${adaptor_graphics_dir}/gles-impl/gles-context.cpp
     ${adaptor_graphics_dir}/gles-impl/gles-sync-object.cpp
+    ${adaptor_graphics_dir}/gles-impl/gles-framebuffer-state-cache.cpp
 )
diff --git a/dali/internal/graphics/gles-impl/gles-context-state-cache.h b/dali/internal/graphics/gles-impl/gles-context-state-cache.h
new file mode 100644 (file)
index 0000000..07f1512
--- /dev/null
@@ -0,0 +1,142 @@
+#ifndef DALI_GRAPHICS_GLES_CONTEXT_STATE_CACHE_H
+#define DALI_GRAPHICS_GLES_CONTEXT_STATE_CACHE_H
+
+/*
+ * Copyright (c) 2021 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.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/graphics-api/graphics-types.h>
+#include <dali/integration-api/gl-abstraction.h>
+#include <dali/integration-api/gl-defines.h>
+
+// INTERNAL INCLUDES
+#include "gles-framebuffer-state-cache.h"
+
+namespace Dali::Graphics
+{
+namespace GLES
+{
+namespace
+{
+static constexpr unsigned int MAX_TEXTURE_UNITS        = 32; // As what is defined in gl-defines.h, which is more than DALi uses anyways
+static constexpr unsigned int MAX_TEXTURE_TARGET       = 4;  // We only support GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP and GL_TEXTURE_EXTERNAL_OES
+static constexpr unsigned int MAX_ATTRIBUTE_CACHE_SIZE = 8;  // Size of the VertexAttributeArray enables
+} // namespace
+
+/**
+ * @brief Cache of GL State per context
+ */
+struct GLStateCache
+{
+  /**
+   * Reset the cached texture ids.
+   */
+  void ResetTextureCache()
+  {
+    // reset the cached texture id's in case the driver re-uses them
+    // when creating new textures
+    for(unsigned int i = 0; i < MAX_TEXTURE_UNITS; ++i)
+    {
+      for(unsigned int j = 0; j < MAX_TEXTURE_TARGET; ++j)
+      {
+        mBoundTextureId[i][j] = 0;
+      }
+    }
+  }
+
+  /**
+   * Reset the cached buffer ids.
+   */
+  void ResetBufferCache()
+  {
+    // reset the cached buffer id's
+    // fixes problem where some drivers will a generate a buffer with the
+    // same id, as the last deleted buffer id.
+    mBoundArrayBufferId        = 0;
+    mBoundElementArrayBufferId = 0;
+  }
+
+  /**
+   * @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);
+  }
+
+  // glEnable/glDisable states
+  bool   mColorMask{true};
+  GLuint mStencilMask{0xFF};
+  bool   mBlendEnabled{false};
+  bool   mDepthBufferEnabled{false};
+  bool   mDepthMaskEnabled{false};
+  bool   mScissorTestEnabled{false};
+  bool   mStencilBufferEnabled{false};
+  bool   mClearColorSet{false};
+
+  // glBindBuffer() state
+  GLuint mBoundArrayBufferId{0};        ///< The ID passed to glBindBuffer(GL_ARRAY_BUFFER)
+  GLuint mBoundElementArrayBufferId{0}; ///< The ID passed to glBindBuffer(GL_ELEMENT_ARRAY_BUFFER)
+
+  // glBindTexture() state
+  GLenum mActiveTextureUnit{MAX_TEXTURE_UNITS};
+  GLuint mBoundTextureId[MAX_TEXTURE_UNITS][MAX_TEXTURE_TARGET]; ///< The ID passed to glBindTexture()
+
+  // glBlendFuncSeparate() state
+  BlendFactor mBlendFuncSeparateSrcRGB{BlendFactor::ONE};    ///< The srcRGB parameter passed to glBlendFuncSeparate()
+  BlendFactor mBlendFuncSeparateDstRGB{BlendFactor::ZERO};   ///< The dstRGB parameter passed to glBlendFuncSeparate()
+  BlendFactor mBlendFuncSeparateSrcAlpha{BlendFactor::ONE};  ///< The srcAlpha parameter passed to glBlendFuncSeparate()
+  BlendFactor mBlendFuncSeparateDstAlpha{BlendFactor::ZERO}; ///< The dstAlpha parameter passed to glBlendFuncSeparate()
+
+  // glBlendEquationSeparate state
+  BlendOp mBlendEquationSeparateModeRGB{BlendOp::ADD};   ///< Controls RGB blend mode
+  BlendOp mBlendEquationSeparateModeAlpha{BlendOp::ADD}; ///< Controls Alpha blend mode
+
+  // glStencilFunc() and glStencilOp() state.
+  CompareOp mStencilFunc{CompareOp::ALWAYS};
+  GLuint    mStencilFuncRef{0};
+  GLuint    mStencilFuncMask{0xFFFFFFFF};
+  StencilOp mStencilOpFail{StencilOp::KEEP};
+  StencilOp mStencilOpDepthFail{StencilOp::KEEP};
+  StencilOp mStencilOpDepthPass{StencilOp::KEEP};
+
+  CompareOp mDepthFunction{CompareOp::LESS}; ///The depth function
+
+  Vector4 mClearColor{Color::WHITE}; ///< clear color. never used until it's been set by the user
+
+  CullMode mCullFaceMode{CullMode::NONE}; ///< Face culling mode
+
+  // Vertex Attribute Buffer enable caching
+  bool mVertexAttributeCachedState[MAX_ATTRIBUTE_CACHE_SIZE];  ///< Value cache for Enable Vertex Attribute
+  bool mVertexAttributeCurrentState[MAX_ATTRIBUTE_CACHE_SIZE]; ///< Current state on the driver for Enable Vertex Attribute
+
+  FrameBufferStateCache mFrameBufferStateCache{}; ///< frame buffer state cache
+};
+
+} // namespace GLES
+
+} // namespace Dali::Graphics
+
+#endif // DALI_GRAPHICS_GLES_CONTEXT_STATE_CACHE_H
index 974869a..fb32249 100644 (file)
@@ -39,10 +39,110 @@ struct Context::Impl
 
   ~Impl() = default;
 
+  /**
+   * Sets the initial GL state.
+   */
+  void InitializeGlState()
+  {
+    auto& gl = *mController.GetGL();
+
+    mGlStateCache.mClearColorSet        = false;
+    mGlStateCache.mColorMask            = true;
+    mGlStateCache.mStencilMask          = 0xFF;
+    mGlStateCache.mBlendEnabled         = false;
+    mGlStateCache.mDepthBufferEnabled   = false;
+    mGlStateCache.mDepthMaskEnabled     = false;
+    mGlStateCache.mScissorTestEnabled   = false;
+    mGlStateCache.mStencilBufferEnabled = false;
+
+    gl.Disable(GL_DITHER);
+
+    mGlStateCache.mBoundArrayBufferId        = 0;
+    mGlStateCache.mBoundElementArrayBufferId = 0;
+    mGlStateCache.mActiveTextureUnit         = 0;
+
+    mGlStateCache.mBlendFuncSeparateSrcRGB   = BlendFactor::ONE;
+    mGlStateCache.mBlendFuncSeparateDstRGB   = BlendFactor::ZERO;
+    mGlStateCache.mBlendFuncSeparateSrcAlpha = BlendFactor::ONE;
+    mGlStateCache.mBlendFuncSeparateDstAlpha = BlendFactor::ZERO;
+
+    // initial state is GL_FUNC_ADD for both RGB and Alpha blend modes
+    mGlStateCache.mBlendEquationSeparateModeRGB   = BlendOp::ADD;
+    mGlStateCache.mBlendEquationSeparateModeAlpha = BlendOp::ADD;
+
+    mGlStateCache.mCullFaceMode = CullMode::NONE; //By default cullface is disabled, front face is set to CCW and cull face is set to back
+
+    //Initialze vertex attribute cache
+    memset(&mGlStateCache.mVertexAttributeCachedState, 0, sizeof(mGlStateCache.mVertexAttributeCachedState));
+    memset(&mGlStateCache.mVertexAttributeCurrentState, 0, sizeof(mGlStateCache.mVertexAttributeCurrentState));
+
+    //Initialize bound 2d texture cache
+    memset(&mGlStateCache.mBoundTextureId, 0, sizeof(mGlStateCache.mBoundTextureId));
+
+    mGlStateCache.mFrameBufferStateCache.Reset();
+  }
+
+  /**
+   * Flushes vertex attribute location changes to the driver
+   */
+  void FlushVertexAttributeLocations()
+  {
+    auto& gl = *mController.GetGL();
+
+    for(unsigned int i = 0; i < MAX_ATTRIBUTE_CACHE_SIZE; ++i)
+    {
+      // see if the cached state is different to the actual state
+      if(mGlStateCache.mVertexAttributeCurrentState[i] != mGlStateCache.mVertexAttributeCachedState[i])
+      {
+        // it's different so make the change to the driver and update the cached state
+        mGlStateCache.mVertexAttributeCurrentState[i] = mGlStateCache.mVertexAttributeCachedState[i];
+
+        if(mGlStateCache.mVertexAttributeCurrentState[i])
+        {
+          gl.EnableVertexAttribArray(i);
+        }
+        else
+        {
+          gl.DisableVertexAttribArray(i);
+        }
+      }
+    }
+  }
+
+  /**
+   * Either enables or disables a vertex attribute location in the cache
+   * The cahnges won't take affect until FlushVertexAttributeLocations is called
+   * @param location attribute location
+   * @param state attribute state
+   */
+  void SetVertexAttributeLocation(unsigned int location, bool state)
+  {
+    auto& gl = *mController.GetGL();
+
+    if(location >= MAX_ATTRIBUTE_CACHE_SIZE)
+    {
+      // not cached, make the gl call through context
+      if(state)
+      {
+        gl.EnableVertexAttribArray(location);
+      }
+      else
+      {
+        gl.DisableVertexAttribArray(location);
+      }
+    }
+    else
+    {
+      // set the cached state, it will be set at the next draw call
+      // if it's different from the current driver state
+      mGlStateCache.mVertexAttributeCachedState[location] = state;
+    }
+  }
+
   EglGraphicsController& mController;
 
-  const GLES::Pipeline* mCurrentPipeline{nullptr}; ///< Currently bound pipeline
-  const GLES::Pipeline* mNewPipeline{nullptr};     ///< New pipeline to be set on flush
+  const GLES::PipelineImpl* mCurrentPipeline{nullptr}; ///< Currently bound pipeline
+  const GLES::PipelineImpl* mNewPipeline{nullptr};     ///< New pipeline to be set on flush
 
   std::vector<Graphics::TextureBinding> mCurrentTextureBindings{};
   std::vector<Graphics::SamplerBinding> mCurrentSamplerBindings{};
@@ -64,6 +164,10 @@ struct Context::Impl
   // Current render pass and render target
   const GLES::RenderTarget* mCurrentRenderTarget{nullptr};
   const GLES::RenderPass*   mCurrentRenderPass{nullptr};
+
+  GLStateCache mGlStateCache{}; ///< GL status cache
+
+  bool mGlContextCreated{false}; ///< True if the OpenGL context has been created
 };
 
 Context::Context(EglGraphicsController& controller)
@@ -77,29 +181,30 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall)
 {
   auto& gl = *mImpl->mController.GetGL();
 
-  // Change pipeline
-  if(mImpl->mNewPipeline)
+  // Execute states if pipeline is changed
+  const auto& currentProgram = mImpl->mCurrentPipeline ? static_cast<const GLES::Program*>(mImpl->mCurrentPipeline->GetCreateInfo().programState->program) : nullptr;
+  const auto& newProgram     = static_cast<const GLES::Program*>(mImpl->mNewPipeline->GetCreateInfo().programState->program);
+
+  if(mImpl->mCurrentPipeline != mImpl->mNewPipeline)
   {
-    // Execute states if different
-    mImpl->mCurrentPipeline = mImpl->mNewPipeline;
-    mImpl->mNewPipeline     = nullptr;
-  }
-  mImpl->mCurrentPipeline->GetPipeline().Bind(nullptr);
+    if(!currentProgram || currentProgram->GetImplementation()->GetGlProgram() != newProgram->GetImplementation()->GetGlProgram())
+    {
+      mImpl->mNewPipeline->Bind(newProgram->GetImplementation()->GetGlProgram());
+    }
 
-  // Blend state
-  ResolveBlendState();
+    // Blend state
+    ResolveBlendState();
 
-  // Resolve rasterization state
-  ResolveRasterizationState();
+    // Resolve rasterization state
+    ResolveRasterizationState();
+  }
 
   // Resolve uniform buffers
   ResolveUniformBuffers();
 
   // Bind textures
   // Map binding# to sampler location
-  const auto program = static_cast<const GLES::Program*>(mImpl->mCurrentPipeline->GetCreateInfo().programState->program);
-
-  const auto& reflection = program->GetReflection();
+  const auto& reflection = newProgram->GetReflection();
   const auto& samplers   = reflection.GetSamplers();
   for(const auto& binding : mImpl->mCurrentTextureBindings)
   {
@@ -115,6 +220,7 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall)
     }
 
     texture->Bind(binding);
+
     texture->Prepare(); // @todo also non-const.
 
     if(binding.binding < samplers.size()) // binding maps to texture unit. (texture bindings should also be in binding order)
@@ -126,19 +232,21 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall)
   }
 
   // for each attribute bind vertices
-  const auto& pipelineState = mImpl->mCurrentPipeline->GetCreateInfo();
+  const auto& pipelineState = mImpl->mNewPipeline->GetCreateInfo();
   const auto& vi            = pipelineState.vertexInputState;
   for(const auto& attr : vi->attributes)
   {
     // Enable location
-    gl.EnableVertexAttribArray(attr.location);
+    mImpl->SetVertexAttributeLocation(attr.location, true);
+
     const auto& bufferSlot    = mImpl->mCurrentVertexBufferBindings[attr.binding];
     const auto& bufferBinding = vi->bufferBindings[attr.binding];
 
     auto glesBuffer = bufferSlot.buffer->GetGLBuffer();
 
     // Bind buffer
-    gl.BindBuffer(GL_ARRAY_BUFFER, glesBuffer);
+    BindBuffer(GL_ARRAY_BUFFER, glesBuffer);
+
     gl.VertexAttribPointer(attr.location,
                            GLVertexFormat(attr.format).size,
                            GLVertexFormat(attr.format).format,
@@ -148,7 +256,7 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall)
   }
 
   // Resolve topology
-  const auto& ia = mImpl->mCurrentPipeline->GetCreateInfo().inputAssemblyState;
+  const auto& ia = mImpl->mNewPipeline->GetCreateInfo().inputAssemblyState;
 
   // Bind uniforms
 
@@ -157,6 +265,11 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall)
   {
     case DrawCallDescriptor::Type::DRAW:
     {
+      mImpl->mGlStateCache.mFrameBufferStateCache.DrawOperation(mImpl->mGlStateCache.mColorMask,
+                                                                mImpl->mGlStateCache.DepthBufferWriteEnabled(),
+                                                                mImpl->mGlStateCache.StencilBufferWriteEnabled());
+      mImpl->FlushVertexAttributeLocations();
+
       gl.DrawArrays(GLESTopology(ia->topology),
                     drawCall.draw.firstVertex,
                     drawCall.draw.vertexCount);
@@ -164,9 +277,14 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall)
     }
     case DrawCallDescriptor::Type::DRAW_INDEXED:
     {
-      const auto& binding    = mImpl->mCurrentIndexBufferBinding;
-      const auto* glesBuffer = binding.buffer;
-      gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, glesBuffer->GetGLBuffer());
+      const auto& binding = mImpl->mCurrentIndexBufferBinding;
+      BindBuffer(GL_ELEMENT_ARRAY_BUFFER, binding.buffer->GetGLBuffer());
+
+      mImpl->mGlStateCache.mFrameBufferStateCache.DrawOperation(mImpl->mGlStateCache.mColorMask,
+                                                                mImpl->mGlStateCache.DepthBufferWriteEnabled(),
+                                                                mImpl->mGlStateCache.StencilBufferWriteEnabled());
+      mImpl->FlushVertexAttributeLocations();
+
       auto indexBufferFormat = GLIndexFormat(binding.format).format;
       gl.DrawElements(GLESTopology(ia->topology),
                       drawCall.drawIndexed.indexCount,
@@ -181,6 +299,13 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall)
   }
 
   ClearState();
+
+  // Change pipeline
+  if(mImpl->mNewPipeline)
+  {
+    mImpl->mCurrentPipeline = mImpl->mNewPipeline;
+    mImpl->mNewPipeline     = nullptr;
+  }
 }
 
 void Context::BindTextures(const std::vector<Graphics::TextureBinding>& bindings)
@@ -217,7 +342,8 @@ void Context::BindIndexBuffer(const IndexBufferBindingDescriptor& indexBufferBin
 
 void Context::BindPipeline(const GLES::Pipeline* newPipeline)
 {
-  mImpl->mNewPipeline = newPipeline;
+  DALI_ASSERT_ALWAYS(newPipeline && "Invalid pipeline");
+  mImpl->mNewPipeline = &newPipeline->GetPipeline();
 }
 
 void Context::BindUniformBuffers(const std::vector<UniformBufferBindingDescriptor>& uboBindings,
@@ -245,68 +371,115 @@ void Context::BindUniformBuffers(const std::vector<UniformBufferBindingDescripto
 
 void Context::ResolveBlendState()
 {
-  const auto& state = mImpl->mCurrentPipeline->GetCreateInfo();
-  const auto& bs    = state.colorBlendState;
-  auto&       gl    = *mImpl->mController.GetGL();
+  const auto& currentBlendState = mImpl->mCurrentPipeline ? mImpl->mCurrentPipeline->GetCreateInfo().colorBlendState : nullptr;
+  const auto& newBlendState     = mImpl->mNewPipeline->GetCreateInfo().colorBlendState;
 
   // TODO: prevent leaking the state
-  if(!bs)
-  {
-    return;
-  }
-
-  bs->blendEnable ? gl.Enable(GL_BLEND) : gl.Disable(GL_BLEND);
-  if(!bs->blendEnable)
+  if(!newBlendState)
   {
     return;
   }
 
-  gl.BlendFunc(GLBlendFunc(bs->srcColorBlendFactor), GLBlendFunc(bs->dstColorBlendFactor));
+  auto& gl = *mImpl->mController.GetGL();
 
-  if((GLBlendFunc(bs->srcColorBlendFactor) == GLBlendFunc(bs->srcAlphaBlendFactor)) &&
-     (GLBlendFunc(bs->dstColorBlendFactor) == GLBlendFunc(bs->dstAlphaBlendFactor)))
+  if(!currentBlendState || currentBlendState->blendEnable != newBlendState->blendEnable)
   {
-    gl.BlendFunc(GLBlendFunc(bs->srcColorBlendFactor), GLBlendFunc(bs->dstColorBlendFactor));
+    if(newBlendState->blendEnable != mImpl->mGlStateCache.mBlendEnabled)
+    {
+      mImpl->mGlStateCache.mBlendEnabled = newBlendState->blendEnable;
+      newBlendState->blendEnable ? gl.Enable(GL_BLEND) : gl.Disable(GL_BLEND);
+    }
   }
-  else
+
+  if(!newBlendState->blendEnable)
   {
-    gl.BlendFuncSeparate(GLBlendFunc(bs->srcColorBlendFactor),
-                         GLBlendFunc(bs->dstColorBlendFactor),
-                         GLBlendFunc(bs->srcAlphaBlendFactor),
-                         GLBlendFunc(bs->dstAlphaBlendFactor));
+    return;
   }
-  if(GLBlendOp(bs->colorBlendOp) == GLBlendOp(bs->alphaBlendOp))
+
+  BlendFactor newSrcRGB(newBlendState->srcColorBlendFactor);
+  BlendFactor newDstRGB(newBlendState->dstColorBlendFactor);
+  BlendFactor newSrcAlpha(newBlendState->srcAlphaBlendFactor);
+  BlendFactor newDstAlpha(newBlendState->dstAlphaBlendFactor);
+
+  if(!currentBlendState ||
+     currentBlendState->srcColorBlendFactor != newSrcRGB ||
+     currentBlendState->dstColorBlendFactor != newDstRGB ||
+     currentBlendState->srcAlphaBlendFactor != newSrcAlpha ||
+     currentBlendState->dstAlphaBlendFactor != newDstAlpha)
   {
-    gl.BlendEquation(GLBlendOp(bs->colorBlendOp));
+    if((mImpl->mGlStateCache.mBlendFuncSeparateSrcRGB != newSrcRGB) ||
+       (mImpl->mGlStateCache.mBlendFuncSeparateDstRGB != newDstRGB) ||
+       (mImpl->mGlStateCache.mBlendFuncSeparateSrcAlpha != newSrcAlpha) ||
+       (mImpl->mGlStateCache.mBlendFuncSeparateDstAlpha != newDstAlpha))
+    {
+      mImpl->mGlStateCache.mBlendFuncSeparateSrcRGB   = newSrcRGB;
+      mImpl->mGlStateCache.mBlendFuncSeparateDstRGB   = newDstRGB;
+      mImpl->mGlStateCache.mBlendFuncSeparateSrcAlpha = newSrcAlpha;
+      mImpl->mGlStateCache.mBlendFuncSeparateDstAlpha = newDstAlpha;
+
+      if(newSrcRGB == newSrcAlpha && newDstRGB == newDstAlpha)
+      {
+        gl.BlendFunc(GLBlendFunc(newSrcRGB), GLBlendFunc(newDstRGB));
+      }
+      else
+      {
+        gl.BlendFuncSeparate(GLBlendFunc(newSrcRGB), GLBlendFunc(newDstRGB), GLBlendFunc(newSrcAlpha), GLBlendFunc(newDstAlpha));
+      }
+    }
   }
-  else
+
+  if(!currentBlendState ||
+     currentBlendState->colorBlendOp != newBlendState->colorBlendOp ||
+     currentBlendState->alphaBlendOp != newBlendState->alphaBlendOp)
   {
-    gl.BlendEquationSeparate(GLBlendOp(bs->colorBlendOp), GLBlendOp(bs->alphaBlendOp));
+    if(mImpl->mGlStateCache.mBlendEquationSeparateModeRGB != newBlendState->colorBlendOp ||
+       mImpl->mGlStateCache.mBlendEquationSeparateModeAlpha != newBlendState->alphaBlendOp)
+    {
+      mImpl->mGlStateCache.mBlendEquationSeparateModeRGB   = newBlendState->colorBlendOp;
+      mImpl->mGlStateCache.mBlendEquationSeparateModeAlpha = newBlendState->alphaBlendOp;
+
+      if(newBlendState->colorBlendOp == newBlendState->alphaBlendOp)
+      {
+        gl.BlendEquation(GLBlendOp(newBlendState->colorBlendOp));
+      }
+      else
+      {
+        gl.BlendEquationSeparate(GLBlendOp(newBlendState->colorBlendOp), GLBlendOp(newBlendState->alphaBlendOp));
+      }
+    }
   }
 }
 
 void Context::ResolveRasterizationState()
 {
-  const auto& state = mImpl->mCurrentPipeline->GetCreateInfo();
-  const auto& rs    = state.rasterizationState;
-  auto&       gl    = *mImpl->mController.GetGL();
+  const auto& currentRasterizationState = mImpl->mCurrentPipeline ? mImpl->mCurrentPipeline->GetCreateInfo().rasterizationState : nullptr;
+  const auto& newRasterizationState     = mImpl->mNewPipeline->GetCreateInfo().rasterizationState;
 
   // TODO: prevent leaking the state
-  if(!rs)
+  if(!newRasterizationState)
   {
     return;
   }
 
-  if(rs->cullMode == CullMode::NONE)
-  {
-    gl.Disable(GL_CULL_FACE);
-  }
-  else
+  auto& gl = *mImpl->mController.GetGL();
+
+  if(!currentRasterizationState ||
+     currentRasterizationState->cullMode != newRasterizationState->cullMode)
   {
-    gl.Enable(GL_CULL_FACE);
-    gl.CullFace(GLCullMode(rs->cullMode));
+    if(mImpl->mGlStateCache.mCullFaceMode != newRasterizationState->cullMode)
+    {
+      mImpl->mGlStateCache.mCullFaceMode = newRasterizationState->cullMode;
+      if(newRasterizationState->cullMode == CullMode::NONE)
+      {
+        gl.Disable(GL_CULL_FACE);
+      }
+      else
+      {
+        gl.Enable(GL_CULL_FACE);
+        gl.CullFace(GLCullMode(newRasterizationState->cullMode));
+      }
+    }
   }
-
   // TODO: implement polygon mode (fill, line, points)
   //       seems like we don't support it (no glPolygonMode())
 }
@@ -325,7 +498,7 @@ void Context::ResolveStandaloneUniforms()
   auto& gl = *mImpl->mController.GetGL();
 
   // Find reflection for program
-  const auto program = static_cast<const GLES::Program*>(mImpl->mCurrentPipeline->GetCreateInfo().programState->program);
+  const auto program = static_cast<const GLES::Program*>(mImpl->mNewPipeline->GetCreateInfo().programState->program);
 
   const auto& reflection = program->GetReflection();
 
@@ -436,7 +609,7 @@ void Context::BeginRenderPass(const BeginRenderPassDescriptor& renderPassBegin)
   if(targetInfo.surface)
   {
     // Bind surface FB
-    gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
+    BindFrameBuffer(GL_FRAMEBUFFER, 0);
   }
   else if(targetInfo.framebuffer)
   {
@@ -457,13 +630,27 @@ void Context::BeginRenderPass(const BeginRenderPassDescriptor& renderPassBegin)
   {
     mask |= GL_COLOR_BUFFER_BIT;
 
-    // Set clear color (todo: cache it!)
+    // Set clear color
     // Something goes wrong here if Alpha mask is GL_TRUE
-    gl.ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
-    gl.ClearColor(renderPassBegin.clearValues[0].color.r,
-                  renderPassBegin.clearValues[0].color.g,
-                  renderPassBegin.clearValues[0].color.b,
-                  renderPassBegin.clearValues[0].color.a);
+    ColorMask(true);
+
+    if(!mImpl->mGlStateCache.mClearColorSet ||
+       mImpl->mGlStateCache.mClearColor.r != renderPassBegin.clearValues[0].color.r ||
+       mImpl->mGlStateCache.mClearColor.g != renderPassBegin.clearValues[0].color.g ||
+       mImpl->mGlStateCache.mClearColor.b != renderPassBegin.clearValues[0].color.b ||
+       mImpl->mGlStateCache.mClearColor.a != renderPassBegin.clearValues[0].color.a)
+    {
+      gl.ClearColor(renderPassBegin.clearValues[0].color.r,
+                    renderPassBegin.clearValues[0].color.g,
+                    renderPassBegin.clearValues[0].color.b,
+                    renderPassBegin.clearValues[0].color.a);
+
+      mImpl->mGlStateCache.mClearColorSet = true;
+      mImpl->mGlStateCache.mClearColor    = Vector4(renderPassBegin.clearValues[0].color.r,
+                                                 renderPassBegin.clearValues[0].color.g,
+                                                 renderPassBegin.clearValues[0].color.b,
+                                                 renderPassBegin.clearValues[0].color.a);
+    }
   }
 
   // check for depth stencil
@@ -472,20 +659,28 @@ void Context::BeginRenderPass(const BeginRenderPassDescriptor& renderPassBegin)
     const auto& depthStencil = attachments.back();
     if(depthStencil.loadOp == AttachmentLoadOp::CLEAR)
     {
-      gl.DepthMask(true);
+      if(!mImpl->mGlStateCache.mDepthMaskEnabled)
+      {
+        mImpl->mGlStateCache.mDepthMaskEnabled = true;
+        gl.DepthMask(true);
+      }
       mask |= GL_DEPTH_BUFFER_BIT;
     }
     if(depthStencil.stencilLoadOp == AttachmentLoadOp::CLEAR)
     {
-      gl.StencilMask(0xFF);
+      if(mImpl->mGlStateCache.mStencilMask != 0xFF)
+      {
+        mImpl->mGlStateCache.mStencilMask = 0xFF;
+        gl.StencilMask(0xFF);
+      }
       mask |= GL_STENCIL_BUFFER_BIT;
     }
   }
 
-  gl.Enable(GL_SCISSOR_TEST);
+  SetScissorTestEnabled(true);
   gl.Scissor(renderPassBegin.renderArea.x, renderPassBegin.renderArea.y, renderPassBegin.renderArea.width, renderPassBegin.renderArea.height);
-  gl.Clear(mask);
-  gl.Disable(GL_SCISSOR_TEST);
+  ClearBuffer(mask, true);
+  SetScissorTestEnabled(false);
 
   mImpl->mCurrentRenderPass   = &renderPass;
   mImpl->mCurrentRenderTarget = &renderTarget;
@@ -510,80 +705,257 @@ void Context::ClearState()
 
 void Context::ColorMask(bool enabled)
 {
-  auto& gl = *mImpl->mController.GetGL();
-  gl.ColorMask(enabled, enabled, enabled, enabled);
+  if(enabled != mImpl->mGlStateCache.mColorMask)
+  {
+    mImpl->mGlStateCache.mColorMask = enabled;
+
+    auto& gl = *mImpl->mController.GetGL();
+    gl.ColorMask(enabled, enabled, enabled, enabled);
+  }
 }
 
 void Context::ClearStencilBuffer()
 {
-  auto& gl = *mImpl->mController.GetGL();
-  gl.Clear(GL_STENCIL_BUFFER_BIT);
+  ClearBuffer(GL_STENCIL_BUFFER_BIT, false);
 }
 
 void Context::ClearDepthBuffer()
 {
-  auto& gl = *mImpl->mController.GetGL();
-  gl.Clear(GL_DEPTH_BUFFER_BIT);
+  ClearBuffer(GL_DEPTH_BUFFER_BIT, false);
 }
 
-void Context::SetStencilTestEnable(bool stencilEnable)
+void Context::ClearBuffer(uint32_t mask, bool forceClear)
 {
-  auto& gl = *mImpl->mController.GetGL();
-  if(stencilEnable)
+  mask = mImpl->mGlStateCache.mFrameBufferStateCache.GetClearMask(mask, forceClear, mImpl->mGlStateCache.mScissorTestEnabled);
+  if(mask > 0)
   {
-    gl.Enable(GL_STENCIL_TEST);
+    auto& gl = *mImpl->mController.GetGL();
+    gl.Clear(mask);
   }
-  else
+}
+
+void Context::SetScissorTestEnabled(bool scissorEnabled)
+{
+  if(mImpl->mGlStateCache.mScissorTestEnabled != scissorEnabled)
   {
-    gl.Disable(GL_STENCIL_TEST);
+    mImpl->mGlStateCache.mScissorTestEnabled = scissorEnabled;
+
+    auto& gl = *mImpl->mController.GetGL();
+    if(scissorEnabled)
+    {
+      gl.Enable(GL_SCISSOR_TEST);
+    }
+    else
+    {
+      gl.Disable(GL_SCISSOR_TEST);
+    }
+  }
+}
+
+void Context::SetStencilTestEnable(bool stencilEnable)
+{
+  if(stencilEnable != mImpl->mGlStateCache.mStencilBufferEnabled)
+  {
+    mImpl->mGlStateCache.mStencilBufferEnabled = stencilEnable;
+
+    auto& gl = *mImpl->mController.GetGL();
+    if(stencilEnable)
+    {
+      gl.Enable(GL_STENCIL_TEST);
+    }
+    else
+    {
+      gl.Disable(GL_STENCIL_TEST);
+    }
   }
 }
 
 void Context::StencilMask(uint32_t writeMask)
 {
-  auto& gl = *mImpl->mController.GetGL();
-  gl.StencilMask(writeMask);
+  if(writeMask != mImpl->mGlStateCache.mStencilMask)
+  {
+    mImpl->mGlStateCache.mStencilMask = writeMask;
+
+    auto& gl = *mImpl->mController.GetGL();
+    gl.StencilMask(writeMask);
+  }
 }
 
 void Context::StencilFunc(Graphics::CompareOp compareOp,
                           uint32_t            reference,
                           uint32_t            compareMask)
 {
-  auto& gl = *mImpl->mController.GetGL();
-  gl.StencilFunc(GLCompareOp(compareOp).op, reference, compareMask);
+  if(compareOp != mImpl->mGlStateCache.mStencilFunc ||
+     reference != mImpl->mGlStateCache.mStencilFuncRef ||
+     compareMask != mImpl->mGlStateCache.mStencilFuncMask)
+  {
+    mImpl->mGlStateCache.mStencilFunc     = compareOp;
+    mImpl->mGlStateCache.mStencilFuncRef  = reference;
+    mImpl->mGlStateCache.mStencilFuncMask = compareMask;
+
+    auto& gl = *mImpl->mController.GetGL();
+    gl.StencilFunc(GLCompareOp(compareOp).op, reference, compareMask);
+  }
 }
 
 void Context::StencilOp(Graphics::StencilOp failOp,
                         Graphics::StencilOp depthFailOp,
                         Graphics::StencilOp passOp)
 {
-  auto& gl = *mImpl->mController.GetGL();
-  gl.StencilOp(GLStencilOp(failOp).op, GLStencilOp(depthFailOp).op, GLStencilOp(passOp).op);
+  if(failOp != mImpl->mGlStateCache.mStencilOpFail ||
+     depthFailOp != mImpl->mGlStateCache.mStencilOpDepthFail ||
+     passOp != mImpl->mGlStateCache.mStencilOpDepthPass)
+  {
+    mImpl->mGlStateCache.mStencilOpFail      = failOp;
+    mImpl->mGlStateCache.mStencilOpDepthFail = depthFailOp;
+    mImpl->mGlStateCache.mStencilOpDepthPass = passOp;
+
+    auto& gl = *mImpl->mController.GetGL();
+    gl.StencilOp(GLStencilOp(failOp).op, GLStencilOp(depthFailOp).op, GLStencilOp(passOp).op);
+  }
 }
 
 void Context::SetDepthCompareOp(Graphics::CompareOp compareOp)
 {
-  auto& gl = *mImpl->mController.GetGL();
-  gl.DepthFunc(GLCompareOp(compareOp).op);
+  if(compareOp != mImpl->mGlStateCache.mDepthFunction)
+  {
+    mImpl->mGlStateCache.mDepthFunction = compareOp;
+    auto& gl                            = *mImpl->mController.GetGL();
+    gl.DepthFunc(GLCompareOp(compareOp).op);
+  }
 }
 
 void Context::SetDepthTestEnable(bool depthTestEnable)
 {
-  auto& gl = *mImpl->mController.GetGL();
-  if(depthTestEnable)
+  if(depthTestEnable != mImpl->mGlStateCache.mDepthBufferEnabled)
   {
-    gl.Enable(GL_DEPTH_TEST);
+    mImpl->mGlStateCache.mDepthBufferEnabled = depthTestEnable;
+
+    auto& gl = *mImpl->mController.GetGL();
+    if(depthTestEnable)
+    {
+      gl.Enable(GL_DEPTH_TEST);
+    }
+    else
+    {
+      gl.Disable(GL_DEPTH_TEST);
+    }
   }
-  else
+}
+
+void Context::SetDepthWriteEnable(bool depthWriteEnable)
+{
+  if(depthWriteEnable != mImpl->mGlStateCache.mDepthMaskEnabled)
   {
-    gl.Disable(GL_DEPTH_TEST);
+    mImpl->mGlStateCache.mDepthMaskEnabled = depthWriteEnable;
+
+    auto& gl = *mImpl->mController.GetGL();
+    gl.DepthMask(depthWriteEnable);
   }
 }
 
-void Context::SetDepthWriteEnable(bool depthWriteEnable)
+void Context::ActiveTexture(uint32_t textureBindingIndex)
+{
+  if(mImpl->mGlStateCache.mActiveTextureUnit != textureBindingIndex)
+  {
+    mImpl->mGlStateCache.mActiveTextureUnit = textureBindingIndex;
+
+    auto& gl = *mImpl->mController.GetGL();
+    gl.ActiveTexture(GL_TEXTURE0 + textureBindingIndex);
+  }
+}
+
+void Context::BindTexture(GLenum target, BoundTextureType textureTypeId, uint32_t textureId)
+{
+  uint32_t typeId = static_cast<uint32_t>(textureTypeId);
+  if(mImpl->mGlStateCache.mBoundTextureId[mImpl->mGlStateCache.mActiveTextureUnit][typeId] != textureId)
+  {
+    mImpl->mGlStateCache.mBoundTextureId[mImpl->mGlStateCache.mActiveTextureUnit][typeId] = textureId;
+
+    auto& gl = *mImpl->mController.GetGL();
+    gl.BindTexture(target, textureId);
+  }
+}
+
+void Context::GenerateMipmap(GLenum target)
+{
+  auto& gl = *mImpl->mController.GetGL();
+  gl.GenerateMipmap(target);
+}
+
+void Context::BindBuffer(GLenum target, uint32_t bufferId)
+{
+  if(mImpl->mGlStateCache.mBoundArrayBufferId != bufferId)
+  {
+    mImpl->mGlStateCache.mBoundArrayBufferId = bufferId;
+
+    auto& gl = *mImpl->mController.GetGL();
+    gl.BindBuffer(target, bufferId);
+  }
+}
+
+void Context::DrawBuffers(uint32_t count, const GLenum* buffers)
+{
+  mImpl->mGlStateCache.mFrameBufferStateCache.DrawOperation(mImpl->mGlStateCache.mColorMask,
+                                                            mImpl->mGlStateCache.DepthBufferWriteEnabled(),
+                                                            mImpl->mGlStateCache.StencilBufferWriteEnabled());
+
+  auto& gl = *mImpl->mController.GetGL();
+  gl.DrawBuffers(count, buffers);
+}
+
+void Context::BindFrameBuffer(GLenum target, uint32_t bufferId)
+{
+  mImpl->mGlStateCache.mFrameBufferStateCache.SetCurrentFrameBuffer(bufferId);
+
+  auto& gl = *mImpl->mController.GetGL();
+  gl.BindFramebuffer(target, bufferId);
+}
+
+void Context::GenFramebuffers(uint32_t count, uint32_t* framebuffers)
 {
   auto& gl = *mImpl->mController.GetGL();
-  gl.DepthMask(depthWriteEnable);
+  gl.GenFramebuffers(count, framebuffers);
+
+  mImpl->mGlStateCache.mFrameBufferStateCache.FrameBuffersCreated(count, framebuffers);
+}
+
+void Context::DeleteFramebuffers(uint32_t count, uint32_t* framebuffers)
+{
+  mImpl->mGlStateCache.mFrameBufferStateCache.FrameBuffersDeleted(count, framebuffers);
+
+  auto& gl = *mImpl->mController.GetGL();
+  gl.DeleteFramebuffers(count, framebuffers);
+}
+
+GLStateCache& Context::GetGLStateCache()
+{
+  return mImpl->mGlStateCache;
+}
+
+void Context::GlContextCreated()
+{
+  if(!mImpl->mGlContextCreated)
+  {
+    mImpl->mGlContextCreated = true;
+
+    // Set the initial GL state
+    mImpl->InitializeGlState();
+  }
+}
+
+void Context::GlContextDestroyed()
+{
+  mImpl->mGlContextCreated = false;
+}
+
+void Context::InvalidateCachedPipeline(GLES::Pipeline* pipeline)
+{
+  // Since the pipeline is deleted, invalidate the cached pipeline.
+  if(mImpl->mCurrentPipeline == &pipeline->GetPipeline())
+  {
+    mImpl->mCurrentPipeline = nullptr;
+  }
 }
 
 } // namespace Dali::Graphics::GLES
index c9df90f..2bba73f 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 #include <dali/graphics-api/graphics-command-buffer.h>
+#include "gles-context-state-cache.h"
 #include "gles-graphics-types.h"
 
 namespace Dali::Graphics
@@ -29,6 +30,8 @@ namespace GLES
 class Pipeline;
 class RenderPass;
 class RenderTarget;
+class Texture;
+
 /**
  * @brief Context represents single GLES context
  */
@@ -137,9 +140,43 @@ public:
    */
   void EndRenderPass();
 
+  /**
+   * @brief Returns the cache of GL state in the context
+   * @return the reference of GL state cache (which can be modified)
+   */
+  GLStateCache& GetGLStateCache();
+
+  /**
+   * @brief Called when the GL context has been created.
+   */
+  void GlContextCreated();
+
+  /**
+   * @brief Called when the GL context has been destroyed.
+   */
+  void GlContextDestroyed();
+
+  /**
+   * @brief Invalidates the cached pipeline object in the context if it matches
+   * This is called before the pipeline is deleted
+   *
+   * @param[in] pipeline The pipeline
+   */
+  void InvalidateCachedPipeline(GLES::Pipeline* pipeline);
+
+  void ActiveTexture(uint32_t textureBindingIndex);
+  void BindTexture(GLenum target, BoundTextureType textureTypeId, uint32_t textureId);
+  void GenerateMipmap(GLenum target);
+  void BindBuffer(GLenum target, uint32_t bufferId);
+  void DrawBuffers(uint32_t count, const GLenum* buffers);
+  void BindFrameBuffer(GLenum target, uint32_t bufferId);
+  void GenFramebuffers(uint32_t count, uint32_t* framebuffers);
+  void DeleteFramebuffers(uint32_t count, uint32_t* framebuffers);
   void ColorMask(bool enabled);
   void ClearStencilBuffer();
   void ClearDepthBuffer();
+  void ClearBuffer(uint32_t mask, bool forceClear);
+  void SetScissorTestEnabled(bool scissorEnabled);
   void SetStencilTestEnable(bool stencilEnable);
   void StencilMask(uint32_t writeMask);
   void StencilFunc(Graphics::CompareOp compareOp,
diff --git a/dali/internal/graphics/gles-impl/gles-framebuffer-state-cache.cpp b/dali/internal/graphics/gles-impl/gles-framebuffer-state-cache.cpp
new file mode 100644 (file)
index 0000000..2fbb32e
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2021 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 "gles-framebuffer-state-cache.h"
+
+// INTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+#include <dali/integration-api/gl-defines.h>
+
+namespace Dali::Graphics
+{
+namespace GLES
+{
+namespace
+{
+const uint32_t INITIAL_FRAMEBUFFER_STATE = 0u;
+}
+
+FrameBufferStateCache::FrameBufferStateCache()
+{
+}
+
+FrameBufferStateCache::~FrameBufferStateCache() = default;
+
+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 = INITIAL_FRAMEBUFFER_STATE;
+      continue;
+    }
+
+    FrameBufferState newFrameBuffer(frameBuffers[i]);
+    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 nullptr;
+}
+
+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);
+}
+
+} // namespace GLES
+
+} // namespace Dali::Graphics
diff --git a/dali/internal/graphics/gles-impl/gles-framebuffer-state-cache.h b/dali/internal/graphics/gles-impl/gles-framebuffer-state-cache.h
new file mode 100644 (file)
index 0000000..02442b8
--- /dev/null
@@ -0,0 +1,153 @@
+#ifndef DALI_GRAPHICS_GLES_CONTEXT_FRAMEBUFFER_STATE_CACHE_H
+#define DALI_GRAPHICS_GLES_CONTEXT_FRAMEBUFFER_STATE_CACHE_H
+
+/*
+ * Copyright (c) 2021 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::Graphics
+{
+namespace GLES
+{
+/**
+ * @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)
+    : mId(id),
+      mState(0)
+    {
+    }
+    GLuint       mId;    ///< Frame buffer id
+    unsigned int mState; ///< State, bitmask of FrameBufferStatus flags
+  };
+
+  using FrameBufferStateVector = Dali::Vector<FrameBufferState>;
+
+  /**
+   * @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);
+
+  FrameBufferStateCache(const FrameBufferStateCache&); ///< undefined copy constructor
+
+  FrameBufferStateCache& operator=(const FrameBufferStateCache&); ///< undefined assignment operator
+
+private:                                        // data
+  FrameBufferStateVector mFrameBufferStates{};    ///< state of the frame buffers
+  GLuint                 mCurrentFrameBufferId{0}; ///< currently bound frame buffer
+};
+
+} // namespace GLES
+
+} // namespace Dali::Graphics
+
+#endif // DALI_GRAPHICS_GLES_CONTEXT_FRAMEBUFFER_STATE_CACHE_H
index 3e4805a..b8dafab 100644 (file)
@@ -83,9 +83,15 @@ void Buffer::InitializeCPUBuffer()
 
 void Buffer::InitializeGPUBuffer()
 {
-  auto gl = mController.GetGL();
+  auto context = mController.GetCurrentContext();
+  auto gl      = mController.GetGL();
+  if(!gl || !context)
+  {
+    return;
+  }
+
   gl->GenBuffers(1, &mBufferId);
-  gl->BindBuffer(GL_ARRAY_BUFFER, mBufferId);
+  context->BindBuffer(GL_ARRAY_BUFFER, mBufferId);
   gl->BufferData(GL_ARRAY_BUFFER, mCreateInfo.size, nullptr, GL_STATIC_DRAW);
 }
 
@@ -123,7 +129,12 @@ void Buffer::DiscardResource()
 
 void Buffer::Bind(Graphics::BufferUsage bindingTarget) const
 {
-  auto gl = mController.GetGL();
+  auto context = mController.GetCurrentContext();
+  auto gl      = mController.GetGL();
+  if(!gl || !context)
+  {
+    return;
+  }
 
   // CPU allocated buffer may be bound only as Uniform Buffer
   // on special binding point
@@ -142,12 +153,12 @@ void Buffer::Bind(Graphics::BufferUsage bindingTarget) const
     {
       case Graphics::BufferUsage::VERTEX_BUFFER:
       {
-        gl->BindBuffer(GL_ARRAY_BUFFER, mBufferId);
+        context->BindBuffer(GL_ARRAY_BUFFER, mBufferId);
         break;
       }
       case Graphics::BufferUsage::INDEX_BUFFER:
       {
-        gl->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, mBufferId);
+        context->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, mBufferId);
         break;
       }
       default:
index 2e52934..d909146 100644 (file)
@@ -92,13 +92,14 @@ Framebuffer::~Framebuffer() = default;
 
 bool Framebuffer::InitializeResource()
 {
-  auto gl = mController.GetGL();
-  if(gl && !mInitialized)
+  auto context = mController.GetCurrentContext();
+  auto gl      = mController.GetGL();
+  if(gl && context && !mInitialized)
   {
     mInitialized = true;
 
-    gl->GenFramebuffers(1, &mFramebufferId);
-    gl->BindFramebuffer(GL_FRAMEBUFFER, mFramebufferId);
+    context->GenFramebuffers(1, &mFramebufferId);
+    context->BindFrameBuffer(GL_FRAMEBUFFER, mFramebufferId);
 
     for(Graphics::ColorAttachment& attachment : mCreateInfo.colorAttachments)
     {
@@ -106,7 +107,7 @@ bool Framebuffer::InitializeResource()
     }
 
     // @todo is this per framebuffer, or more immediate state that needs setting when framebuffer changed?
-    gl->DrawBuffers(mCreateInfo.colorAttachments.size(), COLOR_ATTACHMENTS);
+    context->DrawBuffers(mCreateInfo.colorAttachments.size(), COLOR_ATTACHMENTS);
 
     if(mCreateInfo.depthStencilAttachment.depthTexture)
     {
@@ -135,7 +136,8 @@ bool Framebuffer::InitializeResource()
 
       AttachTexture(stencilTexture, attachmentId, 0, mCreateInfo.depthStencilAttachment.stencilLevel);
     }
-    gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
+
+    context->BindFrameBuffer(GL_FRAMEBUFFER, 0);
   }
 
   return mInitialized;
@@ -143,8 +145,9 @@ bool Framebuffer::InitializeResource()
 
 void Framebuffer::DestroyResource()
 {
-  auto gl = mController.GetGL();
-  if(gl && mInitialized)
+  auto context = mController.GetCurrentContext();
+  auto gl      = mController.GetGL();
+  if(gl && context && mInitialized)
   {
     if(mDepthBufferId)
     {
@@ -155,7 +158,8 @@ void Framebuffer::DestroyResource()
       gl->DeleteRenderbuffers(1, &mStencilBufferId);
     }
 
-    gl->DeleteFramebuffers(1, &mFramebufferId);
+    context->DeleteFramebuffers(1, &mFramebufferId);
+
     mFramebufferId = 0u;
     mInitialized   = false;
   }
@@ -168,8 +172,13 @@ void Framebuffer::DiscardResource()
 
 void Framebuffer::Bind() const
 {
-  auto gl = mController.GetGL();
-  gl->BindFramebuffer(GL_FRAMEBUFFER, mFramebufferId);
+  auto context = mController.GetCurrentContext();
+  auto gl      = mController.GetGL();
+
+  if(gl && context)
+  {
+    context->BindFrameBuffer(GL_FRAMEBUFFER, mFramebufferId);
+  }
 }
 
 void Framebuffer::AttachTexture(const Graphics::Texture* texture, uint32_t attachmentId, uint32_t layerId, uint32_t levelId)
index 603039b..a39260e 100644 (file)
@@ -90,10 +90,9 @@ auto& PipelineImpl::GetController() const
   return mController;
 }
 
-void PipelineImpl::Bind(GLES::PipelineImpl* prevPipeline)
+void PipelineImpl::Bind(const uint32_t glProgram) const
 {
-  auto& gl        = *GetController().GetGL();
-  auto  glProgram = static_cast<const GLES::Program*>(GetCreateInfo().programState->program)->GetImplementation()->GetGlProgram();
+  auto& gl = *GetController().GetGL();
   gl.UseProgram(glProgram);
 }
 
index fca74fa..57c490e 100644 (file)
@@ -30,6 +30,7 @@
 namespace Dali::Graphics::GLES
 {
 class PipelineCache;
+class Program;
 
 /**
  * @brief PipelineImpl is the implementation of Pipeline
@@ -60,12 +61,9 @@ public:
    *
    * Binds Pipeline by binding GL program and flushing state.
    *
-   * If previous pipeline specified, it will be used in order to
-   * avoid redundant state swiches.
-   *
-   * @param[in] prevPipeline previous pipeline
+   * @param[in] glProgram The GL program to be bound
    */
-  void Bind(GLES::PipelineImpl* prevPipeline);
+  void Bind(const uint32_t glProgram) const;
 
   /**
    * @brief Increases ref count
index a2c9617..672e56f 100644 (file)
@@ -113,10 +113,11 @@ bool Texture::InitializeResource()
 
 bool Texture::InitializeNativeImage()
 {
-  auto   gl = mController.GetGL();
+  auto   context = mController.GetCurrentContext();
+  auto   gl      = mController.GetGL();
   GLuint texture{0};
 
-  if(!gl)
+  if(!gl || !context)
   {
     // Do nothing during shutdown
     return false;
@@ -128,7 +129,7 @@ bool Texture::InitializeNativeImage()
   if(created)
   {
     gl->GenTextures(1, &texture);
-    gl->BindTexture(mGlTarget, texture);
+    context->BindTexture(mGlTarget, GetTextureTypeId(), texture);
 
     gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1); // We always use tightly packed data
 
@@ -161,8 +162,9 @@ bool Texture::InitializeNativeImage()
 
 bool Texture::InitializeTexture()
 {
-  auto gl = mController.GetGL();
-  if(!gl)
+  auto context = mController.GetCurrentContext();
+  auto gl      = mController.GetGL();
+  if(!gl || !context)
   {
     // Do nothing during shutdown
     return false;
@@ -185,7 +187,7 @@ bool Texture::InitializeTexture()
       {
         // Bind texture
         gl->GenTextures(1, &texture);
-        gl->BindTexture(GL_TEXTURE_2D, texture);
+        context->BindTexture(GL_TEXTURE_2D, GetTextureTypeId(), texture);
 
         // Allocate memory for the texture
         if(!mIsCompressed)
@@ -233,7 +235,7 @@ bool Texture::InitializeTexture()
       {
         // Bind texture
         gl->GenTextures(1, &texture);
-        gl->BindTexture(GL_TEXTURE_CUBE_MAP, texture);
+        context->BindTexture(GL_TEXTURE_CUBE_MAP, GetTextureTypeId(), texture);
         gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1); // We always use tightly packed data
 
         gl->TexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, Graphics::GLES::GLSamplerFilterAndMipMapMode(Graphics::SamplerFilter::LINEAR, SamplerMipmapMode::NONE));
@@ -312,22 +314,21 @@ void Texture::DiscardResource()
 
 void Texture::Bind(const TextureBinding& binding) const
 {
-  auto gl = mController.GetGL();
-  if(!gl)
+  auto context = mController.GetCurrentContext();
+  auto gl      = mController.GetGL();
+  if(!gl || !context)
   {
     // Do nothing during shutdown
     return;
   }
 
-  gl->ActiveTexture(GL_TEXTURE0 + binding.binding);
-  gl->BindTexture(mGlTarget, mTextureId);
+  context->ActiveTexture(binding.binding);
+  context->BindTexture(mGlTarget, GetTextureTypeId(), mTextureId);
 
   // For GLES2 if there is a sampler set in the binding
   if(binding.sampler)
   {
-    // Non-default.
-    auto*       sampler           = static_cast<const GLES::Sampler*>(binding.sampler);
-    const auto& samplerCreateInfo = sampler->GetCreateInfo();
+    const auto& samplerCreateInfo = static_cast<const GLES::Sampler*>(binding.sampler)->GetCreateInfo();
 
     auto mipMapMode = samplerCreateInfo.mipMapMode;
 
index dc7d921..19619e4 100644 (file)
 
 // INTERNAL INCLUDES
 #include "gles-graphics-resource.h"
+#include "gles-graphics-types.h"
 
 namespace Dali::Graphics::GLES
 {
 using TextureResource = Resource<Graphics::Texture, Graphics::TextureCreateInfo>;
+class Sampler;
 
 /**
  * The Texture class represents a GPU texture object. It's slightly
@@ -65,6 +67,15 @@ public:
   }
 
   /**
+   * @brief Returns the type of the bound Gl texture
+   * @return The type of the bound Gl texture
+   */
+  [[nodiscard]] BoundTextureType GetTextureTypeId() const
+  {
+    return mCreateInfo.nativeImagePtr ? BoundTextureType::TEXTURE_EXTERNAL_OES : static_cast<BoundTextureType>(mCreateInfo.textureType);
+  }
+
+  /**
    * @brief Called when initializing the resource
    *
    * @return True on success
index 35893c8..e90f2bc 100644 (file)
@@ -2018,6 +2018,17 @@ enum class GLESVersion
 };
 
 /**
+ * @brief Types of bond texture cached in the context
+ */
+enum class BoundTextureType
+{
+  TEXTURE_2D,
+  TEXTURE_3D,
+  TEXTURE_CUBEMAP,
+  TEXTURE_EXTERNAL_OES
+};
+
+/**
  * The descriptor of BeginRenderPass command
  */
 struct BeginRenderPassDescriptor
index d19f824..3f00195 100644 (file)
@@ -73,24 +73,24 @@ void EglGraphics::SetIsSurfacelessContextSupported(const bool isSupported)
 
 void EglGraphics::ActivateResourceContext()
 {
-  mGraphicsController.ActivateResourceContext();
-
   if(mEglImplementation && mEglImplementation->IsSurfacelessContextSupported())
   {
     // Make the shared surfaceless context as current before rendering
     mEglImplementation->MakeContextCurrent(EGL_NO_SURFACE, mEglImplementation->GetContext());
   }
+
+  mGraphicsController.ActivateResourceContext();
 }
 
 void EglGraphics::ActivateSurfaceContext(Dali::RenderSurfaceInterface* surface)
 {
-  mGraphicsController.ActivateSurfaceContext(surface);
-
   if(surface)
   {
     surface->InitializeGraphics();
     surface->MakeContextCurrent();
   }
+
+  mGraphicsController.ActivateSurfaceContext(surface);
 }
 
 void EglGraphics::SetFirstFrameAfterResume()
@@ -153,15 +153,14 @@ void EglGraphics::ConfigureSurface(Dali::RenderSurfaceInterface* surface)
   {
     // Create a surfaceless OpenGL context for shared resources
     mEglImplementation->CreateContext();
-    mEglImplementation->MakeContextCurrent(EGL_NO_SURFACE, mEglImplementation->GetContext());
+    ActivateResourceContext();
   }
   else
   {
     currentSurface = surface;
     if(currentSurface)
     {
-      currentSurface->InitializeGraphics();
-      currentSurface->MakeContextCurrent();
+      ActivateSurfaceContext(currentSurface);
     }
   }