Fix INTEGER_OVERFLOW coverity issues
[platform/core/uifw/dali-core.git] / dali / internal / render / shaders / program.cpp
index 1c8bacc..c149767 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 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.
@@ -20,7 +20,6 @@
 
 // EXTERNAL INCLUDES
 #include <cstring>
-#include <iomanip>
 #include <map>
 
 // INTERNAL INCLUDES
@@ -29,9 +28,9 @@
 #include <dali/graphics-api/graphics-program.h>
 #include <dali/graphics-api/graphics-reflection.h>
 #include <dali/integration-api/debug.h>
-#include <dali/integration-api/gl-defines.h>
 #include <dali/internal/common/shader-data.h>
 #include <dali/internal/render/common/performance-monitor.h>
+#include <dali/internal/render/renderers/uniform-buffer-manager.h>
 #include <dali/internal/render/shaders/program-cache.h>
 #include <dali/public-api/common/constants.h>
 #include <dali/public-api/common/dali-common.h>
@@ -44,21 +43,6 @@ namespace Internal
 // LOCAL STUFF
 namespace
 {
-const char* const gStdUniforms[Program::UNIFORM_TYPE_LAST] =
-  {
-    "uMvpMatrix",    // UNIFORM_MVP_MATRIX
-    "uModelView",    // UNIFORM_MODELVIEW_MATRIX
-    "uProjection",   // UNIFORM_PROJECTION_MATRIX
-    "uModelMatrix",  // UNIFORM_MODEL_MATRIX,
-    "uViewMatrix",   // UNIFORM_VIEW_MATRIX,
-    "uNormalMatrix", // UNIFORM_NORMAL_MATRIX
-    "uColor",        // UNIFORM_COLOR
-    "sTexture",      // UNIFORM_SAMPLER
-    "sTextureRect",  // UNIFORM_SAMPLER_RECT
-    "sEffect",       // UNIFORM_EFFECT_SAMPLER
-    "uSize"          // UNIFORM_SIZE
-};
-
 const unsigned int NUMBER_OF_DEFAULT_UNIFORMS = static_cast<unsigned int>(Program::DefaultUniformIndex::COUNT);
 
 /**
@@ -72,119 +56,54 @@ size_t DEFAULT_UNIFORM_HASHTABLE[NUMBER_OF_DEFAULT_UNIFORMS] =
     CalculateHash(std::string("uModelView")),
     CalculateHash(std::string("uNormalMatrix")),
     CalculateHash(std::string("uProjection")),
+    CalculateHash(std::string("uScale")),
     CalculateHash(std::string("uSize")),
-    CalculateHash(std::string("uColor"))};
+    CalculateHash(std::string("uColor")),
+    CalculateHash(std::string("uActorColor"))};
+
+/**
+ * Helper function to calculate the correct alignment of data for uniform buffers
+ * @param dataSize size of uniform buffer
+ * @return size of data aligned to given size
+ */
+inline uint32_t AlignSize(uint32_t dataSize, uint32_t alignSize)
+{
+  return ((dataSize / alignSize) + ((dataSize % alignSize) ? 1u : 0u)) * alignSize;
+}
 
 } // namespace
 
 // IMPLEMENTATION
 
-Program* Program::New(ProgramCache& cache, Internal::ShaderDataPtr shaderData, Graphics::Controller& gfxController, Graphics::UniquePtr<Graphics::Program>&& gfxProgram, bool modifiesGeometry)
+Program* Program::New(ProgramCache& cache, const Internal::ShaderDataPtr& shaderData, Graphics::Controller& gfxController)
 {
-  uint32_t programId{0u};
-
-  // Get program id and use it as hash for the cache
-  // in order to maintain current functionality as long as needed
-  gfxController.GetProgramParameter(*gfxProgram, 1, &programId);
-
-  size_t shaderHash = programId;
+  size_t shaderHash = shaderData->GetHashValue();
 
   Program* program = cache.GetProgram(shaderHash);
 
   if(nullptr == program)
   {
     // program not found so create it
-    program = new Program(cache, shaderData, gfxController, std::move(gfxProgram), modifiesGeometry);
+    program = new Program(cache, shaderData, gfxController);
+
+    DALI_LOG_INFO(Debug::Filter::gShader, Debug::Verbose, "Program::New() created a unique program:\n  VertexShader:\n%s\n\n  FragShader:\n%s\n", shaderData->GetVertexShader(), shaderData->GetFragmentShader());
     cache.AddProgram(shaderHash, program);
   }
 
   return program;
 }
 
