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.binding =
352 compiler_->get_decoration(var.self, spv::Decoration::DecorationBinding);
353 uniform_description.type = spir_type.basetype;
354 uniform_description.rows = spir_type.vecsize;
355 uniform_description.columns = spir_type.columns;
356 uniform_description.bit_width = spir_type.width;
357 uniform_description.array_elements = GetArrayElements(spir_type);
359 spir_type.basetype ==
360 spirv_cross::SPIRType::BaseType::SampledImage)
361 <<
"Vulkan runtime effect had unexpected uniforms outside of the "
362 "uniform buffer object.";
363 data->uniforms.emplace_back(std::move(uniform_description));
366 const auto ubos = compiler_->get_shader_resources().uniform_buffers;
373 "for Vulkan runtime stage backend.";
377 const auto& ubo = ubos[0];
380 compiler_->get_decoration(ubo.id, spv::Decoration::DecorationBinding);
381 auto members = ReadStructMembers(ubo.type_id);
382 std::vector<uint8_t> struct_layout;
383 size_t float_count = 0;
385 for (
size_t i = 0; i < members.size(); i += 1) {
386 const auto& member = members[i];
387 std::vector<int> bytes;
388 switch (member.underlying_type) {
390 size_t padding_count =
391 (member.size +
sizeof(float) - 1) /
sizeof(float);
392 while (padding_count > 0) {
393 struct_layout.push_back(0);
399 size_t member_float_count = member.byte_length /
sizeof(float);
400 float_count += member_float_count;
401 while (member_float_count > 0) {
402 struct_layout.push_back(1);
403 member_float_count--;
408 VALIDATION_LOG <<
"Non-floating-type struct member " << member.name
409 <<
" is not supported.";
413 data->uniforms.emplace_back(UniformDescription{
417 .type = spirv_cross::SPIRType::Struct,
418 .struct_layout = std::move(struct_layout),
419 .struct_float_count = float_count,
424 if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
425 const auto inputs = compiler_->get_shader_resources().stage_inputs;
426 auto input_offsets = ComputeOffsets(inputs);
427 for (
const auto& input : inputs) {
428 std::optional<size_t>
offset = GetOffset(input.id, input_offsets);
430 const auto type = compiler_->get_type(input.type_id);
432 InputDescription input_description;
433 input_description.name = input.name;
434 input_description.location = compiler_->get_decoration(
435 input.id, spv::Decoration::DecorationLocation);
436 input_description.set = compiler_->get_decoration(
437 input.id, spv::Decoration::DecorationDescriptorSet);
438 input_description.binding = compiler_->get_decoration(
439 input.id, spv::Decoration::DecorationBinding);
440 input_description.type =
type.basetype;
441 input_description.bit_width =
type.width;
442 input_description.vec_size =
type.vecsize;
443 input_description.columns =
type.columns;
444 input_description.offset =
offset.value_or(0u);
445 data->inputs.emplace_back(std::move(input_description));
452 std::shared_ptr<ShaderBundleData> Reflector::GenerateShaderBundleData()
const {
453 const auto& entrypoints = compiler_->get_entry_points_and_stages();
454 if (entrypoints.size() != 1u) {
458 auto data = std::make_shared<ShaderBundleData>(
460 entrypoints.front().execution_model,
463 data->SetShaderData(shader_data_);
465 const auto uniforms = compiler_->get_shader_resources().uniform_buffers;
466 for (
const auto& uniform : uniforms) {
467 ShaderBundleData::ShaderUniformStruct uniform_struct;
468 uniform_struct.name = uniform.name;
471 uniform_struct.set = compiler_->get_decoration(
472 uniform.id, spv::Decoration::DecorationDescriptorSet);
473 uniform_struct.binding = compiler_->get_decoration(
474 uniform.id, spv::Decoration::DecorationBinding);
476 const auto type = compiler_->get_type(uniform.type_id);
477 if (
type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
478 std::cerr <<
"Error: Uniform \"" << uniform.name
479 <<
"\" is not a struct. All Flutter GPU shader uniforms must "
485 size_t size_in_bytes = 0;
486 for (
const auto& struct_member : ReadStructMembers(uniform.type_id)) {
487 size_in_bytes += struct_member.byte_length;
491 ShaderBundleData::ShaderUniformStructField uniform_struct_field;
492 uniform_struct_field.name = struct_member.name;
493 uniform_struct_field.type = struct_member.base_type;
494 uniform_struct_field.offset_in_bytes = struct_member.offset;
495 uniform_struct_field.element_size_in_bytes = struct_member.size;
496 uniform_struct_field.total_size_in_bytes = struct_member.byte_length;
497 uniform_struct_field.array_elements = struct_member.array_elements;
498 uniform_struct.fields.push_back(uniform_struct_field);
500 uniform_struct.size_in_bytes = size_in_bytes;
502 data->AddUniformStruct(uniform_struct);
505 const auto sampled_images = compiler_->get_shader_resources().sampled_images;
506 for (
const auto& image : sampled_images) {
507 ShaderBundleData::ShaderUniformTexture uniform_texture;
508 uniform_texture.name = image.name;
511 uniform_texture.set = compiler_->get_decoration(
512 image.id, spv::Decoration::DecorationDescriptorSet);
513 uniform_texture.binding =
514 compiler_->get_decoration(image.id, spv::Decoration::DecorationBinding);
515 data->AddUniformTexture(uniform_texture);
519 if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
520 const auto inputs = compiler_->get_shader_resources().stage_inputs;
521 auto input_offsets = ComputeOffsets(inputs);
522 for (
const auto& input : inputs) {
523 std::optional<size_t>
offset = GetOffset(input.id, input_offsets);
525 const auto type = compiler_->get_type(input.type_id);
527 InputDescription input_description;
528 input_description.name = input.name;
529 input_description.location = compiler_->get_decoration(
530 input.id, spv::Decoration::DecorationLocation);
531 input_description.set = compiler_->get_decoration(
532 input.id, spv::Decoration::DecorationDescriptorSet);
533 input_description.binding = compiler_->get_decoration(
534 input.id, spv::Decoration::DecorationBinding);
535 input_description.type =
type.basetype;
536 input_description.bit_width =
type.width;
537 input_description.vec_size =
type.vecsize;
538 input_description.columns =
type.columns;
539 input_description.offset =
offset.value_or(0u);
540 data->AddInputDescription(std::move(input_description));
547 std::optional<uint32_t> Reflector::GetArrayElements(
548 const spirv_cross::SPIRType&
type)
const {
549 if (
type.array.empty()) {
552 FML_CHECK(
type.array.size() == 1)
553 <<
"Multi-dimensional arrays are not supported.";
554 FML_CHECK(
type.array_size_literal.front())
555 <<
"Must use a literal for array sizes.";
556 return type.array.front();
562 return "Metal Shading Language";
564 return "OpenGL Shading Language";
566 return "OpenGL Shading Language (Relaxed Vulkan Semantics)";
568 return "SkSL Shading Language";
573 std::shared_ptr<fml::Mapping> Reflector::InflateTemplate(
574 std::string_view tmpl)
const {
575 inja::Environment env;
576 env.set_trim_blocks(
true);
577 env.set_lstrip_blocks(
true);
579 env.add_callback(
"camel_case", 1u, [](inja::Arguments& args) {
580 return ToCamelCase(args.at(0u)->get<std::string>());
583 env.add_callback(
"to_shader_stage", 1u, [](inja::Arguments& args) {
587 env.add_callback(
"get_generator_name", 0u,
588 [
type = compiler_.
GetType()](inja::Arguments& args) {
592 auto inflated_template =
593 std::make_shared<std::string>(env.render(tmpl, *template_arguments_));
595 return std::make_shared<fml::NonOwnedMapping>(
596 reinterpret_cast<const uint8_t*
>(inflated_template->data()),
597 inflated_template->size(), [inflated_template](
auto,
auto) {});
600 std::vector<size_t> Reflector::ComputeOffsets(
601 const spirv_cross::SmallVector<spirv_cross::Resource>& resources)
const {
602 std::vector<size_t> offsets(resources.size(), 0);
603 if (resources.size() == 0) {
606 for (
const auto& resource : resources) {
607 const auto type = compiler_->get_type(resource.type_id);
608 auto location = compiler_->get_decoration(
609 resource.id, spv::Decoration::DecorationLocation);
611 if (location >= resources.size() || location < 0) {
614 offsets[location] = (
type.width *
type.vecsize) / 8;
616 for (
size_t i = 1; i < resources.size(); i++) {
617 offsets[i] += offsets[i - 1];
619 for (
size_t i = resources.size() - 1; i > 0; i--) {
620 offsets[i] = offsets[i - 1];
627 std::optional<size_t> Reflector::GetOffset(
629 const std::vector<size_t>& offsets)
const {
631 compiler_->get_decoration(
id, spv::Decoration::DecorationLocation);
632 if (location >= offsets.size()) {
635 return offsets[location];
638 std::optional<nlohmann::json::object_t> Reflector::ReflectResource(
639 const spirv_cross::Resource& resource,
640 std::optional<size_t>
offset)
const {
641 nlohmann::json::object_t result;
643 result[
"name"] = resource.name;
644 result[
"descriptor_set"] = compiler_->get_decoration(
645 resource.id, spv::Decoration::DecorationDescriptorSet);
646 result[
"binding"] = compiler_->get_decoration(
647 resource.id, spv::Decoration::DecorationBinding);
648 result[
"set"] = compiler_->get_decoration(
649 resource.id, spv::Decoration::DecorationDescriptorSet);
650 result[
"location"] = compiler_->get_decoration(
651 resource.id, spv::Decoration::DecorationLocation);
653 compiler_->get_decoration(resource.id, spv::Decoration::DecorationIndex);
658 auto type = ReflectType(resource.type_id);
659 if (!
type.has_value()) {
662 result[
"type"] = std::move(
type.value());
663 result[
"offset"] =
offset.value_or(0u);
667 std::optional<nlohmann::json::object_t> Reflector::ReflectType(
668 const spirv_cross::TypeID& type_id)
const {
669 nlohmann::json::object_t result;
671 const auto type = compiler_->get_type(type_id);
674 result[
"bit_width"] =
type.width;
675 result[
"vec_size"] =
type.vecsize;
676 result[
"columns"] =
type.columns;
677 auto& members = result[
"members"] = nlohmann::json::array_t{};
678 if (
type.basetype == spirv_cross::SPIRType::BaseType::Struct) {
679 for (
const auto& struct_member : ReadStructMembers(type_id)) {
680 auto member = nlohmann::json::object_t{};
681 member[
"name"] = struct_member.name;
682 member[
"type"] = struct_member.type;
683 member[
"base_type"] =
685 member[
"offset"] = struct_member.offset;
686 member[
"size"] = struct_member.size;
687 member[
"byte_length"] = struct_member.byte_length;
688 if (struct_member.array_elements.has_value()) {
689 member[
"array_elements"] = struct_member.array_elements.value();
691 member[
"array_elements"] =
"std::nullopt";
693 members.emplace_back(std::move(member));
700 std::optional<nlohmann::json::array_t> Reflector::ReflectResources(
701 const spirv_cross::SmallVector<spirv_cross::Resource>& resources,
702 bool compute_offsets)
const {
703 nlohmann::json::array_t result;
704 result.reserve(resources.size());
705 std::vector<size_t> offsets;
706 if (compute_offsets) {
707 offsets = ComputeOffsets(resources);
709 for (
const auto& resource : resources) {
710 std::optional<size_t> maybe_offset = std::nullopt;
711 if (compute_offsets) {
712 maybe_offset = GetOffset(resource.id, offsets);
714 if (
auto reflected = ReflectResource(resource, maybe_offset);
715 reflected.has_value()) {
716 result.emplace_back(std::move(reflected.value()));
725 std::stringstream stream;
726 stream <<
"Padding<" << size <<
">";
736 spirv_cross::SPIRType::BaseType
type) {
738 case spirv_cross::SPIRType::BaseType::Boolean:
741 .byte_size =
sizeof(bool),
743 case spirv_cross::SPIRType::BaseType::Float:
746 .byte_size =
sizeof(
Scalar),
748 case spirv_cross::SPIRType::BaseType::Half:
751 .byte_size =
sizeof(
Half),
753 case spirv_cross::SPIRType::BaseType::UInt:
756 .byte_size =
sizeof(uint32_t),
758 case spirv_cross::SPIRType::BaseType::Int:
761 .byte_size =
sizeof(int32_t),
783 auto struct_size = 0u;
784 for (
const auto& member : members) {
785 struct_size += member.byte_length;
790 std::vector<StructMember> Reflector::ReadStructMembers(
791 const spirv_cross::TypeID& type_id)
const {
792 const auto& struct_type = compiler_->get_type(type_id);
793 FML_CHECK(struct_type.basetype == spirv_cross::SPIRType::BaseType::Struct);
795 std::vector<StructMember> result;
797 size_t current_byte_offset = 0;
798 size_t max_member_alignment = 0;
800 for (
size_t i = 0; i < struct_type.member_types.size(); i++) {
801 const auto& member = compiler_->get_type(struct_type.member_types[i]);
802 const auto struct_member_offset =
803 compiler_->type_struct_member_offset(struct_type, i);
804 auto array_elements = GetArrayElements(member);
806 if (struct_member_offset > current_byte_offset) {
807 const auto alignment_pad = struct_member_offset - current_byte_offset;
808 result.emplace_back(StructMember{
810 spirv_cross::SPIRType::BaseType::Void,
812 GetMemberNameAtIndex(struct_type, i).c_str()),
819 current_byte_offset += alignment_pad;
822 max_member_alignment =
823 std::max<size_t>(max_member_alignment,
824 (member.width / 8) * member.columns * member.vecsize);
826 FML_CHECK(current_byte_offset == struct_member_offset);
829 if (member.basetype == spirv_cross::SPIRType::BaseType::Struct) {
832 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
836 uint32_t element_padding = stride - size;
837 result.emplace_back(StructMember{
838 compiler_->get_name(member.self),
840 GetMemberNameAtIndex(struct_type, i),
841 struct_member_offset,
843 stride * array_elements.value_or(1),
847 current_byte_offset += stride * array_elements.value_or(1);
853 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
854 member.width ==
sizeof(
Scalar) * 8 &&
855 member.columns == 4 &&
858 uint32_t stride = GetArrayStride<sizeof(Matrix)>(struct_type, member, i);
859 uint32_t element_padding = stride -
sizeof(Matrix);
860 result.emplace_back(StructMember{
863 GetMemberNameAtIndex(struct_type, i),
864 struct_member_offset,
866 stride * array_elements.value_or(1),
870 current_byte_offset += stride * array_elements.value_or(1);
875 if (member.basetype == spirv_cross::SPIRType::BaseType::UInt &&
876 member.width ==
sizeof(uint32_t) * 8 &&
877 member.columns == 1 &&
881 GetArrayStride<sizeof(UintPoint32)>(struct_type, member, i);
882 uint32_t element_padding = stride -
sizeof(
UintPoint32);
883 result.emplace_back(StructMember{
886 GetMemberNameAtIndex(struct_type, i),
887 struct_member_offset,
889 stride * array_elements.value_or(1),
893 current_byte_offset += stride * array_elements.value_or(1);
898 if (member.basetype == spirv_cross::SPIRType::BaseType::Int &&
899 member.width ==
sizeof(int32_t) * 8 &&
900 member.columns == 1 &&
904 GetArrayStride<sizeof(IPoint32)>(struct_type, member, i);
905 uint32_t element_padding = stride -
sizeof(
IPoint32);
906 result.emplace_back(StructMember{
909 GetMemberNameAtIndex(struct_type, i),
910 struct_member_offset,
912 stride * array_elements.value_or(1),
916 current_byte_offset += stride * array_elements.value_or(1);
921 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
922 member.width ==
sizeof(
float) * 8 &&
923 member.columns == 1 &&
926 uint32_t stride = GetArrayStride<sizeof(Point)>(struct_type, member, i);
927 uint32_t element_padding = stride -
sizeof(
Point);
928 result.emplace_back(StructMember{
931 GetMemberNameAtIndex(struct_type, i),
932 struct_member_offset,
934 stride * array_elements.value_or(1),
938 current_byte_offset += stride * array_elements.value_or(1);
943 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
944 member.width ==
sizeof(
float) * 8 &&
945 member.columns == 1 &&
948 uint32_t stride = GetArrayStride<sizeof(Vector3)>(struct_type, member, i);
949 uint32_t element_padding = stride -
sizeof(Vector3);
950 result.emplace_back(StructMember{
953 GetMemberNameAtIndex(struct_type, i),
954 struct_member_offset,
956 stride * array_elements.value_or(1),
960 current_byte_offset += stride * array_elements.value_or(1);
965 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
966 member.width ==
sizeof(
float) * 8 &&
967 member.columns == 1 &&
970 uint32_t stride = GetArrayStride<sizeof(Vector4)>(struct_type, member, i);
971 uint32_t element_padding = stride -
sizeof(Vector4);
972 result.emplace_back(StructMember{
975 GetMemberNameAtIndex(struct_type, i),
976 struct_member_offset,
978 stride * array_elements.value_or(1),
982 current_byte_offset += stride * array_elements.value_or(1);
987 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
988 member.width ==
sizeof(Half) * 8 &&
989 member.columns == 1 &&
993 GetArrayStride<sizeof(HalfVector2)>(struct_type, member, i);
994 uint32_t element_padding = stride -
sizeof(HalfVector2);
995 result.emplace_back(StructMember{
998 GetMemberNameAtIndex(struct_type, i),
999 struct_member_offset,
1000 sizeof(HalfVector2),
1001 stride * array_elements.value_or(1),
1005 current_byte_offset += stride * array_elements.value_or(1);
1010 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1011 member.width ==
sizeof(Half) * 8 &&
1012 member.columns == 1 &&
1016 GetArrayStride<sizeof(HalfVector3)>(struct_type, member, i);
1017 uint32_t element_padding = stride -
sizeof(HalfVector3);
1018 result.emplace_back(StructMember{
1021 GetMemberNameAtIndex(struct_type, i),
1022 struct_member_offset,
1023 sizeof(HalfVector3),
1024 stride * array_elements.value_or(1),
1028 current_byte_offset += stride * array_elements.value_or(1);
1033 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1034 member.width ==
sizeof(Half) * 8 &&
1035 member.columns == 1 &&
1039 GetArrayStride<sizeof(HalfVector4)>(struct_type, member, i);
1040 uint32_t element_padding = stride -
sizeof(HalfVector4);
1041 result.emplace_back(StructMember{
1044 GetMemberNameAtIndex(struct_type, i),
1045 struct_member_offset,
1046 sizeof(HalfVector4),
1047 stride * array_elements.value_or(1),
1051 current_byte_offset += stride * array_elements.value_or(1);
1058 if (maybe_known_type.has_value() &&
1059 member.columns == 1 &&
1062 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
1064 stride = maybe_known_type.value().byte_size;
1066 uint32_t element_padding = stride - maybe_known_type.value().byte_size;
1068 result.emplace_back(StructMember{
1069 maybe_known_type.value().name,
1071 GetMemberNameAtIndex(struct_type, i),
1072 struct_member_offset,
1073 maybe_known_type.value().byte_size,
1074 stride * array_elements.value_or(1),
1078 current_byte_offset += stride * array_elements.value_or(1);
1086 const size_t size = (member.width * member.columns * member.vecsize) / 8u;
1087 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
1091 auto element_padding = stride - size;
1092 result.emplace_back(StructMember{
1095 GetMemberNameAtIndex(struct_type, i),
1096 struct_member_offset,
1098 stride * array_elements.value_or(1),
1102 current_byte_offset += stride * array_elements.value_or(1);
1107 if (max_member_alignment > 0u) {
1108 const auto struct_length = current_byte_offset;
1110 const auto excess = struct_length % max_member_alignment;
1112 const auto padding = max_member_alignment - excess;
1113 result.emplace_back(StructMember{
1115 spirv_cross::SPIRType::BaseType::Void,
1117 current_byte_offset,
1130 std::optional<Reflector::StructDefinition> Reflector::ReflectStructDefinition(
1131 const spirv_cross::TypeID& type_id)
const {
1132 const auto&
type = compiler_->get_type(type_id);
1133 if (
type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
1134 return std::nullopt;
1137 const auto struct_name = compiler_->get_name(type_id);
1138 if (struct_name.find(
"_RESERVED_IDENTIFIER_") != std::string::npos) {
1139 return std::nullopt;
1142 auto struct_members = ReadStructMembers(type_id);
1145 StructDefinition struc;
1146 struc.name = struct_name;
1147 struc.byte_length = reflected_struct_size;
1148 struc.members = std::move(struct_members);
1152 nlohmann::json::object_t Reflector::EmitStructDefinition(
1153 std::optional<Reflector::StructDefinition> struc)
const {
1154 nlohmann::json::object_t result;
1155 result[
"name"] = struc->name;
1156 result[
"byte_length"] = struc->byte_length;
1157 auto& members = result[
"members"] = nlohmann::json::array_t{};
1158 for (
const auto& struct_member : struc->members) {
1159 auto& member = members.emplace_back(nlohmann::json::object_t{});
1160 member[
"name"] = struct_member.name;
1161 member[
"type"] = struct_member.type;
1162 member[
"base_type"] =
1164 member[
"offset"] = struct_member.offset;
1165 member[
"byte_length"] = struct_member.byte_length;
1166 if (struct_member.array_elements.has_value()) {
1167 member[
"array_elements"] = struct_member.array_elements.value();
1169 member[
"array_elements"] =
"std::nullopt";
1171 member[
"element_padding"] = struct_member.element_padding;
1184 const spirv_cross::Compiler& compiler,
1185 const spirv_cross::Resource* resource) {
1188 const auto&
type = compiler.get_type(resource->type_id);
1190 const auto total_size =
type.columns *
type.vecsize *
type.width / 8u;
1193 if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1194 type.columns == 1u &&
type.vecsize == 2u &&
1195 type.width ==
sizeof(
float) * 8u) {
1197 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1198 type.columns == 1u &&
type.vecsize == 4u &&
1199 type.width ==
sizeof(
float) * 8u) {
1201 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1202 type.columns == 1u &&
type.vecsize == 3u &&
1203 type.width ==
sizeof(
float) * 8u) {
1205 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1206 type.columns == 1u &&
type.vecsize == 1u &&
1207 type.width ==
sizeof(
float) * 8u) {
1209 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Int &&
1210 type.columns == 1u &&
type.vecsize == 1u &&
1211 type.width ==
sizeof(int32_t) * 8u) {
1221 std::optional<Reflector::StructDefinition>
1222 Reflector::ReflectPerVertexStructDefinition(
1223 const spirv_cross::SmallVector<spirv_cross::Resource>& stage_inputs)
const {
1226 if (stage_inputs.empty()) {
1227 return std::nullopt;
1231 std::set<uint32_t> locations;
1232 for (
const auto& input : stage_inputs) {
1233 auto location = compiler_->get_decoration(
1234 input.id, spv::Decoration::DecorationLocation);
1235 if (locations.count(location) != 0) {
1237 return std::nullopt;
1239 locations.insert(location);
1242 for (
size_t i = 0; i < locations.size(); i++) {
1243 if (locations.count(i) != 1) {
1248 return std::nullopt;
1252 auto input_for_location =
1253 [&](uint32_t queried_location) ->
const spirv_cross::Resource* {
1254 for (
const auto& input : stage_inputs) {
1255 auto location = compiler_->get_decoration(
1256 input.id, spv::Decoration::DecorationLocation);
1257 if (location == queried_location) {
1266 StructDefinition struc;
1267 struc.name =
"PerVertexData";
1268 struc.byte_length = 0u;
1269 for (
size_t i = 0; i < locations.size(); i++) {
1270 auto resource = input_for_location(i);
1271 if (resource ==
nullptr) {
1272 return std::nullopt;
1274 const auto vertex_type =
1277 auto member = StructMember{
1278 vertex_type.type_name,
1279 vertex_type.base_type,
1280 vertex_type.variable_name,
1282 vertex_type.byte_length,
1283 vertex_type.byte_length,
1287 struc.byte_length += vertex_type.byte_length;
1288 struc.members.emplace_back(std::move(member));
1293 std::optional<std::string> Reflector::GetMemberNameAtIndexIfExists(
1294 const spirv_cross::SPIRType& parent_type,
1295 size_t index)
const {
1296 if (parent_type.type_alias != 0) {
1297 return GetMemberNameAtIndexIfExists(
1298 compiler_->get_type(parent_type.type_alias), index);
1301 if (
auto found = ir_->meta.find(parent_type.self); found != ir_->meta.end()) {
1302 const auto& members = found->second.members;
1303 if (index < members.size() && !members[index].alias.empty()) {
1304 return members[index].alias;
1307 return std::nullopt;
1310 std::string Reflector::GetMemberNameAtIndex(
1311 const spirv_cross::SPIRType& parent_type,
1313 std::string suffix)
const {
1314 if (
auto name = GetMemberNameAtIndexIfExists(parent_type, index);
1316 return name.value();
1318 static std::atomic_size_t sUnnamedMembersID;
1319 std::stringstream stream;
1320 stream <<
"unnamed_" << sUnnamedMembersID++ << suffix;
1321 return stream.str();
1324 std::vector<Reflector::BindPrototype> Reflector::ReflectBindPrototypes(
1325 const spirv_cross::ShaderResources& resources,
1326 spv::ExecutionModel execution_model)
const {
1327 std::vector<BindPrototype> prototypes;
1328 for (
const auto& uniform_buffer : resources.uniform_buffers) {
1329 auto& proto = prototypes.emplace_back(BindPrototype{});
1330 proto.return_type =
"bool";
1332 proto.descriptor_type =
"DescriptorType::kUniformBuffer";
1334 std::stringstream stream;
1335 stream <<
"Bind uniform buffer for resource named " << uniform_buffer.name
1337 proto.docstring = stream.str();
1339 proto.args.push_back(BindPrototypeArgument{
1340 .type_name =
"ResourceBinder&",
1341 .argument_name =
"command",
1343 proto.args.push_back(BindPrototypeArgument{
1344 .type_name =
"BufferView",
1345 .argument_name =
"view",
1348 for (
const auto& storage_buffer : resources.storage_buffers) {
1349 auto& proto = prototypes.emplace_back(BindPrototype{});
1350 proto.return_type =
"bool";
1352 proto.descriptor_type =
"DescriptorType::kStorageBuffer";
1354 std::stringstream stream;
1355 stream <<
"Bind storage buffer for resource named " << storage_buffer.name
1357 proto.docstring = stream.str();
1359 proto.args.push_back(BindPrototypeArgument{
1360 .type_name =
"ResourceBinder&",
1361 .argument_name =
"command",
1363 proto.args.push_back(BindPrototypeArgument{
1364 .type_name =
"BufferView",
1365 .argument_name =
"view",
1368 for (
const auto& sampled_image : resources.sampled_images) {
1369 auto& proto = prototypes.emplace_back(BindPrototype{});
1370 proto.return_type =
"bool";
1372 proto.descriptor_type =
"DescriptorType::kSampledImage";
1374 std::stringstream stream;
1375 stream <<
"Bind combined image sampler for resource named "
1376 << sampled_image.name <<
".";
1377 proto.docstring = stream.str();
1379 proto.args.push_back(BindPrototypeArgument{
1380 .type_name =
"ResourceBinder&",
1381 .argument_name =
"command",
1383 proto.args.push_back(BindPrototypeArgument{
1384 .type_name =
"std::shared_ptr<const Texture>",
1385 .argument_name =
"texture",
1387 proto.args.push_back(BindPrototypeArgument{
1388 .type_name =
"const std::unique_ptr<const Sampler>&",
1389 .argument_name =
"sampler",
1392 for (
const auto& separate_image : resources.separate_images) {
1393 auto& proto = prototypes.emplace_back(BindPrototype{});
1394 proto.return_type =
"bool";
1396 proto.descriptor_type =
"DescriptorType::kImage";
1398 std::stringstream stream;
1399 stream <<
"Bind separate image for resource named " << separate_image.name
1401 proto.docstring = stream.str();
1403 proto.args.push_back(BindPrototypeArgument{
1404 .type_name =
"Command&",
1405 .argument_name =
"command",
1407 proto.args.push_back(BindPrototypeArgument{
1408 .type_name =
"std::shared_ptr<const Texture>",
1409 .argument_name =
"texture",
1412 for (
const auto& separate_sampler : resources.separate_samplers) {
1413 auto& proto = prototypes.emplace_back(BindPrototype{});
1414 proto.return_type =
"bool";
1416 proto.descriptor_type =
"DescriptorType::kSampler";
1418 std::stringstream stream;
1419 stream <<
"Bind separate sampler for resource named "
1420 << separate_sampler.name <<
".";
1421 proto.docstring = stream.str();
1423 proto.args.push_back(BindPrototypeArgument{
1424 .type_name =
"Command&",
1425 .argument_name =
"command",
1427 proto.args.push_back(BindPrototypeArgument{
1428 .type_name =
"std::shared_ptr<const Sampler>",
1429 .argument_name =
"sampler",
1435 nlohmann::json::array_t Reflector::EmitBindPrototypes(
1436 const spirv_cross::ShaderResources& resources,
1437 spv::ExecutionModel execution_model)
const {
1438 const auto prototypes = ReflectBindPrototypes(resources, execution_model);
1439 nlohmann::json::array_t result;
1440 for (
const auto& res : prototypes) {
1441 auto& item = result.emplace_back(nlohmann::json::object_t{});
1442 item[
"return_type"] = res.return_type;
1443 item[
"name"] = res.name;
1444 item[
"docstring"] = res.docstring;
1445 item[
"descriptor_type"] = res.descriptor_type;
1446 auto& args = item[
"args"] = nlohmann::json::array_t{};
1447 for (
const auto& arg : res.args) {
1448 auto& json_arg = args.emplace_back(nlohmann::json::object_t{});
1449 json_arg[
"type_name"] = arg.type_name;
1450 json_arg[
"argument_name"] = arg.argument_name;