Merge "[AT-SPI] Set default value of ReadingInfoTypes" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / shader-generator / shader-generator.cpp
1 /*
2  * Copyright (c) 2021 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     string line;
148     while(getline(shaderFile, line))
149     {
150       outFile << "\"" << line << "\\n\"" << endl;
151     }
152     outFile << "};" << endl;
153     cout << " [OK]" << endl;
154   }
155   else
156   {
157     cout << " [FAIL]" << endl;
158   }
159 }
160
161 ///////////////////////////////////////////////////////////////////////////////////////////////////
162 /// If required, this accumulates data about all the shaders & generates the built-in cpp & header
163 class BuiltInFilesGenerator
164 {
165 public:
166   /// Constructor
167   /// @param[in]  outDir  The path to the output directory
168   BuiltInFilesGenerator(const fs::path& outDir)
169   : mHeaderFilePath(outDir.string() + "/../" + string(HEADER_FILE_NAME)),
170     mSourceFilePath(outDir.string() + "/" + string(SOURCE_FILE_NAME))
171   {
172   }
173
174   /// Default destructor
175   ~BuiltInFilesGenerator() = default;
176
177   /// Adds the variable and the header file name to the appropriate vectors.
178   /// @param[in]  variableName    The string_view variable name used
179   /// @param[in]  headerFileName  The name of the header used
180   void Add(string&& variableName, const std::string& headerFilename)
181   {
182     mVariableNames.emplace_back(variableName);
183     mHeaderFileNames.emplace_back(headerFilename);
184   }
185
186   // Generates the built in files.
187   void Generate()
188   {
189     GenerateFile(
190       mVariableNames,
191       mHeaderFilePath,
192       "#pragma once\n\n#include <string_view>\n\n",
193       "extern const std::string_view ",
194       ";");
195
196     GenerateFile(
197       mHeaderFileNames,
198       mSourceFilePath,
199       "#include \"../" + string(HEADER_FILE_NAME) + "\"\n\n",
200       "#include \"",
201       "\"");
202   }
203
204 private:
205   /// Generates the required file.
206   /// @param[in]  strings   A reference to the vector to parse
207   /// @param[in]  filePath  Outputs the data to this file
208   /// @param[in]  header    Puts this before parsing any of the vector
209   /// @param[in]  before    For each string, puts this string before it on every line
210   /// @param[in]  after     For each string, puts this string after it on every line
211   void GenerateFile(
212     vector<string>&   strings,
213     const string&     filePath,
214     const string_view header,
215     const string_view before,
216     const string_view after)
217   {
218     sort(strings.begin(), strings.end());
219     cout << "  Generating \"" << filePath << "\"";
220     ofstream outFile(filePath);
221     if(outFile)
222     {
223       outFile << header;
224       for(auto& current : strings)
225       {
226         outFile << before << current << after << endl;
227       }
228       cout << " [OK]" << endl;
229     }
230     else
231     {
232       cout << " [FAIL]" << endl;
233     }
234   }
235
236   constexpr static string_view HEADER_FILE_NAME = "builtin-shader-extern-gen.h";
237   constexpr static string_view SOURCE_FILE_NAME = "builtin-shader-gen.cpp";
238
239   const string   mHeaderFilePath;  ///< Path to the header file to generate
240   const string   mSourceFilePath;  ///< Path to the source file to generate
241   vector<string> mVariableNames;   ///< Holds all the variable names added through Add
242   vector<string> mHeaderFileNames; ///< Holds all the header file names added through Add
243 };
244
245 ///////////////////////////////////////////////////////////////////////////////////////////////////
246 /// Generates the header files from the shaders in the input directory & built-in files if reqruied.
247 ///
248 /// @param[in]  inDir                 The directory where all the input shader source is
249 /// @param[in]  outDir                The directory where the readable shaders will be outputted to
250 /// @param[in]  generateBuiltInFiles  If true, we generate the built-in files as well
251 /// @return 0 if successful, 1 if failure
252 int GenerateShaderSources(fs::path inDir, fs::path outDir, const bool generateBuiltInFiles)
253 {
254   if(!fs::is_directory(inDir))
255   {
256     cerr << "ERROR: " << inDir << " is not a valid directory" << endl;
257     Usage();
258     return 1;
259   }
260
261   try
262   {
263     fs::create_directories(outDir);
264   }
265   catch(...)
266   {
267     cerr << "ERROR: Unable to create directory " << outDir << endl;
268     return 1;
269   }
270
271   cout << "====================================================================" << endl;
272   cout << "Shader Input Directory:  " << inDir << endl;
273   cout << "Shader Output Directory: " << outDir << endl;
274   cout << "====================================================================" << endl;
275
276   BuiltInFilesGenerator generator(outDir);
277
278   for(auto& file : fs::directory_iterator(inDir))
279   {
280     if(file.is_regular_file())
281     {
282       for(const auto& extension : SHADER_EXTENSIONS)
283       {
284         if(file.path().extension() == extension)
285         {
286           const fs::path& path(file.path());
287           const string    filename(path.filename().string());
288           string          shaderVariableName(GetShaderVariableName(filename));
289           ifstream        shaderFile(path);
290           if(shaderFile.is_open())
291           {
292             fs::path outFilePath(GetShaderOutputFilePath(outDir, filename));
293             GenerateHeaderFile(shaderFile, shaderVariableName, outFilePath);
294             generator.Add(std::move(shaderVariableName), outFilePath.filename().string());
295           }
296           break;
297         }
298       }
299     }
300   }
301
302   if(generateBuiltInFiles)
303   {
304     generator.Generate();
305   }
306
307   cout << "====================================================================" << endl;
308   return 0;
309 }
310
311 } // unnamed namespace
312
313 ///////////////////////////////////////////////////////////////////////////////////////////////////
314 /// MAIN.
315 int main(int argc, char* argv[])
316 {
317   PROGRAM_NAME = argv[0];
318
319   bool generateBuiltInFiles = true;
320
321   string inDir;
322   string outDir;
323
324   for(auto i = 1; i < argc; ++i)
325   {
326     string option(argv[i]);
327     if(option == "--skip" || option == "-s")
328     {
329       generateBuiltInFiles = false;
330     }
331     else if(option == "--help" || option == "-h")
332     {
333       cout << "DALi Shader Generator v" << VERSION << endl
334            << endl;
335       Usage();
336       return 0;
337     }
338     else if(option == "--version" || option == "-v")
339     {
340       cout << VERSION << endl;
341       return 0;
342     }
343     else if(*option.begin() == '-')
344     {
345       cerr << "ERROR: " << option << " is not a supported option" << endl;
346       Usage();
347       return 1;
348     }
349     else if(inDir.empty())
350     {
351       inDir = option;
352     }
353     else if(outDir.empty())
354     {
355       outDir = option;
356     }
357     else if(inDir.size() && outDir.size())
358     {
359       cerr << "ERROR: Too many options" << endl;
360       Usage();
361       return 1;
362     }
363   }
364
365   if(inDir.empty() || outDir.empty())
366   {
367     cerr << "ERROR: Both IN_DIR & OUT_DIR not provided" << endl;
368     Usage();
369     return 1;
370   }
371
372   return GenerateShaderSources(inDir, outDir, generateBuiltInFiles);
373 }