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
impeller::compiler::ShaderConfig::entry_point
std::string entry_point
Definition: types.h:78
impeller::compiler::Compiler
Definition: compiler.h:25
impeller::compiler::SourceOptions::type
SourceType type
Definition: source_options.h:21
shader_bundle.h
impeller::compiler::ShaderConfig::language
SourceLanguage language
Definition: types.h:77
impeller::compiler::SourceType::kUnknown
@ kUnknown
impeller::compiler::TargetPlatform::kMetalDesktop
@ kMetalDesktop
impeller::compiler::SourceOptions
Definition: source_options.h:20
impeller::compiler::TargetPlatform::kMetalIOS
@ kMetalIOS
impeller::compiler::ParseShaderBundleConfig
std::optional< ShaderBundleConfig > ParseShaderBundleConfig(const std::string &bundle_config_json, std::ostream &error_stream)
Parse a shader bundle configuration from a given JSON string.
Definition: shader_bundle.cc:19
impeller::compiler::TargetPlatform
TargetPlatform
Definition: types.h:29
impeller::compiler::SourceLanguage::kGLSL
@ kGLSL
impeller::compiler::Reflector::Options::target_platform
TargetPlatform target_platform
Definition: reflector.h:149
reflector.h
impeller::compiler::ShaderConfig::type
SourceType type
Definition: types.h:76
impeller::compiler::SourceOptions::source_language
SourceLanguage source_language
Definition: source_options.h:23
runtime_stage.h
impeller::compiler::ToSourceLanguage
SourceLanguage ToSourceLanguage(const std::string &source_language)
Definition: types.cc:64
impeller::compiler::TargetPlatform::kVulkan
@ kVulkan
impeller::compiler::Reflector::Options::shader_name
std::string shader_name
Definition: reflector.h:151
impeller::compiler::SourceOptions::entry_point_name
std::string entry_point_name
Definition: source_options.h:27
impeller::compiler::Compiler::GetErrorMessages
std::string GetErrorMessages() const
Definition: compiler.cc:455
source_options.h
impeller::compiler::EntryPointFunctionNameFromSourceName
std::string EntryPointFunctionNameFromSourceName(const std::string &file_name, SourceType type, SourceLanguage source_language, const std::string &entry_point_name)
Definition: types.cc:111
impeller::compiler::Compiler::GetReflector
const Reflector * GetReflector() const
Definition: compiler.cc:498
impeller::compiler::SourceOptions::file_name
std::string file_name
Definition: source_options.h:26
impeller::compiler::Reflector::Options
Definition: reflector.h:148
compiler.h
impeller::compiler::GenerateShaderBundleFlatbuffer
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...
Definition: shader_bundle.cc:179
utilities.h
impeller::compiler::SourceLanguage::kUnknown
@ kUnknown
impeller::compiler::Switches::sl_file_name
std::string sl_file_name
Definition: switches.h:30
impeller::compiler::GenerateShaderBackendFB
static std::unique_ptr< fb::shaderbundle::BackendShaderT > GenerateShaderBackendFB(TargetPlatform target_platform, SourceOptions &options, const std::string &shader_name, const ShaderConfig &shader_config)
Definition: shader_bundle.cc:85
impeller::compiler::SourceOptions::target_platform
TargetPlatform target_platform
Definition: source_options.h:22
impeller::compiler::ShaderBundleConfig
std::unordered_map< std::string, ShaderConfig > ShaderBundleConfig
Definition: types.h:81
impeller::compiler::Compiler::IsValid
bool IsValid() const
Definition: compiler.cc:445
impeller::compiler::GenerateShaderFB
static std::unique_ptr< fb::shaderbundle::ShaderT > GenerateShaderFB(SourceOptions options, const std::string &shader_name, const ShaderConfig &shader_config)
Definition: shader_bundle.cc:145
impeller::compiler::TargetPlatform::kOpenGLDesktop
@ kOpenGLDesktop
impeller::compiler::SourceTypeFromString
SourceType SourceTypeFromString(std::string name)
Definition: types.cc:46
impeller::compiler::ShaderConfig::source_file_name
std::string source_file_name
Definition: types.h:75
impeller::compiler::TargetPlatform::kOpenGLES
@ kOpenGLES
impeller::compiler::Switches::CreateSourceOptions
SourceOptions CreateSourceOptions(std::optional< TargetPlatform > target_platform=std::nullopt) const
Definition: switches.cc:307
impeller::compiler::SetPermissiveAccess
bool SetPermissiveAccess(const std::filesystem::path &p)
Sets the file access mode of the file at path 'p' to 0644.
Definition: utilities.cc:16
impeller::compiler::Reflector::GetShaderBundleData
std::shared_ptr< ShaderBundleData > GetShaderBundleData() const
Definition: reflector.cc:134
impeller::compiler::Utf8FromPath
std::string Utf8FromPath(const std::filesystem::path &path)
Converts a native format path to a utf8 string.
Definition: utilities.cc:30
impeller::compiler::Switches
Definition: switches.h:21
impeller::compiler::Reflector::Options::entry_point_name
std::string entry_point_name
Definition: reflector.h:150
impeller
Definition: aiks_blur_unittests.cc:20
impeller::compiler::GenerateShaderBundle
bool GenerateShaderBundle(Switches &switches)
Parses the JSON shader bundle configuration and invokes the compiler multiple times to produce a shad...
Definition: shader_bundle.cc:210
impeller::compiler::ShaderConfig
A shader config parsed as part of a ShaderBundleConfig.
Definition: types.h:74
impeller::compiler::Switches::working_directory
std::shared_ptr< fml::UniqueFD > working_directory
Definition: switches.h:23
types.h
impeller::compiler::Switches::shader_bundle
std::string shader_bundle
Definition: switches.h:32