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;
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));
240 for (
auto value : images.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);
309 std::shared_ptr<fml::Mapping> Reflector::GenerateReflectionHeader()
const {
313 std::shared_ptr<fml::Mapping> Reflector::GenerateReflectionCC()
const {
319 switch (target_platform) {
341 std::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(
391 uniform_description.padding_layout.push_back(
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) {
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++) {
452 for (
auto j = 0u; j < member.element_padding /
sizeof(float);
458 size_t member_float_count = member.byte_length /
sizeof(float);
459 float_count += member_float_count;
460 while (member_float_count > 0) {
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));
513 std::shared_ptr<ShaderBundleData> Reflector::GenerateShaderBundleData()
const {
514 const auto& entrypoints = compiler_->get_entry_points_and_stages();
515 if (entrypoints.size() != 1u) {
519 auto data = std::make_shared<ShaderBundleData>(
521 entrypoints.front().execution_model,
524 data->SetShaderData(shader_data_);
526 const auto uniforms = compiler_->get_shader_resources().uniform_buffers;
527 for (
const auto& uniform : uniforms) {
528 ShaderBundleData::ShaderUniformStruct uniform_struct;
529 uniform_struct.name = uniform.name;
532 uniform_struct.set = compiler_->get_decoration(
533 uniform.id, spv::Decoration::DecorationDescriptorSet);
534 uniform_struct.binding = compiler_->get_decoration(
535 uniform.id, spv::Decoration::DecorationBinding);
537 const auto type = compiler_->get_type(uniform.type_id);
538 if (type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
539 std::cerr <<
"Error: Uniform \"" << uniform.name
540 <<
"\" is not a struct. All Flutter GPU shader uniforms must "
546 size_t size_in_bytes = 0;
547 for (
const auto& struct_member : ReadStructMembers(uniform.type_id)) {
548 size_in_bytes += struct_member.byte_length;
552 ShaderBundleData::ShaderUniformStructField uniform_struct_field;
553 uniform_struct_field.name = struct_member.name;
554 uniform_struct_field.type = struct_member.base_type;
555 uniform_struct_field.offset_in_bytes = struct_member.offset;
556 uniform_struct_field.element_size_in_bytes = struct_member.size;
557 uniform_struct_field.total_size_in_bytes = struct_member.byte_length;
558 uniform_struct_field.array_elements = struct_member.array_elements;
559 uniform_struct.fields.push_back(uniform_struct_field);
561 uniform_struct.size_in_bytes = size_in_bytes;
563 data->AddUniformStruct(uniform_struct);
566 const auto sampled_images = compiler_->get_shader_resources().sampled_images;
567 for (
const auto& image : sampled_images) {
568 ShaderBundleData::ShaderUniformTexture uniform_texture;
569 uniform_texture.name = image.name;
572 uniform_texture.set = compiler_->get_decoration(
573 image.id, spv::Decoration::DecorationDescriptorSet);
574 uniform_texture.binding =
575 compiler_->get_decoration(image.id, spv::Decoration::DecorationBinding);
576 data->AddUniformTexture(uniform_texture);
580 if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
581 const auto inputs = compiler_->get_shader_resources().stage_inputs;
582 auto input_offsets = ComputeOffsets(inputs);
583 for (
const auto& input : inputs) {
584 std::optional<size_t> offset = GetOffset(input.id, input_offsets);
586 const auto type = compiler_->get_type(input.type_id);
588 InputDescription input_description;
589 input_description.name = input.name;
590 input_description.location = compiler_->get_decoration(
591 input.id, spv::Decoration::DecorationLocation);
592 input_description.set = compiler_->get_decoration(
593 input.id, spv::Decoration::DecorationDescriptorSet);
594 input_description.binding = compiler_->get_decoration(
595 input.id, spv::Decoration::DecorationBinding);
596 input_description.type = type.basetype;
597 input_description.bit_width = type.width;
598 input_description.vec_size = type.vecsize;
599 input_description.columns = type.columns;
600 input_description.offset = offset.value_or(0u);
601 data->AddInputDescription(std::move(input_description));
608 std::optional<uint32_t> Reflector::GetArrayElements(
609 const spirv_cross::SPIRType& type)
const {
610 if (type.array.empty()) {
613 FML_CHECK(type.array.size() == 1)
614 <<
"Multi-dimensional arrays are not supported.";
615 FML_CHECK(type.array_size_literal.front())
616 <<
"Must use a literal for array sizes.";
617 return type.array.front();
623 return "Metal Shading Language";
625 return "OpenGL Shading Language";
627 return "OpenGL Shading Language (Relaxed Vulkan Semantics)";
629 return "SkSL Shading Language";
634 std::shared_ptr<fml::Mapping> Reflector::InflateTemplate(
635 std::string_view tmpl)
const {
636 inja::Environment env;
637 env.set_trim_blocks(
true);
638 env.set_lstrip_blocks(
true);
640 env.add_callback(
"camel_case", 1u, [](inja::Arguments& args) {
641 return ToCamelCase(args.at(0u)->get<std::string>());
644 env.add_callback(
"to_shader_stage", 1u, [](inja::Arguments& args) {
648 env.add_callback(
"get_generator_name", 0u,
649 [type = compiler_.
GetType()](inja::Arguments& args) {
653 auto inflated_template =
654 std::make_shared<std::string>(env.render(tmpl, *template_arguments_));
656 return std::make_shared<fml::NonOwnedMapping>(
657 reinterpret_cast<const uint8_t*
>(inflated_template->data()),
658 inflated_template->size(), [inflated_template](
auto,
auto) {});
661 std::vector<size_t> Reflector::ComputeOffsets(
662 const spirv_cross::SmallVector<spirv_cross::Resource>& resources)
const {
663 std::vector<size_t> offsets(resources.size(), 0);
664 if (resources.size() == 0) {
667 for (
const auto& resource : resources) {
668 const auto type = compiler_->get_type(resource.type_id);
669 auto location = compiler_->get_decoration(
670 resource.id, spv::Decoration::DecorationLocation);
675 offsets[
location] = (type.width * type.vecsize) / 8;
677 for (
size_t i = 1; i < resources.size(); i++) {
678 offsets[i] += offsets[i - 1];
680 for (
size_t i = resources.size() - 1; i > 0; i--) {
681 offsets[i] = offsets[i - 1];
688 std::optional<size_t> Reflector::GetOffset(
690 const std::vector<size_t>& offsets)
const {
692 compiler_->get_decoration(
id, spv::Decoration::DecorationLocation);
699 std::optional<nlohmann::json::object_t> Reflector::ReflectResource(
700 const spirv_cross::Resource& resource,
701 std::optional<size_t> offset)
const {
702 nlohmann::json::object_t result;
704 result[
"name"] = resource.name;
705 result[
"descriptor_set"] = compiler_->get_decoration(
706 resource.id, spv::Decoration::DecorationDescriptorSet);
707 result[
"binding"] = compiler_->get_decoration(
708 resource.id, spv::Decoration::DecorationBinding);
709 result[
"set"] = compiler_->get_decoration(
710 resource.id, spv::Decoration::DecorationDescriptorSet);
711 result[
"location"] = compiler_->get_decoration(
712 resource.id, spv::Decoration::DecorationLocation);
714 compiler_->get_decoration(resource.id, spv::Decoration::DecorationIndex);
719 result[
"relaxed_precision"] =
720 compiler_->get_decoration(
721 resource.id, spv::Decoration::DecorationRelaxedPrecision) == 1;
722 result[
"offset"] = offset.value_or(0u);
723 auto type = ReflectType(resource.type_id);
724 if (!type.has_value()) {
727 result[
"type"] = std::move(type.value());
731 std::optional<nlohmann::json::object_t> Reflector::ReflectType(
732 const spirv_cross::TypeID& type_id)
const {
733 nlohmann::json::object_t result;
735 const auto type = compiler_->get_type(type_id);
738 result[
"bit_width"] = type.width;
739 result[
"vec_size"] = type.vecsize;
740 result[
"columns"] = type.columns;
741 auto& members = result[
"members"] = nlohmann::json::array_t{};
742 if (type.basetype == spirv_cross::SPIRType::BaseType::Struct) {
743 for (
const auto& struct_member : ReadStructMembers(type_id)) {
744 auto member = nlohmann::json::object_t{};
745 member[
"name"] = struct_member.name;
746 member[
"type"] = struct_member.type;
747 member[
"base_type"] =
749 member[
"offset"] = struct_member.offset;
750 member[
"size"] = struct_member.size;
751 member[
"byte_length"] = struct_member.byte_length;
752 if (struct_member.array_elements.has_value()) {
753 member[
"array_elements"] = struct_member.array_elements.value();
755 member[
"array_elements"] =
"std::nullopt";
757 if (struct_member.float_type.has_value()) {
758 member[
"float_type"] = struct_member.float_type.value();
760 member[
"float_type"] =
"std::nullopt";
762 members.emplace_back(std::move(member));
769 std::optional<nlohmann::json::array_t> Reflector::ReflectResources(
770 const spirv_cross::SmallVector<spirv_cross::Resource>& resources,
771 bool compute_offsets)
const {
772 nlohmann::json::array_t result;
773 result.reserve(resources.size());
774 std::vector<size_t> offsets;
775 if (compute_offsets) {
776 offsets = ComputeOffsets(resources);
778 for (
const auto& resource : resources) {
779 std::optional<size_t> maybe_offset = std::nullopt;
780 if (compute_offsets) {
781 maybe_offset = GetOffset(resource.id, offsets);
783 if (
auto reflected = ReflectResource(resource, maybe_offset);
784 reflected.has_value()) {
785 result.emplace_back(std::move(reflected.value()));
794 std::stringstream stream;
795 stream <<
"Padding<" << size <<
">";
805 spirv_cross::SPIRType::BaseType type) {
807 case spirv_cross::SPIRType::BaseType::Boolean:
810 .byte_size =
sizeof(bool),
812 case spirv_cross::SPIRType::BaseType::Float:
815 .byte_size =
sizeof(
Scalar),
817 case spirv_cross::SPIRType::BaseType::Half:
820 .byte_size =
sizeof(
Half),
822 case spirv_cross::SPIRType::BaseType::UInt:
825 .byte_size =
sizeof(uint32_t),
827 case spirv_cross::SPIRType::BaseType::Int:
830 .byte_size =
sizeof(int32_t),
852 auto struct_size = 0u;
853 for (
const auto& member : members) {
854 struct_size += member.byte_length;
859 std::vector<StructMember> Reflector::ReadStructMembers(
860 const spirv_cross::TypeID& type_id)
const {
861 const auto& struct_type = compiler_->get_type(type_id);
862 FML_CHECK(struct_type.basetype == spirv_cross::SPIRType::BaseType::Struct);
864 std::vector<StructMember> result;
866 size_t current_byte_offset = 0;
867 size_t max_member_alignment = 0;
869 for (
size_t i = 0; i < struct_type.member_types.size(); i++) {
870 const spirv_cross::SPIRType& member =
871 compiler_->get_type(struct_type.member_types[i]);
872 const uint32_t struct_member_offset =
873 compiler_->type_struct_member_offset(struct_type, i);
874 std::optional<uint32_t> array_elements = GetArrayElements(member);
876 if (struct_member_offset > current_byte_offset) {
877 const size_t alignment_pad = struct_member_offset - current_byte_offset;
878 result.emplace_back(StructMember{
880 spirv_cross::SPIRType::BaseType::Void,
882 std::format(
"_PADDING_{}_", GetMemberNameAtIndex(struct_type, i)),
889 current_byte_offset += alignment_pad;
892 max_member_alignment =
893 std::max<size_t>(max_member_alignment,
894 (member.width / 8) * member.columns * member.vecsize);
896 FML_CHECK(current_byte_offset == struct_member_offset);
899 if (member.basetype == spirv_cross::SPIRType::BaseType::Struct) {
902 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
906 uint32_t element_padding = stride - size;
907 result.emplace_back(StructMember{
908 compiler_->get_name(member.self),
910 GetMemberNameAtIndex(struct_type, i),
911 struct_member_offset,
913 stride * array_elements.value_or(1),
917 current_byte_offset += stride * array_elements.value_or(1);
922 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
923 member.width == 32 && member.columns == 2 && member.vecsize == 2) {
926 uint32_t count = array_elements.value_or(1) * 2;
927 uint32_t stride = 16;
928 uint32_t total_length = stride * count;
930 result.emplace_back(StructMember{
932 spirv_cross::SPIRType::BaseType::Float,
933 GetMemberNameAtIndex(struct_type, i),
934 struct_member_offset,
939 "ShaderFloatType::kMat2",
941 current_byte_offset += total_length;
945 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
946 member.width == 32 && member.columns == 3 && member.vecsize == 3) {
950 uint32_t count = array_elements.value_or(1) * 3;
951 uint32_t stride = 16;
952 uint32_t total_length = stride * count;
954 result.emplace_back(StructMember{
956 spirv_cross::SPIRType::BaseType::Float,
957 GetMemberNameAtIndex(struct_type, i),
958 struct_member_offset,
963 "ShaderFloatType::kMat3",
965 current_byte_offset += total_length;
971 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
972 member.width ==
sizeof(
Scalar) * 8 &&
973 member.columns == 4 &&
976 uint32_t stride = GetArrayStride<sizeof(Matrix)>(struct_type, member, i);
977 uint32_t element_padding = stride -
sizeof(Matrix);
978 result.emplace_back(StructMember{
981 GetMemberNameAtIndex(struct_type, i),
982 struct_member_offset,
984 stride * array_elements.value_or(1),
987 "ShaderFloatType::kMat4",
989 current_byte_offset += stride * array_elements.value_or(1);
994 if (member.basetype == spirv_cross::SPIRType::BaseType::UInt &&
995 member.width ==
sizeof(uint32_t) * 8 &&
996 member.columns == 1 &&
1000 GetArrayStride<sizeof(UintPoint32)>(struct_type, member, i);
1001 uint32_t element_padding = stride -
sizeof(
UintPoint32);
1002 result.emplace_back(StructMember{
1005 GetMemberNameAtIndex(struct_type, i),
1006 struct_member_offset,
1008 stride * array_elements.value_or(1),
1012 current_byte_offset += stride * array_elements.value_or(1);
1017 if (member.basetype == spirv_cross::SPIRType::BaseType::Int &&
1018 member.width ==
sizeof(int32_t) * 8 &&
1019 member.columns == 1 &&
1023 GetArrayStride<sizeof(IPoint32)>(struct_type, member, i);
1024 uint32_t element_padding = stride -
sizeof(
IPoint32);
1025 result.emplace_back(StructMember{
1028 GetMemberNameAtIndex(struct_type, i),
1029 struct_member_offset,
1031 stride * array_elements.value_or(1),
1035 current_byte_offset += stride * array_elements.value_or(1);
1040 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
1041 member.width ==
sizeof(
float) * 8 &&
1042 member.columns == 1 &&
1045 uint32_t stride = GetArrayStride<sizeof(Point)>(struct_type, member, i);
1046 uint32_t element_padding = stride -
sizeof(
Point);
1047 result.emplace_back(StructMember{
1050 GetMemberNameAtIndex(struct_type, i),
1051 struct_member_offset,
1053 stride * array_elements.value_or(1),
1056 "ShaderFloatType::kVec2",
1058 current_byte_offset += stride * array_elements.value_or(1);
1063 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
1064 member.width ==
sizeof(
float) * 8 &&
1065 member.columns == 1 &&
1068 uint32_t stride = GetArrayStride<sizeof(Vector3)>(struct_type, member, i);
1069 uint32_t element_padding = stride -
sizeof(Vector3);
1070 result.emplace_back(StructMember{
1073 GetMemberNameAtIndex(struct_type, i),
1074 struct_member_offset,
1076 stride * array_elements.value_or(1),
1079 "ShaderFloatType::kVec3",
1081 current_byte_offset += stride * array_elements.value_or(1);
1086 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
1087 member.width ==
sizeof(
float) * 8 &&
1088 member.columns == 1 &&
1091 uint32_t stride = GetArrayStride<sizeof(Vector4)>(struct_type, member, i);
1092 uint32_t element_padding = stride -
sizeof(Vector4);
1093 result.emplace_back(StructMember{
1096 GetMemberNameAtIndex(struct_type, i),
1097 struct_member_offset,
1099 stride * array_elements.value_or(1),
1102 "ShaderFloatType::kVec4",
1104 current_byte_offset += stride * array_elements.value_or(1);
1109 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1110 member.width ==
sizeof(Half) * 8 &&
1111 member.columns == 1 &&
1115 GetArrayStride<sizeof(HalfVector2)>(struct_type, member, i);
1116 uint32_t element_padding = stride -
sizeof(HalfVector2);
1117 result.emplace_back(StructMember{
1120 GetMemberNameAtIndex(struct_type, i),
1121 struct_member_offset,
1122 sizeof(HalfVector2),
1123 stride * array_elements.value_or(1),
1127 current_byte_offset += stride * array_elements.value_or(1);
1132 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1133 member.width ==
sizeof(Half) * 8 &&
1134 member.columns == 1 &&
1138 GetArrayStride<sizeof(HalfVector3)>(struct_type, member, i);
1139 uint32_t element_padding = stride -
sizeof(HalfVector3);
1140 result.emplace_back(StructMember{
1143 GetMemberNameAtIndex(struct_type, i),
1144 struct_member_offset,
1145 sizeof(HalfVector3),
1146 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(HalfVector4)>(struct_type, member, i);
1162 uint32_t element_padding = stride -
sizeof(HalfVector4);
1163 result.emplace_back(StructMember{
1166 GetMemberNameAtIndex(struct_type, i),
1167 struct_member_offset,
1168 sizeof(HalfVector4),
1169 stride * array_elements.value_or(1),
1173 current_byte_offset += stride * array_elements.value_or(1);
1180 if (maybe_known_type.has_value() &&
1181 member.columns == 1 &&
1184 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
1186 stride = maybe_known_type.value().byte_size;
1188 std::optional<std::string> float_type = std::nullopt;
1189 if (member.basetype == spirv_cross::SPIRType::BaseType::Float) {
1190 float_type =
"ShaderFloatType::kFloat";
1192 uint32_t element_padding = stride - maybe_known_type.value().byte_size;
1195 result.emplace_back(StructMember{
1196 maybe_known_type.value().name,
1198 GetMemberNameAtIndex(struct_type, i),
1199 struct_member_offset,
1200 maybe_known_type.value().byte_size,
1201 stride * array_elements.value_or(1),
1206 current_byte_offset += stride * array_elements.value_or(1);
1214 const size_t size = (member.width * member.columns * member.vecsize) / 8u;
1215 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
1219 size_t element_padding = stride - size;
1220 result.emplace_back(StructMember{
1223 GetMemberNameAtIndex(struct_type, i),
1224 struct_member_offset,
1226 stride * array_elements.value_or(1),
1230 current_byte_offset += stride * array_elements.value_or(1);
1235 if (max_member_alignment > 0u) {
1236 const size_t struct_length = current_byte_offset;
1238 const size_t excess = struct_length % max_member_alignment;
1240 const auto padding = max_member_alignment - excess;
1241 result.emplace_back(StructMember{
1243 spirv_cross::SPIRType::BaseType::Void,
1245 current_byte_offset,
1258 std::optional<Reflector::StructDefinition> Reflector::ReflectStructDefinition(
1259 const spirv_cross::TypeID& type_id)
const {
1260 const auto& type = compiler_->get_type(type_id);
1261 if (type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
1262 return std::nullopt;
1265 const auto struct_name = compiler_->get_name(type_id);
1266 if (struct_name.find(
"_RESERVED_IDENTIFIER_") != std::string::npos) {
1267 return std::nullopt;
1270 auto struct_members = ReadStructMembers(type_id);
1273 StructDefinition struc;
1274 struc.name = struct_name;
1275 struc.byte_length = reflected_struct_size;
1276 struc.members = std::move(struct_members);
1280 nlohmann::json::object_t Reflector::EmitStructDefinition(
1281 std::optional<Reflector::StructDefinition> struc)
const {
1282 nlohmann::json::object_t result;
1283 result[
"name"] = struc->name;
1284 result[
"byte_length"] = struc->byte_length;
1285 auto& members = result[
"members"] = nlohmann::json::array_t{};
1286 for (
const auto& struct_member : struc->members) {
1287 auto& member = members.emplace_back(nlohmann::json::object_t{});
1288 member[
"name"] = struct_member.name;
1289 member[
"type"] = struct_member.type;
1290 member[
"base_type"] =
1292 member[
"offset"] = struct_member.offset;
1293 member[
"byte_length"] = struct_member.byte_length;
1294 if (struct_member.array_elements.has_value()) {
1295 member[
"array_elements"] = struct_member.array_elements.value();
1297 member[
"array_elements"] =
"std::nullopt";
1299 member[
"element_padding"] = struct_member.element_padding;
1300 if (struct_member.float_type.has_value()) {
1301 member[
"float_type"] = struct_member.float_type.value();
1303 member[
"float_type"] =
"std::nullopt";
1317 const spirv_cross::Compiler& compiler,
1318 const spirv_cross::Resource* resource) {
1321 const auto& type = compiler.get_type(resource->type_id);
1323 const auto total_size = type.columns * type.vecsize * type.width / 8u;
1326 if (type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1327 type.columns == 1u && type.vecsize == 2u &&
1328 type.width ==
sizeof(
float) * 8u) {
1330 }
else if (type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1331 type.columns == 1u && type.vecsize == 4u &&
1332 type.width ==
sizeof(
float) * 8u) {
1334 }
else if (type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1335 type.columns == 1u && type.vecsize == 3u &&
1336 type.width ==
sizeof(
float) * 8u) {
1338 }
else if (type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1339 type.columns == 1u && type.vecsize == 1u &&
1340 type.width ==
sizeof(
float) * 8u) {
1342 }
else if (type.basetype == spirv_cross::SPIRType::BaseType::Int &&
1343 type.columns == 1u && type.vecsize == 1u &&
1344 type.width ==
sizeof(int32_t) * 8u) {
1354 std::optional<Reflector::StructDefinition>
1355 Reflector::ReflectPerVertexStructDefinition(
1356 const spirv_cross::SmallVector<spirv_cross::Resource>& stage_inputs)
const {
1359 if (stage_inputs.empty()) {
1360 return std::nullopt;
1364 std::set<uint32_t> locations;
1365 for (
const auto& input : stage_inputs) {
1366 auto location = compiler_->get_decoration(
1367 input.id, spv::Decoration::DecorationLocation);
1368 if (locations.count(
location) != 0) {
1370 return std::nullopt;
1375 for (
size_t i = 0; i < locations.size(); i++) {
1376 if (locations.count(i) != 1) {
1381 return std::nullopt;
1385 auto input_for_location =
1386 [&](uint32_t queried_location) ->
const spirv_cross::Resource* {
1387 for (
const auto& input : stage_inputs) {
1388 auto location = compiler_->get_decoration(
1389 input.id, spv::Decoration::DecorationLocation);
1390 if (
location == queried_location) {
1399 StructDefinition struc;
1400 struc.name =
"PerVertexData";
1401 struc.byte_length = 0u;
1402 for (
size_t i = 0; i < locations.size(); i++) {
1403 auto resource = input_for_location(i);
1404 if (resource ==
nullptr) {
1405 return std::nullopt;
1407 const auto vertex_type =
1410 auto member = StructMember{
1411 vertex_type.type_name,
1412 vertex_type.base_type,
1413 vertex_type.variable_name,
1415 vertex_type.byte_length,
1416 vertex_type.byte_length,
1420 struc.byte_length += vertex_type.byte_length;
1421 struc.members.emplace_back(std::move(member));
1426 std::optional<std::string> Reflector::GetMemberNameAtIndexIfExists(
1427 const spirv_cross::SPIRType& parent_type,
1428 size_t index)
const {
1429 if (parent_type.type_alias != 0) {
1430 return GetMemberNameAtIndexIfExists(
1431 compiler_->get_type(parent_type.type_alias), index);
1434 if (
auto found = ir_->meta.find(parent_type.self); found != ir_->meta.end()) {
1435 const auto& members = found->second.members;
1436 if (index < members.size() && !members[index].alias.empty()) {
1437 return members[index].alias;
1440 return std::nullopt;
1443 std::string Reflector::GetMemberNameAtIndex(
1444 const spirv_cross::SPIRType& parent_type,
1446 std::string suffix)
const {
1447 if (
auto name = GetMemberNameAtIndexIfExists(parent_type, index);
1449 return name.value();
1451 static std::atomic_size_t sUnnamedMembersID;
1452 std::stringstream stream;
1453 stream <<
"unnamed_" << sUnnamedMembersID++ << suffix;
1454 return stream.str();
1457 std::vector<Reflector::BindPrototype> Reflector::ReflectBindPrototypes(
1458 const spirv_cross::ShaderResources& resources,
1459 spv::ExecutionModel execution_model)
const {
1460 std::vector<BindPrototype> prototypes;
1461 for (
const auto& uniform_buffer : resources.uniform_buffers) {
1462 auto& proto = prototypes.emplace_back(BindPrototype{});
1463 proto.return_type =
"bool";
1465 proto.descriptor_type =
"DescriptorType::kUniformBuffer";
1467 std::stringstream stream;
1468 stream <<
"Bind uniform buffer for resource named " << uniform_buffer.name
1470 proto.docstring = stream.str();
1472 proto.args.push_back(BindPrototypeArgument{
1473 .type_name =
"ResourceBinder&",
1474 .argument_name =
"command",
1476 proto.args.push_back(BindPrototypeArgument{
1477 .type_name =
"BufferView",
1478 .argument_name =
"view",
1481 for (
const auto& storage_buffer : resources.storage_buffers) {
1482 auto& proto = prototypes.emplace_back(BindPrototype{});
1483 proto.return_type =
"bool";
1485 proto.descriptor_type =
"DescriptorType::kStorageBuffer";
1487 std::stringstream stream;
1488 stream <<
"Bind storage buffer for resource named " << storage_buffer.name
1490 proto.docstring = stream.str();
1492 proto.args.push_back(BindPrototypeArgument{
1493 .type_name =
"ResourceBinder&",
1494 .argument_name =
"command",
1496 proto.args.push_back(BindPrototypeArgument{
1497 .type_name =
"BufferView",
1498 .argument_name =
"view",
1501 for (
const auto& sampled_image : resources.sampled_images) {
1502 auto& proto = prototypes.emplace_back(BindPrototype{});
1503 proto.return_type =
"bool";
1505 proto.descriptor_type =
"DescriptorType::kSampledImage";
1507 std::stringstream stream;
1508 stream <<
"Bind combined image sampler for resource named "
1509 << sampled_image.name <<
".";
1510 proto.docstring = stream.str();
1512 proto.args.push_back(BindPrototypeArgument{
1513 .type_name =
"ResourceBinder&",
1514 .argument_name =
"command",
1516 proto.args.push_back(BindPrototypeArgument{
1517 .type_name =
"std::shared_ptr<const Texture>",
1518 .argument_name =
"texture",
1520 proto.args.push_back(BindPrototypeArgument{
1521 .type_name =
"raw_ptr<const Sampler>",
1522 .argument_name =
"sampler",
1525 for (
const auto& separate_image : resources.separate_images) {
1526 auto& proto = prototypes.emplace_back(BindPrototype{});
1527 proto.return_type =
"bool";
1529 proto.descriptor_type =
"DescriptorType::kImage";
1531 std::stringstream stream;
1532 stream <<
"Bind separate image for resource named " << separate_image.name
1534 proto.docstring = stream.str();
1536 proto.args.push_back(BindPrototypeArgument{
1537 .type_name =
"Command&",
1538 .argument_name =
"command",
1540 proto.args.push_back(BindPrototypeArgument{
1541 .type_name =
"std::shared_ptr<const Texture>",
1542 .argument_name =
"texture",
1545 for (
const auto& separate_sampler : resources.separate_samplers) {
1546 auto& proto = prototypes.emplace_back(BindPrototype{});
1547 proto.return_type =
"bool";
1549 proto.descriptor_type =
"DescriptorType::kSampler";
1551 std::stringstream stream;
1552 stream <<
"Bind separate sampler for resource named "
1553 << separate_sampler.name <<
".";
1554 proto.docstring = stream.str();
1556 proto.args.push_back(BindPrototypeArgument{
1557 .type_name =
"Command&",
1558 .argument_name =
"command",
1560 proto.args.push_back(BindPrototypeArgument{
1561 .type_name =
"std::shared_ptr<const Sampler>",
1562 .argument_name =
"sampler",
1568 nlohmann::json::array_t Reflector::EmitBindPrototypes(
1569 const spirv_cross::ShaderResources& resources,
1570 spv::ExecutionModel execution_model)
const {
1571 const auto prototypes = ReflectBindPrototypes(resources, execution_model);
1572 nlohmann::json::array_t result;
1573 for (
const auto& res : prototypes) {
1574 auto& item = result.emplace_back(nlohmann::json::object_t{});
1575 item[
"return_type"] = res.return_type;
1576 item[
"name"] = res.name;
1577 item[
"docstring"] = res.docstring;
1578 item[
"descriptor_type"] = res.descriptor_type;
1579 auto& args = item[
"args"] = nlohmann::json::array_t{};
1580 for (
const auto& arg : res.args) {
1581 auto& json_arg = args.emplace_back(nlohmann::json::object_t{});
1582 json_arg[
"type_name"] = arg.type_name;
1583 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