#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
{
/**
* @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
*/
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)
// 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)
{
- // Move items which are still in use into the new array
- if(entry.pipeline->GetRefCount() != 0)
+ // Program cache require similar action.
+ decltype(mImpl->programEntries) newProgramEntries;
+ newProgramEntries.reserve(mImpl->programEntries.size());
+
+ for(auto& entry : mImpl->programEntries)
{
- newEntries.emplace_back(std::move(entry));
+ // 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;
}
- // Move temporary array in place of stored cache
- // Unused pipelines will be deleted automatically
- mImpl->entries = std::move(newEntries);
+ if(mImpl->shaderEntriesFlushRequired)
+ {
+ // There is at least 1 unused shader
+ mImpl->shaderEntriesFlushRequired = false;
+ bool deleteRequired{false};
+ for(auto& entry : mImpl->shaderEntries)
+ {
+ 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);
+ }
+ }
+}
+
+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