2 * Copyright (c) 2024 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include <dali-toolkit/internal/visuals/image-visual-shader-debug.h>
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>
25 #include <regex> ///< for redefine shader
26 #include <string_view>
28 #include <dali/integration-api/debug.h>
31 #include <dali-toolkit/devel-api/asset-manager/asset-manager.h>
32 #include <dali-toolkit/devel-api/builder/json-parser.h>
42 constexpr auto DALI_DEBUG_IMAGE_VISUAL_SHADER_ENV = "DALI_DEBUG_IMAGE_VISUAL_SHADER";
44 bool DebugImageVisualShaderEnvironmentEnabled()
46 static bool enabled = false;
47 static bool enabledSetted = false;
51 auto debugEnabledString = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_DEBUG_IMAGE_VISUAL_SHADER_ENV);
52 enabled = debugEnabledString ? std::atoi(debugEnabledString) : false;
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";
60 const std::string& GetScriptFilename()
62 // Set the full path for the default script file.
63 const static std::string styleDirPath{AssetManager::GetDaliStylePath()};
64 static std::string mScriptFileName{};
66 if(DALI_UNLIKELY(mScriptFileName.empty()))
68 // Use user's own script if exist.
69 auto environmentScriptFilename = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_DEBUG_IMAGE_VISUAL_SHADER_SCRIPT_FILE_NAME_ENV);
71 mScriptFileName = environmentScriptFilename ? std::string(environmentScriptFilename) : styleDirPath + std::string(DEFAULT_DEBUG_IMAGE_VISUAL_SHADER_SCRIPT_FILE_NAME);
73 DALI_ASSERT_DEBUG(0 != mScriptFileName.length());
76 return mScriptFileName;
79 bool LoadJsonScript(std::string& stringOut)
81 auto styleMonitor = StyleMonitor::Get();
83 // as toolkit is platform agnostic, it cannot load files from filesystem
84 // ask style monitor to load the style sheet
89 return styleMonitor.LoadThemeFile(GetScriptFilename(), stringOut);
91 catch(const std::exception& e)
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());
96 catch(const Dali::DaliException& e)
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);
103 DALI_LOG_ERROR("Something unkown exception throwed during load script file![%s]\n", GetScriptFilename().c_str());
109 // Json keywords what we will get information from json.
110 constexpr std::string_view DEBUG_SCRIPT_VERSION_JSON_KEY = "version";
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";
117 constexpr std::string_view MINIMUM_DEBUG_COLOR_RATE_JSON_KEY = "minimumColorRate";
118 constexpr std::string_view MAXIMUM_DEBUG_COLOR_RATE_JSON_KEY = "maximumColorRate";
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";
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";
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";
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";
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;";
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";
154 bool ParseScriptInfomation(Property::Map& vertexResult, Property::Map& fragmentResult)
156 std::string stringOut;
157 if(!LoadJsonScript(stringOut))
159 DALI_LOG_ERROR("Fail to load script file [%s]\n", GetScriptFilename().c_str());
163 Toolkit::JsonParser parser = Toolkit::JsonParser::New();
165 if(!parser.Parse(stringOut))
167 std::ostringstream stream;
168 if(parser.ParseError())
170 stream << "position: " << parser.GetErrorPosition() << ", line: " << parser.GetErrorLineNumber() << ", column: " << parser.GetErrorColumn() << ", description: " << parser.GetErrorDescription() << ".";
172 DALI_LOG_ERROR("Fail to parse json script\nError : %s\nJson : %s\n", stream.str().c_str(), stringOut.c_str());
176 const auto* rootNode = parser.GetRoot();
179 DALI_LOG_ERROR("Fail to get root node\n");
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;
189 const auto* childNode = node->GetChild(jsonKey);
193 if(childNode->GetType() == TreeNode::FLOAT)
195 oss << childNode->GetFloat();
197 else if(childNode->GetType() == TreeNode::STRING)
199 if(!prefixString.empty())
201 oss << prefixString << " ";
203 oss << childNode->GetString();
205 else if(childNode->GetType() == TreeNode::ARRAY)
207 // Concat strings with line feed
209 for(auto iter = childNode->CBegin(), endIter = childNode->CEnd(); iter != endIter; ++iter)
211 if((*iter).second.GetType() == TreeNode::STRING)
221 if(!prefixString.empty())
223 oss << prefixString << " ";
225 oss << (*iter).second.GetString();
232 if(oss.str().empty() && !defaultValue.empty())
237 if(!oss.str().empty())
239 result.Insert(std::string(macroKey), oss.str());
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);
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);
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);
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);
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);
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);
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);
275 const std::vector<Property::Map>& GetScriptInfomation()
277 static std::vector<Property::Map> results;
279 if(DALI_UNLIKELY(results.empty()))
283 auto& vertexShaderResult = results[0];
284 auto& fragmentShaderResult = results[1];
286 if(!ParseScriptInfomation(vertexShaderResult, fragmentShaderResult))
288 // Use default script information if parse failed.
289 vertexShaderResult.Clear();
290 fragmentShaderResult.Clear();
292 vertexShaderResult.Insert(std::string(DEBUG_APPLY_VARYINGS_CODE_MACRO_KEY), std::string(DEFAULT_DEBUG_APPLY_VARYINGS_CODE_MACRO_VALUE));
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));
308 void RedefineMacro(std::string& shaderCode, std::string macro, std::string value)
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!");
314 std::size_t insertionPoint = found + definition.length();
316 // Automatically insert line-continuation character into value
318 std::sregex_token_iterator first{value.begin(), value.end(), re, -1}, last;
319 for(auto i = first; i != last; ++i)
321 std::string line = std::string(" \\\n") + (*i).str();
322 shaderCode.insert(insertionPoint, line);
323 insertionPoint += line.length();
329 namespace ImageVisualShaderDebug
331 bool DebugImageVisualShaderEnabled()
333 return DebugImageVisualShaderEnvironmentEnabled();
336 void ApplyImageVisualShaderDebugScriptCode(std::string& vertexShader, std::string& fragmentShader)
338 const auto& resultMaps = GetScriptInfomation();
340 for(std::size_t i = 0u; i < resultMaps[0].Count(); ++i)
342 auto key = resultMaps[0].GetKeyAt(i);
343 const auto& value = resultMaps[0].GetValue(i);
345 RedefineMacro(vertexShader, std::move(key.stringKey), value.Get<std::string>());
348 for(std::size_t i = 0u; i < resultMaps[1].Count(); ++i)
350 auto key = resultMaps[1].GetKeyAt(i);
351 const auto& value = resultMaps[1].GetValue(i);
353 RedefineMacro(fragmentShader, std::move(key.stringKey), value.Get<std::string>());
356 } // namespace ImageVisualShaderDebug
358 } // namespace Internal
360 } // namespace Toolkit