/*
- * 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.
#include "gles-graphics-pipeline-cache.h"
#include <algorithm>
+#include "egl-graphics-controller.h"
#include "gles-graphics-pipeline.h"
#include "gles-graphics-program.h"
+namespace
+{
+constexpr uint32_t CACHE_CLEAN_FLUSH_COUNT = 3600u; // 60fps * 60sec / ~3 flushes per frame
+}
+
namespace Dali::Graphics::GLES
{
/**
}
};
+template<>
+struct CachedObjectDeleter<GLES::Program>
+{
+ CachedObjectDeleter() = default;
+
+ void operator()(GLES::Program* object)
+ {
+ // Program deleter should skip discard queue if controller shutting down
+ if(!object->GetController().IsShuttingDown())
+ {
+ object->DiscardResource();
+ }
+ else
+ {
+ // delete object otherwise
+ delete object;
+ }
+ }
+};
+
/**
* @brief The order of states being stored in the cache and mask
*/
{
COLOR_BLEND_STATE_BIT = 0,
VIEWPORT_STATE_BIT = 1,
- FRAMEBUFFER_STATE_BIT = 2,
- BASE_PIPELINE_STATE_BIT = 3,
- DEPTH_STENCIL_STATE_BIT = 4,
- RASTERIZATION_STATE_BIT = 5,
- VERTEX_INPUT_STATE_BIT = 6,
- INPUT_ASSEMBLY_STATE_BIT = 7,
- MAX_STATE = 8
+ BASE_PIPELINE_STATE_BIT = 2,
+ DEPTH_STENCIL_STATE_BIT = 3,
+ RASTERIZATION_STATE_BIT = 4,
+ VERTEX_INPUT_STATE_BIT = 5,
+ INPUT_ASSEMBLY_STATE_BIT = 6,
+ MAX_STATE = 7
};
/**
using PipelineStateCompateFunctionType = bool(const Graphics::PipelineCreateInfo*,
const Graphics::PipelineCreateInfo*);
-static std::vector<PipelineStateCompateFunctionType*> STATE_COMPARE_FUNC_TABLE{};
+static std::vector<PipelineStateCompateFunctionType*>& GetStateCompareFuncTable()
+{
+ static std::vector<PipelineStateCompateFunctionType*> stateCompareFuncTable{};
+ return stateCompareFuncTable;
+}
/**
* @brief Initialises compare function lookup table
*/
void InitialiseStateCompareLookupTable()
{
- STATE_COMPARE_FUNC_TABLE = {
- [](const auto* lhs, const auto* rhs) -> bool // colorBlendState
- {
- const auto& lcb = *lhs->colorBlendState;
- const auto& rcb = *rhs->colorBlendState;
- return lcb.logicOpEnable == rcb.logicOpEnable &&
- lcb.logicOp == rcb.logicOp &&
- cmpf(lcb.blendConstants[0], rcb.blendConstants[0]) &&
- cmpf(lcb.blendConstants[1], rcb.blendConstants[1]) &&
- cmpf(lcb.blendConstants[2], rcb.blendConstants[2]) &&
- cmpf(lcb.blendConstants[3], rcb.blendConstants[3]) &&
- lcb.blendEnable == rcb.blendEnable &&
- lcb.srcColorBlendFactor == rcb.srcColorBlendFactor &&
- lcb.dstColorBlendFactor == rcb.dstColorBlendFactor &&
- lcb.colorBlendOp == rcb.colorBlendOp &&
- lcb.srcAlphaBlendFactor == rcb.srcAlphaBlendFactor &&
- lcb.dstAlphaBlendFactor == rcb.dstAlphaBlendFactor &&
- lcb.alphaBlendOp == rcb.alphaBlendOp &&
- lcb.colorComponentWriteBits == rcb.colorComponentWriteBits;
- },
- [](const auto* lhs, const auto* rhs) -> bool // viewport state
- {
- const auto& lvp = *lhs->viewportState;
- const auto& rvp = *rhs->viewportState;
- return lvp.viewport == rvp.viewport &&
- lvp.scissor == rvp.scissor &&
- lvp.scissorTestEnable == rvp.scissorTestEnable;
- },
- [](const auto* lhs, const auto* rhs) -> bool // framebufferState
- {
- const auto& lfb = *lhs->framebufferState;
- const auto& rfb = *rhs->framebufferState;
- return lfb.framebuffer == rfb.framebuffer;
- },
- [](const auto* lhs, const auto* rhs) -> bool // basePipeline
- {
- return lhs->basePipeline == rhs->basePipeline;
- },
- [](const auto* lhs, const auto* rhs) -> bool // depthStencilState
- {
- const auto& lds = *lhs->depthStencilState;
- const auto& rds = *rhs->depthStencilState;
- return lds.depthTestEnable == rds.depthTestEnable &&
- lds.depthWriteEnable == rds.depthWriteEnable &&
- lds.depthCompareOp == rds.depthCompareOp &&
- lds.stencilTestEnable == rds.stencilTestEnable &&
- lds.front == rds.front &&
- lds.back == rds.back;
- },
- [](const auto* lhs, const auto* rhs) -> bool // rasterizationState
- {
- const auto& lrs = *lhs->rasterizationState;
- const auto& rrs = *rhs->rasterizationState;
- return lrs.cullMode == rrs.cullMode &&
- lrs.polygonMode == rrs.polygonMode &&
- lrs.frontFace == rrs.frontFace;
- },
- [](const auto* lhs, const auto* rhs) -> bool // vertexInputState
- {
- const auto& lvi = *lhs->vertexInputState;
- const auto& rvi = *rhs->vertexInputState;
- return lvi.bufferBindings.size() == rvi.bufferBindings.size() &&
- lvi.attributes.size() == rvi.attributes.size() &&
- std::equal(lvi.bufferBindings.begin(), lvi.bufferBindings.end(), rvi.bufferBindings.begin(), [](const auto& lhs, const auto& rhs) {
- return operator==(lhs, rhs);
- }) &&
- std::equal(lvi.attributes.begin(), lvi.attributes.end(), rvi.attributes.begin(), [](const auto& lhs, const auto& rhs) {
- return operator==(lhs, rhs);
- });
- },
- [](const auto* lhs, const auto* rhs) -> bool // inputAssemblyState
- {
- const auto& lia = *lhs->inputAssemblyState;
- const auto& ria = *rhs->inputAssemblyState;
- return lia.topology == ria.topology &&
- lia.primitiveRestartEnable == ria.primitiveRestartEnable;
- },
- };
+ GetStateCompareFuncTable().clear();
+ GetStateCompareFuncTable().push_back([](const auto* lhs, const auto* rhs) -> bool // colorBlendState
+ {
+ const auto& lcb = *lhs->colorBlendState;
+ const auto& rcb = *rhs->colorBlendState;
+ return lcb.logicOpEnable == rcb.logicOpEnable &&
+ lcb.logicOp == rcb.logicOp &&
+ cmpf(lcb.blendConstants[0], rcb.blendConstants[0]) &&
+ cmpf(lcb.blendConstants[1], rcb.blendConstants[1]) &&
+ cmpf(lcb.blendConstants[2], rcb.blendConstants[2]) &&
+ cmpf(lcb.blendConstants[3], rcb.blendConstants[3]) &&
+ lcb.blendEnable == rcb.blendEnable &&
+ lcb.srcColorBlendFactor == rcb.srcColorBlendFactor &&
+ lcb.dstColorBlendFactor == rcb.dstColorBlendFactor &&
+ lcb.colorBlendOp == rcb.colorBlendOp &&
+ lcb.srcAlphaBlendFactor == rcb.srcAlphaBlendFactor &&
+ lcb.dstAlphaBlendFactor == rcb.dstAlphaBlendFactor &&
+ lcb.alphaBlendOp == rcb.alphaBlendOp &&
+ lcb.colorComponentWriteBits == rcb.colorComponentWriteBits;
+ });
+ GetStateCompareFuncTable().push_back([](const auto* lhs, const auto* rhs) -> bool // viewport state
+ {
+ const auto& lvp = *lhs->viewportState;
+ const auto& rvp = *rhs->viewportState;
+ return lvp.viewport == rvp.viewport &&
+ lvp.scissor == rvp.scissor &&
+ lvp.scissorTestEnable == rvp.scissorTestEnable;
+ });
+ GetStateCompareFuncTable().push_back([](const auto* lhs, const auto* rhs) -> bool // basePipeline
+ {
+ return lhs->basePipeline == rhs->basePipeline;
+ });
+ GetStateCompareFuncTable().push_back([](const auto* lhs, const auto* rhs) -> bool // depthStencilState
+ {
+ const auto& lds = *lhs->depthStencilState;
+ const auto& rds = *rhs->depthStencilState;
+ return lds.depthTestEnable == rds.depthTestEnable &&
+ lds.depthWriteEnable == rds.depthWriteEnable &&
+ lds.depthCompareOp == rds.depthCompareOp &&
+ lds.stencilTestEnable == rds.stencilTestEnable &&
+ lds.front == rds.front &&
+ lds.back == rds.back;
+ });
+ GetStateCompareFuncTable().push_back([](const auto* lhs, const auto* rhs) -> bool // rasterizationState
+ {
+ const auto& lrs = *lhs->rasterizationState;
+ const auto& rrs = *rhs->rasterizationState;
+ return lrs.cullMode == rrs.cullMode &&
+ lrs.polygonMode == rrs.polygonMode &&
+ lrs.frontFace == rrs.frontFace;
+ });
+ GetStateCompareFuncTable().push_back([](const auto* lhs, const auto* rhs) -> bool // vertexInputState
+ {
+ const auto& lvi = *lhs->vertexInputState;
+ const auto& rvi = *rhs->vertexInputState;
+ return lvi.bufferBindings.size() == rvi.bufferBindings.size() &&
+ lvi.attributes.size() == rvi.attributes.size() &&
+ std::equal(lvi.bufferBindings.begin(), lvi.bufferBindings.end(), rvi.bufferBindings.begin(), [](const auto& lhs, const auto& rhs) {
+ return operator==(lhs, rhs);
+ }) &&
+ std::equal(lvi.attributes.begin(), lvi.attributes.end(), rvi.attributes.begin(), [](const auto& lhs, const auto& rhs) {
+ return operator==(lhs, rhs);
+ });
+ });
+ GetStateCompareFuncTable().push_back([](const auto* lhs, const auto* rhs) -> bool // inputAssemblyState
+ {
+ const auto& lia = *lhs->inputAssemblyState;
+ const auto& ria = *rhs->inputAssemblyState;
+ return lia.topology == ria.topology &&
+ lia.primitiveRestartEnable == ria.primitiveRestartEnable;
+ });
}
/**
uint32_t mask{0u};
mask |= bool(info.colorBlendState) << int(StateLookupIndex::COLOR_BLEND_STATE_BIT);
mask |= bool(info.viewportState) << int(StateLookupIndex::VIEWPORT_STATE_BIT);
- mask |= bool(info.framebufferState) << int(StateLookupIndex::FRAMEBUFFER_STATE_BIT);
mask |= bool(info.basePipeline) << int(StateLookupIndex::BASE_PIPELINE_STATE_BIT);
mask |= bool(info.depthStencilState) << int(StateLookupIndex::DEPTH_STENCIL_STATE_BIT);
mask |= bool(info.rasterizationState) << int(StateLookupIndex::RASTERIZATION_STATE_BIT);
* @brief Constructor
*/
explicit Impl(EglGraphicsController& _controller)
- : controller(_controller)
+ : controller(_controller),
+ pipelineEntriesFlushRequired(false),
+ programEntriesFlushRequired(false),
+ shaderEntriesFlushRequired(false)
{
// Initialise lookup table
InitialiseStateCompareLookupTable();
uint32_t stateBitmask{0u};
};
- EglGraphicsController& controller;
- std::vector<CacheEntry> entries;
-
/**
* @brief Sorted array of shaders used to create program
*/
struct ProgramCacheEntry
{
// sorted array of shaders
- std::vector<const Shader*> shaders;
- UniquePtr<ProgramImpl> program{nullptr};
+ std::vector<const Graphics::Shader*> shaders;
+ UniquePtr<ProgramImpl> program{nullptr};
+ };
+
+ struct ShaderCacheEntry
+ {
+ UniquePtr<ShaderImpl> shaderImpl{nullptr};
};
+ EglGraphicsController& controller;
+ std::vector<CacheEntry> entries;
std::vector<ProgramCacheEntry> programEntries;
+ std::vector<ShaderCacheEntry> shaderEntries;
+
+ bool pipelineEntriesFlushRequired : 1;
+ bool programEntriesFlushRequired : 1;
+ bool shaderEntriesFlushRequired : 1;
};
PipelineCache::PipelineCache(EglGraphicsController& controller)
// Test only set states
if((entry.stateBitmask & (1 << i)))
{
- if(!STATE_COMPARE_FUNC_TABLE[i](&info, &cacheInfo))
+ if(!(GetStateCompareFuncTable()[i](&info, &cacheInfo)))
{
break;
}
}
// assert if no shaders given
- std::vector<const Shader*> shaders;
+ std::vector<const Graphics::Shader*> shaders;
shaders.reserve(info.shaderState->size());
for(auto& state : *info.shaderState)
// create new pipeline
auto program = MakeUnique<GLES::ProgramImpl>(programCreateInfo, mImpl->controller);
- program->Create();
+ program->Create(); // Don't currently handle failure.
cachedProgram = program.get();
return std::move(wrapper);
}
+ShaderImpl* PipelineCache::FindShaderImpl(const ShaderCreateInfo& shaderCreateInfo)
+{
+ if(!mImpl->shaderEntries.empty())
+ {
+ for(auto& item : mImpl->shaderEntries)
+ {
+ auto& itemInfo = item.shaderImpl->GetCreateInfo();
+ if(itemInfo.pipelineStage != shaderCreateInfo.pipelineStage ||
+ itemInfo.shaderlanguage != shaderCreateInfo.shaderlanguage ||
+ itemInfo.sourceMode != shaderCreateInfo.sourceMode ||
+ itemInfo.sourceSize != shaderCreateInfo.sourceSize)
+ {
+ continue;
+ }
+
+ if(memcmp(itemInfo.sourceData, shaderCreateInfo.sourceData, itemInfo.sourceSize) == 0)
+ {
+ return item.shaderImpl.get();
+ }
+ }
+ }
+ return nullptr;
+}
+
+Graphics::UniquePtr<Graphics::Shader> PipelineCache::GetShader(const ShaderCreateInfo& shaderCreateInfo,
+ Graphics::UniquePtr<Graphics::Shader>&& oldShader)
+{
+ ShaderImpl* cachedShader = FindShaderImpl(shaderCreateInfo);
+
+ // Return same pointer if nothing changed
+ if(oldShader && *static_cast<GLES::Shader*>(oldShader.get()) == cachedShader)
+ {
+ return std::move(oldShader);
+ }
+
+ if(!cachedShader)
+ {
+ auto shader = MakeUnique<GLES::ShaderImpl>(shaderCreateInfo, mImpl->controller);
+ cachedShader = shader.get();
+
+ mImpl->shaderEntries.emplace_back();
+ mImpl->shaderEntries.back().shaderImpl = std::move(shader);
+ }
+ auto wrapper = MakeUnique<GLES::Shader, CachedObjectDeleter<GLES::Shader>>(cachedShader);
+ return std::move(wrapper);
+}
+
void PipelineCache::FlushCache()
{
- decltype(mImpl->entries) newEntries;
- newEntries.reserve(mImpl->entries.size());
+ if(mImpl->pipelineEntriesFlushRequired)
+ {
+ decltype(mImpl->entries) newEntries;
+ newEntries.reserve(mImpl->entries.size());
- for(auto& entry : mImpl->entries)
+ for(auto& entry : mImpl->entries)
+ {
+ // Move items which are still in use into the new array
+ if(entry.pipeline->GetRefCount() != 0)
+ {
+ newEntries.emplace_back(std::move(entry));
+ }
+ }
+
+ // Move temporary array in place of stored cache
+ // Unused pipelines will be deleted automatically
+ mImpl->entries = std::move(newEntries);
+
+ mImpl->pipelineEntriesFlushRequired = false;
+ }
+
+ if(mImpl->programEntriesFlushRequired)
+ {
+ // Program cache require similar action.
+ decltype(mImpl->programEntries) newProgramEntries;
+ newProgramEntries.reserve(mImpl->programEntries.size());
+
+ for(auto& entry : mImpl->programEntries)
+ {
+ // Move items which are still in use into the new array
+ if(entry.program->GetRefCount() != 0)
+ {
+ newProgramEntries.emplace_back(std::move(entry));
+ }
+ }
+
+ mImpl->programEntries = std::move(newProgramEntries);
+
+ mImpl->programEntriesFlushRequired = false;
+ }
+
+ if(mImpl->shaderEntriesFlushRequired)
{
- // Move items which are still in use into the new array
- if(entry.pipeline->GetRefCount() != 0)
+ // There is at least 1 unused shader
+ mImpl->shaderEntriesFlushRequired = false;
+ bool deleteRequired{false};
+ for(auto& entry : mImpl->shaderEntries)
{
- newEntries.emplace_back(std::move(entry));
+ if(entry.shaderImpl->GetRefCount() == 0)
+ {
+ mImpl->shaderEntriesFlushRequired = true;
+ auto frameCount = entry.shaderImpl->IncreaseFlushCount();
+ if(frameCount > CACHE_CLEAN_FLUSH_COUNT)
+ {
+ deleteRequired = true;
+ }
+ }
+ }
+ if(deleteRequired)
+ {
+ decltype(mImpl->shaderEntries) newShaderEntries;
+ newShaderEntries.reserve(mImpl->shaderEntries.size());
+ for(auto& entry : mImpl->shaderEntries)
+ {
+ if(entry.shaderImpl->GetRefCount() > 0 ||
+ entry.shaderImpl->GetFlushCount() <= CACHE_CLEAN_FLUSH_COUNT)
+ {
+ newShaderEntries.emplace_back(std::move(entry));
+ }
+ }
+ mImpl->shaderEntries = std::move(newShaderEntries);
}
}
+}
- // Move temporary array in place of stored cache
- // Unused pipelines will be deleted automatically
- mImpl->entries = std::move(newEntries);
+void PipelineCache::MarkPipelineCacheFlushRequired()
+{
+ mImpl->pipelineEntriesFlushRequired = true;
+}
- // TODO: program cache may require similar action. However,
- // since there is always one wrapper for Program object
- // kept in the pipeline, then death of pipeline will result
- // killing the program (if program isn't in use anymore)
+void PipelineCache::MarkProgramCacheFlushRequired()
+{
+ mImpl->programEntriesFlushRequired = true;
+}
+
+void PipelineCache::MarkShaderCacheFlushRequired()
+{
+ mImpl->shaderEntriesFlushRequired = true;
}
-} // namespace Dali::Graphics::GLES
\ No newline at end of file
+} // namespace Dali::Graphics::GLES