Cache BindBufferRange call + Reduce cache clear call if we can 75/323175/7
authorEunki, Hong <eunkiki.hong@samsung.com>
Thu, 24 Apr 2025 01:31:57 +0000 (10:31 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Mon, 28 Apr 2025 01:32:48 +0000 (10:32 +0900)
Change-Id: I017b729ca8f6cc93214165ecf47e4f22697c6ec5
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
dali/internal/graphics/gles-impl/egl-graphics-controller.cpp
dali/internal/graphics/gles-impl/egl-graphics-controller.h
dali/internal/graphics/gles-impl/gles-context-state-cache.h
dali/internal/graphics/gles-impl/gles-context.cpp
dali/internal/graphics/gles-impl/gles-context.h
dali/internal/graphics/gles-impl/gles-graphics-program.cpp
dali/internal/graphics/gles-impl/gles-graphics-types.h

index 222e1c71c062a56056b0301c3dd44cf95f2b8d55..fc5030ca248aa56fe58c345e933491c7f257e465 100644 (file)
@@ -1156,10 +1156,13 @@ void EglGraphicsController::ProcessTextureMipmapGenerationQueue()
   {
     auto* texture = mTextureMipmapGenerationRequests.front();
 
-    mCurrentContext->BindTexture(texture->GetGlTarget(), texture->GetTextureTypeId(), texture->GetGLTexture());
-    mCurrentContext->GenerateMipmap(texture->GetGlTarget());
+    if(mDiscardTextureSet.find(texture) == mDiscardTextureSet.end())
+    {
+      mCurrentContext->BindTexture(texture->GetGlTarget(), texture->GetTextureTypeId(), texture->GetGLTexture());
+      mCurrentContext->GenerateMipmap(texture->GetGlTarget());
 
-    mTextureMipmapGenerationRequests.pop();
+      mTextureMipmapGenerationRequests.pop();
+    }
   }
 }
 
index 8f14276c9feb4d3cd41ea6107dd2d4904dff68b1..1e470d4cb2c8e037ec14972d1b7aa23da1ba0acf 100644 (file)
@@ -523,11 +523,17 @@ public:
     // Process main command queue
     ProcessCommandQueues();
 
-    // Reset texture cache in the contexts while destroying textures
-    ResetTextureCache();
+    if(!mDiscardTextureSet.empty())
+    {
+      // Reset texture cache in the contexts while destroying textures
+      ResetTextureCache();
+    }
 
-    // Reset buffer cache in the contexts while destroying buffers
-    ResetBufferCache();
+    if(!mDiscardBufferQueue.empty())
+    {
+      // Reset buffer cache in the contexts while destroying buffers
+      ResetBufferCache();
+    }
 
     // Process discards
     // Note : we don't need to be ResourceContext when we destroy resources.
@@ -764,14 +770,14 @@ public:
   {
     if(mContext)
     {
-      mContext->GetGLStateCache().ResetBufferCache();
+      mContext->ResetBufferCache();
     }
 
     for(auto& context : mSurfaceContexts)
     {
       if(context.second)
       {
-        context.second->GetGLStateCache().ResetBufferCache();
+        context.second->ResetBufferCache();
       }
     }
   }
@@ -873,8 +879,8 @@ private:
   Internal::Adaptor::EglSyncImplementation* mEglSyncImplementation{nullptr};
   Graphics::GraphicsInterface*              mGraphics{nullptr}; // Pointer to owning structure via interface.
 
-  std::queue<GLES::Texture*>         mCreateTextureQueue; ///< Create queue for texture resource
-  std::unordered_set<GLES::Texture*> mDiscardTextureSet;  ///< Discard queue for texture resource
+  std::queue<GLES::Texture*>               mCreateTextureQueue; ///< Create queue for texture resource
+  std::unordered_set<const GLES::Texture*> mDiscardTextureSet;  ///< Discard queue for texture resource
 
   std::queue<GLES::Buffer*> mCreateBufferQueue;  ///< Create queue for buffer resource
   std::queue<GLES::Buffer*> mDiscardBufferQueue; ///< Discard queue for buffer resource
index 07f1512698a842728984fc926a53d805084c3d5b..7caae726f5f6e483c95e7e05ed90c89d7dae37c0 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_GRAPHICS_GLES_CONTEXT_STATE_CACHE_H
 
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
@@ -22,6 +22,7 @@
 #include <dali/graphics-api/graphics-types.h>
 #include <dali/integration-api/gl-abstraction.h>
 #include <dali/integration-api/gl-defines.h>
+#include <cstring> ///< for memset
 
 // INTERNAL INCLUDES
 #include "gles-framebuffer-state-cache.h"
