Merge "Add an API for getting web view when request is intercepted." into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / shader-generator / shader-generator.cpp
1 /*
2  * Copyright (c) 2022 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 #include <algorithm>
19 #include <filesystem>
20 #include <fstream>
21 #include <iostream>
22 #include <string>
23 #include <string_view>
24 #include <vector>
25
26 using namespace std;
27 namespace fs = filesystem;
28
29 namespace
30 {
31 ///////////////////////////////////////////////////////////////////////////////////////////////////
32 string      PROGRAM_NAME; ///< We set the program name on this global early on for use in Usage.
33 string_view VERSION = "1.0.0";
34
35 ///////////////////////////////////////////////////////////////////////////////////////////////////
36 /// Supported extensions for the files in the input directory.
37 // clang-format off
38 constexpr string_view SHADER_EXTENSIONS[] =
39 {
40   ".vert",
41   ".frag",
42   ".def"
43 };
44 // clang-format on
45
46 ///////////////////////////////////////////////////////////////////////////////////////////////////
47 /// Function & variable to retrieve the size of the extension with the largest string size.
48 constexpr auto GetShaderExtensionMaxSize()
49 {
50   auto maxSize = 0u;
51   for(const auto& extension : SHADER_EXTENSIONS)
52   {
53     if(extension.size() > maxSize)
54     {
55       maxSize = extension.size();
56     }
57   }
58   return maxSize;
59 }
60 constexpr const int SHADER_MAX_EXTENSION_SIZE(GetShaderExtensionMaxSize());
61
62 ///////////////////////////////////////////////////////////////////////////////////////////////////
63 /// Prints out the Usage to standard output.
64 void Usage()
65 {
66   cout << "Usage: " << PROGRAM_NAME << " [OPTIONS] [IN_DIR] [OUT_DIR]" << endl;
67   cout << "  IN_DIR:  Input Directory which has all the shader files." << endl;
68   cout << "           Supported extensions:";
69   string extensions;
70   for(const auto& extension : SHADER_EXTENSIONS)
71   {
72     extensions = extensions + " \"" + string(extension) + "\",";
73   }
74   extensions.pop_back(); // Remove the last comma.
75   cout << extensions << "." << endl;
76   cout << "  OUT_DIR: Directory where the generated shader source code will be outputted to." << endl;
77   cout << "           This directory will be created if it does not exist." << endl;
78   cout << "           Any existing files of the same name in the directory will be overwritten." << endl;
79   cout << "  Options: " << endl;
80   cout << "     -s|--skip     Skips the generation of the built-in header and source files" << endl;
81   cout << "     -v|--version  Prints out the version" << endl;
82   cout << "     -h|--help     Help" << endl;
83   cout << "  NOTE: The options can be placed after the IN_DIR & OUT_DIR as well" << endl;
84 }
85
86 ///////////////////////////////////////////////////////////////////////////////////////////////////
87 /// Uses the filename to generate the shader variable name to use in source code.
88 /// @param[in]  filename  The filename of the shader (including the extension)
89 /// @return The shader variable name
90 string GetShaderVariableName(const string& filename)
91 {
92   string shaderVariableName("SHADER_" + filename);
93   for_each(
94     shaderVariableName.begin(),
95     shaderVariableName.end(),
96     [](char& character) {
97       switch(character)
98       {
99         case '-':
100         case '.':
101         {
102           character = '_';
103           break;
104         }
105
106         default:
107         {
108           character = ::toupper(character);
109           break;
110         }
111       }
112     });
113   return shaderVariableName;
114 }
115
116 ///////////////////////////////////////////////////////////////////////////////////////////////////
117 /// Uses the ourDir & filename to generate the path of the output header file for the shader.
118 /// @param[in]  outDir    The directory where the readable shaders will be outputted to
119 /// @param[in]  filename  The filename of the shader (including the extension)
120 /// @return The path to the output file
121 fs::path GetShaderOutputFilePath(fs::path& outDir, const string& filename)
122 {
123   string outFilename(filename);
124   replace(outFilename.end() - SHADER_MAX_EXTENSION_SIZE, outFilename.end(), '.', '-');
125   outFilename = outDir.string() + "/" + outFilename + ".h";
126   return outFilename;
127 }
128
129 ///////////////////////////////////////////////////////////////////////////////////////////////////
130 /// Generates the header file from the input shader file.
131 /// @param[in]  shaderFile          The full path of the input shader file
132 /// @param[in]  shaderVariableName  The variable name to use for the string_view
133 /// @param[in]  outFilePath         The full path to the output file
134 void GenerateHeaderFile(
135   ifstream&       shaderFile,
136   const string&   shaderVariableName,
137   const fs::path& outFilePath)
138 {
139   cout << "  Generating \"" << shaderVariableName << "\" in " << outFilePath.filename();
140   ofstream outFile(outFilePath);
141   if(outFile.is_open())
142   {
143     outFile << "#pragma once" << endl
144             << endl;
145     outFile << "const std::string_view " << shaderVariableName << endl;
146     outFile << "{" << endl;
147
148     // Using Raw String Literal to generate shader files as this will simplify the file layout.
149     // And it will fix some compilation warnings about missing terminating strings.
150     // Note : we should skip empty headline to guarantee that "#version ~~~" as top of shader code.
151     outFile << "R\"(";
152     string line;
153     bool   firstLinePrinted = false;
154     while(getline(shaderFile, line))
155     {
156       if(!firstLinePrinted && line.find_first_not_of(" \t\r\n") == std::string::npos)
157       {
158         // Empty string occured!
159         continue;
160       }
161       firstLinePrinted = true;
162       outFile << line << endl;
163     }
164     outFile << ")\"" << endl;
165     outFile << "};" << endl;
166     cout << " [OK]" << endl;
167   }
168   else
169   {
170     cout << " [FAIL]" << endl;
171   }
172 }
173
174 ///////////////////////////////////////////////////////////////////////////////////////////////////
175 /// If required, this accumulates data about all the shaders & generates the built-in cpp & header
176 class BuiltInFilesGenerator
177 {
178 public:
179   /// Constructor
180   /// @param[in]  outDir  The path to the output directory
181   BuiltInFilesGenerator(const fs::path& outDir)
182   : mHeaderFilePath(outDir.string() + "/../" + string(HEADER_FILE_NAME)),
183     mSourceFilePath(outDir.string() + "/" + string(SOURCE_FILE_NAME))
184   {
185   }
186
187   /// Default destructor
188   ~BuiltInFilesGenerator() = default;
189
190   /// Adds the variable and the header file name to the appropriate vectors.
191   /// @param[in]  variableName    The string_view variable name used
192   /// @param[in]  headerFileName  The name of the header used
193   void Add(string&& variableName, const std::string& headerFilename)
194   {
195     mVariableNames.emplace_back(variableName);
196     mHeaderFileNames.emplace_back(headerFilename);
197   }
198
199   // Generates the built in files.
200   void Generate()
201   {
202     GenerateFile(
203       mVariableNames,
204       mHeaderFilePath,
205       "#pragma once\n\n#include <string_view>\n\n",
206       "extern const std::string_view ",
207       ";");
208
209     GenerateFile(
210       mHeaderFileNames,
211       mSourceFilePath,
212       "#include \"../" + string(HEADER_FILE_NAME) + "\"\n\n",
213       "#include \"",
214       "\"");
215   }
216
217 private:
218   /// Generates the required file.
219   /// @param[in]  strings   A reference to the vector to parse
220   /// @param[in]  filePath  Outputs the data to this file
221   /// @param[in]  header    Puts this before parsing any of the vector
222   /// @param[in]  before    For each string, puts this string before it on every line
223   /// @param[in]  after     For each string, puts this string after it on every line
224   void GenerateFile(
225     vector<string>&   strings,
226     const string&     filePath,
227     const string_view header,
228     const string_view before,
229     const string_view after)
230   {
231     sort(strings.begin(), strings.end());
232     cout << "  Generating \"" << filePath << "\"";
233     ofstream outFile(filePath);
234     if(outFile)
235     {
236       outFile << header;
237       for(auto& current : strings)
238       {
239         outFile << before << current << after << endl;
240       }
241       cout << " [OK]" << endl;
242     }
243     else
244     {
245       cout << " [FAIL]" << endl;
246     }
247   }
248
249   constexpr static string_view HEADER_FILE_NAME = "builtin-shader-extern-gen.h";
250   constexpr static string_view SOURCE_FILE_NAME = "builtin-shader-gen.cpp";
251
252   const string   mHeaderFilePath;  ///< Path to the header file to generate
253   const string   mSourceFilePath;  ///< Path to the source file to generate
254   vector<string> mVariableNames;   ///< Holds all the variable names added through Add
255   vector<string> mHeaderFileNames; ///< Holds all the header file names added through Add
256 };
257
258 ///////////////////////////////////////////////////////////////////////////////////////////////////
259 /// Generates the header files from the shaders in the input directory & built-in files if reqruied.
260 ///
261 /// @param[in]  inDir                 The directory where all the input shader source is
262 /// @param[in]  outDir                The directory where the readable shaders will be outputted to
263 /// @param[in]  generateBuiltInFiles  If true, we generate the built-in files as well
264 /// @return 0 if successful, 1 if failure
265 int GenerateShaderSources(fs::path inDir, fs::path outDir, const bool generateBuiltInFiles)
266 {
267   if(!fs::is_directory(inDir))
268   {
269     cerr << "ERROR: " << inDir << " is not a valid directory" << endl;
270     Usage();
271     return 1;
272   }
273
274   try
275   {
276     fs::create_directories(outDir);
277   }
278   catch(...)
279   {
280     cerr << "ERROR: Unable to create directory " << outDir << endl;
281     return 1;
282   }
283
284   cout << "====================================================================" << endl;
285   cout << "Shader Input Directory:  " << inDir << endl;
286   cout << "Shader Output Directory: " << outDir << endl;
287   cout << "====================================================================" << endl;
288
289   BuiltInFilesGenerator generator(outDir);
290
291   for(auto& file : fs::directory_iterator(inDir))
292   {
293     if(file.is_regular_file())
294     {
295       for(const auto& extension : SHADER_EXTENSIONS)
296       {
297         if(file.path().extension() == extension)
298         {
299           const fs::path& path(file.path());
300           const string    filename(path.filename().string());
301           string          shaderVariableName(GetShaderVariableName(filename));
302           ifstream        shaderFile(path);
303           if(shaderFile.is_open())
304           {
305             fs::path outFilePath(GetShaderOutputFilePath(outDir, filename));
306             GenerateHeaderFile(shaderFile, shaderVariableName, outFilePath);
307             generator.Add(std::move(shaderVariableName), outFilePath.filename().string());
308           }
309           break;
310         }
311       }
312     }
313   }
314
315   if(generateBuiltInFiles)
316   {
317     generator.Generate();
318   }
319
320   cout << "====================================================================" << endl;
321   return 0;
322 }
323
324 } // unnamed namespace
325
326 ///////////////////////////////////////////////////////////////////////////////////////////////////
327 /// MAIN.
328 int main(int argc, char* argv[])
329 {
330   PROGRAM_NAME = argv[0];
331
332   bool generateBuiltInFiles = true;
333
334   string inDir;
335   string outDir;
336
337   for(auto i = 1; i < argc; ++i)
338   {
339     string option(argv[i]);
340     if(option == "--skip" || option == "-s")
341     {
342       generateBuiltInFiles = false;
343     }
344     else if(option == "--help" || option == "-h")
345     {
346       cout << "DALi Shader Generator v" << VERSION << endl
347            << endl;
348       Usage();
349       return 0;
350     }
351     else if(option == "--version" || option == "-v")
352     {
353       cout << VERSION << endl;
354       return 0;
355     }
356     else if(*option.begin() == '-')
357     {
358       cerr << "ERROR: " << option << " is not a supported option" << endl;
359       Usage();
360       return 1;
361     }
362     else if(inDir.empty())
363     {
364       inDir = option;
365     }
366     else if(outDir.empty())
367     {
368       outDir = option;
369     }
370     else if(inDir.size() && outDir.size())
371     {
372       cerr << "ERROR: Too many options" << endl;
373       Usage();
374       return 1;
375     }
376   }
377
378   if(inDir.empty() || outDir.empty())
379   {
380     cerr << "ERROR: Both IN_DIR & OUT_DIR not provided" << endl;
381     Usage();
382     return 1;
383   }
384
385   return GenerateShaderSources(inDir, outDir, generateBuiltInFiles);
386 }