From: Heeyong Song Date: Mon, 8 May 2023 05:59:15 +0000 (+0900) Subject: [Tizen] Remove old pipeline caches X-Git-Tag: accepted/tizen/7.0/unified/20230515.132831~2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=dce28fdca0b3e69ffae2d9183fdacc9ca931f7bd;p=platform%2Fcore%2Fuifw%2Fdali-core.git [Tizen] Remove old pipeline caches Change-Id: I17290e72f05cecedfce82dc665217c38a99a8470 --- 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 4f3bb13..34591ea 100644 --- a/automated-tests/src/dali-internal/utc-Dali-Internal-PipelineCache.cpp +++ b/automated-tests/src/dali-internal/utc-Dali-Internal-PipelineCache.cpp @@ -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. @@ -27,28 +27,26 @@ using namespace Dali; - - template -void InvokeNext(Object *obj, Args... args) +void InvokeNext(Object* obj, 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 -Ret InvokeReturnNext(Object *obj, Args... args) +Ret InvokeReturnNext(Object* obj, 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 +} diff --git a/dali/internal/render/common/render-manager.cpp b/dali/internal/render/common/render-manager.cpp index a1755ce..2590551 100644 --- a/dali/internal/render/common/render-manager.cpp +++ b/dali/internal/render/common/render-manager.cpp @@ -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; } diff --git a/dali/internal/render/renderers/pipeline-cache.cpp b/dali/internal/render/renderers/pipeline-cache.cpp index 3f6bcc6..4335022 100644 --- a/dali/internal/render/renderers/pipeline-cache.cpp +++ b/dali/internal/render/renderers/pipeline-cache.cpp @@ -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 diff --git a/dali/internal/render/renderers/pipeline-cache.h b/dali/internal/render/renderers/pipeline-cache.h index c9027d3..ef55951 100644 --- a/dali/internal/render/renderers/pipeline-cache.h +++ b/dali/internal/render/renderers/pipeline-cache.h @@ -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. @@ -18,10 +18,10 @@ */ // INTERNAL INCLUDES -#include -#include #include #include +#include +#include // EXTERNAL INCLUDES #include @@ -40,6 +40,7 @@ class Geometry; struct PipelineCacheL2 { uint32_t hash{}; + uint32_t referenceCount{0u}; Graphics::ColorBlendState colorBlendState; Graphics::UniquePtr 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 level1nodes; @@ -77,17 +87,16 @@ struct PipelineCacheL0 // L0 cache struct PipelineCacheQueryInfo { // Program/Geometry - Renderer *renderer; - Program *program; - Geometry *geometry; + Renderer* renderer; + Program* program; + Geometry* geometry; 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 level0nodes; + + uint32_t mFrameCount{0u}; }; -} -} +} // namespace Render +} // namespace Dali::Internal #endif // DALI_INTERNAL_RENDER_PIPELINE_CACHE_H diff --git a/dali/internal/render/renderers/render-renderer.cpp b/dali/internal/render/renderers/render-renderer.cpp index 58311f2..803b0a3 100644 --- a/dali/internal/render/renderers/render-renderer.cpp +++ b/dali/internal/render/renderers/render-renderer.cpp @@ -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; } diff --git a/dali/internal/render/renderers/render-renderer.h b/dali/internal/render/renderers/render-renderer.h index b99ac4e..1ba37f1 100644 --- a/dali/internal/render/renderers/render-renderer.h +++ b/dali/internal/render/renderers/render-renderer.h @@ -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 mUniformBufferBindings{}; Render::PipelineCache* mPipelineCache{nullptr}; + PipelineCacheL2* mPipeline{nullptr}; using Hash = std::size_t;