Refactored Uniform Buffer support
[platform/core/uifw/dali-core.git] / dali / internal / render / shaders / program.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 // CLASS HEADER
19 #include <dali/internal/render/shaders/program.h>
20
21 // EXTERNAL INCLUDES
22 #include <cstring>
23 #include <map>
24
25 // INTERNAL INCLUDES
26 #include <dali/devel-api/common/hash.h>
27 #include <dali/graphics-api/graphics-controller.h>
28 #include <dali/graphics-api/graphics-program.h>
29 #include <dali/graphics-api/graphics-reflection.h>
30 #include <dali/integration-api/debug.h>
31 #include <dali/internal/common/shader-data.h>
32 #include <dali/internal/render/common/performance-monitor.h>
33 #include <dali/internal/render/renderers/uniform-buffer-manager.h>
34 #include <dali/internal/render/shaders/program-cache.h>
35 #include <dali/public-api/common/constants.h>
36 #include <dali/public-api/common/dali-common.h>
37 #include <dali/public-api/common/dali-vector.h>
38
39 namespace Dali
40 {
41 namespace Internal
42 {
43 // LOCAL STUFF
44 namespace
45 {
46 const unsigned int NUMBER_OF_DEFAULT_UNIFORMS = static_cast<unsigned int>(Program::DefaultUniformIndex::COUNT);
47
48 /**
49  * List of all default uniforms used for quicker lookup
50  */
51 size_t DEFAULT_UNIFORM_HASHTABLE[NUMBER_OF_DEFAULT_UNIFORMS] =
52   {
53     CalculateHash(std::string("uModelMatrix")),
54     CalculateHash(std::string("uMvpMatrix")),
55     CalculateHash(std::string("uViewMatrix")),
56     CalculateHash(std::string("uModelView")),
57     CalculateHash(std::string("uNormalMatrix")),
58     CalculateHash(std::string("uProjection")),
59     CalculateHash(std::string("uSize")),
60     CalculateHash(std::string("uColor")),
61     CalculateHash(std::string("uActorColor"))};
62
63 /**
64  * Helper function to calculate the correct alignment of data for uniform buffers
65  * @param dataSize size of uniform buffer
66  * @return size of data aligned to given size
67  */
68 inline uint32_t AlignSize(uint32_t dataSize, uint32_t alignSize)
69 {
70   return ((dataSize / alignSize) + ((dataSize % alignSize) ? 1u : 0u)) * alignSize;
71 }
72
73 } // namespace
74
75 // IMPLEMENTATION
76
77 Program* Program::New(ProgramCache& cache, Internal::ShaderDataPtr shaderData, Graphics::Controller& gfxController)
78 {
79   size_t shaderHash = shaderData->GetHashValue();
80
81   Program* program = cache.GetProgram(shaderHash);
82
83   if(nullptr == program)
84   {
85     // program not found so create it
86     program = new Program(cache, shaderData, gfxController);
87     DALI_LOG_RELEASE_INFO("Program::New() created a unique program\n");
88     cache.AddProgram(shaderHash, program);
89   }
90
91   return program;
92 }
93
94 Program::Program(ProgramCache& cache, Internal::ShaderDataPtr shaderData, Graphics::Controller& controller)
95 : mCache(cache),
96   mProjectionMatrix(nullptr),
97   mViewMatrix(nullptr),
98   mGfxProgram(nullptr),
99   mGfxController(controller),
100   mProgramData(shaderData)
101 {
102 }
103
104 Program::~Program() = default;
105
106 void Program::BuildReflection(const Graphics::Reflection& graphicsReflection, Render::UniformBufferManager& uniformBufferManager)
107 {
108   mReflectionDefaultUniforms.clear();
109   mReflectionDefaultUniforms.resize(NUMBER_OF_DEFAULT_UNIFORMS);
110
111   auto uniformBlockCount = graphicsReflection.GetUniformBlockCount();
112
113   // add uniform block fields
114   for(auto i = 0u; i < uniformBlockCount; ++i)
115   {
116     Graphics::UniformBlockInfo uboInfo;
117     graphicsReflection.GetUniformBlock(i, uboInfo);
118
119     // for each member store data
120     for(const auto& item : uboInfo.members)
121     {
122       // Add a hash for the whole name.
123       //
124       // If the name represents an array of basic types, it won't contain an index
125       // operator "[",NN,"]".
126       //
127       // If the name represents an element in an array of structs, it will contain an
128       // index operator, but should be hashed in full.
129       auto hashValue = CalculateHash(item.name);
130       mReflection.emplace_back(ReflectionUniformInfo{hashValue, false, item});
131
132       // update buffer index
133       mReflection.back().uniformInfo.bufferIndex = i;
134
135       // Update default uniforms
136       for(auto j = 0u; j < NUMBER_OF_DEFAULT_UNIFORMS; ++j)
137       {
138         if(hashValue == DEFAULT_UNIFORM_HASHTABLE[j])
139         {
140           mReflectionDefaultUniforms[j] = mReflection.back();
141           break;
142         }
143       }
144     }
145   }
146
147   // add samplers
148   auto samplers = graphicsReflection.GetSamplers(); // Only holds first element of arrays without [].
149   for(const auto& sampler : samplers)
150   {
151     mReflection.emplace_back(ReflectionUniformInfo{CalculateHash(sampler.name), false, sampler});
152   }
153
154   // check for potential collisions
155   std::map<size_t, bool> hashTest;
156   bool                   hasCollisions(false);
157   for(auto&& item : mReflection)
158   {
159     if(hashTest.find(item.hashValue) == hashTest.end())
160     {
161       hashTest[item.hashValue] = false;
162     }
163     else
164     {
165       hashTest[item.hashValue] = true;
166       hasCollisions            = true;
167     }
168   }
169
170   // update collision flag for further use
171   if(hasCollisions)
172   {
173     for(auto&& item : mReflection)
174     {
175       item.hasCollision = hashTest[item.hashValue];
176     }
177   }
178
179   mUniformBlockMemoryRequirements.blockSize.resize(uniformBlockCount);
180   mUniformBlockMemoryRequirements.blockSizeAligned.resize(uniformBlockCount);
181   mUniformBlockMemoryRequirements.blockCount           = uniformBlockCount;
182   mUniformBlockMemoryRequirements.totalSizeRequired    = 0u;
183   mUniformBlockMemoryRequirements.totalCpuSizeRequired = 0u;
184   mUniformBlockMemoryRequirements.totalGpuSizeRequired = 0u;
185
186   for(auto i = 0u; i < uniformBlockCount; ++i)
187   {
188     Graphics::UniformBlockInfo uboInfo;
189     graphicsReflection.GetUniformBlock(i, uboInfo);
190     bool standaloneUniformBlock = (i == 0);
191
192     auto     blockSize        = graphicsReflection.GetUniformBlockSize(i);
193     uint32_t blockAlignment   = uniformBufferManager.GetUniformBlockAlignment(standaloneUniformBlock);
194     auto     alignedBlockSize = AlignSize(blockSize, blockAlignment);
195
196     mUniformBlockMemoryRequirements.blockSize[i]        = blockSize;
197     mUniformBlockMemoryRequirements.blockSizeAligned[i] = alignedBlockSize;
198
199     mUniformBlockMemoryRequirements.totalSizeRequired += alignedBlockSize;
200     mUniformBlockMemoryRequirements.totalCpuSizeRequired += (standaloneUniformBlock) ? alignedBlockSize : 0;
201     mUniformBlockMemoryRequirements.totalGpuSizeRequired += (standaloneUniformBlock) ? 0 : alignedBlockSize;
202   }
203 }
204
205 void Program::SetGraphicsProgram(Graphics::UniquePtr<Graphics::Program>&& program, Render::UniformBufferManager& uniformBufferManager)
206 {
207   mGfxProgram = std::move(program);
208   BuildReflection(mGfxController.GetProgramReflection(*mGfxProgram.get()), uniformBufferManager);
209 }
210
211 bool Program::GetUniform(const std::string_view& name, Hash hashedName, Hash hashedNameNoArray, Graphics::UniformInfo& out) const
212 {
213   if(mReflection.empty())
214   {
215     return false;
216   }
217   DALI_ASSERT_DEBUG(hashedName != 0 && "GetUniform() hash is not set");
218
219   // If name contains a "]", but has nothing after, it's an element in an array,
220   // The reflection doesn't contain such elements, it only contains the name without square brackets
221   // Use the hash without array subscript.
222
223   // If the name contains a "]" anywhere but the end, it's a structure element. The reflection
224   // does contain such elements, so use normal hash.
225   Hash             hash  = hashedName;
226   std::string_view match = name;
227
228   if(!name.empty() && name.back() == ']')
229   {
230     hash     = hashedNameNoArray;
231     auto pos = name.rfind("[");
232     match    = name.substr(0, pos - 1); // Remove subscript
233   }
234
235   for(const ReflectionUniformInfo& item : mReflection)
236   {
237     if(item.hashValue == hash)
238     {
239       if(!item.hasCollision || item.uniformInfo.name == match)
240       {
241         out = item.uniformInfo;
242         return true;
243       }
244       else
245       {
246         return false;
247       }
248     }
249   }
250   return false;
251 }
252
253 const Graphics::UniformInfo* Program::GetDefaultUniform(DefaultUniformIndex defaultUniformIndex) const
254 {
255   if(mReflectionDefaultUniforms.empty())
256   {
257     return nullptr;
258   }
259
260   const auto value = &mReflectionDefaultUniforms[static_cast<uint32_t>(defaultUniformIndex)];
261   return &value->uniformInfo;
262 }
263
264 } // namespace Internal
265
266 } // namespace Dali