Flutter Impeller
impellerc_main.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <filesystem>
6 #include <system_error>
7 
8 #include "flutter/fml/backtrace.h"
9 #include "flutter/fml/command_line.h"
10 #include "flutter/fml/file.h"
11 #include "flutter/fml/mapping.h"
19 
20 namespace impeller {
21 namespace compiler {
22 
23 namespace {
24 
25 Reflector::Options CreateReflectorOptions(const SourceOptions& options,
26  const Switches& switches) {
27  Reflector::Options reflector_options;
28  reflector_options.target_platform = options.target_platform;
29  reflector_options.entry_point_name = options.entry_point_name;
30  reflector_options.shader_name =
31  InferShaderNameFromPath(switches.source_file_name);
32  reflector_options.header_file_name =
33  Utf8FromPath(switches.reflection_header_name.filename());
34  return reflector_options;
35 }
36 
37 std::shared_ptr<Compiler> CreateCompiler(
38  TargetPlatform platform,
39  const std::shared_ptr<const fml::Mapping>& source_file_mapping,
40  const Switches& switches) {
41  SourceOptions options = switches.CreateSourceOptions();
42  options.target_platform = platform;
43  Reflector::Options reflector_options =
44  CreateReflectorOptions(options, switches);
45  return std::make_shared<Compiler>(source_file_mapping, options,
46  reflector_options);
47 }
48 
49 void OutputVerboseErrorFile(const std::string& verbose_error_messages,
50  const Switches& switches) {
51  auto error_mapping = std::make_shared<fml::NonOwnedMapping>(
52  reinterpret_cast<const uint8_t*>(verbose_error_messages.data()),
53  verbose_error_messages.size(), [](auto, auto) {});
54  std::filesystem::path output_path =
55  std::filesystem::path(fml::CreateTemporaryDirectory()) /
56  "impellerc_verbose_error.txt";
57 
58  if (fml::WriteAtomically(*switches.working_directory,
59  Utf8FromPath(output_path).c_str(), *error_mapping)) {
60  std::cerr << "Full \"" << InferShaderNameFromPath(switches.source_file_name)
61  << "\" error output written to " << output_path << std::endl;
62  } else {
63  std::cerr << "Failed to write full \""
64  << InferShaderNameFromPath(switches.source_file_name)
65  << "\" error output to " << output_path << std::endl;
66  }
67 }
68 
69 bool OutputIPLR(const std::vector<std::shared_ptr<Compiler>>& compilers,
70  const Switches& switches) {
71  FML_DCHECK(switches.iplr);
72 
73  RuntimeStageData stages;
74  for (const auto& compiler : compilers) {
75  std::shared_ptr<RuntimeStageData::Shader> stage_data =
76  compiler->GetReflector()->GetRuntimeStageShaderData();
77  if (!stage_data) {
78  std::cerr << "Runtime stage information was nil." << std::endl;
79  return false;
80  }
81  stages.AddShader(stage_data);
82  }
83 
84  auto stage_data_mapping = switches.json_format ? stages.CreateJsonMapping()
85  : stages.CreateMapping();
86  if (!stage_data_mapping) {
87  std::cerr << "Runtime stage data could not be created." << std::endl;
88  return false;
89  }
90  if (!fml::WriteAtomically(*switches.working_directory, //
91  Utf8FromPath(switches.sl_file_name).c_str(), //
92  *stage_data_mapping //
93  )) {
94  std::cerr << "Could not write file to " << switches.sl_file_name
95  << std::endl;
96  return false;
97  }
98  // Tools that consume the runtime stage data expect the access mode to
99  // be 0644.
100  if (!SetPermissiveAccess(switches.sl_file_name)) {
101  return false;
102  }
103  return true;
104 }
105 
106 bool OutputSLFile(const Compiler& compiler, const Switches& switches) {
107  auto sl_file_name = std::filesystem::absolute(
108  std::filesystem::current_path() / switches.sl_file_name);
109  if (!fml::WriteAtomically(*switches.working_directory,
110  Utf8FromPath(sl_file_name).c_str(),
111  *compiler.GetSLShaderSource())) {
112  std::cerr << "Could not write file to " << switches.sl_file_name
113  << std::endl;
114  return false;
115  }
116  return true;
117 }
118 
119 bool OutputSPIRV(const Compiler& compiler, const Switches& switches) {
120  auto spriv_file_name = std::filesystem::absolute(
121  std::filesystem::current_path() / switches.spirv_file_name);
122  if (!fml::WriteAtomically(*switches.working_directory,
123  Utf8FromPath(spriv_file_name).c_str(),
124  *compiler.GetSPIRVAssembly())) {
125  std::cerr << "Could not write file to " << switches.spirv_file_name
126  << std::endl;
127  return false;
128  }
129  return true;
130 }
131 
132 bool ShouldOutputReflectionData(const Switches& switches) {
133  return !switches.reflection_json_name.empty() ||
134  !switches.reflection_header_name.empty() ||
135  !switches.reflection_cc_name.empty();
136 }
137 
138 bool OutputReflectionData(const Compiler& compiler, const Switches& switches) {
139  if (!switches.reflection_json_name.empty()) {
140  auto reflection_json_name = std::filesystem::absolute(
141  std::filesystem::current_path() / switches.reflection_json_name);
142  if (!fml::WriteAtomically(*switches.working_directory,
143  Utf8FromPath(reflection_json_name).c_str(),
144  *compiler.GetReflector()->GetReflectionJSON())) {
145  std::cerr << "Could not write reflection json to "
146  << switches.reflection_json_name << std::endl;
147  return false;
148  }
149  }
150 
151  if (!switches.reflection_header_name.empty()) {
152  auto reflection_header_name = std::filesystem::absolute(
153  std::filesystem::current_path() / switches.reflection_header_name);
154  if (!fml::WriteAtomically(
155  *switches.working_directory,
156  Utf8FromPath(reflection_header_name).c_str(),
157  *compiler.GetReflector()->GetReflectionHeader())) {
158  std::cerr << "Could not write reflection header to "
159  << switches.reflection_header_name << std::endl;
160  return false;
161  }
162  }
163 
164  if (!switches.reflection_cc_name.empty()) {
165  auto reflection_cc_name = std::filesystem::absolute(
166  std::filesystem::current_path() / switches.reflection_cc_name);
167  if (!fml::WriteAtomically(*switches.working_directory,
168  Utf8FromPath(reflection_cc_name).c_str(),
169  *compiler.GetReflector()->GetReflectionCC())) {
170  std::cerr << "Could not write reflection CC to "
171  << switches.reflection_cc_name << std::endl;
172  return false;
173  }
174  }
175  return true;
176 }
177 
178 bool OutputDepfile(const Compiler& compiler, const Switches& switches) {
179  if (!switches.depfile_path.empty()) {
180  std::string result_file = Utf8FromPath(switches.sl_file_name);
181  auto depfile_path = std::filesystem::absolute(
182  std::filesystem::current_path() / switches.depfile_path);
183  if (!fml::WriteAtomically(*switches.working_directory,
184  Utf8FromPath(depfile_path).c_str(),
185  *compiler.CreateDepfileContents({result_file}))) {
186  std::cerr << "Could not write depfile to " << switches.depfile_path
187  << std::endl;
188  return false;
189  }
190  }
191 
192  return true;
193 }
194 
195 } // namespace
196 
197 bool Main(const fml::CommandLine& command_line) {
198  fml::InstallCrashHandler();
199  if (command_line.HasOption("help")) {
200  Switches::PrintHelp(std::cout);
201  return true;
202  }
203 
204  Switches switches(command_line);
205  if (!switches.AreValid(std::cerr)) {
206  std::cerr << "Invalid flags specified." << std::endl;
207  Switches::PrintHelp(std::cerr);
208  return false;
209  }
210 
211  if (!switches.shader_bundle.empty()) {
212  // Invoke the compiler multiple times to build a shader bundle with the
213  // given shader_bundle spec.
214  return GenerateShaderBundle(switches);
215  }
216 
217  std::shared_ptr<fml::FileMapping> source_file_mapping =
218  fml::FileMapping::CreateReadOnly(Utf8FromPath(switches.source_file_name));
219  if (!source_file_mapping) {
220  std::cerr << "Could not open input file." << std::endl;
221  return false;
222  }
223 
224  std::vector<std::shared_ptr<Compiler>> compilers;
225  compilers.reserve(switches.PlatformsToCompile().size());
226  for (const auto& platform : switches.PlatformsToCompile()) {
227  std::shared_ptr<Compiler> compiler =
228  CreateCompiler(platform, source_file_mapping, switches);
229  if (compiler->IsValid()) {
230  compilers.push_back(compiler);
231  } else {
232  std::cerr << "Compilation failed for target: "
233  << TargetPlatformToString(platform) << std::endl;
234 
235  std::string verbose_error_messages = compiler->GetVerboseErrorMessages();
236  if (verbose_error_messages.empty()) {
237  // No verbose error messages. Output the regular error messages.
238  std::cerr << compiler->GetErrorMessages();
239  } else {
240  if (switches.verbose) {
241  // Verbose messages are available and the --verbose flag was set.
242  // Directly output the verbose error messages.
243  std::cerr << verbose_error_messages;
244  } else {
245  // Verbose messages are available and the --verbose flag was not set.
246  // Output the regular error messages and write the verbose error
247  // messages to a file.
248  std::cerr << compiler->GetErrorMessages();
249  OutputVerboseErrorFile(verbose_error_messages, switches);
250  }
251  }
252 
253  return false;
254  }
255  }
256 
257  // --------------------------------------------------------------------------
258  /// 1. Output the source file. When in IPLR/RuntimeStage mode, output the
259  /// serialized IPLR flatbuffer. Otherwise output the shader source in the
260  /// target shading language.
261  ///
262 
263  if (switches.iplr) {
264  if (!OutputIPLR(compilers, switches)) {
265  return false;
266  }
267  } else {
268  // Non-IPLR mode is supported only for single platform targets. There is
269  // exactly 1 created compiler for this case.
270  FML_DCHECK(compilers.size() == 1);
271  if (!OutputSLFile(*compilers.front(), switches)) {
272  return false;
273  }
274  }
275 
276  // Use the first compiler for outputting the SPIRV file, reflection data, and
277  // the depfile. The SPIRV and depfile outputs do not depend on the target
278  // platform, so any valid compiler can be used. Reflection data output is only
279  // supported for single platform targets, so it uses the first (only) valid
280  // compiler as well.
281  auto first_valid_compiler = compilers.front();
282 
283  // --------------------------------------------------------------------------
284  /// 2. Output SPIRV file.
285  ///
286 
287  if (!OutputSPIRV(*first_valid_compiler, switches)) {
288  return false;
289  }
290 
291  // --------------------------------------------------------------------------
292  /// 3. Output shader reflection data.
293  /// May include a JSON file, a C++ header, and/or a C++ TU.
294  ///
295 
296  if (ShouldOutputReflectionData(switches)) {
297  // Outputting reflection data is supported only for single platform targets.
298  FML_DCHECK(compilers.size() == 1);
299  if (!OutputReflectionData(*first_valid_compiler, switches)) {
300  return false;
301  }
302  }
303 
304  // --------------------------------------------------------------------------
305  /// 4. Output a depfile.
306  ///
307 
308  // Dep file output does not depend on the target platform. Any valid compiler
309  // can be used to output it. Arbitrarily pick the first valid compiler.
310  if (!OutputDepfile(*first_valid_compiler, switches)) {
311  return false;
312  }
313 
314  return true;
315 }
316 
317 } // namespace compiler
318 } // namespace impeller
319 
320 int main(int argc, char const* argv[]) {
322  fml::CommandLineFromPlatformOrArgcArgv(argc, argv))
323  ? EXIT_SUCCESS
324  : EXIT_FAILURE;
325 }
std::vector< TargetPlatform > PlatformsToCompile() const
A vector containing at least one valid platform.
Definition: switches.cc:312
bool AreValid(std::ostream &explain) const
Definition: switches.cc:259
static void PrintHelp(std::ostream &stream)
Definition: switches.cc:43
std::filesystem::path source_file_name
Definition: switches.h:26
int main(int argc, char const *argv[])
bool Main(const fml::CommandLine &command_line)
std::string TargetPlatformToString(TargetPlatform platform)
Definition: types.cc:62
bool SetPermissiveAccess(const std::filesystem::path &p)
Sets the file access mode of the file at path 'p' to 0644.
Definition: utilities.cc:16
std::string InferShaderNameFromPath(const std::filesystem::path &path)
Definition: utilities.cc:34
bool GenerateShaderBundle(Switches &switches)
Parses the JSON shader bundle configuration and invokes the compiler multiple times to produce a shad...
static CompilerBackend CreateCompiler(const spirv_cross::ParsedIR &ir, const SourceOptions &source_options)
Definition: compiler.cc:233
std::string Utf8FromPath(const std::filesystem::path &path)
Converts a native format path to a utf8 string.
Definition: utilities.cc:30