2 * Copyright (c) 2021 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.
23 #include <string_view>
27 namespace fs = filesystem;
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";
35 ///////////////////////////////////////////////////////////////////////////////////////////////////
36 /// Supported extensions for the files in the input directory.
38 constexpr string_view SHADER_EXTENSIONS[] =
46 ///////////////////////////////////////////////////////////////////////////////////////////////////
47 /// Function & variable to retrieve the size of the extension with the largest string size.
48 constexpr auto GetShaderExtensionMaxSize()
51 for(const auto& extension : SHADER_EXTENSIONS)
53 if(extension.size() > maxSize)
55 maxSize = extension.size();
60 constexpr const int SHADER_MAX_EXTENSION_SIZE(GetShaderExtensionMaxSize());
62 ///////////////////////////////////////////////////////////////////////////////////////////////////
63 /// Prints out the Usage to standard output.
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:";
70 for(const auto& extension : SHADER_EXTENSIONS)
72 extensions = extensions + " \"" + string(extension) + "\",";
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;
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)
92 string shaderVariableName("SHADER_" + filename);
94 shaderVariableName.begin(),
95 shaderVariableName.end(),
108 character = ::toupper(character);
113 return shaderVariableName;
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)
123 string outFilename(filename);
124 replace(outFilename.end() - SHADER_MAX_EXTENSION_SIZE, outFilename.end(), '.', '-');
125 outFilename = outDir.string() + "/" + outFilename + ".h";
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)
139 cout << " Generating \"" << shaderVariableName << "\" in " << outFilePath.filename();
140 ofstream outFile(outFilePath);
141 if(outFile.is_open())
143 outFile << "#pragma once" << endl
145 outFile << "const std::string_view " << shaderVariableName << endl;
146 outFile << "{" << endl;
148 while(getline(shaderFile, line))
150 outFile << "\"" << line << "\\n\"" << endl;
152 outFile << "};" << endl;
153 cout << " [OK]" << endl;
157 cout << " [FAIL]" << endl;
161 ///////////////////////////////////////////////////////////////////////////////////////////////////
162 /// If required, this accumulates data about all the shaders & generates the built-in cpp & header
163 class BuiltInFilesGenerator
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))
174 /// Default destructor
175 ~BuiltInFilesGenerator() = default;
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)
182 mVariableNames.emplace_back(variableName);
183 mHeaderFileNames.emplace_back(headerFilename);
186 // Generates the built in files.
192 "#pragma once\n\n#include <string_view>\n\n",
193 "extern const std::string_view ",
199 "#include \"../" + string(HEADER_FILE_NAME) + "\"\n\n",
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
212 vector<string>& strings,
213 const string& filePath,
214 const string_view header,
215 const string_view before,
216 const string_view after)
218 sort(strings.begin(), strings.end());
219 cout << " Generating \"" << filePath << "\"";
220 ofstream outFile(filePath);
224 for(auto& current : strings)
226 outFile << before << current << after << endl;
228 cout << " [OK]" << endl;
232 cout << " [FAIL]" << endl;
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";
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
245 ///////////////////////////////////////////////////////////////////////////////////////////////////
246 /// Generates the header files from the shaders in the input directory & built-in files if reqruied.
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)
254 if(!fs::is_directory(inDir))
256 cerr << "ERROR: " << inDir << " is not a valid directory" << endl;
263 fs::create_directories(outDir);
267 cerr << "ERROR: Unable to create directory " << outDir << endl;
271 cout << "====================================================================" << endl;
272 cout << "Shader Input Directory: " << inDir << endl;
273 cout << "Shader Output Directory: " << outDir << endl;
274 cout << "====================================================================" << endl;
276 BuiltInFilesGenerator generator(outDir);
278 for(auto& file : fs::directory_iterator(inDir))
280 if(file.is_regular_file())
282 for(const auto& extension : SHADER_EXTENSIONS)
284 if(file.path().extension() == extension)
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())
292 fs::path outFilePath(GetShaderOutputFilePath(outDir, filename));
293 GenerateHeaderFile(shaderFile, shaderVariableName, outFilePath);
294 generator.Add(std::move(shaderVariableName), outFilePath.filename().string());
302 if(generateBuiltInFiles)
304 generator.Generate();
307 cout << "====================================================================" << endl;
311 } // unnamed namespace
313 ///////////////////////////////////////////////////////////////////////////////////////////////////
315 int main(int argc, char* argv[])
317 PROGRAM_NAME = argv[0];
319 bool generateBuiltInFiles = true;
324 for(auto i = 1; i < argc; ++i)
326 string option(argv[i]);
327 if(option == "--skip" || option == "-s")
329 generateBuiltInFiles = false;
331 else if(option == "--help" || option == "-h")
333 cout << "DALi Shader Generator v" << VERSION << endl
338 else if(option == "--version" || option == "-v")
340 cout << VERSION << endl;
343 else if(*option.begin() == '-')
345 cerr << "ERROR: " << option << " is not a supported option" << endl;
349 else if(inDir.empty())
353 else if(outDir.empty())
357 else if(inDir.size() && outDir.size())
359 cerr << "ERROR: Too many options" << endl;
365 if(inDir.empty() || outDir.empty())
367 cerr << "ERROR: Both IN_DIR & OUT_DIR not provided" << endl;
372 return GenerateShaderSources(inDir, outDir, generateBuiltInFiles);