29#include "spirv_common.hpp"
36 case spv::ExecutionModel::ExecutionModelVertex:
38 case spv::ExecutionModel::ExecutionModelFragment:
40 case spv::ExecutionModel::ExecutionModelGLCompute:
48 if (str ==
"vertex") {
49 return "ShaderStage::kVertex";
52 if (str ==
"fragment") {
53 return "ShaderStage::kFragment";
56 if (str ==
"compute") {
57 return "ShaderStage::kCompute";
60 return "ShaderStage::kUnknown";
64 const std::shared_ptr<const spirv_cross::ParsedIR>& ir,
65 const std::shared_ptr<fml::Mapping>& shader_data,
67 : options_(
std::move(options)),
69 shader_data_(shader_data),
71 if (!ir_ || !compiler_) {
75 if (
auto template_arguments = GenerateTemplateArguments();
76 template_arguments.has_value()) {
78 std::make_unique<nlohmann::json>(std::move(template_arguments.value()));
83 reflection_header_ = GenerateReflectionHeader();
84 if (!reflection_header_) {
88 reflection_cc_ = GenerateReflectionCC();
89 if (!reflection_cc_) {
93 runtime_stage_shader_ = GenerateRuntimeStageData();
95 shader_bundle_data_ = GenerateShaderBundleData();
96 if (!shader_bundle_data_) {
115 std::make_shared<std::string>(template_arguments_->dump(2u));
117 return std::make_shared<fml::NonOwnedMapping>(
118 reinterpret_cast<const uint8_t*
>(json_string->data()),
119 json_string->size(), [json_string](
auto,
auto) {});
123 return reflection_header_;
127 return reflection_cc_;
132 return runtime_stage_shader_;
136 return shader_bundle_data_;
139std::optional<nlohmann::json> Reflector::GenerateTemplateArguments()
const {
142 const auto& entrypoints = compiler_->get_entry_points_and_stages();
143 if (entrypoints.size() != 1) {
144 VALIDATION_LOG <<
"Incorrect number of entrypoints in the shader. Found "
145 << entrypoints.size() <<
" but expected 1.";
149 auto execution_model = entrypoints.front().execution_model;
157 const auto shader_resources = compiler_->get_shader_resources();
161 auto& subpass_inputs = root[
"subpass_inputs"] = nlohmann::json::array_t{};
162 if (
auto subpass_inputs_json =
163 ReflectResources(shader_resources.subpass_inputs);
164 subpass_inputs_json.has_value()) {
165 for (
auto subpass_input : subpass_inputs_json.
value()) {
166 subpass_input[
"descriptor_type"] =
"DescriptorType::kInputAttachment";
167 subpass_inputs.emplace_back(std::move(subpass_input));
176 auto& buffers = root[
"buffers"] = nlohmann::json::array_t{};
177 if (
auto uniform_buffers_json =
178 ReflectResources(shader_resources.uniform_buffers);
179 uniform_buffers_json.has_value()) {
180 for (
auto uniform_buffer : uniform_buffers_json.
value()) {
181 uniform_buffer[
"descriptor_type"] =
"DescriptorType::kUniformBuffer";
182 buffers.emplace_back(std::move(uniform_buffer));
187 if (
auto storage_buffers_json =
188 ReflectResources(shader_resources.storage_buffers);
189 storage_buffers_json.has_value()) {
190 for (
auto uniform_buffer : storage_buffers_json.
value()) {
191 uniform_buffer[
"descriptor_type"] =
"DescriptorType::kStorageBuffer";
192 buffers.emplace_back(std::move(uniform_buffer));
200 auto& stage_inputs = root[
"stage_inputs"] = nlohmann::json::array_t{};
201 if (
auto stage_inputs_json = ReflectResources(
202 shader_resources.stage_inputs,
203 execution_model == spv::ExecutionModelVertex);
204 stage_inputs_json.has_value()) {
205 stage_inputs = std::move(stage_inputs_json.value());
212 auto combined_sampled_images =
213 ReflectResources(shader_resources.sampled_images);
214 auto images = ReflectResources(shader_resources.separate_images);
215 auto samplers = ReflectResources(shader_resources.separate_samplers);
216 if (!combined_sampled_images.has_value() || !
images.has_value() ||
217 !samplers.has_value()) {
220 auto& sampled_images = root[
"sampled_images"] = nlohmann::json::array_t{};
221 for (
auto value : combined_sampled_images.
value()) {
222 value[
"descriptor_type"] =
"DescriptorType::kSampledImage";
223 sampled_images.emplace_back(std::move(value));
226 value[
"descriptor_type"] =
"DescriptorType::kImage";
227 sampled_images.emplace_back(std::move(value));
229 for (
auto value : samplers.
value()) {
230 value[
"descriptor_type"] =
"DescriptorType::kSampledSampler";
231 sampled_images.emplace_back(std::move(value));
235 if (
auto stage_outputs = ReflectResources(shader_resources.stage_outputs);
236 stage_outputs.has_value()) {
237 root[
"stage_outputs"] = std::move(stage_outputs.value());
243 auto& struct_definitions = root[
"struct_definitions"] =
244 nlohmann::json::array_t{};
245 if (entrypoints.front().execution_model ==
246 spv::ExecutionModel::ExecutionModelVertex &&
247 !shader_resources.stage_inputs.empty()) {
249 ReflectPerVertexStructDefinition(shader_resources.stage_inputs);
251 struct_definitions.emplace_back(EmitStructDefinition(struc.value()));
259 std::set<spirv_cross::ID> known_structs;
260 ir_->for_each_typed_id<spirv_cross::SPIRType>(
261 [&](uint32_t,
const spirv_cross::SPIRType&
type) {
262 if (
type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
268 for (
size_t i = 0;
i <
type.member_types.size();
i++) {
269 if (!compiler_->has_member_decoration(
type.self,
i,
270 spv::DecorationOffset)) {
274 if (known_structs.find(
type.self) != known_structs.end()) {
279 known_structs.insert(
type.self);
280 if (
auto struc = ReflectStructDefinition(
type.self);
282 struct_definitions.emplace_back(
283 EmitStructDefinition(struc.value()));
288 root[
"bind_prototypes"] =
289 EmitBindPrototypes(shader_resources, execution_model);
294std::shared_ptr<fml::Mapping> Reflector::GenerateReflectionHeader()
const {
298std::shared_ptr<fml::Mapping> Reflector::GenerateReflectionCC()
const {
304 switch (target_platform) {
326std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
329 if (!backend.has_value()) {
333 const auto& entrypoints = compiler_->get_entry_points_and_stages();
334 if (entrypoints.size() != 1u) {
338 auto data = std::make_unique<RuntimeStageData::Shader>();
340 data->stage = entrypoints.front().execution_model;
341 data->shader = shader_data_;
342 data->backend = backend.value();
345 std::vector<spirv_cross::ID> uniforms =
347 for (
auto& sorted_id : uniforms) {
348 auto var = ir_->ids[sorted_id].get<spirv_cross::SPIRVariable>();
349 const auto spir_type = compiler_->get_type(var.basetype);
350 UniformDescription uniform_description;
351 uniform_description.name = compiler_->get_name(var.self);
352 uniform_description.location = compiler_->get_decoration(
353 var.self, spv::Decoration::DecorationLocation);
354 uniform_description.binding =
355 compiler_->get_decoration(var.self, spv::Decoration::DecorationBinding);
356 uniform_description.type = spir_type.basetype;
357 uniform_description.rows = spir_type.vecsize;
358 uniform_description.columns = spir_type.columns;
359 uniform_description.bit_width = spir_type.width;
360 uniform_description.array_elements = GetArrayElements(spir_type);
362 spir_type.basetype ==
363 spirv_cross::SPIRType::BaseType::SampledImage)
364 <<
"Vulkan runtime effect had unexpected uniforms outside of the "
365 "uniform buffer object.";
366 data->uniforms.emplace_back(std::move(uniform_description));
369 const auto ubos = compiler_->get_shader_resources().uniform_buffers;
376 "for Vulkan runtime stage backend.";
380 const auto& ubo = ubos[0];
383 compiler_->get_decoration(ubo.id, spv::Decoration::DecorationBinding);
384 auto members = ReadStructMembers(ubo.type_id);
385 std::vector<uint8_t> struct_layout;
386 size_t float_count = 0;
388 for (
size_t i = 0;
i < members.size();
i += 1) {
389 const auto& member = members[
i];
390 std::vector<int> bytes;
391 switch (member.underlying_type) {
393 size_t padding_count =
394 (member.size +
sizeof(float) - 1) /
sizeof(float);
395 while (padding_count > 0) {
396 struct_layout.push_back(0);
402 if (member.array_elements > 1) {
405 for (
auto i = 0;
i < member.array_elements;
i++) {
406 for (
auto j = 0u; j < member.size /
sizeof(float); j++) {
407 struct_layout.push_back(1);
409 for (
auto j = 0u; j < member.element_padding /
sizeof(float);
411 struct_layout.push_back(0);
415 size_t member_float_count = member.byte_length /
sizeof(float);
416 float_count += member_float_count;
417 while (member_float_count > 0) {
418 struct_layout.push_back(1);
419 member_float_count--;
425 VALIDATION_LOG <<
"Non-floating-type struct member " << member.name
426 <<
" is not supported.";
430 data->uniforms.emplace_back(UniformDescription{
434 .type = spirv_cross::SPIRType::Struct,
435 .struct_layout = std::move(struct_layout),
436 .struct_float_count = float_count,
441 if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
442 const auto inputs = compiler_->get_shader_resources().stage_inputs;
443 auto input_offsets = ComputeOffsets(inputs);
444 for (
const auto&
input : inputs) {
445 std::optional<size_t> offset = GetOffset(
input.id, input_offsets);
447 const auto type = compiler_->get_type(
input.type_id);
449 InputDescription input_description;
450 input_description.name =
input.name;
451 input_description.location = compiler_->get_decoration(
452 input.id, spv::Decoration::DecorationLocation);
453 input_description.set = compiler_->get_decoration(
454 input.id, spv::Decoration::DecorationDescriptorSet);
455 input_description.binding = compiler_->get_decoration(
456 input.id, spv::Decoration::DecorationBinding);
457 input_description.type =
type.basetype;
458 input_description.bit_width =
type.width;
459 input_description.vec_size =
type.vecsize;
460 input_description.columns =
type.columns;
461 input_description.offset = offset.value_or(0u);
462 data->inputs.emplace_back(std::move(input_description));
469std::shared_ptr<ShaderBundleData> Reflector::GenerateShaderBundleData()
const {
470 const auto& entrypoints = compiler_->get_entry_points_and_stages();
471 if (entrypoints.size() != 1u) {
475 auto data = std::make_shared<ShaderBundleData>(
477 entrypoints.front().execution_model,
480 data->SetShaderData(shader_data_);
482 const auto uniforms = compiler_->get_shader_resources().uniform_buffers;
483 for (
const auto& uniform : uniforms) {
484 ShaderBundleData::ShaderUniformStruct uniform_struct;
485 uniform_struct.name = uniform.name;
488 uniform_struct.set = compiler_->get_decoration(
489 uniform.id, spv::Decoration::DecorationDescriptorSet);
490 uniform_struct.binding = compiler_->get_decoration(
491 uniform.id, spv::Decoration::DecorationBinding);
493 const auto type = compiler_->get_type(uniform.type_id);
494 if (
type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
495 std::cerr <<
"Error: Uniform \"" << uniform.name
496 <<
"\" is not a struct. All Flutter GPU shader uniforms must "
502 size_t size_in_bytes = 0;
503 for (
const auto& struct_member : ReadStructMembers(uniform.type_id)) {
504 size_in_bytes += struct_member.byte_length;
508 ShaderBundleData::ShaderUniformStructField uniform_struct_field;
509 uniform_struct_field.name = struct_member.name;
510 uniform_struct_field.type = struct_member.base_type;
511 uniform_struct_field.offset_in_bytes = struct_member.offset;
512 uniform_struct_field.element_size_in_bytes = struct_member.size;
513 uniform_struct_field.total_size_in_bytes = struct_member.byte_length;
514 uniform_struct_field.array_elements = struct_member.array_elements;
515 uniform_struct.fields.push_back(uniform_struct_field);
517 uniform_struct.size_in_bytes = size_in_bytes;
519 data->AddUniformStruct(uniform_struct);
522 const auto sampled_images = compiler_->get_shader_resources().sampled_images;
523 for (
const auto&
image : sampled_images) {
524 ShaderBundleData::ShaderUniformTexture uniform_texture;
525 uniform_texture.name =
image.name;
528 uniform_texture.set = compiler_->get_decoration(
529 image.id, spv::Decoration::DecorationDescriptorSet);
530 uniform_texture.binding =
531 compiler_->get_decoration(
image.id, spv::Decoration::DecorationBinding);
532 data->AddUniformTexture(uniform_texture);
536 if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
537 const auto inputs = compiler_->get_shader_resources().stage_inputs;
538 auto input_offsets = ComputeOffsets(inputs);
539 for (
const auto&
input : inputs) {
540 std::optional<size_t> offset = GetOffset(
input.id, input_offsets);
542 const auto type = compiler_->get_type(
input.type_id);
544 InputDescription input_description;
545 input_description.name =
input.name;
546 input_description.location = compiler_->get_decoration(
547 input.id, spv::Decoration::DecorationLocation);
548 input_description.set = compiler_->get_decoration(
549 input.id, spv::Decoration::DecorationDescriptorSet);
550 input_description.binding = compiler_->get_decoration(
551 input.id, spv::Decoration::DecorationBinding);
552 input_description.type =
type.basetype;
553 input_description.bit_width =
type.width;
554 input_description.vec_size =
type.vecsize;
555 input_description.columns =
type.columns;
556 input_description.offset = offset.value_or(0u);
557 data->AddInputDescription(std::move(input_description));
564std::optional<uint32_t> Reflector::GetArrayElements(
565 const spirv_cross::SPIRType&
type)
const {
566 if (
type.array.empty()) {
570 <<
"Multi-dimensional arrays are not supported.";
572 <<
"Must use a literal for array sizes.";
573 return type.array.front();
579 return "Metal Shading Language";
581 return "OpenGL Shading Language";
583 return "OpenGL Shading Language (Relaxed Vulkan Semantics)";
585 return "SkSL Shading Language";
590std::shared_ptr<fml::Mapping> Reflector::InflateTemplate(
591 std::string_view tmpl)
const {
592 inja::Environment env;
593 env.set_trim_blocks(
true);
594 env.set_lstrip_blocks(
true);
596 env.add_callback(
"camel_case", 1u, [](inja::Arguments&
args) {
600 env.add_callback(
"to_shader_stage", 1u, [](inja::Arguments&
args) {
604 env.add_callback(
"get_generator_name", 0u,
609 auto inflated_template =
610 std::make_shared<std::string>(env.render(tmpl, *template_arguments_));
612 return std::make_shared<fml::NonOwnedMapping>(
613 reinterpret_cast<const uint8_t*
>(inflated_template->data()),
614 inflated_template->size(), [inflated_template](
auto,
auto) {});
617std::vector<size_t> Reflector::ComputeOffsets(
618 const spirv_cross::SmallVector<spirv_cross::Resource>& resources)
const {
619 std::vector<size_t> offsets(resources.size(), 0);
620 if (resources.size() == 0) {
623 for (
const auto& resource : resources) {
624 const auto type = compiler_->get_type(resource.type_id);
625 auto location = compiler_->get_decoration(
626 resource.id, spv::Decoration::DecorationLocation);
628 if (location >= resources.size() || location < 0) {
631 offsets[location] = (
type.width *
type.vecsize) / 8;
633 for (
size_t i = 1;
i < resources.size();
i++) {
634 offsets[
i] += offsets[
i - 1];
636 for (
size_t i = resources.size() - 1;
i > 0;
i--) {
637 offsets[
i] = offsets[
i - 1];
644std::optional<size_t> Reflector::GetOffset(
646 const std::vector<size_t>& offsets)
const {
648 compiler_->get_decoration(
id, spv::Decoration::DecorationLocation);
649 if (location >= offsets.size()) {
652 return offsets[location];
655std::optional<nlohmann::json::object_t> Reflector::ReflectResource(
656 const spirv_cross::Resource& resource,
657 std::optional<size_t> offset)
const {
658 nlohmann::json::object_t result;
660 result[
"name"] = resource.name;
661 result[
"descriptor_set"] = compiler_->get_decoration(
662 resource.id, spv::Decoration::DecorationDescriptorSet);
663 result[
"binding"] = compiler_->get_decoration(
664 resource.id, spv::Decoration::DecorationBinding);
665 result[
"set"] = compiler_->get_decoration(
666 resource.id, spv::Decoration::DecorationDescriptorSet);
667 result[
"location"] = compiler_->get_decoration(
668 resource.id, spv::Decoration::DecorationLocation);
670 compiler_->get_decoration(resource.id, spv::Decoration::DecorationIndex);
675 result[
"relaxed_precision"] =
676 compiler_->get_decoration(
677 resource.id, spv::Decoration::DecorationRelaxedPrecision) == 1;
678 result[
"offset"] = offset.value_or(0u);
679 auto type = ReflectType(resource.type_id);
680 if (!
type.has_value()) {
683 result[
"type"] = std::move(
type.value());
687std::optional<nlohmann::json::object_t> Reflector::ReflectType(
688 const spirv_cross::TypeID& type_id)
const {
689 nlohmann::json::object_t result;
691 const auto type = compiler_->get_type(type_id);
694 result[
"bit_width"] =
type.width;
695 result[
"vec_size"] =
type.vecsize;
696 result[
"columns"] =
type.columns;
697 auto& members = result[
"members"] = nlohmann::json::array_t{};
698 if (
type.basetype == spirv_cross::SPIRType::BaseType::Struct) {
699 for (
const auto& struct_member : ReadStructMembers(type_id)) {
700 auto member = nlohmann::json::object_t{};
701 member[
"name"] = struct_member.name;
702 member[
"type"] = struct_member.type;
703 member[
"base_type"] =
705 member[
"offset"] = struct_member.offset;
706 member[
"size"] = struct_member.size;
707 member[
"byte_length"] = struct_member.byte_length;
708 if (struct_member.array_elements.has_value()) {
709 member[
"array_elements"] = struct_member.array_elements.value();
711 member[
"array_elements"] =
"std::nullopt";
713 members.emplace_back(std::move(member));
720std::optional<nlohmann::json::array_t> Reflector::ReflectResources(
721 const spirv_cross::SmallVector<spirv_cross::Resource>& resources,
722 bool compute_offsets)
const {
723 nlohmann::json::array_t result;
724 result.reserve(resources.size());
725 std::vector<size_t> offsets;
726 if (compute_offsets) {
727 offsets = ComputeOffsets(resources);
729 for (
const auto& resource : resources) {
730 std::optional<size_t> maybe_offset = std::nullopt;
731 if (compute_offsets) {
732 maybe_offset = GetOffset(resource.id, offsets);
734 if (
auto reflected = ReflectResource(resource, maybe_offset);
735 reflected.has_value()) {
736 result.emplace_back(std::move(reflected.value()));
745 std::stringstream stream;
746 stream <<
"Padding<" << size <<
">";
756 spirv_cross::SPIRType::BaseType
type) {
758 case spirv_cross::SPIRType::BaseType::Boolean:
761 .byte_size =
sizeof(bool),
763 case spirv_cross::SPIRType::BaseType::Float:
766 .byte_size =
sizeof(
Scalar),
768 case spirv_cross::SPIRType::BaseType::Half:
771 .byte_size =
sizeof(
Half),
773 case spirv_cross::SPIRType::BaseType::UInt:
776 .byte_size =
sizeof(uint32_t),
778 case spirv_cross::SPIRType::BaseType::Int:
781 .byte_size =
sizeof(int32_t),
803 auto struct_size = 0u;
804 for (
const auto& member : members) {
805 struct_size += member.byte_length;
810std::vector<StructMember> Reflector::ReadStructMembers(
811 const spirv_cross::TypeID& type_id)
const {
812 const auto& struct_type = compiler_->get_type(type_id);
813 FML_CHECK(struct_type.basetype == spirv_cross::SPIRType::BaseType::Struct);
815 std::vector<StructMember> result;
817 size_t current_byte_offset = 0;
818 size_t max_member_alignment = 0;
820 for (
size_t i = 0;
i < struct_type.member_types.size();
i++) {
821 const auto& member = compiler_->get_type(struct_type.member_types[
i]);
822 const auto struct_member_offset =
823 compiler_->type_struct_member_offset(struct_type,
i);
824 auto array_elements = GetArrayElements(member);
826 if (struct_member_offset > current_byte_offset) {
827 const auto alignment_pad = struct_member_offset - current_byte_offset;
828 result.emplace_back(StructMember{
830 spirv_cross::SPIRType::BaseType::Void,
831 std::format(
"_PADDING_{}_",
832 GetMemberNameAtIndex(struct_type,
i)),
839 current_byte_offset += alignment_pad;
842 max_member_alignment =
843 std::max<size_t>(max_member_alignment,
844 (member.width / 8) * member.columns * member.vecsize);
846 FML_CHECK(current_byte_offset == struct_member_offset);
849 if (member.basetype == spirv_cross::SPIRType::BaseType::Struct) {
852 uint32_t stride = GetArrayStride<0>(struct_type, member,
i);
856 uint32_t element_padding = stride -
size;
857 result.emplace_back(StructMember{
858 compiler_->get_name(member.self),
860 GetMemberNameAtIndex(struct_type,
i),
861 struct_member_offset,
863 stride * array_elements.value_or(1),
867 current_byte_offset += stride * array_elements.value_or(1);
873 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
874 member.width ==
sizeof(
Scalar) * 8 &&
875 member.columns == 4 &&
878 uint32_t stride = GetArrayStride<sizeof(Matrix)>(struct_type, member,
i);
879 uint32_t element_padding = stride -
sizeof(Matrix);
880 result.emplace_back(StructMember{
883 GetMemberNameAtIndex(struct_type,
i),
884 struct_member_offset,
886 stride * array_elements.value_or(1),
890 current_byte_offset += stride * array_elements.value_or(1);
895 if (member.basetype == spirv_cross::SPIRType::BaseType::UInt &&
896 member.width ==
sizeof(uint32_t) * 8 &&
897 member.columns == 1 &&
901 GetArrayStride<sizeof(UintPoint32)>(struct_type, member,
i);
902 uint32_t element_padding = stride -
sizeof(
UintPoint32);
903 result.emplace_back(StructMember{
906 GetMemberNameAtIndex(struct_type,
i),
907 struct_member_offset,
909 stride * array_elements.value_or(1),
913 current_byte_offset += stride * array_elements.value_or(1);
918 if (member.basetype == spirv_cross::SPIRType::BaseType::Int &&
919 member.width ==
sizeof(int32_t) * 8 &&
920 member.columns == 1 &&
924 GetArrayStride<sizeof(IPoint32)>(struct_type, member,
i);
925 uint32_t element_padding = stride -
sizeof(
IPoint32);
926 result.emplace_back(StructMember{
929 GetMemberNameAtIndex(struct_type,
i),
930 struct_member_offset,
932 stride * array_elements.value_or(1),
936 current_byte_offset += stride * array_elements.value_or(1);
941 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
942 member.width ==
sizeof(
float) * 8 &&
943 member.columns == 1 &&
946 uint32_t stride = GetArrayStride<sizeof(Point)>(struct_type, member,
i);
947 uint32_t element_padding = stride -
sizeof(
Point);
948 result.emplace_back(StructMember{
951 GetMemberNameAtIndex(struct_type,
i),
952 struct_member_offset,
954 stride * array_elements.value_or(1),
958 current_byte_offset += stride * array_elements.value_or(1);
963 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
964 member.width ==
sizeof(
float) * 8 &&
965 member.columns == 1 &&
968 uint32_t stride = GetArrayStride<sizeof(Vector3)>(struct_type, member,
i);
969 uint32_t element_padding = stride -
sizeof(Vector3);
970 result.emplace_back(StructMember{
973 GetMemberNameAtIndex(struct_type,
i),
974 struct_member_offset,
976 stride * array_elements.value_or(1),
980 current_byte_offset += stride * array_elements.value_or(1);
985 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
986 member.width ==
sizeof(
float) * 8 &&
987 member.columns == 1 &&
990 uint32_t stride = GetArrayStride<sizeof(Vector4)>(struct_type, member,
i);
991 uint32_t element_padding = stride -
sizeof(Vector4);
992 result.emplace_back(StructMember{
995 GetMemberNameAtIndex(struct_type,
i),
996 struct_member_offset,
998 stride * array_elements.value_or(1),
1002 current_byte_offset += stride * array_elements.value_or(1);
1007 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1008 member.width ==
sizeof(Half) * 8 &&
1009 member.columns == 1 &&
1013 GetArrayStride<sizeof(HalfVector2)>(struct_type, member,
i);
1014 uint32_t element_padding = stride -
sizeof(HalfVector2);
1015 result.emplace_back(StructMember{
1018 GetMemberNameAtIndex(struct_type,
i),
1019 struct_member_offset,
1020 sizeof(HalfVector2),
1021 stride * array_elements.value_or(1),
1025 current_byte_offset += stride * array_elements.value_or(1);
1030 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1031 member.width ==
sizeof(Half) * 8 &&
1032 member.columns == 1 &&
1036 GetArrayStride<sizeof(HalfVector3)>(struct_type, member,
i);
1037 uint32_t element_padding = stride -
sizeof(HalfVector3);
1038 result.emplace_back(StructMember{
1041 GetMemberNameAtIndex(struct_type,
i),
1042 struct_member_offset,
1043 sizeof(HalfVector3),
1044 stride * array_elements.value_or(1),
1048 current_byte_offset += stride * array_elements.value_or(1);
1053 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1054 member.width ==
sizeof(Half) * 8 &&
1055 member.columns == 1 &&
1059 GetArrayStride<sizeof(HalfVector4)>(struct_type, member,
i);
1060 uint32_t element_padding = stride -
sizeof(HalfVector4);
1061 result.emplace_back(StructMember{
1064 GetMemberNameAtIndex(struct_type,
i),
1065 struct_member_offset,
1066 sizeof(HalfVector4),
1067 stride * array_elements.value_or(1),
1071 current_byte_offset += stride * array_elements.value_or(1);
1078 if (maybe_known_type.has_value() &&
1079 member.columns == 1 &&
1082 uint32_t stride = GetArrayStride<0>(struct_type, member,
i);
1084 stride = maybe_known_type.value().byte_size;
1086 uint32_t element_padding = stride - maybe_known_type.value().byte_size;
1088 result.emplace_back(StructMember{
1089 maybe_known_type.value().
name,
1091 GetMemberNameAtIndex(struct_type,
i),
1092 struct_member_offset,
1093 maybe_known_type.value().byte_size,
1094 stride * array_elements.value_or(1),
1098 current_byte_offset += stride * array_elements.value_or(1);
1106 const size_t size = (member.width * member.columns * member.vecsize) / 8u;
1107 uint32_t stride = GetArrayStride<0>(struct_type, member,
i);
1111 auto element_padding = stride -
size;
1112 result.emplace_back(StructMember{
1115 GetMemberNameAtIndex(struct_type,
i),
1116 struct_member_offset,
1118 stride * array_elements.value_or(1),
1122 current_byte_offset += stride * array_elements.value_or(1);
1127 if (max_member_alignment > 0u) {
1128 const auto struct_length = current_byte_offset;
1130 const auto excess = struct_length % max_member_alignment;
1132 const auto padding = max_member_alignment - excess;
1133 result.emplace_back(StructMember{
1135 spirv_cross::SPIRType::BaseType::Void,
1137 current_byte_offset,
1150std::optional<Reflector::StructDefinition> Reflector::ReflectStructDefinition(
1151 const spirv_cross::TypeID& type_id)
const {
1152 const auto&
type = compiler_->get_type(type_id);
1153 if (
type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
1154 return std::nullopt;
1157 const auto struct_name = compiler_->get_name(type_id);
1158 if (struct_name.find(
"_RESERVED_IDENTIFIER_") != std::string::npos) {
1159 return std::nullopt;
1162 auto struct_members = ReadStructMembers(type_id);
1165 StructDefinition struc;
1166 struc.name = struct_name;
1167 struc.byte_length = reflected_struct_size;
1168 struc.members = std::move(struct_members);
1172nlohmann::json::object_t Reflector::EmitStructDefinition(
1173 std::optional<Reflector::StructDefinition> struc)
const {
1174 nlohmann::json::object_t result;
1175 result[
"name"] = struc->name;
1176 result[
"byte_length"] = struc->byte_length;
1177 auto& members = result[
"members"] = nlohmann::json::array_t{};
1178 for (
const auto& struct_member : struc->members) {
1179 auto& member = members.emplace_back(nlohmann::json::object_t{});
1180 member[
"name"] = struct_member.name;
1181 member[
"type"] = struct_member.type;
1182 member[
"base_type"] =
1184 member[
"offset"] = struct_member.offset;
1185 member[
"byte_length"] = struct_member.byte_length;
1186 if (struct_member.array_elements.has_value()) {
1187 member[
"array_elements"] = struct_member.array_elements.value();
1189 member[
"array_elements"] =
"std::nullopt";
1191 member[
"element_padding"] = struct_member.element_padding;
1204 const spirv_cross::Compiler& compiler,
1205 const spirv_cross::Resource* resource) {
1208 const auto&
type = compiler.get_type(resource->type_id);
1210 const auto total_size =
type.columns *
type.vecsize *
type.width / 8u;
1213 if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1214 type.columns == 1u &&
type.vecsize == 2u &&
1215 type.width ==
sizeof(
float) * 8u) {
1217 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1218 type.columns == 1u &&
type.vecsize == 4u &&
1219 type.width ==
sizeof(
float) * 8u) {
1221 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1222 type.columns == 1u &&
type.vecsize == 3u &&
1223 type.width ==
sizeof(
float) * 8u) {
1225 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1226 type.columns == 1u &&
type.vecsize == 1u &&
1227 type.width ==
sizeof(
float) * 8u) {
1229 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Int &&
1230 type.columns == 1u &&
type.vecsize == 1u &&
1231 type.width ==
sizeof(int32_t) * 8u) {
1241std::optional<Reflector::StructDefinition>
1242Reflector::ReflectPerVertexStructDefinition(
1243 const spirv_cross::SmallVector<spirv_cross::Resource>& stage_inputs)
const {
1246 if (stage_inputs.empty()) {
1247 return std::nullopt;
1251 std::set<uint32_t> locations;
1252 for (
const auto&
input : stage_inputs) {
1253 auto location = compiler_->get_decoration(
1254 input.id, spv::Decoration::DecorationLocation);
1255 if (locations.count(location) != 0) {
1257 return std::nullopt;
1259 locations.insert(location);
1262 for (
size_t i = 0;
i < locations.size();
i++) {
1263 if (locations.count(
i) != 1) {
1268 return std::nullopt;
1272 auto input_for_location =
1273 [&](uint32_t queried_location) ->
const spirv_cross::Resource* {
1274 for (
const auto&
input : stage_inputs) {
1275 auto location = compiler_->get_decoration(
1276 input.id, spv::Decoration::DecorationLocation);
1277 if (location == queried_location) {
1286 StructDefinition struc;
1287 struc.name =
"PerVertexData";
1288 struc.byte_length = 0u;
1289 for (
size_t i = 0;
i < locations.size();
i++) {
1290 auto resource = input_for_location(
i);
1291 if (resource ==
nullptr) {
1292 return std::nullopt;
1294 const auto vertex_type =
1297 auto member = StructMember{
1298 vertex_type.type_name,
1299 vertex_type.base_type,
1300 vertex_type.variable_name,
1302 vertex_type.byte_length,
1303 vertex_type.byte_length,
1307 struc.byte_length += vertex_type.byte_length;
1308 struc.members.emplace_back(std::move(member));
1313std::optional<std::string> Reflector::GetMemberNameAtIndexIfExists(
1314 const spirv_cross::SPIRType& parent_type,
1315 size_t index)
const {
1316 if (parent_type.type_alias != 0) {
1317 return GetMemberNameAtIndexIfExists(
1318 compiler_->get_type(parent_type.type_alias), index);
1321 if (
auto found = ir_->meta.find(parent_type.self); found != ir_->meta.end()) {
1322 const auto& members = found->second.members;
1323 if (index < members.size() && !members[index].alias.empty()) {
1324 return members[index].alias;
1327 return std::nullopt;
1330std::string Reflector::GetMemberNameAtIndex(
1331 const spirv_cross::SPIRType& parent_type,
1333 std::string suffix)
const {
1334 if (
auto name = GetMemberNameAtIndexIfExists(parent_type, index);
1336 return name.value();
1338 static std::atomic_size_t sUnnamedMembersID;
1339 std::stringstream stream;
1340 stream <<
"unnamed_" << sUnnamedMembersID++ << suffix;
1341 return stream.str();
1344std::vector<Reflector::BindPrototype> Reflector::ReflectBindPrototypes(
1345 const spirv_cross::ShaderResources& resources,
1346 spv::ExecutionModel execution_model)
const {
1347 std::vector<BindPrototype> prototypes;
1348 for (
const auto& uniform_buffer : resources.uniform_buffers) {
1349 auto& proto = prototypes.emplace_back(BindPrototype{});
1350 proto.return_type =
"bool";
1352 proto.descriptor_type =
"DescriptorType::kUniformBuffer";
1354 std::stringstream stream;
1355 stream <<
"Bind uniform buffer for resource named " << uniform_buffer.name
1357 proto.docstring = stream.str();
1359 proto.args.push_back(BindPrototypeArgument{
1360 .type_name =
"ResourceBinder&",
1361 .argument_name =
"command",
1363 proto.args.push_back(BindPrototypeArgument{
1364 .type_name =
"BufferView",
1365 .argument_name =
"view",
1368 for (
const auto& storage_buffer : resources.storage_buffers) {
1369 auto& proto = prototypes.emplace_back(BindPrototype{});
1370 proto.return_type =
"bool";
1372 proto.descriptor_type =
"DescriptorType::kStorageBuffer";
1374 std::stringstream stream;
1375 stream <<
"Bind storage buffer for resource named " << storage_buffer.name
1377 proto.docstring = stream.str();
1379 proto.args.push_back(BindPrototypeArgument{
1380 .type_name =
"ResourceBinder&",
1381 .argument_name =
"command",
1383 proto.args.push_back(BindPrototypeArgument{
1384 .type_name =
"BufferView",
1385 .argument_name =
"view",
1388 for (
const auto& sampled_image : resources.sampled_images) {
1389 auto& proto = prototypes.emplace_back(BindPrototype{});
1390 proto.return_type =
"bool";
1392 proto.descriptor_type =
"DescriptorType::kSampledImage";
1394 std::stringstream stream;
1395 stream <<
"Bind combined image sampler for resource named "
1396 << sampled_image.name <<
".";
1397 proto.docstring = stream.str();
1399 proto.args.push_back(BindPrototypeArgument{
1400 .type_name =
"ResourceBinder&",
1401 .argument_name =
"command",
1403 proto.args.push_back(BindPrototypeArgument{
1404 .type_name =
"std::shared_ptr<const Texture>",
1405 .argument_name =
"texture",
1407 proto.args.push_back(BindPrototypeArgument{
1408 .type_name =
"raw_ptr<const Sampler>",
1409 .argument_name =
"sampler",
1412 for (
const auto& separate_image : resources.separate_images) {
1413 auto& proto = prototypes.emplace_back(BindPrototype{});
1414 proto.return_type =
"bool";
1416 proto.descriptor_type =
"DescriptorType::kImage";
1418 std::stringstream stream;
1419 stream <<
"Bind separate image for resource named " << separate_image.name
1421 proto.docstring = stream.str();
1423 proto.args.push_back(BindPrototypeArgument{
1424 .type_name =
"Command&",
1425 .argument_name =
"command",
1427 proto.args.push_back(BindPrototypeArgument{
1428 .type_name =
"std::shared_ptr<const Texture>",
1429 .argument_name =
"texture",
1432 for (
const auto& separate_sampler : resources.separate_samplers) {
1433 auto& proto = prototypes.emplace_back(BindPrototype{});
1434 proto.return_type =
"bool";
1436 proto.descriptor_type =
"DescriptorType::kSampler";
1438 std::stringstream stream;
1439 stream <<
"Bind separate sampler for resource named "
1440 << separate_sampler.name <<
".";
1441 proto.docstring = stream.str();
1443 proto.args.push_back(BindPrototypeArgument{
1444 .type_name =
"Command&",
1445 .argument_name =
"command",
1447 proto.args.push_back(BindPrototypeArgument{
1448 .type_name =
"std::shared_ptr<const Sampler>",
1449 .argument_name =
"sampler",
1455nlohmann::json::array_t Reflector::EmitBindPrototypes(
1456 const spirv_cross::ShaderResources& resources,
1457 spv::ExecutionModel execution_model)
const {
1458 const auto prototypes = ReflectBindPrototypes(resources, execution_model);
1459 nlohmann::json::array_t result;
1460 for (
const auto& res : prototypes) {
1461 auto& item = result.emplace_back(nlohmann::json::object_t{});
1462 item[
"return_type"] = res.return_type;
1463 item[
"name"] = res.name;
1464 item[
"docstring"] = res.docstring;
1465 item[
"descriptor_type"] = res.descriptor_type;
1466 auto&
args = item[
"args"] = nlohmann::json::array_t{};
1467 for (
const auto& arg : res.
args) {
1468 auto& json_arg =
args.emplace_back(nlohmann::json::object_t{});
1469 json_arg[
"type_name"] = arg.type_name;
1470 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)
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