[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / public-api / loader / shader-manager.cpp
1 /*
2  * Copyright (c) 2024 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-scene3d/public-api/loader/shader-manager.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/common/map-wrapper.h>
23 #include <dali/public-api/animation/constraint.h>
24 #include <cstring>
25
26 // INTERNAL INCLUDES
27 #include <dali-scene3d/internal/light/light-impl.h>
28 #include <dali-scene3d/internal/loader/hash.h>
29 #include <dali-scene3d/public-api/loader/blend-shape-details.h>
30 #include <dali-scene3d/public-api/loader/node-definition.h>
31
32 #include <dali/integration-api/debug.h>
33
34 namespace
35 {
36 #if defined(DEBUG_ENABLED)
37 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_MODEL_SHADER_MANAGER");
38 #endif
39 } // namespace
40
41 namespace Dali::Scene3D::Loader
42 {
43 namespace
44 {
45 static constexpr uint32_t INDEX_FOR_LIGHT_CONSTRAINT_TAG  = 10;
46 static constexpr uint32_t INDEX_FOR_SHADOW_CONSTRAINT_TAG = 100;
47
48 constexpr uint32_t MINIMUM_SHADER_VERSION_SUPPORT_TEXTURE_TEXEL_AND_SIZE = 300;
49
50 ShaderOption MakeOption(const MaterialDefinition& materialDef, const MeshDefinition& meshDef)
51 {
52   ShaderOption option;
53
54   const bool hasTransparency = MaskMatch(materialDef.mFlags, MaterialDefinition::TRANSPARENCY);
55   if(hasTransparency)
56   {
57     option.SetTransparency();
58   }
59
60   if(hasTransparency ||
61      !materialDef.CheckTextures(MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC) ||
62      !materialDef.CheckTextures(MaterialDefinition::NORMAL | MaterialDefinition::ROUGHNESS))
63   {
64     option.AddOption(ShaderOption::Type::THREE_TEXTURE);
65
66     // For the glTF, each of basecolor, metallic_roughness, normal texture is not essential.
67     if(MaskMatch(materialDef.mFlags, MaterialDefinition::ALBEDO))
68     {
69       option.AddOption(ShaderOption::Type::BASE_COLOR_TEXTURE);
70     }
71
72     if(materialDef.CheckTextures(MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS))
73     {
74       option.AddOption(ShaderOption::Type::METALLIC_ROUGHNESS_TEXTURE);
75     }
76
77     if(MaskMatch(materialDef.mFlags, MaterialDefinition::NORMAL))
78     {
79       option.AddOption(ShaderOption::Type::NORMAL_TEXTURE);
80     }
81   }
82
83   if(materialDef.GetAlphaCutoff() > 0.f)
84   {
85     option.AddOption(ShaderOption::Type::ALPHA_TEST);
86   }
87
88   if(MaskMatch(materialDef.mFlags, MaterialDefinition::SUBSURFACE))
89   {
90     option.AddOption(ShaderOption::Type::SUBSURFACE);
91   }
92
93   if(MaskMatch(materialDef.mFlags, MaterialDefinition::OCCLUSION))
94   {
95     option.AddOption(ShaderOption::Type::OCCLUSION);
96   }
97
98   if(MaskMatch(materialDef.mFlags, MaterialDefinition::EMISSIVE))
99   {
100     option.AddOption(ShaderOption::Type::EMISSIVE);
101   }
102
103   if(MaskMatch(materialDef.mFlags, MaterialDefinition::SPECULAR))
104   {
105     option.AddOption(ShaderOption::Type::SPECULAR);
106   }
107
108   if(MaskMatch(materialDef.mFlags, MaterialDefinition::SPECULAR_COLOR))
109   {
110     option.AddOption(ShaderOption::Type::SPECULAR_COLOR);
111   }
112
113   if(MaskMatch(materialDef.mFlags, MaterialDefinition::GLTF_CHANNELS))
114   {
115     option.AddOption(ShaderOption::Type::GLTF_CHANNELS);
116   }
117
118   if(meshDef.IsSkinned())
119   {
120     option.AddOption(ShaderOption::Type::SKINNING);
121     option.AddJointMacros(meshDef.mJoints.size());
122   }
123   else
124   {
125     option.AddJointMacros(0);
126   }
127
128   if(MaskMatch(meshDef.mFlags, MeshDefinition::FLIP_UVS_VERTICAL))
129   {
130     option.AddOption(ShaderOption::Type::FLIP_UVS_VERTICAL);
131   }
132
133   if(!meshDef.mColors.empty() && meshDef.mColors[0].IsDefined())
134   {
135     option.AddOption(ShaderOption::Type::COLOR_ATTRIBUTE);
136   }
137
138   if(meshDef.mTangentType == Property::VECTOR4)
139   {
140     option.AddOption(ShaderOption::Type::VEC4_TANGENT);
141   }
142
143   if(meshDef.HasBlendShapes())
144   {
145     bool hasPositions = false;
146     bool hasNormals   = false;
147     bool hasTangents  = false;
148     meshDef.RetrieveBlendShapeComponents(hasPositions, hasNormals, hasTangents);
149     if(hasPositions)
150     {
151       option.AddOption(ShaderOption::Type::MORPH_POSITION);
152     }
153
154     if(hasNormals)
155     {
156       option.AddOption(ShaderOption::Type::MORPH_NORMAL);
157     }
158
159     if(hasTangents)
160     {
161       option.AddOption(ShaderOption::Type::MORPH_TANGENT);
162     }
163
164     if(hasPositions || hasNormals || hasTangents)
165     {
166       if(BlendShapes::Version::VERSION_2_0 == meshDef.mBlendShapeVersion)
167       {
168         option.AddOption(ShaderOption::Type::MORPH_VERSION_2_0);
169       }
170     }
171   }
172   if(DALI_UNLIKELY(Dali::Shader::GetShaderLanguageVersion() < MINIMUM_SHADER_VERSION_SUPPORT_TEXTURE_TEXEL_AND_SIZE))
173   {
174     option.AddOption(ShaderOption::Type::SL_VERSION_LOW);
175   }
176
177   return option;
178 }
179 } // namespace
180
181 struct ShaderManager::Impl
182 {
183   std::map<uint64_t, Index>   mShaderMap;
184   std::vector<Dali::Shader>   mShaders;
185   std::vector<Scene3D::Light> mLights;
186
187   Scene3D::Light mShadowLight;
188 };
189
190 ShaderManager::ShaderManager()
191 : mImpl{new Impl()}
192 {
193 }
194
195 ShaderManager::~ShaderManager() = default;
196
197 ShaderOption ShaderManager::ProduceShaderOption(const MaterialDefinition& materialDefinition, const MeshDefinition& meshDefinition)
198 {
199   DALI_LOG_INFO(gLogFilter, Debug::Concise, "Defining shader from mat/mesh definitions\n");
200   return MakeOption(materialDefinition, meshDefinition);
201 }
202
203 Dali::Shader ShaderManager::ProduceShader(const ShaderOption& shaderOption)
204 {
205   Dali::Shader result;
206
207   auto&    shaderMap = mImpl->mShaderMap;
208   uint64_t hash      = shaderOption.GetOptionHash();
209
210 #if defined(DEBUG_ENABLED)
211   std::ostringstream oss;
212   oss << "  ShaderOption defines:";
213   std::vector<std::string> defines;
214   shaderOption.GetDefines(defines);
215   for(auto& def : defines)
216   {
217     oss << def << ", ";
218   }
219   oss << std::endl
220       << "  ShaderOption macro definitions:" << std::endl;
221   for(auto& macro : shaderOption.GetMacroDefinitions())
222   {
223     oss << macro.macro << " : " << macro.definition << std::endl;
224   }
225   DALI_LOG_INFO(gLogFilter, Debug::Concise, "ShaderOption:\n%s\n", oss.str().c_str());
226 #endif
227
228   auto iFind = shaderMap.find(hash);
229   if(iFind != shaderMap.end())
230   {
231     DALI_LOG_INFO(gLogFilter, Debug::Concise, "Defining Shader found: hash: %lx", hash);
232     result = mImpl->mShaders[iFind->second];
233   }
234   else
235   {
236     DALI_LOG_INFO(gLogFilter, Debug::Concise, "Creating new shader: hash: %lx\n", hash);
237     ShaderDefinition shaderDef;
238     shaderDef.mUseBuiltInShader = true;
239
240     shaderOption.GetDefines(shaderDef.mDefines);
241     shaderDef.mMacros                  = shaderOption.GetMacroDefinitions();
242     shaderDef.mUniforms["uCubeMatrix"] = Matrix::IDENTITY;
243
244     shaderMap[hash] = mImpl->mShaders.size();
245
246     auto raw = shaderDef.LoadRaw("");
247     mImpl->mShaders.emplace_back(shaderDef.Load(std::move(raw)));
248     result = mImpl->mShaders.back();
249
250     std::string lightCountPropertyName(Scene3D::Internal::Light::GetLightCountUniformName());
251     result.RegisterProperty(lightCountPropertyName, static_cast<int32_t>(mImpl->mLights.size()));
252
253     for(uint32_t index = 0; index < mImpl->mLights.size(); ++index)
254     {
255       SetLightConstraintToShader(index, result);
256     }
257
258     result.RegisterProperty("uIsShadowEnabled", static_cast<int32_t>(!!mImpl->mShadowLight));
259     if(!!mImpl->mShadowLight)
260     {
261       SetShadowConstraintToShader(result);
262       SetShadowUniformToShader(result);
263     }
264   }
265
266   return result;
267 }
268
269 RendererState::Type ShaderManager::GetRendererState(const MaterialDefinition& materialDefinition)
270 {
271   RendererState::Type rendererState = RendererState::DEPTH_TEST;
272
273   if(!materialDefinition.mDoubleSided)
274   {
275     rendererState |= RendererState::CULL_BACK;
276   }
277
278   const bool hasTransparency = MaskMatch(materialDefinition.mFlags, MaterialDefinition::TRANSPARENCY);
279   if(hasTransparency)
280   {
281     // TODO: this requires more granularity
282     rendererState = (rendererState | RendererState::ALPHA_BLEND);
283   }
284   return rendererState;
285 }
286
287 bool ShaderManager::AddLight(Scene3D::Light light)
288 {
289   if(!light || mImpl->mLights.size() >= Scene3D::Internal::Light::GetMaximumEnabledLightCount())
290   {
291     return false;
292   }
293
294   uint32_t lightIndex = mImpl->mLights.size();
295   mImpl->mLights.push_back(light);
296
297   for(auto&& shader : mImpl->mShaders)
298   {
299     std::string lightCountPropertyName(Scene3D::Internal::Light::GetLightCountUniformName());
300     shader.RegisterProperty(lightCountPropertyName, static_cast<int32_t>(mImpl->mLights.size()));
301   }
302
303   SetLightConstraint(lightIndex);
304
305   return true;
306 }
307
308 void ShaderManager::RemoveLight(Scene3D::Light light)
309 {
310   uint32_t lightCount = mImpl->mLights.size();
311   for(uint32_t index = 0; index < lightCount; ++index)
312   {
313     if(mImpl->mLights[index] != light)
314     {
315       continue;
316     }
317
318     RemoveLightConstraint(index);
319
320     if(!mImpl->mLights.empty() && light != mImpl->mLights.back())
321     {
322       RemoveLightConstraint(mImpl->mLights.size() - 1);
323       mImpl->mLights[index] = mImpl->mLights.back();
324       SetLightConstraint(index);
325     }
326
327     mImpl->mLights.pop_back();
328
329     for(auto&& shader : mImpl->mShaders)
330     {
331       std::string lightCountPropertyName(Scene3D::Internal::Light::GetLightCountUniformName());
332       shader.RegisterProperty(lightCountPropertyName, static_cast<int32_t>(mImpl->mLights.size()));
333     }
334     break;
335   }
336 }
337
338 uint32_t ShaderManager::GetLightCount() const
339 {
340   return mImpl->mLights.size();
341 }
342
343 void ShaderManager::SetShadow(Scene3D::Light light)
344 {
345   mImpl->mShadowLight = light;
346   for(auto&& shader : mImpl->mShaders)
347   {
348     std::string shadowEnabledPropertyName(Scene3D::Internal::Light::GetShadowEnabledUniformName());
349     shader.RegisterProperty(shadowEnabledPropertyName, static_cast<int32_t>(true));
350   }
351
352   SetShadowProperty();
353 }
354
355 void ShaderManager::RemoveShadow()
356 {
357   for(auto&& shader : mImpl->mShaders)
358   {
359     std::string shadowEnabledPropertyName(Scene3D::Internal::Light::GetShadowEnabledUniformName());
360     shader.RegisterProperty(shadowEnabledPropertyName, static_cast<int32_t>(false));
361     shader.RemoveConstraints(INDEX_FOR_SHADOW_CONSTRAINT_TAG);
362   }
363   mImpl->mShadowLight.Reset();
364 }
365
366 void ShaderManager::UpdateShadowUniform(Scene3D::Light light)
367 {
368   if(light != mImpl->mShadowLight)
369   {
370     return;
371   }
372
373   for(auto&& shader : mImpl->mShaders)
374   {
375     SetShadowUniformToShader(shader);
376   }
377 }
378
379 void ShaderManager::SetLightConstraint(uint32_t lightIndex)
380 {
381   for(auto&& shader : mImpl->mShaders)
382   {
383     SetLightConstraintToShader(lightIndex, shader);
384   }
385 }
386
387 void ShaderManager::SetLightConstraintToShader(uint32_t lightIndex, Dali::Shader shader)
388 {
389   std::string lightDirectionPropertyName(Scene3D::Internal::Light::GetLightDirectionUniformName());
390   lightDirectionPropertyName += "[" + std::to_string(lightIndex) + "]";
391   auto             lightDirectionPropertyIndex = shader.RegisterProperty(lightDirectionPropertyName, Vector3::ZAXIS);
392   Dali::Constraint lightDirectionConstraint    = Dali::Constraint::New<Vector3>(shader, lightDirectionPropertyIndex, [](Vector3& output, const PropertyInputContainer& inputs) { output = inputs[0]->GetQuaternion().Rotate(Vector3::ZAXIS); });
393   lightDirectionConstraint.AddSource(Source{mImpl->mLights[lightIndex], Dali::Actor::Property::WORLD_ORIENTATION});
394   lightDirectionConstraint.ApplyPost();
395   lightDirectionConstraint.SetTag(INDEX_FOR_LIGHT_CONSTRAINT_TAG + lightIndex);
396
397   std::string lightColorPropertyName(Scene3D::Internal::Light::GetLightColorUniformName());
398   lightColorPropertyName += "[" + std::to_string(lightIndex) + "]";
399   auto             lightColorPropertyIndex = shader.RegisterProperty(lightColorPropertyName, Vector3(Color::WHITE));
400   Dali::Constraint lightColorConstraint    = Dali::Constraint::New<Vector3>(shader, lightColorPropertyIndex, [](Vector3& output, const PropertyInputContainer& inputs) { output = Vector3(inputs[0]->GetVector4()); });
401   lightColorConstraint.AddSource(Source{mImpl->mLights[lightIndex], Dali::Actor::Property::COLOR});
402   lightColorConstraint.ApplyPost();
403   lightColorConstraint.SetTag(INDEX_FOR_LIGHT_CONSTRAINT_TAG + lightIndex);
404 }
405
406 void ShaderManager::RemoveLightConstraint(uint32_t lightIndex)
407 {
408   for(auto&& shader : mImpl->mShaders)
409   {
410     shader.RemoveConstraints(INDEX_FOR_LIGHT_CONSTRAINT_TAG + lightIndex);
411   }
412 }
413
414 void ShaderManager::SetShadowUniformToShader(Dali::Shader shader)
415 {
416   shader.RegisterProperty("uShadowIntensity", mImpl->mShadowLight.GetShadowIntensity());
417   shader.RegisterProperty("uShadowBias", mImpl->mShadowLight.GetShadowBias());
418   shader.RegisterProperty("uEnableShadowSoftFiltering", static_cast<int>(mImpl->mShadowLight.IsShadowSoftFilteringEnabled()));
419 }
420
421 void ShaderManager::SetShadowProperty()
422 {
423   for(auto&& shader : mImpl->mShaders)
424   {
425     SetShadowUniformToShader(shader);
426     SetShadowConstraintToShader(shader);
427   }
428 }
429
430 void ShaderManager::SetShadowConstraintToShader(Dali::Shader shader)
431 {
432   // Constraint is applied before View/Projection Matrix is computed in update thread.
433   // So, it could show not plausible result if camera properties are changed discontinuesly.
434   // If we want to make it be synchronized, View/Projection matrix are needed to be conputed in below constraint.
435
436   std::string       shadowViewProjectionPropertyName(Scene3D::Internal::Light::GetShadowViewProjectionMatrixUniformName());
437   auto              shadowViewProjectionPropertyIndex = shader.RegisterProperty(shadowViewProjectionPropertyName, Matrix::IDENTITY);
438   Dali::CameraActor shadowLightCamera                 = Dali::Scene3D::Internal::GetImplementation(mImpl->mShadowLight).GetCamera();
439   auto              tempViewProjectionMatrixIndex     = shadowLightCamera.GetPropertyIndex("tempViewProjectionMatrix");
440   if(tempViewProjectionMatrixIndex != Dali::Property::INVALID_INDEX)
441   {
442     tempViewProjectionMatrixIndex = shadowLightCamera.RegisterProperty("tempViewProjectionMatrix", Matrix::IDENTITY);
443   }
444   Dali::Constraint shadowViewProjectionConstraint = Dali::Constraint::New<Matrix>(shader, shadowViewProjectionPropertyIndex, [](Matrix& output, const PropertyInputContainer& inputs) { output = inputs[0]->GetMatrix(); });
445   shadowViewProjectionConstraint.AddSource(Source{shadowLightCamera, tempViewProjectionMatrixIndex});
446   shadowViewProjectionConstraint.ApplyPost();
447   shadowViewProjectionConstraint.SetTag(INDEX_FOR_SHADOW_CONSTRAINT_TAG);
448 }
449
450 } // namespace Dali::Scene3D::Loader