@@ -49,13 +50,7 @@ struct GLStateCache
   {
     // 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;
-      }
-    }
+    memset(&mBoundTextureId, 0, sizeof(mBoundTextureId));
   }
 
   /**
index 6b766025fa858a10794e17d832b5994dba051114..0301d02f9eb695c9c7584c213d0f942ee8e293e9 100644 (file)
 namespace
 {
 DALI_INIT_TIME_CHECKER_FILTER(gTimeCheckerFilter, DALI_EGL_PERFORMANCE_LOG_THRESHOLD_TIME);
-}
+
+/**
+ * Memory compare working on 4-byte types. Since all types used in shaders are
+ * size of 4*N then no need for size and alignment checks.
+ */
+template<class A, class B>
+inline bool memcmp4(A* a, B* b, size_t size)
+{
+  auto* pa = reinterpret_cast<const uint32_t*>(a);
+  auto* pb = reinterpret_cast<const uint32_t*>(b);
+  size >>= 2;
+  while(size-- && *pa++ == *pb++)
+    ;
+  return (-1u == size);
+};
+} // namespace
 
 namespace Dali::Graphics::GLES
 {
@@ -78,7 +93,7 @@ struct Context::Impl
     }
 
     auto* gl = GetGL();
-    if(!gl) // early out if no gl
+    if(DALI_UNLIKELY(!gl)) // early out if no gl
     {
       return;
     }
@@ -233,6 +248,47 @@ struct Context::Impl
     }
   }
 
