Prevent unused attributes from binding bad location
[platform/core/uifw/dali-core.git] / dali / internal / render / renderers / pipeline-cache.cpp
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 // CLASS HEADER
18 #include <dali/internal/render/renderers/pipeline-cache.h>
19
20 // INTERNAL INCLUDES
21 #include <dali/graphics-api/graphics-types.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/internal/render/common/render-instruction.h>
24 #include <dali/internal/render/renderers/render-renderer.h>
25 #include <dali/internal/render/renderers/render-vertex-buffer.h>
26 #include <dali/internal/render/shaders/program.h>
27
28 namespace Dali::Internal::Render
29 {
30 namespace
31 {
32 // Helper to get the vertex input format
33 Dali::Graphics::VertexInputFormat GetPropertyVertexFormat(Property::Type propertyType)
34 {
35   Dali::Graphics::VertexInputFormat type{};
36
37   switch(propertyType)
38   {
39     case Property::BOOLEAN:
40     {
41       type = Dali::Graphics::VertexInputFormat::UNDEFINED; // type = GL_BYTE; @todo new type for this?
42       break;
43     }
44     case Property::INTEGER:
45     {
46       type = Dali::Graphics::VertexInputFormat::INTEGER; // (short)
47       break;
48     }
49     case Property::FLOAT:
50     {
51       type = Dali::Graphics::VertexInputFormat::FLOAT;
52       break;
53     }
54     case Property::VECTOR2:
55     {
56       type = Dali::Graphics::VertexInputFormat::FVECTOR2;
57       break;
58     }
59     case Property::VECTOR3:
60     {
61       type = Dali::Graphics::VertexInputFormat::FVECTOR3;
62       break;
63     }
64     case Property::VECTOR4:
65     {
66       type = Dali::Graphics::VertexInputFormat::FVECTOR4;
67       break;
68     }
69     default:
70     {
71       type = Dali::Graphics::VertexInputFormat::UNDEFINED;
72     }
73   }
74
75   return type;
76 }
77
78 constexpr Graphics::CullMode ConvertCullFace(Dali::FaceCullingMode::Type mode)
79 {
80   switch(mode)
81   {
82     case Dali::FaceCullingMode::NONE:
83     {
84       return Graphics::CullMode::NONE;
85     }
86     case Dali::FaceCullingMode::FRONT:
87     {
88       return Graphics::CullMode::FRONT;
89     }
90     case Dali::FaceCullingMode::BACK:
91     {
92       return Graphics::CullMode::BACK;
93     }
94     case Dali::FaceCullingMode::FRONT_AND_BACK:
95     {
96       return Graphics::CullMode::FRONT_AND_BACK;
97     }
98     default:
99     {
100       return Graphics::CullMode::NONE;
101     }
102   }
103 }
104
105 constexpr Graphics::BlendFactor ConvertBlendFactor(BlendFactor::Type blendFactor)
106 {
107   switch(blendFactor)
108   {
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;
139     default:
140       return Graphics::BlendFactor();
141   }
142 }
143
144 constexpr Graphics::BlendOp ConvertBlendEquation(DevelBlendEquation::Type blendEquation)
145 {
146   switch(blendEquation)
147   {
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{};
172   }
173   return Graphics::BlendOp{};
174 }
175 } // namespace
176
177 PipelineCacheL0* PipelineCache::GetPipelineCacheL0(Program* program, Render::Geometry* geometry)
178 {
179   auto it = std::find_if(level0nodes.begin(), level0nodes.end(), [program, geometry](PipelineCacheL0& item) {
180     return ((item.program == program && item.geometry == geometry));
181   });
182
183   // Add new node to cache
184   if(it == level0nodes.end())
185   {
186     uint32_t bindingIndex{0u};
187     auto&    reflection = graphicsController->GetProgramReflection(program->GetGraphicsProgram());
188
189     Graphics::VertexInputState vertexInputState{};
190     uint32_t                   base = 0;
191
192     for(auto&& vertexBuffer : geometry->GetVertexBuffers())
193     {
194       const VertexBuffer::Format& vertexFormat = *vertexBuffer->GetFormat();
195
196       vertexInputState.bufferBindings.emplace_back(vertexFormat.size, // stride
197                                                    Graphics::VertexInputRate::PER_VERTEX);
198
199       const uint32_t attributeCount          = vertexBuffer->GetAttributeCount();
200       uint32_t       lastBoundAttributeIndex = 0;
201       for(uint32_t i = 0; i < attributeCount; ++i)
202       {
203         auto    attributeName = vertexBuffer->GetAttributeName(i);
204         int32_t pLocation     = reflection.GetVertexAttributeLocation(std::string(attributeName.GetStringView()));
205         if(-1 != pLocation)
206         {
207           auto location = static_cast<uint32_t>(pLocation);
208           vertexInputState.attributes.emplace_back(location,
209                                                    bindingIndex,
210                                                    vertexFormat.components[i].offset,
211                                                    GetPropertyVertexFormat(vertexFormat.components[i].type));
212           ++lastBoundAttributeIndex;
213         }
214         else
215         {
216           DALI_LOG_WARNING("Attribute not found in the shader: %s\n", attributeName.GetCString());
217           // Don't bind unused attributes.
218         }
219       }
220       base += lastBoundAttributeIndex;
221       ++bindingIndex;
222     }
223     PipelineCacheL0 level0;
224     level0.program    = program;
225     level0.geometry   = geometry;
226     level0.inputState = vertexInputState;
227     level0nodes.emplace_back(std::move(level0));
228     it = level0nodes.end() - 1;
229   }
230
231   return &*it;
232 }
233
234 PipelineCacheL1* PipelineCacheL0::GetPipelineCacheL1(Render::Renderer* renderer, bool usingReflection)
235 {
236   // hash must be collision free
237   uint32_t hash = 0;
238   auto     topo = (uint32_t(geometry->GetTopology()) & 0xffu);
239   auto     cull = (uint32_t(renderer->GetFaceCullMode()) & 0xffu);
240
241   static const Graphics::PolygonMode polyTable[] = {
242     Graphics::PolygonMode::POINT,
243     Graphics::PolygonMode::LINE,
244     Graphics::PolygonMode::LINE,
245     Graphics::PolygonMode::LINE,
246     Graphics::PolygonMode::FILL,
247     Graphics::PolygonMode::FILL,
248     Graphics::PolygonMode::FILL};
249
250   auto poly = polyTable[topo];
251
252   static const FaceCullingMode::Type adjFaceCullingMode[4] =
253     {
254       FaceCullingMode::NONE,
255       FaceCullingMode::BACK,
256       FaceCullingMode::FRONT,
257       FaceCullingMode::FRONT_AND_BACK,
258     };
259
260   static const FaceCullingMode::Type normalFaceCullingMode[4] =
261     {
262       FaceCullingMode::NONE,
263       FaceCullingMode::FRONT,
264       FaceCullingMode::BACK,
265       FaceCullingMode::FRONT_AND_BACK,
266     };
267
268   static const FaceCullingMode::Type* cullModeTable[2] = {
269     normalFaceCullingMode,
270     adjFaceCullingMode};
271
272   // Retrieve cull mode
273   auto cullModeTableIndex = uint32_t(usingReflection) & 1u;
274   cull                    = cullModeTable[cullModeTableIndex][renderer->GetFaceCullMode()];
275
276   hash = (topo & 0xffu) | ((cull << 8u) & 0xff00u) | ((uint32_t(poly) << 16u) & 0xff0000u);
277
278   // If L1 not found by hash, create rasterization state describing pipeline and store it
279   auto it = std::find_if(level1nodes.begin(), level1nodes.end(), [hash](PipelineCacheL1& item) {
280     return item.hashCode == hash;
281   });
282
283   PipelineCacheL1* retval = nullptr;
284   if(it == level1nodes.end())
285   {
286     PipelineCacheL1 item;
287     item.hashCode       = hash;
288     item.rs.cullMode    = ConvertCullFace(FaceCullingMode::Type(cull));
289     item.rs.frontFace   = Graphics::FrontFace::COUNTER_CLOCKWISE;
290     item.rs.polygonMode = poly; // not in use
291     item.ia.topology    = geometry->GetTopology();
292     level1nodes.emplace_back(std::move(item));
293     retval = &level1nodes.back();
294   }
295   else
296   {
297     retval = &*it;
298   }
299   return retval;
300 }
301
302 PipelineCacheL2* PipelineCacheL1::GetPipelineCacheL2(bool blend, bool premul, BlendingOptions& blendingOptions)
303 {
304   // early out
305   if(!blend)
306   {
307     if(noBlend.pipeline == nullptr)
308     {
309       // reset all before returning if pipeline has never been created for that case
310       noBlend.hash = 0;
311       memset(&noBlend.colorBlendState, 0, sizeof(Graphics::ColorBlendState));
312     }
313     return &noBlend;
314   }
315
316   auto bitmask = uint32_t(blendingOptions.GetBitmask());
317
318   // Find by bitmask (L2 entries must be sorted by bitmask)
319   auto it = std::find_if(level2nodes.begin(), level2nodes.end(), [bitmask](PipelineCacheL2& item) {
320     return item.hash == bitmask;
321   });
322
323   // TODO: find better way of blend constants lookup
324   PipelineCacheL2* retval = nullptr;
325   if(it != level2nodes.end())
326   {
327     bool hasBlendColor = blendingOptions.GetBlendColor();
328     while(hasBlendColor && it != level2nodes.end() && (*it).hash == bitmask)
329     {
330       Vector4 v(it->colorBlendState.blendConstants);
331       if(v == *blendingOptions.GetBlendColor())
332       {
333         retval = &*it;
334       }
335       ++it;
336     }
337     if(!hasBlendColor)
338     {
339       retval = &*it;
340     }
341   }
342
343   if(!retval)
344   {
345     // create new entry and return it with null pipeline
346     PipelineCacheL2 l2;
347     l2.pipeline           = nullptr;
348     auto& colorBlendState = l2.colorBlendState;
349     colorBlendState.SetBlendEnable(true);
350     Graphics::BlendOp rgbOp   = ConvertBlendEquation(blendingOptions.GetBlendEquationRgb());
351     Graphics::BlendOp alphaOp = ConvertBlendEquation(blendingOptions.GetBlendEquationRgb());
352     if(blendingOptions.IsAdvancedBlendEquationApplied() && premul)
353     {
354       if(rgbOp != alphaOp)
355       {
356         DALI_LOG_ERROR("Advanced Blend Equation MUST be applied by using BlendEquation.\n");
357         alphaOp = rgbOp;
358       }
359     }
360
361     colorBlendState
362       .SetSrcColorBlendFactor(ConvertBlendFactor(blendingOptions.GetBlendSrcFactorRgb()))
363       .SetSrcAlphaBlendFactor(ConvertBlendFactor(blendingOptions.GetBlendSrcFactorAlpha()))
364       .SetDstColorBlendFactor(ConvertBlendFactor(blendingOptions.GetBlendDestFactorRgb()))
365       .SetDstAlphaBlendFactor(ConvertBlendFactor(blendingOptions.GetBlendDestFactorAlpha()))
366       .SetColorBlendOp(rgbOp)
367       .SetAlphaBlendOp(alphaOp);
368
369     // Blend color is optional and rarely used
370     auto* blendColor = const_cast<Vector4*>(blendingOptions.GetBlendColor());
371     if(blendColor)
372     {
373       colorBlendState.SetBlendConstants(blendColor->AsFloat());
374     }
375
376     l2.hash = blendingOptions.GetBitmask();
377     level2nodes.emplace_back(std::move(l2));
378
379     std::sort(level2nodes.begin(), level2nodes.end(), [](PipelineCacheL2& lhs, PipelineCacheL2& rhs) {
380       return lhs.hash < rhs.hash;
381     });
382
383     // run same function to retrieve retval
384     retval = GetPipelineCacheL2(blend, premul, blendingOptions);
385   }
386
387   return retval;
388 }
389
390 PipelineCache::PipelineCache(Graphics::Controller& controller)
391 : graphicsController(&controller)
392 {
393 }
394
395 PipelineResult PipelineCache::GetPipeline(const PipelineCacheQueryInfo& queryInfo, bool createNewIfNotFound)
396 {
397   auto* level0 = GetPipelineCacheL0(queryInfo.program, queryInfo.geometry);
398   auto* level1 = level0->GetPipelineCacheL1(queryInfo.renderer, queryInfo.cameraUsingReflection);
399   auto* level2 = level1->GetPipelineCacheL2(queryInfo.blendingEnabled, queryInfo.alphaPremultiplied, *queryInfo.blendingOptions);
400
401   // Create new pipeline at level2 if requested
402   if(level2->pipeline == nullptr && createNewIfNotFound)
403   {
404     Graphics::ProgramState programState{};
405     programState.program = &queryInfo.program->GetGraphicsProgram();
406     // Create the pipeline
407     Graphics::PipelineCreateInfo createInfo;
408     createInfo
409       .SetInputAssemblyState(&level1->ia)
410       .SetVertexInputState(&level0->inputState)
411       .SetRasterizationState(&level1->rs)
412       .SetColorBlendState(&level2->colorBlendState)
413       .SetProgramState(&programState);
414
415     // Store a pipeline per renderer per render (renderer can be owned by multiple nodes,
416     // and re-drawn in multiple instructions).
417     // @todo This is only needed because ColorBlend state can change. Fixme!
418     // This is ameliorated by the fact that implementation caches pipelines, and we're only storing
419     // handles.
420     level2->pipeline = graphicsController->CreatePipeline(createInfo, nullptr);
421   }
422
423   PipelineResult result{};
424
425   result.pipeline = level2->pipeline.get();
426   result.level0   = level0;
427   result.level1   = level1;
428   result.level2   = level2;
429
430   return result;
431 }
432
433 } // namespace Dali::Internal::Render