15 #include "flutter/fml/logging.h"
16 #include "fml/backtrace.h"
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_;
140 std::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));
270 for (
auto value : images.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);
339 std::shared_ptr<fml::Mapping> Reflector::GenerateReflectionHeader()
const {
343 std::shared_ptr<fml::Mapping> Reflector::GenerateReflectionCC()
const {
349 switch (target_platform) {
371 std::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(
421 uniform_description.padding_layout.push_back(
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) {
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++) {
482 for (
auto j = 0u; j < member.element_padding /
sizeof(float);
488 size_t member_float_count = member.byte_length /
sizeof(float);
489 float_count += member_float_count;
490 while (member_float_count > 0) {
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));
543 std::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));
640 std::optional<uint32_t> Reflector::GetArrayElements(
641 const spirv_cross::SPIRType& type)
const {
642 if (type.array.empty()) {
645 FML_CHECK(type.array.size() == 1)
646 <<
"Multi-dimensional arrays are not supported.";
647 FML_CHECK(type.array_size_literal.front())
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";
666 std::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) {
673 return ToCamelCase(args.at(0u)->get<std::string>());
676 env.add_callback(
"to_shader_stage", 1u, [](inja::Arguments& args) {
680 env.add_callback(
"get_generator_name", 0u,
681 [type = compiler_.
GetType()](inja::Arguments& args) {
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) {});
693 std::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);
707 offsets[
location] = (type.width * type.vecsize) / 8;
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];
720 std::optional<size_t> Reflector::GetOffset(
722 const std::vector<size_t>& offsets)
const {
724 compiler_->get_decoration(
id, spv::Decoration::DecorationLocation);
731 std::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());
763 std::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));
801 std::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;
891 std::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,
1319 std::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);
1341 nlohmann::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) {
1415 std::optional<Reflector::StructDefinition>
1416 Reflector::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));
1487 std::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;
1504 std::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();
1518 std::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",
1629 nlohmann::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
Vector2 padding
The halo padding in source space.
static std::optional< KnownType > ReadKnownScalarType(spirv_cross::SPIRType::BaseType type)
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 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::optional< RuntimeStageBackend > GetRuntimeStageBackend(TargetPlatform target_platform)
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
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