Merge "Added shader support to pipeline cache" into devel/master
[platform/core/uifw/dali-adaptor.git] / dali / internal / graphics / gles-impl / gles-graphics-pipeline-cache.cpp
index f9a7d16..b567a27 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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-pipeline.h"
 #include "gles-graphics-program.h"
 
+namespace
+{
+constexpr uint32_t CACHE_CLEAN_FLUSH_COUNT = 3600u; // 60fps * 60sec / ~3 flushes per frame
+}
+
 namespace Dali::Graphics::GLES
 {
 /**
@@ -133,85 +138,88 @@ operator==(const Dali::Graphics::VertexInputState::Binding& lhs, const Dali::Gra
 using PipelineStateCompateFunctionType = bool(const Graphics::PipelineCreateInfo*,
                                               const Graphics::PipelineCreateInfo*);
 
-static std::vector<PipelineStateCompateFunctionType*> STATE_COMPARE_FUNC_TABLE{};
+static std::vector<PipelineStateCompateFunctionType*>& GetStateCompareFuncTable()
+{
+  static std::vector<PipelineStateCompateFunctionType*> stateCompareFuncTable{};
+  return stateCompareFuncTable;
+}
 
 /**
  * @brief Initialises compare function lookup table
  */
 void InitialiseStateCompareLookupTable()
 {
-  STATE_COMPARE_FUNC_TABLE = {
-    [](const auto* lhs, const auto* rhs) -> bool // colorBlendState
-    {
-      const auto& lcb = *lhs->colorBlendState;
-      const auto& rcb = *rhs->colorBlendState;
-      return lcb.logicOpEnable == rcb.logicOpEnable &&
-             lcb.logicOp == rcb.logicOp &&
-             cmpf(lcb.blendConstants[0], rcb.blendConstants[0]) &&
-             cmpf(lcb.blendConstants[1], rcb.blendConstants[1]) &&
-             cmpf(lcb.blendConstants[2], rcb.blendConstants[2]) &&
-             cmpf(lcb.blendConstants[3], rcb.blendConstants[3]) &&
-             lcb.blendEnable == rcb.blendEnable &&
-             lcb.srcColorBlendFactor == rcb.srcColorBlendFactor &&
-             lcb.dstColorBlendFactor == rcb.dstColorBlendFactor &&
-             lcb.colorBlendOp == rcb.colorBlendOp &&
-             lcb.srcAlphaBlendFactor == rcb.srcAlphaBlendFactor &&
-             lcb.dstAlphaBlendFactor == rcb.dstAlphaBlendFactor &&
-             lcb.alphaBlendOp == rcb.alphaBlendOp &&
-             lcb.colorComponentWriteBits == rcb.colorComponentWriteBits;
-    },
-    [](const auto* lhs, const auto* rhs) -> bool // viewport state
-    {
-      const auto& lvp = *lhs->viewportState;
-      const auto& rvp = *rhs->viewportState;
-      return lvp.viewport == rvp.viewport &&
-             lvp.scissor == rvp.scissor &&
-             lvp.scissorTestEnable == rvp.scissorTestEnable;
-    },
-    [](const auto* lhs, const auto* rhs) -> bool // basePipeline
-    {
-      return lhs->basePipeline == rhs->basePipeline;
-    },
-    [](const auto* lhs, const auto* rhs) -> bool // depthStencilState
-    {
-      const auto& lds = *lhs->depthStencilState;
-      const auto& rds = *rhs->depthStencilState;
-      return lds.depthTestEnable == rds.depthTestEnable &&
-             lds.depthWriteEnable == rds.depthWriteEnable &&
-             lds.depthCompareOp == rds.depthCompareOp &&
-             lds.stencilTestEnable == rds.stencilTestEnable &&
-             lds.front == rds.front &&
-             lds.back == rds.back;
-    },
-    [](const auto* lhs, const auto* rhs) -> bool // rasterizationState
-    {
-      const auto& lrs = *lhs->rasterizationState;
-      const auto& rrs = *rhs->rasterizationState;
-      return lrs.cullMode == rrs.cullMode &&
-             lrs.polygonMode == rrs.polygonMode &&
-             lrs.frontFace == rrs.frontFace;
-    },
-    [](const auto* lhs, const auto* rhs) -> bool // vertexInputState
-    {
-      const auto& lvi = *lhs->vertexInputState;
-      const auto& rvi = *rhs->vertexInputState;
-      return lvi.bufferBindings.size() == rvi.bufferBindings.size() &&
-             lvi.attributes.size() == rvi.attributes.size() &&
-             std::equal(lvi.bufferBindings.begin(), lvi.bufferBindings.end(), rvi.bufferBindings.begin(), [](const auto& lhs, const auto& rhs) {
-               return operator==(lhs, rhs);
-             }) &&
-             std::equal(lvi.attributes.begin(), lvi.attributes.end(), rvi.attributes.begin(), [](const auto& lhs, const auto& rhs) {
-               return operator==(lhs, rhs);
-             });
-    },
-    [](const auto* lhs, const auto* rhs) -> bool // inputAssemblyState
-    {
-      const auto& lia = *lhs->inputAssemblyState;
-      const auto& ria = *rhs->inputAssemblyState;
-      return lia.topology == ria.topology &&
-             lia.primitiveRestartEnable == ria.primitiveRestartEnable;
-    },
-  };
+  GetStateCompareFuncTable().clear();
+  GetStateCompareFuncTable().push_back([](const auto* lhs, const auto* rhs) -> bool // colorBlendState
+                                       {
+                                         const auto& lcb = *lhs->colorBlendState;
+                                         const auto& rcb = *rhs->colorBlendState;
+                                         return lcb.logicOpEnable == rcb.logicOpEnable &&
+                                                lcb.logicOp == rcb.logicOp &&
+                                                cmpf(lcb.blendConstants[0], rcb.blendConstants[0]) &&
+                                                cmpf(lcb.blendConstants[1], rcb.blendConstants[1]) &&
+                                                cmpf(lcb.blendConstants[2], rcb.blendConstants[2]) &&
+                                                cmpf(lcb.blendConstants[3], rcb.blendConstants[3]) &&
+                                                lcb.blendEnable == rcb.blendEnable &&
+                                                lcb.srcColorBlendFactor == rcb.srcColorBlendFactor &&
+                                                lcb.dstColorBlendFactor == rcb.dstColorBlendFactor &&
+                                                lcb.colorBlendOp == rcb.colorBlendOp &&
+                                                lcb.srcAlphaBlendFactor == rcb.srcAlphaBlendFactor &&
+                                                lcb.dstAlphaBlendFactor == rcb.dstAlphaBlendFactor &&
+                                                lcb.alphaBlendOp == rcb.alphaBlendOp &&
+                                                lcb.colorComponentWriteBits == rcb.colorComponentWriteBits;
+                                       });
+  GetStateCompareFuncTable().push_back([](const auto* lhs, const auto* rhs) -> bool // viewport state
+                                       {
+                                         const auto& lvp = *lhs->viewportState;
+                                         const auto& rvp = *rhs->viewportState;
+                                         return lvp.viewport == rvp.viewport &&
+                                                lvp.scissor == rvp.scissor &&
+                                                lvp.scissorTestEnable == rvp.scissorTestEnable;
+                                       });
+  GetStateCompareFuncTable().push_back([](const auto* lhs, const auto* rhs) -> bool // basePipeline
+                                       {
+                                         return lhs->basePipeline == rhs->basePipeline;
+                                       });
+  GetStateCompareFuncTable().push_back([](const auto* lhs, const auto* rhs) -> bool // depthStencilState
+                                       {
+                                         const auto& lds = *lhs->depthStencilState;
+                                         const auto& rds = *rhs->depthStencilState;
+                                         return lds.depthTestEnable == rds.depthTestEnable &&
+                                                lds.depthWriteEnable == rds.depthWriteEnable &&
+                                                lds.depthCompareOp == rds.depthCompareOp &&
+                                                lds.stencilTestEnable == rds.stencilTestEnable &&
+                                                lds.front == rds.front &&
+                                                lds.back == rds.back;
+                                       });
+  GetStateCompareFuncTable().push_back([](const auto* lhs, const auto* rhs) -> bool // rasterizationState
+                                       {
+                                         const auto& lrs = *lhs->rasterizationState;
+                                         const auto& rrs = *rhs->rasterizationState;
+                                         return lrs.cullMode == rrs.cullMode &&
+                                                lrs.polygonMode == rrs.polygonMode &&
+                                                lrs.frontFace == rrs.frontFace;
+                                       });
+  GetStateCompareFuncTable().push_back([](const auto* lhs, const auto* rhs) -> bool // vertexInputState
+                                       {
+                                         const auto& lvi = *lhs->vertexInputState;
+                                         const auto& rvi = *rhs->vertexInputState;
+                                         return lvi.bufferBindings.size() == rvi.bufferBindings.size() &&
+                                                lvi.attributes.size() == rvi.attributes.size() &&
+                                                std::equal(lvi.bufferBindings.begin(), lvi.bufferBindings.end(), rvi.bufferBindings.begin(), [](const auto& lhs, const auto& rhs) {
+                                                  return operator==(lhs, rhs);
+                                                }) &&
+                                                std::equal(lvi.attributes.begin(), lvi.attributes.end(), rvi.attributes.begin(), [](const auto& lhs, const auto& rhs) {
+                                                  return operator==(lhs, rhs);
+                                                });
+                                       });
+  GetStateCompareFuncTable().push_back([](const auto* lhs, const auto* rhs) -> bool // inputAssemblyState
+                                       {
+                                         const auto& lia = *lhs->inputAssemblyState;
+                                         const auto& ria = *rhs->inputAssemblyState;
+                                         return lia.topology == ria.topology &&
+                                                lia.primitiveRestartEnable == ria.primitiveRestartEnable;
+                                       });
 }
 
 /**
@@ -242,7 +250,10 @@ struct PipelineCache::Impl
    * @brief Constructor
    */
   explicit Impl(EglGraphicsController& _controller)
-  : controller(_controller)
+  : controller(_controller),
+    pipelineEntriesFlushRequired(false),
+    programEntriesFlushRequired(false),
+    shaderEntriesFlushRequired(false)
   {
     // Initialise lookup table
     InitialiseStateCompareLookupTable();
@@ -282,9 +293,6 @@ struct PipelineCache::Impl
     uint32_t                stateBitmask{0u};
   };
 
-  EglGraphicsController&  controller;
-  std::vector<CacheEntry> entries;
-
   /**
    * @brief Sorted array of shaders used to create program
    */
@@ -295,7 +303,19 @@ struct PipelineCache::Impl
     UniquePtr<ProgramImpl>               program{nullptr};
   };
 
