Merge "Fix svace issue for image-operator" into devel/master
[platform/core/uifw/dali-adaptor.git] / dali / internal / graphics / gles-impl / gles-graphics-pipeline-cache.cpp
1 /*
2  * Copyright (c) 2023 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
18 #include "gles-graphics-pipeline-cache.h"
19 #include <algorithm>
20 #include "egl-graphics-controller.h"
21 #include "gles-graphics-pipeline.h"
22 #include "gles-graphics-program.h"
23
24 namespace Dali::Graphics::GLES
25 {
26 /**
27  * @brief custom delete function for cached object
28  */
29 template<class T>
30 struct CachedObjectDeleter
31 {
32   CachedObjectDeleter() = default;
33
34   void operator()(T* object)
35   {
36     // Discard resource (add it to discard queue)
37     object->DiscardResource();
38   }
39 };
40
41 template<>
42 struct CachedObjectDeleter<GLES::Program>
43 {
44   CachedObjectDeleter() = default;
45
46   void operator()(GLES::Program* object)
47   {
48     // Program deleter should skip discard queue if controller shutting down
49     if(!object->GetController().IsShuttingDown())
50     {
51       object->DiscardResource();
52     }
53     else
54     {
55       // delete object otherwise
56       delete object;
57     }
58   }
59 };
60
61 /**
62  * @brief The order of states being stored in the cache and mask
63  */
64 enum class StateLookupIndex : uint32_t
65 {
66   COLOR_BLEND_STATE_BIT    = 0,
67   VIEWPORT_STATE_BIT       = 1,
68   BASE_PIPELINE_STATE_BIT  = 2,
69   DEPTH_STENCIL_STATE_BIT  = 3,
70   RASTERIZATION_STATE_BIT  = 4,
71   VERTEX_INPUT_STATE_BIT   = 5,
72   INPUT_ASSEMBLY_STATE_BIT = 6,
73   MAX_STATE                = 7
74 };
75
76 /**
77  * Helper float compare function
78  */
79 static bool cmpf(float A, float B, float epsilon = 0.005f)
80 {
81   return (fabs(A - B) < epsilon);
82 }
83
84 /**
85  * Helper operators
86  */
87 static bool operator==(const Graphics::Viewport& lhs, const Graphics::Viewport& rhs)
88 {
89   return cmpf(lhs.x, rhs.x) &&
90          cmpf(lhs.y, rhs.y) &&
91          cmpf(lhs.width, rhs.width) &&
92          cmpf(lhs.height, rhs.height) &&
93          cmpf(lhs.minDepth, rhs.minDepth) &&
94          cmpf(lhs.maxDepth, rhs.maxDepth);
95 }
96
97 static bool operator==(const Graphics::Rect2D& lhs, const Graphics::Rect2D& rhs)
98 {
99   return cmpf(lhs.x, rhs.x) &&
100          cmpf(lhs.y, rhs.y) &&
101          cmpf(lhs.width, rhs.width) &&
102          cmpf(lhs.height, rhs.height);
103 }
104
105 static bool operator==(const Graphics::StencilOpState& lhs, const Graphics::StencilOpState& rhs)
106 {
107   return lhs.failOp == rhs.failOp &&
108          lhs.passOp == rhs.passOp &&
109          lhs.depthFailOp == rhs.depthFailOp &&
110          lhs.compareOp == rhs.compareOp &&
111          lhs.compareMask == rhs.compareMask &&
112          lhs.writeMask == rhs.writeMask &&
113          lhs.reference == rhs.reference;
114 }
115
116 static bool
117 operator==(const Dali::Graphics::VertexInputState::Attribute& lhs,
118            const Dali::Graphics::VertexInputState::Attribute& rhs)
119 {
120   return lhs.location == rhs.location &&
121          lhs.binding == rhs.binding &&
122          lhs.offset == rhs.offset &&
123          lhs.format == rhs.format;
124 }
125
126 static bool
127 operator==(const Dali::Graphics::VertexInputState::Binding& lhs, const Dali::Graphics::VertexInputState::Binding& rhs)
128 {
129   return lhs.stride == rhs.stride &&
130          lhs.inputRate == rhs.inputRate;
131 }
132
133 using PipelineStateCompateFunctionType = bool(const Graphics::PipelineCreateInfo*,
134                                               const Graphics::PipelineCreateInfo*);
135
136 static std::vector<PipelineStateCompateFunctionType*>& GetStateCompareFuncTable()
137 {
138   static std::vector<PipelineStateCompateFunctionType*> stateCompareFuncTable{};
139   return stateCompareFuncTable;
140 }
141
142 /**
143  * @brief Initialises compare function lookup table
144  */
145 void InitialiseStateCompareLookupTable()
146 {
147   GetStateCompareFuncTable().clear();
148   GetStateCompareFuncTable().push_back([](const auto* lhs, const auto* rhs) -> bool // colorBlendState
149                                        {
150                                          const auto& lcb = *lhs->colorBlendState;
151                                          const auto& rcb = *rhs->colorBlendState;
152                                          return lcb.logicOpEnable == rcb.logicOpEnable &&
153                                                 lcb.logicOp == rcb.logicOp &&
154                                                 cmpf(lcb.blendConstants[0], rcb.blendConstants[0]) &&
155                                                 cmpf(lcb.blendConstants[1], rcb.blendConstants[1]) &&
156                                                 cmpf(lcb.blendConstants[2], rcb.blendConstants[2]) &&
157                                                 cmpf(lcb.blendConstants[3], rcb.blendConstants[3]) &&
158                                                 lcb.blendEnable == rcb.blendEnable &&
159                                                 lcb.srcColorBlendFactor == rcb.srcColorBlendFactor &&
160                                                 lcb.dstColorBlendFactor == rcb.dstColorBlendFactor &&
161                                                 lcb.colorBlendOp == rcb.colorBlendOp &&
162                                                 lcb.srcAlphaBlendFactor == rcb.srcAlphaBlendFactor &&
163                                                 lcb.dstAlphaBlendFactor == rcb.dstAlphaBlendFactor &&
164                                                 lcb.alphaBlendOp == rcb.alphaBlendOp &&
165                                                 lcb.colorComponentWriteBits == rcb.colorComponentWriteBits;
166                                        });
167   GetStateCompareFuncTable().push_back([](const auto* lhs, const auto* rhs) -> bool // viewport state
168                                        {
169                                          const auto& lvp = *lhs->viewportState;
170                                          const auto& rvp = *rhs->viewportState;
171                                          return lvp.viewport == rvp.viewport &&
172                                                 lvp.scissor == rvp.scissor &&
173                                                 lvp.scissorTestEnable == rvp.scissorTestEnable;
174                                        });
175   GetStateCompareFuncTable().push_back([](const auto* lhs, const auto* rhs) -> bool // basePipeline
176                                        {
177                                          return lhs->basePipeline == rhs->basePipeline;
178                                        });
179   GetStateCompareFuncTable().push_back([](const auto* lhs, const auto* rhs) -> bool // depthStencilState
180                                        {
181                                          const auto& lds = *lhs->depthStencilState;
182                                          const auto& rds = *rhs->depthStencilState;
183                                          return lds.depthTestEnable == rds.depthTestEnable &&
184                                                 lds.depthWriteEnable == rds.depthWriteEnable &&
185                                                 lds.depthCompareOp == rds.depthCompareOp &&
186                                                 lds.stencilTestEnable == rds.stencilTestEnable &&
187                                                 lds.front == rds.front &&
188                                                 lds.back == rds.back;
189                                        });
190   GetStateCompareFuncTable().push_back([](const auto* lhs, const auto* rhs) -> bool // rasterizationState
191                                        {
192                                          const auto& lrs = *lhs->rasterizationState;
193                                          const auto& rrs = *rhs->rasterizationState;
194                                          return lrs.cullMode == rrs.cullMode &&
195                                                 lrs.polygonMode == rrs.polygonMode &&
196                                                 lrs.frontFace == rrs.frontFace;
197                                        });
198   GetStateCompareFuncTable().push_back([](const auto* lhs, const auto* rhs) -> bool // vertexInputState
199                                        {
200                                          const auto& lvi = *lhs->vertexInputState;
201                                          const auto& rvi = *rhs->vertexInputState;
202                                          return lvi.bufferBindings.size() == rvi.bufferBindings.size() &&
203                                                 lvi.attributes.size() == rvi.attributes.size() &&
204                                                 std::equal(lvi.bufferBindings.begin(), lvi.bufferBindings.end(), rvi.bufferBindings.begin(), [](const auto& lhs, const auto& rhs) {
205                                                   return operator==(lhs, rhs);
206                                                 }) &&
207                                                 std::equal(lvi.attributes.begin(), lvi.attributes.end(), rvi.attributes.begin(), [](const auto& lhs, const auto& rhs) {
208                                                   return operator==(lhs, rhs);
209                                                 });
210                                        });
211   GetStateCompareFuncTable().push_back([](const auto* lhs, const auto* rhs) -> bool // inputAssemblyState
212                                        {
213                                          const auto& lia = *lhs->inputAssemblyState;
214                                          const auto& ria = *rhs->inputAssemblyState;
215                                          return lia.topology == ria.topology &&
216                                                 lia.primitiveRestartEnable == ria.primitiveRestartEnable;
217                                        });
218 }
219
220 /**
221  * @brief Helper function calculating the bitmask of set states
222  *
223  * @param[in] info Valid PipelineCreateInfo structure
224  * @return bitmask of set states
225  */
226 inline uint32_t GetStateBitmask(const PipelineCreateInfo& info)
227 {
228   uint32_t mask{0u};
229   mask |= bool(info.colorBlendState) << int(StateLookupIndex::COLOR_BLEND_STATE_BIT);
230   mask |= bool(info.viewportState) << int(StateLookupIndex::VIEWPORT_STATE_BIT);
231   mask |= bool(info.basePipeline) << int(StateLookupIndex::BASE_PIPELINE_STATE_BIT);
232   mask |= bool(info.depthStencilState) << int(StateLookupIndex::DEPTH_STENCIL_STATE_BIT);
233   mask |= bool(info.rasterizationState) << int(StateLookupIndex::RASTERIZATION_STATE_BIT);
234   mask |= bool(info.vertexInputState) << int(StateLookupIndex::VERTEX_INPUT_STATE_BIT);
235   mask |= bool(info.inputAssemblyState) << int(StateLookupIndex::INPUT_ASSEMBLY_STATE_BIT);
236   return mask;
237 }
238
239 /**
240  * @brief Implementation of cache
241  */
242 struct PipelineCache::Impl
243 {
244   /**
245    * @brief Constructor
246    */
247   explicit Impl(EglGraphicsController& _controller)
248   : controller(_controller)
249   {
250     // Initialise lookup table
251     InitialiseStateCompareLookupTable();
252   }
253
254   /**
255    * @brief destructor
256    */
257   ~Impl()
258   {
259     // First destroy pipelines
260     entries.clear();
261
262     // Now programs
263     programEntries.clear();
264   }
265
266   /**
267    * @brief Structure describes a single cache entry
268    */
269   struct CacheEntry
270   {
271     CacheEntry() = default;
272
273     CacheEntry(UniquePtr<PipelineImpl>&& _pipeline, uint32_t _bitmask)
274     : pipeline(std::move(_pipeline)),
275       stateBitmask(_bitmask)
276     {
277     }
278
279     ~CacheEntry() = default;
280
281     CacheEntry(CacheEntry&&) = default;
282     CacheEntry& operator=(CacheEntry&&) = default;
283
284     UniquePtr<PipelineImpl> pipeline{nullptr};
285     uint32_t                stateBitmask{0u};
286   };
287
288   EglGraphicsController&  controller;
289   std::vector<CacheEntry> entries;
290
291   /**
292    * @brief Sorted array of shaders used to create program
293    */
294   struct ProgramCacheEntry
295   {
296     // sorted array of shaders
297     std::vector<const Graphics::Shader*> shaders;
298     UniquePtr<ProgramImpl>               program{nullptr};
299   };
300
301   std::vector<ProgramCacheEntry> programEntries;
302 };
303
304 PipelineCache::PipelineCache(EglGraphicsController& controller)
305 {
306   mImpl = std::make_unique<Impl>(controller);
307 }
308
309 PipelineCache::~PipelineCache() = default;
310
311 PipelineImpl* PipelineCache::FindPipelineImpl(const PipelineCreateInfo& info)
312 {
313   auto bitmask = GetStateBitmask(info);
314
315   for(auto& entry : mImpl->entries)
316   {
317     auto& pipeline  = entry.pipeline;
318     auto& cacheInfo = pipeline->GetCreateInfo();
319     if(!info.programState)
320     {
321       continue;
322     }
323
324     // Check whether the program is the same
325     if(info.programState->program)
326     {
327       const auto& lhsProgram = *static_cast<const GLES::Program*>(info.programState->program);
328       const auto& rhsProgram = *static_cast<const GLES::Program*>(cacheInfo.programState->program);
329       if(lhsProgram != rhsProgram)
330       {
331         continue;
332       }
333
334       // Test whether set states bitmask matches
335       if(entry.stateBitmask != bitmask)
336       {
337         continue;
338       }
339
340       // Now test only for states that are set
341       auto i = 0;
342       for(i = 0; i < int(StateLookupIndex::MAX_STATE); ++i)
343       {
344         // Test only set states
345         if((entry.stateBitmask & (1 << i)))
346         {
347           if(!(GetStateCompareFuncTable()[i](&info, &cacheInfo)))
348           {
349             break;
350           }
351         }
352       }
353
354       // TODO: For now ignoring dynamic state mask and allocator
355       // Getting as far as here, we have found our pipeline impl
356       if(i == int(StateLookupIndex::MAX_STATE))
357       {
358         return pipeline.get();
359       }
360     }
361   }
362   return nullptr;
363 }
364
365 ProgramImpl* PipelineCache::FindProgramImpl(const ProgramCreateInfo& info)
366 {
367   if(mImpl->programEntries.empty())
368   {
369     return nullptr;
370   }
371
372   // assert if no shaders given
373   std::vector<const Graphics::Shader*> shaders;
374   shaders.reserve(info.shaderState->size());
375
376   for(auto& state : *info.shaderState)
377   {
378     shaders.push_back(state.shader);
379   }
380
381   // sort
382   std::sort(shaders.begin(), shaders.end());
383
384   for(auto& item : mImpl->programEntries)
385   {
386     if(item.shaders.size() != shaders.size())
387     {
388       continue;
389     }
390
391     int k = shaders.size();
392     while(--k >= 0 && item.shaders[k] == shaders[k])
393       ;
394
395     if(k < 0)
396     {
397       return item.program.get();
398     }
399   }
400   return nullptr;
401 }
402
403 Graphics::UniquePtr<Graphics::Pipeline> PipelineCache::GetPipeline(const PipelineCreateInfo&                 pipelineCreateInfo,
404                                                                    Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline)
405 {
406   auto cachedPipeline = FindPipelineImpl(pipelineCreateInfo);
407
408   // Return same pointer if nothing changed
409   if(oldPipeline && *static_cast<GLES::Pipeline*>(oldPipeline.get()) == cachedPipeline)
410   {
411     return std::move(oldPipeline);
412   }
413
414   if(!cachedPipeline)
415   {
416     // create new pipeline
417     auto pipeline = MakeUnique<GLES::PipelineImpl>(pipelineCreateInfo, mImpl->controller, *this);
418
419     cachedPipeline = pipeline.get();
420
421     // add it to cache
422     mImpl->entries.emplace_back(std::move(pipeline), GetStateBitmask(pipelineCreateInfo));
423   }
424
425   auto wrapper = MakeUnique<GLES::Pipeline, CachedObjectDeleter<GLES::Pipeline>>(*cachedPipeline);
426   return std::move(wrapper);
427 }
428
429 Graphics::UniquePtr<Graphics::Program> PipelineCache::GetProgram(const ProgramCreateInfo&                 programCreateInfo,
430                                                                  Graphics::UniquePtr<Graphics::Program>&& oldProgram)
431 {
432   ProgramImpl* cachedProgram = FindProgramImpl(programCreateInfo);
433
434   // Return same pointer if nothing changed
435   if(oldProgram && *static_cast<GLES::Program*>(oldProgram.get()) == cachedProgram)
436   {
437     return std::move(oldProgram);
438   }
439
440   if(!cachedProgram)
441   {
442     // create new pipeline
443     auto program = MakeUnique<GLES::ProgramImpl>(programCreateInfo, mImpl->controller);
444
445     program->Create();
446
447     cachedProgram = program.get();
448
449     // add it to cache
450     mImpl->programEntries.emplace_back();
451     auto& item   = mImpl->programEntries.back();
452     item.program = std::move(program);
453     for(auto& state : *programCreateInfo.shaderState)
454     {
455       item.shaders.push_back(state.shader);
456     }
457
458     std::sort(item.shaders.begin(), item.shaders.end());
459   }
460
461   auto wrapper = MakeUnique<GLES::Program, CachedObjectDeleter<GLES::Program>>(cachedProgram);
462   return std::move(wrapper);
463 }
464
465 void PipelineCache::FlushCache()
466 {
467   decltype(mImpl->entries) newEntries;
468   newEntries.reserve(mImpl->entries.size());
469
470   for(auto& entry : mImpl->entries)
471   {
472     // Move items which are still in use into the new array
473     if(entry.pipeline->GetRefCount() != 0)
474     {
475       newEntries.emplace_back(std::move(entry));
476     }
477   }
478
479   // Move temporary array in place of stored cache
480   // Unused pipelines will be deleted automatically
481   mImpl->entries = std::move(newEntries);
482
483   // TODO: program cache may require similar action. However,
484   //       since there is always one wrapper for Program object
485   //       kept in the pipeline, then death of pipeline will result
486   //       killing the program (if program isn't in use anymore)
487 }
488
489 } // namespace Dali::Graphics::GLES