[Tizen] Remove old pipeline caches 67/292667/2
authorHeeyong Song <heeyong.song@samsung.com>
Mon, 8 May 2023 05:59:15 +0000 (14:59 +0900)
committerHeeyong Song <heeyong.song@samsung.com>
Thu, 11 May 2023 06:14:47 +0000 (15:14 +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 4f3bb13..34591ea 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.
 
 using namespace Dali;
 
-
-
 template<class Object, class... Args>
-void InvokeNext(Object *obj, Args... args)
+void InvokeNext(Objectobj, Args... args)
 {
-  auto addr = __builtin_return_address(0);
+  auto    addr = __builtin_return_address(0);
   Dl_info info;
   dladdr(addr, &info);
   auto func = dlsym(RTLD_NEXT, info.dli_sname);
-  typedef void(*FuncPtr)(void*, Args...);
+  typedef void (*FuncPtr)(void*, Args...);
   auto memb = FuncPtr(func);
   memb(obj, args...);
 }
 
 template<class Ret, class Object, class... Args>
-Ret InvokeReturnNext(Object *obj, Args... args)
+Ret InvokeReturnNext(Objectobj, Args... args)
 {
-  auto addr = __builtin_return_address(0);
+  auto    addr = __builtin_return_address(0);
   Dl_info info;
   dladdr(addr, &info);
   auto func = dlsym(RTLD_NEXT, info.dli_sname);
-  typedef Ret(*FuncPtr)(void*, Args...);
+  typedef Ret (*FuncPtr)(void*, Args...);
   auto memb = FuncPtr(func);
   return memb(obj, args...);
 }
@@ -60,18 +58,17 @@ namespace Internal
 {
 namespace Render
 {
-
 // Store internal PipelineCache as singleton
 
 PipelineCache::PipelineCache(Dali::Graphics::Controller& controller)
 {
   gPipelineCache = this;
-  InvokeNext( this, &controller );
+  InvokeNext(this, &controller);
 }
 
-}
-}
-}
+} // namespace Render
+} // namespace Internal
+} // namespace Dali
 
 int UtcDaliCorePipelineCacheTest(void)
 {
@@ -82,10 +79,10 @@ int UtcDaliCorePipelineCacheTest(void)
 
   // PipelineCache* cache = PipelineCache::GetPipelineCacheWithController( &application.GetGraphicsController() );
   // Pipeline cache must be initialized
-  DALI_TEST_EQUALS( gPipelineCache != 0, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(gPipelineCache != 0, true, TEST_LOCATION);
 
   // Test size of level0 nodes (should be 0, nothing added yet)
-  DALI_TEST_EQUALS( (gPipelineCache->level0nodes.size() == 0), true, TEST_LOCATION);
+  DALI_TEST_EQUALS((gPipelineCache->level0nodes.size() == 0), true, TEST_LOCATION);
 
   // Create something to render
   Geometry   geometry   = CreateQuadGeometry();
@@ -107,11 +104,11 @@ int UtcDaliCorePipelineCacheTest(void)
   application.Render();
 
   // 1 pipeline should be added
-  DALI_TEST_EQUALS( (gPipelineCache->level0nodes.size() == 1), true, TEST_LOCATION);
+  DALI_TEST_EQUALS((gPipelineCache->level0nodes.size() == 1), true, TEST_LOCATION);
 
   // Add another actor, new pipeline will be created
-  Shader shader1    = Shader::New("newVertexSrc", "newFragmentSrc");
-  Actor actor1 = Actor::New();
+  Shader   shader1   = Shader::New("newVertexSrc", "newFragmentSrc");
+  Actor    actor1    = Actor::New();
   Renderer renderer1 = Renderer::New(geometry, shader1);
   renderer1.SetProperty(Dali::Renderer::Property::BLEND_MODE, Dali::BlendMode::ON);
   actor1.AddRenderer(renderer1);
@@ -121,11 +118,11 @@ int UtcDaliCorePipelineCacheTest(void)
   application.SendNotification();
   application.Render();
 
-  DALI_TEST_EQUALS( (gPipelineCache->level0nodes.size() == 2), true, TEST_LOCATION);
+  DALI_TEST_EQUALS((gPipelineCache->level0nodes.size() == 2), true, TEST_LOCATION);
 
   // Now add 3rd actor reusing first pipeline
   {
-    Actor actor2 = Actor::New();
+    Actor    actor2    = Actor::New();
     Renderer renderer2 = Renderer::New(geometry, shader);
     renderer2.SetProperty(Dali::Renderer::Property::BLEND_MODE, Dali::BlendMode::ON);
     actor2.AddRenderer(renderer);
@@ -136,11 +133,11 @@ int UtcDaliCorePipelineCacheTest(void)
   application.Render();
 
   // Number of pipelines shouldn't change
-  DALI_TEST_EQUALS( (gPipelineCache->level0nodes.size() == 2), true, TEST_LOCATION);
+  DALI_TEST_EQUALS((gPipelineCache->level0nodes.size() == 2), true, TEST_LOCATION);
 
   // Test final 'noBlend' path on first pipeline
   {
-    Actor actor3 = Actor::New();
+    Actor    actor3    = Actor::New();
     Renderer renderer3 = Renderer::New(geometry, shader);
     renderer3.SetProperty(Dali::Renderer::Property::BLEND_MODE, Dali::BlendMode::OFF);
     actor3.AddRenderer(renderer3);
@@ -151,7 +148,20 @@ int UtcDaliCorePipelineCacheTest(void)
   application.Render();
 
   // Test whether noBlend pipeline is set in cache
-  DALI_TEST_EQUALS( gPipelineCache->level0nodes[0].level1nodes[0].noBlend.pipeline != nullptr, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(gPipelineCache->level0nodes[0].level1nodes[0].noBlend.pipeline != nullptr, true, 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(), 1, TEST_LOCATION);
 
   END_TEST;
-}
\ No newline at end of file
+}
index a1755ce..2590551 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 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.
@@ -133,6 +133,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)
@@ -478,6 +479,9 @@ void RenderManager::PreRender(Integration::RenderStatus& status, bool forceClear
     }
   }
 
+  // Reset pipeline cache before rendering
+  mImpl->pipelineCache->PreRender();
+
   mImpl->commandBufferSubmitted = false;
 }
 
