Fix svace issue for image-operator
[platform/core/uifw/dali-adaptor.git] / dali / internal / graphics / gles-impl / gles-graphics-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
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*> STATE_COMPARE_FUNC_TABLE{};
137
138 /**
139  * @brief Initialises compare function lookup table
140  */
141 void InitialiseStateCompareLookupTable()
142 {
143   STATE_COMPARE_FUNC_TABLE = {
144     [](const auto* lhs, const auto* rhs) -> bool // colorBlendState
145     {
146       const auto& lcb = *lhs->colorBlendState;
147       const auto& rcb = *rhs->colorBlendState;
148       return lcb.logicOpEnable == rcb.logicOpEnable &&
149              lcb.logicOp == rcb.logicOp &&
150              cmpf(lcb.blendConstants[0], rcb.blendConstants[0]) &&
151              cmpf(lcb.blendConstants[1], rcb.blendConstants[1]) &&
152              cmpf(lcb.blendConstants[2], rcb.blendConstants[2]) &&
153              cmpf(lcb.blendConstants[3], rcb.blendConstants[3]) &&
154              lcb.blendEnable == rcb.blendEnable &&
155              lcb.srcColorBlendFactor == rcb.srcColorBlendFactor &&
156              lcb.dstColorBlendFactor == rcb.dstColorBlendFactor &&
157              lcb.colorBlendOp == rcb.colorBlendOp &&
158              lcb.srcAlphaBlendFactor == rcb.srcAlphaBlendFactor &&
159              lcb.dstAlphaBlendFactor == rcb.dstAlphaBlendFactor &&
160              lcb.alphaBlendOp == rcb.alphaBlendOp &&
161              lcb.colorComponentWriteBits == rcb.colorComponentWriteBits;
162     },
163     [](const auto* lhs, const auto* rhs) -> bool // viewport state
164     {
165       const auto& lvp = *lhs->viewportState;
166       const auto& rvp = *rhs->viewportState;
167       return lvp.viewport == rvp.viewport &&
168              lvp.scissor == rvp.scissor &&
169              lvp.scissorTestEnable == rvp.scissorTestEnable;
170     },
171     [](const auto* lhs, const auto* rhs) -> bool // basePipeline
172     {
173       return lhs->basePipeline == rhs->basePipeline;
174     },
175     [](const auto* lhs, const auto* rhs) -> bool // depthStencilState
176     {
177       const auto& lds = *lhs->depthStencilState;
178       const auto& rds = *rhs->depthStencilState;
179       return lds.depthTestEnable == rds.depthTestEnable &&
180              lds.depthWriteEnable == rds.depthWriteEnable &&
181              lds.depthCompareOp == rds.depthCompareOp &&
182              lds.stencilTestEnable == rds.stencilTestEnable &&
183              lds.front == rds.front &&
184              lds.back == rds.back;
185     },
186     [](const auto* lhs, const auto* rhs) -> bool // rasterizationState
187     {
188       const auto& lrs = *lhs->rasterizationState;
189       const auto& rrs = *rhs->rasterizationState;
190       return lrs.cullMode == rrs.cullMode &&
191              lrs.polygonMode == rrs.polygonMode &&
192              lrs.frontFace == rrs.frontFace;
193     },
194     [](const auto* lhs, const auto* rhs) -> bool // vertexInputState
195     {
196       const auto& lvi = *lhs->vertexInputState;
197       const auto& rvi = *rhs->vertexInputState;
198       return lvi.bufferBindings.size() == rvi.bufferBindings.size() &&
199              lvi.attributes.size() == rvi.attributes.size() &&
200              std::equal(lvi.bufferBindings.begin(), lvi.bufferBindings.end(), rvi.bufferBindings.begin(), [](const auto& lhs, const auto& rhs) {
201                return operator==(lhs, rhs);
202              }) &&
203              std::equal(lvi.attributes.begin(), lvi.attributes.end(), rvi.attributes.begin(), [](const auto& lhs, const auto& rhs) {
204                return operator==(lhs, rhs);
205              });
206     },
207     [](const auto* lhs, const auto* rhs) -> bool // inputAssemblyState
208     {
209       const auto& lia = *lhs->inputAssemblyState;
210       const auto& ria = *rhs->inputAssemblyState;
211       return lia.topology == ria.topology &&
212              lia.primitiveRestartEnable == ria.primitiveRestartEnable;
213     },
214   };
215 }
216
217 /**
218  * @brief Helper function calculating the bitmask of set states
219  *
220  * @param[in] info Valid PipelineCreateInfo structure
221  * @return bitmask of set states
222  */
223 inline uint32_t GetStateBitmask(const PipelineCreateInfo& info)
224 {
225   uint32_t mask{0u};
226   mask |= bool(info.colorBlendState) << int(StateLookupIndex::COLOR_BLEND_STATE_BIT);
227   mask |= bool(info.viewportState) << int(StateLookupIndex::VIEWPORT_STATE_BIT);
228   mask |= bool(info.basePipeline) << int(StateLookupIndex::BASE_PIPELINE_STATE_BIT);
229   mask |= bool(info.depthStencilState) << int(StateLookupIndex::DEPTH_STENCIL_STATE_BIT);
230   mask |= bool(info.rasterizationState) << int(StateLookupIndex::RASTERIZATION_STATE_BIT);
231   mask |= bool(info.vertexInputState) << int(StateLookupIndex::VERTEX_INPUT_STATE_BIT);
232   mask |= bool(info.inputAssemblyState) << int(StateLookupIndex::INPUT_ASSEMBLY_STATE_BIT);
233   return mask;
234 }
235
236 /**
237  * @brief Implementation of cache
238  */
239 struct PipelineCache::Impl
240 {
241   /**
242    * @brief Constructor
243    */
244   explicit Impl(EglGraphicsController& _controller)
245   : controller(_controller)
246   {
247     // Initialise lookup table
248     InitialiseStateCompareLookupTable();
249   }
250
251   /**
252    * @brief destructor
253    */
254   ~Impl()
255   {
256     // First destroy pipelines
257     entries.clear();
258
259     // Now programs
260     programEntries.clear();
261   }
262
263   /**
264    * @brief Structure describes a single cache entry
265    */
266   struct CacheEntry
267   {
268     CacheEntry() = default;
269
270     CacheEntry(UniquePtr<PipelineImpl>&& _pipeline, uint32_t _bitmask)
271     : pipeline(std::move(_pipeline)),
272       stateBitmask(_bitmask)
273     {
274     }
275
276     ~CacheEntry() = default;
277
278     CacheEntry(CacheEntry&&) = default;
279     CacheEntry& operator=(CacheEntry&&) = default;
280
281     UniquePtr<PipelineImpl> pipeline{nullptr};
282     uint32_t                stateBitmask{0u};
283   };
284
285   EglGraphicsController&  controller;
286   std::vector<CacheEntry> entries;
287
288   /**
289    * @brief Sorted array of shaders used to create program
290    */
291   struct ProgramCacheEntry
292   {
293     // sorted array of shaders
294     std::vector<const Graphics::Shader*> shaders;
295     UniquePtr<ProgramImpl>               program{nullptr};
296   };
297
298   std::vector<ProgramCacheEntry> programEntries;
299 };
300
301 PipelineCache::PipelineCache(EglGraphicsController& controller)
302 {
303   mImpl = std::make_unique<Impl>(controller);
304 }
305
306 PipelineCache::~PipelineCache() = default;
307
308 PipelineImpl* PipelineCache::FindPipelineImpl(const PipelineCreateInfo& info)
309 {
310   auto bitmask = GetStateBitmask(info);
311
312   for(auto& entry : mImpl->entries)
313   {
314     auto& pipeline  = entry.pipeline;
315     auto& cacheInfo = pipeline->GetCreateInfo();
316     if(!info.programState)
317     {
318       continue;
319     }
320
321     // Check whether the program is the same
322     if(info.programState->program)
323     {
324       const auto& lhsProgram = *static_cast<const GLES::Program*>(info.programState->program);
325       const auto& rhsProgram = *static_cast<const GLES::Program*>(cacheInfo.programState->program);
326       if(lhsProgram != rhsProgram)
327       {
328         continue;
329       }
330
331       // Test whether set states bitmask matches
332       if(entry.stateBitmask != bitmask)
333       {
334         continue;
335       }
336
337       // Now test only for states that are set
338       auto i = 0;
339       for(i = 0; i < int(StateLookupIndex::MAX_STATE); ++i)
340       {
341         // Test only set states
342         if((entry.stateBitmask & (1 << i)))
343         {
344           if(!STATE_COMPARE_FUNC_TABLE[i](&info, &cacheInfo))
345           {
346             break;
347           }
348         }
349       }
350
351       // TODO: For now ignoring dynamic state mask and allocator
352       // Getting as far as here, we have found our pipeline impl
353       if(i == int(StateLookupIndex::MAX_STATE))
354       {
355         return pipeline.get();
356       }
357     }
358   }
359   return nullptr;
360 }
361
362 ProgramImpl* PipelineCache::FindProgramImpl(const ProgramCreateInfo& info)
363 {
364   if(mImpl->programEntries.empty())
365   {
366     return nullptr;
367   }
368
369   // assert if no shaders given
370   std::vector<const Graphics::Shader*> shaders;
371   shaders.reserve(info.shaderState->size());
372
373   for(auto& state : *info.shaderState)
374   {
375     shaders.push_back(state.shader);
376   }
377
378   // sort
379   std::sort(shaders.begin(), shaders.end());
380
381   for(auto& item : mImpl->programEntries)
382   {
383     if(item.shaders.size() != shaders.size())
384     {
385       continue;
386     }
387
388     int k = shaders.size();
389     while(--k >= 0 && item.shaders[k] == shaders[k])
390       ;
391
392     if(k < 0)
393     {
394       return item.program.get();
395     }
396   }
397   return nullptr;
398 }
399
400 Graphics::UniquePtr<Graphics::Pipeline> PipelineCache::GetPipeline(const PipelineCreateInfo&                 pipelineCreateInfo,
401                                                                    Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline)
402 {
403   auto cachedPipeline = FindPipelineImpl(pipelineCreateInfo);
404
405   // Return same pointer if nothing changed
406   if(oldPipeline && *static_cast<GLES::Pipeline*>(oldPipeline.get()) == cachedPipeline)
407   {
408     return std::move(oldPipeline);
409   }
410
411   if(!cachedPipeline)
412   {
413     // create new pipeline
414     auto pipeline = MakeUnique<GLES::PipelineImpl>(pipelineCreateInfo, mImpl->controller, *this);
415
416     cachedPipeline = pipeline.get();
417
418     // add it to cache
419     mImpl->entries.emplace_back(std::move(pipeline), GetStateBitmask(pipelineCreateInfo));
420   }
421
422   auto wrapper = MakeUnique<GLES::Pipeline, CachedObjectDeleter<GLES::Pipeline>>(*cachedPipeline);
423   return std::move(wrapper);
424 }
425
426 Graphics::UniquePtr<Graphics::Program> PipelineCache::GetProgram(const ProgramCreateInfo&                 programCreateInfo,
427                                                                  Graphics::UniquePtr<Graphics::Program>&& oldProgram)
428 {
429   ProgramImpl* cachedProgram = FindProgramImpl(programCreateInfo);
430
431   // Return same pointer if nothing changed
432   if(oldProgram && *static_cast<GLES::Program*>(oldProgram.get()) == cachedProgram)
433   {
434     return std::move(oldProgram);
435   }
436
437   if(!cachedProgram)
438   {
439     // create new pipeline
440     auto program = MakeUnique<GLES::ProgramImpl>(programCreateInfo, mImpl->controller);
441
442     program->Create();
443
444     cachedProgram = program.get();
445
446     // add it to cache
447     mImpl->programEntries.emplace_back();
448     auto& item   = mImpl->programEntries.back();
449     item.program = std::move(program);
450     for(auto& state : *programCreateInfo.shaderState)
451     {
452       item.shaders.push_back(state.shader);
453     }
454
455     std::sort(item.shaders.begin(), item.shaders.end());
456   }
457
458   auto wrapper = MakeUnique<GLES::Program, CachedObjectDeleter<GLES::Program>>(cachedProgram);
459   return std::move(wrapper);
460 }
461
462 void PipelineCache::FlushCache()
463 {
464   decltype(mImpl->entries) newEntries;
465   newEntries.reserve(mImpl->entries.size());
466
467   for(auto& entry : mImpl->entries)
468   {
469     // Move items which are still in use into the new array
470     if(entry.pipeline->GetRefCount() != 0)
471     {
472       newEntries.emplace_back(std::move(entry));
473     }
474   }
475
476   // Move temporary array in place of stored cache
477   // Unused pipelines will be deleted automatically
478   mImpl->entries = std::move(newEntries);
479
480   // TODO: program cache may require similar action. However,
481   //       since there is always one wrapper for Program object
482   //       kept in the pipeline, then death of pipeline will result
483   //       killing the program (if program isn't in use anymore)
484 }
485
486 } // namespace Dali::Graphics::GLES