[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / image-visual-shader-debug.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 // CLASS HEADER
18 #include <dali-toolkit/internal/visuals/image-visual-shader-debug.h>
19
20 // EXTERNAL INCLUDES
21 #include <dali/devel-api/adaptor-framework/environment-variable.h>
22 #include <dali/devel-api/adaptor-framework/style-monitor.h> ///< for load json file.
23 #include <dali/public-api/common/vector-wrapper.h>
24
25 #include <regex> ///< for redefine shader
26 #include <string_view>
27
28 #include <dali/integration-api/debug.h>
29
30 // INTERNAL INCLUDES
31 #include <dali-toolkit/devel-api/asset-manager/asset-manager.h>
32 #include <dali-toolkit/devel-api/builder/json-parser.h>
33
34 namespace Dali
35 {
36 namespace Toolkit
37 {
38 namespace Internal
39 {
40 namespace
41 {
42 constexpr auto DALI_DEBUG_IMAGE_VISUAL_SHADER_ENV = "DALI_DEBUG_IMAGE_VISUAL_SHADER";
43
44 bool DebugImageVisualShaderEnvironmentEnabled()
45 {
46   static bool enabled       = false;
47   static bool enabledSetted = false;
48   if(!enabledSetted)
49   {
50     enabledSetted           = true;
51     auto debugEnabledString = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_DEBUG_IMAGE_VISUAL_SHADER_ENV);
52     enabled                 = debugEnabledString ? std::atoi(debugEnabledString) : false;
53   }
54   return enabled;
55 }
56
57 constexpr auto DALI_DEBUG_IMAGE_VISUAL_SHADER_SCRIPT_FILE_NAME_ENV = "DALI_DEBUG_IMAGE_VISUAL_SHADER_SCRIPT_FILE_NAME";
58 constexpr auto DEFAULT_DEBUG_IMAGE_VISUAL_SHADER_SCRIPT_FILE_NAME  = "debug-image-visual-shader-script.json";
59
60 const std::string& GetScriptFilename()
61 {
62   // Set the full path for the default script file.
63   const static std::string styleDirPath{AssetManager::GetDaliStylePath()};
64   static std::string       mScriptFileName{};
65
66   if(DALI_UNLIKELY(mScriptFileName.empty()))
67   {
68     // Use user's own script if exist.
69     auto environmentScriptFilename = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_DEBUG_IMAGE_VISUAL_SHADER_SCRIPT_FILE_NAME_ENV);
70
71     mScriptFileName = environmentScriptFilename ? std::string(environmentScriptFilename) : styleDirPath + std::string(DEFAULT_DEBUG_IMAGE_VISUAL_SHADER_SCRIPT_FILE_NAME);
72
73     DALI_ASSERT_DEBUG(0 != mScriptFileName.length());
74   }
75
76   return mScriptFileName;
77 }
78
79 bool LoadJsonScript(std::string& stringOut)
80 {
81   auto styleMonitor = StyleMonitor::Get();
82
83   // as toolkit is platform agnostic, it cannot load files from filesystem
84   // ask style monitor to load the style sheet
85   if(styleMonitor)
86   {
87     try
88     {
89       return styleMonitor.LoadThemeFile(GetScriptFilename(), stringOut);
90     }
91     catch(const std::exception& e)
92     {
93       DALI_LOG_ERROR("Something system exception throwed during load script file![%s]\n", GetScriptFilename().c_str());
94       DALI_LOG_ERROR("Error message : [%s]\n", e.what());
95     }
96     catch(const Dali::DaliException& e)
97     {
98       DALI_LOG_ERROR("Something Dali exception throwed during load script file![%s]\n", GetScriptFilename().c_str());
99       DALI_LOG_ERROR("Error message : [%s]\n", e.condition);
100     }
101     catch(...)
102     {
103       DALI_LOG_ERROR("Something unkown exception throwed during load script file![%s]\n", GetScriptFilename().c_str());
104     }
105   }
106
107   return false;
108 }
109 // Json keywords what we will get information from json.
110 constexpr std::string_view DEBUG_SCRIPT_VERSION_JSON_KEY = "version";
111
112 constexpr std::string_view DEBUG_EXTRA_ATTRIBUTES_JSON_KEY    = "extraAttributes";
113 constexpr std::string_view DEBUG_EXTRA_VARYINGS_JSON_KEY      = "extraVaryings";
114 constexpr std::string_view DEBUG_EXTRA_UNIFORMS_JSON_KEY      = "extraUniforms";
115 constexpr std::string_view DEBUG_APPLY_VARYINGS_CODE_JSON_KEY = "applyVaryingsCode";
116
117 constexpr std::string_view MINIMUM_DEBUG_COLOR_RATE_JSON_KEY = "minimumColorRate";
118 constexpr std::string_view MAXIMUM_DEBUG_COLOR_RATE_JSON_KEY = "maximumColorRate";
119
120 constexpr std::string_view DEBUG_RED_CHANNEL_CODE_JSON_KEY   = "redChannelCodes";
121 constexpr std::string_view DEBUG_GREEN_CHANNEL_CODE_JSON_KEY = "greenChannelCodes";
122 constexpr std::string_view DEBUG_BLUE_CHANNEL_CODE_JSON_KEY  = "blueChannelCodes";
123 constexpr std::string_view DEBUG_TRIGGER_CODE_JSON_KEY       = "triggerCode";
124 constexpr std::string_view DEBUG_RATIO_CODE_JSON_KEY         = "ratioCode";
125
126 // Macro keywords what we will replace at vertex/fragment shader.
127 constexpr std::string_view DEBUG_EXTRA_ATTRIBUTES_MACRO_KEY    = "DEBUG_EXTRA_ATTRIBUTES";
128 constexpr std::string_view DEBUG_EXTRA_VARYINGS_MACRO_KEY      = "DEBUG_EXTRA_VARYINGS";
129 constexpr std::string_view DEBUG_EXTRA_UNIFORMS_MACRO_KEY      = "DEBUG_EXTRA_UNIFORMS";
130 constexpr std::string_view DEBUG_APPLY_VARYINGS_CODE_MACRO_KEY = "DEBUG_APPLY_VARYING_CODE";
131
132 constexpr std::string_view MINIMUM_DEBUG_COLOR_RATE_MACRO_KEY = "MINIMUM_DEBUG_COLOR_RATE";
133 constexpr std::string_view MAXIMUM_DEBUG_COLOR_RATE_MACRO_KEY = "MAXIMUM_DEBUG_COLOR_RATE";
134
135 constexpr std::string_view DEBUG_TRIGGER_RED_CODE_MACRO_KEY   = "DEBUG_TRIGGER_RED_CODE";
136 constexpr std::string_view DEBUG_TRIGGER_GREEN_CODE_MACRO_KEY = "DEBUG_TRIGGER_GREEN_CODE";
137 constexpr std::string_view DEBUG_TRIGGER_BLUE_CODE_MACRO_KEY  = "DEBUG_TRIGGER_BLUE_CODE";
138 constexpr std::string_view DEBUG_RATIO_RED_CODE_MACRO_KEY     = "DEBUG_RATIO_RED_CODE";
139 constexpr std::string_view DEBUG_RATIO_GREEN_CODE_MACRO_KEY   = "DEBUG_RATIO_GREEN_CODE";
140 constexpr std::string_view DEBUG_RATIO_BLUE_CODE_MACRO_KEY    = "DEBUG_RATIO_BLUE_CODE";
141
142 // Default macro keywords when we fail to parse script.
143 constexpr std::string_view DEFAULT_DEBUG_COLOR_RATE_MACRO_VALUE          = "0.0";
144 constexpr std::string_view DEFAULT_DEBUG_TRIGGER_CODE_MACRO_VALUE        = "return false;";
145 constexpr std::string_view DEFAULT_DEBUG_RATIO_CODE_MACRO_VALUE          = "return 0.0;";
146 constexpr std::string_view DEFAULT_DEBUG_APPLY_VARYINGS_CODE_MACRO_VALUE = "return;";
147
148 constexpr std::string_view EMPTY_STRING                    = "";
149 constexpr std::string_view VERTEX_SHADER_ATTRIBUTES_PREFIX = "INPUT";
150 constexpr std::string_view VERTEX_SHADER_VARYINGS_PREFIX   = "OUTPUT";
151 constexpr std::string_view FRAGMENT_SHADER_VARYINGS_PREFIX = "INPUT";
152 constexpr std::string_view UNIFORMS_PREFIX                 = "uniform";
153
154 bool ParseScriptInfomation(Property::Map& vertexResult, Property::Map& fragmentResult)
155 {
156   std::string stringOut;
157   if(!LoadJsonScript(stringOut))
158   {
159     DALI_LOG_ERROR("Fail to load script file [%s]\n", GetScriptFilename().c_str());
160     return false;
161   }
162
163   Toolkit::JsonParser parser = Toolkit::JsonParser::New();
164
165   if(!parser.Parse(stringOut))
166   {
167     std::ostringstream stream;
168     if(parser.ParseError())
169     {
170       stream << "position: " << parser.GetErrorPosition() << ", line: " << parser.GetErrorLineNumber() << ", column: " << parser.GetErrorColumn() << ", description: " << parser.GetErrorDescription() << ".";
171     }
172     DALI_LOG_ERROR("Fail to parse json script\nError : %s\nJson : %s\n", stream.str().c_str(), stringOut.c_str());
173     return false;
174   }
175
176   const auto* rootNode = parser.GetRoot();
177   if(!rootNode)
178   {
179     DALI_LOG_ERROR("Fail to get root node\n");
180     return false;
181   }
182
183   auto InsertScriptMap = [](Property::Map& result, const TreeNode* node, const std::string_view& jsonKey, const std::string_view& macroKey, const std::string_view& defaultValue, const std::string_view& prefixString) {
184     std::ostringstream oss;
185     oss.clear();
186
187     if(node)
188     {
189       const auto* childNode = node->GetChild(jsonKey);
190
191       if(childNode)
192       {
193         if(childNode->GetType() == TreeNode::FLOAT)
194         {
195           oss << childNode->GetFloat();
196         }
197         else if(childNode->GetType() == TreeNode::STRING)
198         {
199           if(!prefixString.empty())
200           {
201             oss << prefixString << " ";
202           }
203           oss << childNode->GetString();
204         }
205         else if(childNode->GetType() == TreeNode::ARRAY)
206         {
207           // Concat strings with line feed
208           bool isFirst = true;
209           for(auto iter = childNode->CBegin(), endIter = childNode->CEnd(); iter != endIter; ++iter)
210           {
211             if((*iter).second.GetType() == TreeNode::STRING)
212             {
213               if(isFirst)
214               {
215                 isFirst = false;
216               }
217               else
218               {
219                 oss << "\n";
220               }
221               if(!prefixString.empty())
222               {
223                 oss << prefixString << " ";
224               }
225               oss << (*iter).second.GetString();
226             }
227           }
228         }
229       }
230     }
231
232     if(oss.str().empty() && !defaultValue.empty())
233     {
234       oss << defaultValue;
235     }
236
237     if(!oss.str().empty())
238     {
239       result.Insert(std::string(macroKey), oss.str());
240     }
241   };
242
243   auto InsertChannelScriptMap = [&InsertScriptMap](Property::Map& result, const TreeNode* node, const std::string_view& channelJsonKey, const std::string_view& triggerMacroKey, const std::string_view& ratioMacroKey) {
244     const auto* channelNode = node->GetChild(channelJsonKey);
245     InsertScriptMap(result, channelNode, DEBUG_TRIGGER_CODE_JSON_KEY, triggerMacroKey, DEFAULT_DEBUG_TRIGGER_CODE_MACRO_VALUE, EMPTY_STRING);
246     InsertScriptMap(result, channelNode, DEBUG_RATIO_CODE_JSON_KEY, ratioMacroKey, DEFAULT_DEBUG_RATIO_CODE_MACRO_VALUE, EMPTY_STRING);
247   };
248
249   // Get attribute value code
250   InsertScriptMap(vertexResult, rootNode, DEBUG_EXTRA_ATTRIBUTES_JSON_KEY, DEBUG_EXTRA_ATTRIBUTES_MACRO_KEY, EMPTY_STRING, VERTEX_SHADER_ATTRIBUTES_PREFIX);
251
252   // Get varying value code
253   InsertScriptMap(vertexResult, rootNode, DEBUG_EXTRA_VARYINGS_JSON_KEY, DEBUG_EXTRA_VARYINGS_MACRO_KEY, EMPTY_STRING, VERTEX_SHADER_VARYINGS_PREFIX);
254   InsertScriptMap(fragmentResult, rootNode, DEBUG_EXTRA_VARYINGS_JSON_KEY, DEBUG_EXTRA_VARYINGS_MACRO_KEY, EMPTY_STRING, FRAGMENT_SHADER_VARYINGS_PREFIX);
255
256   // Get uniform value code
257   InsertScriptMap(vertexResult, rootNode, DEBUG_EXTRA_UNIFORMS_JSON_KEY, DEBUG_EXTRA_UNIFORMS_MACRO_KEY, EMPTY_STRING, UNIFORMS_PREFIX);
258   InsertScriptMap(fragmentResult, rootNode, DEBUG_EXTRA_UNIFORMS_JSON_KEY, DEBUG_EXTRA_UNIFORMS_MACRO_KEY, EMPTY_STRING, UNIFORMS_PREFIX);
259
260   // Get apply varying code
261   InsertScriptMap(vertexResult, rootNode, DEBUG_APPLY_VARYINGS_CODE_JSON_KEY, DEBUG_APPLY_VARYINGS_CODE_MACRO_KEY, DEFAULT_DEBUG_APPLY_VARYINGS_CODE_MACRO_VALUE, EMPTY_STRING);
262
263   // Get color rate
264   InsertScriptMap(fragmentResult, rootNode, MINIMUM_DEBUG_COLOR_RATE_JSON_KEY, MINIMUM_DEBUG_COLOR_RATE_MACRO_KEY, DEFAULT_DEBUG_COLOR_RATE_MACRO_VALUE, EMPTY_STRING);
265   InsertScriptMap(fragmentResult, rootNode, MAXIMUM_DEBUG_COLOR_RATE_JSON_KEY, MAXIMUM_DEBUG_COLOR_RATE_MACRO_KEY, DEFAULT_DEBUG_COLOR_RATE_MACRO_VALUE, EMPTY_STRING);
266
267   // Get each color ChannelCodes
268   InsertChannelScriptMap(fragmentResult, rootNode, DEBUG_RED_CHANNEL_CODE_JSON_KEY, DEBUG_TRIGGER_RED_CODE_MACRO_KEY, DEBUG_RATIO_RED_CODE_MACRO_KEY);
269   InsertChannelScriptMap(fragmentResult, rootNode, DEBUG_GREEN_CHANNEL_CODE_JSON_KEY, DEBUG_TRIGGER_GREEN_CODE_MACRO_KEY, DEBUG_RATIO_GREEN_CODE_MACRO_KEY);
270   InsertChannelScriptMap(fragmentResult, rootNode, DEBUG_BLUE_CHANNEL_CODE_JSON_KEY, DEBUG_TRIGGER_BLUE_CODE_MACRO_KEY, DEBUG_RATIO_BLUE_CODE_MACRO_KEY);
271
272   return true;
273 }
274
275 const std::vector<Property::Map>& GetScriptInfomation()
276 {
277   static std::vector<Property::Map> results;
278
279   if(DALI_UNLIKELY(results.empty()))
280   {
281     results.resize(2);
282
283     auto& vertexShaderResult   = results[0];
284     auto& fragmentShaderResult = results[1];
285
286     if(!ParseScriptInfomation(vertexShaderResult, fragmentShaderResult))
287     {
288       // Use default script information if parse failed.
289       vertexShaderResult.Clear();
290       fragmentShaderResult.Clear();
291
292       vertexShaderResult.Insert(std::string(DEBUG_APPLY_VARYINGS_CODE_MACRO_KEY), std::string(DEFAULT_DEBUG_APPLY_VARYINGS_CODE_MACRO_VALUE));
293
294       fragmentShaderResult.Insert(std::string(MINIMUM_DEBUG_COLOR_RATE_MACRO_KEY), std::string(DEFAULT_DEBUG_COLOR_RATE_MACRO_VALUE));
295       fragmentShaderResult.Insert(std::string(MAXIMUM_DEBUG_COLOR_RATE_MACRO_KEY), std::string(DEFAULT_DEBUG_COLOR_RATE_MACRO_VALUE));
296       fragmentShaderResult.Insert(std::string(DEBUG_TRIGGER_RED_CODE_MACRO_KEY), std::string(DEFAULT_DEBUG_TRIGGER_CODE_MACRO_VALUE));
297       fragmentShaderResult.Insert(std::string(DEBUG_TRIGGER_GREEN_CODE_MACRO_KEY), std::string(DEFAULT_DEBUG_TRIGGER_CODE_MACRO_VALUE));
298       fragmentShaderResult.Insert(std::string(DEBUG_TRIGGER_BLUE_CODE_MACRO_KEY), std::string(DEFAULT_DEBUG_TRIGGER_CODE_MACRO_VALUE));
299       fragmentShaderResult.Insert(std::string(DEBUG_RATIO_RED_CODE_MACRO_KEY), std::string(DEFAULT_DEBUG_RATIO_CODE_MACRO_VALUE));
300       fragmentShaderResult.Insert(std::string(DEBUG_RATIO_GREEN_CODE_MACRO_KEY), std::string(DEFAULT_DEBUG_RATIO_CODE_MACRO_VALUE));
301       fragmentShaderResult.Insert(std::string(DEBUG_RATIO_BLUE_CODE_MACRO_KEY), std::string(DEFAULT_DEBUG_RATIO_CODE_MACRO_VALUE));
302     }
303   }
304
305   return results;
306 }
307
308 void RedefineMacro(std::string& shaderCode, std::string macro, std::string value)
309 {
310   std::string definition = "#define " + macro;
311   std::size_t found      = shaderCode.find(definition);
312   DALI_ASSERT_ALWAYS(found != std::string::npos && "Macro keyword was not exist in shader code!");
313
314   std::size_t insertionPoint = found + definition.length();
315
316   // Automatically insert line-continuation character into value
317   std::regex                 re("\n");
318   std::sregex_token_iterator first{value.begin(), value.end(), re, -1}, last;
319   for(auto i = first; i != last; ++i)
320   {
321     std::string line = std::string(" \\\n") + (*i).str();
322     shaderCode.insert(insertionPoint, line);
323     insertionPoint += line.length();
324   }
325 }
326
327 } // namespace
328
329 namespace ImageVisualShaderDebug
330 {
331 bool DebugImageVisualShaderEnabled()
332 {
333   return DebugImageVisualShaderEnvironmentEnabled();
334 }
335
336 void ApplyImageVisualShaderDebugScriptCode(std::string& vertexShader, std::string& fragmentShader)
337 {
338   const auto& resultMaps = GetScriptInfomation();
339
340   for(std::size_t i = 0u; i < resultMaps[0].Count(); ++i)
341   {
342     auto        key   = resultMaps[0].GetKeyAt(i);
343     const auto& value = resultMaps[0].GetValue(i);
344
345     RedefineMacro(vertexShader, std::move(key.stringKey), value.Get<std::string>());
346   }
347
348   for(std::size_t i = 0u; i < resultMaps[1].Count(); ++i)
349   {
350     auto        key   = resultMaps[1].GetKeyAt(i);
351     const auto& value = resultMaps[1].GetValue(i);
352
353     RedefineMacro(fragmentShader, std::move(key.stringKey), value.Get<std::string>());
354   }
355 }
356 } // namespace ImageVisualShaderDebug
357
358 } // namespace Internal
359
360 } // namespace Toolkit
361
362 } // namespace Dali