Flutter Impeller
spirv_compiler.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 
6 
7 #include <array>
8 
12 
13 namespace impeller {
14 namespace compiler {
15 
17  std::shared_ptr<const fml::Mapping> sources)
18  : options_(options), sources_(std::move(sources)) {}
19 
21 
22 std::shared_ptr<fml::Mapping> SPIRVCompiler::CompileToSPV(
23  std::stringstream& stream,
24  const shaderc::CompileOptions& spirv_options) const {
25  if (!sources_ || sources_->GetMapping() == nullptr) {
26  COMPILER_ERROR(stream) << "Invalid sources for SPIRV Compiler.";
27  return nullptr;
28  }
29 
30  shaderc::Compiler spv_compiler;
31  if (!spv_compiler.IsValid()) {
32  COMPILER_ERROR(stream) << "Could not initialize the "
34  << " to SPIRV compiler.";
35  return nullptr;
36  }
37 
38  const auto shader_kind = ToShaderCShaderKind(options_.type);
39 
40  if (shader_kind == shaderc_shader_kind::shaderc_glsl_infer_from_source) {
41  COMPILER_ERROR(stream) << "Could not figure out shader stage.";
42  return nullptr;
43  }
44 
45  auto result = std::make_shared<shaderc::SpvCompilationResult>(
46  spv_compiler.CompileGlslToSpv(
47  reinterpret_cast<const char*>(sources_->GetMapping()), // source_text
48  sources_->GetSize(), // source_text_size
49  shader_kind, // shader_kind
50  Utf8FromPath(options_.file_name).c_str(), // input_file_name
51  options_.entry_point_name.c_str(), // entry_point_name
52  spirv_options // options
53  ));
54  if (result->GetCompilationStatus() !=
55  shaderc_compilation_status::shaderc_compilation_status_success) {
57  << " to SPIRV failed; "
59  result->GetCompilationStatus())
60  << ". " << result->GetNumErrors() << " error(s) and "
61  << result->GetNumWarnings() << " warning(s).";
62  // It should normally be enough to check that there are errors or warnings,
63  // but some cases result in no errors or warnings and still have an error
64  // message. If there's a message we should print it.
65  if (result->GetNumErrors() > 0 || result->GetNumWarnings() > 0 ||
66  !result->GetErrorMessage().empty()) {
67  COMPILER_ERROR_NO_PREFIX(stream) << result->GetErrorMessage();
68  }
69  return nullptr;
70  }
71 
72  if (!result) {
73  COMPILER_ERROR(stream) << "Could not fetch SPIRV from compile job.";
74  return nullptr;
75  }
76 
77  const auto data_length = (result->cend() - result->cbegin()) *
78  sizeof(decltype(result)::element_type::element_type);
79 
80  return std::make_unique<fml::NonOwnedMapping>(
81  reinterpret_cast<const uint8_t*>(result->cbegin()), //
82  data_length, //
83  [result](auto, auto) {} //
84  );
85 }
86 
87 std::string SPIRVCompiler::GetSourcePrefix() const {
88  std::stringstream stream;
89  stream << options_.file_name << ": ";
90  return stream.str();
91 }
92 
93 static void SetDefaultLimitations(shaderc::CompileOptions& compiler_opts) {
94  using Limit = std::pair<shaderc_limit, int>;
95  static constexpr std::array<Limit, 83> limits = {
96  Limit{shaderc_limit::shaderc_limit_max_lights, 8},
97  Limit{shaderc_limit::shaderc_limit_max_clip_planes, 6},
98  Limit{shaderc_limit::shaderc_limit_max_texture_units, 2},
99  Limit{shaderc_limit::shaderc_limit_max_texture_coords, 8},
100  Limit{shaderc_limit::shaderc_limit_max_vertex_attribs, 16},
101  Limit{shaderc_limit::shaderc_limit_max_vertex_uniform_components, 4096},
102  Limit{shaderc_limit::shaderc_limit_max_varying_floats, 60},
103  Limit{shaderc_limit::shaderc_limit_max_vertex_texture_image_units, 16},
104  Limit{shaderc_limit::shaderc_limit_max_combined_texture_image_units, 80},
105  Limit{shaderc_limit::shaderc_limit_max_texture_image_units, 16},
106  Limit{shaderc_limit::shaderc_limit_max_fragment_uniform_components, 1024},
107  Limit{shaderc_limit::shaderc_limit_max_draw_buffers, 8},
108  Limit{shaderc_limit::shaderc_limit_max_vertex_uniform_vectors, 256},
109  Limit{shaderc_limit::shaderc_limit_max_varying_vectors, 15},
110  Limit{shaderc_limit::shaderc_limit_max_fragment_uniform_vectors, 256},
111  Limit{shaderc_limit::shaderc_limit_max_vertex_output_vectors, 16},
112  Limit{shaderc_limit::shaderc_limit_max_fragment_input_vectors, 15},
113  Limit{shaderc_limit::shaderc_limit_min_program_texel_offset, -8},
114  Limit{shaderc_limit::shaderc_limit_max_program_texel_offset, 7},
115  Limit{shaderc_limit::shaderc_limit_max_clip_distances, 8},
116  Limit{shaderc_limit::shaderc_limit_max_compute_work_group_count_x, 65535},
117  Limit{shaderc_limit::shaderc_limit_max_compute_work_group_count_y, 65535},
118  Limit{shaderc_limit::shaderc_limit_max_compute_work_group_count_z, 65535},
119  Limit{shaderc_limit::shaderc_limit_max_compute_work_group_size_x, 1024},
120  Limit{shaderc_limit::shaderc_limit_max_compute_work_group_size_y, 1024},
121  Limit{shaderc_limit::shaderc_limit_max_compute_work_group_size_z, 64},
122  Limit{shaderc_limit::shaderc_limit_max_compute_uniform_components, 512},
123  Limit{shaderc_limit::shaderc_limit_max_compute_texture_image_units, 16},
124  Limit{shaderc_limit::shaderc_limit_max_compute_image_uniforms, 8},
125  Limit{shaderc_limit::shaderc_limit_max_compute_atomic_counters, 8},
126  Limit{shaderc_limit::shaderc_limit_max_compute_atomic_counter_buffers, 1},
127  Limit{shaderc_limit::shaderc_limit_max_varying_components, 60},
128  Limit{shaderc_limit::shaderc_limit_max_vertex_output_components, 64},
129  Limit{shaderc_limit::shaderc_limit_max_geometry_input_components, 64},
130  Limit{shaderc_limit::shaderc_limit_max_geometry_output_components, 128},
131  Limit{shaderc_limit::shaderc_limit_max_fragment_input_components, 128},
132  Limit{shaderc_limit::shaderc_limit_max_image_units, 8},
133  Limit{shaderc_limit::
134  shaderc_limit_max_combined_image_units_and_fragment_outputs,
135  8},
136  Limit{shaderc_limit::shaderc_limit_max_combined_shader_output_resources,
137  8},
138  Limit{shaderc_limit::shaderc_limit_max_image_samples, 0},
139  Limit{shaderc_limit::shaderc_limit_max_vertex_image_uniforms, 0},
140  Limit{shaderc_limit::shaderc_limit_max_tess_control_image_uniforms, 0},
141  Limit{shaderc_limit::shaderc_limit_max_tess_evaluation_image_uniforms, 0},
142  Limit{shaderc_limit::shaderc_limit_max_geometry_image_uniforms, 0},
143  Limit{shaderc_limit::shaderc_limit_max_fragment_image_uniforms, 8},
144  Limit{shaderc_limit::shaderc_limit_max_combined_image_uniforms, 8},
145  Limit{shaderc_limit::shaderc_limit_max_geometry_texture_image_units, 16},
146  Limit{shaderc_limit::shaderc_limit_max_geometry_output_vertices, 256},
147  Limit{shaderc_limit::shaderc_limit_max_geometry_total_output_components,
148  1024},
149  Limit{shaderc_limit::shaderc_limit_max_geometry_uniform_components, 512},
150  Limit{shaderc_limit::shaderc_limit_max_geometry_varying_components, 60},
151  Limit{shaderc_limit::shaderc_limit_max_tess_control_input_components,
152  128},
153  Limit{shaderc_limit::shaderc_limit_max_tess_control_output_components,
154  128},
155  Limit{shaderc_limit::shaderc_limit_max_tess_control_texture_image_units,
156  16},
157  Limit{shaderc_limit::shaderc_limit_max_tess_control_uniform_components,
158  1024},
159  Limit{
160  shaderc_limit::shaderc_limit_max_tess_control_total_output_components,
161  4096},
162  Limit{shaderc_limit::shaderc_limit_max_tess_evaluation_input_components,
163  128},
164  Limit{shaderc_limit::shaderc_limit_max_tess_evaluation_output_components,
165  128},
166  Limit{
167  shaderc_limit::shaderc_limit_max_tess_evaluation_texture_image_units,
168  16},
169  Limit{shaderc_limit::shaderc_limit_max_tess_evaluation_uniform_components,
170  1024},
171  Limit{shaderc_limit::shaderc_limit_max_tess_patch_components, 120},
172  Limit{shaderc_limit::shaderc_limit_max_patch_vertices, 32},
173  Limit{shaderc_limit::shaderc_limit_max_tess_gen_level, 64},
174  Limit{shaderc_limit::shaderc_limit_max_viewports, 16},
175  Limit{shaderc_limit::shaderc_limit_max_vertex_atomic_counters, 0},
176  Limit{shaderc_limit::shaderc_limit_max_tess_control_atomic_counters, 0},
177  Limit{shaderc_limit::shaderc_limit_max_tess_evaluation_atomic_counters,
178  0},
179  Limit{shaderc_limit::shaderc_limit_max_geometry_atomic_counters, 0},
180  Limit{shaderc_limit::shaderc_limit_max_fragment_atomic_counters, 8},
181  Limit{shaderc_limit::shaderc_limit_max_combined_atomic_counters, 8},
182  Limit{shaderc_limit::shaderc_limit_max_atomic_counter_bindings, 1},
183  Limit{shaderc_limit::shaderc_limit_max_vertex_atomic_counter_buffers, 0},
184  Limit{
185  shaderc_limit::shaderc_limit_max_tess_control_atomic_counter_buffers,
186  0},
187  Limit{shaderc_limit::
188  shaderc_limit_max_tess_evaluation_atomic_counter_buffers,
189  0},
190  Limit{shaderc_limit::shaderc_limit_max_geometry_atomic_counter_buffers,
191  0},
192  Limit{shaderc_limit::shaderc_limit_max_fragment_atomic_counter_buffers,
193  0},
194  Limit{shaderc_limit::shaderc_limit_max_combined_atomic_counter_buffers,
195  1},
196  Limit{shaderc_limit::shaderc_limit_max_atomic_counter_buffer_size, 32},
197  Limit{shaderc_limit::shaderc_limit_max_transform_feedback_buffers, 4},
198  Limit{shaderc_limit::
199  shaderc_limit_max_transform_feedback_interleaved_components,
200  64},
201  Limit{shaderc_limit::shaderc_limit_max_cull_distances, 8},
202  Limit{shaderc_limit::shaderc_limit_max_combined_clip_and_cull_distances,
203  8},
204  Limit{shaderc_limit::shaderc_limit_max_samples, 4},
205  };
206  for (auto& [limit, value] : limits) {
207  compiler_opts.SetLimit(limit, value);
208  }
209 }
210 
211 static void SetBindingBaseOffset(shaderc::CompileOptions& options) {
212  constexpr uint32_t kBindingBaseOffset = 64;
213  static const shaderc_uniform_kind kUniformKinds[] = {
214  shaderc_uniform_kind::shaderc_uniform_kind_sampler,
215  shaderc_uniform_kind::shaderc_uniform_kind_texture,
216  shaderc_uniform_kind::shaderc_uniform_kind_image,
217  shaderc_uniform_kind::shaderc_uniform_kind_buffer, // UBOs
218  shaderc_uniform_kind::shaderc_uniform_kind_storage_buffer, // SSBOs
219  };
220 
221  for (size_t i = 0u; i < sizeof(kUniformKinds) / sizeof(shaderc_uniform_kind);
222  i++) {
223  options.SetBindingBaseForStage(
224  shaderc_shader_kind::shaderc_fragment_shader, //
225  kUniformKinds[i], //
226  kBindingBaseOffset //
227  );
228  }
229 }
230 
231 //------------------------------------------------------------------------------
232 /// @brief Wraps a shared includer so unique includers may be created to
233 /// satisfy the shaderc API. This is a simple proxy object and does
234 /// nothing.
235 ///
236 class UniqueIncluder final : public shaderc::CompileOptions::IncluderInterface {
237  public:
238  static std::unique_ptr<UniqueIncluder> Make(
239  std::shared_ptr<Includer> includer) {
240  // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
241  return std::unique_ptr<UniqueIncluder>(
242  new UniqueIncluder(std::move(includer)));
243  }
244 
245  // |shaderc::CompileOptions::IncluderInterface|
246  ~UniqueIncluder() = default;
247 
248  // |shaderc::CompileOptions::IncluderInterface|
249  shaderc_include_result* GetInclude(const char* requested_source,
250  shaderc_include_type type,
251  const char* requesting_source,
252  size_t include_depth) override {
253  return includer_->GetInclude(requested_source, //
254  type, //
255  requesting_source, //
256  include_depth //
257  );
258  }
259 
260  // |shaderc::CompileOptions::IncluderInterface|
261  void ReleaseInclude(shaderc_include_result* data) override {
262  return includer_->ReleaseInclude(data);
263  }
264 
265  private:
266  std::shared_ptr<Includer> includer_;
267 
268  explicit UniqueIncluder(std::shared_ptr<Includer> includer)
269  : includer_(std::move(includer)) {
270  FML_CHECK(includer_);
271  }
272 
273  UniqueIncluder(const UniqueIncluder&) = delete;
274 
275  UniqueIncluder& operator=(const UniqueIncluder&) = delete;
276 };
277 
278 shaderc::CompileOptions SPIRVCompilerOptions::BuildShadercOptions() const {
279  shaderc::CompileOptions options;
280 
281  SetDefaultLimitations(options);
282  SetBindingBaseOffset(options);
283 
284  options.SetAutoBindUniforms(true);
285  options.SetAutoMapLocations(true);
286  options.SetPreserveBindings(true);
287 
288  options.SetOptimizationLevel(optimization_level);
289 
290  if (generate_debug_info) {
291  options.SetGenerateDebugInfo();
292  }
293 
294  if (source_langauge.has_value()) {
295  options.SetSourceLanguage(source_langauge.value());
296  }
297 
298  if (source_profile.has_value()) {
299  options.SetForcedVersionProfile(source_profile->version,
300  source_profile->profile);
301  }
302 
303  if (target.has_value()) {
304  options.SetTargetEnvironment(target->env, target->version);
305  options.SetTargetSpirv(target->spirv_version);
306  }
307 
308  for (const auto& macro : macro_definitions) {
309  options.AddMacroDefinition(macro);
310  }
311 
312  if (includer) {
313  options.SetIncluder(UniqueIncluder::Make(includer));
314  }
315 
316  options.SetVulkanRulesRelaxed(relaxed_vulkan_rules);
317 
318  return options;
319 }
320 
321 } // namespace compiler
322 } // namespace impeller
GLenum type
std::shared_ptr< fml::Mapping > CompileToSPV(std::stringstream &error_stream, const shaderc::CompileOptions &spirv_options) const
SPIRVCompiler(const SourceOptions &options, std::shared_ptr< const fml::Mapping > sources)
Wraps a shared includer so unique includers may be created to satisfy the shaderc API....
void ReleaseInclude(shaderc_include_result *data) override
static std::unique_ptr< UniqueIncluder > Make(std::shared_ptr< Includer > includer)
shaderc_include_result * GetInclude(const char *requested_source, shaderc_include_type type, const char *requesting_source, size_t include_depth) override
int32_t value
#define COMPILER_ERROR_NO_PREFIX(stream)
Definition: logger.h:42
#define COMPILER_ERROR(stream)
Definition: logger.h:39
std::string SourceLanguageToString(SourceLanguage source_language)
Definition: types.cc:90
static void SetBindingBaseOffset(shaderc::CompileOptions &options)
shaderc_shader_kind ToShaderCShaderKind(SourceType type)
Definition: types.cc:174
static void SetDefaultLimitations(shaderc::CompileOptions &compiler_opts)
std::string ShaderCErrorToString(shaderc_compilation_status status)
Definition: types.cc:149
std::string Utf8FromPath(const std::filesystem::path &path)
Converts a native format path to a utf8 string.
Definition: utilities.cc:30
Definition: comparable.h:95
std::optional< shaderc_source_language > source_langauge
std::vector< std::string > macro_definitions
shaderc_optimization_level optimization_level
std::optional< SPIRVCompilerSourceProfile > source_profile
std::shared_ptr< Includer > includer
shaderc::CompileOptions BuildShadercOptions() const
std::optional< SPIRVCompilerTargetEnv > target
std::filesystem::path file_name
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:69