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;
163 uint32_t workgroup_size_x = 0u;
164 uint32_t workgroup_size_y = 0u;
165 uint32_t workgroup_size_z = 0u;
166 if (execution_model == spv::ExecutionModel::ExecutionModelGLCompute) {
167 spirv_cross::SpecializationConstant spec_x, spec_y, spec_z;
168 compiler_->get_work_group_size_specialization_constants(spec_x, spec_y,
170 const auto local_size = [&](spirv_cross::SpecializationConstant& spec,
171 uint32_t index) -> uint32_t {
176 : compiler_->get_execution_mode_argument(
177 spv::ExecutionMode::ExecutionModeLocalSize, index);
179 workgroup_size_x = local_size(spec_x, 0u);
180 workgroup_size_y = local_size(spec_y, 1u);
181 workgroup_size_z = local_size(spec_z, 2u);
183 root[
"workgroup_size_x"] = workgroup_size_x;
184 root[
"workgroup_size_y"] = workgroup_size_y;
185 root[
"workgroup_size_z"] = workgroup_size_z;
188 const auto shader_resources = compiler_->get_shader_resources();
192 auto& subpass_inputs = root[
"subpass_inputs"] = nlohmann::json::array_t{};
193 if (
auto subpass_inputs_json =
194 ReflectResources(shader_resources.subpass_inputs);
195 subpass_inputs_json.has_value()) {
196 for (
auto subpass_input : subpass_inputs_json.
value()) {
197 subpass_input[
"descriptor_type"] =
"DescriptorType::kInputAttachment";
198 subpass_inputs.emplace_back(std::move(subpass_input));
207 auto& buffers = root[
"buffers"] = nlohmann::json::array_t{};
208 if (
auto uniform_buffers_json =
209 ReflectResources(shader_resources.uniform_buffers);
210 uniform_buffers_json.has_value()) {
211 for (
auto uniform_buffer : uniform_buffers_json.
value()) {
212 uniform_buffer[
"descriptor_type"] =
"DescriptorType::kUniformBuffer";
213 buffers.emplace_back(std::move(uniform_buffer));
218 if (
auto storage_buffers_json =
219 ReflectResources(shader_resources.storage_buffers);
220 storage_buffers_json.has_value()) {
221 for (
auto uniform_buffer : storage_buffers_json.
value()) {
222 uniform_buffer[
"descriptor_type"] =
"DescriptorType::kStorageBuffer";
223 buffers.emplace_back(std::move(uniform_buffer));
231 auto& uniforms = root[
"uniforms"] = nlohmann::json::array_t{};
232 if (
auto uniforms_json =
233 ReflectResources(shader_resources.gl_plain_uniforms);
234 uniforms_json.has_value()) {
235 for (
auto uniform : uniforms_json.
value()) {
236 uniform[
"descriptor_type"] =
"DescriptorType::kUniform";
237 uniforms.emplace_back(std::move(uniform));
245 auto& stage_inputs = root[
"stage_inputs"] = nlohmann::json::array_t{};
246 if (
auto stage_inputs_json = ReflectResources(
247 shader_resources.stage_inputs,
248 execution_model == spv::ExecutionModelVertex);
249 stage_inputs_json.has_value()) {
250 stage_inputs = std::move(stage_inputs_json.value());
257 auto combined_sampled_images =
258 ReflectResources(shader_resources.sampled_images);
259 auto images = ReflectResources(shader_resources.separate_images);
260 auto samplers = ReflectResources(shader_resources.separate_samplers);
261 if (!combined_sampled_images.has_value() || !
images.has_value() ||
262 !samplers.has_value()) {
265 auto& sampled_images = root[
"sampled_images"] = nlohmann::json::array_t{};
266 for (
auto value : combined_sampled_images.
value()) {
267 value[
"descriptor_type"] =
"DescriptorType::kSampledImage";
268 sampled_images.emplace_back(std::move(value));
271 value[
"descriptor_type"] =
"DescriptorType::kImage";
272 sampled_images.emplace_back(std::move(value));
274 for (
auto value : samplers.
value()) {
275 value[
"descriptor_type"] =
"DescriptorType::kSampledSampler";
276 sampled_images.emplace_back(std::move(value));
280 if (
auto stage_outputs = ReflectResources(shader_resources.stage_outputs);
281 stage_outputs.has_value()) {
282 root[
"stage_outputs"] = std::move(stage_outputs.value());
288 auto& struct_definitions = root[
"struct_definitions"] =
289 nlohmann::json::array_t{};
290 if (entrypoints.front().execution_model ==
291 spv::ExecutionModel::ExecutionModelVertex &&
292 !shader_resources.stage_inputs.empty()) {
294 ReflectPerVertexStructDefinition(shader_resources.stage_inputs);
296 struct_definitions.emplace_back(EmitStructDefinition(struc.value()));
304 std::set<spirv_cross::ID> known_structs;
305 ir_->for_each_typed_id<spirv_cross::SPIRType>(
306 [&](uint32_t,
const spirv_cross::SPIRType&
type) {
307 if (
type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
313 for (
size_t i = 0;
i <
type.member_types.size();
i++) {
314 if (!compiler_->has_member_decoration(
type.self,
i,
315 spv::DecorationOffset)) {
319 if (known_structs.find(
type.self) != known_structs.end()) {
324 known_structs.insert(
type.self);
325 if (
auto struc = ReflectStructDefinition(
type.self);
327 struct_definitions.emplace_back(
328 EmitStructDefinition(struc.value()));
333 root[
"bind_prototypes"] =
334 EmitBindPrototypes(shader_resources, execution_model);
339std::shared_ptr<fml::Mapping> Reflector::GenerateReflectionHeader()
const {
343std::shared_ptr<fml::Mapping> Reflector::GenerateReflectionCC()
const {
349 switch (target_platform) {
371std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
374 if (!backend.has_value()) {
378 const auto& entrypoints = compiler_->get_entry_points_and_stages();
379 if (entrypoints.size() != 1u) {
383 auto data = std::make_unique<RuntimeStageData::Shader>();
385 data->stage = entrypoints.front().execution_model;
386 data->shader = shader_data_;
387 data->backend = backend.value();
390 std::vector<spirv_cross::ID> uniforms =
392 for (
auto& sorted_id : uniforms) {
393 auto var = ir_->ids[sorted_id].get<spirv_cross::SPIRVariable>();
394 const auto spir_type = compiler_->get_type(var.basetype);
395 UniformDescription uniform_description;
396 uniform_description.name = compiler_->get_name(var.self);
397 uniform_description.location = compiler_->get_decoration(
398 var.self, spv::Decoration::DecorationLocation);
399 uniform_description.binding =
400 compiler_->get_decoration(var.self, spv::Decoration::DecorationBinding);
401 uniform_description.type = spir_type.basetype;
402 uniform_description.rows = spir_type.vecsize;
403 uniform_description.columns = spir_type.columns;
404 uniform_description.bit_width = spir_type.width;
405 uniform_description.array_elements = GetArrayElements(spir_type);
408 uniform_description.type == spirv_cross::SPIRType::BaseType::Float) {
414 if (spir_type.vecsize == 3 &&
415 (spir_type.columns == 1 || spir_type.columns == 3)) {
416 for (
size_t c = 0;
c < spir_type.columns;
c++) {
417 for (
size_t v = 0; v < 3; v++) {
418 uniform_description.padding_layout.push_back(
419 fb::PaddingType::kFloat);
421 uniform_description.padding_layout.push_back(
422 fb::PaddingType::kPadding);
428 spir_type.basetype ==
429 spirv_cross::SPIRType::BaseType::SampledImage)
430 <<
"Vulkan runtime effect had unexpected uniforms outside of the "
431 "uniform buffer object.";
432 data->uniforms.emplace_back(std::move(uniform_description));
435 const auto ubos = compiler_->get_shader_resources().uniform_buffers;
442 "for Vulkan runtime stage backend.";
446 const auto& ubo = ubos[0];
449 compiler_->get_decoration(ubo.id, spv::Decoration::DecorationBinding);
450 auto members = ReadStructMembers(ubo.type_id);
451 std::vector<fb::PaddingType> padding_layout;
452 std::vector<StructField> struct_fields;
453 struct_fields.reserve(members.size());
454 size_t float_count = 0;
456 for (
size_t i = 0;
i < members.size();
i += 1) {
457 const auto& member = members[
i];
458 std::vector<int> bytes;
459 switch (member.underlying_type) {
461 size_t padding_count =
462 (member.size +
sizeof(float) - 1) /
sizeof(float);
463 while (padding_count > 0) {
464 padding_layout.push_back(fb::PaddingType::kPadding);
470 StructField field_desc;
471 field_desc.name = member.name;
472 field_desc.byte_size =
473 member.size * member.array_elements.value_or(1);
474 struct_fields.push_back(field_desc);
475 if (member.array_elements > 1) {
478 for (
auto i = 0;
i < member.array_elements;
i++) {
479 for (
auto j = 0u; j < member.size /
sizeof(float); j++) {
480 padding_layout.push_back(fb::PaddingType::kFloat);
482 for (
auto j = 0u; j < member.element_padding /
sizeof(float);
484 padding_layout.push_back(fb::PaddingType::kPadding);
488 size_t member_float_count = member.byte_length /
sizeof(float);
489 float_count += member_float_count;
490 while (member_float_count > 0) {
491 padding_layout.push_back(fb::PaddingType::kFloat);
492 member_float_count--;
498 VALIDATION_LOG <<
"Non-floating-type struct member " << member.name
499 <<
" is not supported.";
503 data->uniforms.emplace_back(UniformDescription{
507 .type = spirv_cross::SPIRType::Struct,
508 .padding_layout = std::move(padding_layout),
509 .struct_fields = std::move(struct_fields),
510 .struct_float_count = float_count,
515 if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
516 const auto inputs = compiler_->get_shader_resources().stage_inputs;
517 auto input_offsets = ComputeOffsets(inputs);
518 for (
const auto&
input : inputs) {
519 std::optional<size_t> offset = GetOffset(
input.id, input_offsets);
521 const auto type = compiler_->get_type(
input.type_id);
523 InputDescription input_description;
524 input_description.name =
input.name;
525 input_description.location = compiler_->get_decoration(
526 input.id, spv::Decoration::DecorationLocation);
527 input_description.set = compiler_->get_decoration(
528 input.id, spv::Decoration::DecorationDescriptorSet);
529 input_description.binding = compiler_->get_decoration(
530 input.id, spv::Decoration::DecorationBinding);
531 input_description.type =
type.basetype;
532 input_description.bit_width =
type.width;
533 input_description.vec_size =
type.vecsize;
534 input_description.columns =
type.columns;
535 input_description.offset = offset.value_or(0u);
536 data->inputs.emplace_back(std::move(input_description));
543std::shared_ptr<ShaderBundleData> Reflector::GenerateShaderBundleData()
const {
544 const auto& entrypoints = compiler_->get_entry_points_and_stages();
545 if (entrypoints.size() != 1u) {
549 auto data = std::make_shared<ShaderBundleData>(
551 entrypoints.front().execution_model,
554 data->SetShaderData(shader_data_);
556 const auto uniforms = compiler_->get_shader_resources().uniform_buffers;
557 for (
const auto& uniform : uniforms) {
558 ShaderBundleData::ShaderUniformStruct uniform_struct;
559 uniform_struct.name = uniform.name;
562 uniform_struct.set = compiler_->get_decoration(
563 uniform.id, spv::Decoration::DecorationDescriptorSet);
564 uniform_struct.binding = compiler_->get_decoration(
565 uniform.id, spv::Decoration::DecorationBinding);
567 const auto type = compiler_->get_type(uniform.type_id);
568 if (
type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
569 std::cerr <<
"Error: Uniform \"" << uniform.name
570 <<
"\" is not a struct. All Flutter GPU shader uniforms must "
576 size_t size_in_bytes = 0;
577 for (
const auto& struct_member : ReadStructMembers(uniform.type_id)) {
578 size_in_bytes += struct_member.byte_length;
582 ShaderBundleData::ShaderUniformStructField uniform_struct_field;
583 uniform_struct_field.name = struct_member.name;
584 uniform_struct_field.type = struct_member.base_type;
585 uniform_struct_field.offset_in_bytes = struct_member.offset;
586 uniform_struct_field.element_size_in_bytes = struct_member.size;
587 uniform_struct_field.total_size_in_bytes = struct_member.byte_length;
588 uniform_struct_field.array_elements = struct_member.array_elements;
589 uniform_struct_field.vec_size = struct_member.vec_size;
590 uniform_struct_field.columns = struct_member.columns;
591 uniform_struct.fields.push_back(uniform_struct_field);
593 uniform_struct.size_in_bytes = size_in_bytes;
595 data->AddUniformStruct(uniform_struct);
598 const auto sampled_images = compiler_->get_shader_resources().sampled_images;
599 for (
const auto&
image : sampled_images) {
600 ShaderBundleData::ShaderUniformTexture uniform_texture;
601 uniform_texture.name =
image.name;
604 uniform_texture.set = compiler_->get_decoration(
605 image.id, spv::Decoration::DecorationDescriptorSet);
606 uniform_texture.binding =
607 compiler_->get_decoration(
image.id, spv::Decoration::DecorationBinding);
608 data->AddUniformTexture(uniform_texture);
612 if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
613 const auto inputs = compiler_->get_shader_resources().stage_inputs;
614 auto input_offsets = ComputeOffsets(inputs);
615 for (
const auto&
input : inputs) {
616 std::optional<size_t> offset = GetOffset(
input.id, input_offsets);
618 const auto type = compiler_->get_type(
input.type_id);
620 InputDescription input_description;
621 input_description.name =
input.name;
622 input_description.location = compiler_->get_decoration(
623 input.id, spv::Decoration::DecorationLocation);
624 input_description.set = compiler_->get_decoration(
625 input.id, spv::Decoration::DecorationDescriptorSet);
626 input_description.binding = compiler_->get_decoration(
627 input.id, spv::Decoration::DecorationBinding);
628 input_description.type =
type.basetype;
629 input_description.bit_width =
type.width;
630 input_description.vec_size =
type.vecsize;
631 input_description.columns =
type.columns;
632 input_description.offset = offset.value_or(0u);
633 data->AddInputDescription(std::move(input_description));
640std::optional<uint32_t> Reflector::GetArrayElements(
641 const spirv_cross::SPIRType&
type)
const {
642 if (
type.array.empty()) {
646 <<
"Multi-dimensional arrays are not supported.";
648 <<
"Must use a literal for array sizes.";
649 return type.array.front();
655 return "Metal Shading Language";
657 return "OpenGL Shading Language";
659 return "OpenGL Shading Language (Relaxed Vulkan Semantics)";
661 return "SkSL Shading Language";
666std::shared_ptr<fml::Mapping> Reflector::InflateTemplate(
667 std::string_view tmpl)
const {
668 inja::Environment env;
669 env.set_trim_blocks(
true);
670 env.set_lstrip_blocks(
true);
672 env.add_callback(
"camel_case", 1u, [](inja::Arguments&
args) {
676 env.add_callback(
"to_shader_stage", 1u, [](inja::Arguments&
args) {
680 env.add_callback(
"get_generator_name", 0u,
685 auto inflated_template =
686 std::make_shared<std::string>(env.render(tmpl, *template_arguments_));
688 return std::make_shared<fml::NonOwnedMapping>(
689 reinterpret_cast<const uint8_t*
>(inflated_template->data()),
690 inflated_template->size(), [inflated_template](
auto,
auto) {});
693std::vector<size_t> Reflector::ComputeOffsets(
694 const spirv_cross::SmallVector<spirv_cross::Resource>& resources)
const {
695 std::vector<size_t> offsets(resources.size(), 0);
696 if (resources.size() == 0) {
699 for (
const auto& resource : resources) {
700 const auto type = compiler_->get_type(resource.type_id);
701 auto location = compiler_->get_decoration(
702 resource.id, spv::Decoration::DecorationLocation);
709 for (
size_t i = 1;
i < resources.size();
i++) {
710 offsets[
i] += offsets[
i - 1];
712 for (
size_t i = resources.size() - 1;
i > 0;
i--) {
713 offsets[
i] = offsets[
i - 1];
720std::optional<size_t> Reflector::GetOffset(
722 const std::vector<size_t>& offsets)
const {
724 compiler_->get_decoration(
id, spv::Decoration::DecorationLocation);
731std::optional<nlohmann::json::object_t> Reflector::ReflectResource(
732 const spirv_cross::Resource& resource,
733 std::optional<size_t> offset)
const {
734 nlohmann::json::object_t result;
736 result[
"name"] = resource.name;
737 result[
"descriptor_set"] = compiler_->get_decoration(
738 resource.id, spv::Decoration::DecorationDescriptorSet);
739 result[
"binding"] = compiler_->get_decoration(
740 resource.id, spv::Decoration::DecorationBinding);
741 result[
"set"] = compiler_->get_decoration(
742 resource.id, spv::Decoration::DecorationDescriptorSet);
743 result[
"location"] = compiler_->get_decoration(
744 resource.id, spv::Decoration::DecorationLocation);
746 compiler_->get_decoration(resource.id, spv::Decoration::DecorationIndex);
751 result[
"relaxed_precision"] =
752 compiler_->get_decoration(
753 resource.id, spv::Decoration::DecorationRelaxedPrecision) == 1;
754 result[
"offset"] = offset.value_or(0u);
755 auto type = ReflectType(resource.type_id);
756 if (!
type.has_value()) {
759 result[
"type"] = std::move(
type.value());
763std::optional<nlohmann::json::object_t> Reflector::ReflectType(
764 const spirv_cross::TypeID& type_id)
const {
765 nlohmann::json::object_t result;
767 const auto type = compiler_->get_type(type_id);
770 result[
"bit_width"] =
type.width;
771 result[
"vec_size"] =
type.vecsize;
772 result[
"columns"] =
type.columns;
773 auto& members = result[
"members"] = nlohmann::json::array_t{};
774 if (
type.basetype == spirv_cross::SPIRType::BaseType::Struct) {
775 for (
const auto& struct_member : ReadStructMembers(type_id)) {
776 auto member = nlohmann::json::object_t{};
777 member[
"name"] = struct_member.name;
778 member[
"type"] = struct_member.type;
779 member[
"base_type"] =
781 member[
"offset"] = struct_member.offset;
782 member[
"size"] = struct_member.size;
783 member[
"byte_length"] = struct_member.byte_length;
784 if (struct_member.array_elements.has_value()) {
785 member[
"array_elements"] = struct_member.array_elements.value();
787 member[
"array_elements"] =
"std::nullopt";
789 if (struct_member.float_type.has_value()) {
790 member[
"float_type"] = struct_member.float_type.value();
792 member[
"float_type"] =
"std::nullopt";
794 members.emplace_back(std::move(member));
801std::optional<nlohmann::json::array_t> Reflector::ReflectResources(
802 const spirv_cross::SmallVector<spirv_cross::Resource>& resources,
803 bool compute_offsets)
const {
804 nlohmann::json::array_t result;
805 result.reserve(resources.size());
806 std::vector<size_t> offsets;
807 if (compute_offsets) {
808 offsets = ComputeOffsets(resources);
810 for (
const auto& resource : resources) {
811 std::optional<size_t> maybe_offset = std::nullopt;
812 if (compute_offsets) {
813 maybe_offset = GetOffset(resource.id, offsets);
815 if (
auto reflected = ReflectResource(resource, maybe_offset);
816 reflected.has_value()) {
817 result.emplace_back(std::move(reflected.value()));
826 std::stringstream stream;
827 stream <<
"Padding<" << size <<
">";
837 spirv_cross::SPIRType::BaseType
type) {
839 case spirv_cross::SPIRType::BaseType::Boolean:
842 .byte_size =
sizeof(bool),
844 case spirv_cross::SPIRType::BaseType::Float:
847 .byte_size =
sizeof(
Scalar),
849 case spirv_cross::SPIRType::BaseType::Half:
852 .byte_size =
sizeof(
Half),
854 case spirv_cross::SPIRType::BaseType::UInt:
857 .byte_size =
sizeof(uint32_t),
859 case spirv_cross::SPIRType::BaseType::Int:
862 .byte_size =
sizeof(int32_t),
884 auto struct_size = 0u;
885 for (
const auto& member : members) {
886 struct_size += member.byte_length;
891std::vector<StructMember> Reflector::ReadStructMembers(
892 const spirv_cross::TypeID& type_id)
const {
893 const auto& struct_type = compiler_->get_type(type_id);
894 FML_CHECK(struct_type.basetype == spirv_cross::SPIRType::BaseType::Struct);
896 std::vector<StructMember> result;
898 size_t current_byte_offset = 0;
899 size_t max_member_alignment = 0;
901 for (
size_t i = 0;
i < struct_type.member_types.size();
i++) {
902 const spirv_cross::SPIRType& member =
903 compiler_->get_type(struct_type.member_types[
i]);
904 const uint32_t struct_member_offset =
905 compiler_->type_struct_member_offset(struct_type,
i);
906 std::optional<uint32_t> array_elements = GetArrayElements(member);
908 if (struct_member_offset > current_byte_offset) {
909 const size_t alignment_pad = struct_member_offset - current_byte_offset;
910 result.emplace_back(StructMember{
912 spirv_cross::SPIRType::BaseType::Void,
914 std::format(
"_PADDING_{}_", GetMemberNameAtIndex(struct_type,
i)),
921 current_byte_offset += alignment_pad;
924 max_member_alignment =
925 std::max<size_t>(max_member_alignment,
926 (member.width / 8) * member.columns * member.vecsize);
928 FML_CHECK(current_byte_offset == struct_member_offset);
931 if (member.basetype == spirv_cross::SPIRType::BaseType::Struct) {
934 uint32_t stride = GetArrayStride<0>(struct_type, member,
i);
938 uint32_t element_padding = stride -
size;
939 result.emplace_back(StructMember{
940 compiler_->get_name(member.self),
942 GetMemberNameAtIndex(struct_type,
i),
943 struct_member_offset,
945 stride * array_elements.value_or(1),
949 current_byte_offset += stride * array_elements.value_or(1);
954 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
955 member.width == 32 && member.columns == 2 && member.vecsize == 2) {
958 uint32_t count = array_elements.value_or(1) * 2;
959 uint32_t stride = 16;
960 uint32_t total_length = stride * count;
962 result.emplace_back(StructMember{
964 spirv_cross::SPIRType::BaseType::Float,
965 GetMemberNameAtIndex(struct_type,
i),
966 struct_member_offset,
971 "ShaderFloatType::kMat2",
975 current_byte_offset += total_length;
979 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
980 member.width == 32 && member.columns == 3 && member.vecsize == 3) {
984 uint32_t count = array_elements.value_or(1) * 3;
985 uint32_t stride = 16;
986 uint32_t total_length = stride * count;
988 result.emplace_back(StructMember{
990 spirv_cross::SPIRType::BaseType::Float,
991 GetMemberNameAtIndex(struct_type,
i),
992 struct_member_offset,
997 "ShaderFloatType::kMat3",
1001 current_byte_offset += total_length;
1007 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
1008 member.width ==
sizeof(
Scalar) * 8 &&
1009 member.columns == 4 &&
1012 uint32_t stride = GetArrayStride<sizeof(Matrix)>(struct_type, member,
i);
1013 uint32_t element_padding = stride -
sizeof(Matrix);
1014 result.emplace_back(StructMember{
1017 GetMemberNameAtIndex(struct_type,
i),
1018 struct_member_offset,
1020 stride * array_elements.value_or(1),
1023 "ShaderFloatType::kMat4",
1027 current_byte_offset += stride * array_elements.value_or(1);
1032 if (member.basetype == spirv_cross::SPIRType::BaseType::UInt &&
1033 member.width ==
sizeof(uint32_t) * 8 &&
1034 member.columns == 1 &&
1038 GetArrayStride<sizeof(UintPoint32)>(struct_type, member,
i);
1039 uint32_t element_padding = stride -
sizeof(
UintPoint32);
1040 result.emplace_back(StructMember{
1043 GetMemberNameAtIndex(struct_type,
i),
1044 struct_member_offset,
1046 stride * array_elements.value_or(1),
1053 current_byte_offset += stride * array_elements.value_or(1);
1058 if (member.basetype == spirv_cross::SPIRType::BaseType::Int &&
1059 member.width ==
sizeof(int32_t) * 8 &&
1060 member.columns == 1 &&
1064 GetArrayStride<sizeof(IPoint32)>(struct_type, member,
i);
1065 uint32_t element_padding = stride -
sizeof(
IPoint32);
1066 result.emplace_back(StructMember{
1069 GetMemberNameAtIndex(struct_type,
i),
1070 struct_member_offset,
1072 stride * array_elements.value_or(1),
1079 current_byte_offset += stride * array_elements.value_or(1);
1084 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
1085 member.width ==
sizeof(
float) * 8 &&
1086 member.columns == 1 &&
1089 uint32_t stride = GetArrayStride<sizeof(Point)>(struct_type, member,
i);
1090 uint32_t element_padding = stride -
sizeof(
Point);
1091 result.emplace_back(StructMember{
1094 GetMemberNameAtIndex(struct_type,
i),
1095 struct_member_offset,
1097 stride * array_elements.value_or(1),
1100 "ShaderFloatType::kVec2",
1104 current_byte_offset += stride * array_elements.value_or(1);
1109 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
1110 member.width ==
sizeof(
float) * 8 &&
1111 member.columns == 1 &&
1114 uint32_t stride = GetArrayStride<sizeof(Vector3)>(struct_type, member,
i);
1115 uint32_t element_padding = stride -
sizeof(Vector3);
1116 result.emplace_back(StructMember{
1119 GetMemberNameAtIndex(struct_type,
i),
1120 struct_member_offset,
1122 stride * array_elements.value_or(1),
1125 "ShaderFloatType::kVec3",
1129 current_byte_offset += stride * array_elements.value_or(1);
1134 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
1135 member.width ==
sizeof(
float) * 8 &&
1136 member.columns == 1 &&
1139 uint32_t stride = GetArrayStride<sizeof(Vector4)>(struct_type, member,
i);
1140 uint32_t element_padding = stride -
sizeof(Vector4);
1141 result.emplace_back(StructMember{
1144 GetMemberNameAtIndex(struct_type,
i),
1145 struct_member_offset,
1147 stride * array_elements.value_or(1),
1150 "ShaderFloatType::kVec4",
1154 current_byte_offset += stride * array_elements.value_or(1);
1159 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1160 member.width ==
sizeof(Half) * 8 &&
1161 member.columns == 1 &&
1165 GetArrayStride<sizeof(HalfVector2)>(struct_type, member,
i);
1166 uint32_t element_padding = stride -
sizeof(HalfVector2);
1167 result.emplace_back(StructMember{
1170 GetMemberNameAtIndex(struct_type,
i),
1171 struct_member_offset,
1172 sizeof(HalfVector2),
1173 stride * array_elements.value_or(1),
1180 current_byte_offset += stride * array_elements.value_or(1);
1185 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1186 member.width ==
sizeof(Half) * 8 &&
1187 member.columns == 1 &&
1191 GetArrayStride<sizeof(HalfVector3)>(struct_type, member,
i);
1192 uint32_t element_padding = stride -
sizeof(HalfVector3);
1193 result.emplace_back(StructMember{
1196 GetMemberNameAtIndex(struct_type,
i),
1197 struct_member_offset,
1198 sizeof(HalfVector3),
1199 stride * array_elements.value_or(1),
1206 current_byte_offset += stride * array_elements.value_or(1);
1211 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1212 member.width ==
sizeof(Half) * 8 &&
1213 member.columns == 1 &&
1217 GetArrayStride<sizeof(HalfVector4)>(struct_type, member,
i);
1218 uint32_t element_padding = stride -
sizeof(HalfVector4);
1219 result.emplace_back(StructMember{
1222 GetMemberNameAtIndex(struct_type,
i),
1223 struct_member_offset,
1224 sizeof(HalfVector4),
1225 stride * array_elements.value_or(1),
1232 current_byte_offset += stride * array_elements.value_or(1);
1239 if (maybe_known_type.has_value() &&
1240 member.columns == 1 &&
1243 uint32_t stride = GetArrayStride<0>(struct_type, member,
i);
1245 stride = maybe_known_type.value().byte_size;
1247 std::optional<std::string> float_type = std::nullopt;
1248 if (member.basetype == spirv_cross::SPIRType::BaseType::Float) {
1249 float_type =
"ShaderFloatType::kFloat";
1251 uint32_t element_padding = stride - maybe_known_type.value().byte_size;
1254 result.emplace_back(StructMember{
1255 maybe_known_type.value().
name,
1257 GetMemberNameAtIndex(struct_type,
i),
1258 struct_member_offset,
1259 maybe_known_type.value().byte_size,
1260 stride * array_elements.value_or(1),
1267 current_byte_offset += stride * array_elements.value_or(1);
1275 const size_t size = (member.width * member.columns * member.vecsize) / 8u;
1276 uint32_t stride = GetArrayStride<0>(struct_type, member,
i);
1280 size_t element_padding = stride -
size;
1281 result.emplace_back(StructMember{
1284 GetMemberNameAtIndex(struct_type,
i),
1285 struct_member_offset,
1287 stride * array_elements.value_or(1),
1291 current_byte_offset += stride * array_elements.value_or(1);
1296 if (max_member_alignment > 0u) {
1297 const size_t struct_length = current_byte_offset;
1299 const size_t excess = struct_length % max_member_alignment;
1301 const auto padding = max_member_alignment - excess;
1302 result.emplace_back(StructMember{
1304 spirv_cross::SPIRType::BaseType::Void,
1306 current_byte_offset,
1319std::optional<Reflector::StructDefinition> Reflector::ReflectStructDefinition(
1320 const spirv_cross::TypeID& type_id)
const {
1321 const auto&
type = compiler_->get_type(type_id);
1322 if (
type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
1323 return std::nullopt;
1326 const auto struct_name = compiler_->get_name(type_id);
1327 if (struct_name.find(
"_RESERVED_IDENTIFIER_") != std::string::npos) {
1328 return std::nullopt;
1331 auto struct_members = ReadStructMembers(type_id);
1334 StructDefinition struc;
1335 struc.name = struct_name;
1336 struc.byte_length = reflected_struct_size;
1337 struc.members = std::move(struct_members);
1341nlohmann::json::object_t Reflector::EmitStructDefinition(
1342 std::optional<Reflector::StructDefinition> struc)
const {
1343 nlohmann::json::object_t result;
1344 result[
"name"] = struc->name;
1345 result[
"byte_length"] = struc->byte_length;
1346 auto& members = result[
"members"] = nlohmann::json::array_t{};
1347 for (
const auto& struct_member : struc->members) {
1348 auto& member = members.emplace_back(nlohmann::json::object_t{});
1349 member[
"name"] = struct_member.name;
1350 member[
"type"] = struct_member.type;
1351 member[
"base_type"] =
1353 member[
"offset"] = struct_member.offset;
1354 member[
"byte_length"] = struct_member.byte_length;
1355 if (struct_member.array_elements.has_value()) {
1356 member[
"array_elements"] = struct_member.array_elements.value();
1358 member[
"array_elements"] =
"std::nullopt";
1360 member[
"element_padding"] = struct_member.element_padding;
1361 if (struct_member.float_type.has_value()) {
1362 member[
"float_type"] = struct_member.float_type.value();
1364 member[
"float_type"] =
"std::nullopt";
1378 const spirv_cross::Compiler& compiler,
1379 const spirv_cross::Resource* resource) {
1382 const auto&
type = compiler.get_type(resource->type_id);
1384 const auto total_size =
type.columns *
type.vecsize *
type.width / 8u;
1387 if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1388 type.columns == 1u &&
type.vecsize == 2u &&
1389 type.width ==
sizeof(
float) * 8u) {
1391 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1392 type.columns == 1u &&
type.vecsize == 4u &&
1393 type.width ==
sizeof(
float) * 8u) {
1395 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1396 type.columns == 1u &&
type.vecsize == 3u &&
1397 type.width ==
sizeof(
float) * 8u) {
1399 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1400 type.columns == 1u &&
type.vecsize == 1u &&
1401 type.width ==
sizeof(
float) * 8u) {
1403 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Int &&
1404 type.columns == 1u &&
type.vecsize == 1u &&
1405 type.width ==
sizeof(int32_t) * 8u) {
1415std::optional<Reflector::StructDefinition>
1416Reflector::ReflectPerVertexStructDefinition(
1417 const spirv_cross::SmallVector<spirv_cross::Resource>& stage_inputs)
const {
1420 if (stage_inputs.empty()) {
1421 return std::nullopt;
1425 std::set<uint32_t> locations;
1426 for (
const auto&
input : stage_inputs) {
1427 auto location = compiler_->get_decoration(
1428 input.id, spv::Decoration::DecorationLocation);
1429 if (locations.count(
location) != 0) {
1431 return std::nullopt;
1436 for (
size_t i = 0;
i < locations.size();
i++) {
1437 if (locations.count(
i) != 1) {
1442 return std::nullopt;
1446 auto input_for_location =
1447 [&](uint32_t queried_location) ->
const spirv_cross::Resource* {
1448 for (
const auto&
input : stage_inputs) {
1449 auto location = compiler_->get_decoration(
1450 input.id, spv::Decoration::DecorationLocation);
1451 if (
location == queried_location) {
1460 StructDefinition struc;
1461 struc.name =
"PerVertexData";
1462 struc.byte_length = 0u;
1463 for (
size_t i = 0;
i < locations.size();
i++) {
1464 auto resource = input_for_location(
i);
1465 if (resource ==
nullptr) {
1466 return std::nullopt;
1468 const auto vertex_type =
1471 auto member = StructMember{
1472 vertex_type.type_name,
1473 vertex_type.base_type,
1474 vertex_type.variable_name,
1476 vertex_type.byte_length,
1477 vertex_type.byte_length,
1481 struc.byte_length += vertex_type.byte_length;
1482 struc.members.emplace_back(std::move(member));
1487std::optional<std::string> Reflector::GetMemberNameAtIndexIfExists(
1488 const spirv_cross::SPIRType& parent_type,
1489 size_t index)
const {
1490 if (parent_type.type_alias != 0) {
1491 return GetMemberNameAtIndexIfExists(
1492 compiler_->get_type(parent_type.type_alias), index);
1495 if (
auto found = ir_->meta.find(parent_type.self); found != ir_->meta.end()) {
1496 const auto& members = found->second.members;
1497 if (index < members.size() && !members[index].alias.empty()) {
1498 return members[index].alias;
1501 return std::nullopt;
1504std::string Reflector::GetMemberNameAtIndex(
1505 const spirv_cross::SPIRType& parent_type,
1507 std::string suffix)
const {
1508 if (
auto name = GetMemberNameAtIndexIfExists(parent_type, index);
1510 return name.value();
1512 static std::atomic_size_t sUnnamedMembersID;
1513 std::stringstream stream;
1514 stream <<
"unnamed_" << sUnnamedMembersID++ << suffix;
1515 return stream.str();
1518std::vector<Reflector::BindPrototype> Reflector::ReflectBindPrototypes(
1519 const spirv_cross::ShaderResources& resources,
1520 spv::ExecutionModel execution_model)
const {
1521 std::vector<BindPrototype> prototypes;
1522 for (
const auto& uniform_buffer : resources.uniform_buffers) {
1523 auto& proto = prototypes.emplace_back(BindPrototype{});
1524 proto.return_type =
"bool";
1526 proto.descriptor_type =
"DescriptorType::kUniformBuffer";
1528 std::stringstream stream;
1529 stream <<
"Bind uniform buffer for resource named " << uniform_buffer.name
1531 proto.docstring = stream.str();
1533 proto.args.push_back(BindPrototypeArgument{
1534 .type_name =
"ResourceBinder&",
1535 .argument_name =
"command",
1537 proto.args.push_back(BindPrototypeArgument{
1538 .type_name =
"BufferView",
1539 .argument_name =
"view",
1542 for (
const auto& storage_buffer : resources.storage_buffers) {
1543 auto& proto = prototypes.emplace_back(BindPrototype{});
1544 proto.return_type =
"bool";
1546 proto.descriptor_type =
"DescriptorType::kStorageBuffer";
1548 std::stringstream stream;
1549 stream <<
"Bind storage buffer for resource named " << storage_buffer.name
1551 proto.docstring = stream.str();
1553 proto.args.push_back(BindPrototypeArgument{
1554 .type_name =
"ResourceBinder&",
1555 .argument_name =
"command",
1557 proto.args.push_back(BindPrototypeArgument{
1558 .type_name =
"BufferView",
1559 .argument_name =
"view",
1562 for (
const auto& sampled_image : resources.sampled_images) {
1563 auto& proto = prototypes.emplace_back(BindPrototype{});
1564 proto.return_type =
"bool";
1566 proto.descriptor_type =
"DescriptorType::kSampledImage";
1568 std::stringstream stream;
1569 stream <<
"Bind combined image sampler for resource named "
1570 << sampled_image.name <<
".";
1571 proto.docstring = stream.str();
1573 proto.args.push_back(BindPrototypeArgument{
1574 .type_name =
"ResourceBinder&",
1575 .argument_name =
"command",
1577 proto.args.push_back(BindPrototypeArgument{
1578 .type_name =
"std::shared_ptr<const Texture>",
1579 .argument_name =
"texture",
1581 proto.args.push_back(BindPrototypeArgument{
1582 .type_name =
"raw_ptr<const Sampler>",
1583 .argument_name =
"sampler",
1586 for (
const auto& separate_image : resources.separate_images) {
1587 auto& proto = prototypes.emplace_back(BindPrototype{});
1588 proto.return_type =
"bool";
1590 proto.descriptor_type =
"DescriptorType::kImage";
1592 std::stringstream stream;
1593 stream <<
"Bind separate image for resource named " << separate_image.name
1595 proto.docstring = stream.str();
1597 proto.args.push_back(BindPrototypeArgument{
1598 .type_name =
"Command&",
1599 .argument_name =
"command",
1601 proto.args.push_back(BindPrototypeArgument{
1602 .type_name =
"std::shared_ptr<const Texture>",
1603 .argument_name =
"texture",
1606 for (
const auto& separate_sampler : resources.separate_samplers) {
1607 auto& proto = prototypes.emplace_back(BindPrototype{});
1608 proto.return_type =
"bool";
1610 proto.descriptor_type =
"DescriptorType::kSampler";
1612 std::stringstream stream;
1613 stream <<
"Bind separate sampler for resource named "
1614 << separate_sampler.name <<
".";
1615 proto.docstring = stream.str();
1617 proto.args.push_back(BindPrototypeArgument{
1618 .type_name =
"Command&",
1619 .argument_name =
"command",
1621 proto.args.push_back(BindPrototypeArgument{
1622 .type_name =
"std::shared_ptr<const Sampler>",
1623 .argument_name =
"sampler",
1629nlohmann::json::array_t Reflector::EmitBindPrototypes(
1630 const spirv_cross::ShaderResources& resources,
1631 spv::ExecutionModel execution_model)
const {
1632 const auto prototypes = ReflectBindPrototypes(resources, execution_model);
1633 nlohmann::json::array_t result;
1634 for (
const auto& res : prototypes) {
1635 auto& item = result.emplace_back(nlohmann::json::object_t{});
1636 item[
"return_type"] = res.return_type;
1637 item[
"name"] = res.name;
1638 item[
"docstring"] = res.docstring;
1639 item[
"descriptor_type"] = res.descriptor_type;
1640 auto&
args = item[
"args"] = nlohmann::json::array_t{};
1641 for (
const auto& arg : res.
args) {
1642 auto& json_arg =
args.emplace_back(nlohmann::json::object_t{});
1643 json_arg[
"type_name"] = arg.type_name;
1644 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
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
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 StringToShaderStage(const std::string &str)
static std::string ExecutionModelToStringName(spv::ExecutionModel model)
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
impeller::ShaderType type
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