14 #include "flutter/fml/closure.h"
15 #include "flutter/fml/logging.h"
30 using Type = spirv_cross::SPIRType::BaseType;
33 return "ShaderType::kVoid";
35 return "ShaderType::kBoolean";
37 return "ShaderType::kSignedByte";
39 return "ShaderType::kUnsignedByte";
41 return "ShaderType::kSignedShort";
43 return "ShaderType::kUnsignedShort";
45 return "ShaderType::kSignedInt";
47 return "ShaderType::kUnsignedInt";
49 return "ShaderType::kSignedInt64";
51 return "ShaderType::kUnsignedInt64";
52 case Type::AtomicCounter:
53 return "ShaderType::kAtomicCounter";
55 return "ShaderType::kHalfFloat";
57 return "ShaderType::kFloat";
59 return "ShaderType::kDouble";
61 return "ShaderType::kStruct";
63 return "ShaderType::kImage";
64 case Type::SampledImage:
65 return "ShaderType::kSampledImage";
67 return "ShaderType::kSampler";
69 return "ShaderType::kUnknown";
75 case spv::ExecutionModel::ExecutionModelVertex:
77 case spv::ExecutionModel::ExecutionModelFragment:
79 case spv::ExecutionModel::ExecutionModelTessellationControl:
80 return "tessellation_control";
81 case spv::ExecutionModel::ExecutionModelTessellationEvaluation:
82 return "tessellation_evaluation";
83 case spv::ExecutionModel::ExecutionModelGLCompute:
91 if (str ==
"vertex") {
92 return "ShaderStage::kVertex";
95 if (str ==
"fragment") {
96 return "ShaderStage::kFragment";
99 if (str ==
"tessellation_control") {
100 return "ShaderStage::kTessellationControl";
103 if (str ==
"tessellation_evaluation") {
104 return "ShaderStage::kTessellationEvaluation";
107 if (str ==
"compute") {
108 return "ShaderStage::kCompute";
111 return "ShaderStage::kUnknown";
115 std::shared_ptr<const spirv_cross::ParsedIR> ir,
116 std::shared_ptr<fml::Mapping> shader_data,
118 : options_(
std::move(options)),
120 shader_data_(
std::move(shader_data)),
121 compiler_(
std::move(compiler)) {
122 if (!ir_ || !compiler_) {
126 if (
auto template_arguments = GenerateTemplateArguments();
127 template_arguments.has_value()) {
128 template_arguments_ =
129 std::make_unique<nlohmann::json>(std::move(template_arguments.value()));
134 reflection_header_ = GenerateReflectionHeader();
135 if (!reflection_header_) {
139 reflection_cc_ = GenerateReflectionCC();
140 if (!reflection_cc_) {
144 runtime_stage_data_ = GenerateRuntimeStageData();
145 if (!runtime_stage_data_) {
164 std::make_shared<std::string>(template_arguments_->dump(2u));
166 return std::make_shared<fml::NonOwnedMapping>(
167 reinterpret_cast<const uint8_t*
>(json_string->data()),
168 json_string->size(), [json_string](
auto,
auto) {});
172 return reflection_header_;
176 return reflection_cc_;
180 return runtime_stage_data_;
183 std::optional<nlohmann::json> Reflector::GenerateTemplateArguments()
const {
186 const auto& entrypoints = compiler_->get_entry_points_and_stages();
187 if (entrypoints.size() != 1) {
188 VALIDATION_LOG <<
"Incorrect number of entrypoints in the shader. Found "
189 << entrypoints.size() <<
" but expected 1.";
193 auto execution_model = entrypoints.front().execution_model;
201 const auto shader_resources = compiler_->get_shader_resources();
205 auto& buffers = root[
"buffers"] = nlohmann::json::array_t{};
206 if (
auto uniform_buffers_json =
207 ReflectResources(shader_resources.uniform_buffers);
208 uniform_buffers_json.has_value()) {
209 for (
auto uniform_buffer : uniform_buffers_json.value()) {
210 uniform_buffer[
"descriptor_type"] =
"DescriptorType::kUniformBuffer";
211 buffers.emplace_back(std::move(uniform_buffer));
216 if (
auto storage_buffers_json =
217 ReflectResources(shader_resources.storage_buffers);
218 storage_buffers_json.has_value()) {
219 for (
auto uniform_buffer : storage_buffers_json.value()) {
220 uniform_buffer[
"descriptor_type"] =
"DescriptorType::kStorageBuffer";
221 buffers.emplace_back(std::move(uniform_buffer));
229 auto& stage_inputs = root[
"stage_inputs"] = nlohmann::json::array_t{};
230 if (
auto stage_inputs_json = ReflectResources(
231 shader_resources.stage_inputs,
232 execution_model == spv::ExecutionModelVertex);
233 stage_inputs_json.has_value()) {
234 stage_inputs = std::move(stage_inputs_json.value());
241 auto combined_sampled_images =
242 ReflectResources(shader_resources.sampled_images);
243 auto images = ReflectResources(shader_resources.separate_images);
244 auto samplers = ReflectResources(shader_resources.separate_samplers);
245 if (!combined_sampled_images.has_value() || !images.has_value() ||
246 !samplers.has_value()) {
249 auto& sampled_images = root[
"sampled_images"] = nlohmann::json::array_t{};
250 for (
auto value : combined_sampled_images.value()) {
251 value[
"descriptor_type"] =
"DescriptorType::kSampledImage";
252 sampled_images.emplace_back(std::move(value));
254 for (
auto value : images.value()) {
255 value[
"descriptor_type"] =
"DescriptorType::kImage";
256 sampled_images.emplace_back(std::move(value));
258 for (
auto value : samplers.value()) {
259 value[
"descriptor_type"] =
"DescriptorType::kSampledSampler";
260 sampled_images.emplace_back(std::move(value));
264 if (
auto stage_outputs = ReflectResources(shader_resources.stage_outputs);
265 stage_outputs.has_value()) {
266 root[
"stage_outputs"] = std::move(stage_outputs.value());
272 auto& struct_definitions = root[
"struct_definitions"] =
273 nlohmann::json::array_t{};
274 if (entrypoints.front().execution_model ==
275 spv::ExecutionModel::ExecutionModelVertex &&
276 !shader_resources.stage_inputs.empty()) {
278 ReflectPerVertexStructDefinition(shader_resources.stage_inputs);
280 struct_definitions.emplace_back(EmitStructDefinition(struc.value()));
288 std::set<spirv_cross::ID> known_structs;
289 ir_->for_each_typed_id<spirv_cross::SPIRType>(
290 [&](uint32_t,
const spirv_cross::SPIRType& type) {
291 if (known_structs.find(type.self) != known_structs.end()) {
296 known_structs.insert(type.self);
297 if (
auto struc = ReflectStructDefinition(type.self);
299 struct_definitions.emplace_back(
300 EmitStructDefinition(struc.value()));
305 root[
"bind_prototypes"] =
306 EmitBindPrototypes(shader_resources, execution_model);
311 std::shared_ptr<fml::Mapping> Reflector::GenerateReflectionHeader()
const {
315 std::shared_ptr<fml::Mapping> Reflector::GenerateReflectionCC()
const {
319 std::shared_ptr<RuntimeStageData> Reflector::GenerateRuntimeStageData()
const {
320 const auto& entrypoints = compiler_->get_entry_points_and_stages();
321 if (entrypoints.size() != 1u) {
325 auto data = std::make_shared<RuntimeStageData>(
327 entrypoints.front().execution_model,
330 data->SetShaderData(shader_data_);
332 data->SetSkSLData(sksl_data_);
336 std::vector<spirv_cross::ID> uniforms =
339 for (
auto& sorted_id : uniforms) {
340 auto var = ir_->ids[sorted_id].get<spirv_cross::SPIRVariable>();
341 const auto spir_type = compiler_->get_type(var.basetype);
342 UniformDescription uniform_description;
343 uniform_description.name = compiler_->get_name(var.self);
344 uniform_description.location = compiler_->get_decoration(
345 var.self, spv::Decoration::DecorationLocation);
346 uniform_description.type = spir_type.basetype;
347 uniform_description.rows = spir_type.vecsize;
348 uniform_description.columns = spir_type.columns;
349 uniform_description.bit_width = spir_type.width;
350 uniform_description.array_elements = GetArrayElements(spir_type);
351 data->AddUniformDescription(std::move(uniform_description));
356 std::optional<uint32_t> Reflector::GetArrayElements(
357 const spirv_cross::SPIRType& type)
const {
358 if (type.array.empty()) {
361 FML_CHECK(type.array.size() == 1)
362 <<
"Multi-dimensional arrays are not supported.";
363 FML_CHECK(type.array_size_literal.front())
364 <<
"Must use a literal for array sizes.";
365 return type.array.front();
371 return "Metal Shading Language";
373 return "OpenGL Shading Language";
375 return "SkSL Shading Language";
380 std::shared_ptr<fml::Mapping> Reflector::InflateTemplate(
381 std::string_view tmpl)
const {
382 inja::Environment env;
383 env.set_trim_blocks(
true);
384 env.set_lstrip_blocks(
true);
386 env.add_callback(
"camel_case", 1u, [](inja::Arguments& args) {
390 env.add_callback(
"to_shader_stage", 1u, [](inja::Arguments& args) {
394 env.add_callback(
"get_generator_name", 0u,
395 [type = compiler_.
GetType()](inja::Arguments& args) {
399 auto inflated_template =
400 std::make_shared<std::string>(env.render(tmpl, *template_arguments_));
402 return std::make_shared<fml::NonOwnedMapping>(
403 reinterpret_cast<const uint8_t*
>(inflated_template->data()),
404 inflated_template->size(), [inflated_template](
auto,
auto) {});
407 std::vector<size_t> Reflector::ComputeOffsets(
408 const spirv_cross::SmallVector<spirv_cross::Resource>& resources)
const {
409 std::vector<size_t> offsets(resources.size(), 0);
410 if (resources.size() == 0) {
413 for (
const auto& resource : resources) {
414 const auto type = compiler_->get_type(resource.type_id);
415 auto location = compiler_->get_decoration(
416 resource.id, spv::Decoration::DecorationLocation);
418 if (location >= resources.size() || location < 0) {
421 offsets[location] = (type.width * type.vecsize) / 8;
423 for (
size_t i = 1; i < resources.size(); i++) {
424 offsets[i] += offsets[i - 1];
426 for (
size_t i = resources.size() - 1; i > 0; i--) {
427 offsets[i] = offsets[i - 1];
434 std::optional<nlohmann::json::object_t> Reflector::ReflectResource(
435 const spirv_cross::Resource& resource,
436 std::optional<size_t> offset)
const {
437 nlohmann::json::object_t result;
439 result[
"name"] = resource.name;
440 result[
"descriptor_set"] = compiler_->get_decoration(
441 resource.id, spv::Decoration::DecorationDescriptorSet);
442 result[
"binding"] = compiler_->get_decoration(
443 resource.id, spv::Decoration::DecorationBinding);
444 result[
"set"] = compiler_->get_decoration(
445 resource.id, spv::Decoration::DecorationDescriptorSet);
446 result[
"location"] = compiler_->get_decoration(
447 resource.id, spv::Decoration::DecorationLocation);
449 compiler_->get_decoration(resource.id, spv::Decoration::DecorationIndex);
454 auto type = ReflectType(resource.type_id);
455 if (!type.has_value()) {
458 result[
"type"] = std::move(type.value());
459 result[
"offset"] = offset.value_or(0u);
463 std::optional<nlohmann::json::object_t> Reflector::ReflectType(
464 const spirv_cross::TypeID& type_id)
const {
465 nlohmann::json::object_t result;
467 const auto type = compiler_->get_type(type_id);
470 result[
"bit_width"] = type.width;
471 result[
"vec_size"] = type.vecsize;
472 result[
"columns"] = type.columns;
473 auto& members = result[
"members"] = nlohmann::json::array_t{};
474 if (type.basetype == spirv_cross::SPIRType::BaseType::Struct) {
475 for (
const auto& struct_member : ReadStructMembers(type_id)) {
476 auto member = nlohmann::json::object_t{};
477 member[
"name"] = struct_member.name;
478 member[
"type"] = struct_member.type;
479 member[
"base_type"] = struct_member.base_type;
480 member[
"offset"] = struct_member.offset;
481 member[
"size"] = struct_member.size;
482 member[
"byte_length"] = struct_member.byte_length;
483 if (struct_member.array_elements.has_value()) {
484 member[
"array_elements"] = struct_member.array_elements.value();
486 member[
"array_elements"] =
"std::nullopt";
488 members.emplace_back(std::move(member));
495 std::optional<nlohmann::json::array_t> Reflector::ReflectResources(
496 const spirv_cross::SmallVector<spirv_cross::Resource>& resources,
497 bool compute_offsets)
const {
498 nlohmann::json::array_t result;
499 result.reserve(resources.size());
500 std::vector<size_t> offsets;
501 if (compute_offsets) {
502 offsets = ComputeOffsets(resources);
504 for (
const auto& resource : resources) {
505 std::optional<size_t> maybe_offset = std::nullopt;
506 if (compute_offsets) {
507 auto location = compiler_->get_decoration(
508 resource.id, spv::Decoration::DecorationLocation);
509 maybe_offset = offsets[location];
511 if (
auto reflected = ReflectResource(resource, maybe_offset);
512 reflected.has_value()) {
513 result.emplace_back(std::move(reflected.value()));
522 std::stringstream stream;
523 stream <<
"Padding<" << size <<
">";
533 spirv_cross::SPIRType::BaseType type) {
535 case spirv_cross::SPIRType::BaseType::Boolean:
538 .byte_size =
sizeof(bool),
540 case spirv_cross::SPIRType::BaseType::Float:
543 .byte_size =
sizeof(
Scalar),
545 case spirv_cross::SPIRType::BaseType::Half:
548 .byte_size =
sizeof(
Half),
550 case spirv_cross::SPIRType::BaseType::UInt:
553 .byte_size =
sizeof(uint32_t),
555 case spirv_cross::SPIRType::BaseType::Int:
558 .byte_size =
sizeof(int32_t),
580 auto struct_size = 0u;
581 for (
const auto& member : members) {
582 struct_size += member.byte_length;
587 std::vector<StructMember> Reflector::ReadStructMembers(
588 const spirv_cross::TypeID& type_id)
const {
589 const auto& struct_type = compiler_->get_type(type_id);
590 FML_CHECK(struct_type.basetype == spirv_cross::SPIRType::BaseType::Struct);
592 std::vector<StructMember> result;
594 size_t current_byte_offset = 0;
595 size_t max_member_alignment = 0;
597 for (
size_t i = 0; i < struct_type.member_types.size(); i++) {
598 const auto& member = compiler_->get_type(struct_type.member_types[i]);
599 const auto struct_member_offset =
600 compiler_->type_struct_member_offset(struct_type, i);
601 auto array_elements = GetArrayElements(member);
603 if (struct_member_offset > current_byte_offset) {
604 const auto alignment_pad = struct_member_offset - current_byte_offset;
605 result.emplace_back(StructMember{
609 GetMemberNameAtIndex(struct_type, i).c_str()),
616 current_byte_offset += alignment_pad;
619 max_member_alignment =
620 std::max<size_t>(max_member_alignment,
621 (member.width / 8) * member.columns * member.vecsize);
623 FML_CHECK(current_byte_offset == struct_member_offset);
626 if (member.basetype == spirv_cross::SPIRType::BaseType::Struct) {
629 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
633 uint32_t element_padding = stride - size;
634 result.emplace_back(StructMember{
635 compiler_->get_name(member.self),
637 GetMemberNameAtIndex(struct_type, i),
638 struct_member_offset,
640 stride * array_elements.value_or(1),
644 current_byte_offset += stride * array_elements.value_or(1);
650 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
651 member.width ==
sizeof(
Scalar) * 8 &&
652 member.columns == 4 &&
655 uint32_t stride = GetArrayStride<sizeof(Matrix)>(struct_type, member, i);
656 uint32_t element_padding = stride -
sizeof(Matrix);
657 result.emplace_back(StructMember{
660 GetMemberNameAtIndex(struct_type, i),
661 struct_member_offset,
663 stride * array_elements.value_or(1),
667 current_byte_offset += stride * array_elements.value_or(1);
672 if (member.basetype == spirv_cross::SPIRType::BaseType::UInt &&
673 member.width ==
sizeof(uint32_t) * 8 &&
674 member.columns == 1 &&
678 GetArrayStride<sizeof(UintPoint32)>(struct_type, member, i);
679 uint32_t element_padding = stride -
sizeof(
UintPoint32);
680 result.emplace_back(StructMember{
683 GetMemberNameAtIndex(struct_type, i),
684 struct_member_offset,
686 stride * array_elements.value_or(1),
690 current_byte_offset += stride * array_elements.value_or(1);
695 if (member.basetype == spirv_cross::SPIRType::BaseType::Int &&
696 member.width ==
sizeof(int32_t) * 8 &&
697 member.columns == 1 &&
701 GetArrayStride<sizeof(IPoint32)>(struct_type, member, i);
702 uint32_t element_padding = stride -
sizeof(
IPoint32);
703 result.emplace_back(StructMember{
706 GetMemberNameAtIndex(struct_type, i),
707 struct_member_offset,
709 stride * array_elements.value_or(1),
713 current_byte_offset += stride * array_elements.value_or(1);
718 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
719 member.width ==
sizeof(
float) * 8 &&
720 member.columns == 1 &&
723 uint32_t stride = GetArrayStride<sizeof(Point)>(struct_type, member, i);
724 uint32_t element_padding = stride -
sizeof(
Point);
725 result.emplace_back(StructMember{
728 GetMemberNameAtIndex(struct_type, i),
729 struct_member_offset,
731 stride * array_elements.value_or(1),
735 current_byte_offset += stride * array_elements.value_or(1);
740 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
741 member.width ==
sizeof(
float) * 8 &&
742 member.columns == 1 &&
745 uint32_t stride = GetArrayStride<sizeof(Vector3)>(struct_type, member, i);
746 uint32_t element_padding = stride -
sizeof(Vector3);
747 result.emplace_back(StructMember{
750 GetMemberNameAtIndex(struct_type, i),
751 struct_member_offset,
753 stride * array_elements.value_or(1),
757 current_byte_offset += stride * array_elements.value_or(1);
762 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
763 member.width ==
sizeof(
float) * 8 &&
764 member.columns == 1 &&
767 uint32_t stride = GetArrayStride<sizeof(Vector4)>(struct_type, member, i);
768 uint32_t element_padding = stride -
sizeof(Vector4);
769 result.emplace_back(StructMember{
772 GetMemberNameAtIndex(struct_type, i),
773 struct_member_offset,
775 stride * array_elements.value_or(1),
779 current_byte_offset += stride * array_elements.value_or(1);
784 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
785 member.width ==
sizeof(Half) * 8 &&
786 member.columns == 1 &&
790 GetArrayStride<sizeof(HalfVector2)>(struct_type, member, i);
791 uint32_t element_padding = stride -
sizeof(HalfVector2);
792 result.emplace_back(StructMember{
795 GetMemberNameAtIndex(struct_type, i),
796 struct_member_offset,
798 stride * array_elements.value_or(1),
802 current_byte_offset += stride * array_elements.value_or(1);
807 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
808 member.width ==
sizeof(Half) * 8 &&
809 member.columns == 1 &&
813 GetArrayStride<sizeof(HalfVector3)>(struct_type, member, i);
814 uint32_t element_padding = stride -
sizeof(HalfVector3);
815 result.emplace_back(StructMember{
818 GetMemberNameAtIndex(struct_type, i),
819 struct_member_offset,
821 stride * array_elements.value_or(1),
825 current_byte_offset += stride * array_elements.value_or(1);
830 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
831 member.width ==
sizeof(Half) * 8 &&
832 member.columns == 1 &&
836 GetArrayStride<sizeof(HalfVector4)>(struct_type, member, i);
837 uint32_t element_padding = stride -
sizeof(HalfVector4);
838 result.emplace_back(StructMember{
841 GetMemberNameAtIndex(struct_type, i),
842 struct_member_offset,
844 stride * array_elements.value_or(1),
848 current_byte_offset += stride * array_elements.value_or(1);
855 if (maybe_known_type.has_value() &&
856 member.columns == 1 &&
859 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
861 stride = maybe_known_type.value().byte_size;
863 uint32_t element_padding = stride - maybe_known_type.value().byte_size;
865 result.emplace_back(StructMember{
866 maybe_known_type.value().name,
868 GetMemberNameAtIndex(struct_type, i),
869 struct_member_offset,
870 maybe_known_type.value().byte_size,
871 stride * array_elements.value_or(1),
875 current_byte_offset += stride * array_elements.value_or(1);
883 const size_t size = (member.width * member.columns * member.vecsize) / 8u;
884 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
888 auto element_padding = stride - size;
889 result.emplace_back(StructMember{
892 GetMemberNameAtIndex(struct_type, i),
893 struct_member_offset,
895 stride * array_elements.value_or(1),
899 current_byte_offset += stride * array_elements.value_or(1);
904 if (max_member_alignment > 0u) {
905 const auto struct_length = current_byte_offset;
907 const auto excess = struct_length % max_member_alignment;
909 const auto padding = max_member_alignment - excess;
910 result.emplace_back(StructMember{
913 spirv_cross::SPIRType::BaseType::Void),
928 std::optional<Reflector::StructDefinition> Reflector::ReflectStructDefinition(
929 const spirv_cross::TypeID& type_id)
const {
930 const auto& type = compiler_->get_type(type_id);
931 if (type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
935 const auto struct_name = compiler_->get_name(type_id);
936 if (struct_name.find(
"_RESERVED_IDENTIFIER_") != std::string::npos) {
940 auto struct_members = ReadStructMembers(type_id);
943 StructDefinition struc;
944 struc.name = struct_name;
945 struc.byte_length = reflected_struct_size;
946 struc.members = std::move(struct_members);
950 nlohmann::json::object_t Reflector::EmitStructDefinition(
951 std::optional<Reflector::StructDefinition> struc)
const {
952 nlohmann::json::object_t result;
953 result[
"name"] = struc->name;
954 result[
"byte_length"] = struc->byte_length;
955 auto& members = result[
"members"] = nlohmann::json::array_t{};
956 for (
const auto& struct_member : struc->members) {
957 auto& member = members.emplace_back(nlohmann::json::object_t{});
958 member[
"name"] = struct_member.name;
959 member[
"type"] = struct_member.type;
960 member[
"base_type"] = struct_member.base_type;
961 member[
"offset"] = struct_member.offset;
962 member[
"byte_length"] = struct_member.byte_length;
963 if (struct_member.array_elements.has_value()) {
964 member[
"array_elements"] = struct_member.array_elements.value();
966 member[
"array_elements"] =
"std::nullopt";
968 member[
"element_padding"] = struct_member.element_padding;
981 const spirv_cross::Compiler& compiler,
982 const spirv_cross::Resource* resource) {
985 const auto type = compiler.get_type(resource->type_id);
987 const auto total_size = type.columns * type.vecsize * type.width / 8u;
990 if (type.basetype == spirv_cross::SPIRType::BaseType::Float &&
991 type.columns == 1u && type.vecsize == 2u &&
992 type.width ==
sizeof(
float) * 8u) {
994 }
else if (type.basetype == spirv_cross::SPIRType::BaseType::Float &&
995 type.columns == 1u && type.vecsize == 4u &&
996 type.width ==
sizeof(
float) * 8u) {
998 }
else if (type.basetype == spirv_cross::SPIRType::BaseType::Float &&
999 type.columns == 1u && type.vecsize == 3u &&
1000 type.width ==
sizeof(
float) * 8u) {
1002 }
else if (type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1003 type.columns == 1u && type.vecsize == 1u &&
1004 type.width ==
sizeof(
float) * 8u) {
1006 }
else if (type.basetype == spirv_cross::SPIRType::BaseType::Int &&
1007 type.columns == 1u && type.vecsize == 1u &&
1008 type.width ==
sizeof(int32_t) * 8u) {
1018 std::optional<Reflector::StructDefinition>
1019 Reflector::ReflectPerVertexStructDefinition(
1020 const spirv_cross::SmallVector<spirv_cross::Resource>& stage_inputs)
const {
1023 if (stage_inputs.empty()) {
1024 return std::nullopt;
1028 std::set<uint32_t> locations;
1029 for (
const auto& input : stage_inputs) {
1030 auto location = compiler_->get_decoration(
1031 input.id, spv::Decoration::DecorationLocation);
1032 if (locations.count(location) != 0) {
1034 return std::nullopt;
1036 locations.insert(location);
1039 for (
size_t i = 0; i < locations.size(); i++) {
1040 if (locations.count(i) != 1) {
1045 return std::nullopt;
1049 auto input_for_location =
1050 [&](uint32_t queried_location) ->
const spirv_cross::Resource* {
1051 for (
const auto& input : stage_inputs) {
1052 auto location = compiler_->get_decoration(
1053 input.id, spv::Decoration::DecorationLocation);
1054 if (location == queried_location) {
1063 StructDefinition struc;
1064 struc.name =
"PerVertexData";
1065 struc.byte_length = 0u;
1066 for (
size_t i = 0; i < locations.size(); i++) {
1067 auto resource = input_for_location(i);
1068 if (resource ==
nullptr) {
1069 return std::nullopt;
1071 const auto vertex_type =
1074 auto member = StructMember{
1075 vertex_type.type_name,
1076 vertex_type.base_type_name,
1077 vertex_type.variable_name,
1079 vertex_type.byte_length,
1080 vertex_type.byte_length,
1084 struc.byte_length += vertex_type.byte_length;
1085 struc.members.emplace_back(std::move(member));
1090 std::optional<std::string> Reflector::GetMemberNameAtIndexIfExists(
1091 const spirv_cross::SPIRType& parent_type,
1092 size_t index)
const {
1093 if (parent_type.type_alias != 0) {
1094 return GetMemberNameAtIndexIfExists(
1095 compiler_->get_type(parent_type.type_alias), index);
1098 if (
auto found = ir_->meta.find(parent_type.self); found != ir_->meta.end()) {
1099 const auto& members = found->second.members;
1100 if (index < members.size() && !members[index].alias.empty()) {
1101 return members[index].alias;
1104 return std::nullopt;
1107 std::string Reflector::GetMemberNameAtIndex(
1108 const spirv_cross::SPIRType& parent_type,
1110 std::string suffix)
const {
1111 if (
auto name = GetMemberNameAtIndexIfExists(parent_type, index);
1113 return name.value();
1115 static std::atomic_size_t sUnnamedMembersID;
1116 std::stringstream stream;
1117 stream <<
"unnamed_" << sUnnamedMembersID++ << suffix;
1118 return stream.str();
1121 std::vector<Reflector::BindPrototype> Reflector::ReflectBindPrototypes(
1122 const spirv_cross::ShaderResources& resources,
1123 spv::ExecutionModel execution_model)
const {
1124 std::vector<BindPrototype> prototypes;
1125 for (
const auto& uniform_buffer : resources.uniform_buffers) {
1126 auto& proto = prototypes.emplace_back(BindPrototype{});
1127 proto.return_type =
"bool";
1130 std::stringstream stream;
1131 stream <<
"Bind uniform buffer for resource named " << uniform_buffer.name
1133 proto.docstring = stream.str();
1135 proto.args.push_back(BindPrototypeArgument{
1136 .type_name =
"ResourceBinder&",
1137 .argument_name =
"command",
1139 proto.args.push_back(BindPrototypeArgument{
1140 .type_name =
"BufferView",
1141 .argument_name =
"view",
1144 for (
const auto& storage_buffer : resources.storage_buffers) {
1145 auto& proto = prototypes.emplace_back(BindPrototype{});
1146 proto.return_type =
"bool";
1149 std::stringstream stream;
1150 stream <<
"Bind storage buffer for resource named " << storage_buffer.name
1152 proto.docstring = stream.str();
1154 proto.args.push_back(BindPrototypeArgument{
1155 .type_name =
"ResourceBinder&",
1156 .argument_name =
"command",
1158 proto.args.push_back(BindPrototypeArgument{
1159 .type_name =
"BufferView",
1160 .argument_name =
"view",
1163 for (
const auto& sampled_image : resources.sampled_images) {
1164 auto& proto = prototypes.emplace_back(BindPrototype{});
1165 proto.return_type =
"bool";
1168 std::stringstream stream;
1169 stream <<
"Bind combined image sampler for resource named "
1170 << sampled_image.name <<
".";
1171 proto.docstring = stream.str();
1173 proto.args.push_back(BindPrototypeArgument{
1174 .type_name =
"ResourceBinder&",
1175 .argument_name =
"command",
1177 proto.args.push_back(BindPrototypeArgument{
1178 .type_name =
"std::shared_ptr<const Texture>",
1179 .argument_name =
"texture",
1181 proto.args.push_back(BindPrototypeArgument{
1182 .type_name =
"std::shared_ptr<const Sampler>",
1183 .argument_name =
"sampler",
1186 for (
const auto& separate_image : resources.separate_images) {
1187 auto& proto = prototypes.emplace_back(BindPrototype{});
1188 proto.return_type =
"bool";
1191 std::stringstream stream;
1192 stream <<
"Bind separate image for resource named " << separate_image.name
1194 proto.docstring = stream.str();
1196 proto.args.push_back(BindPrototypeArgument{
1197 .type_name =
"Command&",
1198 .argument_name =
"command",
1200 proto.args.push_back(BindPrototypeArgument{
1201 .type_name =
"std::shared_ptr<const Texture>",
1202 .argument_name =
"texture",
1205 for (
const auto& separate_sampler : resources.separate_samplers) {
1206 auto& proto = prototypes.emplace_back(BindPrototype{});
1207 proto.return_type =
"bool";
1210 std::stringstream stream;
1211 stream <<
"Bind separate sampler for resource named "
1212 << separate_sampler.name <<
".";
1213 proto.docstring = stream.str();
1215 proto.args.push_back(BindPrototypeArgument{
1216 .type_name =
"Command&",
1217 .argument_name =
"command",
1219 proto.args.push_back(BindPrototypeArgument{
1220 .type_name =
"std::shared_ptr<const Sampler>",
1221 .argument_name =
"sampler",
1227 nlohmann::json::array_t Reflector::EmitBindPrototypes(
1228 const spirv_cross::ShaderResources& resources,
1229 spv::ExecutionModel execution_model)
const {
1230 const auto prototypes = ReflectBindPrototypes(resources, execution_model);
1231 nlohmann::json::array_t result;
1232 for (
const auto& res : prototypes) {
1233 auto& item = result.emplace_back(nlohmann::json::object_t{});
1234 item[
"return_type"] = res.return_type;
1235 item[
"name"] = res.name;
1236 item[
"docstring"] = res.docstring;
1237 auto& args = item[
"args"] = nlohmann::json::array_t{};
1238 for (
const auto& arg : res.args) {
1239 auto& json_arg = args.emplace_back(nlohmann::json::object_t{});
1240 json_arg[
"type_name"] = arg.type_name;
1241 json_arg[
"argument_name"] = arg.argument_name;