From adb2d00b8a9f18094ed8b5cd160f4f30615ab779 Mon Sep 17 00:00:00 2001 From: Heeyong Song Date: Mon, 8 May 2023 14:59:15 +0900 Subject: [PATCH] Remove old pipeline caches Change-Id: I17290e72f05cecedfce82dc665217c38a99a8470 --- .../utc-Dali-Internal-PipelineCache.cpp | 17 ++++- dali/internal/render/common/render-manager.cpp | 5 +- dali/internal/render/renderers/pipeline-cache.cpp | 81 +++++++++++++++++++++- dali/internal/render/renderers/pipeline-cache.h | 35 ++++++++-- dali/internal/render/renderers/render-renderer.cpp | 13 +++- dali/internal/render/renderers/render-renderer.h | 3 +- 6 files changed, 142 insertions(+), 12 deletions(-) diff --git a/automated-tests/src/dali-internal/utc-Dali-Internal-PipelineCache.cpp b/automated-tests/src/dali-internal/utc-Dali-Internal-PipelineCache.cpp index 9783618..352bce9 100644 --- a/automated-tests/src/dali-internal/utc-Dali-Internal-PipelineCache.cpp +++ b/automated-tests/src/dali-internal/utc-Dali-Internal-PipelineCache.cpp @@ -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 +} diff --git a/dali/internal/render/common/render-manager.cpp b/dali/internal/render/common/render-manager.cpp index 9a895fc..6b58571 100644 --- a/dali/internal/render/common/render-manager.cpp +++ b/dali/internal/render/common/render-manager.cpp @@ -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; } diff --git a/dali/internal/render/renderers/pipeline-cache.cpp b/dali/internal/render/renderers/pipeline-cache.cpp index 4e4633f..92fe22b 100644 --- a/dali/internal/render/renderers/pipeline-cache.cpp +++ b/dali/internal/render/renderers/pipeline-cache.cpp @@ -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 diff --git a/dali/internal/render/renderers/pipeline-cache.h b/dali/internal/render/renderers/pipeline-cache.h index 6bf11d8..659cecc 100644 --- a/dali/internal/render/renderers/pipeline-cache.h +++ b/dali/internal/render/renderers/pipeline-cache.h @@ -40,6 +40,7 @@ class Geometry; struct PipelineCacheL2 { uint32_t hash{}; + uint32_t referenceCount{0u}; Graphics::ColorBlendState colorBlendState; Graphics::UniquePtr 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 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 diff --git a/dali/internal/render/renderers/render-renderer.cpp b/dali/internal/render/renderers/render-renderer.cpp index c6e4b24..486eb2f 100644 --- a/dali/internal/render/renderers/render-renderer.cpp +++ b/dali/internal/render/renderers/render-renderer.cpp @@ -174,7 +174,7 @@ MemoryPoolObjectAllocator& GetRenderRendererMemoryPool() static MemoryPoolObjectAllocator 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; } diff --git a/dali/internal/render/renderers/render-renderer.h b/dali/internal/render/renderers/render-renderer.h index 38b0493..202277f 100644 --- a/dali/internal/render/renderers/render-renderer.h +++ b/dali/internal/render/renderers/render-renderer.h @@ -57,8 +57,8 @@ namespace Render { struct ShaderCache; class PipelineCache; +class PipelineCacheL2; class UniformBufferManager; -class PipelineCache; class Renderer; using RendererKey = MemoryPoolKey; @@ -596,6 +596,7 @@ private: std::vector mUniformBufferBindings{}; Render::PipelineCache* mPipelineCache{nullptr}; + PipelineCacheL2* mPipeline{nullptr}; using Hash = std::size_t; -- 2.7.4