+  /**
+   * Prepare to buffer range cache for performance.
+   * We could skip various memory reserving when BindBufferRange called.
+   */
+  void PrepareBufferRangeCache(size_t maxBindings)
+  {
+    if(mUniformBufferBindingCache.Count() < maxBindings)
+    {
+      const auto oldCount = mUniformBufferBindingCache.Count();
+      mUniformBufferBindingCache.ResizeUninitialized(maxBindings);
+      for(auto i = oldCount; i < maxBindings; ++i)
+      {
+        mUniformBufferBindingCache[i].buffer = nullptr;
+      }
+    }
+  }
+
+  /**
+   * Binds and cache buffer ranges.
+   * Cache information 'MUST' be cleard when buffer pointer changed, or some programs invalidated.
+   */
+  void BindBufferRange(const UniformBufferBindingDescriptor& binding)
+  {
+    auto* gl = GetGL();
+    if(DALI_UNLIKELY(!gl)) // early out if no gl
+    {
+      return;
+    }
+
+    DALI_ASSERT_DEBUG(mUniformBufferBindingCache.Count() > binding.binding && "PrepareBufferRangeCache not called!");
+
+    auto& cachedBinding = mUniformBufferBindingCache[binding.binding];
+
+    if(!memcmp4(&cachedBinding, &binding, sizeof(UniformBufferBindingDescriptor)))
+    {
+      // Cache not hit. Update cache and call glBindBufferRange
+      memcpy(&cachedBinding, &binding, sizeof(UniformBufferBindingDescriptor));
+      gl->BindBufferRange(GL_UNIFORM_BUFFER, binding.binding, binding.buffer->GetGLBuffer(), GLintptr(binding.offset), GLintptr(binding.dataSize));
+    }
+  }
+
   /**
    * Get the pointer to the GL implementation
    * @return The GL implementation, nullptr if the context has not been created or shutting down
@@ -265,6 +321,9 @@ struct Context::Impl
   Dali::Vector<UniformBufferBindingDescriptor> mCurrentUBOBindings{};
   UniformBufferBindingDescriptor               mCurrentStandaloneUBOBinding{};
 
+  // Keep bind buffer range. Should be cleared if program changed.
+  Dali::Vector<UniformBufferBindingDescriptor> mUniformBufferBindingCache;
+
   // Current render pass and render target
   const GLES::RenderTarget* mCurrentRenderTarget{nullptr};
   const GLES::RenderPass*   mCurrentRenderPass{nullptr};
@@ -272,7 +331,8 @@ struct Context::Impl
   // Each context must have own VAOs as they cannot be shared
   std::unordered_map<const GLES::ProgramImpl*, std::map<std::size_t, uint32_t>> mProgramVAOMap;              ///< GL program-VAO map
   uint32_t                                                                      mProgramVAOCurrentState{0u}; ///< Currently bound VAO
-  GLStateCache                                                                  mGlStateCache{};             ///< GL status cache
+
+  GLStateCache mGlStateCache{}; ///< GL status cache
 
   std::vector<Dali::GLuint> mDiscardedVAOList{};
 
@@ -304,7 +364,7 @@ Context::~Context()
 void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall, GLES::TextureDependencyChecker& dependencyChecker)
 {
   auto* gl = mImpl->GetGL();
-  if(!gl) // Early out if no gl
+  if(DALI_UNLIKELY(!gl)) // Early out if no gl
   {
     return;
   }
@@ -346,6 +406,8 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall, GLES::
     {
       mImpl->mNewPipeline->Bind(newProgram->GetImplementation()->GetGlProgram());
       programChanged = true;
+
+      ClearUniformBufferCache();
     }
 
     // Blend state
@@ -643,7 +705,7 @@ void Context::ResolveBlendState()
   }
 
   auto* gl = mImpl->GetGL();
-  if(!gl) // Early out if no gl
+  if(DALI_UNLIKELY(!gl)) // Early out if no gl
   {
     return;
   }
@@ -732,7 +794,7 @@ void Context::ResolveRasterizationState()
   }
 
   auto* gl = mImpl->GetGL();
-  if(!gl) // Early out if no gl
+  if(DALI_UNLIKELY(!gl)) // Early out if no gl
   {
     return;
   }
@@ -773,14 +835,12 @@ void Context::ResolveUniformBuffers()
 
 void Context::ResolveGpuUniformBuffers()
 {
-  if(auto* gl = mImpl->GetGL())
+  mImpl->PrepareBufferRangeCache(mImpl->mCurrentUBOBindings.Count());
+  for(const auto& binding : mImpl->mCurrentUBOBindings)
   {
-    for(const auto& binding : mImpl->mCurrentUBOBindings)
+    if(DALI_LIKELY(binding.buffer && binding.dataSize > 0u))
     {
-      if(DALI_LIKELY(binding.buffer && binding.dataSize > 0u))
-      {
-        gl->BindBufferRange(GL_UNIFORM_BUFFER, binding.binding, binding.buffer->GetGLBuffer(), GLintptr(binding.offset), GLintptr(binding.dataSize));
-      }
+      mImpl->BindBufferRange(binding);
     }
   }
 }
@@ -815,7 +875,7 @@ void Context::BeginRenderPass(const BeginRenderPassDescriptor& renderPassBegin)
   const auto& targetInfo = renderTarget.GetCreateInfo();
 
   auto* gl = mImpl->GetGL();
-  if(!gl) // Early out if no gl
+  if(DALI_UNLIKELY(!gl)) // Early out if no gl
   {
     return;
   }
@@ -984,6 +1044,11 @@ void Context::ClearVertexBufferCache()
   }
 }
 
+void Context::ClearUniformBufferCache()
+{
+  mImpl->mUniformBufferBindingCache.Clear();
+}
+
 void Context::ColorMask(bool enabled)
 {
   auto* gl = mImpl->GetGL();
@@ -1265,10 +1330,9 @@ void Context::InvalidateCachedPipeline(GLES::Pipeline* pipeline)
           }
 
           // Clear cached Vertex buffer.
+          ResetBufferCache();
           ClearVertexBufferCache();
 
-          mImpl->mGlStateCache.ResetBufferCache();
-
           mImpl->mProgramVAOMap.erase(iter);
         }
       }
@@ -1345,15 +1409,21 @@ void Context::PrepareForNativeRendering()
   DALI_TIME_CHECKER_END_WITH_MESSAGE(gTimeCheckerFilter, "PrepareForNativeRendering");
 }
 
-void Context::ResetGLESState()
+void Context::ResetBufferCache()
 {
   mImpl->mGlStateCache.ResetBufferCache();
+  ClearUniformBufferCache();
+}
+
+void Context::ResetGLESState()
+{
   mImpl->mGlStateCache.ResetTextureCache();
   mImpl->mCurrentPipeline = nullptr;
 
   mImpl->mCurrentIndexBufferBinding = {};
 
   ClearState();
+  ResetBufferCache();
   ClearVertexBufferCache();
   mImpl->InitializeGlState();
 }
index ca63593a0a433feb32af63722577569da8100a58..4faa5fa562b5c0fcdb9ee76ecc471f83e2f07e13 100644 (file)
@@ -225,6 +225,8 @@ public:
   void SetDepthTestEnable(bool depthTestEnable);
   void SetDepthWriteEnable(bool depthWriteEnable);
 
+  void ResetBufferCache();
+
   void ResetGLESState();
 
 private:
@@ -238,6 +240,11 @@ private:
    */
   void ClearVertexBufferCache();
 
+  /**
+   * @brief Clear cached bind buffer state
+   */
+  void ClearUniformBufferCache();
+
 private:
   struct Impl;
   std::unique_ptr<Impl> mImpl;
index 29c239a758618da7cd60e77796904c9c866588d9..848986befbf50576e455280f13cc40856c0295d3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
 #include "gles-graphics-program.h"
 
 // INTERNAL HEADERS
