[dali_2.3.26] Merge branch 'devel/master'
[platform/core/uifw/dali-core.git] / dali / internal / event / rendering / shader-impl.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/internal/event/rendering/shader-impl.h> // Dali::Internal::Shader
20
21 // INTERNAL INCLUDES
22 #include <dali/devel-api/scripting/scripting.h>
23 #include <dali/internal/event/common/property-helper.h> // DALI_PROPERTY_TABLE_BEGIN, DALI_PROPERTY, DALI_PROPERTY_TABLE_END
24 #include <dali/internal/event/common/thread-local-storage.h>
25 #include <dali/internal/event/effects/shader-factory.h>
26 #include <dali/internal/update/manager/update-manager.h>
27 #include <dali/public-api/object/type-registry.h>
28
29 namespace Dali
30 {
31 namespace Internal
32 {
33 namespace
34 {
35 /**
36  *            |name             |type    |writable|animatable|constraint-input|enum for index-checking|
37  */
38 DALI_PROPERTY_TABLE_BEGIN
39 DALI_PROPERTY("program", MAP, true, false, false, Dali::Shader::Property::PROGRAM)
40 DALI_PROPERTY_TABLE_END(DEFAULT_ACTOR_PROPERTY_START_INDEX, ShaderDefaultProperties)
41
42 Dali::Scripting::StringEnum ShaderHintsTable[] =
43   {{"NONE", Dali::Shader::Hint::NONE},
44    {"OUTPUT_IS_TRANSPARENT", Dali::Shader::Hint::OUTPUT_IS_TRANSPARENT},
45    {"MODIFIES_GEOMETRY", Dali::Shader::Hint::MODIFIES_GEOMETRY}};
46
47 const uint32_t ShaderHintsTableSize = static_cast<uint32_t>(sizeof(ShaderHintsTable) / sizeof(ShaderHintsTable[0]));
48
49 static constexpr uint32_t         DEFAULT_RENDER_PASS_TAG = 0u;
50 static constexpr std::string_view DEFAULT_SHADER_NAME     = "";
51
52 BaseHandle Create()
53 {
54   return Dali::BaseHandle();
55 }
56
57 TypeRegistration mType(typeid(Dali::Shader), typeid(Dali::Handle), Create, ShaderDefaultProperties);
58
59 #define TOKEN_STRING(x) (#x)
60
61 void AppendString(std::string& to, const std::string& append)
62 {
63   if(to.size())
64   {
65     to += ",";
66   }
67   to += append;
68 }
69
70 Property::Value HintString(const Dali::Shader::Hint::Value& hints)
71 {
72   std::string s;
73
74   if(hints == Dali::Shader::Hint::NONE)
75   {
76     s = "NONE";
77   }
78
79   if(hints & Dali::Shader::Hint::OUTPUT_IS_TRANSPARENT)
80   {
81     AppendString(s, "OUTPUT_IS_TRANSPARENT");
82   }
83
84   if(hints & Dali::Shader::Hint::MODIFIES_GEOMETRY)
85   {
86     AppendString(s, "MODIFIES_GEOMETRY");
87   }
88
89   return Property::Value(s);
90 }
91
92 void GetShaderData(const Property::Map& shaderMap, std::string& vertexShader, std::string& fragmentShader, uint32_t& renderPassTag, Dali::Shader::Hint::Value& hints, std::string& name)
93 {
94   hints         = Dali::Shader::Hint::NONE;
95   renderPassTag = DEFAULT_RENDER_PASS_TAG;
96   name          = DEFAULT_SHADER_NAME;
97
98   if(Property::Value* value = shaderMap.Find("vertex"))
99   {
100     vertexShader = value->Get<std::string>();
101   }
102
103   if(Property::Value* value = shaderMap.Find("fragment"))
104   {
105     fragmentShader = value->Get<std::string>();
106   }
107
108   if(Property::Value* value = shaderMap.Find("renderPassTag"))
109   {
110     renderPassTag = static_cast<uint32_t>(value->Get<int32_t>());
111   }
112
113   if(Property::Value* value = shaderMap.Find("name"))
114   {
115     name = value->Get<std::string>();
116   }
117
118   if(Property::Value* value = shaderMap.Find("hints"))
119   {
120     int32_t hintInteger = 0;
121     if(value->Get(hintInteger))
122     {
123       hints = static_cast<Dali::Shader::Hint::Value>(hintInteger);
124     }
125     else
126     {
127       static_cast<void>( // ignore return
128         Scripting::GetEnumeration<Dali::Shader::Hint::Value>(value->Get<std::string>().c_str(),
129                                                              ShaderHintsTable,
130                                                              ShaderHintsTableSize,
131                                                              hints));
132     }
133   }
134 }
135
136 } // unnamed namespace
137
138 ShaderPtr Shader::New(std::string_view          vertexShader,
139                       std::string_view          fragmentShader,
140                       Dali::Shader::Hint::Value hints,
141                       std::string_view          shaderName)
142 {
143   // create scene object first so it's guaranteed to exist for the event side
144   auto                             sceneObject = new SceneGraph::Shader();
145   OwnerPointer<SceneGraph::Shader> transferOwnership(sceneObject);
146   // pass the pointer to base for message passing
147   ShaderPtr shader(new Shader(sceneObject));
148   // transfer scene object ownership to update manager
149   auto&&                     services      = shader->GetEventThreadServices();
150   SceneGraph::UpdateManager& updateManager = services.GetUpdateManager();
151   AddShaderMessage(updateManager, transferOwnership);
152
153   services.RegisterObject(shader.Get());
154   shader->UpdateShaderData(vertexShader, fragmentShader, DEFAULT_RENDER_PASS_TAG, hints, shaderName);
155
156   return shader;
157 }
158
159 ShaderPtr Shader::New(Dali::Property::Value shaderMap)
160 {
161   // create scene object first so it's guaranteed to exist for the event side
162   auto                             sceneObject = new SceneGraph::Shader();
163   OwnerPointer<SceneGraph::Shader> transferOwnership(sceneObject);
164   // pass the pointer to base for message passing
165   ShaderPtr shader(new Shader(sceneObject));
166   // transfer scene object ownership to update manager
167   auto&&                     services      = shader->GetEventThreadServices();
168   SceneGraph::UpdateManager& updateManager = services.GetUpdateManager();
169   AddShaderMessage(updateManager, transferOwnership);
170
171   services.RegisterObject(shader.Get());
172   shader->SetShaderProperty(shaderMap);
173
174   return shader;
175 }
176
177 const SceneGraph::Shader& Shader::GetShaderSceneObject() const
178 {
179   return static_cast<const SceneGraph::Shader&>(GetSceneObject());
180 }
181
182 void Shader::SetDefaultProperty(Property::Index index, const Property::Value& propertyValue)
183 {
184   switch(index)
185   {
186     case Dali::Shader::Property::PROGRAM:
187     {
188       SetShaderProperty(propertyValue);
189       break;
190     }
191   }
192 }
193
194 Property::Value Shader::GetDefaultProperty(Property::Index index) const
195 {
196   Property::Value value;
197
198   switch(index)
199   {
200     case Dali::Shader::Property::PROGRAM:
201     {
202       if(mShaderDataList.size() == 1u)
203       {
204         Dali::Property::Map map;
205         map["vertex"]        = Property::Value(mShaderDataList.front()->GetVertexShader());
206         map["fragment"]      = Property::Value(mShaderDataList.front()->GetFragmentShader());
207         map["renderPassTag"] = Property::Value(static_cast<int32_t>(mShaderDataList.front()->GetRenderPassTag()));
208         map["hints"]         = HintString(mShaderDataList.front()->GetHints());
209         map["name"]          = Property::Value(mShaderDataList.front()->GetName());
210         value                = map;
211       }
212       else
213       {
214         Dali::Property::Array array;
215         for(auto&& shaderData : mShaderDataList)
216         {
217           if(shaderData)
218           {
219             Dali::Property::Map map;
220             map["vertex"]        = Property::Value(shaderData->GetVertexShader());
221             map["fragment"]      = Property::Value(shaderData->GetFragmentShader());
222             map["renderPassTag"] = Property::Value(static_cast<int32_t>(shaderData->GetRenderPassTag()));
223             map["hints"]         = HintString(shaderData->GetHints());
224             map["name"]          = Property::Value(shaderData->GetName());
225             array.PushBack(map);
226           }
227         }
228         value = array;
229       }
230       break;
231     }
232   }
233
234   return value;
235 }
236
237 Property::Value Shader::GetDefaultPropertyCurrentValue(Property::Index index) const
238 {
239   return GetDefaultProperty(index); // Event-side only properties
240 }
241
242 Shader::Shader(const SceneGraph::Shader* sceneObject)
243 : Object(sceneObject)
244 {
245 }
246
247 void Shader::UpdateShaderData(std::string_view          vertexSource,
248                               std::string_view          fragmentSource,
249                               uint32_t                  renderPassTag,
250                               Dali::Shader::Hint::Value hints,
251                               std::string_view          name)
252 {
253   // Try to load a pre-compiled shader binary for the source pair:
254   ThreadLocalStorage&     tls           = ThreadLocalStorage::Get();
255   ShaderFactory&          shaderFactory = tls.GetShaderFactory();
256   size_t                  shaderHash;
257   Internal::ShaderDataPtr shaderData = shaderFactory.Load(vertexSource, fragmentSource, hints, renderPassTag, name, shaderHash);
258
259   std::vector<Internal::ShaderDataPtr>::iterator shaderDataIterator = std::find_if(mShaderDataList.begin(), mShaderDataList.end(), [&shaderData](const Internal::ShaderDataPtr& shaderDataItem) { return shaderDataItem->GetRenderPassTag() == shaderData->GetRenderPassTag(); });
260   if(shaderDataIterator != mShaderDataList.end())
261   {
262     *shaderDataIterator = shaderData;
263   }
264   else
265   {
266     mShaderDataList.push_back(shaderData);
267   }
268
269   // Add shader data to scene-object
270   SceneGraph::UpdateShaderDataMessage(GetEventThreadServices(), GetShaderSceneObject(), shaderData);
271 }
272
273 void Shader::SetShaderProperty(const Dali::Property::Value& shaderMap)
274 {
275   if(shaderMap.GetType() == Property::MAP)
276   {
277     const Dali::Property::Map* map = shaderMap.GetMap();
278     if(map)
279     {
280       std::string               vertex;
281       std::string               fragment;
282       uint32_t                  renderPassTag{DEFAULT_RENDER_PASS_TAG};
283       Dali::Shader::Hint::Value hints(Dali::Shader::Hint::NONE);
284       std::string               name(DEFAULT_SHADER_NAME);
285       GetShaderData(*map, vertex, fragment, renderPassTag, hints, name);
286
287       UpdateShaderData(vertex, fragment, renderPassTag, hints, name);
288     }
289   }
290   else if(shaderMap.GetType() == Property::ARRAY)
291   {
292     const Dali::Property::Array* array = shaderMap.GetArray();
293     if(array)
294     {
295       uint32_t arraySize = array->Count();
296       for(uint32_t i = 0; i < arraySize; ++i)
297       {
298         const Dali::Property::Map* map = array->GetElementAt(i).GetMap();
299         if(map)
300         {
301           std::string               vertex;
302           std::string               fragment;
303           uint32_t                  renderPassTag{DEFAULT_RENDER_PASS_TAG};
304           Dali::Shader::Hint::Value hints(Dali::Shader::Hint::NONE);
305           std::string               name(DEFAULT_SHADER_NAME);
306           GetShaderData(*map, vertex, fragment, renderPassTag, hints, name);
307
308           UpdateShaderData(vertex, fragment, renderPassTag, hints, name);
309         }
310       }
311     }
312   }
313   else
314   {
315     DALI_LOG_ERROR("Shader program property should be a map or array of map.\n");
316   }
317 }
318
319 Shader::~Shader()
320 {
321   if(EventThreadServices::IsCoreRunning())
322   {
323     EventThreadServices&       eventThreadServices = GetEventThreadServices();
324     SceneGraph::UpdateManager& updateManager       = eventThreadServices.GetUpdateManager();
325     RemoveShaderMessage(updateManager, &GetShaderSceneObject());
326
327     eventThreadServices.UnregisterObject(this);
328   }
329 }
330
331 uint32_t Shader::GetShaderLanguageVersion()
332 {
333   Dali::Internal::ThreadLocalStorage& tls = Dali::Internal::ThreadLocalStorage::Get();
334   return tls.GetShaderLanguageVersion();
335 }
336
337 std::string Shader::GetShaderVersionPrefix()
338 {
339   Dali::Internal::ThreadLocalStorage& tls = Dali::Internal::ThreadLocalStorage::Get();
340   return tls.GetShaderVersionPrefix();
341 }
342
343 std::string Shader::GetVertexShaderPrefix()
344 {
345   Dali::Internal::ThreadLocalStorage& tls = Dali::Internal::ThreadLocalStorage::Get();
346   return GenerateTaggedShaderPrefix(tls.GetVertexShaderPrefix());
347 }
348
349 std::string Shader::GetFragmentShaderPrefix()
350 {
351   Dali::Internal::ThreadLocalStorage& tls = Dali::Internal::ThreadLocalStorage::Get();
352   return GenerateTaggedShaderPrefix(tls.GetFragmentShaderPrefix());
353 }
354
355 std::string Shader::GenerateTaggedShaderPrefix(const std::string& shaderPrefix)
356 {
357   // The tag is added at the top of vertex/fragment shader and
358   // contains an offset where the source code starts after
359   // the prefix.
360   // This offset is used later by the graphics backend to ignore
361   // the legacy prefix if provided with new versioned shader.
362   // When shader contains tagged prefix, then it starts with:
363   // "//@legacy-prefix-end <offset>" tag.
364   static const std::string TAG           = "//@legacy-prefix-end ";
365   const uint32_t           OFFSET_DIGITS = 5; // offset allocates 5 digits
366
367   auto prefix       = std::string(TAG + "00000\n") + shaderPrefix;
368   auto prefixLength = prefix.length();
369   char tmp          = *(prefix.data() + TAG.length() + OFFSET_DIGITS);
370   std::snprintf(prefix.data() + TAG.size(), OFFSET_DIGITS + 1, "%05d", int(prefixLength));
371   *(prefix.data() + TAG.length() + OFFSET_DIGITS) = tmp;
372   return prefix;
373 }
374
375 } // namespace Internal
376 } // namespace Dali