-uint32_t Program::RegisterUniform(ConstString name)
-{
-  uint32_t index = 0;
-  // find the value from cache
-  for(; index < static_cast<uint32_t>(mUniformLocations.size()); ++index)
-  {
-    if(mUniformLocations[index].first == name)
-    {
-      // name found so return index
-      return index;
-    }
-  }
-  // if we get here, index is one past end so push back the new name
-  mUniformLocations.push_back(std::make_pair(name, UNIFORM_NOT_QUERIED));
-  return index;
-}
-
-bool Program::ModifiesGeometry()
-{
-  return mModifiesGeometry;
-}
-
-Program::Program(ProgramCache& cache, Internal::ShaderDataPtr shaderData, Graphics::Controller& controller, Graphics::UniquePtr<Graphics::Program>&& gfxProgram, bool modifiesGeometry)
+Program::Program(ProgramCache& cache, Internal::ShaderDataPtr shaderData, Graphics::Controller& controller)
 : mCache(cache),
-  mProjectionMatrix(nullptr),
-  mViewMatrix(nullptr),
-  mProgramId(0),
-  mGfxProgram(std::move(gfxProgram)),
+  mGfxProgram(nullptr),
   mGfxController(controller),
-  mProgramData(shaderData),
-  mModifiesGeometry(modifiesGeometry)
+  mProgramData(std::move(shaderData))
 {
-  // reserve space for standard uniforms
-  mUniformLocations.reserve(UNIFORM_TYPE_LAST);
-
-  // reset built in uniform names in cache
-  for(uint32_t i = 0; i < UNIFORM_TYPE_LAST; ++i)
-  {
-    RegisterUniform(ConstString(gStdUniforms[i]));
-  }
-
-  // reset values
-  ResetUniformCache();
-
-  // Get program id and use it as hash for the cache
-  // in order to maintain current functionality as long as needed
-  mGfxController.GetProgramParameter(*mGfxProgram, 1, &mProgramId);
-
-  BuildReflection(controller.GetProgramReflection(*mGfxProgram.get()));
 }
 
-Program::~Program()
-{
-}
+Program::~Program() = default;
 
-void Program::ResetUniformCache()
-{
-  // reset all gl uniform locations
-  for(uint32_t i = 0; i < mUniformLocations.size(); ++i)
-  {
-    // reset gl program locations and names
-    mUniformLocations[i].second = UNIFORM_NOT_QUERIED;
-  }
-
-  mSamplerUniformLocations.clear();
-
-  // reset uniform caches
-  mSizeUniformCache.x = mSizeUniformCache.y = mSizeUniformCache.z = 0.f;
-
-  for(uint32_t i = 0; i < MAX_UNIFORM_CACHE_SIZE; ++i)
-  {
-    // GL initializes uniforms to 0
-    mUniformCacheInt[i]       = 0;
-    mUniformCacheFloat[i]     = 0.0f;
-    mUniformCacheFloat2[i][0] = 0.0f;
-    mUniformCacheFloat2[i][1] = 0.0f;
-    mUniformCacheFloat4[i][0] = 0.0f;
-    mUniformCacheFloat4[i][1] = 0.0f;
-    mUniformCacheFloat4[i][2] = 0.0f;
-    mUniformCacheFloat4[i][3] = 0.0f;
-  }
-}
-
-void Program::BuildReflection(const Graphics::Reflection& graphicsReflection)
+void Program::BuildReflection(const Graphics::Reflection& graphicsReflection, Render::UniformBufferManager& uniformBufferManager)
 {
   mReflectionDefaultUniforms.clear();
   mReflectionDefaultUniforms.resize(NUMBER_OF_DEFAULT_UNIFORMS);
@@ -200,6 +119,13 @@ void Program::BuildReflection(const Graphics::Reflection& graphicsReflection)
     // for each member store data
     for(const auto& item : uboInfo.members)
     {
+      // Add a hash for the whole name.
+      //
+      // If the name represents an array of basic types, it won't contain an index
+      // operator "[",NN,"]".
+      //
+      // If the name represents an element in an array of structs, it will contain an
+      // index operator, but should be hashed in full.
       auto hashValue = CalculateHash(item.name);
       mReflection.emplace_back(ReflectionUniformInfo{hashValue, false, item});
 
@@ -207,11 +133,11 @@ void Program::BuildReflection(const Graphics::Reflection& graphicsReflection)
       mReflection.back().uniformInfo.bufferIndex = i;
 
       // Update default uniforms
-      for(auto i = 0u; i < NUMBER_OF_DEFAULT_UNIFORMS; ++i)
+      for(auto j = 0u; j < NUMBER_OF_DEFAULT_UNIFORMS; ++j)
       {
-        if(hashValue == DEFAULT_UNIFORM_HASHTABLE[i])
+        if(hashValue == DEFAULT_UNIFORM_HASHTABLE[j])
         {
-          mReflectionDefaultUniforms[i] = mReflection.back();
+          mReflectionDefaultUniforms[j] = mReflection.back();
           break;
         }
       }
@@ -219,7 +145,7 @@ void Program::BuildReflection(const Graphics::Reflection& graphicsReflection)
   }
 
   // add samplers
-  auto samplers = graphicsReflection.GetSamplers();
+  auto samplers = graphicsReflection.GetSamplers(); // Only holds first element of arrays without [].
   for(const auto& sampler : samplers)
   {
     mReflection.emplace_back(ReflectionUniformInfo{CalculateHash(sampler.name), false, sampler});
@@ -249,24 +175,86 @@ void Program::BuildReflection(const Graphics::Reflection& graphicsReflection)
       item.hasCollision = hashTest[item.hashValue];
     }
   }
