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 result[
"relaxed_precision"] =
659 compiler_->get_decoration(
660 resource.id, spv::Decoration::DecorationRelaxedPrecision) == 1;
661 result[
"offset"] =
offset.value_or(0u);
662 auto type = ReflectType(resource.type_id);
663 if (!
type.has_value()) {
666 result[
"type"] = std::move(
type.value());
670 std::optional<nlohmann::json::object_t> Reflector::ReflectType(
671 const spirv_cross::TypeID& type_id)
const {
672 nlohmann::json::object_t result;
674 const auto type = compiler_->get_type(type_id);
677 result[
"bit_width"] =
type.width;
678 result[
"vec_size"] =
type.vecsize;
679 result[
"columns"] =
type.columns;
680 auto& members = result[
"members"] = nlohmann::json::array_t{};
681 if (
type.basetype == spirv_cross::SPIRType::BaseType::Struct) {
682 for (
const auto& struct_member : ReadStructMembers(type_id)) {
683 auto member = nlohmann::json::object_t{};
684 member[
"name"] = struct_member.name;
685 member[
"type"] = struct_member.type;
686 member[
"base_type"] =
688 member[
"offset"] = struct_member.offset;
689 member[
"size"] = struct_member.size;
690 member[
"byte_length"] = struct_member.byte_length;
691 if (struct_member.array_elements.has_value()) {
692 member[
"array_elements"] = struct_member.array_elements.value();
694 member[
"array_elements"] =
"std::nullopt";
696 members.emplace_back(std::move(member));
703 std::optional<nlohmann::json::array_t> Reflector::ReflectResources(
704 const spirv_cross::SmallVector<spirv_cross::Resource>& resources,
705 bool compute_offsets)
const {
706 nlohmann::json::array_t result;
707 result.reserve(resources.size());
708 std::vector<size_t> offsets;
709 if (compute_offsets) {
710 offsets = ComputeOffsets(resources);
712 for (
const auto& resource : resources) {
713 std::optional<size_t> maybe_offset = std::nullopt;
714 if (compute_offsets) {
715 maybe_offset = GetOffset(resource.id, offsets);
717 if (
auto reflected = ReflectResource(resource, maybe_offset);
718 reflected.has_value()) {
719 result.emplace_back(std::move(reflected.value()));
728 std::stringstream stream;
729 stream <<
"Padding<" << size <<
">";
739 spirv_cross::SPIRType::BaseType
type) {
741 case spirv_cross::SPIRType::BaseType::Boolean:
744 .byte_size =
sizeof(bool),
746 case spirv_cross::SPIRType::BaseType::Float:
749 .byte_size =
sizeof(
Scalar),
751 case spirv_cross::SPIRType::BaseType::Half:
754 .byte_size =
sizeof(
Half),
756 case spirv_cross::SPIRType::BaseType::UInt:
759 .byte_size =
sizeof(uint32_t),
761 case spirv_cross::SPIRType::BaseType::Int:
764 .byte_size =
sizeof(int32_t),
786 auto struct_size = 0u;
787 for (
const auto& member : members) {
788 struct_size += member.byte_length;
793 std::vector<StructMember> Reflector::ReadStructMembers(
794 const spirv_cross::TypeID& type_id)
const {
795 const auto& struct_type = compiler_->get_type(type_id);
796 FML_CHECK(struct_type.basetype == spirv_cross::SPIRType::BaseType::Struct);
798 std::vector<StructMember> result;
800 size_t current_byte_offset = 0;
801 size_t max_member_alignment = 0;
803 for (
size_t i = 0; i < struct_type.member_types.size(); i++) {
804 const auto& member = compiler_->get_type(struct_type.member_types[i]);
805 const auto struct_member_offset =
806 compiler_->type_struct_member_offset(struct_type, i);
807 auto array_elements = GetArrayElements(member);
809 if (struct_member_offset > current_byte_offset) {
810 const auto alignment_pad = struct_member_offset - current_byte_offset;
811 result.emplace_back(StructMember{
813 spirv_cross::SPIRType::BaseType::Void,
815 GetMemberNameAtIndex(struct_type, i).c_str()),
822 current_byte_offset += alignment_pad;
825 max_member_alignment =
826 std::max<size_t>(max_member_alignment,
827 (member.width / 8) * member.columns * member.vecsize);
829 FML_CHECK(current_byte_offset == struct_member_offset);
832 if (member.basetype == spirv_cross::SPIRType::BaseType::Struct) {
835 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
839 uint32_t element_padding = stride - size;
840 result.emplace_back(StructMember{
841 compiler_->get_name(member.self),
843 GetMemberNameAtIndex(struct_type, i),
844 struct_member_offset,
846 stride * array_elements.value_or(1),
850 current_byte_offset += stride * array_elements.value_or(1);
856 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
857 member.width ==
sizeof(
Scalar) * 8 &&
858 member.columns == 4 &&
861 uint32_t stride = GetArrayStride<sizeof(Matrix)>(struct_type, member, i);
862 uint32_t element_padding = stride -
sizeof(Matrix);
863 result.emplace_back(StructMember{
866 GetMemberNameAtIndex(struct_type, i),
867 struct_member_offset,
869 stride * array_elements.value_or(1),
873 current_byte_offset += stride * array_elements.value_or(1);
878 if (member.basetype == spirv_cross::SPIRType::BaseType::UInt &&
879 member.width ==
sizeof(uint32_t) * 8 &&
880 member.columns == 1 &&
884 GetArrayStride<sizeof(UintPoint32)>(struct_type, member, i);
885 uint32_t element_padding = stride -
sizeof(
UintPoint32);
886 result.emplace_back(StructMember{
889 GetMemberNameAtIndex(struct_type, i),
890 struct_member_offset,
892 stride * array_elements.value_or(1),
896 current_byte_offset += stride * array_elements.value_or(1);
901 if (member.basetype == spirv_cross::SPIRType::BaseType::Int &&
902 member.width ==
sizeof(int32_t) * 8 &&
903 member.columns == 1 &&
907 GetArrayStride<sizeof(IPoint32)>(struct_type, member, i);
908 uint32_t element_padding = stride -
sizeof(
IPoint32);
909 result.emplace_back(StructMember{
912 GetMemberNameAtIndex(struct_type, i),
913 struct_member_offset,
915 stride * array_elements.value_or(1),
919 current_byte_offset += stride * array_elements.value_or(1);
924 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
925 member.width ==
sizeof(
float) * 8 &&
926 member.columns == 1 &&
929 uint32_t stride = GetArrayStride<sizeof(Point)>(struct_type, member, i);
930 uint32_t element_padding = stride -
sizeof(
Point);
931 result.emplace_back(StructMember{
934 GetMemberNameAtIndex(struct_type, i),
935 struct_member_offset,
937 stride * array_elements.value_or(1),
941 current_byte_offset += stride * array_elements.value_or(1);
946 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
947 member.width ==
sizeof(
float) * 8 &&
948 member.columns == 1 &&
951 uint32_t stride = GetArrayStride<sizeof(Vector3)>(struct_type, member, i);
952 uint32_t element_padding = stride -
sizeof(Vector3);
953 result.emplace_back(StructMember{
956 GetMemberNameAtIndex(struct_type, i),
957 struct_member_offset,
959 stride * array_elements.value_or(1),
963 current_byte_offset += stride * array_elements.value_or(1);
968 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
969 member.width ==
sizeof(
float) * 8 &&
970 member.columns == 1 &&
973 uint32_t stride = GetArrayStride<sizeof(Vector4)>(struct_type, member, i);
974 uint32_t element_padding = stride -
sizeof(Vector4);
975 result.emplace_back(StructMember{
978 GetMemberNameAtIndex(struct_type, i),
979 struct_member_offset,
981 stride * array_elements.value_or(1),
985 current_byte_offset += stride * array_elements.value_or(1);
990 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
991 member.width ==
sizeof(Half) * 8 &&
992 member.columns == 1 &&
996 GetArrayStride<sizeof(HalfVector2)>(struct_type, member, i);
997 uint32_t element_padding = stride -
sizeof(HalfVector2);
998 result.emplace_back(StructMember{
1001 GetMemberNameAtIndex(struct_type, i),
1002 struct_member_offset,
1003 sizeof(HalfVector2),
1004 stride * array_elements.value_or(1),
1008 current_byte_offset += stride * array_elements.value_or(1);
1013 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1014 member.width ==
sizeof(Half) * 8 &&
1015 member.columns == 1 &&
1019 GetArrayStride<sizeof(HalfVector3)>(struct_type, member, i);
1020 uint32_t element_padding = stride -
sizeof(HalfVector3);
1021 result.emplace_back(StructMember{
1024 GetMemberNameAtIndex(struct_type, i),
1025 struct_member_offset,
1026 sizeof(HalfVector3),
1027 stride * array_elements.value_or(1),
1031 current_byte_offset += stride * array_elements.value_or(1);
1036 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1037 member.width ==
sizeof(Half) * 8 &&
1038 member.columns == 1 &&
1042 GetArrayStride<sizeof(HalfVector4)>(struct_type, member, i);
1043 uint32_t element_padding = stride -
sizeof(HalfVector4);
1044 result.emplace_back(StructMember{
1047 GetMemberNameAtIndex(struct_type, i),
1048 struct_member_offset,
1049 sizeof(HalfVector4),
1050 stride * array_elements.value_or(1),
1054 current_byte_offset += stride * array_elements.value_or(1);
1061 if (maybe_known_type.has_value() &&
1062 member.columns == 1 &&
1065 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
1067 stride = maybe_known_type.value().byte_size;
1069 uint32_t element_padding = stride - maybe_known_type.value().byte_size;
1071 result.emplace_back(StructMember{
1072 maybe_known_type.value().name,
1074 GetMemberNameAtIndex(struct_type, i),
1075 struct_member_offset,
1076 maybe_known_type.value().byte_size,
1077 stride * array_elements.value_or(1),
1081 current_byte_offset += stride * array_elements.value_or(1);
1089 const size_t size = (member.width * member.columns * member.vecsize) / 8u;
1090 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
1094 auto element_padding = stride - size;
1095 result.emplace_back(StructMember{
1098 GetMemberNameAtIndex(struct_type, i),
1099 struct_member_offset,
1101 stride * array_elements.value_or(1),
1105 current_byte_offset += stride * array_elements.value_or(1);
1110 if (max_member_alignment > 0u) {
1111 const auto struct_length = current_byte_offset;
1113 const auto excess = struct_length % max_member_alignment;
1115 const auto padding = max_member_alignment - excess;
1116 result.emplace_back(StructMember{
1118 spirv_cross::SPIRType::BaseType::Void,
1120 current_byte_offset,
1133 std::optional<Reflector::StructDefinition> Reflector::ReflectStructDefinition(
1134 const spirv_cross::TypeID& type_id)
const {
1135 const auto&
type = compiler_->get_type(type_id);
1136 if (
type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
1137 return std::nullopt;
1140 const auto struct_name = compiler_->get_name(type_id);
1141 if (struct_name.find(
"_RESERVED_IDENTIFIER_") != std::string::npos) {
1142 return std::nullopt;
1145 auto struct_members = ReadStructMembers(type_id);
1148 StructDefinition struc;
1149 struc.name = struct_name;
1150 struc.byte_length = reflected_struct_size;
1151 struc.members = std::move(struct_members);
1155 nlohmann::json::object_t Reflector::EmitStructDefinition(
1156 std::optional<Reflector::StructDefinition> struc)
const {
1157 nlohmann::json::object_t result;
1158 result[
"name"] = struc->name;
1159 result[
"byte_length"] = struc->byte_length;
1160 auto& members = result[
"members"] = nlohmann::json::array_t{};
1161 for (
const auto& struct_member : struc->members) {
1162 auto& member = members.emplace_back(nlohmann::json::object_t{});
1163 member[
"name"] = struct_member.name;
1164 member[
"type"] = struct_member.type;
1165 member[
"base_type"] =
1167 member[
"offset"] = struct_member.offset;
1168 member[
"byte_length"] = struct_member.byte_length;
1169 if (struct_member.array_elements.has_value()) {
1170 member[
"array_elements"] = struct_member.array_elements.value();
1172 member[
"array_elements"] =
"std::nullopt";
1174 member[
"element_padding"] = struct_member.element_padding;
1187 const spirv_cross::Compiler& compiler,
1188 const spirv_cross::Resource* resource) {
1191 const auto&
type = compiler.get_type(resource->type_id);
1193 const auto total_size =
type.columns *
type.vecsize *
type.width / 8u;
1196 if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1197 type.columns == 1u &&
type.vecsize == 2u &&
1198 type.width ==
sizeof(
float) * 8u) {
1200 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1201 type.columns == 1u &&
type.vecsize == 4u &&
1202 type.width ==
sizeof(
float) * 8u) {
1204 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1205 type.columns == 1u &&
type.vecsize == 3u &&
1206 type.width ==
sizeof(
float) * 8u) {
1208 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1209 type.columns == 1u &&
type.vecsize == 1u &&
1210 type.width ==
sizeof(
float) * 8u) {
1212 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Int &&
1213 type.columns == 1u &&
type.vecsize == 1u &&
1214 type.width ==
sizeof(int32_t) * 8u) {
1224 std::optional<Reflector::StructDefinition>
1225 Reflector::ReflectPerVertexStructDefinition(
1226 const spirv_cross::SmallVector<spirv_cross::Resource>& stage_inputs)
const {
1229 if (stage_inputs.empty()) {
1230 return std::nullopt;
1234 std::set<uint32_t> locations;
1235 for (
const auto& input : stage_inputs) {
1236 auto location = compiler_->get_decoration(
1237 input.id, spv::Decoration::DecorationLocation);
1238 if (locations.count(location) != 0) {
1240 return std::nullopt;
1242 locations.insert(location);
1245 for (
size_t i = 0; i < locations.size(); i++) {
1246 if (locations.count(i) != 1) {
1251 return std::nullopt;
1255 auto input_for_location =
1256 [&](uint32_t queried_location) ->
const spirv_cross::Resource* {
1257 for (
const auto& input : stage_inputs) {
1258 auto location = compiler_->get_decoration(
1259 input.id, spv::Decoration::DecorationLocation);
1260 if (location == queried_location) {
1269 StructDefinition struc;
1270 struc.name =
"PerVertexData";
1271 struc.byte_length = 0u;
1272 for (
size_t i = 0; i < locations.size(); i++) {
1273 auto resource = input_for_location(i);
1274 if (resource ==
nullptr) {
1275 return std::nullopt;
1277 const auto vertex_type =
1280 auto member = StructMember{
1281 vertex_type.type_name,
1282 vertex_type.base_type,
1283 vertex_type.variable_name,
1285 vertex_type.byte_length,
1286 vertex_type.byte_length,
1290 struc.byte_length += vertex_type.byte_length;
1291 struc.members.emplace_back(std::move(member));
1296 std::optional<std::string> Reflector::GetMemberNameAtIndexIfExists(
1297 const spirv_cross::SPIRType& parent_type,
1298 size_t index)
const {
1299 if (parent_type.type_alias != 0) {
1300 return GetMemberNameAtIndexIfExists(
1301 compiler_->get_type(parent_type.type_alias), index);
1304 if (
auto found = ir_->meta.find(parent_type.self); found != ir_->meta.end()) {
1305 const auto& members = found->second.members;
1306 if (index < members.size() && !members[index].alias.empty()) {
1307 return members[index].alias;
1310 return std::nullopt;
1313 std::string Reflector::GetMemberNameAtIndex(
1314 const spirv_cross::SPIRType& parent_type,
1316 std::string suffix)
const {
1317 if (
auto name = GetMemberNameAtIndexIfExists(parent_type, index);
1319 return name.value();
1321 static std::atomic_size_t sUnnamedMembersID;
1322 std::stringstream stream;
1323 stream <<
"unnamed_" << sUnnamedMembersID++ << suffix;
1324 return stream.str();
1327 std::vector<Reflector::BindPrototype> Reflector::ReflectBindPrototypes(
1328 const spirv_cross::ShaderResources& resources,
1329 spv::ExecutionModel execution_model)
const {
1330 std::vector<BindPrototype> prototypes;
1331 for (
const auto& uniform_buffer : resources.uniform_buffers) {
1332 auto& proto = prototypes.emplace_back(BindPrototype{});
1333 proto.return_type =
"bool";
1335 proto.descriptor_type =
"DescriptorType::kUniformBuffer";
1337 std::stringstream stream;
1338 stream <<
"Bind uniform buffer for resource named " << uniform_buffer.name
1340 proto.docstring = stream.str();
1342 proto.args.push_back(BindPrototypeArgument{
1343 .type_name =
"ResourceBinder&",
1344 .argument_name =
"command",
1346 proto.args.push_back(BindPrototypeArgument{
1347 .type_name =
"BufferView",
1348 .argument_name =
"view",
1351 for (
const auto& storage_buffer : resources.storage_buffers) {
1352 auto& proto = prototypes.emplace_back(BindPrototype{});
1353 proto.return_type =
"bool";
1355 proto.descriptor_type =
"DescriptorType::kStorageBuffer";
1357 std::stringstream stream;
1358 stream <<
"Bind storage buffer for resource named " << storage_buffer.name
1360 proto.docstring = stream.str();
1362 proto.args.push_back(BindPrototypeArgument{
1363 .type_name =
"ResourceBinder&",
1364 .argument_name =
"command",
1366 proto.args.push_back(BindPrototypeArgument{
1367 .type_name =
"BufferView",
1368 .argument_name =
"view",
1371 for (
const auto& sampled_image : resources.sampled_images) {
1372 auto& proto = prototypes.emplace_back(BindPrototype{});
1373 proto.return_type =
"bool";
1375 proto.descriptor_type =
"DescriptorType::kSampledImage";
1377 std::stringstream stream;
1378 stream <<
"Bind combined image sampler for resource named "
1379 << sampled_image.name <<
".";
1380 proto.docstring = stream.str();
1382 proto.args.push_back(BindPrototypeArgument{
1383 .type_name =
"ResourceBinder&",
1384 .argument_name =
"command",
1386 proto.args.push_back(BindPrototypeArgument{
1387 .type_name =
"std::shared_ptr<const Texture>",
1388 .argument_name =
"texture",
1390 proto.args.push_back(BindPrototypeArgument{
1391 .type_name =
"const std::unique_ptr<const Sampler>&",
1392 .argument_name =
"sampler",
1395 for (
const auto& separate_image : resources.separate_images) {
1396 auto& proto = prototypes.emplace_back(BindPrototype{});
1397 proto.return_type =
"bool";
1399 proto.descriptor_type =
"DescriptorType::kImage";
1401 std::stringstream stream;
1402 stream <<
"Bind separate image for resource named " << separate_image.name
1404 proto.docstring = stream.str();
1406 proto.args.push_back(BindPrototypeArgument{
1407 .type_name =
"Command&",
1408 .argument_name =
"command",
1410 proto.args.push_back(BindPrototypeArgument{
1411 .type_name =
"std::shared_ptr<const Texture>",
1412 .argument_name =
"texture",
1415 for (
const auto& separate_sampler : resources.separate_samplers) {
1416 auto& proto = prototypes.emplace_back(BindPrototype{});
1417 proto.return_type =
"bool";
1419 proto.descriptor_type =
"DescriptorType::kSampler";
1421 std::stringstream stream;
1422 stream <<
"Bind separate sampler for resource named "
1423 << separate_sampler.name <<
".";
1424 proto.docstring = stream.str();
1426 proto.args.push_back(BindPrototypeArgument{
1427 .type_name =
"Command&",
1428 .argument_name =
"command",
1430 proto.args.push_back(BindPrototypeArgument{
1431 .type_name =
"std::shared_ptr<const Sampler>",
1432 .argument_name =
"sampler",
1438 nlohmann::json::array_t Reflector::EmitBindPrototypes(
1439 const spirv_cross::ShaderResources& resources,
1440 spv::ExecutionModel execution_model)
const {
1441 const auto prototypes = ReflectBindPrototypes(resources, execution_model);
1442 nlohmann::json::array_t result;
1443 for (
const auto& res : prototypes) {
1444 auto& item = result.emplace_back(nlohmann::json::object_t{});
1445 item[
"return_type"] = res.return_type;
1446 item[
"name"] = res.name;
1447 item[
"docstring"] = res.docstring;
1448 item[
"descriptor_type"] = res.descriptor_type;
1449 auto& args = item[
"args"] = nlohmann::json::array_t{};
1450 for (
const auto& arg : res.args) {
1451 auto& json_arg = args.emplace_back(nlohmann::json::object_t{});
1452 json_arg[
"type_name"] = arg.type_name;
1453 json_arg[
"argument_name"] = arg.argument_name;