/*
- * 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 <dali/internal/render/renderers/pipeline-cache.h>
// INTERNAL INCLUDES
-#include <dali/internal/render/renderers/render-renderer.h>
#include <dali/graphics-api/graphics-types.h>
#include <dali/integration-api/debug.h>
#include <dali/internal/render/common/render-instruction.h>
+#include <dali/internal/render/renderers/render-renderer.h>
#include <dali/internal/render/renderers/render-vertex-buffer.h>
#include <dali/internal/render/shaders/program.h>
{
Dali::Graphics::VertexInputFormat type{};
- switch (propertyType)
+ switch(propertyType)
{
case Property::BOOLEAN:
{
constexpr Graphics::CullMode ConvertCullFace(Dali::FaceCullingMode::Type mode)
{
- switch (mode)
+ switch(mode)
{
case Dali::FaceCullingMode::NONE:
{
constexpr Graphics::BlendFactor ConvertBlendFactor(BlendFactor::Type blendFactor)
{
- switch (blendFactor)
+ switch(blendFactor)
{
case BlendFactor::ZERO:
return Graphics::BlendFactor::ZERO;
constexpr Graphics::BlendOp ConvertBlendEquation(DevelBlendEquation::Type blendEquation)
{
- switch (blendEquation)
+ switch(blendEquation)
{
case DevelBlendEquation::ADD:
return Graphics::BlendOp::ADD;
case DevelBlendEquation::REVERSE_SUBTRACT:
return Graphics::BlendOp::REVERSE_SUBTRACT;
case DevelBlendEquation::COLOR:
+ return Graphics::BlendOp::COLOR;
case DevelBlendEquation::COLOR_BURN:
+ return Graphics::BlendOp::COLOR_BURN;
case DevelBlendEquation::COLOR_DODGE:
+ return Graphics::BlendOp::COLOR_DODGE;
case DevelBlendEquation::DARKEN:
+ return Graphics::BlendOp::DARKEN;
case DevelBlendEquation::DIFFERENCE:
+ return Graphics::BlendOp::DIFFERENCE;
case DevelBlendEquation::EXCLUSION:
+ return Graphics::BlendOp::EXCLUSION;
case DevelBlendEquation::HARD_LIGHT:
+ return Graphics::BlendOp::HARD_LIGHT;
case DevelBlendEquation::HUE:
+ return Graphics::BlendOp::HUE;
case DevelBlendEquation::LIGHTEN:
+ return Graphics::BlendOp::LIGHTEN;
case DevelBlendEquation::LUMINOSITY:
+ return Graphics::BlendOp::LUMINOSITY;
case DevelBlendEquation::MAX:
+ return Graphics::BlendOp::MAX;
case DevelBlendEquation::MIN:
+ return Graphics::BlendOp::MIN;
case DevelBlendEquation::MULTIPLY:
+ return Graphics::BlendOp::MULTIPLY;
case DevelBlendEquation::OVERLAY:
+ return Graphics::BlendOp::OVERLAY;
case DevelBlendEquation::SATURATION:
+ return Graphics::BlendOp::SATURATION;
case DevelBlendEquation::SCREEN:
+ return Graphics::BlendOp::SCREEN;
case DevelBlendEquation::SOFT_LIGHT:
- return Graphics::BlendOp{};
+ return Graphics::BlendOp::SOFT_LIGHT;
}
return Graphics::BlendOp{};
}
-}
-
+} // namespace
-PipelineCacheL0 *PipelineCache::GetPipelineCacheL0(Program *program, Render::Geometry *geometry)
+PipelineCacheL0* PipelineCache::GetPipelineCacheL0(std::size_t hash, Program* program, Render::Geometry* geometry)
{
- auto it = std::find_if(level0nodes.begin(), level0nodes.end(), [program, geometry](
- PipelineCacheL0 &item)
- {
- return ((item.program == program && item.geometry == geometry));
+ auto it = std::find_if(level0nodes.begin(), level0nodes.end(), [hash, program, geometry](PipelineCacheL0& item) {
+ return ((item.hash == hash && item.program == program && item.geometry == geometry));
});
- std::vector<int32_t> attributeLocations;
-
// Add new node to cache
- if (it == level0nodes.end())
+ if(it == level0nodes.end())
{
uint32_t bindingIndex{0u};
- auto &reflection = graphicsController->GetProgramReflection(program->GetGraphicsProgram());
+ auto& reflection = graphicsController->GetProgramReflection(program->GetGraphicsProgram());
Graphics::VertexInputState vertexInputState{};
uint32_t base = 0;
- for (auto &&vertexBuffer : geometry->GetVertexBuffers())
+ for(auto&& vertexBuffer : geometry->GetVertexBuffers())
{
- const VertexBuffer::Format &vertexFormat = *vertexBuffer->GetFormat();
+ const VertexBuffer::Format& vertexFormat = *vertexBuffer->GetFormat();
vertexInputState.bufferBindings.emplace_back(vertexFormat.size, // stride
Graphics::VertexInputRate::PER_VERTEX);
- const uint32_t attributeCount = vertexBuffer->GetAttributeCount();
- for (uint32_t i = 0; i < attributeCount; ++i)
+ const uint32_t attributeCount = vertexBuffer->GetAttributeCount();
+ uint32_t lastBoundAttributeIndex = 0;
+ for(uint32_t i = 0; i < attributeCount; ++i)
{
auto attributeName = vertexBuffer->GetAttributeName(i);
int32_t pLocation = reflection.GetVertexAttributeLocation(std::string(attributeName.GetStringView()));
- if (-1 == pLocation)
+ if(-1 != pLocation)
+ {
+ auto location = static_cast<uint32_t>(pLocation);
+ vertexInputState.attributes.emplace_back(location,
+ bindingIndex,
+ vertexFormat.components[i].offset,
+ GetPropertyVertexFormat(vertexFormat.components[i].type));
+ ++lastBoundAttributeIndex;
+ }
+ else
{
DALI_LOG_WARNING("Attribute not found in the shader: %s\n", attributeName.GetCString());
+ // Don't bind unused attributes.
}
- attributeLocations.emplace_back(pLocation);
-
- auto location = static_cast<uint32_t>(attributeLocations[base + i]);
-
- vertexInputState.attributes.emplace_back(location,
- bindingIndex,
- vertexFormat.components[i].offset,
- GetPropertyVertexFormat(vertexFormat.components[i].type));
}
- base += attributeCount;
+ base += lastBoundAttributeIndex;
++bindingIndex;
}
PipelineCacheL0 level0;
+ level0.hash = hash;
level0.program = program;
level0.geometry = geometry;
level0.inputState = vertexInputState;
return &*it;
}
-PipelineCacheL1 *PipelineCacheL0::GetPipelineCacheL1(Render::Renderer *renderer, bool usingReflection)
+PipelineCacheL1* PipelineCacheL0::GetPipelineCacheL1(Render::Renderer* renderer, bool usingReflection)
{
// hash must be collision free
uint32_t hash = 0;
Graphics::PolygonMode::LINE,
Graphics::PolygonMode::FILL,
Graphics::PolygonMode::FILL,
- Graphics::PolygonMode::FILL
- };
+ Graphics::PolygonMode::FILL};
auto poly = polyTable[topo];
static const FaceCullingMode::Type adjFaceCullingMode[4] =
- {
- FaceCullingMode::NONE,
- FaceCullingMode::BACK,
- FaceCullingMode::FRONT,
- FaceCullingMode::FRONT_AND_BACK,
- };
+ {
+ FaceCullingMode::NONE,
+ FaceCullingMode::BACK,
+ FaceCullingMode::FRONT,
+ FaceCullingMode::FRONT_AND_BACK,
+ };
static const FaceCullingMode::Type normalFaceCullingMode[4] =
- {
- FaceCullingMode::NONE,
- FaceCullingMode::FRONT,
- FaceCullingMode::BACK,
- FaceCullingMode::FRONT_AND_BACK,
- };
-
- static const FaceCullingMode::Type *cullModeTable[2] = {
+ {
+ FaceCullingMode::NONE,
+ FaceCullingMode::FRONT,
+ FaceCullingMode::BACK,
+ FaceCullingMode::FRONT_AND_BACK,
+ };
+
+ static const FaceCullingMode::Type* cullModeTable[2] = {
normalFaceCullingMode,
- adjFaceCullingMode
- };
+ adjFaceCullingMode};
// Retrieve cull mode
auto cullModeTableIndex = uint32_t(usingReflection) & 1u;
- cull = cullModeTable[cullModeTableIndex][renderer->GetFaceCullMode()];
+ cull = cullModeTable[cullModeTableIndex][renderer->GetFaceCullMode()];
hash = (topo & 0xffu) | ((cull << 8u) & 0xff00u) | ((uint32_t(poly) << 16u) & 0xff0000u);
// If L1 not found by hash, create rasterization state describing pipeline and store it
- auto it = std::find_if(level1nodes.begin(), level1nodes.end(),
- [hash](PipelineCacheL1 &item)
- {
- return item.hashCode == hash;
- });
-
- PipelineCacheL1 *retval = nullptr;
- if (it == level1nodes.end())
+ auto it = std::find_if(level1nodes.begin(), level1nodes.end(), [hash](PipelineCacheL1& item) {
+ return item.hashCode == hash;
+ });
+
+ PipelineCacheL1* retval = nullptr;
+ if(it == level1nodes.end())
{
PipelineCacheL1 item;
item.hashCode = hash;
return retval;
}
-PipelineCacheL2 *PipelineCacheL1::GetPipelineCacheL2(bool blend, bool premul, BlendingOptions &blendingOptions)
+PipelineCacheL2* PipelineCacheL1::GetPipelineCacheL2(bool blend, bool premul, BlendingOptions& blendingOptions)
{
// early out
- if (!blend)
+ if(!blend)
{
- if (noBlend.pipeline == nullptr)
+ if(noBlend.pipeline == nullptr)
{
// reset all before returning if pipeline has never been created for that case
noBlend.hash = 0;
auto bitmask = uint32_t(blendingOptions.GetBitmask());
// Find by bitmask (L2 entries must be sorted by bitmask)
- auto it = std::find_if(level2nodes.begin(), level2nodes.end(), [bitmask](PipelineCacheL2 &item)
- {
+ auto it = std::find_if(level2nodes.begin(), level2nodes.end(), [bitmask](PipelineCacheL2& item) {
return item.hash == bitmask;
});
// TODO: find better way of blend constants lookup
- PipelineCacheL2 *retval = nullptr;
- if (it != level2nodes.end())
+ PipelineCacheL2* retval = nullptr;
+ if(it != level2nodes.end())
{
bool hasBlendColor = blendingOptions.GetBlendColor();
while(hasBlendColor && it != level2nodes.end() && (*it).hash == bitmask)
{
- Vector4 v( it->colorBlendState.blendConstants );
- if( v == *blendingOptions.GetBlendColor() )
+ Vector4 v(it->colorBlendState.blendConstants);
+ if(v == *blendingOptions.GetBlendColor())
{
retval = &*it;
}
}
}
- if (!retval)
+ if(!retval)
{
// create new entry and return it with null pipeline
PipelineCacheL2 l2;
- l2.pipeline = nullptr;
- auto &colorBlendState = l2.colorBlendState;
+ l2.pipeline = nullptr;
+ auto& colorBlendState = l2.colorBlendState;
colorBlendState.SetBlendEnable(true);
Graphics::BlendOp rgbOp = ConvertBlendEquation(blendingOptions.GetBlendEquationRgb());
- Graphics::BlendOp alphaOp = ConvertBlendEquation(blendingOptions.GetBlendEquationRgb());
- if (blendingOptions.IsAdvancedBlendEquationApplied() && premul)
+ Graphics::BlendOp alphaOp = ConvertBlendEquation(blendingOptions.GetBlendEquationAlpha());
+ if(blendingOptions.IsAdvancedBlendEquationApplied() && premul)
{
- if (rgbOp != alphaOp)
+ if(rgbOp != alphaOp)
{
DALI_LOG_ERROR("Advanced Blend Equation MUST be applied by using BlendEquation.\n");
alphaOp = rgbOp;
.SetAlphaBlendOp(alphaOp);
// Blend color is optional and rarely used
- auto *blendColor = const_cast<Vector4 *>(blendingOptions.GetBlendColor());
- if (blendColor)
+ auto* blendColor = const_cast<Vector4*>(blendingOptions.GetBlendColor());
+ if(blendColor)
{
colorBlendState.SetBlendConstants(blendColor->AsFloat());
}
l2.hash = blendingOptions.GetBitmask();
level2nodes.emplace_back(std::move(l2));
- std::sort(level2nodes.begin(), level2nodes.end(), [](PipelineCacheL2 &lhs, PipelineCacheL2 &rhs)
- {
+ std::sort(level2nodes.begin(), level2nodes.end(), [](PipelineCacheL2& lhs, PipelineCacheL2& rhs) {
return lhs.hash < rhs.hash;
});
return retval;
}
-PipelineCache::PipelineCache(Graphics::Controller& controller) :
-graphicsController(&controller)
+void PipelineCacheQueryInfo::GenerateHash()
+{
+ // Lightweight hash value generation.
+ hash = (reinterpret_cast<std::size_t>(program) >> Dali::Log<sizeof(decltype(*program))>::value) ^
+ (reinterpret_cast<std::size_t>(geometry) >> Dali::Log<sizeof(decltype(*geometry))>::value) ^
+ ((blendingEnabled ? 1u : 0u) << 0u) ^
+ ((alphaPremultiplied ? 1u : 0u) << 1u) ^
+ (static_cast<std::size_t>(geometry->GetTopology()) << 2u) ^
+ (static_cast<std::size_t>(renderer->GetFaceCullMode()) << 5u) ^
+ ((cameraUsingReflection ? 1u : 0u) << 8u) ^
+ (blendingEnabled ? static_cast<std::size_t>(blendingOptions->GetBitmask()) : 0xDA11u);
+}
+
+bool PipelineCacheQueryInfo::Equal(const PipelineCacheQueryInfo& lhs, const PipelineCacheQueryInfo& rhs) noexcept
{
+ // Naive equal check.
+ const bool ret = (lhs.hash == rhs.hash) && // Check hash value first
+ (lhs.program == rhs.program) &&
+ (lhs.geometry == rhs.geometry) &&
+ (lhs.blendingEnabled == rhs.blendingEnabled) &&
+ (lhs.alphaPremultiplied == rhs.alphaPremultiplied) &&
+ (lhs.geometry->GetTopology() == rhs.geometry->GetTopology()) &&
+ (lhs.renderer->GetFaceCullMode() == rhs.renderer->GetFaceCullMode()) &&
+ (lhs.cameraUsingReflection == rhs.cameraUsingReflection) &&
+ (!lhs.blendingEnabled ||
+ (lhs.blendingOptions->GetBitmask() == rhs.blendingOptions->GetBitmask() &&
+ ((lhs.blendingOptions->GetBlendColor() == nullptr && rhs.blendingOptions->GetBlendColor() == nullptr) ||
+ (lhs.blendingOptions->GetBlendColor() &&
+ rhs.blendingOptions->GetBlendColor() &&
+ (*lhs.blendingOptions->GetBlendColor() == *rhs.blendingOptions->GetBlendColor())))));
+
+ return ret;
}
-PipelineResult PipelineCache::GetPipeline(const PipelineCacheQueryInfo &queryInfo, bool createNewIfNotFound)
+PipelineCache::PipelineCache(Graphics::Controller& controller)
+: graphicsController(&controller)
{
- auto *level0 = GetPipelineCacheL0(queryInfo.program, queryInfo.geometry);
- auto *level1 = level0->GetPipelineCacheL1(queryInfo.renderer, queryInfo.cameraUsingReflection);
- auto *level2 = level1->GetPipelineCacheL2(queryInfo.blendingEnabled, queryInfo.alphaPremultiplied, *queryInfo.blendingOptions);
+ // Clean up cache first
+ CleanLatestUsedCache();
+}
+
+PipelineResult PipelineCache::GetPipeline(const PipelineCacheQueryInfo& queryInfo, bool createNewIfNotFound)
+{
+ // Seperate branch whether query use blending or not.
+ const int latestUsedCacheIndex = queryInfo.blendingEnabled ? 0 : 1;
+
+ // If we can reuse latest bound pipeline, Fast return.
+ if(ReuseLatestBoundPipeline(latestUsedCacheIndex, queryInfo))
+ {
+ return mLatestResult[latestUsedCacheIndex];
+ }
+
+ auto* level0 = GetPipelineCacheL0(queryInfo.hash, queryInfo.program, queryInfo.geometry);
+ auto* level1 = level0->GetPipelineCacheL1(queryInfo.renderer, queryInfo.cameraUsingReflection);
+ auto* level2 = level1->GetPipelineCacheL2(queryInfo.blendingEnabled, queryInfo.alphaPremultiplied, *queryInfo.blendingOptions);
// Create new pipeline at level2 if requested
- if (level2->pipeline == nullptr && createNewIfNotFound)
+ if(level2->pipeline == nullptr && createNewIfNotFound)
{
Graphics::ProgramState programState{};
programState.program = &queryInfo.program->GetGraphicsProgram();
// Store a pipeline per renderer per render (renderer can be owned by multiple nodes,
// and re-drawn in multiple instructions).
- // @todo This is only needed because ColorBlend state can change. Fixme!
- // This is ameliorated by the fact that implementation caches pipelines, and we're only storing
- // handles.
level2->pipeline = graphicsController->CreatePipeline(createInfo, nullptr);
}
result.level1 = level1;
result.level2 = level2;
+ // Copy query and result
+ mLatestQuery[latestUsedCacheIndex] = queryInfo;
+ mLatestResult[latestUsedCacheIndex] = result;
+
return result;
}
-}
\ No newline at end of file
+bool PipelineCache::ReuseLatestBoundPipeline(const int latestUsedCacheIndex, const PipelineCacheQueryInfo& queryInfo) const
+{
+ return mLatestResult[latestUsedCacheIndex].pipeline != nullptr && PipelineCacheQueryInfo::Equal(queryInfo, mLatestQuery[latestUsedCacheIndex]);
+}
+
+} // namespace Dali::Internal::Render