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 
24  const Switches& switches) {
25  Reflector::Options reflector_options;
26  reflector_options.target_platform = options.target_platform;
27  reflector_options.entry_point_name = options.entry_point_name;
28  reflector_options.shader_name =
30  reflector_options.header_file_name =
31  Utf8FromPath(switches.reflection_header_name.filename());
32  return reflector_options;
33 }
34 
35 /// Run the shader compiler to geneate SkSL reflection data.
36 /// If there is an error, prints error text and returns `nullptr`.
37 static std::shared_ptr<RuntimeStageData::Shader> CompileSkSL(
38  std::shared_ptr<fml::Mapping> source_file_mapping,
39  const Switches& switches) {
40  auto options = switches.CreateSourceOptions(TargetPlatform::kSkSL);
41 
42  Reflector::Options sksl_reflector_options =
43  CreateReflectorOptions(options, switches);
44  sksl_reflector_options.target_platform = TargetPlatform::kSkSL;
45 
46  Compiler sksl_compiler =
47  Compiler(std::move(source_file_mapping), options, sksl_reflector_options);
48  if (!sksl_compiler.IsValid()) {
49  std::cerr << "Compilation to SkSL failed." << std::endl;
50  std::cerr << sksl_compiler.GetErrorMessages() << std::endl;
51  return nullptr;
52  }
53  return sksl_compiler.GetReflector()->GetRuntimeStageShaderData();
54 }
55 
56 static bool OutputIPLR(
57  const Switches& switches,
58  const std::shared_ptr<fml::Mapping>& source_file_mapping) {
59  FML_DCHECK(switches.iplr);
60 
61  RuntimeStageData stages;
62  std::shared_ptr<RuntimeStageData::Shader> sksl_shader;
64  sksl_shader = CompileSkSL(source_file_mapping, switches);
65  if (!sksl_shader) {
66  return false;
67  }
68  stages.AddShader(sksl_shader);
69  }
70 
71  for (const auto& platform : switches.PlatformsToCompile()) {
72  if (platform == TargetPlatform::kSkSL) {
73  // Already handled above.
74  continue;
75  }
76  SourceOptions options = switches.CreateSourceOptions(platform);
77 
78  // Invoke the compiler and generate reflection data for a single shader.
79 
80  Reflector::Options reflector_options =
81  CreateReflectorOptions(options, switches);
82  Compiler compiler(source_file_mapping, options, reflector_options);
83  if (!compiler.IsValid()) {
84  std::cerr << "Compilation failed." << std::endl;
85  std::cerr << compiler.GetErrorMessages() << std::endl;
86  return false;
87  }
88 
89  auto reflector = compiler.GetReflector();
90  if (reflector == nullptr) {
91  std::cerr << "Could not create reflector." << std::endl;
92  return false;
93  }
94 
95  auto stage_data = reflector->GetRuntimeStageShaderData();
96  if (!stage_data) {
97  std::cerr << "Runtime stage information was nil." << std::endl;
98  return false;
99  }
100 
101  stages.AddShader(stage_data);
102  }
103 
104  auto stage_data_mapping = switches.json_format ? stages.CreateJsonMapping()
105  : stages.CreateMapping();
106  if (!stage_data_mapping) {
107  std::cerr << "Runtime stage data could not be created." << std::endl;
108  return false;
109  }
110  if (!fml::WriteAtomically(*switches.working_directory, //
111  Utf8FromPath(switches.sl_file_name).c_str(), //
112  *stage_data_mapping //
113  )) {
114  std::cerr << "Could not write file to " << switches.sl_file_name
115  << std::endl;
116  return false;
117  }
118  // Tools that consume the runtime stage data expect the access mode to
119  // be 0644.
120  if (!SetPermissiveAccess(switches.sl_file_name)) {
121  return false;
122  }
123  return true;
124 }
125 
126 static bool OutputSLFile(const Compiler& compiler, const Switches& switches) {
127  // --------------------------------------------------------------------------
128  /// 2. Output the source file. When in IPLR/RuntimeStage mode, output the
129  /// serialized IPLR flatbuffer.
130  ///
131 
132  auto sl_file_name = std::filesystem::absolute(
133  std::filesystem::current_path() / switches.sl_file_name);
134  if (!fml::WriteAtomically(*switches.working_directory,
135  Utf8FromPath(sl_file_name).c_str(),
136  *compiler.GetSLShaderSource())) {
137  std::cerr << "Could not write file to " << switches.sl_file_name
138  << std::endl;
139  return false;
140  }
141  return true;
142 }
143 
144 static bool OutputReflectionData(const Compiler& compiler,
145  const Switches& switches,
146  const SourceOptions& options) {
147  // --------------------------------------------------------------------------
148  /// 3. Output shader reflection data.
149  /// May include a JSON file, a C++ header, and/or a C++ TU.
150  ///
151 
153  if (!switches.reflection_json_name.empty()) {
154  auto reflection_json_name = std::filesystem::absolute(
155  std::filesystem::current_path() / switches.reflection_json_name);
156  if (!fml::WriteAtomically(
157  *switches.working_directory,
158  Utf8FromPath(reflection_json_name).c_str(),
159  *compiler.GetReflector()->GetReflectionJSON())) {
160  std::cerr << "Could not write reflection json to "
161  << switches.reflection_json_name << std::endl;
162  return false;
163  }
164  }
165 
166  if (!switches.reflection_header_name.empty()) {
167  auto reflection_header_name = std::filesystem::absolute(
168  std::filesystem::current_path() / switches.reflection_header_name);
169  if (!fml::WriteAtomically(
170  *switches.working_directory,
171  Utf8FromPath(reflection_header_name).c_str(),
172  *compiler.GetReflector()->GetReflectionHeader())) {
173  std::cerr << "Could not write reflection header to "
174  << switches.reflection_header_name << std::endl;
175  return false;
176  }
177  }
178 
179  if (!switches.reflection_cc_name.empty()) {
180  auto reflection_cc_name = std::filesystem::absolute(
181  std::filesystem::current_path() / switches.reflection_cc_name);
182  if (!fml::WriteAtomically(*switches.working_directory,
183  Utf8FromPath(reflection_cc_name).c_str(),
184  *compiler.GetReflector()->GetReflectionCC())) {
185  std::cerr << "Could not write reflection CC to "
186  << switches.reflection_cc_name << std::endl;
187  return false;
188  }
189  }
190  }
191  return true;
192 }
193 
194 static bool OutputDepfile(const Compiler& compiler, const Switches& switches) {
195  // --------------------------------------------------------------------------
196  /// 4. Output a depfile.
197  ///
198 
199  if (!switches.depfile_path.empty()) {
200  std::string result_file;
201  switch (switches.SelectDefaultTargetPlatform()) {
212  result_file = Utf8FromPath(switches.sl_file_name);
213  break;
215  result_file = Utf8FromPath(switches.spirv_file_name);
216  break;
217  }
218  auto depfile_path = std::filesystem::absolute(
219  std::filesystem::current_path() / switches.depfile_path);
220  if (!fml::WriteAtomically(*switches.working_directory,
221  Utf8FromPath(depfile_path).c_str(),
222  *compiler.CreateDepfileContents({result_file}))) {
223  std::cerr << "Could not write depfile to " << switches.depfile_path
224  << std::endl;
225  return false;
226  }
227  }
228 
229  return true;
230 }
231 
232 bool Main(const fml::CommandLine& command_line) {
233  fml::InstallCrashHandler();
234  if (command_line.HasOption("help")) {
235  Switches::PrintHelp(std::cout);
236  return true;
237  }
238 
239  Switches switches(command_line);
240  if (!switches.AreValid(std::cerr)) {
241  std::cerr << "Invalid flags specified." << std::endl;
242  Switches::PrintHelp(std::cerr);
243  return false;
244  }
245 
246  if (!switches.shader_bundle.empty()) {
247  // Invoke the compiler multiple times to build a shader bundle with the
248  // given shader_bundle spec.
249  return GenerateShaderBundle(switches);
250  }
251 
252  std::shared_ptr<fml::FileMapping> source_file_mapping =
253  fml::FileMapping::CreateReadOnly(Utf8FromPath(switches.source_file_name));
254  if (!source_file_mapping) {
255  std::cerr << "Could not open input file." << std::endl;
256  return false;
257  }
258 
259  if (switches.iplr && !OutputIPLR(switches, source_file_mapping)) {
260  return false;
261  }
262 
263  // Create at least one compiler to output the SL file, reflection data, and a
264  // depfile.
265 
266  SourceOptions options = switches.CreateSourceOptions();
267 
268  // Invoke the compiler and generate reflection data for a single shader.
269 
270  Reflector::Options reflector_options =
271  CreateReflectorOptions(options, switches);
272 
273  Compiler compiler(source_file_mapping, options, reflector_options);
274  if (!compiler.IsValid()) {
275  std::cerr << "Compilation failed." << std::endl;
276  std::cerr << compiler.GetErrorMessages() << std::endl;
277  return false;
278  }
279 
280  auto spriv_file_name = std::filesystem::absolute(
281  std::filesystem::current_path() / switches.spirv_file_name);
282  if (!fml::WriteAtomically(*switches.working_directory,
283  Utf8FromPath(spriv_file_name).c_str(),
284  *compiler.GetSPIRVAssembly())) {
285  std::cerr << "Could not write file to " << switches.spirv_file_name
286  << std::endl;
287  return false;
288  }
289 
290  if (!switches.iplr && !OutputSLFile(compiler, switches)) {
291  return false;
292  }
293 
294  if (!OutputReflectionData(compiler, switches, options)) {
295  return false;
296  }
297 
298  if (!OutputDepfile(compiler, switches)) {
299  return false;
300  }
301 
302  return true;
303 }
304 
305 } // namespace compiler
306 } // namespace impeller
307 
308 int main(int argc, char const* argv[]) {
310  fml::CommandLineFromPlatformOrArgcArgv(argc, argv))
311  ? EXIT_SUCCESS
312  : EXIT_FAILURE;
313 }
std::shared_ptr< fml::Mapping > GetSPIRVAssembly() const
Definition: compiler.cc:486
const Reflector * GetReflector() const
Definition: compiler.cc:547
std::unique_ptr< fml::Mapping > CreateDepfileContents(std::initializer_list< std::string > targets) const
Definition: compiler.cc:532
std::shared_ptr< fml::Mapping > GetSLShaderSource() const
Definition: compiler.cc:490
std::string GetErrorMessages() const
Definition: compiler.cc:504
std::shared_ptr< fml::Mapping > GetReflectionJSON() const
Definition: reflector.cc:109
std::shared_ptr< fml::Mapping > GetReflectionCC() const
Definition: reflector.cc:126
std::shared_ptr< RuntimeStageData::Shader > GetRuntimeStageShaderData() const
Definition: reflector.cc:130
std::shared_ptr< fml::Mapping > GetReflectionHeader() const
Definition: reflector.cc:122
std::shared_ptr< fml::Mapping > CreateMapping() const
void AddShader(const std::shared_ptr< Shader > &data)
std::shared_ptr< fml::Mapping > CreateJsonMapping() const
std::vector< TargetPlatform > PlatformsToCompile() const
A vector containing at least one valid platform.
Definition: switches.cc:305
std::filesystem::path reflection_cc_name
Definition: switches.h:37
std::filesystem::path sl_file_name
Definition: switches.h:31
std::shared_ptr< fml::UniqueFD > working_directory
Definition: switches.h:24
SourceOptions CreateSourceOptions(std::optional< TargetPlatform > target_platform=std::nullopt) const
Definition: switches.cc:320
bool AreValid(std::ostream &explain) const
Definition: switches.cc:252
static void PrintHelp(std::ostream &stream)
Definition: switches.cc:42
std::filesystem::path reflection_json_name
Definition: switches.h:35
std::filesystem::path spirv_file_name
Definition: switches.h:34
std::filesystem::path depfile_path
Definition: switches.h:38
std::filesystem::path source_file_name
Definition: switches.h:26
std::filesystem::path reflection_header_name
Definition: switches.h:36
TargetPlatform SelectDefaultTargetPlatform() const
Definition: switches.cc:312
int main(int argc, char const *argv[])
bool Main(const fml::CommandLine &command_line)
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
static bool OutputDepfile(const Compiler &compiler, const Switches &switches)
bool GenerateShaderBundle(Switches &switches)
Parses the JSON shader bundle configuration and invokes the compiler multiple times to produce a shad...
static Reflector::Options CreateReflectorOptions(const SourceOptions &options, const Switches &switches)
static bool OutputReflectionData(const Compiler &compiler, const Switches &switches, const SourceOptions &options)
static bool OutputIPLR(const Switches &switches, const std::shared_ptr< fml::Mapping > &source_file_mapping)
std::string Utf8FromPath(const std::filesystem::path &path)
Converts a native format path to a utf8 string.
Definition: utilities.cc:30
bool TargetPlatformBundlesSkSL(TargetPlatform platform)
Definition: types.cc:315
static std::shared_ptr< RuntimeStageData::Shader > CompileSkSL(std::shared_ptr< fml::Mapping > source_file_mapping, const Switches &switches)
bool TargetPlatformNeedsReflection(TargetPlatform platform)
Definition: types.cc:130
static bool OutputSLFile(const Compiler &compiler, const Switches &switches)