index 3f6bcc6..4335022 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.
@@ -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)
 {
@@ -315,6 +317,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
@@ -364,7 +381,7 @@ PipelineCacheL2* PipelineCacheL1::GetPipelineCacheL2(bool blend, bool premul, Bl
     auto& colorBlendState = l2.colorBlendState;
     colorBlendState.SetBlendEnable(true);
     Graphics::BlendOp rgbOp   = ConvertBlendEquation(blendingOptions.GetBlendEquationRgb());
-    Graphics::BlendOp alphaOp = ConvertBlendEquation(blendingOptions.GetBlendEquationRgb());
+    Graphics::BlendOp alphaOp = ConvertBlendEquation(blendingOptions.GetBlendEquationAlpha());
     if(blendingOptions.IsAdvancedBlendEquationApplied() && premul)
     {
       if(rgbOp != alphaOp)
@@ -403,6 +420,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();
+}
+
 PipelineCache::PipelineCache(Graphics::Controller& controller)
 : graphicsController(&controller)
 {
@@ -436,11 +475,46 @@ PipelineResult PipelineCache::GetPipeline(const PipelineCacheQueryInfo& queryInf
   PipelineResult result{};
 
   result.pipeline = level2->pipeline.get();
-  result.level0   = level0;
-  result.level1   = level1;
   result.level2   = level2;
 
+  level2->referenceCount++;
+
   return result;
 }
 
+void PipelineCache::PreRender()
+{
+  // 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 c9027d3..ef55951 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_RENDER_PIPELINE_CACHE_H
 
 /*
- * 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.
  */
 
 // INTERNAL INCLUDES
-#include <dali/internal/common/blending-options.h>
-#include <dali/graphics-api/graphics-types.h>
 #include <dali/graphics-api/graphics-controller.h>
 #include <dali/graphics-api/graphics-pipeline.h>
+#include <dali/graphics-api/graphics-types.h>
+#include <dali/internal/common/blending-options.h>
 
 // EXTERNAL INCLUDES
 #include <vector>
@@ -40,6 +40,7 @@ class Geometry;
 struct PipelineCacheL2
 {
   uint32_t                                hash{};
+  uint32_t                                referenceCount{0u};
   Graphics::ColorBlendState               colorBlendState;
   Graphics::UniquePtr<Graphics::Pipeline> pipeline;
 };
@@ -49,8 +50,12 @@ struct PipelineCacheL2
  */
 struct PipelineCacheL1
 {
+  PipelineCacheL2* GetPipelineCacheL2(bool blend, bool premul, BlendingOptions& blendingOptions);
 
-  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{};
@@ -65,10 +70,15 @@ struct PipelineCacheL1
  */
 struct PipelineCacheL0 // L0 cache
 {
-  PipelineCacheL1 *GetPipelineCacheL1(Render::Renderer *renderer, bool usingReflection);
+  PipelineCacheL1* GetPipelineCacheL1(Render::Renderer* renderer, bool usingReflection);
 
-  Geometry                   *geometry{};
-  Program                    *program{};
+  /**
+   * @brief Clear unused caches.
+   */
+  void ClearUnusedCache();
+
+  Geometry*                  geometry{};
+  Program*                   program{};
   Graphics::VertexInputState inputState;
 
   std::vector<PipelineCacheL1> level1nodes;
@@ -77,17 +87,16 @@ struct PipelineCacheL0 // L0 cache
 struct PipelineCacheQueryInfo
 {
   // Program/Geometry
-  Renderer *renderer;
-  Program  *program;
-  Geometry *geometry;
+  Rendererrenderer;
+  Program*  program;
+  Geometrygeometry;
 
   bool cameraUsingReflection;
 
   // Blending
-  bool blendingEnabled;
-  bool alphaPremultiplied;
-  BlendingOptions *blendingOptions;
-
+  bool             blendingEnabled;
+  bool             alphaPremultiplied;
+  BlendingOptions* blendingOptions;
 };
 
 /**
@@ -96,10 +105,7 @@ struct PipelineCacheQueryInfo
 struct PipelineResult
 {
   Graphics::Pipeline* pipeline;
-
-  PipelineCacheL0* level0;
-  PipelineCacheL1* level1;
-  PipelineCacheL2* level2;
+  PipelineCacheL2*    level2;
 };
 
 /**
@@ -117,22 +123,40 @@ public:
   /**
    * Retrieves next cache level
    */
-  PipelineCacheL0* GetPipelineCacheL0( Program *program, Render::Geometry *geometry);
+  PipelineCacheL0* GetPipelineCacheL0(Program* program, Render::Geometry* geometry);
 
   /**
    * Retrieves pipeline matching queryInfo struct
    *
    * May retrieve existing pipeline or create one or return nullptr.
    */
-  PipelineResult GetPipeline( const PipelineCacheQueryInfo& queryInfo, bool createNewIfNotFound );
+  PipelineResult GetPipeline(const PipelineCacheQueryInfo& queryInfo, bool createNewIfNotFound);
+
+  /**
+   * @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 unused caches.
+   */
+  void ClearUnusedCache();
 
-  Graphics::Controller* graphicsController{nullptr};
+private:
+  Graphics::Controller*        graphicsController{nullptr};
   std::vector<PipelineCacheL0> level0nodes;
+
+  uint32_t mFrameCount{0u};
 };
 
-}
-}
+} // namespace Render
+} // namespace Dali::Internal
 
 #endif // DALI_INTERNAL_RENDER_PIPELINE_CACHE_H
index 58311f2..803b0a3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 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.
@@ -206,7 +206,11 @@ void Renderer::Initialize(Graphics::Controller& graphicsController, ProgramCache
   mPipelineCache        = &pipelineCache;
 }
 
-Renderer::~Renderer() = default;
+Renderer::~Renderer()
+{
+  // Reset old pipeline
+  mPipelineCache->ResetPipeline(mPipeline);
+}
 
 void Renderer::SetGeometry(Render::Geometry* geometry)
 {
@@ -948,8 +952,14 @@ Graphics::Pipeline& Renderer::PrepareGraphicsPipeline(
   queryInfo.alphaPremultiplied    = mPremultipliedAlphaEnabled;
   queryInfo.cameraUsingReflection = instruction.GetCamera()->GetReflectionUsed();
 
+  // 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 b99ac4e..1ba37f1 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_RENDER_RENDERER_H
 
 /*
- * Copyright (c) 2022 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.
@@ -57,8 +57,8 @@ namespace Render
 {
 struct ShaderCache;
 class PipelineCache;
+class PipelineCacheL2;
 class UniformBufferManager;
-class PipelineCache;
 
 /**
  * Renderers are used to render meshes
@@ -547,6 +547,7 @@ private:
   std::vector<Graphics::UniformBufferBinding> mUniformBufferBindings{};
 
   Render::PipelineCache* mPipelineCache{nullptr};
+  PipelineCacheL2*       mPipeline{nullptr};
 
   using Hash = std::size_t;