14 #include "flutter/fml/logging.h"
30 using Type = spirv_cross::SPIRType::BaseType;
33 return "ShaderType::kVoid";
35 return "ShaderType::kBoolean";
37 return "ShaderType::kSignedByte";
39 return "ShaderType::kUnsignedByte";
41 return "ShaderType::kSignedShort";
43 return "ShaderType::kUnsignedShort";
45 return "ShaderType::kSignedInt";
47 return "ShaderType::kUnsignedInt";
49 return "ShaderType::kSignedInt64";
51 return "ShaderType::kUnsignedInt64";
52 case Type::AtomicCounter:
53 return "ShaderType::kAtomicCounter";
55 return "ShaderType::kHalfFloat";
57 return "ShaderType::kFloat";
59 return "ShaderType::kDouble";
61 return "ShaderType::kStruct";
63 return "ShaderType::kImage";
64 case Type::SampledImage:
65 return "ShaderType::kSampledImage";
67 return "ShaderType::kSampler";
69 return "ShaderType::kUnknown";
75 case spv::ExecutionModel::ExecutionModelVertex:
77 case spv::ExecutionModel::ExecutionModelFragment:
79 case spv::ExecutionModel::ExecutionModelGLCompute:
87 if (str ==
"vertex") {
88 return "ShaderStage::kVertex";
91 if (str ==
"fragment") {
92 return "ShaderStage::kFragment";
95 if (str ==
"compute") {
96 return "ShaderStage::kCompute";
99 return "ShaderStage::kUnknown";
103 const std::shared_ptr<const spirv_cross::ParsedIR>& ir,
104 const std::shared_ptr<fml::Mapping>& shader_data,
106 : options_(
std::move(options)),
108 shader_data_(shader_data),
109 compiler_(compiler) {
110 if (!ir_ || !compiler_) {
114 if (
auto template_arguments = GenerateTemplateArguments();
115 template_arguments.has_value()) {
116 template_arguments_ =
117 std::make_unique<nlohmann::json>(std::move(template_arguments.value()));
122 reflection_header_ = GenerateReflectionHeader();
123 if (!reflection_header_) {
127 reflection_cc_ = GenerateReflectionCC();
128 if (!reflection_cc_) {
132 runtime_stage_shader_ = GenerateRuntimeStageData();
149 std::make_shared<std::string>(template_arguments_->dump(2u));
151 return std::make_shared<fml::NonOwnedMapping>(
152 reinterpret_cast<const uint8_t*
>(json_string->data()),
153 json_string->size(), [json_string](
auto,
auto) {});
157 return reflection_header_;
161 return reflection_cc_;
166 return runtime_stage_shader_;
169 std::optional<nlohmann::json> Reflector::GenerateTemplateArguments()
const {
172 const auto& entrypoints = compiler_->get_entry_points_and_stages();
173 if (entrypoints.size() != 1) {
174 VALIDATION_LOG <<
"Incorrect number of entrypoints in the shader. Found "
175 << entrypoints.size() <<
" but expected 1.";
179 auto execution_model = entrypoints.front().execution_model;
187 const auto shader_resources = compiler_->get_shader_resources();
191 auto& subpass_inputs = root[
"subpass_inputs"] = nlohmann::json::array_t{};
192 if (
auto subpass_inputs_json =
193 ReflectResources(shader_resources.subpass_inputs);
194 subpass_inputs_json.has_value()) {
195 for (
auto subpass_input : subpass_inputs_json.value()) {
196 subpass_input[
"descriptor_type"] =
"DescriptorType::kInputAttachment";
197 subpass_inputs.emplace_back(std::move(subpass_input));
206 auto& buffers = root[
"buffers"] = nlohmann::json::array_t{};
207 if (
auto uniform_buffers_json =
208 ReflectResources(shader_resources.uniform_buffers);
209 uniform_buffers_json.has_value()) {
210 for (
auto uniform_buffer : uniform_buffers_json.value()) {
211 uniform_buffer[
"descriptor_type"] =
"DescriptorType::kUniformBuffer";
212 buffers.emplace_back(std::move(uniform_buffer));
217 if (
auto storage_buffers_json =
218 ReflectResources(shader_resources.storage_buffers);
219 storage_buffers_json.has_value()) {
220 for (
auto uniform_buffer : storage_buffers_json.value()) {
221 uniform_buffer[
"descriptor_type"] =
"DescriptorType::kStorageBuffer";
222 buffers.emplace_back(std::move(uniform_buffer));
230 auto& stage_inputs = root[
"stage_inputs"] = nlohmann::json::array_t{};
231 if (
auto stage_inputs_json = ReflectResources(
232 shader_resources.stage_inputs,
233 execution_model == spv::ExecutionModelVertex);
234 stage_inputs_json.has_value()) {
235 stage_inputs = std::move(stage_inputs_json.value());
242 auto combined_sampled_images =
243 ReflectResources(shader_resources.sampled_images);
244 auto images = ReflectResources(shader_resources.separate_images);
245 auto samplers = ReflectResources(shader_resources.separate_samplers);
246 if (!combined_sampled_images.has_value() || !images.has_value() ||
247 !samplers.has_value()) {
250 auto& sampled_images = root[
"sampled_images"] = nlohmann::json::array_t{};
251 for (
auto value : combined_sampled_images.value()) {
252 value[
"descriptor_type"] =
"DescriptorType::kSampledImage";
253 sampled_images.emplace_back(std::move(value));
255 for (
auto value : images.value()) {
256 value[
"descriptor_type"] =
"DescriptorType::kImage";
257 sampled_images.emplace_back(std::move(value));
259 for (
auto value : samplers.value()) {
260 value[
"descriptor_type"] =
"DescriptorType::kSampledSampler";
261 sampled_images.emplace_back(std::move(value));
265 if (
auto stage_outputs = ReflectResources(shader_resources.stage_outputs);
266 stage_outputs.has_value()) {
267 root[
"stage_outputs"] = std::move(stage_outputs.value());
273 auto& struct_definitions = root[
"struct_definitions"] =
274 nlohmann::json::array_t{};
275 if (entrypoints.front().execution_model ==
276 spv::ExecutionModel::ExecutionModelVertex &&
277 !shader_resources.stage_inputs.empty()) {
279 ReflectPerVertexStructDefinition(shader_resources.stage_inputs);
281 struct_definitions.emplace_back(EmitStructDefinition(struc.value()));
289 std::set<spirv_cross::ID> known_structs;
290 ir_->for_each_typed_id<spirv_cross::SPIRType>(
291 [&](uint32_t,
const spirv_cross::SPIRType& type) {
292 if (known_structs.find(type.self) != known_structs.end()) {
297 known_structs.insert(type.self);
298 if (
auto struc = ReflectStructDefinition(type.self);
300 struct_definitions.emplace_back(
301 EmitStructDefinition(struc.value()));
306 root[
"bind_prototypes"] =
307 EmitBindPrototypes(shader_resources, execution_model);
312 std::shared_ptr<fml::Mapping> Reflector::GenerateReflectionHeader()
const {
316 std::shared_ptr<fml::Mapping> Reflector::GenerateReflectionCC()
const {
322 switch (target_platform) {
342 std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
345 if (!backend.has_value()) {
349 const auto& entrypoints = compiler_->get_entry_points_and_stages();
350 if (entrypoints.size() != 1u) {
354 auto data = std::make_unique<RuntimeStageData::Shader>();
356 data->stage = entrypoints.front().execution_model;
357 data->shader = shader_data_;
358 data->backend = backend.value();
361 std::vector<spirv_cross::ID> uniforms =
364 for (
auto& sorted_id : uniforms) {
365 auto var = ir_->ids[sorted_id].get<spirv_cross::SPIRVariable>();
366 const auto spir_type = compiler_->get_type(var.basetype);
367 UniformDescription uniform_description;
368 uniform_description.name = compiler_->get_name(var.self);
369 uniform_description.location = compiler_->get_decoration(
370 var.self, spv::Decoration::DecorationLocation);
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);
376 data->uniforms.emplace_back(std::move(uniform_description));
380 if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
381 const auto inputs = compiler_->get_shader_resources().stage_inputs;
382 auto input_offsets = ComputeOffsets(inputs);
383 for (
const auto& input : inputs) {
384 auto location = compiler_->get_decoration(
385 input.id, spv::Decoration::DecorationLocation);
386 std::optional<size_t> offset = input_offsets[location];
388 const auto type = compiler_->get_type(input.type_id);
390 InputDescription input_description;
391 input_description.name = input.name;
392 input_description.location = compiler_->get_decoration(
393 input.id, spv::Decoration::DecorationLocation);
394 input_description.set = compiler_->get_decoration(
395 input.id, spv::Decoration::DecorationDescriptorSet);
396 input_description.binding = compiler_->get_decoration(
397 input.id, spv::Decoration::DecorationBinding);
398 input_description.type = type.basetype;
399 input_description.bit_width = type.width;
400 input_description.vec_size = type.vecsize;
401 input_description.columns = type.columns;
402 input_description.offset = offset.value_or(0u);
403 data->inputs.emplace_back(std::move(input_description));
410 std::optional<uint32_t> Reflector::GetArrayElements(
411 const spirv_cross::SPIRType& type)
const {
412 if (type.array.empty()) {
415 FML_CHECK(type.array.size() == 1)
416 <<
"Multi-dimensional arrays are not supported.";
417 FML_CHECK(type.array_size_literal.front())
418 <<
"Must use a literal for array sizes.";
419 return type.array.front();
425 return "Metal Shading Language";
427 return "OpenGL Shading Language";
429 return "SkSL Shading Language";
434 std::shared_ptr<fml::Mapping> Reflector::InflateTemplate(
435 std::string_view tmpl)
const {
436 inja::Environment env;
437 env.set_trim_blocks(
true);
438 env.set_lstrip_blocks(
true);
440 env.add_callback(
"camel_case", 1u, [](inja::Arguments& args) {
441 return ToCamelCase(args.at(0u)->get<std::string>());
444 env.add_callback(
"to_shader_stage", 1u, [](inja::Arguments& args) {
448 env.add_callback(
"get_generator_name", 0u,
449 [type = compiler_.
GetType()](inja::Arguments& args) {
453 auto inflated_template =
454 std::make_shared<std::string>(env.render(tmpl, *template_arguments_));
456 return std::make_shared<fml::NonOwnedMapping>(
457 reinterpret_cast<const uint8_t*
>(inflated_template->data()),
458 inflated_template->size(), [inflated_template](
auto,
auto) {});
461 std::vector<size_t> Reflector::ComputeOffsets(
462 const spirv_cross::SmallVector<spirv_cross::Resource>& resources)
const {
463 std::vector<size_t> offsets(resources.size(), 0);
464 if (resources.size() == 0) {
467 for (
const auto& resource : resources) {
468 const auto type = compiler_->get_type(resource.type_id);
469 auto location = compiler_->get_decoration(
470 resource.id, spv::Decoration::DecorationLocation);
472 if (location >= resources.size() || location < 0) {
475 offsets[location] = (type.width * type.vecsize) / 8;
477 for (
size_t i = 1; i < resources.size(); i++) {
478 offsets[i] += offsets[i - 1];
480 for (
size_t i = resources.size() - 1; i > 0; i--) {
481 offsets[i] = offsets[i - 1];
488 std::optional<nlohmann::json::object_t> Reflector::ReflectResource(
489 const spirv_cross::Resource& resource,
490 std::optional<size_t> offset)
const {
491 nlohmann::json::object_t result;
493 result[
"name"] = resource.name;
494 result[
"descriptor_set"] = compiler_->get_decoration(
495 resource.id, spv::Decoration::DecorationDescriptorSet);
496 result[
"binding"] = compiler_->get_decoration(
497 resource.id, spv::Decoration::DecorationBinding);
498 result[
"set"] = compiler_->get_decoration(
499 resource.id, spv::Decoration::DecorationDescriptorSet);
500 result[
"location"] = compiler_->get_decoration(
501 resource.id, spv::Decoration::DecorationLocation);
503 compiler_->get_decoration(resource.id, spv::Decoration::DecorationIndex);
508 auto type = ReflectType(resource.type_id);
509 if (!type.has_value()) {
512 result[
"type"] = std::move(type.value());
513 result[
"offset"] = offset.value_or(0u);
517 std::optional<nlohmann::json::object_t> Reflector::ReflectType(
518 const spirv_cross::TypeID& type_id)
const {
519 nlohmann::json::object_t result;
521 const auto type = compiler_->get_type(type_id);
524 result[
"bit_width"] = type.width;
525 result[
"vec_size"] = type.vecsize;
526 result[
"columns"] = type.columns;
527 auto& members = result[
"members"] = nlohmann::json::array_t{};
528 if (type.basetype == spirv_cross::SPIRType::BaseType::Struct) {
529 for (
const auto& struct_member : ReadStructMembers(type_id)) {
530 auto member = nlohmann::json::object_t{};
531 member[
"name"] = struct_member.name;
532 member[
"type"] = struct_member.type;
533 member[
"base_type"] = struct_member.base_type;
534 member[
"offset"] = struct_member.offset;
535 member[
"size"] = struct_member.size;
536 member[
"byte_length"] = struct_member.byte_length;
537 if (struct_member.array_elements.has_value()) {
538 member[
"array_elements"] = struct_member.array_elements.value();
540 member[
"array_elements"] =
"std::nullopt";
542 members.emplace_back(std::move(member));
549 std::optional<nlohmann::json::array_t> Reflector::ReflectResources(
550 const spirv_cross::SmallVector<spirv_cross::Resource>& resources,
551 bool compute_offsets)
const {
552 nlohmann::json::array_t result;
553 result.reserve(resources.size());
554 std::vector<size_t> offsets;
555 if (compute_offsets) {
556 offsets = ComputeOffsets(resources);
558 for (
const auto& resource : resources) {
559 std::optional<size_t> maybe_offset = std::nullopt;
560 if (compute_offsets) {
561 auto location = compiler_->get_decoration(
562 resource.id, spv::Decoration::DecorationLocation);
563 maybe_offset = offsets[location];
565 if (
auto reflected = ReflectResource(resource, maybe_offset);
566 reflected.has_value()) {
567 result.emplace_back(std::move(reflected.value()));
576 std::stringstream stream;
577 stream <<
"Padding<" << size <<
">";
587 spirv_cross::SPIRType::BaseType type) {
589 case spirv_cross::SPIRType::BaseType::Boolean:
592 .byte_size =
sizeof(bool),
594 case spirv_cross::SPIRType::BaseType::Float:
597 .byte_size =
sizeof(
Scalar),
599 case spirv_cross::SPIRType::BaseType::Half:
602 .byte_size =
sizeof(
Half),
604 case spirv_cross::SPIRType::BaseType::UInt:
607 .byte_size =
sizeof(uint32_t),
609 case spirv_cross::SPIRType::BaseType::Int:
612 .byte_size =
sizeof(int32_t),
634 auto struct_size = 0u;
635 for (
const auto& member : members) {
636 struct_size += member.byte_length;
641 std::vector<StructMember> Reflector::ReadStructMembers(
642 const spirv_cross::TypeID& type_id)
const {
643 const auto& struct_type = compiler_->get_type(type_id);
644 FML_CHECK(struct_type.basetype == spirv_cross::SPIRType::BaseType::Struct);
646 std::vector<StructMember> result;
648 size_t current_byte_offset = 0;
649 size_t max_member_alignment = 0;
651 for (
size_t i = 0; i < struct_type.member_types.size(); i++) {
652 const auto& member = compiler_->get_type(struct_type.member_types[i]);
653 const auto struct_member_offset =
654 compiler_->type_struct_member_offset(struct_type, i);
655 auto array_elements = GetArrayElements(member);
657 if (struct_member_offset > current_byte_offset) {
658 const auto alignment_pad = struct_member_offset - current_byte_offset;
659 result.emplace_back(StructMember{
663 GetMemberNameAtIndex(struct_type, i).c_str()),
670 current_byte_offset += alignment_pad;
673 max_member_alignment =
674 std::max<size_t>(max_member_alignment,
675 (member.width / 8) * member.columns * member.vecsize);
677 FML_CHECK(current_byte_offset == struct_member_offset);
680 if (member.basetype == spirv_cross::SPIRType::BaseType::Struct) {
683 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
687 uint32_t element_padding = stride - size;
688 result.emplace_back(StructMember{
689 compiler_->get_name(member.self),
691 GetMemberNameAtIndex(struct_type, i),
692 struct_member_offset,
694 stride * array_elements.value_or(1),
698 current_byte_offset += stride * array_elements.value_or(1);
704 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
705 member.width ==
sizeof(
Scalar) * 8 &&
706 member.columns == 4 &&
709 uint32_t stride = GetArrayStride<sizeof(Matrix)>(struct_type, member, i);
710 uint32_t element_padding = stride -
sizeof(Matrix);
711 result.emplace_back(StructMember{
714 GetMemberNameAtIndex(struct_type, i),
715 struct_member_offset,
717 stride * array_elements.value_or(1),
721 current_byte_offset += stride * array_elements.value_or(1);
726 if (member.basetype == spirv_cross::SPIRType::BaseType::UInt &&
727 member.width ==
sizeof(uint32_t) * 8 &&
728 member.columns == 1 &&
732 GetArrayStride<sizeof(UintPoint32)>(struct_type, member, i);
733 uint32_t element_padding = stride -
sizeof(
UintPoint32);
734 result.emplace_back(StructMember{
737 GetMemberNameAtIndex(struct_type, i),
738 struct_member_offset,
740 stride * array_elements.value_or(1),
744 current_byte_offset += stride * array_elements.value_or(1);
749 if (member.basetype == spirv_cross::SPIRType::BaseType::Int &&
750 member.width ==
sizeof(int32_t) * 8 &&
751 member.columns == 1 &&
755 GetArrayStride<sizeof(IPoint32)>(struct_type, member, i);
756 uint32_t element_padding = stride -
sizeof(
IPoint32);
757 result.emplace_back(StructMember{
760 GetMemberNameAtIndex(struct_type, i),
761 struct_member_offset,
763 stride * array_elements.value_or(1),
767 current_byte_offset += stride * array_elements.value_or(1);
772 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
773 member.width ==
sizeof(
float) * 8 &&
774 member.columns == 1 &&
777 uint32_t stride = GetArrayStride<sizeof(Point)>(struct_type, member, i);
778 uint32_t element_padding = stride -
sizeof(
Point);
779 result.emplace_back(StructMember{
782 GetMemberNameAtIndex(struct_type, i),
783 struct_member_offset,
785 stride * array_elements.value_or(1),
789 current_byte_offset += stride * array_elements.value_or(1);
794 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
795 member.width ==
sizeof(
float) * 8 &&
796 member.columns == 1 &&
799 uint32_t stride = GetArrayStride<sizeof(Vector3)>(struct_type, member, i);
800 uint32_t element_padding = stride -
sizeof(Vector3);
801 result.emplace_back(StructMember{
804 GetMemberNameAtIndex(struct_type, i),
805 struct_member_offset,
807 stride * array_elements.value_or(1),
811 current_byte_offset += stride * array_elements.value_or(1);
816 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
817 member.width ==
sizeof(
float) * 8 &&
818 member.columns == 1 &&
821 uint32_t stride = GetArrayStride<sizeof(Vector4)>(struct_type, member, i);
822 uint32_t element_padding = stride -
sizeof(Vector4);
823 result.emplace_back(StructMember{
826 GetMemberNameAtIndex(struct_type, i),
827 struct_member_offset,
829 stride * array_elements.value_or(1),
833 current_byte_offset += stride * array_elements.value_or(1);
838 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
839 member.width ==
sizeof(Half) * 8 &&
840 member.columns == 1 &&
844 GetArrayStride<sizeof(HalfVector2)>(struct_type, member, i);
845 uint32_t element_padding = stride -
sizeof(HalfVector2);
846 result.emplace_back(StructMember{
849 GetMemberNameAtIndex(struct_type, i),
850 struct_member_offset,
852 stride * array_elements.value_or(1),
856 current_byte_offset += stride * array_elements.value_or(1);
861 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
862 member.width ==
sizeof(Half) * 8 &&
863 member.columns == 1 &&
867 GetArrayStride<sizeof(HalfVector3)>(struct_type, member, i);
868 uint32_t element_padding = stride -
sizeof(HalfVector3);
869 result.emplace_back(StructMember{
872 GetMemberNameAtIndex(struct_type, i),
873 struct_member_offset,
875 stride * array_elements.value_or(1),
879 current_byte_offset += stride * array_elements.value_or(1);
884 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
885 member.width ==
sizeof(Half) * 8 &&
886 member.columns == 1 &&
890 GetArrayStride<sizeof(HalfVector4)>(struct_type, member, i);
891 uint32_t element_padding = stride -
sizeof(HalfVector4);
892 result.emplace_back(StructMember{
895 GetMemberNameAtIndex(struct_type, i),
896 struct_member_offset,
898 stride * array_elements.value_or(1),
902 current_byte_offset += stride * array_elements.value_or(1);
909 if (maybe_known_type.has_value() &&
910 member.columns == 1 &&
913 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
915 stride = maybe_known_type.value().byte_size;
917 uint32_t element_padding = stride - maybe_known_type.value().byte_size;
919 result.emplace_back(StructMember{
920 maybe_known_type.value().name,
922 GetMemberNameAtIndex(struct_type, i),
923 struct_member_offset,
924 maybe_known_type.value().byte_size,
925 stride * array_elements.value_or(1),
929 current_byte_offset += stride * array_elements.value_or(1);
937 const size_t size = (member.width * member.columns * member.vecsize) / 8u;
938 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
942 auto element_padding = stride - size;
943 result.emplace_back(StructMember{
946 GetMemberNameAtIndex(struct_type, i),
947 struct_member_offset,
949 stride * array_elements.value_or(1),
953 current_byte_offset += stride * array_elements.value_or(1);
958 if (max_member_alignment > 0u) {
959 const auto struct_length = current_byte_offset;
961 const auto excess = struct_length % max_member_alignment;
963 const auto padding = max_member_alignment - excess;
964 result.emplace_back(StructMember{
967 spirv_cross::SPIRType::BaseType::Void),
982 std::optional<Reflector::StructDefinition> Reflector::ReflectStructDefinition(
983 const spirv_cross::TypeID& type_id)
const {
984 const auto& type = compiler_->get_type(type_id);
985 if (type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
989 const auto struct_name = compiler_->get_name(type_id);
990 if (struct_name.find(
"_RESERVED_IDENTIFIER_") != std::string::npos) {
994 auto struct_members = ReadStructMembers(type_id);
997 StructDefinition struc;
998 struc.name = struct_name;
999 struc.byte_length = reflected_struct_size;
1000 struc.members = std::move(struct_members);
1004 nlohmann::json::object_t Reflector::EmitStructDefinition(
1005 std::optional<Reflector::StructDefinition> struc)
const {
1006 nlohmann::json::object_t result;
1007 result[
"name"] = struc->name;
1008 result[
"byte_length"] = struc->byte_length;
1009 auto& members = result[
"members"] = nlohmann::json::array_t{};
1010 for (
const auto& struct_member : struc->members) {
1011 auto& member = members.emplace_back(nlohmann::json::object_t{});
1012 member[
"name"] = struct_member.name;
1013 member[
"type"] = struct_member.type;
1014 member[
"base_type"] = struct_member.base_type;
1015 member[
"offset"] = struct_member.offset;
1016 member[
"byte_length"] = struct_member.byte_length;
1017 if (struct_member.array_elements.has_value()) {
1018 member[
"array_elements"] = struct_member.array_elements.value();
1020 member[
"array_elements"] =
"std::nullopt";
1022 member[
"element_padding"] = struct_member.element_padding;
1035 const spirv_cross::Compiler& compiler,
1036 const spirv_cross::Resource* resource) {
1039 const auto type = compiler.get_type(resource->type_id);
1041 const auto total_size = type.columns * type.vecsize * type.width / 8u;
1044 if (type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1045 type.columns == 1u && type.vecsize == 2u &&
1046 type.width ==
sizeof(
float) * 8u) {
1048 }
else if (type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1049 type.columns == 1u && type.vecsize == 4u &&
1050 type.width ==
sizeof(
float) * 8u) {
1052 }
else if (type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1053 type.columns == 1u && type.vecsize == 3u &&
1054 type.width ==
sizeof(
float) * 8u) {
1056 }
else if (type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1057 type.columns == 1u && type.vecsize == 1u &&
1058 type.width ==
sizeof(
float) * 8u) {
1060 }
else if (type.basetype == spirv_cross::SPIRType::BaseType::Int &&
1061 type.columns == 1u && type.vecsize == 1u &&
1062 type.width ==
sizeof(int32_t) * 8u) {
1072 std::optional<Reflector::StructDefinition>
1073 Reflector::ReflectPerVertexStructDefinition(
1074 const spirv_cross::SmallVector<spirv_cross::Resource>& stage_inputs)
const {
1077 if (stage_inputs.empty()) {
1078 return std::nullopt;
1082 std::set<uint32_t> locations;
1083 for (
const auto& input : stage_inputs) {
1084 auto location = compiler_->get_decoration(
1085 input.id, spv::Decoration::DecorationLocation);
1086 if (locations.count(location) != 0) {
1088 return std::nullopt;
1090 locations.insert(location);
1093 for (
size_t i = 0; i < locations.size(); i++) {
1094 if (locations.count(i) != 1) {
1099 return std::nullopt;
1103 auto input_for_location =
1104 [&](uint32_t queried_location) ->
const spirv_cross::Resource* {
1105 for (
const auto& input : stage_inputs) {
1106 auto location = compiler_->get_decoration(
1107 input.id, spv::Decoration::DecorationLocation);
1108 if (location == queried_location) {
1117 StructDefinition struc;
1118 struc.name =
"PerVertexData";
1119 struc.byte_length = 0u;
1120 for (
size_t i = 0; i < locations.size(); i++) {
1121 auto resource = input_for_location(i);
1122 if (resource ==
nullptr) {
1123 return std::nullopt;
1125 const auto vertex_type =
1128 auto member = StructMember{
1129 vertex_type.type_name,
1130 vertex_type.base_type_name,
1131 vertex_type.variable_name,
1133 vertex_type.byte_length,
1134 vertex_type.byte_length,
1138 struc.byte_length += vertex_type.byte_length;
1139 struc.members.emplace_back(std::move(member));
1144 std::optional<std::string> Reflector::GetMemberNameAtIndexIfExists(
1145 const spirv_cross::SPIRType& parent_type,
1146 size_t index)
const {
1147 if (parent_type.type_alias != 0) {
1148 return GetMemberNameAtIndexIfExists(
1149 compiler_->get_type(parent_type.type_alias), index);
1152 if (
auto found = ir_->meta.find(parent_type.self); found != ir_->meta.end()) {
1153 const auto& members = found->second.members;
1154 if (index < members.size() && !members[index].alias.empty()) {
1155 return members[index].alias;
1158 return std::nullopt;
1161 std::string Reflector::GetMemberNameAtIndex(
1162 const spirv_cross::SPIRType& parent_type,
1164 std::string suffix)
const {
1165 if (
auto name = GetMemberNameAtIndexIfExists(parent_type, index);
1167 return name.value();
1169 static std::atomic_size_t sUnnamedMembersID;
1170 std::stringstream stream;
1171 stream <<
"unnamed_" << sUnnamedMembersID++ << suffix;
1172 return stream.str();
1175 std::vector<Reflector::BindPrototype> Reflector::ReflectBindPrototypes(
1176 const spirv_cross::ShaderResources& resources,
1177 spv::ExecutionModel execution_model)
const {
1178 std::vector<BindPrototype> prototypes;
1179 for (
const auto& uniform_buffer : resources.uniform_buffers) {
1180 auto& proto = prototypes.emplace_back(BindPrototype{});
1181 proto.return_type =
"bool";
1184 std::stringstream stream;
1185 stream <<
"Bind uniform buffer for resource named " << uniform_buffer.name
1187 proto.docstring = stream.str();
1189 proto.args.push_back(BindPrototypeArgument{
1190 .type_name =
"ResourceBinder&",
1191 .argument_name =
"command",
1193 proto.args.push_back(BindPrototypeArgument{
1194 .type_name =
"BufferView",
1195 .argument_name =
"view",
1198 for (
const auto& storage_buffer : resources.storage_buffers) {
1199 auto& proto = prototypes.emplace_back(BindPrototype{});
1200 proto.return_type =
"bool";
1203 std::stringstream stream;
1204 stream <<
"Bind storage buffer for resource named " << storage_buffer.name
1206 proto.docstring = stream.str();
1208 proto.args.push_back(BindPrototypeArgument{
1209 .type_name =
"ResourceBinder&",
1210 .argument_name =
"command",
1212 proto.args.push_back(BindPrototypeArgument{
1213 .type_name =
"BufferView",
1214 .argument_name =
"view",
1217 for (
const auto& sampled_image : resources.sampled_images) {
1218 auto& proto = prototypes.emplace_back(BindPrototype{});
1219 proto.return_type =
"bool";
1222 std::stringstream stream;
1223 stream <<
"Bind combined image sampler for resource named "
1224 << sampled_image.name <<
".";
1225 proto.docstring = stream.str();
1227 proto.args.push_back(BindPrototypeArgument{
1228 .type_name =
"ResourceBinder&",
1229 .argument_name =
"command",
1231 proto.args.push_back(BindPrototypeArgument{
1232 .type_name =
"std::shared_ptr<const Texture>",
1233 .argument_name =
"texture",
1235 proto.args.push_back(BindPrototypeArgument{
1236 .type_name =
"std::shared_ptr<const Sampler>",
1237 .argument_name =
"sampler",
1240 for (
const auto& separate_image : resources.separate_images) {
1241 auto& proto = prototypes.emplace_back(BindPrototype{});
1242 proto.return_type =
"bool";
1245 std::stringstream stream;
1246 stream <<
"Bind separate image for resource named " << separate_image.name
1248 proto.docstring = stream.str();
1250 proto.args.push_back(BindPrototypeArgument{
1251 .type_name =
"Command&",
1252 .argument_name =
"command",
1254 proto.args.push_back(BindPrototypeArgument{
1255 .type_name =
"std::shared_ptr<const Texture>",
1256 .argument_name =
"texture",
1259 for (
const auto& separate_sampler : resources.separate_samplers) {
1260 auto& proto = prototypes.emplace_back(BindPrototype{});
1261 proto.return_type =
"bool";
1264 std::stringstream stream;
1265 stream <<
"Bind separate sampler for resource named "
1266 << separate_sampler.name <<
".";
1267 proto.docstring = stream.str();
1269 proto.args.push_back(BindPrototypeArgument{
1270 .type_name =
"Command&",
1271 .argument_name =
"command",
1273 proto.args.push_back(BindPrototypeArgument{
1274 .type_name =
"std::shared_ptr<const Sampler>",
1275 .argument_name =
"sampler",
1281 nlohmann::json::array_t Reflector::EmitBindPrototypes(
1282 const spirv_cross::ShaderResources& resources,
1283 spv::ExecutionModel execution_model)
const {
1284 const auto prototypes = ReflectBindPrototypes(resources, execution_model);
1285 nlohmann::json::array_t result;
1286 for (
const auto& res : prototypes) {
1287 auto& item = result.emplace_back(nlohmann::json::object_t{});
1288 item[
"return_type"] = res.return_type;
1289 item[
"name"] = res.name;
1290 item[
"docstring"] = res.docstring;
1291 auto& args = item[
"args"] = nlohmann::json::array_t{};
1292 for (
const auto& arg : res.args) {
1293 auto& json_arg = args.emplace_back(nlohmann::json::object_t{});
1294 json_arg[
"type_name"] = arg.type_name;
1295 json_arg[
"argument_name"] = arg.argument_name;