+  struct ShaderCacheEntry
+  {
+    UniquePtr<ShaderImpl> shaderImpl{nullptr};
+  };
+
+  EglGraphicsController&         controller;
+  std::vector<CacheEntry>        entries;
   std::vector<ProgramCacheEntry> programEntries;
+  std::vector<ShaderCacheEntry>  shaderEntries;
+
+  bool pipelineEntriesFlushRequired : 1;
+  bool programEntriesFlushRequired : 1;
+  bool shaderEntriesFlushRequired : 1;
 };
 
 PipelineCache::PipelineCache(EglGraphicsController& controller)
@@ -341,7 +361,7 @@ PipelineImpl* PipelineCache::FindPipelineImpl(const PipelineCreateInfo& info)
         // Test only set states
         if((entry.stateBitmask & (1 << i)))
         {
-          if(!STATE_COMPARE_FUNC_TABLE[i](&info, &cacheInfo))
+          if(!(GetStateCompareFuncTable()[i](&info, &cacheInfo)))
           {
             break;
           }
@@ -439,7 +459,7 @@ Graphics::UniquePtr<Graphics::Program> PipelineCache::GetProgram(const ProgramCr
     // create new pipeline
     auto program = MakeUnique<GLES::ProgramImpl>(programCreateInfo, mImpl->controller);
 
-    program->Create();
+    program->Create(); // Don't currently handle failure.
 
     cachedProgram = program.get();
 
@@ -459,28 +479,143 @@ Graphics::UniquePtr<Graphics::Program> PipelineCache::GetProgram(const ProgramCr
   return std::move(wrapper);
 }
 
+ShaderImpl* PipelineCache::FindShaderImpl(const ShaderCreateInfo& shaderCreateInfo)
+{
+  if(!mImpl->shaderEntries.empty())
+  {
+    for(auto& item : mImpl->shaderEntries)
+    {
+      auto& itemInfo = item.shaderImpl->GetCreateInfo();
+      if(itemInfo.pipelineStage != shaderCreateInfo.pipelineStage ||
+         itemInfo.shaderlanguage != shaderCreateInfo.shaderlanguage ||
+         itemInfo.sourceMode != shaderCreateInfo.sourceMode ||
+         itemInfo.sourceSize != shaderCreateInfo.sourceSize)
+      {
+        continue;
+      }
+
+      if(memcmp(itemInfo.sourceData, shaderCreateInfo.sourceData, itemInfo.sourceSize) == 0)
+      {
+        return item.shaderImpl.get();
+      }
+    }
+  }
+  return nullptr;
+}
+
+Graphics::UniquePtr<Graphics::Shader> PipelineCache::GetShader(const ShaderCreateInfo&                 shaderCreateInfo,
+                                                               Graphics::UniquePtr<Graphics::Shader>&& oldShader)
+{
+  ShaderImpl* cachedShader = FindShaderImpl(shaderCreateInfo);
+
+  // Return same pointer if nothing changed
+  if(oldShader && *static_cast<GLES::Shader*>(oldShader.get()) == cachedShader)
+  {
+    return std::move(oldShader);
+  }
+
+  if(!cachedShader)
+  {
+    auto shader  = MakeUnique<GLES::ShaderImpl>(shaderCreateInfo, mImpl->controller);
+    cachedShader = shader.get();
+
+    mImpl->shaderEntries.emplace_back();
+    mImpl->shaderEntries.back().shaderImpl = std::move(shader);
+  }
+  auto wrapper = MakeUnique<GLES::Shader, CachedObjectDeleter<GLES::Shader>>(cachedShader);
+  return std::move(wrapper);
+}
+
 void PipelineCache::FlushCache()
 {
-  decltype(mImpl->entries) newEntries;
-  newEntries.reserve(mImpl->entries.size());
+  if(mImpl->pipelineEntriesFlushRequired)
+  {
+    decltype(mImpl->entries) newEntries;
+    newEntries.reserve(mImpl->entries.size());
 
-  for(auto& entry : mImpl->entries)
+    for(auto& entry : mImpl->entries)
+    {
+      // Move items which are still in use into the new array
+      if(entry.pipeline->GetRefCount() != 0)
+      {
+        newEntries.emplace_back(std::move(entry));
+      }
+    }
+
+    // Move temporary array in place of stored cache
+    // Unused pipelines will be deleted automatically
+    mImpl->entries = std::move(newEntries);
+
+    mImpl->pipelineEntriesFlushRequired = false;
+  }
+
+  if(mImpl->programEntriesFlushRequired)
+  {
+    // Program cache require similar action.
+    decltype(mImpl->programEntries) newProgramEntries;
+    newProgramEntries.reserve(mImpl->programEntries.size());
+
+    for(auto& entry : mImpl->programEntries)
+    {
+      // Move items which are still in use into the new array
+      if(entry.program->GetRefCount() != 0)
+      {
+        newProgramEntries.emplace_back(std::move(entry));
+      }
+    }
+
+    mImpl->programEntries = std::move(newProgramEntries);
+
+    mImpl->programEntriesFlushRequired = false;
+  }
+
+  if(mImpl->shaderEntriesFlushRequired)
   {
-    // Move items which are still in use into the new array
-    if(entry.pipeline->GetRefCount() != 0)
+    // There is at least 1 unused shader
+    mImpl->shaderEntriesFlushRequired = false;
+    bool deleteRequired{false};
+    for(auto& entry : mImpl->shaderEntries)
+    {
+      if(entry.shaderImpl->GetRefCount() == 0)
+      {
+        mImpl->shaderEntriesFlushRequired = true;
+        auto frameCount                   = entry.shaderImpl->IncreaseFlushCount();
+        if(frameCount > CACHE_CLEAN_FLUSH_COUNT)
+        {
+          deleteRequired = true;
+        }
+      }
+    }
+    if(deleteRequired)
     {
-      newEntries.emplace_back(std::move(entry));
+      decltype(mImpl->shaderEntries) newShaderEntries;
+      newShaderEntries.reserve(mImpl->shaderEntries.size());
+      for(auto& entry : mImpl->shaderEntries)
+      {
+        if(entry.shaderImpl->GetRefCount() > 0 ||
+           entry.shaderImpl->GetFlushCount() <= CACHE_CLEAN_FLUSH_COUNT)
+        {
+          newShaderEntries.emplace_back(std::move(entry));
+        }
+      }
+      mImpl->shaderEntries = std::move(newShaderEntries);
     }
   }
+}
+
+void PipelineCache::MarkPipelineCacheFlushRequired()
+{
+  mImpl->pipelineEntriesFlushRequired = true;
+}
 
-  // Move temporary array in place of stored cache
-  // Unused pipelines will be deleted automatically
-  mImpl->entries = std::move(newEntries);
+void PipelineCache::MarkProgramCacheFlushRequired()
+{
+  mImpl->programEntriesFlushRequired = true;
+}
 
-  // TODO: program cache may require similar action. However,
-  //       since there is always one wrapper for Program object
-  //       kept in the pipeline, then death of pipeline will result
-  //       killing the program (if program isn't in use anymore)
+void PipelineCache::MarkShaderCacheFlushRequired()
+{
+  mImpl->shaderEntriesFlushRequired = true;
 }
 
-} // namespace Dali::Graphics::GLES
\ No newline at end of file
+} // namespace Dali::Graphics::GLES