29#include "runtime_stage_types_flatbuffers.h"
30#include "spirv_common.hpp"
37 case spv::ExecutionModel::ExecutionModelVertex:
39 case spv::ExecutionModel::ExecutionModelFragment:
41 case spv::ExecutionModel::ExecutionModelGLCompute:
49 if (str ==
"vertex") {
50 return "ShaderStage::kVertex";
53 if (str ==
"fragment") {
54 return "ShaderStage::kFragment";
57 if (str ==
"compute") {
58 return "ShaderStage::kCompute";
61 return "ShaderStage::kUnknown";
65 const std::shared_ptr<const spirv_cross::ParsedIR>& ir,
66 const std::shared_ptr<fml::Mapping>& shader_data,
68 : options_(
std::move(options)),
70 shader_data_(shader_data),
72 if (!ir_ || !compiler_) {
76 if (
auto template_arguments = GenerateTemplateArguments();
77 template_arguments.has_value()) {
79 std::make_unique<nlohmann::json>(std::move(template_arguments.value()));
84 reflection_header_ = GenerateReflectionHeader();
85 if (!reflection_header_) {
89 reflection_cc_ = GenerateReflectionCC();
90 if (!reflection_cc_) {
94 runtime_stage_shader_ = GenerateRuntimeStageData();
96 shader_bundle_data_ = GenerateShaderBundleData();
97 if (!shader_bundle_data_) {
116 std::make_shared<std::string>(template_arguments_->dump(2u));
118 return std::make_shared<fml::NonOwnedMapping>(
119 reinterpret_cast<const uint8_t*
>(json_string->data()),
120 json_string->size(), [json_string](
auto,
auto) {});
124 return reflection_header_;
128 return reflection_cc_;
133 return runtime_stage_shader_;
137 return shader_bundle_data_;
140std::optional<nlohmann::json> Reflector::GenerateTemplateArguments()
const {
143 const auto& entrypoints = compiler_->get_entry_points_and_stages();
144 if (entrypoints.size() != 1) {
145 VALIDATION_LOG <<
"Incorrect number of entrypoints in the shader. Found "
146 << entrypoints.size() <<
" but expected 1.";
150 auto execution_model = entrypoints.front().execution_model;
158 const auto shader_resources = compiler_->get_shader_resources();
162 auto& subpass_inputs = root[
"subpass_inputs"] = nlohmann::json::array_t{};
163 if (
auto subpass_inputs_json =
164 ReflectResources(shader_resources.subpass_inputs);
165 subpass_inputs_json.has_value()) {
166 for (
auto subpass_input : subpass_inputs_json.
value()) {
167 subpass_input[
"descriptor_type"] =
"DescriptorType::kInputAttachment";
168 subpass_inputs.emplace_back(std::move(subpass_input));
177 auto& buffers = root[
"buffers"] = nlohmann::json::array_t{};
178 if (
auto uniform_buffers_json =
179 ReflectResources(shader_resources.uniform_buffers);
180 uniform_buffers_json.has_value()) {
181 for (
auto uniform_buffer : uniform_buffers_json.
value()) {
182 uniform_buffer[
"descriptor_type"] =
"DescriptorType::kUniformBuffer";
183 buffers.emplace_back(std::move(uniform_buffer));
188 if (
auto storage_buffers_json =
189 ReflectResources(shader_resources.storage_buffers);
190 storage_buffers_json.has_value()) {
191 for (
auto uniform_buffer : storage_buffers_json.
value()) {
192 uniform_buffer[
"descriptor_type"] =
"DescriptorType::kStorageBuffer";
193 buffers.emplace_back(std::move(uniform_buffer));
201 auto& uniforms = root[
"uniforms"] = nlohmann::json::array_t{};
202 if (
auto uniforms_json =
203 ReflectResources(shader_resources.gl_plain_uniforms);
204 uniforms_json.has_value()) {
205 for (
auto uniform : uniforms_json.
value()) {
206 uniform[
"descriptor_type"] =
"DescriptorType::kUniform";
207 uniforms.emplace_back(std::move(uniform));
215 auto& stage_inputs = root[
"stage_inputs"] = nlohmann::json::array_t{};
216 if (
auto stage_inputs_json = ReflectResources(
217 shader_resources.stage_inputs,
218 execution_model == spv::ExecutionModelVertex);
219 stage_inputs_json.has_value()) {
220 stage_inputs = std::move(stage_inputs_json.value());
227 auto combined_sampled_images =
228 ReflectResources(shader_resources.sampled_images);
229 auto images = ReflectResources(shader_resources.separate_images);
230 auto samplers = ReflectResources(shader_resources.separate_samplers);
231 if (!combined_sampled_images.has_value() || !
images.has_value() ||
232 !samplers.has_value()) {
235 auto& sampled_images = root[
"sampled_images"] = nlohmann::json::array_t{};
236 for (
auto value : combined_sampled_images.
value()) {
237 value[
"descriptor_type"] =
"DescriptorType::kSampledImage";
238 sampled_images.emplace_back(std::move(value));
241 value[
"descriptor_type"] =
"DescriptorType::kImage";
242 sampled_images.emplace_back(std::move(value));
244 for (
auto value : samplers.
value()) {
245 value[
"descriptor_type"] =
"DescriptorType::kSampledSampler";
246 sampled_images.emplace_back(std::move(value));
250 if (
auto stage_outputs = ReflectResources(shader_resources.stage_outputs);
251 stage_outputs.has_value()) {
252 root[
"stage_outputs"] = std::move(stage_outputs.value());
258 auto& struct_definitions = root[
"struct_definitions"] =
259 nlohmann::json::array_t{};
260 if (entrypoints.front().execution_model ==
261 spv::ExecutionModel::ExecutionModelVertex &&
262 !shader_resources.stage_inputs.empty()) {
264 ReflectPerVertexStructDefinition(shader_resources.stage_inputs);
266 struct_definitions.emplace_back(EmitStructDefinition(struc.value()));
274 std::set<spirv_cross::ID> known_structs;
275 ir_->for_each_typed_id<spirv_cross::SPIRType>(
276 [&](uint32_t,
const spirv_cross::SPIRType&
type) {
277 if (
type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
283 for (
size_t i = 0;
i <
type.member_types.size();
i++) {
284 if (!compiler_->has_member_decoration(
type.self,
i,
285 spv::DecorationOffset)) {
289 if (known_structs.find(
type.self) != known_structs.end()) {
294 known_structs.insert(
type.self);
295 if (
auto struc = ReflectStructDefinition(
type.self);
297 struct_definitions.emplace_back(
298 EmitStructDefinition(struc.value()));
303 root[
"bind_prototypes"] =
304 EmitBindPrototypes(shader_resources, execution_model);
309std::shared_ptr<fml::Mapping> Reflector::GenerateReflectionHeader()
const {
313std::shared_ptr<fml::Mapping> Reflector::GenerateReflectionCC()
const {
319 switch (target_platform) {
341std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
344 if (!backend.has_value()) {
348 const auto& entrypoints = compiler_->get_entry_points_and_stages();
349 if (entrypoints.size() != 1u) {
353 auto data = std::make_unique<RuntimeStageData::Shader>();
355 data->stage = entrypoints.front().execution_model;
356 data->shader = shader_data_;
357 data->backend = backend.value();
360 std::vector<spirv_cross::ID> uniforms =
362 for (
auto& sorted_id : uniforms) {
363 auto var = ir_->ids[sorted_id].get<spirv_cross::SPIRVariable>();
364 const auto spir_type = compiler_->get_type(var.basetype);
365 UniformDescription uniform_description;
366 uniform_description.name = compiler_->get_name(var.self);
367 uniform_description.location = compiler_->get_decoration(
368 var.self, spv::Decoration::DecorationLocation);
369 uniform_description.binding =
370 compiler_->get_decoration(var.self, spv::Decoration::DecorationBinding);
371 uniform_description.type = spir_type.basetype;
372 uniform_description.rows = spir_type.vecsize;
373 uniform_description.columns = spir_type.columns;
374 uniform_description.bit_width = spir_type.width;
375 uniform_description.array_elements = GetArrayElements(spir_type);
378 uniform_description.type == spirv_cross::SPIRType::BaseType::Float) {
384 if (spir_type.vecsize == 3 &&
385 (spir_type.columns == 1 || spir_type.columns == 3)) {
386 for (
size_t c = 0;
c < spir_type.columns;
c++) {
387 for (
size_t v = 0; v < 3; v++) {
388 uniform_description.padding_layout.push_back(
389 fb::PaddingType::kFloat);
391 uniform_description.padding_layout.push_back(
392 fb::PaddingType::kPadding);
398 spir_type.basetype ==
399 spirv_cross::SPIRType::BaseType::SampledImage)
400 <<
"Vulkan runtime effect had unexpected uniforms outside of the "
401 "uniform buffer object.";
402 data->uniforms.emplace_back(std::move(uniform_description));
405 const auto ubos = compiler_->get_shader_resources().uniform_buffers;
412 "for Vulkan runtime stage backend.";
416 const auto& ubo = ubos[0];
419 compiler_->get_decoration(ubo.id, spv::Decoration::DecorationBinding);
420 auto members = ReadStructMembers(ubo.type_id);
421 std::vector<fb::PaddingType> padding_layout;
422 std::vector<StructField> struct_fields;
423 struct_fields.reserve(members.size());
424 size_t float_count = 0;
426 for (
size_t i = 0;
i < members.size();
i += 1) {
427 const auto& member = members[
i];
428 std::vector<int> bytes;
429 switch (member.underlying_type) {
431 size_t padding_count =
432 (member.size +
sizeof(float) - 1) /
sizeof(float);
433 while (padding_count > 0) {
434 padding_layout.push_back(fb::PaddingType::kPadding);
440 StructField field_desc;
441 field_desc.name = member.name;
442 field_desc.byte_size =
443 member.size * member.array_elements.value_or(1);
444 struct_fields.push_back(field_desc);
445 if (member.array_elements > 1) {
448 for (
auto i = 0;
i < member.array_elements;
i++) {
449 for (
auto j = 0u; j < member.size /
sizeof(float); j++) {
450 padding_layout.push_back(fb::PaddingType::kFloat);
452 for (
auto j = 0u; j < member.element_padding /
sizeof(float);
454 padding_layout.push_back(fb::PaddingType::kPadding);
458 size_t member_float_count = member.byte_length /
sizeof(float);
459 float_count += member_float_count;
460 while (member_float_count > 0) {
461 padding_layout.push_back(fb::PaddingType::kFloat);
462 member_float_count--;
468 VALIDATION_LOG <<
"Non-floating-type struct member " << member.name
469 <<
" is not supported.";
473 data->uniforms.emplace_back(UniformDescription{
477 .type = spirv_cross::SPIRType::Struct,
478 .padding_layout = std::move(padding_layout),
479 .struct_fields = std::move(struct_fields),
480 .struct_float_count = float_count,
485 if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
486 const auto inputs = compiler_->get_shader_resources().stage_inputs;
487 auto input_offsets = ComputeOffsets(inputs);
488 for (
const auto&
input : inputs) {
489 std::optional<size_t> offset = GetOffset(
input.id, input_offsets);
491 const auto type = compiler_->get_type(
input.type_id);
493 InputDescription input_description;
494 input_description.name =
input.name;
495 input_description.location = compiler_->get_decoration(
496 input.id, spv::Decoration::DecorationLocation);
497 input_description.set = compiler_->get_decoration(
498 input.id, spv::Decoration::DecorationDescriptorSet);
499 input_description.binding = compiler_->get_decoration(
500 input.id, spv::Decoration::DecorationBinding);
501 input_description.type =
type.basetype;
502 input_description.bit_width =
type.width;
503 input_description.vec_size =
type.vecsize;
504 input_description.columns =
type.columns;
505 input_description.offset = offset.value_or(0u);
506 data->inputs.emplace_back(std::move(input_description));
513std::shared_ptr<ShaderBundleData> Reflector::GenerateShaderBundleData()
const {
514 const auto& entrypoints = compiler_->get_entry_points_and_stages();
515 if (entrypoints.size() != 1u) {
519 auto data = std::make_shared<ShaderBundleData>(
521 entrypoints.front().execution_model,
524 data->SetShaderData(shader_data_);
526 const auto uniforms = compiler_->get_shader_resources().uniform_buffers;
527 for (
const auto& uniform : uniforms) {
528 ShaderBundleData::ShaderUniformStruct uniform_struct;
529 uniform_struct.name = uniform.name;
532 uniform_struct.set = compiler_->get_decoration(
533 uniform.id, spv::Decoration::DecorationDescriptorSet);
534 uniform_struct.binding = compiler_->get_decoration(
535 uniform.id, spv::Decoration::DecorationBinding);
537 const auto type = compiler_->get_type(uniform.type_id);
538 if (
type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
539 std::cerr <<
"Error: Uniform \"" << uniform.name
540 <<
"\" is not a struct. All Flutter GPU shader uniforms must "
546 size_t size_in_bytes = 0;
547 for (
const auto& struct_member : ReadStructMembers(uniform.type_id)) {
548 size_in_bytes += struct_member.byte_length;
552 ShaderBundleData::ShaderUniformStructField uniform_struct_field;
553 uniform_struct_field.name = struct_member.name;
554 uniform_struct_field.type = struct_member.base_type;
555 uniform_struct_field.offset_in_bytes = struct_member.offset;
556 uniform_struct_field.element_size_in_bytes = struct_member.size;
557 uniform_struct_field.total_size_in_bytes = struct_member.byte_length;
558 uniform_struct_field.array_elements = struct_member.array_elements;
559 uniform_struct.fields.push_back(uniform_struct_field);
561 uniform_struct.size_in_bytes = size_in_bytes;
563 data->AddUniformStruct(uniform_struct);
566 const auto sampled_images = compiler_->get_shader_resources().sampled_images;
567 for (
const auto&
image : sampled_images) {
568 ShaderBundleData::ShaderUniformTexture uniform_texture;
569 uniform_texture.name =
image.name;
572 uniform_texture.set = compiler_->get_decoration(
573 image.id, spv::Decoration::DecorationDescriptorSet);
574 uniform_texture.binding =
575 compiler_->get_decoration(
image.id, spv::Decoration::DecorationBinding);
576 data->AddUniformTexture(uniform_texture);
580 if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
581 const auto inputs = compiler_->get_shader_resources().stage_inputs;
582 auto input_offsets = ComputeOffsets(inputs);
583 for (
const auto&
input : inputs) {
584 std::optional<size_t> offset = GetOffset(
input.id, input_offsets);
586 const auto type = compiler_->get_type(
input.type_id);
588 InputDescription input_description;
589 input_description.name =
input.name;
590 input_description.location = compiler_->get_decoration(
591 input.id, spv::Decoration::DecorationLocation);
592 input_description.set = compiler_->get_decoration(
593 input.id, spv::Decoration::DecorationDescriptorSet);
594 input_description.binding = compiler_->get_decoration(
595 input.id, spv::Decoration::DecorationBinding);
596 input_description.type =
type.basetype;
597 input_description.bit_width =
type.width;
598 input_description.vec_size =
type.vecsize;
599 input_description.columns =
type.columns;
600 input_description.offset = offset.value_or(0u);
601 data->AddInputDescription(std::move(input_description));
608std::optional<uint32_t> Reflector::GetArrayElements(
609 const spirv_cross::SPIRType&
type)
const {
610 if (
type.array.empty()) {
614 <<
"Multi-dimensional arrays are not supported.";
616 <<
"Must use a literal for array sizes.";
617 return type.array.front();
623 return "Metal Shading Language";
625 return "OpenGL Shading Language";
627 return "OpenGL Shading Language (Relaxed Vulkan Semantics)";
629 return "SkSL Shading Language";
634std::shared_ptr<fml::Mapping> Reflector::InflateTemplate(
635 std::string_view tmpl)
const {
636 inja::Environment env;
637 env.set_trim_blocks(
true);
638 env.set_lstrip_blocks(
true);
640 env.add_callback(
"camel_case", 1u, [](inja::Arguments&
args) {
644 env.add_callback(
"to_shader_stage", 1u, [](inja::Arguments&
args) {
648 env.add_callback(
"get_generator_name", 0u,
653 auto inflated_template =
654 std::make_shared<std::string>(env.render(tmpl, *template_arguments_));
656 return std::make_shared<fml::NonOwnedMapping>(
657 reinterpret_cast<const uint8_t*
>(inflated_template->data()),
658 inflated_template->size(), [inflated_template](
auto,
auto) {});
661std::vector<size_t> Reflector::ComputeOffsets(
662 const spirv_cross::SmallVector<spirv_cross::Resource>& resources)
const {
663 std::vector<size_t> offsets(resources.size(), 0);
664 if (resources.size() == 0) {
667 for (
const auto& resource : resources) {
668 const auto type = compiler_->get_type(resource.type_id);
669 auto location = compiler_->get_decoration(
670 resource.id, spv::Decoration::DecorationLocation);
677 for (
size_t i = 1;
i < resources.size();
i++) {
678 offsets[
i] += offsets[
i - 1];
680 for (
size_t i = resources.size() - 1;
i > 0;
i--) {
681 offsets[
i] = offsets[
i - 1];
688std::optional<size_t> Reflector::GetOffset(
690 const std::vector<size_t>& offsets)
const {
692 compiler_->get_decoration(
id, spv::Decoration::DecorationLocation);
699std::optional<nlohmann::json::object_t> Reflector::ReflectResource(
700 const spirv_cross::Resource& resource,
701 std::optional<size_t> offset)
const {
702 nlohmann::json::object_t result;
704 result[
"name"] = resource.name;
705 result[
"descriptor_set"] = compiler_->get_decoration(
706 resource.id, spv::Decoration::DecorationDescriptorSet);
707 result[
"binding"] = compiler_->get_decoration(
708 resource.id, spv::Decoration::DecorationBinding);
709 result[
"set"] = compiler_->get_decoration(
710 resource.id, spv::Decoration::DecorationDescriptorSet);
711 result[
"location"] = compiler_->get_decoration(
712 resource.id, spv::Decoration::DecorationLocation);
714 compiler_->get_decoration(resource.id, spv::Decoration::DecorationIndex);
719 result[
"relaxed_precision"] =
720 compiler_->get_decoration(
721 resource.id, spv::Decoration::DecorationRelaxedPrecision) == 1;
722 result[
"offset"] = offset.value_or(0u);
723 auto type = ReflectType(resource.type_id);
724 if (!
type.has_value()) {
727 result[
"type"] = std::move(
type.value());
731std::optional<nlohmann::json::object_t> Reflector::ReflectType(
732 const spirv_cross::TypeID& type_id)
const {
733 nlohmann::json::object_t result;
735 const auto type = compiler_->get_type(type_id);
738 result[
"bit_width"] =
type.width;
739 result[
"vec_size"] =
type.vecsize;
740 result[
"columns"] =
type.columns;
741 auto& members = result[
"members"] = nlohmann::json::array_t{};
742 if (
type.basetype == spirv_cross::SPIRType::BaseType::Struct) {
743 for (
const auto& struct_member : ReadStructMembers(type_id)) {
744 auto member = nlohmann::json::object_t{};
745 member[
"name"] = struct_member.name;
746 member[
"type"] = struct_member.type;
747 member[
"base_type"] =
749 member[
"offset"] = struct_member.offset;
750 member[
"size"] = struct_member.size;
751 member[
"byte_length"] = struct_member.byte_length;
752 if (struct_member.array_elements.has_value()) {
753 member[
"array_elements"] = struct_member.array_elements.value();
755 member[
"array_elements"] =
"std::nullopt";
757 members.emplace_back(std::move(member));
764std::optional<nlohmann::json::array_t> Reflector::ReflectResources(
765 const spirv_cross::SmallVector<spirv_cross::Resource>& resources,
766 bool compute_offsets)
const {
767 nlohmann::json::array_t result;
768 result.reserve(resources.size());
769 std::vector<size_t> offsets;
770 if (compute_offsets) {
771 offsets = ComputeOffsets(resources);
773 for (
const auto& resource : resources) {
774 std::optional<size_t> maybe_offset = std::nullopt;
775 if (compute_offsets) {
776 maybe_offset = GetOffset(resource.id, offsets);
778 if (
auto reflected = ReflectResource(resource, maybe_offset);
779 reflected.has_value()) {
780 result.emplace_back(std::move(reflected.value()));
789 std::stringstream stream;
790 stream <<
"Padding<" << size <<
">";
800 spirv_cross::SPIRType::BaseType
type) {
802 case spirv_cross::SPIRType::BaseType::Boolean:
805 .byte_size =
sizeof(bool),
807 case spirv_cross::SPIRType::BaseType::Float:
810 .byte_size =
sizeof(
Scalar),
812 case spirv_cross::SPIRType::BaseType::Half:
815 .byte_size =
sizeof(
Half),
817 case spirv_cross::SPIRType::BaseType::UInt:
820 .byte_size =
sizeof(uint32_t),
822 case spirv_cross::SPIRType::BaseType::Int:
825 .byte_size =
sizeof(int32_t),
847 auto struct_size = 0u;
848 for (
const auto& member : members) {
849 struct_size += member.byte_length;
854std::vector<StructMember> Reflector::ReadStructMembers(
855 const spirv_cross::TypeID& type_id)
const {
856 const auto& struct_type = compiler_->get_type(type_id);
857 FML_CHECK(struct_type.basetype == spirv_cross::SPIRType::BaseType::Struct);
859 std::vector<StructMember> result;
861 size_t current_byte_offset = 0;
862 size_t max_member_alignment = 0;
864 for (
size_t i = 0;
i < struct_type.member_types.size();
i++) {
865 const spirv_cross::SPIRType& member =
866 compiler_->get_type(struct_type.member_types[
i]);
867 const uint32_t struct_member_offset =
868 compiler_->type_struct_member_offset(struct_type,
i);
869 std::optional<uint32_t> array_elements = GetArrayElements(member);
871 if (struct_member_offset > current_byte_offset) {
872 const size_t alignment_pad = struct_member_offset - current_byte_offset;
873 result.emplace_back(StructMember{
875 spirv_cross::SPIRType::BaseType::Void,
877 std::format(
"_PADDING_{}_", GetMemberNameAtIndex(struct_type,
i)),
884 current_byte_offset += alignment_pad;
887 max_member_alignment =
888 std::max<size_t>(max_member_alignment,
889 (member.width / 8) * member.columns * member.vecsize);
891 FML_CHECK(current_byte_offset == struct_member_offset);
894 if (member.basetype == spirv_cross::SPIRType::BaseType::Struct) {
897 uint32_t stride = GetArrayStride<0>(struct_type, member,
i);
901 uint32_t element_padding = stride -
size;
902 result.emplace_back(StructMember{
903 compiler_->get_name(member.self),
905 GetMemberNameAtIndex(struct_type,
i),
906 struct_member_offset,
908 stride * array_elements.value_or(1),
912 current_byte_offset += stride * array_elements.value_or(1);
917 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
918 member.width == 32 && member.columns == 2 && member.vecsize == 2) {
921 uint32_t count = array_elements.value_or(1) * 2;
922 uint32_t stride = 16;
923 uint32_t total_length = stride * count;
925 result.emplace_back(StructMember{
927 spirv_cross::SPIRType::BaseType::Float,
928 GetMemberNameAtIndex(struct_type,
i),
929 struct_member_offset,
935 current_byte_offset += total_length;
939 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
940 member.width == 32 && member.columns == 3 && member.vecsize == 3) {
944 uint32_t count = array_elements.value_or(1) * 3;
945 uint32_t stride = 16;
946 uint32_t total_length = stride * count;
948 result.emplace_back(StructMember{
950 spirv_cross::SPIRType::BaseType::Float,
951 GetMemberNameAtIndex(struct_type,
i),
952 struct_member_offset,
958 current_byte_offset += total_length;
964 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
965 member.width ==
sizeof(
Scalar) * 8 &&
966 member.columns == 4 &&
969 uint32_t stride = GetArrayStride<sizeof(Matrix)>(struct_type, member,
i);
970 uint32_t element_padding = stride -
sizeof(Matrix);
971 result.emplace_back(StructMember{
974 GetMemberNameAtIndex(struct_type,
i),
975 struct_member_offset,
977 stride * array_elements.value_or(1),
981 current_byte_offset += stride * array_elements.value_or(1);
986 if (member.basetype == spirv_cross::SPIRType::BaseType::UInt &&
987 member.width ==
sizeof(uint32_t) * 8 &&
988 member.columns == 1 &&
992 GetArrayStride<sizeof(UintPoint32)>(struct_type, member,
i);
993 uint32_t element_padding = stride -
sizeof(
UintPoint32);
994 result.emplace_back(StructMember{
997 GetMemberNameAtIndex(struct_type,
i),
998 struct_member_offset,
1000 stride * array_elements.value_or(1),
1004 current_byte_offset += stride * array_elements.value_or(1);
1009 if (member.basetype == spirv_cross::SPIRType::BaseType::Int &&
1010 member.width ==
sizeof(int32_t) * 8 &&
1011 member.columns == 1 &&
1015 GetArrayStride<sizeof(IPoint32)>(struct_type, member,
i);
1016 uint32_t element_padding = stride -
sizeof(
IPoint32);
1017 result.emplace_back(StructMember{
1020 GetMemberNameAtIndex(struct_type,
i),
1021 struct_member_offset,
1023 stride * array_elements.value_or(1),
1027 current_byte_offset += stride * array_elements.value_or(1);
1032 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
1033 member.width ==
sizeof(
float) * 8 &&
1034 member.columns == 1 &&
1037 uint32_t stride = GetArrayStride<sizeof(Point)>(struct_type, member,
i);
1038 uint32_t element_padding = stride -
sizeof(
Point);
1039 result.emplace_back(StructMember{
1042 GetMemberNameAtIndex(struct_type,
i),
1043 struct_member_offset,
1045 stride * array_elements.value_or(1),
1049 current_byte_offset += stride * array_elements.value_or(1);
1054 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
1055 member.width ==
sizeof(
float) * 8 &&
1056 member.columns == 1 &&
1059 uint32_t stride = GetArrayStride<sizeof(Vector3)>(struct_type, member,
i);
1060 uint32_t element_padding = stride -
sizeof(Vector3);
1061 result.emplace_back(StructMember{
1064 GetMemberNameAtIndex(struct_type,
i),
1065 struct_member_offset,
1067 stride * array_elements.value_or(1),
1071 current_byte_offset += stride * array_elements.value_or(1);
1076 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
1077 member.width ==
sizeof(
float) * 8 &&
1078 member.columns == 1 &&
1081 uint32_t stride = GetArrayStride<sizeof(Vector4)>(struct_type, member,
i);
1082 uint32_t element_padding = stride -
sizeof(Vector4);
1083 result.emplace_back(StructMember{
1086 GetMemberNameAtIndex(struct_type,
i),
1087 struct_member_offset,
1089 stride * array_elements.value_or(1),
1093 current_byte_offset += stride * array_elements.value_or(1);
1098 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1099 member.width ==
sizeof(Half) * 8 &&
1100 member.columns == 1 &&
1104 GetArrayStride<sizeof(HalfVector2)>(struct_type, member,
i);
1105 uint32_t element_padding = stride -
sizeof(HalfVector2);
1106 result.emplace_back(StructMember{
1109 GetMemberNameAtIndex(struct_type,
i),
1110 struct_member_offset,
1111 sizeof(HalfVector2),
1112 stride * array_elements.value_or(1),
1116 current_byte_offset += stride * array_elements.value_or(1);
1121 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1122 member.width ==
sizeof(Half) * 8 &&
1123 member.columns == 1 &&
1127 GetArrayStride<sizeof(HalfVector3)>(struct_type, member,
i);
1128 uint32_t element_padding = stride -
sizeof(HalfVector3);
1129 result.emplace_back(StructMember{
1132 GetMemberNameAtIndex(struct_type,
i),
1133 struct_member_offset,
1134 sizeof(HalfVector3),
1135 stride * array_elements.value_or(1),
1139 current_byte_offset += stride * array_elements.value_or(1);
1144 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1145 member.width ==
sizeof(Half) * 8 &&
1146 member.columns == 1 &&
1150 GetArrayStride<sizeof(HalfVector4)>(struct_type, member,
i);
1151 uint32_t element_padding = stride -
sizeof(HalfVector4);
1152 result.emplace_back(StructMember{
1155 GetMemberNameAtIndex(struct_type,
i),
1156 struct_member_offset,
1157 sizeof(HalfVector4),
1158 stride * array_elements.value_or(1),
1162 current_byte_offset += stride * array_elements.value_or(1);
1169 if (maybe_known_type.has_value() &&
1170 member.columns == 1 &&
1173 uint32_t stride = GetArrayStride<0>(struct_type, member,
i);
1175 stride = maybe_known_type.value().byte_size;
1177 uint32_t element_padding = stride - maybe_known_type.value().byte_size;
1179 result.emplace_back(StructMember{
1180 maybe_known_type.value().
name,
1182 GetMemberNameAtIndex(struct_type,
i),
1183 struct_member_offset,
1184 maybe_known_type.value().byte_size,
1185 stride * array_elements.value_or(1),
1189 current_byte_offset += stride * array_elements.value_or(1);
1197 const size_t size = (member.width * member.columns * member.vecsize) / 8u;
1198 uint32_t stride = GetArrayStride<0>(struct_type, member,
i);
1202 size_t element_padding = stride -
size;
1203 result.emplace_back(StructMember{
1206 GetMemberNameAtIndex(struct_type,
i),
1207 struct_member_offset,
1209 stride * array_elements.value_or(1),
1213 current_byte_offset += stride * array_elements.value_or(1);
1218 if (max_member_alignment > 0u) {
1219 const size_t struct_length = current_byte_offset;
1221 const size_t excess = struct_length % max_member_alignment;
1223 const auto padding = max_member_alignment - excess;
1224 result.emplace_back(StructMember{
1226 spirv_cross::SPIRType::BaseType::Void,
1228 current_byte_offset,
1241std::optional<Reflector::StructDefinition> Reflector::ReflectStructDefinition(
1242 const spirv_cross::TypeID& type_id)
const {
1243 const auto&
type = compiler_->get_type(type_id);
1244 if (
type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
1245 return std::nullopt;
1248 const auto struct_name = compiler_->get_name(type_id);
1249 if (struct_name.find(
"_RESERVED_IDENTIFIER_") != std::string::npos) {
1250 return std::nullopt;
1253 auto struct_members = ReadStructMembers(type_id);
1256 StructDefinition struc;
1257 struc.name = struct_name;
1258 struc.byte_length = reflected_struct_size;
1259 struc.members = std::move(struct_members);
1263nlohmann::json::object_t Reflector::EmitStructDefinition(
1264 std::optional<Reflector::StructDefinition> struc)
const {
1265 nlohmann::json::object_t result;
1266 result[
"name"] = struc->name;
1267 result[
"byte_length"] = struc->byte_length;
1268 auto& members = result[
"members"] = nlohmann::json::array_t{};
1269 for (
const auto& struct_member : struc->members) {
1270 auto& member = members.emplace_back(nlohmann::json::object_t{});
1271 member[
"name"] = struct_member.name;
1272 member[
"type"] = struct_member.type;
1273 member[
"base_type"] =
1275 member[
"offset"] = struct_member.offset;
1276 member[
"byte_length"] = struct_member.byte_length;
1277 if (struct_member.array_elements.has_value()) {
1278 member[
"array_elements"] = struct_member.array_elements.value();
1280 member[
"array_elements"] =
"std::nullopt";
1282 member[
"element_padding"] = struct_member.element_padding;
1295 const spirv_cross::Compiler& compiler,
1296 const spirv_cross::Resource* resource) {
1299 const auto&
type = compiler.get_type(resource->type_id);
1301 const auto total_size =
type.columns *
type.vecsize *
type.width / 8u;
1304 if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1305 type.columns == 1u &&
type.vecsize == 2u &&
1306 type.width ==
sizeof(
float) * 8u) {
1308 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1309 type.columns == 1u &&
type.vecsize == 4u &&
1310 type.width ==
sizeof(
float) * 8u) {
1312 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1313 type.columns == 1u &&
type.vecsize == 3u &&
1314 type.width ==
sizeof(
float) * 8u) {
1316 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1317 type.columns == 1u &&
type.vecsize == 1u &&
1318 type.width ==
sizeof(
float) * 8u) {
1320 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Int &&
1321 type.columns == 1u &&
type.vecsize == 1u &&
1322 type.width ==
sizeof(int32_t) * 8u) {
1332std::optional<Reflector::StructDefinition>
1333Reflector::ReflectPerVertexStructDefinition(
1334 const spirv_cross::SmallVector<spirv_cross::Resource>& stage_inputs)
const {
1337 if (stage_inputs.empty()) {
1338 return std::nullopt;
1342 std::set<uint32_t> locations;
1343 for (
const auto&
input : stage_inputs) {
1344 auto location = compiler_->get_decoration(
1345 input.id, spv::Decoration::DecorationLocation);
1346 if (locations.count(
location) != 0) {
1348 return std::nullopt;
1353 for (
size_t i = 0;
i < locations.size();
i++) {
1354 if (locations.count(
i) != 1) {
1359 return std::nullopt;
1363 auto input_for_location =
1364 [&](uint32_t queried_location) ->
const spirv_cross::Resource* {
1365 for (
const auto&
input : stage_inputs) {
1366 auto location = compiler_->get_decoration(
1367 input.id, spv::Decoration::DecorationLocation);
1368 if (
location == queried_location) {
1377 StructDefinition struc;
1378 struc.name =
"PerVertexData";
1379 struc.byte_length = 0u;
1380 for (
size_t i = 0;
i < locations.size();
i++) {
1381 auto resource = input_for_location(
i);
1382 if (resource ==
nullptr) {
1383 return std::nullopt;
1385 const auto vertex_type =
1388 auto member = StructMember{
1389 vertex_type.type_name,
1390 vertex_type.base_type,
1391 vertex_type.variable_name,
1393 vertex_type.byte_length,
1394 vertex_type.byte_length,
1398 struc.byte_length += vertex_type.byte_length;
1399 struc.members.emplace_back(std::move(member));
1404std::optional<std::string> Reflector::GetMemberNameAtIndexIfExists(
1405 const spirv_cross::SPIRType& parent_type,
1406 size_t index)
const {
1407 if (parent_type.type_alias != 0) {
1408 return GetMemberNameAtIndexIfExists(
1409 compiler_->get_type(parent_type.type_alias), index);
1412 if (
auto found = ir_->meta.find(parent_type.self); found != ir_->meta.end()) {
1413 const auto& members = found->second.members;
1414 if (index < members.size() && !members[index].alias.empty()) {
1415 return members[index].alias;
1418 return std::nullopt;
1421std::string Reflector::GetMemberNameAtIndex(
1422 const spirv_cross::SPIRType& parent_type,
1424 std::string suffix)
const {
1425 if (
auto name = GetMemberNameAtIndexIfExists(parent_type, index);
1427 return name.value();
1429 static std::atomic_size_t sUnnamedMembersID;
1430 std::stringstream stream;
1431 stream <<
"unnamed_" << sUnnamedMembersID++ << suffix;
1432 return stream.str();
1435std::vector<Reflector::BindPrototype> Reflector::ReflectBindPrototypes(
1436 const spirv_cross::ShaderResources& resources,
1437 spv::ExecutionModel execution_model)
const {
1438 std::vector<BindPrototype> prototypes;
1439 for (
const auto& uniform_buffer : resources.uniform_buffers) {
1440 auto& proto = prototypes.emplace_back(BindPrototype{});
1441 proto.return_type =
"bool";
1443 proto.descriptor_type =
"DescriptorType::kUniformBuffer";
1445 std::stringstream stream;
1446 stream <<
"Bind uniform buffer for resource named " << uniform_buffer.name
1448 proto.docstring = stream.str();
1450 proto.args.push_back(BindPrototypeArgument{
1451 .type_name =
"ResourceBinder&",
1452 .argument_name =
"command",
1454 proto.args.push_back(BindPrototypeArgument{
1455 .type_name =
"BufferView",
1456 .argument_name =
"view",
1459 for (
const auto& storage_buffer : resources.storage_buffers) {
1460 auto& proto = prototypes.emplace_back(BindPrototype{});
1461 proto.return_type =
"bool";
1463 proto.descriptor_type =
"DescriptorType::kStorageBuffer";
1465 std::stringstream stream;
1466 stream <<
"Bind storage buffer for resource named " << storage_buffer.name
1468 proto.docstring = stream.str();
1470 proto.args.push_back(BindPrototypeArgument{
1471 .type_name =
"ResourceBinder&",
1472 .argument_name =
"command",
1474 proto.args.push_back(BindPrototypeArgument{
1475 .type_name =
"BufferView",
1476 .argument_name =
"view",
1479 for (
const auto& sampled_image : resources.sampled_images) {
1480 auto& proto = prototypes.emplace_back(BindPrototype{});
1481 proto.return_type =
"bool";
1483 proto.descriptor_type =
"DescriptorType::kSampledImage";
1485 std::stringstream stream;
1486 stream <<
"Bind combined image sampler for resource named "
1487 << sampled_image.name <<
".";
1488 proto.docstring = stream.str();
1490 proto.args.push_back(BindPrototypeArgument{
1491 .type_name =
"ResourceBinder&",
1492 .argument_name =
"command",
1494 proto.args.push_back(BindPrototypeArgument{
1495 .type_name =
"std::shared_ptr<const Texture>",
1496 .argument_name =
"texture",
1498 proto.args.push_back(BindPrototypeArgument{
1499 .type_name =
"raw_ptr<const Sampler>",
1500 .argument_name =
"sampler",
1503 for (
const auto& separate_image : resources.separate_images) {
1504 auto& proto = prototypes.emplace_back(BindPrototype{});
1505 proto.return_type =
"bool";
1507 proto.descriptor_type =
"DescriptorType::kImage";
1509 std::stringstream stream;
1510 stream <<
"Bind separate image for resource named " << separate_image.name
1512 proto.docstring = stream.str();
1514 proto.args.push_back(BindPrototypeArgument{
1515 .type_name =
"Command&",
1516 .argument_name =
"command",
1518 proto.args.push_back(BindPrototypeArgument{
1519 .type_name =
"std::shared_ptr<const Texture>",
1520 .argument_name =
"texture",
1523 for (
const auto& separate_sampler : resources.separate_samplers) {
1524 auto& proto = prototypes.emplace_back(BindPrototype{});
1525 proto.return_type =
"bool";
1527 proto.descriptor_type =
"DescriptorType::kSampler";
1529 std::stringstream stream;
1530 stream <<
"Bind separate sampler for resource named "
1531 << separate_sampler.name <<
".";
1532 proto.docstring = stream.str();
1534 proto.args.push_back(BindPrototypeArgument{
1535 .type_name =
"Command&",
1536 .argument_name =
"command",
1538 proto.args.push_back(BindPrototypeArgument{
1539 .type_name =
"std::shared_ptr<const Sampler>",
1540 .argument_name =
"sampler",
1546nlohmann::json::array_t Reflector::EmitBindPrototypes(
1547 const spirv_cross::ShaderResources& resources,
1548 spv::ExecutionModel execution_model)
const {
1549 const auto prototypes = ReflectBindPrototypes(resources, execution_model);
1550 nlohmann::json::array_t result;
1551 for (
const auto& res : prototypes) {
1552 auto& item = result.emplace_back(nlohmann::json::object_t{});
1553 item[
"return_type"] = res.return_type;
1554 item[
"name"] = res.name;
1555 item[
"docstring"] = res.docstring;
1556 item[
"descriptor_type"] = res.descriptor_type;
1557 auto&
args = item[
"args"] = nlohmann::json::array_t{};
1558 for (
const auto& arg : res.
args) {
1559 auto& json_arg =
args.emplace_back(nlohmann::json::object_t{});
1560 json_arg[
"type_name"] = arg.type_name;
1561 json_arg[
"argument_name"] = arg.argument_name;
static const char * kVulkanUBOName
Reflector(Options options, const std::shared_ptr< const spirv_cross::ParsedIR > &ir, const std::shared_ptr< fml::Mapping > &shader_data, const CompilerBackend &compiler)
std::shared_ptr< fml::Mapping > GetReflectionJSON() const
std::shared_ptr< fml::Mapping > GetReflectionCC() const
std::shared_ptr< RuntimeStageData::Shader > GetRuntimeStageShaderData() const
std::shared_ptr< ShaderBundleData > GetShaderBundleData() const
std::shared_ptr< fml::Mapping > GetReflectionHeader() const
FlutterVulkanImage * image
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
#define FML_CHECK(condition)
#define FML_UNREACHABLE()
Vector2 padding
The halo padding in source space.
std::array< MockImage, 3 > images
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all 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
static std::string TypeNameWithPaddingOfSize(size_t size)
static VertexType VertexTypeFromInputResource(const spirv_cross::Compiler &compiler, const spirv_cross::Resource *resource)
static std::string ToString(CompilerBackend::Type type)
static std::optional< RuntimeStageBackend > GetRuntimeStageBackend(TargetPlatform target_platform)
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...
static std::string ExecutionModelToString(spv::ExecutionModel model)
static std::string StringToShaderStage(const std::string &str)
bool TargetPlatformIsMetal(TargetPlatform platform)
constexpr std::string_view kReflectionHeaderTemplate
std::string ToCamelCase(std::string_view string)
constexpr std::string_view kReflectionCCTemplate
static std::optional< KnownType > ReadKnownScalarType(spirv_cross::SPIRType::BaseType type)
bool StringStartsWith(const std::string &target, const std::string &prefix)
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.
TPoint< int32_t > IPoint32
TPoint< uint32_t > UintPoint32
A storage only class for half precision floating point.
spirv_cross::Compiler * GetCompiler()
uint32_t GetExtendedMSLResourceBinding(ExtendedResourceIndex index, spirv_cross::ID id) const
TargetPlatform target_platform
std::string entry_point_name
std::string header_file_name
static std::string BaseTypeToString(spirv_cross::SPIRType::BaseType type)
spirv_cross::SPIRType::BaseType base_type
std::string variable_name
std::shared_ptr< const fml::Mapping > data