Flutter Engine
The Flutter Engine
reflector.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// FLUTTER_NOLINT: https://github.com/flutter/flutter/issues/105732
6
8
9#include <atomic>
10#include <optional>
11#include <set>
12#include <sstream>
13
14#include "flutter/fml/logging.h"
15#include "fml/backtrace.h"
28#include "spirv_common.hpp"
29
30namespace impeller {
31namespace compiler {
32
33static std::string ExecutionModelToString(spv::ExecutionModel model) {
34 switch (model) {
35 case spv::ExecutionModel::ExecutionModelVertex:
36 return "vertex";
37 case spv::ExecutionModel::ExecutionModelFragment:
38 return "fragment";
39 case spv::ExecutionModel::ExecutionModelGLCompute:
40 return "compute";
41 default:
42 return "unsupported";
43 }
44}
45
46static std::string StringToShaderStage(const std::string& str) {
47 if (str == "vertex") {
48 return "ShaderStage::kVertex";
49 }
50
51 if (str == "fragment") {
52 return "ShaderStage::kFragment";
53 }
54
55 if (str == "compute") {
56 return "ShaderStage::kCompute";
57 }
58
59 return "ShaderStage::kUnknown";
60}
61
63 const std::shared_ptr<const spirv_cross::ParsedIR>& ir,
64 const std::shared_ptr<fml::Mapping>& shader_data,
66 : options_(std::move(options)),
67 ir_(ir),
68 shader_data_(shader_data),
69 compiler_(compiler) {
70 if (!ir_ || !compiler_) {
71 return;
72 }
73
74 if (auto template_arguments = GenerateTemplateArguments();
75 template_arguments.has_value()) {
76 template_arguments_ =
77 std::make_unique<nlohmann::json>(std::move(template_arguments.value()));
78 } else {
79 return;
80 }
81
82 reflection_header_ = GenerateReflectionHeader();
83 if (!reflection_header_) {
84 return;
85 }
86
87 reflection_cc_ = GenerateReflectionCC();
88 if (!reflection_cc_) {
89 return;
90 }
91
92 runtime_stage_shader_ = GenerateRuntimeStageData();
93
94 shader_bundle_data_ = GenerateShaderBundleData();
95 if (!shader_bundle_data_) {
96 return;
97 }
98
99 is_valid_ = true;
100}
101
102Reflector::~Reflector() = default;
103
104bool Reflector::IsValid() const {
105 return is_valid_;
106}
107
108std::shared_ptr<fml::Mapping> Reflector::GetReflectionJSON() const {
109 if (!is_valid_) {
110 return nullptr;
111 }
112
113 auto json_string =
114 std::make_shared<std::string>(template_arguments_->dump(2u));
115
116 return std::make_shared<fml::NonOwnedMapping>(
117 reinterpret_cast<const uint8_t*>(json_string->data()),
118 json_string->size(), [json_string](auto, auto) {});
119}
120
121std::shared_ptr<fml::Mapping> Reflector::GetReflectionHeader() const {
122 return reflection_header_;
123}
124
125std::shared_ptr<fml::Mapping> Reflector::GetReflectionCC() const {
126 return reflection_cc_;
127}
128
129std::shared_ptr<RuntimeStageData::Shader> Reflector::GetRuntimeStageShaderData()
130 const {
131 return runtime_stage_shader_;
132}
133
134std::shared_ptr<ShaderBundleData> Reflector::GetShaderBundleData() const {
135 return shader_bundle_data_;
136}
137
138std::optional<nlohmann::json> Reflector::GenerateTemplateArguments() const {
139 nlohmann::json root;
140
141 const auto& entrypoints = compiler_->get_entry_points_and_stages();
142 if (entrypoints.size() != 1) {
143 VALIDATION_LOG << "Incorrect number of entrypoints in the shader. Found "
144 << entrypoints.size() << " but expected 1.";
145 return std::nullopt;
146 }
147
148 auto execution_model = entrypoints.front().execution_model;
149 {
150 root["entrypoint"] = options_.entry_point_name;
151 root["shader_name"] = options_.shader_name;
152 root["shader_stage"] = ExecutionModelToString(execution_model);
153 root["header_file_name"] = options_.header_file_name;
154 }
155
156 const auto shader_resources = compiler_->get_shader_resources();
157
158 // Subpass Inputs.
159 {
160 auto& subpass_inputs = root["subpass_inputs"] = nlohmann::json::array_t{};
161 if (auto subpass_inputs_json =
162 ReflectResources(shader_resources.subpass_inputs);
163 subpass_inputs_json.has_value()) {
164 for (auto subpass_input : subpass_inputs_json.value()) {
165 subpass_input["descriptor_type"] = "DescriptorType::kInputAttachment";
166 subpass_inputs.emplace_back(std::move(subpass_input));
167 }
168 } else {
169 return std::nullopt;
170 }
171 }
172
173 // Uniform and storage buffers.
174 {
175 auto& buffers = root["buffers"] = nlohmann::json::array_t{};
176 if (auto uniform_buffers_json =
177 ReflectResources(shader_resources.uniform_buffers);
178 uniform_buffers_json.has_value()) {
179 for (auto uniform_buffer : uniform_buffers_json.value()) {
180 uniform_buffer["descriptor_type"] = "DescriptorType::kUniformBuffer";
181 buffers.emplace_back(std::move(uniform_buffer));
182 }
183 } else {
184 return std::nullopt;
185 }
186 if (auto storage_buffers_json =
187 ReflectResources(shader_resources.storage_buffers);
188 storage_buffers_json.has_value()) {
189 for (auto uniform_buffer : storage_buffers_json.value()) {
190 uniform_buffer["descriptor_type"] = "DescriptorType::kStorageBuffer";
191 buffers.emplace_back(std::move(uniform_buffer));
192 }
193 } else {
194 return std::nullopt;
195 }
196 }
197
198 {
199 auto& stage_inputs = root["stage_inputs"] = nlohmann::json::array_t{};
200 if (auto stage_inputs_json = ReflectResources(
201 shader_resources.stage_inputs,
202 /*compute_offsets=*/execution_model == spv::ExecutionModelVertex);
203 stage_inputs_json.has_value()) {
204 stage_inputs = std::move(stage_inputs_json.value());
205 } else {
206 return std::nullopt;
207 }
208 }
209
210 {
211 auto combined_sampled_images =
212 ReflectResources(shader_resources.sampled_images);
213 auto images = ReflectResources(shader_resources.separate_images);
214 auto samplers = ReflectResources(shader_resources.separate_samplers);
215 if (!combined_sampled_images.has_value() || !images.has_value() ||
216 !samplers.has_value()) {
217 return std::nullopt;
218 }
219 auto& sampled_images = root["sampled_images"] = nlohmann::json::array_t{};
220 for (auto value : combined_sampled_images.value()) {
221 value["descriptor_type"] = "DescriptorType::kSampledImage";
222 sampled_images.emplace_back(std::move(value));
223 }
224 for (auto value : images.value()) {
225 value["descriptor_type"] = "DescriptorType::kImage";
226 sampled_images.emplace_back(std::move(value));
227 }
228 for (auto value : samplers.value()) {
229 value["descriptor_type"] = "DescriptorType::kSampledSampler";
230 sampled_images.emplace_back(std::move(value));
231 }
232 }
233
234 if (auto stage_outputs = ReflectResources(shader_resources.stage_outputs);
235 stage_outputs.has_value()) {
236 root["stage_outputs"] = std::move(stage_outputs.value());
237 } else {
238 return std::nullopt;
239 }
240
241 {
242 auto& struct_definitions = root["struct_definitions"] =
243 nlohmann::json::array_t{};
244 if (entrypoints.front().execution_model ==
245 spv::ExecutionModel::ExecutionModelVertex &&
246 !shader_resources.stage_inputs.empty()) {
247 if (auto struc =
248 ReflectPerVertexStructDefinition(shader_resources.stage_inputs);
249 struc.has_value()) {
250 struct_definitions.emplace_back(EmitStructDefinition(struc.value()));
251 } else {
252 // If there are stage inputs, it is an error to not generate a per
253 // vertex data struct for a vertex like shader stage.
254 return std::nullopt;
255 }
256 }
257
258 std::set<spirv_cross::ID> known_structs;
259 ir_->for_each_typed_id<spirv_cross::SPIRType>(
260 [&](uint32_t, const spirv_cross::SPIRType& type) {
261 if (type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
262 return;
263 }
264 // Skip structs that do not have layout offset decorations.
265 // These structs are used internally within the shader and are not
266 // part of the shader's interface.
267 for (size_t i = 0; i < type.member_types.size(); i++) {
268 if (!compiler_->has_member_decoration(type.self, i,
269 spv::DecorationOffset)) {
270 return;
271 }
272 }
273 if (known_structs.find(type.self) != known_structs.end()) {
274 // Iterating over types this way leads to duplicates which may cause
275 // duplicate struct definitions.
276 return;
277 }
278 known_structs.insert(type.self);
279 if (auto struc = ReflectStructDefinition(type.self);
280 struc.has_value()) {
281 struct_definitions.emplace_back(
282 EmitStructDefinition(struc.value()));
283 }
284 });
285 }
286
287 root["bind_prototypes"] =
288 EmitBindPrototypes(shader_resources, execution_model);
289
290 return root;
291}
292
293std::shared_ptr<fml::Mapping> Reflector::GenerateReflectionHeader() const {
294 return InflateTemplate(kReflectionHeaderTemplate);
295}
296
297std::shared_ptr<fml::Mapping> Reflector::GenerateReflectionCC() const {
298 return InflateTemplate(kReflectionCCTemplate);
299}
300
301static std::optional<RuntimeStageBackend> GetRuntimeStageBackend(
302 TargetPlatform target_platform) {
303 switch (target_platform) {
310 return std::nullopt;
319 }
321}
322
323std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
324 const {
326 if (!backend.has_value()) {
327 return nullptr;
328 }
329
330 const auto& entrypoints = compiler_->get_entry_points_and_stages();
331 if (entrypoints.size() != 1u) {
332 VALIDATION_LOG << "Single entrypoint not found.";
333 return nullptr;
334 }
335 auto data = std::make_unique<RuntimeStageData::Shader>();
336 data->entrypoint = options_.entry_point_name;
337 data->stage = entrypoints.front().execution_model;
338 data->shader = shader_data_;
339 data->backend = backend.value();
340
341 // Sort the IR so that the uniforms are in declaration order.
342 std::vector<spirv_cross::ID> uniforms =
343 SortUniforms(ir_.get(), compiler_.GetCompiler());
344 for (auto& sorted_id : uniforms) {
345 auto var = ir_->ids[sorted_id].get<spirv_cross::SPIRVariable>();
346 const auto spir_type = compiler_->get_type(var.basetype);
347 UniformDescription uniform_description;
348 uniform_description.name = compiler_->get_name(var.self);
349 uniform_description.location = compiler_->get_decoration(
350 var.self, spv::Decoration::DecorationLocation);
351 uniform_description.binding =
352 compiler_->get_decoration(var.self, spv::Decoration::DecorationBinding);
353 uniform_description.type = spir_type.basetype;
354 uniform_description.rows = spir_type.vecsize;
355 uniform_description.columns = spir_type.columns;
356 uniform_description.bit_width = spir_type.width;
357 uniform_description.array_elements = GetArrayElements(spir_type);
359 spir_type.basetype ==
360 spirv_cross::SPIRType::BaseType::SampledImage)
361 << "Vulkan runtime effect had unexpected uniforms outside of the "
362 "uniform buffer object.";
363 data->uniforms.emplace_back(std::move(uniform_description));
364 }
365
366 const auto ubos = compiler_->get_shader_resources().uniform_buffers;
367 if (data->backend == RuntimeStageBackend::kVulkan && !ubos.empty()) {
368 if (ubos.size() != 1 && ubos[0].name != RuntimeStage::kVulkanUBOName) {
369 VALIDATION_LOG << "Expected a single UBO resource named "
370 "'"
372 << "' "
373 "for Vulkan runtime stage backend.";
374 return nullptr;
375 }
376
377 const auto& ubo = ubos[0];
378
379 size_t binding =
380 compiler_->get_decoration(ubo.id, spv::Decoration::DecorationBinding);
381 auto members = ReadStructMembers(ubo.type_id);
382 std::vector<uint8_t> struct_layout;
383 size_t float_count = 0;
384
385 for (size_t i = 0; i < members.size(); i += 1) {
386 const auto& member = members[i];
387 std::vector<int> bytes;
388 switch (member.underlying_type) {
390 size_t padding_count =
391 (member.size + sizeof(float) - 1) / sizeof(float);
392 while (padding_count > 0) {
393 struct_layout.push_back(0);
394 padding_count--;
395 }
396 break;
397 }
399 size_t member_float_count = member.byte_length / sizeof(float);
400 float_count += member_float_count;
401 while (member_float_count > 0) {
402 struct_layout.push_back(1);
403 member_float_count--;
404 }
405 break;
406 }
408 VALIDATION_LOG << "Non-floating-type struct member " << member.name
409 << " is not supported.";
410 return nullptr;
411 }
412 }
413 data->uniforms.emplace_back(UniformDescription{
414 .name = ubo.name,
415 .location = binding,
416 .binding = binding,
417 .type = spirv_cross::SPIRType::Struct,
418 .struct_layout = std::move(struct_layout),
419 .struct_float_count = float_count,
420 });
421 }
422
423 // We only need to worry about storing vertex attributes.
424 if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
425 const auto inputs = compiler_->get_shader_resources().stage_inputs;
426 auto input_offsets = ComputeOffsets(inputs);
427 for (const auto& input : inputs) {
428 std::optional<size_t> offset = GetOffset(input.id, input_offsets);
429
430 const auto type = compiler_->get_type(input.type_id);
431
432 InputDescription input_description;
433 input_description.name = input.name;
434 input_description.location = compiler_->get_decoration(
435 input.id, spv::Decoration::DecorationLocation);
436 input_description.set = compiler_->get_decoration(
437 input.id, spv::Decoration::DecorationDescriptorSet);
438 input_description.binding = compiler_->get_decoration(
439 input.id, spv::Decoration::DecorationBinding);
440 input_description.type = type.basetype;
441 input_description.bit_width = type.width;
442 input_description.vec_size = type.vecsize;
443 input_description.columns = type.columns;
444 input_description.offset = offset.value_or(0u);
445 data->inputs.emplace_back(std::move(input_description));
446 }
447 }
448
449 return data;
450}
451
452std::shared_ptr<ShaderBundleData> Reflector::GenerateShaderBundleData() const {
453 const auto& entrypoints = compiler_->get_entry_points_and_stages();
454 if (entrypoints.size() != 1u) {
455 VALIDATION_LOG << "Single entrypoint not found.";
456 return nullptr;
457 }
458 auto data = std::make_shared<ShaderBundleData>(
459 options_.entry_point_name, //
460 entrypoints.front().execution_model, //
461 options_.target_platform //
462 );
463 data->SetShaderData(shader_data_);
464
465 const auto uniforms = compiler_->get_shader_resources().uniform_buffers;
466 for (const auto& uniform : uniforms) {
467 ShaderBundleData::ShaderUniformStruct uniform_struct;
468 uniform_struct.name = uniform.name;
469 uniform_struct.ext_res_0 = compiler_.GetExtendedMSLResourceBinding(
471 uniform_struct.set = compiler_->get_decoration(
472 uniform.id, spv::Decoration::DecorationDescriptorSet);
473 uniform_struct.binding = compiler_->get_decoration(
474 uniform.id, spv::Decoration::DecorationBinding);
475
476 const auto type = compiler_->get_type(uniform.type_id);
477 if (type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
478 std::cerr << "Error: Uniform \"" << uniform.name
479 << "\" is not a struct. All Flutter GPU shader uniforms must "
480 "be structs."
481 << std::endl;
482 return nullptr;
483 }
484
485 size_t size_in_bytes = 0;
486 for (const auto& struct_member : ReadStructMembers(uniform.type_id)) {
487 size_in_bytes += struct_member.byte_length;
488 if (StringStartsWith(struct_member.name, "_PADDING_")) {
489 continue;
490 }
491 ShaderBundleData::ShaderUniformStructField uniform_struct_field;
492 uniform_struct_field.name = struct_member.name;
493 uniform_struct_field.type = struct_member.base_type;
494 uniform_struct_field.offset_in_bytes = struct_member.offset;
495 uniform_struct_field.element_size_in_bytes = struct_member.size;
496 uniform_struct_field.total_size_in_bytes = struct_member.byte_length;
497 uniform_struct_field.array_elements = struct_member.array_elements;
498 uniform_struct.fields.push_back(uniform_struct_field);
499 }
500 uniform_struct.size_in_bytes = size_in_bytes;
501
502 data->AddUniformStruct(uniform_struct);
503 }
504
505 const auto sampled_images = compiler_->get_shader_resources().sampled_images;
506 for (const auto& image : sampled_images) {
507 ShaderBundleData::ShaderUniformTexture uniform_texture;
508 uniform_texture.name = image.name;
509 uniform_texture.ext_res_0 = compiler_.GetExtendedMSLResourceBinding(
511 uniform_texture.set = compiler_->get_decoration(
512 image.id, spv::Decoration::DecorationDescriptorSet);
513 uniform_texture.binding =
514 compiler_->get_decoration(image.id, spv::Decoration::DecorationBinding);
515 data->AddUniformTexture(uniform_texture);
516 }
517
518 // We only need to worry about storing vertex attributes.
519 if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
520 const auto inputs = compiler_->get_shader_resources().stage_inputs;
521 auto input_offsets = ComputeOffsets(inputs);
522 for (const auto& input : inputs) {
523 std::optional<size_t> offset = GetOffset(input.id, input_offsets);
524
525 const auto type = compiler_->get_type(input.type_id);
526
527 InputDescription input_description;
528 input_description.name = input.name;
529 input_description.location = compiler_->get_decoration(
530 input.id, spv::Decoration::DecorationLocation);
531 input_description.set = compiler_->get_decoration(
532 input.id, spv::Decoration::DecorationDescriptorSet);
533 input_description.binding = compiler_->get_decoration(
534 input.id, spv::Decoration::DecorationBinding);
535 input_description.type = type.basetype;
536 input_description.bit_width = type.width;
537 input_description.vec_size = type.vecsize;
538 input_description.columns = type.columns;
539 input_description.offset = offset.value_or(0u);
540 data->AddInputDescription(std::move(input_description));
541 }
542 }
543
544 return data;
545}
546
547std::optional<uint32_t> Reflector::GetArrayElements(
548 const spirv_cross::SPIRType& type) const {
549 if (type.array.empty()) {
550 return std::nullopt;
551 }
552 FML_CHECK(type.array.size() == 1)
553 << "Multi-dimensional arrays are not supported.";
554 FML_CHECK(type.array_size_literal.front())
555 << "Must use a literal for array sizes.";
556 return type.array.front();
557}
558
560 switch (type) {
562 return "Metal Shading Language";
564 return "OpenGL Shading Language";
566 return "OpenGL Shading Language (Relaxed Vulkan Semantics)";
568 return "SkSL Shading Language";
569 }
571}
572
573std::shared_ptr<fml::Mapping> Reflector::InflateTemplate(
574 std::string_view tmpl) const {
575 inja::Environment env;
576 env.set_trim_blocks(true);
577 env.set_lstrip_blocks(true);
578
579 env.add_callback("camel_case", 1u, [](inja::Arguments& args) {
580 return ToCamelCase(args.at(0u)->get<std::string>());
581 });
582
583 env.add_callback("to_shader_stage", 1u, [](inja::Arguments& args) {
584 return StringToShaderStage(args.at(0u)->get<std::string>());
585 });
586
587 env.add_callback("get_generator_name", 0u,
588 [type = compiler_.GetType()](inja::Arguments& args) {
589 return ToString(type);
590 });
591
592 auto inflated_template =
593 std::make_shared<std::string>(env.render(tmpl, *template_arguments_));
594
595 return std::make_shared<fml::NonOwnedMapping>(
596 reinterpret_cast<const uint8_t*>(inflated_template->data()),
597 inflated_template->size(), [inflated_template](auto, auto) {});
598}
599
600std::vector<size_t> Reflector::ComputeOffsets(
601 const spirv_cross::SmallVector<spirv_cross::Resource>& resources) const {
602 std::vector<size_t> offsets(resources.size(), 0);
603 if (resources.size() == 0) {
604 return offsets;
605 }
606 for (const auto& resource : resources) {
607 const auto type = compiler_->get_type(resource.type_id);
608 auto location = compiler_->get_decoration(
609 resource.id, spv::Decoration::DecorationLocation);
610 // Malformed shader, will be caught later on.
611 if (location >= resources.size() || location < 0) {
612 location = 0;
613 }
614 offsets[location] = (type.width * type.vecsize) / 8;
615 }
616 for (size_t i = 1; i < resources.size(); i++) {
617 offsets[i] += offsets[i - 1];
618 }
619 for (size_t i = resources.size() - 1; i > 0; i--) {
620 offsets[i] = offsets[i - 1];
621 }
622 offsets[0] = 0;
623
624 return offsets;
625}
626
627std::optional<size_t> Reflector::GetOffset(
628 spirv_cross::ID id,
629 const std::vector<size_t>& offsets) const {
630 uint32_t location =
631 compiler_->get_decoration(id, spv::Decoration::DecorationLocation);
632 if (location >= offsets.size()) {
633 return std::nullopt;
634 }
635 return offsets[location];
636}
637
638std::optional<nlohmann::json::object_t> Reflector::ReflectResource(
639 const spirv_cross::Resource& resource,
640 std::optional<size_t> offset) const {
641 nlohmann::json::object_t result;
642
643 result["name"] = resource.name;
644 result["descriptor_set"] = compiler_->get_decoration(
645 resource.id, spv::Decoration::DecorationDescriptorSet);
646 result["binding"] = compiler_->get_decoration(
647 resource.id, spv::Decoration::DecorationBinding);
648 result["set"] = compiler_->get_decoration(
649 resource.id, spv::Decoration::DecorationDescriptorSet);
650 result["location"] = compiler_->get_decoration(
651 resource.id, spv::Decoration::DecorationLocation);
652 result["index"] =
653 compiler_->get_decoration(resource.id, spv::Decoration::DecorationIndex);
654 result["ext_res_0"] = compiler_.GetExtendedMSLResourceBinding(
656 result["ext_res_1"] = compiler_.GetExtendedMSLResourceBinding(
658 auto type = ReflectType(resource.type_id);
659 if (!type.has_value()) {
660 return std::nullopt;
661 }
662 result["type"] = std::move(type.value());
663 result["offset"] = offset.value_or(0u);
664 return result;
665}
666
667std::optional<nlohmann::json::object_t> Reflector::ReflectType(
668 const spirv_cross::TypeID& type_id) const {
669 nlohmann::json::object_t result;
670
671 const auto type = compiler_->get_type(type_id);
672
673 result["type_name"] = StructMember::BaseTypeToString(type.basetype);
674 result["bit_width"] = type.width;
675 result["vec_size"] = type.vecsize;
676 result["columns"] = type.columns;
677 auto& members = result["members"] = nlohmann::json::array_t{};
678 if (type.basetype == spirv_cross::SPIRType::BaseType::Struct) {
679 for (const auto& struct_member : ReadStructMembers(type_id)) {
680 auto member = nlohmann::json::object_t{};
681 member["name"] = struct_member.name;
682 member["type"] = struct_member.type;
683 member["base_type"] =
684 StructMember::BaseTypeToString(struct_member.base_type);
685 member["offset"] = struct_member.offset;
686 member["size"] = struct_member.size;
687 member["byte_length"] = struct_member.byte_length;
688 if (struct_member.array_elements.has_value()) {
689 member["array_elements"] = struct_member.array_elements.value();
690 } else {
691 member["array_elements"] = "std::nullopt";
692 }
693 members.emplace_back(std::move(member));
694 }
695 }
696
697 return result;
698}
699
700std::optional<nlohmann::json::array_t> Reflector::ReflectResources(
701 const spirv_cross::SmallVector<spirv_cross::Resource>& resources,
702 bool compute_offsets) const {
703 nlohmann::json::array_t result;
704 result.reserve(resources.size());
705 std::vector<size_t> offsets;
706 if (compute_offsets) {
707 offsets = ComputeOffsets(resources);
708 }
709 for (const auto& resource : resources) {
710 std::optional<size_t> maybe_offset = std::nullopt;
711 if (compute_offsets) {
712 maybe_offset = GetOffset(resource.id, offsets);
713 }
714 if (auto reflected = ReflectResource(resource, maybe_offset);
715 reflected.has_value()) {
716 result.emplace_back(std::move(reflected.value()));
717 } else {
718 return std::nullopt;
719 }
720 }
721 return result;
722}
723
724static std::string TypeNameWithPaddingOfSize(size_t size) {
725 std::stringstream stream;
726 stream << "Padding<" << size << ">";
727 return stream.str();
728}
729
730struct KnownType {
731 std::string name;
732 size_t byte_size = 0;
733};
734
735static std::optional<KnownType> ReadKnownScalarType(
736 spirv_cross::SPIRType::BaseType type) {
737 switch (type) {
738 case spirv_cross::SPIRType::BaseType::Boolean:
739 return KnownType{
740 .name = "bool",
741 .byte_size = sizeof(bool),
742 };
743 case spirv_cross::SPIRType::BaseType::Float:
744 return KnownType{
745 .name = "Scalar",
746 .byte_size = sizeof(Scalar),
747 };
748 case spirv_cross::SPIRType::BaseType::Half:
749 return KnownType{
750 .name = "Half",
751 .byte_size = sizeof(Half),
752 };
753 case spirv_cross::SPIRType::BaseType::UInt:
754 return KnownType{
755 .name = "uint32_t",
756 .byte_size = sizeof(uint32_t),
757 };
758 case spirv_cross::SPIRType::BaseType::Int:
759 return KnownType{
760 .name = "int32_t",
761 .byte_size = sizeof(int32_t),
762 };
763 default:
764 break;
765 }
766 return std::nullopt;
767}
768
769//------------------------------------------------------------------------------
770/// @brief Get the reflected struct size. In the vast majority of the
771/// cases, this is the same as the declared struct size as given by
772/// the compiler. But, additional padding may need to be introduced
773/// after the end of the struct to keep in line with the alignment
774/// requirement of the individual struct members. This method
775/// figures out the actual size of the reflected struct that can be
776/// referenced in native code.
777///
778/// @param[in] members The members
779///
780/// @return The reflected structure size.
781///
782static size_t GetReflectedStructSize(const std::vector<StructMember>& members) {
783 auto struct_size = 0u;
784 for (const auto& member : members) {
785 struct_size += member.byte_length;
786 }
787 return struct_size;
788}
789
790std::vector<StructMember> Reflector::ReadStructMembers(
791 const spirv_cross::TypeID& type_id) const {
792 const auto& struct_type = compiler_->get_type(type_id);
793 FML_CHECK(struct_type.basetype == spirv_cross::SPIRType::BaseType::Struct);
794
795 std::vector<StructMember> result;
796
797 size_t current_byte_offset = 0;
798 size_t max_member_alignment = 0;
799
800 for (size_t i = 0; i < struct_type.member_types.size(); i++) {
801 const auto& member = compiler_->get_type(struct_type.member_types[i]);
802 const auto struct_member_offset =
803 compiler_->type_struct_member_offset(struct_type, i);
804 auto array_elements = GetArrayElements(member);
805
806 if (struct_member_offset > current_byte_offset) {
807 const auto alignment_pad = struct_member_offset - current_byte_offset;
808 result.emplace_back(StructMember{
809 TypeNameWithPaddingOfSize(alignment_pad), // type
810 spirv_cross::SPIRType::BaseType::Void, // basetype
811 SPrintF("_PADDING_%s_",
812 GetMemberNameAtIndex(struct_type, i).c_str()), // name
813 current_byte_offset, // offset
814 alignment_pad, // size
815 alignment_pad, // byte_length
816 std::nullopt, // array_elements
817 0, // element_padding
818 });
819 current_byte_offset += alignment_pad;
820 }
821
822 max_member_alignment =
823 std::max<size_t>(max_member_alignment,
824 (member.width / 8) * member.columns * member.vecsize);
825
826 FML_CHECK(current_byte_offset == struct_member_offset);
827
828 // A user defined struct.
829 if (member.basetype == spirv_cross::SPIRType::BaseType::Struct) {
830 const size_t size =
831 GetReflectedStructSize(ReadStructMembers(member.self));
832 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
833 if (stride == 0) {
834 stride = size;
835 }
836 uint32_t element_padding = stride - size;
837 result.emplace_back(StructMember{
838 compiler_->get_name(member.self), // type
839 member.basetype, // basetype
840 GetMemberNameAtIndex(struct_type, i), // name
841 struct_member_offset, // offset
842 size, // size
843 stride * array_elements.value_or(1), // byte_length
844 array_elements, // array_elements
845 element_padding, // element_padding
846 });
847 current_byte_offset += stride * array_elements.value_or(1);
848 continue;
849 }
850
851 // Tightly packed 4x4 Matrix is special cased as we know how to work with
852 // those.
853 if (member.basetype == spirv_cross::SPIRType::BaseType::Float && //
854 member.width == sizeof(Scalar) * 8 && //
855 member.columns == 4 && //
856 member.vecsize == 4 //
857 ) {
858 uint32_t stride = GetArrayStride<sizeof(Matrix)>(struct_type, member, i);
859 uint32_t element_padding = stride - sizeof(Matrix);
860 result.emplace_back(StructMember{
861 "Matrix", // type
862 member.basetype, // basetype
863 GetMemberNameAtIndex(struct_type, i), // name
864 struct_member_offset, // offset
865 sizeof(Matrix), // size
866 stride * array_elements.value_or(1), // byte_length
867 array_elements, // array_elements
868 element_padding, // element_padding
869 });
870 current_byte_offset += stride * array_elements.value_or(1);
871 continue;
872 }
873
874 // Tightly packed UintPoint32 (uvec2)
875 if (member.basetype == spirv_cross::SPIRType::BaseType::UInt && //
876 member.width == sizeof(uint32_t) * 8 && //
877 member.columns == 1 && //
878 member.vecsize == 2 //
879 ) {
880 uint32_t stride =
881 GetArrayStride<sizeof(UintPoint32)>(struct_type, member, i);
882 uint32_t element_padding = stride - sizeof(UintPoint32);
883 result.emplace_back(StructMember{
884 "UintPoint32", // type
885 member.basetype, // basetype
886 GetMemberNameAtIndex(struct_type, i), // name
887 struct_member_offset, // offset
888 sizeof(UintPoint32), // size
889 stride * array_elements.value_or(1), // byte_length
890 array_elements, // array_elements
891 element_padding, // element_padding
892 });
893 current_byte_offset += stride * array_elements.value_or(1);
894 continue;
895 }
896
897 // Tightly packed UintPoint32 (ivec2)
898 if (member.basetype == spirv_cross::SPIRType::BaseType::Int && //
899 member.width == sizeof(int32_t) * 8 && //
900 member.columns == 1 && //
901 member.vecsize == 2 //
902 ) {
903 uint32_t stride =
904 GetArrayStride<sizeof(IPoint32)>(struct_type, member, i);
905 uint32_t element_padding = stride - sizeof(IPoint32);
906 result.emplace_back(StructMember{
907 "IPoint32", // type
908 member.basetype, // basetype
909 GetMemberNameAtIndex(struct_type, i), // name
910 struct_member_offset, // offset
911 sizeof(IPoint32), // size
912 stride * array_elements.value_or(1), // byte_length
913 array_elements, // array_elements
914 element_padding, // element_padding
915 });
916 current_byte_offset += stride * array_elements.value_or(1);
917 continue;
918 }
919
920 // Tightly packed Point (vec2).
921 if (member.basetype == spirv_cross::SPIRType::BaseType::Float && //
922 member.width == sizeof(float) * 8 && //
923 member.columns == 1 && //
924 member.vecsize == 2 //
925 ) {
926 uint32_t stride = GetArrayStride<sizeof(Point)>(struct_type, member, i);
927 uint32_t element_padding = stride - sizeof(Point);
928 result.emplace_back(StructMember{
929 "Point", // type
930 member.basetype, // basetype
931 GetMemberNameAtIndex(struct_type, i), // name
932 struct_member_offset, // offset
933 sizeof(Point), // size
934 stride * array_elements.value_or(1), // byte_length
935 array_elements, // array_elements
936 element_padding, // element_padding
937 });
938 current_byte_offset += stride * array_elements.value_or(1);
939 continue;
940 }
941
942 // Tightly packed Vector3.
943 if (member.basetype == spirv_cross::SPIRType::BaseType::Float && //
944 member.width == sizeof(float) * 8 && //
945 member.columns == 1 && //
946 member.vecsize == 3 //
947 ) {
948 uint32_t stride = GetArrayStride<sizeof(Vector3)>(struct_type, member, i);
949 uint32_t element_padding = stride - sizeof(Vector3);
950 result.emplace_back(StructMember{
951 "Vector3", // type
952 member.basetype, // basetype
953 GetMemberNameAtIndex(struct_type, i), // name
954 struct_member_offset, // offset
955 sizeof(Vector3), // size
956 stride * array_elements.value_or(1), // byte_length
957 array_elements, // array_elements
958 element_padding, // element_padding
959 });
960 current_byte_offset += stride * array_elements.value_or(1);
961 continue;
962 }
963
964 // Tightly packed Vector4.
965 if (member.basetype == spirv_cross::SPIRType::BaseType::Float && //
966 member.width == sizeof(float) * 8 && //
967 member.columns == 1 && //
968 member.vecsize == 4 //
969 ) {
970 uint32_t stride = GetArrayStride<sizeof(Vector4)>(struct_type, member, i);
971 uint32_t element_padding = stride - sizeof(Vector4);
972 result.emplace_back(StructMember{
973 "Vector4", // type
974 member.basetype, // basetype
975 GetMemberNameAtIndex(struct_type, i), // name
976 struct_member_offset, // offset
977 sizeof(Vector4), // size
978 stride * array_elements.value_or(1), // byte_length
979 array_elements, // array_elements
980 element_padding, // element_padding
981 });
982 current_byte_offset += stride * array_elements.value_or(1);
983 continue;
984 }
985
986 // Tightly packed half Point (vec2).
987 if (member.basetype == spirv_cross::SPIRType::BaseType::Half && //
988 member.width == sizeof(Half) * 8 && //
989 member.columns == 1 && //
990 member.vecsize == 2 //
991 ) {
992 uint32_t stride =
993 GetArrayStride<sizeof(HalfVector2)>(struct_type, member, i);
994 uint32_t element_padding = stride - sizeof(HalfVector2);
995 result.emplace_back(StructMember{
996 "HalfVector2", // type
997 member.basetype, // basetype
998 GetMemberNameAtIndex(struct_type, i), // name
999 struct_member_offset, // offset
1000 sizeof(HalfVector2), // size
1001 stride * array_elements.value_or(1), // byte_length
1002 array_elements, // array_elements
1003 element_padding, // element_padding
1004 });
1005 current_byte_offset += stride * array_elements.value_or(1);
1006 continue;
1007 }
1008
1009 // Tightly packed Half Float Vector3.
1010 if (member.basetype == spirv_cross::SPIRType::BaseType::Half && //
1011 member.width == sizeof(Half) * 8 && //
1012 member.columns == 1 && //
1013 member.vecsize == 3 //
1014 ) {
1015 uint32_t stride =
1016 GetArrayStride<sizeof(HalfVector3)>(struct_type, member, i);
1017 uint32_t element_padding = stride - sizeof(HalfVector3);
1018 result.emplace_back(StructMember{
1019 "HalfVector3", // type
1020 member.basetype, // basetype
1021 GetMemberNameAtIndex(struct_type, i), // name
1022 struct_member_offset, // offset
1023 sizeof(HalfVector3), // size
1024 stride * array_elements.value_or(1), // byte_length
1025 array_elements, // array_elements
1026 element_padding, // element_padding
1027 });
1028 current_byte_offset += stride * array_elements.value_or(1);
1029 continue;
1030 }
1031
1032 // Tightly packed Half Float Vector4.
1033 if (member.basetype == spirv_cross::SPIRType::BaseType::Half && //
1034 member.width == sizeof(Half) * 8 && //
1035 member.columns == 1 && //
1036 member.vecsize == 4 //
1037 ) {
1038 uint32_t stride =
1039 GetArrayStride<sizeof(HalfVector4)>(struct_type, member, i);
1040 uint32_t element_padding = stride - sizeof(HalfVector4);
1041 result.emplace_back(StructMember{
1042 "HalfVector4", // type
1043 member.basetype, // basetype
1044 GetMemberNameAtIndex(struct_type, i), // name
1045 struct_member_offset, // offset
1046 sizeof(HalfVector4), // size
1047 stride * array_elements.value_or(1), // byte_length
1048 array_elements, // array_elements
1049 element_padding, // element_padding
1050 });
1051 current_byte_offset += stride * array_elements.value_or(1);
1052 continue;
1053 }
1054
1055 // Other isolated scalars (like bool, int, float/Scalar, etc..).
1056 {
1057 auto maybe_known_type = ReadKnownScalarType(member.basetype);
1058 if (maybe_known_type.has_value() && //
1059 member.columns == 1 && //
1060 member.vecsize == 1 //
1061 ) {
1062 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
1063 if (stride == 0) {
1064 stride = maybe_known_type.value().byte_size;
1065 }
1066 uint32_t element_padding = stride - maybe_known_type.value().byte_size;
1067 // Add the type directly.
1068 result.emplace_back(StructMember{
1069 maybe_known_type.value().name, // type
1070 member.basetype, // basetype
1071 GetMemberNameAtIndex(struct_type, i), // name
1072 struct_member_offset, // offset
1073 maybe_known_type.value().byte_size, // size
1074 stride * array_elements.value_or(1), // byte_length
1075 array_elements, // array_elements
1076 element_padding, // element_padding
1077 });
1078 current_byte_offset += stride * array_elements.value_or(1);
1079 continue;
1080 }
1081 }
1082
1083 // Catch all for unknown types. Just add the necessary padding to the struct
1084 // and move on.
1085 {
1086 const size_t size = (member.width * member.columns * member.vecsize) / 8u;
1087 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
1088 if (stride == 0) {
1089 stride = size;
1090 }
1091 auto element_padding = stride - size;
1092 result.emplace_back(StructMember{
1094 member.basetype, // basetype
1095 GetMemberNameAtIndex(struct_type, i), // name
1096 struct_member_offset, // offset
1097 size, // size
1098 stride * array_elements.value_or(1), // byte_length
1099 array_elements, // array_elements
1100 element_padding, // element_padding
1101 });
1102 current_byte_offset += stride * array_elements.value_or(1);
1103 continue;
1104 }
1105 }
1106
1107 if (max_member_alignment > 0u) {
1108 const auto struct_length = current_byte_offset;
1109 {
1110 const auto excess = struct_length % max_member_alignment;
1111 if (excess != 0) {
1112 const auto padding = max_member_alignment - excess;
1113 result.emplace_back(StructMember{
1114 TypeNameWithPaddingOfSize(padding), // type
1115 spirv_cross::SPIRType::BaseType::Void, // basetype
1116 "_PADDING_", // name
1117 current_byte_offset, // offset
1118 padding, // size
1119 padding, // byte_length
1120 std::nullopt, // array_elements
1121 0, // element_padding
1122 });
1123 }
1124 }
1125 }
1126
1127 return result;
1128}
1129
1130std::optional<Reflector::StructDefinition> Reflector::ReflectStructDefinition(
1131 const spirv_cross::TypeID& type_id) const {
1132 const auto& type = compiler_->get_type(type_id);
1133 if (type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
1134 return std::nullopt;
1135 }
1136
1137 const auto struct_name = compiler_->get_name(type_id);
1138 if (struct_name.find("_RESERVED_IDENTIFIER_") != std::string::npos) {
1139 return std::nullopt;
1140 }
1141
1142 auto struct_members = ReadStructMembers(type_id);
1143 auto reflected_struct_size = GetReflectedStructSize(struct_members);
1144
1145 StructDefinition struc;
1146 struc.name = struct_name;
1147 struc.byte_length = reflected_struct_size;
1148 struc.members = std::move(struct_members);
1149 return struc;
1150}
1151
1152nlohmann::json::object_t Reflector::EmitStructDefinition(
1153 std::optional<Reflector::StructDefinition> struc) const {
1154 nlohmann::json::object_t result;
1155 result["name"] = struc->name;
1156 result["byte_length"] = struc->byte_length;
1157 auto& members = result["members"] = nlohmann::json::array_t{};
1158 for (const auto& struct_member : struc->members) {
1159 auto& member = members.emplace_back(nlohmann::json::object_t{});
1160 member["name"] = struct_member.name;
1161 member["type"] = struct_member.type;
1162 member["base_type"] =
1163 StructMember::BaseTypeToString(struct_member.base_type);
1164 member["offset"] = struct_member.offset;
1165 member["byte_length"] = struct_member.byte_length;
1166 if (struct_member.array_elements.has_value()) {
1167 member["array_elements"] = struct_member.array_elements.value();
1168 } else {
1169 member["array_elements"] = "std::nullopt";
1170 }
1171 member["element_padding"] = struct_member.element_padding;
1172 }
1173 return result;
1174}
1175
1177 std::string type_name;
1178 spirv_cross::SPIRType::BaseType base_type;
1179 std::string variable_name;
1180 size_t byte_length = 0u;
1181};
1182
1184 const spirv_cross::Compiler& compiler,
1185 const spirv_cross::Resource* resource) {
1187 result.variable_name = resource->name;
1188 const auto& type = compiler.get_type(resource->type_id);
1189 result.base_type = type.basetype;
1190 const auto total_size = type.columns * type.vecsize * type.width / 8u;
1191 result.byte_length = total_size;
1192
1193 if (type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1194 type.columns == 1u && type.vecsize == 2u &&
1195 type.width == sizeof(float) * 8u) {
1196 result.type_name = "Point";
1197 } else if (type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1198 type.columns == 1u && type.vecsize == 4u &&
1199 type.width == sizeof(float) * 8u) {
1200 result.type_name = "Vector4";
1201 } else if (type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1202 type.columns == 1u && type.vecsize == 3u &&
1203 type.width == sizeof(float) * 8u) {
1204 result.type_name = "Vector3";
1205 } else if (type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1206 type.columns == 1u && type.vecsize == 1u &&
1207 type.width == sizeof(float) * 8u) {
1208 result.type_name = "Scalar";
1209 } else if (type.basetype == spirv_cross::SPIRType::BaseType::Int &&
1210 type.columns == 1u && type.vecsize == 1u &&
1211 type.width == sizeof(int32_t) * 8u) {
1212 result.type_name = "int32_t";
1213 } else {
1214 // Catch all unknown padding.
1216 }
1217
1218 return result;
1219}
1220
1221std::optional<Reflector::StructDefinition>
1222Reflector::ReflectPerVertexStructDefinition(
1223 const spirv_cross::SmallVector<spirv_cross::Resource>& stage_inputs) const {
1224 // Avoid emitting a zero sized structure. The code gen templates assume a
1225 // non-zero size.
1226 if (stage_inputs.empty()) {
1227 return std::nullopt;
1228 }
1229
1230 // Validate locations are contiguous and there are no duplicates.
1231 std::set<uint32_t> locations;
1232 for (const auto& input : stage_inputs) {
1233 auto location = compiler_->get_decoration(
1234 input.id, spv::Decoration::DecorationLocation);
1235 if (locations.count(location) != 0) {
1236 // Duplicate location. Bail.
1237 return std::nullopt;
1238 }
1239 locations.insert(location);
1240 }
1241
1242 for (size_t i = 0; i < locations.size(); i++) {
1243 if (locations.count(i) != 1) {
1244 // Locations are not contiguous. This usually happens when a single stage
1245 // input takes multiple input slots. No reflection information can be
1246 // generated for such cases anyway. So bail! It is up to the shader author
1247 // to make sure one stage input maps to a single input slot.
1248 return std::nullopt;
1249 }
1250 }
1251
1252 auto input_for_location =
1253 [&](uint32_t queried_location) -> const spirv_cross::Resource* {
1254 for (const auto& input : stage_inputs) {
1255 auto location = compiler_->get_decoration(
1256 input.id, spv::Decoration::DecorationLocation);
1257 if (location == queried_location) {
1258 return &input;
1259 }
1260 }
1261 // This really cannot happen with all the validation above.
1263 return nullptr;
1264 };
1265
1266 StructDefinition struc;
1267 struc.name = "PerVertexData";
1268 struc.byte_length = 0u;
1269 for (size_t i = 0; i < locations.size(); i++) {
1270 auto resource = input_for_location(i);
1271 if (resource == nullptr) {
1272 return std::nullopt;
1273 }
1274 const auto vertex_type =
1276
1277 auto member = StructMember{
1278 vertex_type.type_name, // type
1279 vertex_type.base_type, // base type
1280 vertex_type.variable_name, // name
1281 struc.byte_length, // offset
1282 vertex_type.byte_length, // size
1283 vertex_type.byte_length, // byte_length
1284 std::nullopt, // array_elements
1285 0, // element_padding
1286 };
1287 struc.byte_length += vertex_type.byte_length;
1288 struc.members.emplace_back(std::move(member));
1289 }
1290 return struc;
1291}
1292
1293std::optional<std::string> Reflector::GetMemberNameAtIndexIfExists(
1294 const spirv_cross::SPIRType& parent_type,
1295 size_t index) const {
1296 if (parent_type.type_alias != 0) {
1297 return GetMemberNameAtIndexIfExists(
1298 compiler_->get_type(parent_type.type_alias), index);
1299 }
1300
1301 if (auto found = ir_->meta.find(parent_type.self); found != ir_->meta.end()) {
1302 const auto& members = found->second.members;
1303 if (index < members.size() && !members[index].alias.empty()) {
1304 return members[index].alias;
1305 }
1306 }
1307 return std::nullopt;
1308}
1309
1310std::string Reflector::GetMemberNameAtIndex(
1311 const spirv_cross::SPIRType& parent_type,
1312 size_t index,
1313 std::string suffix) const {
1314 if (auto name = GetMemberNameAtIndexIfExists(parent_type, index);
1315 name.has_value()) {
1316 return name.value();
1317 }
1318 static std::atomic_size_t sUnnamedMembersID;
1319 std::stringstream stream;
1320 stream << "unnamed_" << sUnnamedMembersID++ << suffix;
1321 return stream.str();
1322}
1323
1324std::vector<Reflector::BindPrototype> Reflector::ReflectBindPrototypes(
1325 const spirv_cross::ShaderResources& resources,
1326 spv::ExecutionModel execution_model) const {
1327 std::vector<BindPrototype> prototypes;
1328 for (const auto& uniform_buffer : resources.uniform_buffers) {
1329 auto& proto = prototypes.emplace_back(BindPrototype{});
1330 proto.return_type = "bool";
1331 proto.name = ToCamelCase(uniform_buffer.name);
1332 proto.descriptor_type = "DescriptorType::kUniformBuffer";
1333 {
1334 std::stringstream stream;
1335 stream << "Bind uniform buffer for resource named " << uniform_buffer.name
1336 << ".";
1337 proto.docstring = stream.str();
1338 }
1339 proto.args.push_back(BindPrototypeArgument{
1340 .type_name = "ResourceBinder&",
1341 .argument_name = "command",
1342 });
1343 proto.args.push_back(BindPrototypeArgument{
1344 .type_name = "BufferView",
1345 .argument_name = "view",
1346 });
1347 }
1348 for (const auto& storage_buffer : resources.storage_buffers) {
1349 auto& proto = prototypes.emplace_back(BindPrototype{});
1350 proto.return_type = "bool";
1351 proto.name = ToCamelCase(storage_buffer.name);
1352 proto.descriptor_type = "DescriptorType::kStorageBuffer";
1353 {
1354 std::stringstream stream;
1355 stream << "Bind storage buffer for resource named " << storage_buffer.name
1356 << ".";
1357 proto.docstring = stream.str();
1358 }
1359 proto.args.push_back(BindPrototypeArgument{
1360 .type_name = "ResourceBinder&",
1361 .argument_name = "command",
1362 });
1363 proto.args.push_back(BindPrototypeArgument{
1364 .type_name = "BufferView",
1365 .argument_name = "view",
1366 });
1367 }
1368 for (const auto& sampled_image : resources.sampled_images) {
1369 auto& proto = prototypes.emplace_back(BindPrototype{});
1370 proto.return_type = "bool";
1371 proto.name = ToCamelCase(sampled_image.name);
1372 proto.descriptor_type = "DescriptorType::kSampledImage";
1373 {
1374 std::stringstream stream;
1375 stream << "Bind combined image sampler for resource named "
1376 << sampled_image.name << ".";
1377 proto.docstring = stream.str();
1378 }
1379 proto.args.push_back(BindPrototypeArgument{
1380 .type_name = "ResourceBinder&",
1381 .argument_name = "command",
1382 });
1383 proto.args.push_back(BindPrototypeArgument{
1384 .type_name = "std::shared_ptr<const Texture>",
1385 .argument_name = "texture",
1386 });
1387 proto.args.push_back(BindPrototypeArgument{
1388 .type_name = "const std::unique_ptr<const Sampler>&",
1389 .argument_name = "sampler",
1390 });
1391 }
1392 for (const auto& separate_image : resources.separate_images) {
1393 auto& proto = prototypes.emplace_back(BindPrototype{});
1394 proto.return_type = "bool";
1395 proto.name = ToCamelCase(separate_image.name);
1396 proto.descriptor_type = "DescriptorType::kImage";
1397 {
1398 std::stringstream stream;
1399 stream << "Bind separate image for resource named " << separate_image.name
1400 << ".";
1401 proto.docstring = stream.str();
1402 }
1403 proto.args.push_back(BindPrototypeArgument{
1404 .type_name = "Command&",
1405 .argument_name = "command",
1406 });
1407 proto.args.push_back(BindPrototypeArgument{
1408 .type_name = "std::shared_ptr<const Texture>",
1409 .argument_name = "texture",
1410 });
1411 }
1412 for (const auto& separate_sampler : resources.separate_samplers) {
1413 auto& proto = prototypes.emplace_back(BindPrototype{});
1414 proto.return_type = "bool";
1415 proto.name = ToCamelCase(separate_sampler.name);
1416 proto.descriptor_type = "DescriptorType::kSampler";
1417 {
1418 std::stringstream stream;
1419 stream << "Bind separate sampler for resource named "
1420 << separate_sampler.name << ".";
1421 proto.docstring = stream.str();
1422 }
1423 proto.args.push_back(BindPrototypeArgument{
1424 .type_name = "Command&",
1425 .argument_name = "command",
1426 });
1427 proto.args.push_back(BindPrototypeArgument{
1428 .type_name = "std::shared_ptr<const Sampler>",
1429 .argument_name = "sampler",
1430 });
1431 }
1432 return prototypes;
1433}
1434
1435nlohmann::json::array_t Reflector::EmitBindPrototypes(
1436 const spirv_cross::ShaderResources& resources,
1437 spv::ExecutionModel execution_model) const {
1438 const auto prototypes = ReflectBindPrototypes(resources, execution_model);
1439 nlohmann::json::array_t result;
1440 for (const auto& res : prototypes) {
1441 auto& item = result.emplace_back(nlohmann::json::object_t{});
1442 item["return_type"] = res.return_type;
1443 item["name"] = res.name;
1444 item["docstring"] = res.docstring;
1445 item["descriptor_type"] = res.descriptor_type;
1446 auto& args = item["args"] = nlohmann::json::array_t{};
1447 for (const auto& arg : res.args) {
1448 auto& json_arg = args.emplace_back(nlohmann::json::object_t{});
1449 json_arg["type_name"] = arg.type_name;
1450 json_arg["argument_name"] = arg.argument_name;
1451 }
1452 }
1453 return result;
1454}
1455
1456} // namespace compiler
1457} // namespace impeller
const char * options
const char * backend
static size_t total_size(SkSBlockAllocator< N > &pool)
static SkString resource(SkPDFResourceType type, int index)
GLenum type
static const char * kVulkanUBOName
Definition: runtime_stage.h:22
Reflector(Options options, const std::shared_ptr< const spirv_cross::ParsedIR > &ir, const std::shared_ptr< fml::Mapping > &shader_data, const CompilerBackend &compiler)
Definition: reflector.cc:62
std::shared_ptr< fml::Mapping > GetReflectionJSON() const
Definition: reflector.cc:108
std::shared_ptr< fml::Mapping > GetReflectionCC() const
Definition: reflector.cc:125
std::shared_ptr< RuntimeStageData::Shader > GetRuntimeStageShaderData() const
Definition: reflector.cc:129
std::shared_ptr< ShaderBundleData > GetShaderBundleData() const
Definition: reflector.cc:134
std::shared_ptr< fml::Mapping > GetReflectionHeader() const
Definition: reflector.cc:121
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
uint8_t value
GAsyncResult * result
#define FML_CHECK(condition)
Definition: logging.h:85
#define FML_UNREACHABLE()
Definition: logging.h:109
std::array< MockImage, 3 > images
Definition: mock_vulkan.cc:41
sk_sp< const SkImage > image
Definition: SkRecords.h:269
Definition: __init__.py:1
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
static std::string TypeNameWithPaddingOfSize(size_t size)
Definition: reflector.cc:724
static VertexType VertexTypeFromInputResource(const spirv_cross::Compiler &compiler, const spirv_cross::Resource *resource)
Definition: reflector.cc:1183
static std::string ToString(CompilerBackend::Type type)
Definition: reflector.cc:559
static std::optional< RuntimeStageBackend > GetRuntimeStageBackend(TargetPlatform target_platform)
Definition: reflector.cc:301
static size_t GetReflectedStructSize(const std::vector< StructMember > &members)
Get the reflected struct size. In the vast majority of the cases, this is the same as the declared st...
Definition: reflector.cc:782
static std::string ExecutionModelToString(spv::ExecutionModel model)
Definition: reflector.cc:33
static std::string StringToShaderStage(const std::string &str)
Definition: reflector.cc:46
constexpr std::string_view kReflectionHeaderTemplate
std::string ToCamelCase(std::string_view string)
Definition: utilities.cc:39
constexpr std::string_view kReflectionCCTemplate
static std::optional< KnownType > ReadKnownScalarType(spirv_cross::SPIRType::BaseType type)
Definition: reflector.cc:735
bool StringStartsWith(const std::string &target, const std::string &prefix)
Definition: utilities.cc:87
std::vector< spirv_cross::ID > SortUniforms(const spirv_cross::ParsedIR *ir, const spirv_cross::Compiler *compiler, std::optional< spirv_cross::SPIRType::BaseType > type_filter, bool include)
Sorts uniform declarations in an IR according to decoration order.
float Scalar
Definition: scalar.h:18
TPoint< Scalar > Point
Definition: point.h:322
TPoint< int32_t > IPoint32
Definition: point.h:324
std::string SPrintF(const char *format,...)
Definition: strings.cc:12
TPoint< uint32_t > UintPoint32
Definition: point.h:325
list offsets
Definition: mskp_parser.py:37
string root
Definition: scale_cpu.py:20
SK_API sk_sp< PrecompileColorFilter > Matrix()
Definition: ref_ptr.h:256
SeparatedVector2 offset
A storage only class for half precision floating point.
Definition: half.h:41
spirv_cross::Compiler * GetCompiler()
uint32_t GetExtendedMSLResourceBinding(ExtendedResourceIndex index, spirv_cross::ID id) const
static std::string BaseTypeToString(spirv_cross::SPIRType::BaseType type)
Definition: reflector.h:44
spirv_cross::SPIRType::BaseType base_type
Definition: reflector.cc:1178
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
#define VALIDATION_LOG
Definition: validation.h:73