+#include <dali/devel-api/adaptor-framework/file-loader.h>
 #include <dali/internal/graphics/common/shader-parser.h>
 #include <dali/public-api/dali-adaptor-version.h>
-#include <dali/devel-api/adaptor-framework/file-loader.h>
 #include "egl-graphics-controller.h"
 #include "gles-graphics-reflection.h"
 #include "gles-graphics-shader.h"
 
 // EXTERNAL HEADERS
-#include <iostream>
-#include <filesystem>
 #include <unistd.h>
+#include <filesystem>
 #include <fstream>
+#include <iostream>
 
 static constexpr const char* FRAGMENT_SHADER_ADVANCED_BLEND_EQUATION_PREFIX =
   "#ifdef GL_KHR_blend_equation_advanced\n"
@@ -344,7 +344,6 @@ bool ProgramImpl::Create()
   }
 
   // Set up uniform block bindings
-  auto binding    = 0u;
   auto blockCount = reflection->GetUniformBlockCount();
   for(uint32_t i = 1; i < blockCount; ++i) // Ignore emulated block at #0
   {
@@ -353,7 +352,7 @@ bool ProgramImpl::Create()
 
     // make binding point
     auto blockIndex = gl->GetUniformBlockIndex(program, uboInfo.name.c_str());
-    gl->UniformBlockBinding(program, blockIndex, binding++);
+    gl->UniformBlockBinding(program, blockIndex, uboInfo.binding);
   }
 
   return true;
@@ -570,8 +569,8 @@ bool ProgramImpl::IsEnableProgramBinary() const
 std::string ProgramImpl::GetProgramBinaryName()
 {
   // Check shader with dali-version, name and total shader size
-  const auto& info = mImpl->createInfo;
-  uint32_t totalShaderSize = 0u;
+  const auto& info            = mImpl->createInfo;
+  uint32_t    totalShaderSize = 0u;
   for(const auto& state : *info.shaderState)
   {
     const auto* shader = static_cast<const GLES::Shader*>(state.shader);
@@ -586,7 +585,7 @@ bool ProgramImpl::LoadProgramBinary()
 {
   auto binaryShaderFilename = GetSystemProgramBinaryPath() + GetProgramBinaryName();
 
-  bool result = false;
+  bool               result = false;
   Dali::Vector<char> buffer;
   result = Dali::FileLoader::ReadFile(binaryShaderFilename, buffer);
 
@@ -644,10 +643,10 @@ bool ProgramImpl::LoadProgramBinary()
 
 void ProgramImpl::SaveProgramBinary()
 {
-  GLint binaryLength{0u};
-  GLint binarySize{0u};
+  GLint  binaryLength{0u};
+  GLint  binarySize{0u};
   GLenum format;
-  auto gl = mImpl->controller.GetGL();
+  auto   gl = mImpl->controller.GetGL();
   if(!gl)
   {
     DALI_LOG_ERROR("Can't Get GL \n");
@@ -663,18 +662,18 @@ void ProgramImpl::SaveProgramBinary()
 
   std::vector<uint8_t> programBinary(binaryLength);
   gl->GetProgramBinary(mImpl->glProgram, binaryLength, &binarySize, &format, programBinary.data());
-  if (binarySize != binaryLength)
+  if(binarySize != binaryLength)
   {
     DALI_LOG_ERROR("Program binary created but size mismatch %d != %d\n", binarySize, binaryLength);
     return;
   }
 
-  auto programBinaryName = GetSystemProgramBinaryPath() + GetProgramBinaryName();
+  auto programBinaryName     = GetSystemProgramBinaryPath() + GetProgramBinaryName();
   auto programBinaryNameTemp = programBinaryName + std::to_string(getpid()) + ".tmp";
-  bool loaded = SaveFile(programBinaryNameTemp, (unsigned char*)programBinary.data(), binaryLength);
+  bool loaded                = SaveFile(programBinaryNameTemp, (unsigned char*)programBinary.data(), binaryLength);
   if(!loaded)
   {
-    DALI_LOG_ERROR("Program binary save failed!! file = %s \n",programBinaryName.c_str());
+    DALI_LOG_ERROR("Program binary save failed!! file = %s \n", programBinaryName.c_str());
     return;
   }
 
index fc8e2d7dcafd15b33397ea63fe3ac49e23fc070d..56db06ad076b13de5a9c190864eae92e76acf53a 100644 (file)
@@ -1151,6 +1151,9 @@ struct UniformBufferBindingDescriptor
   uint32_t            binding;
 };
 
+// Check the size of struct is times of 4, So we could use memcmp4
+static_assert(sizeof(UniformBufferBindingDescriptor) % 4 == 0);
+
 /**
  * @brief The descriptor of draw call
  */