Flutter Impeller
shader_bundle.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 
10 
13 #include "impeller/shader_bundle/shader_bundle_flatbuffers.h"
14 #include "third_party/json/include/nlohmann/json.hpp"
15 
16 namespace impeller {
17 namespace compiler {
18 
19 std::optional<ShaderBundleConfig> ParseShaderBundleConfig(
20  const std::string& bundle_config_json,
21  std::ostream& error_stream) {
22  auto json = nlohmann::json::parse(bundle_config_json, nullptr, false);
23  if (json.is_discarded() || !json.is_object()) {
24  error_stream << "The shader bundle is not a valid JSON object."
25  << std::endl;
26  return std::nullopt;
27  }
28 
29  ShaderBundleConfig bundle;
30  for (auto& [shader_name, shader_value] : json.items()) {
31  if (bundle.find(shader_name) != bundle.end()) {
32  error_stream << "Duplicate shader \"" << shader_name << "\"."
33  << std::endl;
34  return std::nullopt;
35  }
36  if (!shader_value.is_object()) {
37  error_stream << "Invalid shader entry \"" << shader_name
38  << "\": Entry is not a JSON object." << std::endl;
39  return std::nullopt;
40  }
41 
42  ShaderConfig shader;
43 
44  if (!shader_value.contains("file")) {
45  error_stream << "Invalid shader entry \"" << shader_name
46  << "\": Missing required \"file\" field." << std::endl;
47  return std::nullopt;
48  }
49  shader.source_file_name = shader_value["file"];
50 
51  if (!shader_value.contains("type")) {
52  error_stream << "Invalid shader entry \"" << shader_name
53  << "\": Missing required \"type\" field." << std::endl;
54  return std::nullopt;
55  }
56  shader.type = SourceTypeFromString(shader_value["type"]);
57  if (shader.type == SourceType::kUnknown) {
58  error_stream << "Invalid shader entry \"" << shader_name
59  << "\": Shader type " << shader_value["type"]
60  << " is unknown." << std::endl;
61  return std::nullopt;
62  }
63 
64  shader.language = shader_value.contains("language")
65  ? ToSourceLanguage(shader_value["language"])
67  if (shader.language == SourceLanguage::kUnknown) {
68  error_stream << "Invalid shader entry \"" << shader_name
69  << "\": Unknown language type " << shader_value["language"]
70  << "." << std::endl;
71  return std::nullopt;
72  }
73 
74  shader.entry_point = shader_value.contains("entry_point")
75  ? shader_value["entry_point"]
76  : "main";
77 
78  bundle[shader_name] = shader;
79  }
80 
81  return bundle;
82 }
83 
84 static std::unique_ptr<fb::shaderbundle::BackendShaderT>
86  SourceOptions& options,
87  const std::string& shader_name,
88  const ShaderConfig& shader_config) {
89  auto result = std::make_unique<fb::shaderbundle::BackendShaderT>();
90 
91  std::shared_ptr<fml::FileMapping> source_file_mapping =
92  fml::FileMapping::CreateReadOnly(shader_config.source_file_name);
93  if (!source_file_mapping) {
94  std::cerr << "Could not open file for bundled shader \"" << shader_name
95  << "\"." << std::endl;
96  return nullptr;
97  }
98 
99  /// Override options.
100  options.target_platform = target_platform;
101  options.file_name = shader_name; // This is just used for error messages.
102  options.type = shader_config.type;
103  options.source_language = shader_config.language;
105  shader_config.source_file_name, options.type, options.source_language,
106  shader_config.entry_point);
107 
108  Reflector::Options reflector_options;
109  reflector_options.target_platform = options.target_platform;
110  reflector_options.entry_point_name = options.entry_point_name;
111  reflector_options.shader_name = shader_name;
112 
113  Compiler compiler(source_file_mapping, options, reflector_options);
114  if (!compiler.IsValid()) {
115  std::cerr << "Compilation failed for bundled shader \"" << shader_name
116  << "\"." << std::endl;
117  std::cerr << compiler.GetErrorMessages() << std::endl;
118  return nullptr;
119  }
120 
121  auto reflector = compiler.GetReflector();
122  if (reflector == nullptr) {
123  std::cerr << "Could not create reflector for bundled shader \""
124  << shader_name << "\"." << std::endl;
125  return nullptr;
126  }
127 
128  auto bundle_data = reflector->GetShaderBundleData();
129  if (!bundle_data) {
130  std::cerr << "Bundled shader information was nil for \"" << shader_name
131  << "\"." << std::endl;
132  return nullptr;
133  }
134 
135  result = bundle_data->CreateFlatbuffer();
136  if (!result) {
137  std::cerr << "Failed to create flatbuffer for bundled shader \""
138  << shader_name << "\"." << std::endl;
139  return nullptr;
140  }
141 
142  return result;
143 }
144 
145 static std::unique_ptr<fb::shaderbundle::ShaderT> GenerateShaderFB(
146  SourceOptions options,
147  const std::string& shader_name,
148  const ShaderConfig& shader_config) {
149  auto result = std::make_unique<fb::shaderbundle::ShaderT>();
150  result->name = shader_name;
151  result->metal_ios = GenerateShaderBackendFB(
152  TargetPlatform::kMetalIOS, options, shader_name, shader_config);
153  if (!result->metal_ios) {
154  return nullptr;
155  }
156  result->metal_desktop = GenerateShaderBackendFB(
157  TargetPlatform::kMetalDesktop, options, shader_name, shader_config);
158  if (!result->metal_desktop) {
159  return nullptr;
160  }
161  result->opengl_es = GenerateShaderBackendFB(
162  TargetPlatform::kOpenGLES, options, shader_name, shader_config);
163  if (!result->opengl_es) {
164  return nullptr;
165  }
166  result->opengl_desktop = GenerateShaderBackendFB(
167  TargetPlatform::kOpenGLDesktop, options, shader_name, shader_config);
168  if (!result->opengl_desktop) {
169  return nullptr;
170  }
171  result->vulkan = GenerateShaderBackendFB(TargetPlatform::kVulkan, options,
172  shader_name, shader_config);
173  if (!result->vulkan) {
174  return nullptr;
175  }
176  return result;
177 }
178 
179 std::optional<fb::shaderbundle::ShaderBundleT> GenerateShaderBundleFlatbuffer(
180  const std::string& bundle_config_json,
181  const SourceOptions& options) {
182  // --------------------------------------------------------------------------
183  /// 1. Parse the bundle configuration.
184  ///
185 
186  std::optional<ShaderBundleConfig> bundle_config =
187  ParseShaderBundleConfig(bundle_config_json, std::cerr);
188  if (!bundle_config) {
189  return std::nullopt;
190  }
191 
192  // --------------------------------------------------------------------------
193  /// 2. Build the deserialized shader bundle.
194  ///
195 
196  fb::shaderbundle::ShaderBundleT shader_bundle;
197 
198  for (const auto& [shader_name, shader_config] : bundle_config.value()) {
199  std::unique_ptr<fb::shaderbundle::ShaderT> shader =
200  GenerateShaderFB(options, shader_name, shader_config);
201  if (!shader) {
202  return std::nullopt;
203  }
204  shader_bundle.shaders.push_back(std::move(shader));
205  }
206 
207  return shader_bundle;
208 }
209 
211  // --------------------------------------------------------------------------
212  /// 1. Parse the shader bundle and generate the flatbuffer result.
213  ///
214 
215  auto shader_bundle = GenerateShaderBundleFlatbuffer(
216  switches.shader_bundle, switches.CreateSourceOptions());
217  if (!shader_bundle.has_value()) {
218  // Specific error messages are already handled by
219  // GenerateShaderBundleFlatbuffer.
220  return false;
221  }
222 
223  // --------------------------------------------------------------------------
224  /// 2. Serialize the shader bundle and write to disk.
225  ///
226 
227  auto builder = std::make_shared<flatbuffers::FlatBufferBuilder>();
228  builder->Finish(fb::shaderbundle::ShaderBundle::Pack(*builder.get(),
229  &shader_bundle.value()),
230  fb::shaderbundle::ShaderBundleIdentifier());
231  auto mapping = std::make_shared<fml::NonOwnedMapping>(
232  builder->GetBufferPointer(), builder->GetSize(),
233  [builder](auto, auto) {});
234 
235  auto sl_file_name = std::filesystem::absolute(
236  std::filesystem::current_path() / switches.sl_file_name);
237 
238  if (!fml::WriteAtomically(*switches.working_directory, //
239  Utf8FromPath(sl_file_name).c_str(), //
240  *mapping //
241  )) {
242  std::cerr << "Could not write file to " << switches.sl_file_name
243  << std::endl;
244  return false;
245  }
246  // Tools that consume the runtime stage data expect the access mode to
247  // be 0644.
248  if (!SetPermissiveAccess(sl_file_name)) {
249  return false;
250  }
251 
252  return true;
253 }
254 
255 } // namespace compiler
256 } // namespace impeller
const Reflector * GetReflector() const
Definition: compiler.cc:547
std::string GetErrorMessages() const
Definition: compiler.cc:504
std::shared_ptr< ShaderBundleData > GetShaderBundleData() const
Definition: reflector.cc:134
std::shared_ptr< fml::UniqueFD > working_directory
Definition: switches.h:23
SourceOptions CreateSourceOptions(std::optional< TargetPlatform > target_platform=std::nullopt) const
Definition: switches.cc:308
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::optional< fb::shaderbundle::ShaderBundleT > GenerateShaderBundleFlatbuffer(const std::string &bundle_config_json, const SourceOptions &options)
Parses the JSON shader bundle configuration and invokes the compiler multiple times to produce a shad...
static std::unique_ptr< fb::shaderbundle::BackendShaderT > GenerateShaderBackendFB(TargetPlatform target_platform, SourceOptions &options, const std::string &shader_name, const ShaderConfig &shader_config)
bool GenerateShaderBundle(Switches &switches)
Parses the JSON shader bundle configuration and invokes the compiler multiple times to produce a shad...
std::unordered_map< std::string, ShaderConfig > ShaderBundleConfig
Definition: types.h:82
SourceType SourceTypeFromString(std::string name)
Definition: types.cc:46
std::optional< ShaderBundleConfig > ParseShaderBundleConfig(const std::string &bundle_config_json, std::ostream &error_stream)
Parse a shader bundle configuration from a given JSON string.
static std::unique_ptr< fb::shaderbundle::ShaderT > GenerateShaderFB(SourceOptions options, const std::string &shader_name, const ShaderConfig &shader_config)
std::string EntryPointFunctionNameFromSourceName(const std::string &file_name, SourceType type, SourceLanguage source_language, const std::string &entry_point_name)
Definition: types.cc:113
std::string Utf8FromPath(const std::filesystem::path &path)
Converts a native format path to a utf8 string.
Definition: utilities.cc:30
SourceLanguage ToSourceLanguage(const std::string &source_language)
Definition: types.cc:64
A shader config parsed as part of a ShaderBundleConfig.
Definition: types.h:75
std::string source_file_name
Definition: types.h:76
SourceLanguage language
Definition: types.h:78