2 * Copyright (c) 2022 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 // 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.
153 bool firstLinePrinted = false;
154 while(getline(shaderFile, line))
156 if(!firstLinePrinted && line.find_first_not_of(" \t\r\n") == std::string::npos)
158 // Empty string occured!
161 firstLinePrinted = true;
162 outFile << line << endl;
164 outFile << ")\"" << endl;
165 outFile << "};" << endl;
166 cout << " [OK]" << endl;
170 cout << " [FAIL]" << endl;
174 ///////////////////////////////////////////////////////////////////////////////////////////////////
175 /// If required, this accumulates data about all the shaders & generates the built-in cpp & header
176 class BuiltInFilesGenerator
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))
187 /// Default destructor
188 ~BuiltInFilesGenerator() = default;
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)
195 mVariableNames.emplace_back(variableName);
196 mHeaderFileNames.emplace_back(headerFilename);
199 // Generates the built in files.
205 "#pragma once\n\n#include <string_view>\n\n",
206 "extern const std::string_view ",
212 "#include \"../" + string(HEADER_FILE_NAME) + "\"\n\n",
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
225 vector<string>& strings,
226 const string& filePath,
227 const string_view header,
228 const string_view before,
229 const string_view after)
231 sort(strings.begin(), strings.end());
232 cout << " Generating \"" << filePath << "\"";
233 ofstream outFile(filePath);
237 for(auto& current : strings)
239 outFile << before << current << after << endl;
241 cout << " [OK]" << endl;
245 cout << " [FAIL]" << endl;
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";
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
258 ///////////////////////////////////////////////////////////////////////////////////////////////////
259 /// Generates the header files from the shaders in the input directory & built-in files if reqruied.
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)
267 if(!fs::is_directory(inDir))
269 cerr << "ERROR: " << inDir << " is not a valid directory" << endl;
276 fs::create_directories(outDir);
280 cerr << "ERROR: Unable to create directory " << outDir << endl;
284 cout << "====================================================================" << endl;
285 cout << "Shader Input Directory: " << inDir << endl;
286 cout << "Shader Output Directory: " << outDir << endl;
287 cout << "====================================================================" << endl;
289 BuiltInFilesGenerator generator(outDir);
291 for(auto& file : fs::directory_iterator(inDir))
293 if(file.is_regular_file())
295 for(const auto& extension : SHADER_EXTENSIONS)
297 if(file.path().extension() == extension)
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())
305 fs::path outFilePath(GetShaderOutputFilePath(outDir, filename));
306 GenerateHeaderFile(shaderFile, shaderVariableName, outFilePath);
307 generator.Add(std::move(shaderVariableName), outFilePath.filename().string());
315 if(generateBuiltInFiles)
317 generator.Generate();
320 cout << "====================================================================" << endl;
324 } // unnamed namespace
326 ///////////////////////////////////////////////////////////////////////////////////////////////////
328 int main(int argc, char* argv[])
330 PROGRAM_NAME = argv[0];
332 bool generateBuiltInFiles = true;
337 for(auto i = 1; i < argc; ++i)
339 string option(argv[i]);
340 if(option == "--skip" || option == "-s")
342 generateBuiltInFiles = false;
344 else if(option == "--help" || option == "-h")
346 cout << "DALi Shader Generator v" << VERSION << endl
351 else if(option == "--version" || option == "-v")
353 cout << VERSION << endl;
356 else if(*option.begin() == '-')
358 cerr << "ERROR: " << option << " is not a supported option" << endl;
362 else if(inDir.empty())
366 else if(outDir.empty())
370 else if(inDir.size() && outDir.size())
372 cerr << "ERROR: Too many options" << endl;
378 if(inDir.empty() || outDir.empty())
380 cerr << "ERROR: Both IN_DIR & OUT_DIR not provided" << endl;
385 return GenerateShaderSources(inDir, outDir, generateBuiltInFiles);