/*
- * 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(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<class Ret, class Object, class... Args>
-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...);
}
{
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)
{
// 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();
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);
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);
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);
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
+}
/*
- * 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.
~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)
}
}
+ // Reset pipeline cache before rendering
+ mImpl->pipelineCache->PreRender();
+
mImpl->commandBufferSubmitted = false;
}
/*
- * 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.
{
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)
{
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
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)
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)
{
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
#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>
struct PipelineCacheL2
{
uint32_t hash{};
+ uint32_t referenceCount{0u};
Graphics::ColorBlendState colorBlendState;
Graphics::UniquePtr<Graphics::Pipeline> pipeline;
};
*/
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{};
*/
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;
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;
};
/**
struct PipelineResult
{
Graphics::Pipeline* pipeline;
-
- PipelineCacheL0* level0;
- PipelineCacheL1* level1;
- PipelineCacheL2* level2;
+ PipelineCacheL2* level2;
};
/**
/**
* 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
/*
- * 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.
mPipelineCache = &pipelineCache;
}
-Renderer::~Renderer() = default;
+Renderer::~Renderer()
+{
+ // Reset old pipeline
+ mPipelineCache->ResetPipeline(mPipeline);
+}
void Renderer::SetGeometry(Render::Geometry* geometry)
{
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;
}
#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.
{
struct ShaderCache;
class PipelineCache;
+class PipelineCacheL2;
class UniformBufferManager;
-class PipelineCache;
/**
* Renderers are used to render meshes
std::vector<Graphics::UniformBufferBinding> mUniformBufferBindings{};
Render::PipelineCache* mPipelineCache{nullptr};
+ PipelineCacheL2* mPipeline{nullptr};
using Hash = std::size_t;