14 #include "flutter/fml/logging.h"
15 #include "fml/backtrace.h"
28 #include "spirv_common.hpp"
35 case spv::ExecutionModel::ExecutionModelVertex:
37 case spv::ExecutionModel::ExecutionModelFragment:
39 case spv::ExecutionModel::ExecutionModelGLCompute:
47 if (str ==
"vertex") {
48 return "ShaderStage::kVertex";
51 if (str ==
"fragment") {
52 return "ShaderStage::kFragment";
55 if (str ==
"compute") {
56 return "ShaderStage::kCompute";
59 return "ShaderStage::kUnknown";
63 const std::shared_ptr<const spirv_cross::ParsedIR>& ir,
64 const std::shared_ptr<fml::Mapping>& shader_data,
66 : options_(
std::move(options)),
68 shader_data_(shader_data),
70 if (!ir_ || !compiler_) {
74 if (
auto template_arguments = GenerateTemplateArguments();
75 template_arguments.has_value()) {
77 std::make_unique<nlohmann::json>(std::move(template_arguments.value()));
82 reflection_header_ = GenerateReflectionHeader();
83 if (!reflection_header_) {
87 reflection_cc_ = GenerateReflectionCC();
88 if (!reflection_cc_) {
92 runtime_stage_shader_ = GenerateRuntimeStageData();
94 shader_bundle_data_ = GenerateShaderBundleData();
95 if (!shader_bundle_data_) {
114 std::make_shared<std::string>(template_arguments_->dump(2u));
116 return std::make_shared<fml::NonOwnedMapping>(
117 reinterpret_cast<const uint8_t*
>(json_string->data()),
118 json_string->size(), [json_string](
auto,
auto) {});
122 return reflection_header_;
126 return reflection_cc_;
131 return runtime_stage_shader_;
135 return shader_bundle_data_;
138 std::optional<nlohmann::json> Reflector::GenerateTemplateArguments()
const {
141 const auto& entrypoints = compiler_->get_entry_points_and_stages();
142 if (entrypoints.size() != 1) {
143 VALIDATION_LOG <<
"Incorrect number of entrypoints in the shader. Found "
144 << entrypoints.size() <<
" but expected 1.";
148 auto execution_model = entrypoints.front().execution_model;
156 const auto shader_resources = compiler_->get_shader_resources();
160 auto& subpass_inputs = root[
"subpass_inputs"] = nlohmann::json::array_t{};
161 if (
auto subpass_inputs_json =
162 ReflectResources(shader_resources.subpass_inputs);
163 subpass_inputs_json.has_value()) {
164 for (
auto subpass_input : subpass_inputs_json.value()) {
165 subpass_input[
"descriptor_type"] =
"DescriptorType::kInputAttachment";
166 subpass_inputs.emplace_back(std::move(subpass_input));
175 auto& buffers = root[
"buffers"] = nlohmann::json::array_t{};
176 if (
auto uniform_buffers_json =
177 ReflectResources(shader_resources.uniform_buffers);
178 uniform_buffers_json.has_value()) {
179 for (
auto uniform_buffer : uniform_buffers_json.value()) {
180 uniform_buffer[
"descriptor_type"] =
"DescriptorType::kUniformBuffer";
181 buffers.emplace_back(std::move(uniform_buffer));
186 if (
auto storage_buffers_json =
187 ReflectResources(shader_resources.storage_buffers);
188 storage_buffers_json.has_value()) {
189 for (
auto uniform_buffer : storage_buffers_json.value()) {
190 uniform_buffer[
"descriptor_type"] =
"DescriptorType::kStorageBuffer";
191 buffers.emplace_back(std::move(uniform_buffer));
199 auto& stage_inputs = root[
"stage_inputs"] = nlohmann::json::array_t{};
200 if (
auto stage_inputs_json = ReflectResources(
201 shader_resources.stage_inputs,
202 execution_model == spv::ExecutionModelVertex);
203 stage_inputs_json.has_value()) {
204 stage_inputs = std::move(stage_inputs_json.value());
211 auto combined_sampled_images =
212 ReflectResources(shader_resources.sampled_images);
213 auto images = ReflectResources(shader_resources.separate_images);
214 auto samplers = ReflectResources(shader_resources.separate_samplers);
215 if (!combined_sampled_images.has_value() || !images.has_value() ||
216 !samplers.has_value()) {
219 auto& sampled_images = root[
"sampled_images"] = nlohmann::json::array_t{};
220 for (
auto value : combined_sampled_images.value()) {
221 value[
"descriptor_type"] =
"DescriptorType::kSampledImage";
222 sampled_images.emplace_back(std::move(value));
224 for (
auto value : images.value()) {
225 value[
"descriptor_type"] =
"DescriptorType::kImage";
226 sampled_images.emplace_back(std::move(value));
228 for (
auto value : samplers.value()) {
229 value[
"descriptor_type"] =
"DescriptorType::kSampledSampler";
230 sampled_images.emplace_back(std::move(value));
234 if (
auto stage_outputs = ReflectResources(shader_resources.stage_outputs);
235 stage_outputs.has_value()) {
236 root[
"stage_outputs"] = std::move(stage_outputs.value());
242 auto& struct_definitions = root[
"struct_definitions"] =
243 nlohmann::json::array_t{};
244 if (entrypoints.front().execution_model ==
245 spv::ExecutionModel::ExecutionModelVertex &&
246 !shader_resources.stage_inputs.empty()) {
248 ReflectPerVertexStructDefinition(shader_resources.stage_inputs);
250 struct_definitions.emplace_back(EmitStructDefinition(struc.value()));
258 std::set<spirv_cross::ID> known_structs;
259 ir_->for_each_typed_id<spirv_cross::SPIRType>(
260 [&](uint32_t,
const spirv_cross::SPIRType& type) {
261 if (type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
267 for (
size_t i = 0; i < type.member_types.size(); i++) {
268 if (!compiler_->has_member_decoration(type.self, i,
269 spv::DecorationOffset)) {
273 if (known_structs.find(type.self) != known_structs.end()) {
278 known_structs.insert(type.self);
279 if (
auto struc = ReflectStructDefinition(type.self);
281 struct_definitions.emplace_back(
282 EmitStructDefinition(struc.value()));
287 root[
"bind_prototypes"] =
288 EmitBindPrototypes(shader_resources, execution_model);
293 std::shared_ptr<fml::Mapping> Reflector::GenerateReflectionHeader()
const {
297 std::shared_ptr<fml::Mapping> Reflector::GenerateReflectionCC()
const {
303 switch (target_platform) {
323 std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
326 if (!backend.has_value()) {
330 const auto& entrypoints = compiler_->get_entry_points_and_stages();
331 if (entrypoints.size() != 1u) {
335 auto data = std::make_unique<RuntimeStageData::Shader>();
337 data->stage = entrypoints.front().execution_model;
338 data->shader = shader_data_;
339 data->backend = backend.value();
342 std::vector<spirv_cross::ID> uniforms =
344 for (
auto& sorted_id : uniforms) {
345 auto var = ir_->ids[sorted_id].get<spirv_cross::SPIRVariable>();
346 const auto spir_type = compiler_->get_type(var.basetype);
347 UniformDescription uniform_description;
348 uniform_description.name = compiler_->get_name(var.self);
349 uniform_description.location = compiler_->get_decoration(
350 var.self, spv::Decoration::DecorationLocation);
351 uniform_description.type = spir_type.basetype;
352 uniform_description.rows = spir_type.vecsize;
353 uniform_description.columns = spir_type.columns;
354 uniform_description.bit_width = spir_type.width;
355 uniform_description.array_elements = GetArrayElements(spir_type);
357 spir_type.basetype ==
358 spirv_cross::SPIRType::BaseType::SampledImage)
359 <<
"Vulkan runtime effect had unexpected uniforms outside of the "
360 "uniform buffer object.";
361 data->uniforms.emplace_back(std::move(uniform_description));
364 const auto ubos = compiler_->get_shader_resources().uniform_buffers;
371 "for Vulkan runtime stage backend.";
375 const auto& ubo = ubos[0];
377 auto members = ReadStructMembers(ubo.type_id);
378 std::vector<uint8_t> struct_layout;
379 size_t float_count = 0;
381 for (
size_t i = 0; i < members.size(); i += 1) {
382 const auto& member = members[i];
383 std::vector<int> bytes;
384 switch (member.underlying_type) {
386 size_t padding_count =
387 (member.size +
sizeof(float) - 1) /
sizeof(float);
388 while (padding_count > 0) {
389 struct_layout.push_back(0);
395 size_t member_float_count = member.byte_length /
sizeof(float);
396 float_count += member_float_count;
397 while (member_float_count > 0) {
398 struct_layout.push_back(1);
399 member_float_count--;
404 VALIDATION_LOG <<
"Non-floating-type struct member " << member.name
405 <<
" is not supported.";
409 data->uniforms.emplace_back(UniformDescription{
413 .type = spirv_cross::SPIRType::Struct,
414 .struct_layout = std::move(struct_layout),
415 .struct_float_count = float_count,
420 if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
421 const auto inputs = compiler_->get_shader_resources().stage_inputs;
422 auto input_offsets = ComputeOffsets(inputs);
423 for (
const auto& input : inputs) {
424 std::optional<size_t>
offset = GetOffset(input.id, input_offsets);
426 const auto type = compiler_->get_type(input.type_id);
428 InputDescription input_description;
429 input_description.name = input.name;
430 input_description.location = compiler_->get_decoration(
431 input.id, spv::Decoration::DecorationLocation);
432 input_description.set = compiler_->get_decoration(
433 input.id, spv::Decoration::DecorationDescriptorSet);
434 input_description.binding = compiler_->get_decoration(
435 input.id, spv::Decoration::DecorationBinding);
436 input_description.type = type.basetype;
437 input_description.bit_width = type.width;
438 input_description.vec_size = type.vecsize;
439 input_description.columns = type.columns;
440 input_description.offset =
offset.value_or(0u);
441 data->inputs.emplace_back(std::move(input_description));
448 std::shared_ptr<ShaderBundleData> Reflector::GenerateShaderBundleData()
const {
449 const auto& entrypoints = compiler_->get_entry_points_and_stages();
450 if (entrypoints.size() != 1u) {
454 auto data = std::make_shared<ShaderBundleData>(
456 entrypoints.front().execution_model,
459 data->SetShaderData(shader_data_);
461 const auto uniforms = compiler_->get_shader_resources().uniform_buffers;
462 for (
const auto& uniform : uniforms) {
463 ShaderBundleData::ShaderUniformStruct uniform_struct;
464 uniform_struct.name = uniform.name;
467 uniform_struct.set = compiler_->get_decoration(
468 uniform.id, spv::Decoration::DecorationDescriptorSet);
469 uniform_struct.binding = compiler_->get_decoration(
470 uniform.id, spv::Decoration::DecorationBinding);
472 const auto type = compiler_->get_type(uniform.type_id);
473 if (type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
474 std::cerr <<
"Error: Uniform \"" << uniform.name
475 <<
"\" is not a struct. All Flutter GPU shader uniforms must "
481 size_t size_in_bytes = 0;
482 for (
const auto& struct_member : ReadStructMembers(uniform.type_id)) {
483 size_in_bytes += struct_member.byte_length;
487 ShaderBundleData::ShaderUniformStructField uniform_struct_field;
488 uniform_struct_field.name = struct_member.name;
489 uniform_struct_field.type = struct_member.base_type;
490 uniform_struct_field.offset_in_bytes = struct_member.offset;
491 uniform_struct_field.element_size_in_bytes = struct_member.size;
492 uniform_struct_field.total_size_in_bytes = struct_member.byte_length;
493 uniform_struct_field.array_elements = struct_member.array_elements;
494 uniform_struct.fields.push_back(uniform_struct_field);
496 uniform_struct.size_in_bytes = size_in_bytes;
498 data->AddUniformStruct(uniform_struct);
501 const auto sampled_images = compiler_->get_shader_resources().sampled_images;
502 for (
const auto& image : sampled_images) {
503 ShaderBundleData::ShaderUniformTexture uniform_texture;
504 uniform_texture.name = image.name;
507 uniform_texture.set = compiler_->get_decoration(
508 image.id, spv::Decoration::DecorationDescriptorSet);
509 uniform_texture.binding =
510 compiler_->get_decoration(image.id, spv::Decoration::DecorationBinding);
511 data->AddUniformTexture(uniform_texture);
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->AddInputDescription(std::move(input_description));
543 std::optional<uint32_t> Reflector::GetArrayElements(
544 const spirv_cross::SPIRType& type)
const {
545 if (type.array.empty()) {
548 FML_CHECK(type.array.size() == 1)
549 <<
"Multi-dimensional arrays are not supported.";
550 FML_CHECK(type.array_size_literal.front())
551 <<
"Must use a literal for array sizes.";
552 return type.array.front();
558 return "Metal Shading Language";
560 return "OpenGL Shading Language";
562 return "OpenGL Shading Language (Relaxed Vulkan Semantics)";
564 return "SkSL Shading Language";
569 std::shared_ptr<fml::Mapping> Reflector::InflateTemplate(
570 std::string_view tmpl)
const {
571 inja::Environment env;
572 env.set_trim_blocks(
true);
573 env.set_lstrip_blocks(
true);
575 env.add_callback(
"camel_case", 1u, [](inja::Arguments& args) {
576 return ToCamelCase(args.at(0u)->get<std::string>());
579 env.add_callback(
"to_shader_stage", 1u, [](inja::Arguments& args) {
583 env.add_callback(
"get_generator_name", 0u,
584 [type = compiler_.
GetType()](inja::Arguments& args) {
588 auto inflated_template =
589 std::make_shared<std::string>(env.render(tmpl, *template_arguments_));
591 return std::make_shared<fml::NonOwnedMapping>(
592 reinterpret_cast<const uint8_t*
>(inflated_template->data()),
593 inflated_template->size(), [inflated_template](
auto,
auto) {});
596 std::vector<size_t> Reflector::ComputeOffsets(
597 const spirv_cross::SmallVector<spirv_cross::Resource>& resources)
const {
598 std::vector<size_t> offsets(resources.size(), 0);
599 if (resources.size() == 0) {
602 for (
const auto& resource : resources) {
603 const auto type = compiler_->get_type(resource.type_id);
604 auto location = compiler_->get_decoration(
605 resource.id, spv::Decoration::DecorationLocation);
607 if (location >= resources.size() || location < 0) {
610 offsets[location] = (type.width * type.vecsize) / 8;
612 for (
size_t i = 1; i < resources.size(); i++) {
613 offsets[i] += offsets[i - 1];
615 for (
size_t i = resources.size() - 1; i > 0; i--) {
616 offsets[i] = offsets[i - 1];
623 std::optional<size_t> Reflector::GetOffset(
625 const std::vector<size_t>& offsets)
const {
627 compiler_->get_decoration(
id, spv::Decoration::DecorationLocation);
628 if (location >= offsets.size()) {
631 return offsets[location];
634 std::optional<nlohmann::json::object_t> Reflector::ReflectResource(
635 const spirv_cross::Resource& resource,
636 std::optional<size_t>
offset)
const {
637 nlohmann::json::object_t result;
639 result[
"name"] = resource.name;
640 result[
"descriptor_set"] = compiler_->get_decoration(
641 resource.id, spv::Decoration::DecorationDescriptorSet);
642 result[
"binding"] = compiler_->get_decoration(
643 resource.id, spv::Decoration::DecorationBinding);
644 result[
"set"] = compiler_->get_decoration(
645 resource.id, spv::Decoration::DecorationDescriptorSet);
646 result[
"location"] = compiler_->get_decoration(
647 resource.id, spv::Decoration::DecorationLocation);
649 compiler_->get_decoration(resource.id, spv::Decoration::DecorationIndex);
654 auto type = ReflectType(resource.type_id);
655 if (!type.has_value()) {
658 result[
"type"] = std::move(type.value());
659 result[
"offset"] =
offset.value_or(0u);
663 std::optional<nlohmann::json::object_t> Reflector::ReflectType(
664 const spirv_cross::TypeID& type_id)
const {
665 nlohmann::json::object_t result;
667 const auto type = compiler_->get_type(type_id);
670 result[
"bit_width"] = type.width;
671 result[
"vec_size"] = type.vecsize;
672 result[
"columns"] = type.columns;
673 auto& members = result[
"members"] = nlohmann::json::array_t{};
674 if (type.basetype == spirv_cross::SPIRType::BaseType::Struct) {
675 for (
const auto& struct_member : ReadStructMembers(type_id)) {
676 auto member = nlohmann::json::object_t{};
677 member[
"name"] = struct_member.name;
678 member[
"type"] = struct_member.type;
679 member[
"base_type"] =
681 member[
"offset"] = struct_member.offset;
682 member[
"size"] = struct_member.size;
683 member[
"byte_length"] = struct_member.byte_length;
684 if (struct_member.array_elements.has_value()) {
685 member[
"array_elements"] = struct_member.array_elements.value();
687 member[
"array_elements"] =
"std::nullopt";
689 members.emplace_back(std::move(member));
696 std::optional<nlohmann::json::array_t> Reflector::ReflectResources(
697 const spirv_cross::SmallVector<spirv_cross::Resource>& resources,
698 bool compute_offsets)
const {
699 nlohmann::json::array_t result;
700 result.reserve(resources.size());
701 std::vector<size_t> offsets;
702 if (compute_offsets) {
703 offsets = ComputeOffsets(resources);
705 for (
const auto& resource : resources) {
706 std::optional<size_t> maybe_offset = std::nullopt;
707 if (compute_offsets) {
708 maybe_offset = GetOffset(resource.id, offsets);
710 if (
auto reflected = ReflectResource(resource, maybe_offset);
711 reflected.has_value()) {
712 result.emplace_back(std::move(reflected.value()));
721 std::stringstream stream;
722 stream <<
"Padding<" << size <<
">";
732 spirv_cross::SPIRType::BaseType type) {
734 case spirv_cross::SPIRType::BaseType::Boolean:
737 .byte_size =
sizeof(bool),
739 case spirv_cross::SPIRType::BaseType::Float:
742 .byte_size =
sizeof(
Scalar),
744 case spirv_cross::SPIRType::BaseType::Half:
747 .byte_size =
sizeof(
Half),
749 case spirv_cross::SPIRType::BaseType::UInt:
752 .byte_size =
sizeof(uint32_t),
754 case spirv_cross::SPIRType::BaseType::Int:
757 .byte_size =
sizeof(int32_t),
779 auto struct_size = 0u;
780 for (
const auto& member : members) {
781 struct_size += member.byte_length;
786 std::vector<StructMember> Reflector::ReadStructMembers(
787 const spirv_cross::TypeID& type_id)
const {
788 const auto& struct_type = compiler_->get_type(type_id);
789 FML_CHECK(struct_type.basetype == spirv_cross::SPIRType::BaseType::Struct);
791 std::vector<StructMember> result;
793 size_t current_byte_offset = 0;
794 size_t max_member_alignment = 0;
796 for (
size_t i = 0; i < struct_type.member_types.size(); i++) {
797 const auto& member = compiler_->get_type(struct_type.member_types[i]);
798 const auto struct_member_offset =
799 compiler_->type_struct_member_offset(struct_type, i);
800 auto array_elements = GetArrayElements(member);
802 if (struct_member_offset > current_byte_offset) {
803 const auto alignment_pad = struct_member_offset - current_byte_offset;
804 result.emplace_back(StructMember{
806 spirv_cross::SPIRType::BaseType::Void,
808 GetMemberNameAtIndex(struct_type, i).c_str()),
815 current_byte_offset += alignment_pad;
818 max_member_alignment =
819 std::max<size_t>(max_member_alignment,
820 (member.width / 8) * member.columns * member.vecsize);
822 FML_CHECK(current_byte_offset == struct_member_offset);
825 if (member.basetype == spirv_cross::SPIRType::BaseType::Struct) {
828 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
832 uint32_t element_padding = stride - size;
833 result.emplace_back(StructMember{
834 compiler_->get_name(member.self),
836 GetMemberNameAtIndex(struct_type, i),
837 struct_member_offset,
839 stride * array_elements.value_or(1),
843 current_byte_offset += stride * array_elements.value_or(1);
849 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
850 member.width ==
sizeof(
Scalar) * 8 &&
851 member.columns == 4 &&
854 uint32_t stride = GetArrayStride<sizeof(Matrix)>(struct_type, member, i);
855 uint32_t element_padding = stride -
sizeof(Matrix);
856 result.emplace_back(StructMember{
859 GetMemberNameAtIndex(struct_type, i),
860 struct_member_offset,
862 stride * array_elements.value_or(1),
866 current_byte_offset += stride * array_elements.value_or(1);
871 if (member.basetype == spirv_cross::SPIRType::BaseType::UInt &&
872 member.width ==
sizeof(uint32_t) * 8 &&
873 member.columns == 1 &&
877 GetArrayStride<sizeof(UintPoint32)>(struct_type, member, i);
878 uint32_t element_padding = stride -
sizeof(
UintPoint32);
879 result.emplace_back(StructMember{
882 GetMemberNameAtIndex(struct_type, i),
883 struct_member_offset,
885 stride * array_elements.value_or(1),
889 current_byte_offset += stride * array_elements.value_or(1);
894 if (member.basetype == spirv_cross::SPIRType::BaseType::Int &&
895 member.width ==
sizeof(int32_t) * 8 &&
896 member.columns == 1 &&
900 GetArrayStride<sizeof(IPoint32)>(struct_type, member, i);
901 uint32_t element_padding = stride -
sizeof(
IPoint32);
902 result.emplace_back(StructMember{
905 GetMemberNameAtIndex(struct_type, i),
906 struct_member_offset,
908 stride * array_elements.value_or(1),
912 current_byte_offset += stride * array_elements.value_or(1);
917 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
918 member.width ==
sizeof(
float) * 8 &&
919 member.columns == 1 &&
922 uint32_t stride = GetArrayStride<sizeof(Point)>(struct_type, member, i);
923 uint32_t element_padding = stride -
sizeof(
Point);
924 result.emplace_back(StructMember{
927 GetMemberNameAtIndex(struct_type, i),
928 struct_member_offset,
930 stride * array_elements.value_or(1),
934 current_byte_offset += stride * array_elements.value_or(1);
939 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
940 member.width ==
sizeof(
float) * 8 &&
941 member.columns == 1 &&
944 uint32_t stride = GetArrayStride<sizeof(Vector3)>(struct_type, member, i);
945 uint32_t element_padding = stride -
sizeof(Vector3);
946 result.emplace_back(StructMember{
949 GetMemberNameAtIndex(struct_type, i),
950 struct_member_offset,
952 stride * array_elements.value_or(1),
956 current_byte_offset += stride * array_elements.value_or(1);
961 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
962 member.width ==
sizeof(
float) * 8 &&
963 member.columns == 1 &&
966 uint32_t stride = GetArrayStride<sizeof(Vector4)>(struct_type, member, i);
967 uint32_t element_padding = stride -
sizeof(Vector4);
968 result.emplace_back(StructMember{
971 GetMemberNameAtIndex(struct_type, i),
972 struct_member_offset,
974 stride * array_elements.value_or(1),
978 current_byte_offset += stride * array_elements.value_or(1);
983 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
984 member.width ==
sizeof(Half) * 8 &&
985 member.columns == 1 &&
989 GetArrayStride<sizeof(HalfVector2)>(struct_type, member, i);
990 uint32_t element_padding = stride -
sizeof(HalfVector2);
991 result.emplace_back(StructMember{
994 GetMemberNameAtIndex(struct_type, i),
995 struct_member_offset,
997 stride * array_elements.value_or(1),
1001 current_byte_offset += stride * array_elements.value_or(1);
1006 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1007 member.width ==
sizeof(Half) * 8 &&
1008 member.columns == 1 &&
1012 GetArrayStride<sizeof(HalfVector3)>(struct_type, member, i);
1013 uint32_t element_padding = stride -
sizeof(HalfVector3);
1014 result.emplace_back(StructMember{
1017 GetMemberNameAtIndex(struct_type, i),
1018 struct_member_offset,
1019 sizeof(HalfVector3),
1020 stride * array_elements.value_or(1),
1024 current_byte_offset += stride * array_elements.value_or(1);
1029 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1030 member.width ==
sizeof(Half) * 8 &&
1031 member.columns == 1 &&
1035 GetArrayStride<sizeof(HalfVector4)>(struct_type, member, i);
1036 uint32_t element_padding = stride -
sizeof(HalfVector4);
1037 result.emplace_back(StructMember{
1040 GetMemberNameAtIndex(struct_type, i),
1041 struct_member_offset,
1042 sizeof(HalfVector4),
1043 stride * array_elements.value_or(1),
1047 current_byte_offset += stride * array_elements.value_or(1);
1054 if (maybe_known_type.has_value() &&
1055 member.columns == 1 &&
1058 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
1060 stride = maybe_known_type.value().byte_size;
1062 uint32_t element_padding = stride - maybe_known_type.value().byte_size;
1064 result.emplace_back(StructMember{
1065 maybe_known_type.value().name,
1067 GetMemberNameAtIndex(struct_type, i),
1068 struct_member_offset,
1069 maybe_known_type.value().byte_size,
1070 stride * array_elements.value_or(1),
1074 current_byte_offset += stride * array_elements.value_or(1);
1082 const size_t size = (member.width * member.columns * member.vecsize) / 8u;
1083 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
1087 auto element_padding = stride - size;
1088 result.emplace_back(StructMember{
1091 GetMemberNameAtIndex(struct_type, i),
1092 struct_member_offset,
1094 stride * array_elements.value_or(1),
1098 current_byte_offset += stride * array_elements.value_or(1);
1103 if (max_member_alignment > 0u) {
1104 const auto struct_length = current_byte_offset;
1106 const auto excess = struct_length % max_member_alignment;
1108 const auto padding = max_member_alignment - excess;
1109 result.emplace_back(StructMember{
1111 spirv_cross::SPIRType::BaseType::Void,
1113 current_byte_offset,
1126 std::optional<Reflector::StructDefinition> Reflector::ReflectStructDefinition(
1127 const spirv_cross::TypeID& type_id)
const {
1128 const auto& type = compiler_->get_type(type_id);
1129 if (type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
1130 return std::nullopt;
1133 const auto struct_name = compiler_->get_name(type_id);
1134 if (struct_name.find(
"_RESERVED_IDENTIFIER_") != std::string::npos) {
1135 return std::nullopt;
1138 auto struct_members = ReadStructMembers(type_id);
1141 StructDefinition struc;
1142 struc.name = struct_name;
1143 struc.byte_length = reflected_struct_size;
1144 struc.members = std::move(struct_members);
1148 nlohmann::json::object_t Reflector::EmitStructDefinition(
1149 std::optional<Reflector::StructDefinition> struc)
const {
1150 nlohmann::json::object_t result;
1151 result[
"name"] = struc->name;
1152 result[
"byte_length"] = struc->byte_length;
1153 auto& members = result[
"members"] = nlohmann::json::array_t{};
1154 for (
const auto& struct_member : struc->members) {
1155 auto& member = members.emplace_back(nlohmann::json::object_t{});
1156 member[
"name"] = struct_member.name;
1157 member[
"type"] = struct_member.type;
1158 member[
"base_type"] =
1160 member[
"offset"] = struct_member.offset;
1161 member[
"byte_length"] = struct_member.byte_length;
1162 if (struct_member.array_elements.has_value()) {
1163 member[
"array_elements"] = struct_member.array_elements.value();
1165 member[
"array_elements"] =
"std::nullopt";
1167 member[
"element_padding"] = struct_member.element_padding;
1180 const spirv_cross::Compiler& compiler,
1181 const spirv_cross::Resource* resource) {
1184 const auto& type = compiler.get_type(resource->type_id);
1186 const auto total_size = type.columns * type.vecsize * type.width / 8u;
1189 if (type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1190 type.columns == 1u && type.vecsize == 2u &&
1191 type.width ==
sizeof(
float) * 8u) {
1193 }
else if (type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1194 type.columns == 1u && type.vecsize == 4u &&
1195 type.width ==
sizeof(
float) * 8u) {
1197 }
else if (type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1198 type.columns == 1u && type.vecsize == 3u &&
1199 type.width ==
sizeof(
float) * 8u) {
1201 }
else if (type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1202 type.columns == 1u && type.vecsize == 1u &&
1203 type.width ==
sizeof(
float) * 8u) {
1205 }
else if (type.basetype == spirv_cross::SPIRType::BaseType::Int &&
1206 type.columns == 1u && type.vecsize == 1u &&
1207 type.width ==
sizeof(int32_t) * 8u) {
1217 std::optional<Reflector::StructDefinition>
1218 Reflector::ReflectPerVertexStructDefinition(
1219 const spirv_cross::SmallVector<spirv_cross::Resource>& stage_inputs)
const {
1222 if (stage_inputs.empty()) {
1223 return std::nullopt;
1227 std::set<uint32_t> locations;
1228 for (
const auto& input : stage_inputs) {
1229 auto location = compiler_->get_decoration(
1230 input.id, spv::Decoration::DecorationLocation);
1231 if (locations.count(location) != 0) {
1233 return std::nullopt;
1235 locations.insert(location);
1238 for (
size_t i = 0; i < locations.size(); i++) {
1239 if (locations.count(i) != 1) {
1244 return std::nullopt;
1248 auto input_for_location =
1249 [&](uint32_t queried_location) ->
const spirv_cross::Resource* {
1250 for (
const auto& input : stage_inputs) {
1251 auto location = compiler_->get_decoration(
1252 input.id, spv::Decoration::DecorationLocation);
1253 if (location == queried_location) {
1262 StructDefinition struc;
1263 struc.name =
"PerVertexData";
1264 struc.byte_length = 0u;
1265 for (
size_t i = 0; i < locations.size(); i++) {
1266 auto resource = input_for_location(i);
1267 if (resource ==
nullptr) {
1268 return std::nullopt;
1270 const auto vertex_type =
1273 auto member = StructMember{
1274 vertex_type.type_name,
1275 vertex_type.base_type,
1276 vertex_type.variable_name,
1278 vertex_type.byte_length,
1279 vertex_type.byte_length,
1283 struc.byte_length += vertex_type.byte_length;
1284 struc.members.emplace_back(std::move(member));
1289 std::optional<std::string> Reflector::GetMemberNameAtIndexIfExists(
1290 const spirv_cross::SPIRType& parent_type,
1291 size_t index)
const {
1292 if (parent_type.type_alias != 0) {
1293 return GetMemberNameAtIndexIfExists(
1294 compiler_->get_type(parent_type.type_alias), index);
1297 if (
auto found = ir_->meta.find(parent_type.self); found != ir_->meta.end()) {
1298 const auto& members = found->second.members;
1299 if (index < members.size() && !members[index].alias.empty()) {
1300 return members[index].alias;
1303 return std::nullopt;
1306 std::string Reflector::GetMemberNameAtIndex(
1307 const spirv_cross::SPIRType& parent_type,
1309 std::string suffix)
const {
1310 if (
auto name = GetMemberNameAtIndexIfExists(parent_type, index);
1312 return name.value();
1314 static std::atomic_size_t sUnnamedMembersID;
1315 std::stringstream stream;
1316 stream <<
"unnamed_" << sUnnamedMembersID++ << suffix;
1317 return stream.str();
1320 std::vector<Reflector::BindPrototype> Reflector::ReflectBindPrototypes(
1321 const spirv_cross::ShaderResources& resources,
1322 spv::ExecutionModel execution_model)
const {
1323 std::vector<BindPrototype> prototypes;
1324 for (
const auto& uniform_buffer : resources.uniform_buffers) {
1325 auto& proto = prototypes.emplace_back(BindPrototype{});
1326 proto.return_type =
"bool";
1328 proto.descriptor_type =
"DescriptorType::kUniformBuffer";
1330 std::stringstream stream;
1331 stream <<
"Bind uniform buffer for resource named " << uniform_buffer.name
1333 proto.docstring = stream.str();
1335 proto.args.push_back(BindPrototypeArgument{
1336 .type_name =
"ResourceBinder&",
1337 .argument_name =
"command",
1339 proto.args.push_back(BindPrototypeArgument{
1340 .type_name =
"BufferView",
1341 .argument_name =
"view",
1344 for (
const auto& storage_buffer : resources.storage_buffers) {
1345 auto& proto = prototypes.emplace_back(BindPrototype{});
1346 proto.return_type =
"bool";
1348 proto.descriptor_type =
"DescriptorType::kStorageBuffer";
1350 std::stringstream stream;
1351 stream <<
"Bind storage buffer for resource named " << storage_buffer.name
1353 proto.docstring = stream.str();
1355 proto.args.push_back(BindPrototypeArgument{
1356 .type_name =
"ResourceBinder&",
1357 .argument_name =
"command",
1359 proto.args.push_back(BindPrototypeArgument{
1360 .type_name =
"BufferView",
1361 .argument_name =
"view",
1364 for (
const auto& sampled_image : resources.sampled_images) {
1365 auto& proto = prototypes.emplace_back(BindPrototype{});
1366 proto.return_type =
"bool";
1368 proto.descriptor_type =
"DescriptorType::kSampledImage";
1370 std::stringstream stream;
1371 stream <<
"Bind combined image sampler for resource named "
1372 << sampled_image.name <<
".";
1373 proto.docstring = stream.str();
1375 proto.args.push_back(BindPrototypeArgument{
1376 .type_name =
"ResourceBinder&",
1377 .argument_name =
"command",
1379 proto.args.push_back(BindPrototypeArgument{
1380 .type_name =
"std::shared_ptr<const Texture>",
1381 .argument_name =
"texture",
1383 proto.args.push_back(BindPrototypeArgument{
1384 .type_name =
"const std::unique_ptr<const Sampler>&",
1385 .argument_name =
"sampler",
1388 for (
const auto& separate_image : resources.separate_images) {
1389 auto& proto = prototypes.emplace_back(BindPrototype{});
1390 proto.return_type =
"bool";
1392 proto.descriptor_type =
"DescriptorType::kImage";
1394 std::stringstream stream;
1395 stream <<
"Bind separate image for resource named " << separate_image.name
1397 proto.docstring = stream.str();
1399 proto.args.push_back(BindPrototypeArgument{
1400 .type_name =
"Command&",
1401 .argument_name =
"command",
1403 proto.args.push_back(BindPrototypeArgument{
1404 .type_name =
"std::shared_ptr<const Texture>",
1405 .argument_name =
"texture",
1408 for (
const auto& separate_sampler : resources.separate_samplers) {
1409 auto& proto = prototypes.emplace_back(BindPrototype{});
1410 proto.return_type =
"bool";
1412 proto.descriptor_type =
"DescriptorType::kSampler";
1414 std::stringstream stream;
1415 stream <<
"Bind separate sampler for resource named "
1416 << separate_sampler.name <<
".";
1417 proto.docstring = stream.str();
1419 proto.args.push_back(BindPrototypeArgument{
1420 .type_name =
"Command&",
1421 .argument_name =
"command",
1423 proto.args.push_back(BindPrototypeArgument{
1424 .type_name =
"std::shared_ptr<const Sampler>",
1425 .argument_name =
"sampler",
1431 nlohmann::json::array_t Reflector::EmitBindPrototypes(
1432 const spirv_cross::ShaderResources& resources,
1433 spv::ExecutionModel execution_model)
const {
1434 const auto prototypes = ReflectBindPrototypes(resources, execution_model);
1435 nlohmann::json::array_t result;
1436 for (
const auto& res : prototypes) {
1437 auto& item = result.emplace_back(nlohmann::json::object_t{});
1438 item[
"return_type"] = res.return_type;
1439 item[
"name"] = res.name;
1440 item[
"docstring"] = res.docstring;
1441 item[
"descriptor_type"] = res.descriptor_type;
1442 auto& args = item[
"args"] = nlohmann::json::array_t{};
1443 for (
const auto& arg : res.args) {
1444 auto& json_arg = args.emplace_back(nlohmann::json::object_t{});
1445 json_arg[
"type_name"] = arg.type_name;
1446 json_arg[
"argument_name"] = arg.argument_name;