+
+  mUniformBlockMemoryRequirements.blockSize.resize(uniformBlockCount);
+  mUniformBlockMemoryRequirements.blockSizeAligned.resize(uniformBlockCount);
+  mUniformBlockMemoryRequirements.blockCount           = uniformBlockCount;
+  mUniformBlockMemoryRequirements.totalSizeRequired    = 0u;
+  mUniformBlockMemoryRequirements.totalCpuSizeRequired = 0u;
+  mUniformBlockMemoryRequirements.totalGpuSizeRequired = 0u;
+
+  for(auto i = 0u; i < uniformBlockCount; ++i)
+  {
+    Graphics::UniformBlockInfo uboInfo;
+    graphicsReflection.GetUniformBlock(i, uboInfo);
+    bool standaloneUniformBlock = (i == 0);
+
+    auto     blockSize        = graphicsReflection.GetUniformBlockSize(i);
+    uint32_t blockAlignment   = uniformBufferManager.GetUniformBlockAlignment(standaloneUniformBlock);
+    auto     alignedBlockSize = AlignSize(blockSize, blockAlignment);
+
+    mUniformBlockMemoryRequirements.blockSize[i]        = blockSize;
+    mUniformBlockMemoryRequirements.blockSizeAligned[i] = alignedBlockSize;
+
+    mUniformBlockMemoryRequirements.totalSizeRequired += alignedBlockSize;
+    mUniformBlockMemoryRequirements.totalCpuSizeRequired += (standaloneUniformBlock) ? alignedBlockSize : 0;
+    mUniformBlockMemoryRequirements.totalGpuSizeRequired += (standaloneUniformBlock) ? 0 : alignedBlockSize;
+  }
+}
+
+void Program::SetGraphicsProgram(Graphics::UniquePtr<Graphics::Program>&& program, Render::UniformBufferManager& uniformBufferManager)
+{
+  mGfxProgram = std::move(program);
+  BuildReflection(mGfxController.GetProgramReflection(*mGfxProgram.get()), uniformBufferManager);
 }
 
-bool Program::GetUniform(const std::string& name, size_t hashedName, Graphics::UniformInfo& out) const
+bool Program::GetUniform(const std::string_view& name, Hash hashedName, Hash hashedNameNoArray, Graphics::UniformInfo& out) const
 {
   if(mReflection.empty())
   {
     return false;
   }
+  DALI_ASSERT_DEBUG(hashedName != 0 && "GetUniform() hash is not set");
+
+  // If name contains a "]", but has nothing after, it's an element in an array,
+  // The reflection doesn't contain such elements, it only contains the name without square brackets
+  // Use the hash without array subscript.
 
-  hashedName = !hashedName ? CalculateHash(name, '[') : hashedName;
+  // If the name contains a "]" anywhere but the end, it's a structure element. The reflection
+  // does contain such elements, so use normal hash.
+  Hash             hash  = hashedName;
+  std::string_view match = name;
+
+  int arrayIndex = 0;
+
+  if(!name.empty() && name.back() == ']')
+  {
+    auto pos = name.rfind("[");
+    if(pos != std::string::npos)
+    {
+      hash       = hashedNameNoArray;
+      match      = name.substr(0, pos); // Remove subscript
+      arrayIndex = atoi(&name[pos + 1]);
+    }
+  }
 
   for(const ReflectionUniformInfo& item : mReflection)
   {
-    if(item.hashValue == hashedName)
+    if(item.hashValue == hash)
     {
-      if(!item.hasCollision || item.uniformInfo.name == name)
+      if(!item.hasCollision || item.uniformInfo.name == match)
       {
         out = item.uniformInfo;
+
+        // Array out of bounds
+        if(item.uniformInfo.elementCount > 0 && arrayIndex >= int(item.uniformInfo.elementCount))
+        {
+          DALI_LOG_ERROR("Uniform %s, array index out of bound [%d >= %d]!\n",
+                         item.uniformInfo.name.c_str(),
+                         int(arrayIndex),
+                         int(item.uniformInfo.elementCount));
+          return false;
+        }
         return true;
       }
       else