Flutter Impeller
runtime_stage.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 #include <atomic>
9 #include <cstdint>
10 #include <memory>
11 #include <sstream>
12 #include <string>
13 
14 #include "fml/mapping.h"
18 #include "impeller/runtime_stage/runtime_stage_flatbuffers.h"
19 #include "runtime_stage_types_flatbuffers.h"
20 
21 namespace impeller {
22 
23 namespace {
24 // Process-unique fallback library id for runtime stages decoded without an
25 // asset path (e.g. tests, future in-memory APIs). Kept local to this
26 // translation unit because `impeller::renderer` (where the equivalent
27 // `ShaderKey::MakeFallbackLibraryId` lives) already depends on
28 // `impeller::runtime_stage`, so it is not reachable from here.
29 std::string MakeFallbackLibraryId() {
30  static std::atomic<uint64_t> counter{0};
31  return "auto:" +
32  std::to_string(counter.fetch_add(1, std::memory_order_relaxed));
33 }
34 } // namespace
35 
36 static RuntimeUniformType ToType(fb::UniformDataType type) {
37  switch (type) {
44  }
45  FML_UNREACHABLE();
46 }
47 
48 static RuntimeShaderStage ToShaderStage(fb::Stage stage) {
49  switch (stage) {
50  case fb::Stage::kVertex:
52  case fb::Stage::kFragment:
54  case fb::Stage::kCompute:
56  }
57  FML_UNREACHABLE();
58 }
59 
60 /// The generated name from GLSLang/shaderc for the UBO containing non-opaque
61 /// uniforms specified in the user-written runtime effect shader.
62 ///
63 /// Vulkan does not allow non-opaque uniforms outside of a UBO.
64 const char* RuntimeStage::kVulkanUBOName =
65  "_RESERVED_IDENTIFIER_FIXUP_gl_DefaultUniformBlock";
66 
67 absl::StatusOr<RuntimeStage> RuntimeStage::Create(
68  const fb::RuntimeStage* runtime_stage,
69  const std::shared_ptr<fml::Mapping>& payload) {
70  if (!runtime_stage) {
71  return absl::InvalidArgumentError("Runtime stage is null.");
72  }
73 
74  RuntimeStage stage(payload);
75  stage.stage_ = ToShaderStage(runtime_stage->stage());
76  stage.entrypoint_ = runtime_stage->entrypoint()->str();
77  stage.library_id_ = MakeFallbackLibraryId();
78 
79  auto* uniforms = runtime_stage->uniforms();
80 
81  // Note: image bindings are screwy and will always have the same offset.
82  // track the binding of the UBO to determine where the image bindings go.
83  // This is only guaranteed to give us the correct bindings if there is a
84  // single sampler2D.
85  std::optional<size_t> ubo_id;
86  if (uniforms) {
87  for (auto i = uniforms->begin(), end = uniforms->end(); i != end; i++) {
89  desc.name = i->name()->str();
90  desc.location = i->location();
91  desc.binding = i->binding();
92  desc.type = ToType(i->type());
93  if (desc.type == kStruct) {
94  ubo_id = desc.location;
95  desc.binding = desc.location;
96  }
98  static_cast<size_t>(i->rows()), static_cast<size_t>(i->columns())};
99  desc.bit_width = i->bit_width();
100  desc.array_elements = i->array_elements();
101  if (i->padding_layout()) {
102  for (const auto& byte_type : *i->padding_layout()) {
104  switch (byte_type) {
107  break;
110  break;
111  }
112  desc.padding_layout.push_back(type);
113  }
114  }
115  if (i->struct_fields()) {
116  for (const auto& elem : *i->struct_fields()) {
117  desc.struct_fields.emplace_back(
118  StructField{.name = elem->name()->str(),
119  .byte_size = static_cast<size_t>(elem->byte_size())});
120  }
121  }
122  desc.struct_float_count = i->struct_float_count();
123  stage.uniforms_.push_back(std::move(desc));
124  }
125  }
126 
127  stage.code_mapping_ = std::make_shared<fml::NonOwnedMapping>(
128  runtime_stage->shader()->data(), //
129  runtime_stage->shader()->size(), //
130  [payload = stage.payload_](auto, auto) {} //
131  );
132 
133  size_t binding = 64;
134  if (ubo_id.has_value() && ubo_id.value() == binding) {
135  binding++;
136  }
137  for (auto& uniform : stage.uniforms_) {
138  if (uniform.type == kSampledImage) {
139  uniform.binding = binding;
140  binding++;
141  if (ubo_id.has_value() && ubo_id.value() == binding) {
142  binding++;
143  }
144  }
145  }
146 
147  for (const auto& uniform : stage.GetUniforms()) {
148  if (uniform.type == kStruct) {
149  stage.descriptor_set_layouts_.push_back(DescriptorSetLayout{
150  static_cast<uint32_t>(uniform.location),
153  });
154  } else if (uniform.type == kSampledImage) {
155  stage.descriptor_set_layouts_.push_back(DescriptorSetLayout{
156  static_cast<uint32_t>(uniform.binding),
159  });
160  }
161  }
162 
163  return stage;
164 }
165 
166 std::unique_ptr<RuntimeStage> RuntimeStage::RuntimeStageIfPresent(
167  const fb::RuntimeStage* runtime_stage,
168  const std::shared_ptr<fml::Mapping>& payload) {
169  auto stage = Create(runtime_stage, payload);
170  if (!stage.ok()) {
171  return nullptr;
172  }
173  return std::make_unique<RuntimeStage>(std::move(*stage));
174 }
175 
176 absl::StatusOr<RuntimeStage::Map> RuntimeStage::DecodeRuntimeStages(
177  const std::shared_ptr<fml::Mapping>& payload) {
178  if (payload == nullptr || !payload->GetMapping()) {
179  return absl::InvalidArgumentError("Payload is null or empty.");
180  }
181  if (!fb::RuntimeStagesBufferHasIdentifier(payload->GetMapping())) {
182  return absl::InvalidArgumentError(
183  "Payload does not have valid identifier.");
184  }
185 
186  flatbuffers::Verifier verifier(payload->GetMapping(), payload->GetSize());
187  if (!fb::VerifyRuntimeStagesBuffer(verifier)) {
188  return absl::InvalidArgumentError(
189  "Runtime stages buffer failed verification.");
190  }
191 
192  auto raw_stages = fb::GetRuntimeStages(payload->GetMapping());
193  if (!raw_stages) {
194  return absl::InvalidArgumentError("Failed to get runtime stages.");
195  }
196 
197  const uint32_t version = raw_stages->format_version();
198  const auto expected =
199  static_cast<uint32_t>(fb::RuntimeStagesFormatVersion::kVersion);
200  if (version != expected) {
201  std::stringstream stream;
202  stream << "Unsupported runtime stages format version. Expected " << expected
203  << ", got " << version << ".";
204  return absl::InvalidArgumentError(stream.str());
205  }
206 
207  return Map{
209  RuntimeStageIfPresent(raw_stages->sksl(), payload)},
211  RuntimeStageIfPresent(raw_stages->metal(), payload)},
213  RuntimeStageIfPresent(raw_stages->opengles(), payload)},
215  RuntimeStageIfPresent(raw_stages->opengles3(), payload)},
217  RuntimeStageIfPresent(raw_stages->vulkan(), payload)},
218  };
219 }
220 
221 RuntimeStage::RuntimeStage(std::shared_ptr<fml::Mapping> payload)
222  : payload_(std::move(payload)) {}
223 
224 RuntimeStage::~RuntimeStage() = default;
225 RuntimeStage::RuntimeStage(RuntimeStage&&) = default;
226 RuntimeStage& RuntimeStage::operator=(RuntimeStage&&) = default;
227 
228 const std::shared_ptr<fml::Mapping>& RuntimeStage::GetCodeMapping() const {
229  return code_mapping_;
230 }
231 
232 const std::vector<RuntimeUniformDescription>& RuntimeStage::GetUniforms()
233  const {
234  return uniforms_;
235 }
236 
238  const std::string& name) const {
239  for (const auto& uniform : uniforms_) {
240  if (uniform.name == name) {
241  return &uniform;
242  }
243  }
244  return nullptr;
245 }
246 
247 const std::string& RuntimeStage::GetEntrypoint() const {
248  return entrypoint_;
249 }
250 
252  return stage_;
253 }
254 
255 bool RuntimeStage::IsDirty() const {
256  return is_dirty_;
257 }
258 
260  is_dirty_ = false;
261 }
262 
263 void RuntimeStage::SetLibraryId(std::string library_id) {
264  library_id_ = std::move(library_id);
265 }
266 
267 const std::string& RuntimeStage::GetLibraryId() const {
268  return library_id_;
269 }
270 
271 const std::vector<DescriptorSetLayout>& RuntimeStage::GetDescriptorSetLayouts()
272  const {
273  return descriptor_set_layouts_;
274 }
275 
276 } // namespace impeller
RuntimeStage(RuntimeStage &&)
const std::string & GetEntrypoint() const
const std::string & GetLibraryId() const
RuntimeStage & operator=(RuntimeStage &&)
static absl::StatusOr< RuntimeStage > Create(const fb::RuntimeStage *runtime_stage, const std::shared_ptr< fml::Mapping > &payload)
const std::vector< RuntimeUniformDescription > & GetUniforms() const
void SetLibraryId(std::string library_id)
const RuntimeUniformDescription * GetUniform(const std::string &name) const
std::map< RuntimeStageBackend, std::shared_ptr< RuntimeStage > > Map
Definition: runtime_stage.h:25
static const char * kVulkanUBOName
Definition: runtime_stage.h:23
const std::shared_ptr< fml::Mapping > & GetCodeMapping() const
const std::vector< DescriptorSetLayout > & GetDescriptorSetLayouts() const
static absl::StatusOr< Map > DecodeRuntimeStages(const std::shared_ptr< fml::Mapping > &payload)
RuntimeShaderStage GetShaderStage() const
constexpr ShaderStage ToShaderStage(RuntimeShaderStage stage)
Definition: shader_types.h:29
static RuntimeUniformType ToType(fb::UniformDataType type)
constexpr auto kPadding
Definition: comparable.h:93
RuntimeUniformDimensions dimensions
Definition: runtime_types.h:58
std::vector< StructField > struct_fields
Definition: runtime_types.h:64
std::vector< RuntimePaddingType > padding_layout
Definition: runtime_types.h:61
std::optional< size_t > array_elements
Definition: runtime_types.h:60
size_t binding
Location, but for Vulkan.
Definition: runtime_types.h:56
const size_t end