Merge changes I1b1abc2c,I47083821,Icbe72d36,I6292beb0,I14b750ac, ... into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / public-api / loader / shader-definition.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-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   std::string definition = "#define " + macro;
95   std::size_t found      = shaderCode.find(definition);
96   if(found != std::string::npos)
97   {
98     std::size_t insertionPoint = found + definition.length();
99
100     // Automatically insert line-continuation character into value
101     std::regex                 re("\n");
102     std::sregex_token_iterator first{value.begin(), value.end(), re, -1}, last;
103     for(auto i = first; i != last; ++i)
104     {
105       std::string line = std::string(" \\\n") + (*i).str();
106       shaderCode.insert(insertionPoint, line);
107       insertionPoint += line.length();
108     }
109   }
110 }
111
112 ShaderDefinition::RawData
113 ShaderDefinition::LoadRaw(const std::string& shadersPath) const
114 {
115   RawData raw;
116
117   bool fail = false;
118   if(!mUseBuiltInShader)
119   {
120     raw.mVertexShaderSource = LoadTextFile((shadersPath + mVertexShaderPath).c_str(), &fail);
121     if(!fail)
122     {
123       raw.mFragmentShaderSource = LoadTextFile((shadersPath + mFragmentShaderPath).c_str(), &fail);
124       if(fail)
125       {
126         ExceptionFlinger(ASSERT_LOCATION) << "Failed to load shader source from '" << shadersPath + mFragmentShaderPath << "'.";
127       }
128     }
129     else
130     {
131       ExceptionFlinger(ASSERT_LOCATION) << "Failed to load shader source from '" << shadersPath + mVertexShaderPath << "'.";
132     }
133   }
134   else
135   {
136     raw.mVertexShaderSource         = SHADER_DEFAULT_PHYSICALLY_BASED_SHADER_VERT.data();
137     raw.mFragmentShaderSource       = SHADER_DEFAULT_PHYSICALLY_BASED_SHADER_FRAG.data();
138     raw.mShadowVertexShaderSource   = SHADER_SHADOW_MAP_SHADER_VERT.data();
139     raw.mShadowFragmentShaderSource = SHADER_SHADOW_MAP_SHADER_FRAG.data();
140   }
141
142   if(!fail)
143   {
144     for(const auto& definevar : mDefines)
145     {
146       ApplyDefine(raw.mVertexShaderSource, definevar);
147       ApplyDefine(raw.mFragmentShaderSource, definevar);
148       ApplyDefine(raw.mShadowVertexShaderSource, definevar);
149     }
150     for(const auto& macroDef : mMacros)
151     {
152       RedefineMacro(raw.mVertexShaderSource, macroDef.macro, macroDef.definition);
153       RedefineMacro(raw.mFragmentShaderSource, macroDef.macro, macroDef.definition);
154       RedefineMacro(raw.mShadowVertexShaderSource, macroDef.macro, macroDef.definition);
155     }
156   }
157
158   return raw;
159 }
160
161 Shader ShaderDefinition::Load(RawData&& raw) const
162 {
163   uint32_t hints = Shader::Hint::NONE;
164   for(const auto& hint : mHints)
165   {
166     if(hint == SHADER_HINT_OUTPUT_IS_TRANSPARENT)
167     {
168       hints |= Shader::Hint::OUTPUT_IS_TRANSPARENT;
169     }
170     else if(hint == SHADER_HINT_MODIFIES_GEOMETRY)
171     {
172       hints |= Shader::Hint::MODIFIES_GEOMETRY;
173     }
174   }
175
176   Property::Map map[2];
177   map[0]["vertex"]        = raw.mVertexShaderSource;
178   map[0]["fragment"]      = raw.mFragmentShaderSource;
179   map[0]["renderPassTag"] = 0;
180   map[0]["hints"]         = static_cast<Shader::Hint::Value>(hints);
181
182   map[1]["vertex"]        = raw.mShadowVertexShaderSource;
183   map[1]["fragment"]      = raw.mShadowFragmentShaderSource;
184   map[1]["renderPassTag"] = 10;
185
186   Property::Array array;
187   array.PushBack(map[0]);
188   array.PushBack(map[1]);
189
190   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Vert Shader src: \n%s\n", raw.mVertexShaderSource.c_str());
191   Shader shader = Shader::New(array);
192   for(Property::Map::SizeType i0 = 0, i1 = mUniforms.Count(); i0 != i1; ++i0)
193   {
194     auto pair = mUniforms.GetKeyValue(i0);
195     DALI_ASSERT_ALWAYS(pair.first.type == Property::Key::STRING);
196     shader.RegisterProperty(pair.first.stringKey, pair.second);
197   }
198
199   return shader;
200 }
201
202 } // namespace Dali::Scene3D::Loader