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_field.vec_size = struct_member.vec_size;
560 uniform_struct_field.columns = struct_member.columns;
561 uniform_struct.fields.push_back(uniform_struct_field);
563 uniform_struct.size_in_bytes = size_in_bytes;
565 data->AddUniformStruct(uniform_struct);
568 const auto sampled_images = compiler_->get_shader_resources().sampled_images;
569 for (
const auto&
image : sampled_images) {
570 ShaderBundleData::ShaderUniformTexture uniform_texture;
571 uniform_texture.name =
image.name;
574 uniform_texture.set = compiler_->get_decoration(
575 image.id, spv::Decoration::DecorationDescriptorSet);
576 uniform_texture.binding =
577 compiler_->get_decoration(
image.id, spv::Decoration::DecorationBinding);
578 data->AddUniformTexture(uniform_texture);
582 if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
583 const auto inputs = compiler_->get_shader_resources().stage_inputs;
584 auto input_offsets = ComputeOffsets(inputs);
585 for (
const auto&
input : inputs) {
586 std::optional<size_t> offset = GetOffset(
input.id, input_offsets);
588 const auto type = compiler_->get_type(
input.type_id);
590 InputDescription input_description;
591 input_description.name =
input.name;
592 input_description.location = compiler_->get_decoration(
593 input.id, spv::Decoration::DecorationLocation);
594 input_description.set = compiler_->get_decoration(
595 input.id, spv::Decoration::DecorationDescriptorSet);
596 input_description.binding = compiler_->get_decoration(
597 input.id, spv::Decoration::DecorationBinding);
598 input_description.type =
type.basetype;
599 input_description.bit_width =
type.width;
600 input_description.vec_size =
type.vecsize;
601 input_description.columns =
type.columns;
602 input_description.offset = offset.value_or(0u);
603 data->AddInputDescription(std::move(input_description));
610std::optional<uint32_t> Reflector::GetArrayElements(
611 const spirv_cross::SPIRType&
type)
const {
612 if (
type.array.empty()) {
616 <<
"Multi-dimensional arrays are not supported.";
618 <<
"Must use a literal for array sizes.";
619 return type.array.front();
625 return "Metal Shading Language";
627 return "OpenGL Shading Language";
629 return "OpenGL Shading Language (Relaxed Vulkan Semantics)";
631 return "SkSL Shading Language";
636std::shared_ptr<fml::Mapping> Reflector::InflateTemplate(
637 std::string_view tmpl)
const {
638 inja::Environment env;
639 env.set_trim_blocks(
true);
640 env.set_lstrip_blocks(
true);
642 env.add_callback(
"camel_case", 1u, [](inja::Arguments&
args) {
646 env.add_callback(
"to_shader_stage", 1u, [](inja::Arguments&
args) {
650 env.add_callback(
"get_generator_name", 0u,
655 auto inflated_template =
656 std::make_shared<std::string>(env.render(tmpl, *template_arguments_));
658 return std::make_shared<fml::NonOwnedMapping>(
659 reinterpret_cast<const uint8_t*
>(inflated_template->data()),
660 inflated_template->size(), [inflated_template](
auto,
auto) {});
663std::vector<size_t> Reflector::ComputeOffsets(
664 const spirv_cross::SmallVector<spirv_cross::Resource>& resources)
const {
665 std::vector<size_t> offsets(resources.size(), 0);
666 if (resources.size() == 0) {
669 for (
const auto& resource : resources) {
670 const auto type = compiler_->get_type(resource.type_id);
671 auto location = compiler_->get_decoration(
672 resource.id, spv::Decoration::DecorationLocation);
679 for (
size_t i = 1;
i < resources.size();
i++) {
680 offsets[
i] += offsets[
i - 1];
682 for (
size_t i = resources.size() - 1;
i > 0;
i--) {
683 offsets[
i] = offsets[
i - 1];
690std::optional<size_t> Reflector::GetOffset(
692 const std::vector<size_t>& offsets)
const {
694 compiler_->get_decoration(
id, spv::Decoration::DecorationLocation);
701std::optional<nlohmann::json::object_t> Reflector::ReflectResource(
702 const spirv_cross::Resource& resource,
703 std::optional<size_t> offset)
const {
704 nlohmann::json::object_t result;
706 result[
"name"] = resource.name;
707 result[
"descriptor_set"] = compiler_->get_decoration(
708 resource.id, spv::Decoration::DecorationDescriptorSet);
709 result[
"binding"] = compiler_->get_decoration(
710 resource.id, spv::Decoration::DecorationBinding);
711 result[
"set"] = compiler_->get_decoration(
712 resource.id, spv::Decoration::DecorationDescriptorSet);
713 result[
"location"] = compiler_->get_decoration(
714 resource.id, spv::Decoration::DecorationLocation);
716 compiler_->get_decoration(resource.id, spv::Decoration::DecorationIndex);
721 result[
"relaxed_precision"] =
722 compiler_->get_decoration(
723 resource.id, spv::Decoration::DecorationRelaxedPrecision) == 1;
724 result[
"offset"] = offset.value_or(0u);
725 auto type = ReflectType(resource.type_id);
726 if (!
type.has_value()) {
729 result[
"type"] = std::move(
type.value());
733std::optional<nlohmann::json::object_t> Reflector::ReflectType(
734 const spirv_cross::TypeID& type_id)
const {
735 nlohmann::json::object_t result;
737 const auto type = compiler_->get_type(type_id);
740 result[
"bit_width"] =
type.width;
741 result[
"vec_size"] =
type.vecsize;
742 result[
"columns"] =
type.columns;
743 auto& members = result[
"members"] = nlohmann::json::array_t{};
744 if (
type.basetype == spirv_cross::SPIRType::BaseType::Struct) {
745 for (
const auto& struct_member : ReadStructMembers(type_id)) {
746 auto member = nlohmann::json::object_t{};
747 member[
"name"] = struct_member.name;
748 member[
"type"] = struct_member.type;
749 member[
"base_type"] =
751 member[
"offset"] = struct_member.offset;
752 member[
"size"] = struct_member.size;
753 member[
"byte_length"] = struct_member.byte_length;
754 if (struct_member.array_elements.has_value()) {
755 member[
"array_elements"] = struct_member.array_elements.value();
757 member[
"array_elements"] =
"std::nullopt";
759 if (struct_member.float_type.has_value()) {
760 member[
"float_type"] = struct_member.float_type.value();
762 member[
"float_type"] =
"std::nullopt";
764 members.emplace_back(std::move(member));
771std::optional<nlohmann::json::array_t> Reflector::ReflectResources(
772 const spirv_cross::SmallVector<spirv_cross::Resource>& resources,
773 bool compute_offsets)
const {
774 nlohmann::json::array_t result;
775 result.reserve(resources.size());
776 std::vector<size_t> offsets;
777 if (compute_offsets) {
778 offsets = ComputeOffsets(resources);
780 for (
const auto& resource : resources) {
781 std::optional<size_t> maybe_offset = std::nullopt;
782 if (compute_offsets) {
783 maybe_offset = GetOffset(resource.id, offsets);
785 if (
auto reflected = ReflectResource(resource, maybe_offset);
786 reflected.has_value()) {
787 result.emplace_back(std::move(reflected.value()));
796 std::stringstream stream;
797 stream <<
"Padding<" << size <<
">";
807 spirv_cross::SPIRType::BaseType
type) {
809 case spirv_cross::SPIRType::BaseType::Boolean:
812 .byte_size =
sizeof(bool),
814 case spirv_cross::SPIRType::BaseType::Float:
817 .byte_size =
sizeof(
Scalar),
819 case spirv_cross::SPIRType::BaseType::Half:
822 .byte_size =
sizeof(
Half),
824 case spirv_cross::SPIRType::BaseType::UInt:
827 .byte_size =
sizeof(uint32_t),
829 case spirv_cross::SPIRType::BaseType::Int:
832 .byte_size =
sizeof(int32_t),
854 auto struct_size = 0u;
855 for (
const auto& member : members) {
856 struct_size += member.byte_length;
861std::vector<StructMember> Reflector::ReadStructMembers(
862 const spirv_cross::TypeID& type_id)
const {
863 const auto& struct_type = compiler_->get_type(type_id);
864 FML_CHECK(struct_type.basetype == spirv_cross::SPIRType::BaseType::Struct);
866 std::vector<StructMember> result;
868 size_t current_byte_offset = 0;
869 size_t max_member_alignment = 0;
871 for (
size_t i = 0;
i < struct_type.member_types.size();
i++) {
872 const spirv_cross::SPIRType& member =
873 compiler_->get_type(struct_type.member_types[
i]);
874 const uint32_t struct_member_offset =
875 compiler_->type_struct_member_offset(struct_type,
i);
876 std::optional<uint32_t> array_elements = GetArrayElements(member);
878 if (struct_member_offset > current_byte_offset) {
879 const size_t alignment_pad = struct_member_offset - current_byte_offset;
880 result.emplace_back(StructMember{
882 spirv_cross::SPIRType::BaseType::Void,
884 std::format(
"_PADDING_{}_", GetMemberNameAtIndex(struct_type,
i)),
891 current_byte_offset += alignment_pad;
894 max_member_alignment =
895 std::max<size_t>(max_member_alignment,
896 (member.width / 8) * member.columns * member.vecsize);
898 FML_CHECK(current_byte_offset == struct_member_offset);
901 if (member.basetype == spirv_cross::SPIRType::BaseType::Struct) {
904 uint32_t stride = GetArrayStride<0>(struct_type, member,
i);
908 uint32_t element_padding = stride -
size;
909 result.emplace_back(StructMember{
910 compiler_->get_name(member.self),
912 GetMemberNameAtIndex(struct_type,
i),
913 struct_member_offset,
915 stride * array_elements.value_or(1),
919 current_byte_offset += stride * array_elements.value_or(1);
924 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
925 member.width == 32 && member.columns == 2 && member.vecsize == 2) {
928 uint32_t count = array_elements.value_or(1) * 2;
929 uint32_t stride = 16;
930 uint32_t total_length = stride * count;
932 result.emplace_back(StructMember{
934 spirv_cross::SPIRType::BaseType::Float,
935 GetMemberNameAtIndex(struct_type,
i),
936 struct_member_offset,
941 "ShaderFloatType::kMat2",
945 current_byte_offset += total_length;
949 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
950 member.width == 32 && member.columns == 3 && member.vecsize == 3) {
954 uint32_t count = array_elements.value_or(1) * 3;
955 uint32_t stride = 16;
956 uint32_t total_length = stride * count;
958 result.emplace_back(StructMember{
960 spirv_cross::SPIRType::BaseType::Float,
961 GetMemberNameAtIndex(struct_type,
i),
962 struct_member_offset,
967 "ShaderFloatType::kMat3",
971 current_byte_offset += total_length;
977 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
978 member.width ==
sizeof(
Scalar) * 8 &&
979 member.columns == 4 &&
982 uint32_t stride = GetArrayStride<sizeof(Matrix)>(struct_type, member,
i);
983 uint32_t element_padding = stride -
sizeof(Matrix);
984 result.emplace_back(StructMember{
987 GetMemberNameAtIndex(struct_type,
i),
988 struct_member_offset,
990 stride * array_elements.value_or(1),
993 "ShaderFloatType::kMat4",
997 current_byte_offset += stride * array_elements.value_or(1);
1002 if (member.basetype == spirv_cross::SPIRType::BaseType::UInt &&
1003 member.width ==
sizeof(uint32_t) * 8 &&
1004 member.columns == 1 &&
1008 GetArrayStride<sizeof(UintPoint32)>(struct_type, member,
i);
1009 uint32_t element_padding = stride -
sizeof(
UintPoint32);
1010 result.emplace_back(StructMember{
1013 GetMemberNameAtIndex(struct_type,
i),
1014 struct_member_offset,
1016 stride * array_elements.value_or(1),
1023 current_byte_offset += stride * array_elements.value_or(1);
1028 if (member.basetype == spirv_cross::SPIRType::BaseType::Int &&
1029 member.width ==
sizeof(int32_t) * 8 &&
1030 member.columns == 1 &&
1034 GetArrayStride<sizeof(IPoint32)>(struct_type, member,
i);
1035 uint32_t element_padding = stride -
sizeof(
IPoint32);
1036 result.emplace_back(StructMember{
1039 GetMemberNameAtIndex(struct_type,
i),
1040 struct_member_offset,
1042 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(Point)>(struct_type, member,
i);
1060 uint32_t element_padding = stride -
sizeof(
Point);
1061 result.emplace_back(StructMember{
1064 GetMemberNameAtIndex(struct_type,
i),
1065 struct_member_offset,
1067 stride * array_elements.value_or(1),
1070 "ShaderFloatType::kVec2",
1074 current_byte_offset += stride * array_elements.value_or(1);
1079 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
1080 member.width ==
sizeof(
float) * 8 &&
1081 member.columns == 1 &&
1084 uint32_t stride = GetArrayStride<sizeof(Vector3)>(struct_type, member,
i);
1085 uint32_t element_padding = stride -
sizeof(Vector3);
1086 result.emplace_back(StructMember{
1089 GetMemberNameAtIndex(struct_type,
i),
1090 struct_member_offset,
1092 stride * array_elements.value_or(1),
1095 "ShaderFloatType::kVec3",
1099 current_byte_offset += stride * array_elements.value_or(1);
1104 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
1105 member.width ==
sizeof(
float) * 8 &&
1106 member.columns == 1 &&
1109 uint32_t stride = GetArrayStride<sizeof(Vector4)>(struct_type, member,
i);
1110 uint32_t element_padding = stride -
sizeof(Vector4);
1111 result.emplace_back(StructMember{
1114 GetMemberNameAtIndex(struct_type,
i),
1115 struct_member_offset,
1117 stride * array_elements.value_or(1),
1120 "ShaderFloatType::kVec4",
1124 current_byte_offset += stride * array_elements.value_or(1);
1129 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1130 member.width ==
sizeof(Half) * 8 &&
1131 member.columns == 1 &&
1135 GetArrayStride<sizeof(HalfVector2)>(struct_type, member,
i);
1136 uint32_t element_padding = stride -
sizeof(HalfVector2);
1137 result.emplace_back(StructMember{
1140 GetMemberNameAtIndex(struct_type,
i),
1141 struct_member_offset,
1142 sizeof(HalfVector2),
1143 stride * array_elements.value_or(1),
1150 current_byte_offset += stride * array_elements.value_or(1);
1155 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1156 member.width ==
sizeof(Half) * 8 &&
1157 member.columns == 1 &&
1161 GetArrayStride<sizeof(HalfVector3)>(struct_type, member,
i);
1162 uint32_t element_padding = stride -
sizeof(HalfVector3);
1163 result.emplace_back(StructMember{
1166 GetMemberNameAtIndex(struct_type,
i),
1167 struct_member_offset,
1168 sizeof(HalfVector3),
1169 stride * array_elements.value_or(1),
1176 current_byte_offset += stride * array_elements.value_or(1);
1181 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1182 member.width ==
sizeof(Half) * 8 &&
1183 member.columns == 1 &&
1187 GetArrayStride<sizeof(HalfVector4)>(struct_type, member,
i);
1188 uint32_t element_padding = stride -
sizeof(HalfVector4);
1189 result.emplace_back(StructMember{
1192 GetMemberNameAtIndex(struct_type,
i),
1193 struct_member_offset,
1194 sizeof(HalfVector4),
1195 stride * array_elements.value_or(1),
1202 current_byte_offset += stride * array_elements.value_or(1);
1209 if (maybe_known_type.has_value() &&
1210 member.columns == 1 &&
1213 uint32_t stride = GetArrayStride<0>(struct_type, member,
i);
1215 stride = maybe_known_type.value().byte_size;
1217 std::optional<std::string> float_type = std::nullopt;
1218 if (member.basetype == spirv_cross::SPIRType::BaseType::Float) {
1219 float_type =
"ShaderFloatType::kFloat";
1221 uint32_t element_padding = stride - maybe_known_type.value().byte_size;
1224 result.emplace_back(StructMember{
1225 maybe_known_type.value().
name,
1227 GetMemberNameAtIndex(struct_type,
i),
1228 struct_member_offset,
1229 maybe_known_type.value().byte_size,
1230 stride * array_elements.value_or(1),
1237 current_byte_offset += stride * array_elements.value_or(1);
1245 const size_t size = (member.width * member.columns * member.vecsize) / 8u;
1246 uint32_t stride = GetArrayStride<0>(struct_type, member,
i);
1250 size_t element_padding = stride -
size;
1251 result.emplace_back(StructMember{
1254 GetMemberNameAtIndex(struct_type,
i),
1255 struct_member_offset,
1257 stride * array_elements.value_or(1),
1261 current_byte_offset += stride * array_elements.value_or(1);
1266 if (max_member_alignment > 0u) {
1267 const size_t struct_length = current_byte_offset;
1269 const size_t excess = struct_length % max_member_alignment;
1271 const auto padding = max_member_alignment - excess;
1272 result.emplace_back(StructMember{
1274 spirv_cross::SPIRType::BaseType::Void,
1276 current_byte_offset,
1289std::optional<Reflector::StructDefinition> Reflector::ReflectStructDefinition(
1290 const spirv_cross::TypeID& type_id)
const {
1291 const auto&
type = compiler_->get_type(type_id);
1292 if (
type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
1293 return std::nullopt;
1296 const auto struct_name = compiler_->get_name(type_id);
1297 if (struct_name.find(
"_RESERVED_IDENTIFIER_") != std::string::npos) {
1298 return std::nullopt;
1301 auto struct_members = ReadStructMembers(type_id);
1304 StructDefinition struc;
1305 struc.name = struct_name;
1306 struc.byte_length = reflected_struct_size;
1307 struc.members = std::move(struct_members);
1311nlohmann::json::object_t Reflector::EmitStructDefinition(
1312 std::optional<Reflector::StructDefinition> struc)
const {
1313 nlohmann::json::object_t result;
1314 result[
"name"] = struc->name;
1315 result[
"byte_length"] = struc->byte_length;
1316 auto& members = result[
"members"] = nlohmann::json::array_t{};
1317 for (
const auto& struct_member : struc->members) {
1318 auto& member = members.emplace_back(nlohmann::json::object_t{});
1319 member[
"name"] = struct_member.name;
1320 member[
"type"] = struct_member.type;
1321 member[
"base_type"] =
1323 member[
"offset"] = struct_member.offset;
1324 member[
"byte_length"] = struct_member.byte_length;
1325 if (struct_member.array_elements.has_value()) {
1326 member[
"array_elements"] = struct_member.array_elements.value();
1328 member[
"array_elements"] =
"std::nullopt";
1330 member[
"element_padding"] = struct_member.element_padding;
1331 if (struct_member.float_type.has_value()) {
1332 member[
"float_type"] = struct_member.float_type.value();
1334 member[
"float_type"] =
"std::nullopt";
1348 const spirv_cross::Compiler& compiler,
1349 const spirv_cross::Resource* resource) {
1352 const auto&
type = compiler.get_type(resource->type_id);
1354 const auto total_size =
type.columns *
type.vecsize *
type.width / 8u;
1357 if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1358 type.columns == 1u &&
type.vecsize == 2u &&
1359 type.width ==
sizeof(
float) * 8u) {
1361 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1362 type.columns == 1u &&
type.vecsize == 4u &&
1363 type.width ==
sizeof(
float) * 8u) {
1365 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1366 type.columns == 1u &&
type.vecsize == 3u &&
1367 type.width ==
sizeof(
float) * 8u) {
1369 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1370 type.columns == 1u &&
type.vecsize == 1u &&
1371 type.width ==
sizeof(
float) * 8u) {
1373 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Int &&
1374 type.columns == 1u &&
type.vecsize == 1u &&
1375 type.width ==
sizeof(int32_t) * 8u) {
1385std::optional<Reflector::StructDefinition>
1386Reflector::ReflectPerVertexStructDefinition(
1387 const spirv_cross::SmallVector<spirv_cross::Resource>& stage_inputs)
const {
1390 if (stage_inputs.empty()) {
1391 return std::nullopt;
1395 std::set<uint32_t> locations;
1396 for (
const auto&
input : stage_inputs) {
1397 auto location = compiler_->get_decoration(
1398 input.id, spv::Decoration::DecorationLocation);
1399 if (locations.count(
location) != 0) {
1401 return std::nullopt;
1406 for (
size_t i = 0;
i < locations.size();
i++) {
1407 if (locations.count(
i) != 1) {
1412 return std::nullopt;
1416 auto input_for_location =
1417 [&](uint32_t queried_location) ->
const spirv_cross::Resource* {
1418 for (
const auto&
input : stage_inputs) {
1419 auto location = compiler_->get_decoration(
1420 input.id, spv::Decoration::DecorationLocation);
1421 if (
location == queried_location) {
1430 StructDefinition struc;
1431 struc.name =
"PerVertexData";
1432 struc.byte_length = 0u;
1433 for (
size_t i = 0;
i < locations.size();
i++) {
1434 auto resource = input_for_location(
i);
1435 if (resource ==
nullptr) {
1436 return std::nullopt;
1438 const auto vertex_type =
1441 auto member = StructMember{
1442 vertex_type.type_name,
1443 vertex_type.base_type,
1444 vertex_type.variable_name,
1446 vertex_type.byte_length,
1447 vertex_type.byte_length,
1451 struc.byte_length += vertex_type.byte_length;
1452 struc.members.emplace_back(std::move(member));
1457std::optional<std::string> Reflector::GetMemberNameAtIndexIfExists(
1458 const spirv_cross::SPIRType& parent_type,
1459 size_t index)
const {
1460 if (parent_type.type_alias != 0) {
1461 return GetMemberNameAtIndexIfExists(
1462 compiler_->get_type(parent_type.type_alias), index);
1465 if (
auto found = ir_->meta.find(parent_type.self); found != ir_->meta.end()) {
1466 const auto& members = found->second.members;
1467 if (index < members.size() && !members[index].alias.empty()) {
1468 return members[index].alias;
1471 return std::nullopt;
1474std::string Reflector::GetMemberNameAtIndex(
1475 const spirv_cross::SPIRType& parent_type,
1477 std::string suffix)
const {
1478 if (
auto name = GetMemberNameAtIndexIfExists(parent_type, index);
1480 return name.value();
1482 static std::atomic_size_t sUnnamedMembersID;
1483 std::stringstream stream;
1484 stream <<
"unnamed_" << sUnnamedMembersID++ << suffix;
1485 return stream.str();
1488std::vector<Reflector::BindPrototype> Reflector::ReflectBindPrototypes(
1489 const spirv_cross::ShaderResources& resources,
1490 spv::ExecutionModel execution_model)
const {
1491 std::vector<BindPrototype> prototypes;
1492 for (
const auto& uniform_buffer : resources.uniform_buffers) {
1493 auto& proto = prototypes.emplace_back(BindPrototype{});
1494 proto.return_type =
"bool";
1496 proto.descriptor_type =
"DescriptorType::kUniformBuffer";
1498 std::stringstream stream;
1499 stream <<
"Bind uniform buffer for resource named " << uniform_buffer.name
1501 proto.docstring = stream.str();
1503 proto.args.push_back(BindPrototypeArgument{
1504 .type_name =
"ResourceBinder&",
1505 .argument_name =
"command",
1507 proto.args.push_back(BindPrototypeArgument{
1508 .type_name =
"BufferView",
1509 .argument_name =
"view",
1512 for (
const auto& storage_buffer : resources.storage_buffers) {
1513 auto& proto = prototypes.emplace_back(BindPrototype{});
1514 proto.return_type =
"bool";
1516 proto.descriptor_type =
"DescriptorType::kStorageBuffer";
1518 std::stringstream stream;
1519 stream <<
"Bind storage buffer for resource named " << storage_buffer.name
1521 proto.docstring = stream.str();
1523 proto.args.push_back(BindPrototypeArgument{
1524 .type_name =
"ResourceBinder&",
1525 .argument_name =
"command",
1527 proto.args.push_back(BindPrototypeArgument{
1528 .type_name =
"BufferView",
1529 .argument_name =
"view",
1532 for (
const auto& sampled_image : resources.sampled_images) {
1533 auto& proto = prototypes.emplace_back(BindPrototype{});
1534 proto.return_type =
"bool";
1536 proto.descriptor_type =
"DescriptorType::kSampledImage";
1538 std::stringstream stream;
1539 stream <<
"Bind combined image sampler for resource named "
1540 << sampled_image.name <<
".";
1541 proto.docstring = stream.str();
1543 proto.args.push_back(BindPrototypeArgument{
1544 .type_name =
"ResourceBinder&",
1545 .argument_name =
"command",
1547 proto.args.push_back(BindPrototypeArgument{
1548 .type_name =
"std::shared_ptr<const Texture>",
1549 .argument_name =
"texture",
1551 proto.args.push_back(BindPrototypeArgument{
1552 .type_name =
"raw_ptr<const Sampler>",
1553 .argument_name =
"sampler",
1556 for (
const auto& separate_image : resources.separate_images) {
1557 auto& proto = prototypes.emplace_back(BindPrototype{});
1558 proto.return_type =
"bool";
1560 proto.descriptor_type =
"DescriptorType::kImage";
1562 std::stringstream stream;
1563 stream <<
"Bind separate image for resource named " << separate_image.name
1565 proto.docstring = stream.str();
1567 proto.args.push_back(BindPrototypeArgument{
1568 .type_name =
"Command&",
1569 .argument_name =
"command",
1571 proto.args.push_back(BindPrototypeArgument{
1572 .type_name =
"std::shared_ptr<const Texture>",
1573 .argument_name =
"texture",
1576 for (
const auto& separate_sampler : resources.separate_samplers) {
1577 auto& proto = prototypes.emplace_back(BindPrototype{});
1578 proto.return_type =
"bool";
1580 proto.descriptor_type =
"DescriptorType::kSampler";
1582 std::stringstream stream;
1583 stream <<
"Bind separate sampler for resource named "
1584 << separate_sampler.name <<
".";
1585 proto.docstring = stream.str();
1587 proto.args.push_back(BindPrototypeArgument{
1588 .type_name =
"Command&",
1589 .argument_name =
"command",
1591 proto.args.push_back(BindPrototypeArgument{
1592 .type_name =
"std::shared_ptr<const Sampler>",
1593 .argument_name =
"sampler",
1599nlohmann::json::array_t Reflector::EmitBindPrototypes(
1600 const spirv_cross::ShaderResources& resources,
1601 spv::ExecutionModel execution_model)
const {
1602 const auto prototypes = ReflectBindPrototypes(resources, execution_model);
1603 nlohmann::json::array_t result;
1604 for (
const auto& res : prototypes) {
1605 auto& item = result.emplace_back(nlohmann::json::object_t{});
1606 item[
"return_type"] = res.return_type;
1607 item[
"name"] = res.name;
1608 item[
"docstring"] = res.docstring;
1609 item[
"descriptor_type"] = res.descriptor_type;
1610 auto&
args = item[
"args"] = nlohmann::json::array_t{};
1611 for (
const auto& arg : res.
args) {
1612 auto& json_arg =
args.emplace_back(nlohmann::json::object_t{});
1613 json_arg[
"type_name"] = arg.type_name;
1614 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