Remove old pipeline caches 55/292455/1
authorHeeyong Song <heeyong.song@samsung.com>
Mon, 8 May 2023 05:59:15 +0000 (14:59 +0900)
committerHeeyong Song <heeyong.song@samsung.com>
Mon, 8 May 2023 09:24:02 +0000 (18:24 +0900)
Change-Id: I17290e72f05cecedfce82dc665217c38a99a8470

automated-tests/src/dali-internal/utc-Dali-Internal-PipelineCache.cpp
dali/internal/render/common/render-manager.cpp
dali/internal/render/renderers/pipeline-cache.cpp
dali/internal/render/renderers/pipeline-cache.h
dali/internal/render/renderers/render-renderer.cpp
dali/internal/render/renderers/render-renderer.h

index 9783618..352bce9 100644 (file)
@@ -159,7 +159,22 @@ int UtcDaliCorePipelineCacheTest(void)
       }
     }
   }
+
+  DALI_TEST_EQUALS(gPipelineCache->level0nodes.size(), 3, TEST_LOCATION);
   DALI_TEST_EQUALS(noBlendFoundCount, 1u, TEST_LOCATION);
 
+  // Remove renderer to test whether old pipeline is removed
+  application.GetScene().Remove(actor1);
+  actor1.RemoveRenderer(renderer1);
+  renderer1.Reset();
+
+  // Make the frame count of the pipeline cache large to clean cache
+  gPipelineCache->mFrameCount = 1000;
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(gPipelineCache->level0nodes.size(), 2, TEST_LOCATION);
+
   END_TEST;
-}
\ No newline at end of file
+}
index 9a895fc..6b58571 100644 (file)
@@ -128,6 +128,7 @@ struct RenderManager::Impl
   ~Impl()
   {
     threadPool.reset(nullptr); // reset now to maintain correct destruction order
+    rendererContainer.Clear(); // clear now before the pipeline cache is deleted
   }
 
   void AddRenderTracker(Render::RenderTracker* renderTracker)
@@ -463,8 +464,8 @@ void RenderManager::PreRender(Integration::RenderStatus& status, bool forceClear
     }
   }
 
-  // Clean latest used pipeline
-  mImpl->pipelineCache->CleanLatestUsedCache();
+  // Reset pipeline cache before rendering
+  mImpl->pipelineCache->PreRender();
 
   mImpl->commandBufferSubmitted = false;
 }
