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