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