index 4e4633f..92fe22b 100644 (file)
@@ -29,6 +29,8 @@ namespace Dali::Internal::Render
 {
 namespace
 {
+constexpr uint32_t CACHE_CLEAN_FRAME_COUNT = 600; // 60fps * 10sec
+
 // Helper to get the vertex input format
 Dali::Graphics::VertexInputFormat GetPropertyVertexFormat(Property::Type propertyType)
 {
@@ -322,6 +324,21 @@ PipelineCacheL1* PipelineCacheL0::GetPipelineCacheL1(Render::Renderer* renderer,
   return retval;
 }
 
+void PipelineCacheL0::ClearUnusedCache()
+{
+  for(auto iter = level1nodes.begin(); iter != level1nodes.end();)
+  {
+    if(iter->ClearUnusedCache())
+    {
+      iter = level1nodes.erase(iter);
+    }
+    else
+    {
+      iter++;
+    }
+  }
+}
+
 PipelineCacheL2* PipelineCacheL1::GetPipelineCacheL2(bool blend, bool premul, BlendingOptions& blendingOptions)
 {
   // early out
@@ -410,6 +427,28 @@ PipelineCacheL2* PipelineCacheL1::GetPipelineCacheL2(bool blend, bool premul, Bl
   return retval;
 }
 
+bool PipelineCacheL1::ClearUnusedCache()
+{
+  if(noBlend.referenceCount > 0)
+  {
+    return false;
+  }
+
+  for(auto iter = level2nodes.begin(); iter != level2nodes.end();)
+  {
+    if(iter->referenceCount == 0)
+    {
+      iter = level2nodes.erase(iter);
+    }
+    else
+    {
+      iter++;
+    }
+  }
+
+  return level2nodes.empty();
+}
+
 void PipelineCacheQueryInfo::GenerateHash()
 {
   // Lightweight hash value generation.
@@ -459,6 +498,7 @@ PipelineResult PipelineCache::GetPipeline(const PipelineCacheQueryInfo& queryInf
   // If we can reuse latest bound pipeline, Fast return.
   if(ReuseLatestBoundPipeline(latestUsedCacheIndex, queryInfo))
   {
+    mLatestResult[latestUsedCacheIndex].level2->referenceCount++;
     return mLatestResult[latestUsedCacheIndex];
   }
 
@@ -488,10 +528,10 @@ PipelineResult PipelineCache::GetPipeline(const PipelineCacheQueryInfo& queryInf
   PipelineResult result{};
 
   result.pipeline = level2->pipeline.get();
-  result.level0   = level0;
-  result.level1   = level1;
   result.level2   = level2;
 
+  level2->referenceCount++;
+
   // Copy query and result
   mLatestQuery[latestUsedCacheIndex]  = queryInfo;
   mLatestResult[latestUsedCacheIndex] = result;
@@ -504,4 +544,41 @@ bool PipelineCache::ReuseLatestBoundPipeline(const int latestUsedCacheIndex, con
   return mLatestResult[latestUsedCacheIndex].pipeline != nullptr && PipelineCacheQueryInfo::Equal(queryInfo, mLatestQuery[latestUsedCacheIndex]);
 }
 
+void PipelineCache::PreRender()
+{
+  CleanLatestUsedCache();
+
+  // We don't need to check this every frame
+  if(++mFrameCount >= CACHE_CLEAN_FRAME_COUNT)
+  {
+    mFrameCount = 0u;
+    ClearUnusedCache();
+  }
+}
+
+void PipelineCache::ClearUnusedCache()
+{
+  for(auto iter = level0nodes.begin(); iter != level0nodes.end();)
+  {
+    iter->ClearUnusedCache();
+
+    if(iter->level1nodes.empty())
+    {
+      iter = level0nodes.erase(iter);
+    }
+    else
+    {
+      iter++;
+    }
+  }
+}
+
+void PipelineCache::ResetPipeline(PipelineCacheL2* pipelineCache)
+{
+  if(pipelineCache)
+  {
+    pipelineCache->referenceCount--;
+  }
+}
+
 } // namespace Dali::Internal::Render
index 6bf11d8..659cecc 100644 (file)
@@ -40,6 +40,7 @@ class Geometry;
 struct PipelineCacheL2
 {
   uint32_t                                hash{};
+  uint32_t                                referenceCount{0u};
   Graphics::ColorBlendState               colorBlendState;
   Graphics::UniquePtr<Graphics::Pipeline> pipeline;
 };
@@ -51,6 +52,11 @@ struct PipelineCacheL1
 {
   PipelineCacheL2* GetPipelineCacheL2(bool blend, bool premul, BlendingOptions& blendingOptions);
 
+  /**
+   * @brief Clear unused caches.
+   */
+  bool ClearUnusedCache();
+
   uint32_t                     hashCode{}; // 1byte cull, 1byte poly, 1byte frontface
   Graphics::RasterizationState rs{};
   Graphics::InputAssemblyState ia{};
@@ -66,6 +72,11 @@ struct PipelineCacheL0 // L0 cache
 {
   PipelineCacheL1* GetPipelineCacheL1(Render::Renderer* renderer, bool usingReflection);
 
+  /**
+   * @brief Clear unused caches.
+   */
+  void ClearUnusedCache();
+
   std::size_t                hash{};
   Geometry*                  geometry{};
   Program*                   program{};
@@ -104,10 +115,7 @@ struct PipelineCacheQueryInfo
 struct PipelineResult
 {
   Graphics::Pipeline* pipeline;
-
-  PipelineCacheL0* level0;
-  PipelineCacheL1* level1;
-  PipelineCacheL2* level2;
+  PipelineCacheL2*    level2;
 };
 
 /**
@@ -146,6 +154,18 @@ public:
   bool ReuseLatestBoundPipeline(const int latestUsedCacheIndex, const PipelineCacheQueryInfo& queryInfo) const;
 
   /**
+   * @brief This is called before rendering every frame.
+   */
+  void PreRender();
+
+  /**
+   * @brief Decrease the reference count of the pipeline cache.
+   * @param pipelineCache The pipeline cache to decrease the reference count
+   */
+  void ResetPipeline(PipelineCacheL2* pipelineCache);
+
+private:
+  /**
    * @brief Clear latest bound result.
    */
   void CleanLatestUsedCache()
@@ -155,6 +175,11 @@ public:
     mLatestResult[1].pipeline = nullptr;
   }
 
+  /**
+   * @brief Clear unused caches.
+   */
+  void ClearUnusedCache();
+
 private:
   Graphics::Controller*        graphicsController{nullptr};
   std::vector<PipelineCacheL0> level0nodes;
@@ -163,6 +188,8 @@ private:
   // (Since most UI case (like Text and Image) enable blend, and most 3D case disable blend.)
   PipelineCacheQueryInfo mLatestQuery[2];  ///< Latest requested query info. It will be invalidate after query's renderer / geometry / blendingOptions value changed.
   PipelineResult         mLatestResult[2]; ///< Latest used result. It will be invalidate when we call CleanLatestUsedCache() or some cache changed.
+
+  uint32_t mFrameCount{0u};
 };
 
 } // namespace Render
index c6e4b24..486eb2f 100644 (file)
@@ -174,7 +174,7 @@ MemoryPoolObjectAllocator<Renderer>& GetRenderRendererMemoryPool()
   static MemoryPoolObjectAllocator<Renderer> gRenderRendererMemoryPool;
   return gRenderRendererMemoryPool;
 }
-}
+} // namespace
 
 void Renderer::PrepareCommandBuffer()
 {
@@ -245,7 +245,11 @@ void Renderer::Initialize(Graphics::Controller& graphicsController, ProgramCache
   mPipelineCache        = &pipelineCache;
 }
 
-Renderer::~Renderer() = default;
+Renderer::~Renderer()
+{
+  // Reset old pipeline
+  mPipelineCache->ResetPipeline(mPipeline);
+}
 
 void Renderer::operator delete(void* ptr)
 {
@@ -967,9 +971,14 @@ Graphics::Pipeline& Renderer::PrepareGraphicsPipeline(
 
   queryInfo.GenerateHash();
 
+  // Reset old pipeline
+  mPipelineCache->ResetPipeline(mPipeline);
+
   // Find or generate new pipeline.
   auto pipelineResult = mPipelineCache->GetPipeline(queryInfo, true);
 
+  mPipeline = pipelineResult.level2;
+
   // should be never null?
   return *pipelineResult.pipeline;
 }
index 38b0493..202277f 100644 (file)
@@ -57,8 +57,8 @@ namespace Render
 {
 struct ShaderCache;
 class PipelineCache;
+class PipelineCacheL2;
 class UniformBufferManager;
-class PipelineCache;
 class Renderer;
 
 using RendererKey = MemoryPoolKey<Render::Renderer>;
@@ -596,6 +596,7 @@ private:
   std::vector<Graphics::UniformBufferBinding> mUniformBufferBindings{};
 
   Render::PipelineCache* mPipelineCache{nullptr};
+  PipelineCacheL2*       mPipeline{nullptr};
 
   using Hash = std::size_t;