[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / public-api / loader / shader-definition.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-definition.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/object/property-array.h>
23 #include <regex>
24
25 // INTERNAL INCLUDES
26 #include <dali-scene3d/internal/graphics/builtin-shader-extern-gen.h>
27 #include <dali-scene3d/public-api/loader/utils.h>
28
29 #include <dali/integration-api/debug.h>
30
31 namespace
32 {
33 #if defined(DEBUG_ENABLED)
34 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_MODEL_SHADER_DEFINITION");
35 #endif
36 } // namespace
37
38 namespace Dali::Scene3D::Loader
39 {
40 namespace
41 {
42 const char* SHADER_HINT_OUTPUT_IS_TRANSPARENT("OUTPUT_IS_TRANSPARENT"); ///< Might generate transparent alpha from opaque inputs.
43 const char* SHADER_HINT_MODIFIES_GEOMETRY("MODIFIES_GEOMETRY");         ///< Might change position of vertices, this option disables any culling optimizations.
44 } // namespace
45
46 ShaderDefinition::ShaderDefinition(const ShaderDefinition& other)
47 : mRendererState(other.mRendererState),
48   mVertexShaderPath(other.mVertexShaderPath),
49   mFragmentShaderPath(other.mFragmentShaderPath),
50   mDefines(other.mDefines),
51   mHints(other.mHints),
52   mUniforms(other.mUniforms),
53   mUseBuiltInShader(other.mUseBuiltInShader)
54 {
55 }
56
57 void ApplyDefine(std::string& shaderCode, const std::string& definevar)
58 {
59   const std::string IF_1 = "#if 1";
60
61   std::size_t found = shaderCode.find(definevar);
62   while(found != std::string::npos)
63   {
64     // Greater then "@" character means is a letter,
65     // therefore is not has the definevar we looking for.
66     if((found + definevar.length()) < shaderCode.length() && shaderCode.at(found + definevar.length()) > '@')
67     {
68       found = shaderCode.find(definevar, found + definevar.length());
69       continue;
70     }
71     if(found > 0u && shaderCode.at(found - 1) > '@')
72     {
73       found = shaderCode.find(definevar, found + definevar.length());
74       continue;
75     }
76
77     std::size_t defidx     = shaderCode.rfind("#ifdef", found);
78     std::size_t newlineidx = shaderCode.rfind("\n", found);
79     if(newlineidx < defidx && defidx != std::string::npos)
80     {
81       shaderCode.replace(defidx, found - defidx + definevar.length(), IF_1);
82       found = defidx + IF_1.length();
83     }
84     else
85     {
86       found += definevar.length();
87     }
88     found = shaderCode.find(definevar, found);
89   }
90 }
91
92 void RedefineMacro(std::string& shaderCode, const std::string& macro, const std::string& value)
93 {
94   if(!value.empty())
95   {
96     std::string definition = "#define " + macro;
97     std::size_t found      = shaderCode.find(definition);
98     if(found != std::string::npos)
99     {
100       std::size_t insertionPoint = found + definition.length();
101
102       // Automatically insert line-continuation character into value
103       std::regex                 re("\n");
104       std::sregex_token_iterator first{value.begin(), value.end(), re, -1}, last;
105       for(auto i = first; i != last; ++i)
106       {
107         std::string line = std::string(" \\\n") + (*i).str();
108         shaderCode.insert(insertionPoint, line);
109         insertionPoint += line.length();
110       }
111     }
112   }
113   else
114   {
115     std::size_t invocation = shaderCode.rfind(macro);
116     if(invocation != std::string::npos)
117     {
118       std::size_t start = shaderCode.rfind("\n", invocation);
119       std::size_t end   = shaderCode.find("\n", invocation);
120       shaderCode.erase(start, end - start);
121     }
122   }
123 }
124
125 ShaderDefinition::RawData
126 ShaderDefinition::LoadRaw(const std::string& shadersPath) const
127 {
128   RawData raw;
129
130   bool fail = false;
131   if(!mUseBuiltInShader)
132   {
133     raw.mVertexShaderSource = LoadTextFile((shadersPath + mVertexShaderPath).c_str(), &fail);
134     if(!fail)
135     {
136       raw.mFragmentShaderSource = LoadTextFile((shadersPath + mFragmentShaderPath).c_str(), &fail);
137       if(fail)
138       {
139         ExceptionFlinger(ASSERT_LOCATION) << "Failed to load shader source from '" << shadersPath + mFragmentShaderPath << "'.";
140       }
141     }
142     else
143     {
144       ExceptionFlinger(ASSERT_LOCATION) << "Failed to load shader source from '" << shadersPath + mVertexShaderPath << "'.";
145     }
146   }
147   else
148   {
149     raw.mVertexShaderSource         = Dali::Shader::GetVertexShaderPrefix() + SHADER_DEFAULT_PHYSICALLY_BASED_SHADER_VERT.data();
150     raw.mFragmentShaderSource       = Dali::Shader::GetFragmentShaderPrefix() + SHADER_DEFAULT_PHYSICALLY_BASED_SHADER_FRAG.data();
151     raw.mShadowVertexShaderSource   = Dali::Shader::GetVertexShaderPrefix() + SHADER_SHADOW_MAP_SHADER_VERT.data();
152     raw.mShadowFragmentShaderSource = Dali::Shader::GetFragmentShaderPrefix() + SHADER_SHADOW_MAP_SHADER_FRAG.data();
153   }
154
155   if(!fail)
156   {
157     for(const auto& definevar : mDefines)
158     {
159       ApplyDefine(raw.mVertexShaderSource, definevar);
160       ApplyDefine(raw.mFragmentShaderSource, definevar);
161       ApplyDefine(raw.mShadowVertexShaderSource, definevar);
162       ApplyDefine(raw.mShadowFragmentShaderSource, definevar);
163     }
164     for(const auto& macroDef : mMacros)
165     {
166       RedefineMacro(raw.mVertexShaderSource, macroDef.macro, macroDef.definition);
167       RedefineMacro(raw.mFragmentShaderSource, macroDef.macro, macroDef.definition);
168       RedefineMacro(raw.mShadowVertexShaderSource, macroDef.macro, macroDef.definition);
169       RedefineMacro(raw.mShadowFragmentShaderSource, macroDef.macro, macroDef.definition);
170     }
171   }
172
173   return raw;
174 }
175
176 Shader ShaderDefinition::Load(RawData&& raw) const
177 {
178   uint32_t hints = Shader::Hint::NONE;
179   for(const auto& hint : mHints)
180   {
181     if(hint == SHADER_HINT_OUTPUT_IS_TRANSPARENT)
182     {
183       hints |= Shader::Hint::OUTPUT_IS_TRANSPARENT;
184     }
185     else if(hint == SHADER_HINT_MODIFIES_GEOMETRY)
186     {
187       hints |= Shader::Hint::MODIFIES_GEOMETRY;
188     }
189   }
190
191   Property::Map map[2];
192   map[0]["vertex"]        = raw.mVertexShaderSource;
193   map[0]["fragment"]      = raw.mFragmentShaderSource;
194   map[0]["renderPassTag"] = 0;
195   map[0]["hints"]         = static_cast<Shader::Hint::Value>(hints);
196   map[0]["name"]          = "SCENE3D_PBR";
197
198   map[1]["vertex"]        = raw.mShadowVertexShaderSource;
199   map[1]["fragment"]      = raw.mShadowFragmentShaderSource;
200   map[1]["renderPassTag"] = 10;
201   map[1]["name"]          = "SCENE3D_SHADOW_MAP";
202
203   Property::Array array;
204   array.PushBack(map[0]);
205   array.PushBack(map[1]);
206
207   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Vert Shader src: \n%s\n", raw.mVertexShaderSource.c_str());
208   Shader shader = Shader::New(array);
209   for(Property::Map::SizeType i0 = 0, i1 = mUniforms.Count(); i0 != i1; ++i0)
210   {
211     auto pair = mUniforms.GetKeyValue(i0);
212     DALI_ASSERT_ALWAYS(pair.first.type == Property::Key::STRING);
213     shader.RegisterProperty(pair.first.stringKey, pair.second);
214   }
215
216   return shader;
217 }
218
219 } // namespace Dali::Scene3D::Loader