2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <dali/internal/render/renderers/pipeline-cache.h>
21 #include <dali/internal/render/renderers/render-renderer.h>
22 #include <dali/graphics-api/graphics-types.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/internal/render/common/render-instruction.h>
25 #include <dali/internal/render/renderers/render-vertex-buffer.h>
26 #include <dali/internal/render/shaders/program.h>
28 namespace Dali::Internal::Render
32 // Helper to get the vertex input format
33 Dali::Graphics::VertexInputFormat GetPropertyVertexFormat(Property::Type propertyType)
35 Dali::Graphics::VertexInputFormat type{};
39 case Property::BOOLEAN:
41 type = Dali::Graphics::VertexInputFormat::UNDEFINED; // type = GL_BYTE; @todo new type for this?
44 case Property::INTEGER:
46 type = Dali::Graphics::VertexInputFormat::INTEGER; // (short)
51 type = Dali::Graphics::VertexInputFormat::FLOAT;
54 case Property::VECTOR2:
56 type = Dali::Graphics::VertexInputFormat::FVECTOR2;
59 case Property::VECTOR3:
61 type = Dali::Graphics::VertexInputFormat::FVECTOR3;
64 case Property::VECTOR4:
66 type = Dali::Graphics::VertexInputFormat::FVECTOR4;
71 type = Dali::Graphics::VertexInputFormat::UNDEFINED;
78 constexpr Graphics::CullMode ConvertCullFace(Dali::FaceCullingMode::Type mode)
82 case Dali::FaceCullingMode::NONE:
84 return Graphics::CullMode::NONE;
86 case Dali::FaceCullingMode::FRONT:
88 return Graphics::CullMode::FRONT;
90 case Dali::FaceCullingMode::BACK:
92 return Graphics::CullMode::BACK;
94 case Dali::FaceCullingMode::FRONT_AND_BACK:
96 return Graphics::CullMode::FRONT_AND_BACK;
100 return Graphics::CullMode::NONE;
105 constexpr Graphics::BlendFactor ConvertBlendFactor(BlendFactor::Type blendFactor)
109 case BlendFactor::ZERO:
110 return Graphics::BlendFactor::ZERO;
111 case BlendFactor::ONE:
112 return Graphics::BlendFactor::ONE;
113 case BlendFactor::SRC_COLOR:
114 return Graphics::BlendFactor::SRC_COLOR;
115 case BlendFactor::ONE_MINUS_SRC_COLOR:
116 return Graphics::BlendFactor::ONE_MINUS_SRC_COLOR;
117 case BlendFactor::SRC_ALPHA:
118 return Graphics::BlendFactor::SRC_ALPHA;
119 case BlendFactor::ONE_MINUS_SRC_ALPHA:
120 return Graphics::BlendFactor::ONE_MINUS_SRC_ALPHA;
121 case BlendFactor::DST_ALPHA:
122 return Graphics::BlendFactor::DST_ALPHA;
123 case BlendFactor::ONE_MINUS_DST_ALPHA:
124 return Graphics::BlendFactor::ONE_MINUS_DST_ALPHA;
125 case BlendFactor::DST_COLOR:
126 return Graphics::BlendFactor::DST_COLOR;
127 case BlendFactor::ONE_MINUS_DST_COLOR:
128 return Graphics::BlendFactor::ONE_MINUS_DST_COLOR;
129 case BlendFactor::SRC_ALPHA_SATURATE:
130 return Graphics::BlendFactor::SRC_ALPHA_SATURATE;
131 case BlendFactor::CONSTANT_COLOR:
132 return Graphics::BlendFactor::CONSTANT_COLOR;
133 case BlendFactor::ONE_MINUS_CONSTANT_COLOR:
134 return Graphics::BlendFactor::ONE_MINUS_CONSTANT_COLOR;
135 case BlendFactor::CONSTANT_ALPHA:
136 return Graphics::BlendFactor::CONSTANT_ALPHA;
137 case BlendFactor::ONE_MINUS_CONSTANT_ALPHA:
138 return Graphics::BlendFactor::ONE_MINUS_CONSTANT_ALPHA;
140 return Graphics::BlendFactor();
144 constexpr Graphics::BlendOp ConvertBlendEquation(DevelBlendEquation::Type blendEquation)
146 switch (blendEquation)
148 case DevelBlendEquation::ADD:
149 return Graphics::BlendOp::ADD;
150 case DevelBlendEquation::SUBTRACT:
151 return Graphics::BlendOp::SUBTRACT;
152 case DevelBlendEquation::REVERSE_SUBTRACT:
153 return Graphics::BlendOp::REVERSE_SUBTRACT;
154 case DevelBlendEquation::COLOR:
155 case DevelBlendEquation::COLOR_BURN:
156 case DevelBlendEquation::COLOR_DODGE:
157 case DevelBlendEquation::DARKEN:
158 case DevelBlendEquation::DIFFERENCE:
159 case DevelBlendEquation::EXCLUSION:
160 case DevelBlendEquation::HARD_LIGHT:
161 case DevelBlendEquation::HUE:
162 case DevelBlendEquation::LIGHTEN:
163 case DevelBlendEquation::LUMINOSITY:
164 case DevelBlendEquation::MAX:
165 case DevelBlendEquation::MIN:
166 case DevelBlendEquation::MULTIPLY:
167 case DevelBlendEquation::OVERLAY:
168 case DevelBlendEquation::SATURATION:
169 case DevelBlendEquation::SCREEN:
170 case DevelBlendEquation::SOFT_LIGHT:
171 return Graphics::BlendOp{};
173 return Graphics::BlendOp{};
178 PipelineCacheL0 *PipelineCache::GetPipelineCacheL0(Program *program, Render::Geometry *geometry)
180 auto it = std::find_if(level0nodes.begin(), level0nodes.end(), [program, geometry](
181 PipelineCacheL0 &item)
183 return ((item.program == program && item.geometry == geometry));
186 std::vector<int32_t> attributeLocations;
188 // Add new node to cache
189 if (it == level0nodes.end())
191 uint32_t bindingIndex{0u};
192 auto &reflection = graphicsController->GetProgramReflection(program->GetGraphicsProgram());
194 Graphics::VertexInputState vertexInputState{};
197 for (auto &&vertexBuffer : geometry->GetVertexBuffers())
199 const VertexBuffer::Format &vertexFormat = *vertexBuffer->GetFormat();
201 vertexInputState.bufferBindings.emplace_back(vertexFormat.size, // stride
202 Graphics::VertexInputRate::PER_VERTEX);
204 const uint32_t attributeCount = vertexBuffer->GetAttributeCount();
205 for (uint32_t i = 0; i < attributeCount; ++i)
207 auto attributeName = vertexBuffer->GetAttributeName(i);
208 int32_t pLocation = reflection.GetVertexAttributeLocation(std::string(attributeName.GetStringView()));
211 DALI_LOG_WARNING("Attribute not found in the shader: %s\n", attributeName.GetCString());
213 attributeLocations.emplace_back(pLocation);
215 auto location = static_cast<uint32_t>(attributeLocations[base + i]);
217 vertexInputState.attributes.emplace_back(location,
219 vertexFormat.components[i].offset,
220 GetPropertyVertexFormat(vertexFormat.components[i].type));
222 base += attributeCount;
225 PipelineCacheL0 level0;
226 level0.program = program;
227 level0.geometry = geometry;
228 level0.inputState = vertexInputState;
229 level0nodes.emplace_back(std::move(level0));
230 it = level0nodes.end() - 1;
236 PipelineCacheL1 *PipelineCacheL0::GetPipelineCacheL1(Render::Renderer *renderer, bool usingReflection)
238 // hash must be collision free
240 auto topo = (uint32_t(geometry->GetTopology()) & 0xffu);
241 auto cull = (uint32_t(renderer->GetFaceCullMode()) & 0xffu);
243 static const Graphics::PolygonMode polyTable[] = {
244 Graphics::PolygonMode::POINT,
245 Graphics::PolygonMode::LINE,
246 Graphics::PolygonMode::LINE,
247 Graphics::PolygonMode::LINE,
248 Graphics::PolygonMode::FILL,
249 Graphics::PolygonMode::FILL,
250 Graphics::PolygonMode::FILL
253 auto poly = polyTable[topo];
255 static const FaceCullingMode::Type adjFaceCullingMode[4] =
257 FaceCullingMode::NONE,
258 FaceCullingMode::BACK,
259 FaceCullingMode::FRONT,
260 FaceCullingMode::FRONT_AND_BACK,
263 static const FaceCullingMode::Type normalFaceCullingMode[4] =
265 FaceCullingMode::NONE,
266 FaceCullingMode::FRONT,
267 FaceCullingMode::BACK,
268 FaceCullingMode::FRONT_AND_BACK,
271 static const FaceCullingMode::Type *cullModeTable[2] = {
272 normalFaceCullingMode,
276 // Retrieve cull mode
277 auto cullModeTableIndex = uint32_t(usingReflection) & 1u;
278 cull = cullModeTable[cullModeTableIndex][renderer->GetFaceCullMode()];
280 hash = (topo & 0xffu) | ((cull << 8u) & 0xff00u) | ((uint32_t(poly) << 16u) & 0xff0000u);
282 // If L1 not found by hash, create rasterization state describing pipeline and store it
283 auto it = std::find_if(level1nodes.begin(), level1nodes.end(),
284 [hash](PipelineCacheL1 &item)
286 return item.hashCode == hash;
289 PipelineCacheL1 *retval = nullptr;
290 if (it == level1nodes.end())
292 PipelineCacheL1 item;
293 item.hashCode = hash;
294 item.rs.cullMode = ConvertCullFace(FaceCullingMode::Type(cull));
295 item.rs.frontFace = Graphics::FrontFace::COUNTER_CLOCKWISE;
296 item.rs.polygonMode = poly; // not in use
297 item.ia.topology = geometry->GetTopology();
298 level1nodes.emplace_back(std::move(item));
299 retval = &level1nodes.back();
308 PipelineCacheL2 *PipelineCacheL1::GetPipelineCacheL2(bool blend, bool premul, BlendingOptions &blendingOptions)
313 if (noBlend.pipeline == nullptr)
315 // reset all before returning if pipeline has never been created for that case
317 memset(&noBlend.colorBlendState, 0, sizeof(Graphics::ColorBlendState));
322 auto bitmask = uint32_t(blendingOptions.GetBitmask());
324 // Find by bitmask (L2 entries must be sorted by bitmask)
325 auto it = std::find_if(level2nodes.begin(), level2nodes.end(), [bitmask](PipelineCacheL2 &item)
327 return item.hash == bitmask;
330 // TODO: find better way of blend constants lookup
331 PipelineCacheL2 *retval = nullptr;
332 if (it != level2nodes.end())
334 bool hasBlendColor = blendingOptions.GetBlendColor();
335 while(hasBlendColor && it != level2nodes.end() && (*it).hash == bitmask)
337 Vector4 v( it->colorBlendState.blendConstants );
338 if( v == *blendingOptions.GetBlendColor() )
352 // create new entry and return it with null pipeline
354 l2.pipeline = nullptr;
355 auto &colorBlendState = l2.colorBlendState;
356 colorBlendState.SetBlendEnable(true);
357 Graphics::BlendOp rgbOp = ConvertBlendEquation(blendingOptions.GetBlendEquationRgb());
358 Graphics::BlendOp alphaOp = ConvertBlendEquation(blendingOptions.GetBlendEquationRgb());
359 if (blendingOptions.IsAdvancedBlendEquationApplied() && premul)
361 if (rgbOp != alphaOp)
363 DALI_LOG_ERROR("Advanced Blend Equation MUST be applied by using BlendEquation.\n");
369 .SetSrcColorBlendFactor(ConvertBlendFactor(blendingOptions.GetBlendSrcFactorRgb()))
370 .SetSrcAlphaBlendFactor(ConvertBlendFactor(blendingOptions.GetBlendSrcFactorAlpha()))
371 .SetDstColorBlendFactor(ConvertBlendFactor(blendingOptions.GetBlendDestFactorRgb()))
372 .SetDstAlphaBlendFactor(ConvertBlendFactor(blendingOptions.GetBlendDestFactorAlpha()))
373 .SetColorBlendOp(rgbOp)
374 .SetAlphaBlendOp(alphaOp);
376 // Blend color is optional and rarely used
377 auto *blendColor = const_cast<Vector4 *>(blendingOptions.GetBlendColor());
380 colorBlendState.SetBlendConstants(blendColor->AsFloat());
383 l2.hash = blendingOptions.GetBitmask();
384 level2nodes.emplace_back(std::move(l2));
386 std::sort(level2nodes.begin(), level2nodes.end(), [](PipelineCacheL2 &lhs, PipelineCacheL2 &rhs)
388 return lhs.hash < rhs.hash;
391 // run same function to retrieve retval
392 retval = GetPipelineCacheL2(blend, premul, blendingOptions);
398 PipelineCache::PipelineCache(Graphics::Controller& controller) :
399 graphicsController(&controller)
403 PipelineResult PipelineCache::GetPipeline(const PipelineCacheQueryInfo &queryInfo, bool createNewIfNotFound)
405 auto *level0 = GetPipelineCacheL0(queryInfo.program, queryInfo.geometry);
406 auto *level1 = level0->GetPipelineCacheL1(queryInfo.renderer, queryInfo.cameraUsingReflection);
407 auto *level2 = level1->GetPipelineCacheL2(queryInfo.blendingEnabled, queryInfo.alphaPremultiplied, *queryInfo.blendingOptions);
409 // Create new pipeline at level2 if requested
410 if (level2->pipeline == nullptr && createNewIfNotFound)
412 Graphics::ProgramState programState{};
413 programState.program = &queryInfo.program->GetGraphicsProgram();
414 // Create the pipeline
415 Graphics::PipelineCreateInfo createInfo;
417 .SetInputAssemblyState(&level1->ia)
418 .SetVertexInputState(&level0->inputState)
419 .SetRasterizationState(&level1->rs)
420 .SetColorBlendState(&level2->colorBlendState)
421 .SetProgramState(&programState);
423 // Store a pipeline per renderer per render (renderer can be owned by multiple nodes,
424 // and re-drawn in multiple instructions).
425 // @todo This is only needed because ColorBlend state can change. Fixme!
426 // This is ameliorated by the fact that implementation caches pipelines, and we're only storing
428 level2->pipeline = graphicsController->CreatePipeline(createInfo, nullptr);
431 PipelineResult result{};
433 result.pipeline = level2->pipeline.get();
434 result.level0 = level0;
435 result.level1 = level1;
436 result.level2 = level2;