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) {
325 std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
328 if (!backend.has_value()) {
332 const auto& entrypoints = compiler_->get_entry_points_and_stages();
333 if (entrypoints.size() != 1u) {
337 auto data = std::make_unique<RuntimeStageData::Shader>();
339 data->stage = entrypoints.front().execution_model;
340 data->shader = shader_data_;
341 data->backend = backend.value();
344 std::vector<spirv_cross::ID> uniforms =
346 for (
auto& sorted_id : uniforms) {
347 auto var = ir_->ids[sorted_id].get<spirv_cross::SPIRVariable>();
348 const auto spir_type = compiler_->get_type(var.basetype);
349 UniformDescription uniform_description;
350 uniform_description.name = compiler_->get_name(var.self);
351 uniform_description.location = compiler_->get_decoration(
352 var.self, spv::Decoration::DecorationLocation);
353 uniform_description.binding =
354 compiler_->get_decoration(var.self, spv::Decoration::DecorationBinding);
355 uniform_description.type = spir_type.basetype;
356 uniform_description.rows = spir_type.vecsize;
357 uniform_description.columns = spir_type.columns;
358 uniform_description.bit_width = spir_type.width;
359 uniform_description.array_elements = GetArrayElements(spir_type);
361 spir_type.basetype ==
362 spirv_cross::SPIRType::BaseType::SampledImage)
363 <<
"Vulkan runtime effect had unexpected uniforms outside of the "
364 "uniform buffer object.";
365 data->uniforms.emplace_back(std::move(uniform_description));
368 const auto ubos = compiler_->get_shader_resources().uniform_buffers;
375 "for Vulkan runtime stage backend.";
379 const auto& ubo = ubos[0];
382 compiler_->get_decoration(ubo.id, spv::Decoration::DecorationBinding);
383 auto members = ReadStructMembers(ubo.type_id);
384 std::vector<uint8_t> struct_layout;
385 size_t float_count = 0;
387 for (
size_t i = 0; i < members.size(); i += 1) {
388 const auto& member = members[i];
389 std::vector<int> bytes;
390 switch (member.underlying_type) {
392 size_t padding_count =
393 (member.size +
sizeof(float) - 1) /
sizeof(float);
394 while (padding_count > 0) {
395 struct_layout.push_back(0);
401 if (member.array_elements > 1) {
404 for (
auto i = 0; i < member.array_elements; i++) {
405 for (
auto j = 0u; j < member.size /
sizeof(float); j++) {
406 struct_layout.push_back(1);
408 for (
auto j = 0u; j < member.element_padding /
sizeof(float);
410 struct_layout.push_back(0);
414 size_t member_float_count = member.byte_length /
sizeof(float);
415 float_count += member_float_count;
416 while (member_float_count > 0) {
417 struct_layout.push_back(1);
418 member_float_count--;
424 VALIDATION_LOG <<
"Non-floating-type struct member " << member.name
425 <<
" is not supported.";
429 data->uniforms.emplace_back(UniformDescription{
433 .type = spirv_cross::SPIRType::Struct,
434 .struct_layout = std::move(struct_layout),
435 .struct_float_count = float_count,
440 if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
441 const auto inputs = compiler_->get_shader_resources().stage_inputs;
442 auto input_offsets = ComputeOffsets(inputs);
443 for (
const auto& input : inputs) {
444 std::optional<size_t> offset = GetOffset(input.id, input_offsets);
446 const auto type = compiler_->get_type(input.type_id);
448 InputDescription input_description;
449 input_description.name = input.name;
450 input_description.location = compiler_->get_decoration(
451 input.id, spv::Decoration::DecorationLocation);
452 input_description.set = compiler_->get_decoration(
453 input.id, spv::Decoration::DecorationDescriptorSet);
454 input_description.binding = compiler_->get_decoration(
455 input.id, spv::Decoration::DecorationBinding);
456 input_description.type =
type.basetype;
457 input_description.bit_width =
type.width;
458 input_description.vec_size =
type.vecsize;
459 input_description.columns =
type.columns;
460 input_description.offset = offset.value_or(0u);
461 data->inputs.emplace_back(std::move(input_description));
468 std::shared_ptr<ShaderBundleData> Reflector::GenerateShaderBundleData()
const {
469 const auto& entrypoints = compiler_->get_entry_points_and_stages();
470 if (entrypoints.size() != 1u) {
474 auto data = std::make_shared<ShaderBundleData>(
476 entrypoints.front().execution_model,
479 data->SetShaderData(shader_data_);
481 const auto uniforms = compiler_->get_shader_resources().uniform_buffers;
482 for (
const auto& uniform : uniforms) {
483 ShaderBundleData::ShaderUniformStruct uniform_struct;
484 uniform_struct.name = uniform.name;
487 uniform_struct.set = compiler_->get_decoration(
488 uniform.id, spv::Decoration::DecorationDescriptorSet);
489 uniform_struct.binding = compiler_->get_decoration(
490 uniform.id, spv::Decoration::DecorationBinding);
492 const auto type = compiler_->get_type(uniform.type_id);
493 if (
type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
494 std::cerr <<
"Error: Uniform \"" << uniform.name
495 <<
"\" is not a struct. All Flutter GPU shader uniforms must "
501 size_t size_in_bytes = 0;
502 for (
const auto& struct_member : ReadStructMembers(uniform.type_id)) {
503 size_in_bytes += struct_member.byte_length;
507 ShaderBundleData::ShaderUniformStructField uniform_struct_field;
508 uniform_struct_field.name = struct_member.name;
509 uniform_struct_field.type = struct_member.base_type;
510 uniform_struct_field.offset_in_bytes = struct_member.offset;
511 uniform_struct_field.element_size_in_bytes = struct_member.size;
512 uniform_struct_field.total_size_in_bytes = struct_member.byte_length;
513 uniform_struct_field.array_elements = struct_member.array_elements;
514 uniform_struct.fields.push_back(uniform_struct_field);
516 uniform_struct.size_in_bytes = size_in_bytes;
518 data->AddUniformStruct(uniform_struct);
521 const auto sampled_images = compiler_->get_shader_resources().sampled_images;
522 for (
const auto& image : sampled_images) {
523 ShaderBundleData::ShaderUniformTexture uniform_texture;
524 uniform_texture.name = image.name;
527 uniform_texture.set = compiler_->get_decoration(
528 image.id, spv::Decoration::DecorationDescriptorSet);
529 uniform_texture.binding =
530 compiler_->get_decoration(image.id, spv::Decoration::DecorationBinding);
531 data->AddUniformTexture(uniform_texture);
535 if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
536 const auto inputs = compiler_->get_shader_resources().stage_inputs;
537 auto input_offsets = ComputeOffsets(inputs);
538 for (
const auto& input : inputs) {
539 std::optional<size_t> offset = GetOffset(input.id, input_offsets);
541 const auto type = compiler_->get_type(input.type_id);
543 InputDescription input_description;
544 input_description.name = input.name;
545 input_description.location = compiler_->get_decoration(
546 input.id, spv::Decoration::DecorationLocation);
547 input_description.set = compiler_->get_decoration(
548 input.id, spv::Decoration::DecorationDescriptorSet);
549 input_description.binding = compiler_->get_decoration(
550 input.id, spv::Decoration::DecorationBinding);
551 input_description.type =
type.basetype;
552 input_description.bit_width =
type.width;
553 input_description.vec_size =
type.vecsize;
554 input_description.columns =
type.columns;
555 input_description.offset = offset.value_or(0u);
556 data->AddInputDescription(std::move(input_description));
563 std::optional<uint32_t> Reflector::GetArrayElements(
564 const spirv_cross::SPIRType&
type)
const {
565 if (
type.array.empty()) {
568 FML_CHECK(
type.array.size() == 1)
569 <<
"Multi-dimensional arrays are not supported.";
570 FML_CHECK(
type.array_size_literal.front())
571 <<
"Must use a literal for array sizes.";
572 return type.array.front();
578 return "Metal Shading Language";
580 return "OpenGL Shading Language";
582 return "OpenGL Shading Language (Relaxed Vulkan Semantics)";
584 return "SkSL Shading Language";
589 std::shared_ptr<fml::Mapping> Reflector::InflateTemplate(
590 std::string_view tmpl)
const {
591 inja::Environment env;
592 env.set_trim_blocks(
true);
593 env.set_lstrip_blocks(
true);
595 env.add_callback(
"camel_case", 1u, [](inja::Arguments& args) {
596 return ToCamelCase(args.at(0u)->get<std::string>());
599 env.add_callback(
"to_shader_stage", 1u, [](inja::Arguments& args) {
603 env.add_callback(
"get_generator_name", 0u,
604 [
type = compiler_.
GetType()](inja::Arguments& args) {
608 auto inflated_template =
609 std::make_shared<std::string>(env.render(tmpl, *template_arguments_));
611 return std::make_shared<fml::NonOwnedMapping>(
612 reinterpret_cast<const uint8_t*
>(inflated_template->data()),
613 inflated_template->size(), [inflated_template](
auto,
auto) {});
616 std::vector<size_t> Reflector::ComputeOffsets(
617 const spirv_cross::SmallVector<spirv_cross::Resource>& resources)
const {
618 std::vector<size_t> offsets(resources.size(), 0);
619 if (resources.size() == 0) {
622 for (
const auto& resource : resources) {
623 const auto type = compiler_->get_type(resource.type_id);
624 auto location = compiler_->get_decoration(
625 resource.id, spv::Decoration::DecorationLocation);
627 if (location >= resources.size() || location < 0) {
630 offsets[location] = (
type.width *
type.vecsize) / 8;
632 for (
size_t i = 1; i < resources.size(); i++) {
633 offsets[i] += offsets[i - 1];
635 for (
size_t i = resources.size() - 1; i > 0; i--) {
636 offsets[i] = offsets[i - 1];
643 std::optional<size_t> Reflector::GetOffset(
645 const std::vector<size_t>& offsets)
const {
647 compiler_->get_decoration(
id, spv::Decoration::DecorationLocation);
648 if (location >= offsets.size()) {
651 return offsets[location];
654 std::optional<nlohmann::json::object_t> Reflector::ReflectResource(
655 const spirv_cross::Resource& resource,
656 std::optional<size_t> offset)
const {
657 nlohmann::json::object_t result;
659 result[
"name"] = resource.name;
660 result[
"descriptor_set"] = compiler_->get_decoration(
661 resource.id, spv::Decoration::DecorationDescriptorSet);
662 result[
"binding"] = compiler_->get_decoration(
663 resource.id, spv::Decoration::DecorationBinding);
664 result[
"set"] = compiler_->get_decoration(
665 resource.id, spv::Decoration::DecorationDescriptorSet);
666 result[
"location"] = compiler_->get_decoration(
667 resource.id, spv::Decoration::DecorationLocation);
669 compiler_->get_decoration(resource.id, spv::Decoration::DecorationIndex);
674 result[
"relaxed_precision"] =
675 compiler_->get_decoration(
676 resource.id, spv::Decoration::DecorationRelaxedPrecision) == 1;
677 result[
"offset"] = offset.value_or(0u);
678 auto type = ReflectType(resource.type_id);
679 if (!
type.has_value()) {
682 result[
"type"] = std::move(
type.value());
686 std::optional<nlohmann::json::object_t> Reflector::ReflectType(
687 const spirv_cross::TypeID& type_id)
const {
688 nlohmann::json::object_t result;
690 const auto type = compiler_->get_type(type_id);
693 result[
"bit_width"] =
type.width;
694 result[
"vec_size"] =
type.vecsize;
695 result[
"columns"] =
type.columns;
696 auto& members = result[
"members"] = nlohmann::json::array_t{};
697 if (
type.basetype == spirv_cross::SPIRType::BaseType::Struct) {
698 for (
const auto& struct_member : ReadStructMembers(type_id)) {
699 auto member = nlohmann::json::object_t{};
700 member[
"name"] = struct_member.name;
701 member[
"type"] = struct_member.type;
702 member[
"base_type"] =
704 member[
"offset"] = struct_member.offset;
705 member[
"size"] = struct_member.size;
706 member[
"byte_length"] = struct_member.byte_length;
707 if (struct_member.array_elements.has_value()) {
708 member[
"array_elements"] = struct_member.array_elements.value();
710 member[
"array_elements"] =
"std::nullopt";
712 members.emplace_back(std::move(member));
719 std::optional<nlohmann::json::array_t> Reflector::ReflectResources(
720 const spirv_cross::SmallVector<spirv_cross::Resource>& resources,
721 bool compute_offsets)
const {
722 nlohmann::json::array_t result;
723 result.reserve(resources.size());
724 std::vector<size_t> offsets;
725 if (compute_offsets) {
726 offsets = ComputeOffsets(resources);
728 for (
const auto& resource : resources) {
729 std::optional<size_t> maybe_offset = std::nullopt;
730 if (compute_offsets) {
731 maybe_offset = GetOffset(resource.id, offsets);
733 if (
auto reflected = ReflectResource(resource, maybe_offset);
734 reflected.has_value()) {
735 result.emplace_back(std::move(reflected.value()));
744 std::stringstream stream;
745 stream <<
"Padding<" << size <<
">";
755 spirv_cross::SPIRType::BaseType
type) {
757 case spirv_cross::SPIRType::BaseType::Boolean:
760 .byte_size =
sizeof(bool),
762 case spirv_cross::SPIRType::BaseType::Float:
765 .byte_size =
sizeof(
Scalar),
767 case spirv_cross::SPIRType::BaseType::Half:
770 .byte_size =
sizeof(
Half),
772 case spirv_cross::SPIRType::BaseType::UInt:
775 .byte_size =
sizeof(uint32_t),
777 case spirv_cross::SPIRType::BaseType::Int:
780 .byte_size =
sizeof(int32_t),
802 auto struct_size = 0u;
803 for (
const auto& member : members) {
804 struct_size += member.byte_length;
809 std::vector<StructMember> Reflector::ReadStructMembers(
810 const spirv_cross::TypeID& type_id)
const {
811 const auto& struct_type = compiler_->get_type(type_id);
812 FML_CHECK(struct_type.basetype == spirv_cross::SPIRType::BaseType::Struct);
814 std::vector<StructMember> result;
816 size_t current_byte_offset = 0;
817 size_t max_member_alignment = 0;
819 for (
size_t i = 0; i < struct_type.member_types.size(); i++) {
820 const auto& member = compiler_->get_type(struct_type.member_types[i]);
821 const auto struct_member_offset =
822 compiler_->type_struct_member_offset(struct_type, i);
823 auto array_elements = GetArrayElements(member);
825 if (struct_member_offset > current_byte_offset) {
826 const auto alignment_pad = struct_member_offset - current_byte_offset;
827 result.emplace_back(StructMember{
829 spirv_cross::SPIRType::BaseType::Void,
831 GetMemberNameAtIndex(struct_type, i).c_str()),
838 current_byte_offset += alignment_pad;
841 max_member_alignment =
842 std::max<size_t>(max_member_alignment,
843 (member.width / 8) * member.columns * member.vecsize);
845 FML_CHECK(current_byte_offset == struct_member_offset);
848 if (member.basetype == spirv_cross::SPIRType::BaseType::Struct) {
851 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
855 uint32_t element_padding = stride - size;
856 result.emplace_back(StructMember{
857 compiler_->get_name(member.self),
859 GetMemberNameAtIndex(struct_type, i),
860 struct_member_offset,
862 stride * array_elements.value_or(1),
866 current_byte_offset += stride * array_elements.value_or(1);
872 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
873 member.width ==
sizeof(
Scalar) * 8 &&
874 member.columns == 4 &&
877 uint32_t stride = GetArrayStride<sizeof(Matrix)>(struct_type, member, i);
878 uint32_t element_padding = stride -
sizeof(Matrix);
879 result.emplace_back(StructMember{
882 GetMemberNameAtIndex(struct_type, i),
883 struct_member_offset,
885 stride * array_elements.value_or(1),
889 current_byte_offset += stride * array_elements.value_or(1);
894 if (member.basetype == spirv_cross::SPIRType::BaseType::UInt &&
895 member.width ==
sizeof(uint32_t) * 8 &&
896 member.columns == 1 &&
900 GetArrayStride<sizeof(UintPoint32)>(struct_type, member, i);
901 uint32_t element_padding = stride -
sizeof(
UintPoint32);
902 result.emplace_back(StructMember{
905 GetMemberNameAtIndex(struct_type, i),
906 struct_member_offset,
908 stride * array_elements.value_or(1),
912 current_byte_offset += stride * array_elements.value_or(1);
917 if (member.basetype == spirv_cross::SPIRType::BaseType::Int &&
918 member.width ==
sizeof(int32_t) * 8 &&
919 member.columns == 1 &&
923 GetArrayStride<sizeof(IPoint32)>(struct_type, member, i);
924 uint32_t element_padding = stride -
sizeof(
IPoint32);
925 result.emplace_back(StructMember{
928 GetMemberNameAtIndex(struct_type, i),
929 struct_member_offset,
931 stride * array_elements.value_or(1),
935 current_byte_offset += stride * array_elements.value_or(1);
940 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
941 member.width ==
sizeof(
float) * 8 &&
942 member.columns == 1 &&
945 uint32_t stride = GetArrayStride<sizeof(Point)>(struct_type, member, i);
946 uint32_t element_padding = stride -
sizeof(
Point);
947 result.emplace_back(StructMember{
950 GetMemberNameAtIndex(struct_type, i),
951 struct_member_offset,
953 stride * array_elements.value_or(1),
957 current_byte_offset += stride * array_elements.value_or(1);
962 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
963 member.width ==
sizeof(
float) * 8 &&
964 member.columns == 1 &&
967 uint32_t stride = GetArrayStride<sizeof(Vector3)>(struct_type, member, i);
968 uint32_t element_padding = stride -
sizeof(Vector3);
969 result.emplace_back(StructMember{
972 GetMemberNameAtIndex(struct_type, i),
973 struct_member_offset,
975 stride * array_elements.value_or(1),
979 current_byte_offset += stride * array_elements.value_or(1);
984 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
985 member.width ==
sizeof(
float) * 8 &&
986 member.columns == 1 &&
989 uint32_t stride = GetArrayStride<sizeof(Vector4)>(struct_type, member, i);
990 uint32_t element_padding = stride -
sizeof(Vector4);
991 result.emplace_back(StructMember{
994 GetMemberNameAtIndex(struct_type, i),
995 struct_member_offset,
997 stride * array_elements.value_or(1),
1001 current_byte_offset += stride * array_elements.value_or(1);
1006 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1007 member.width ==
sizeof(Half) * 8 &&
1008 member.columns == 1 &&
1012 GetArrayStride<sizeof(HalfVector2)>(struct_type, member, i);
1013 uint32_t element_padding = stride -
sizeof(HalfVector2);
1014 result.emplace_back(StructMember{
1017 GetMemberNameAtIndex(struct_type, i),
1018 struct_member_offset,
1019 sizeof(HalfVector2),
1020 stride * array_elements.value_or(1),
1024 current_byte_offset += stride * array_elements.value_or(1);
1029 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1030 member.width ==
sizeof(Half) * 8 &&
1031 member.columns == 1 &&
1035 GetArrayStride<sizeof(HalfVector3)>(struct_type, member, i);
1036 uint32_t element_padding = stride -
sizeof(HalfVector3);
1037 result.emplace_back(StructMember{
1040 GetMemberNameAtIndex(struct_type, i),
1041 struct_member_offset,
1042 sizeof(HalfVector3),
1043 stride * array_elements.value_or(1),
1047 current_byte_offset += stride * array_elements.value_or(1);
1052 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1053 member.width ==
sizeof(Half) * 8 &&
1054 member.columns == 1 &&
1058 GetArrayStride<sizeof(HalfVector4)>(struct_type, member, i);
1059 uint32_t element_padding = stride -
sizeof(HalfVector4);
1060 result.emplace_back(StructMember{
1063 GetMemberNameAtIndex(struct_type, i),
1064 struct_member_offset,
1065 sizeof(HalfVector4),
1066 stride * array_elements.value_or(1),
1070 current_byte_offset += stride * array_elements.value_or(1);
1077 if (maybe_known_type.has_value() &&
1078 member.columns == 1 &&
1081 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
1083 stride = maybe_known_type.value().byte_size;
1085 uint32_t element_padding = stride - maybe_known_type.value().byte_size;
1087 result.emplace_back(StructMember{
1088 maybe_known_type.value().name,
1090 GetMemberNameAtIndex(struct_type, i),
1091 struct_member_offset,
1092 maybe_known_type.value().byte_size,
1093 stride * array_elements.value_or(1),
1097 current_byte_offset += stride * array_elements.value_or(1);
1105 const size_t size = (member.width * member.columns * member.vecsize) / 8u;
1106 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
1110 auto element_padding = stride - size;
1111 result.emplace_back(StructMember{
1114 GetMemberNameAtIndex(struct_type, i),
1115 struct_member_offset,
1117 stride * array_elements.value_or(1),
1121 current_byte_offset += stride * array_elements.value_or(1);
1126 if (max_member_alignment > 0u) {
1127 const auto struct_length = current_byte_offset;
1129 const auto excess = struct_length % max_member_alignment;
1131 const auto padding = max_member_alignment - excess;
1132 result.emplace_back(StructMember{
1134 spirv_cross::SPIRType::BaseType::Void,
1136 current_byte_offset,
1149 std::optional<Reflector::StructDefinition> Reflector::ReflectStructDefinition(
1150 const spirv_cross::TypeID& type_id)
const {
1151 const auto&
type = compiler_->get_type(type_id);
1152 if (
type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
1153 return std::nullopt;
1156 const auto struct_name = compiler_->get_name(type_id);
1157 if (struct_name.find(
"_RESERVED_IDENTIFIER_") != std::string::npos) {
1158 return std::nullopt;
1161 auto struct_members = ReadStructMembers(type_id);
1164 StructDefinition struc;
1165 struc.name = struct_name;
1166 struc.byte_length = reflected_struct_size;
1167 struc.members = std::move(struct_members);
1171 nlohmann::json::object_t Reflector::EmitStructDefinition(
1172 std::optional<Reflector::StructDefinition> struc)
const {
1173 nlohmann::json::object_t result;
1174 result[
"name"] = struc->name;
1175 result[
"byte_length"] = struc->byte_length;
1176 auto& members = result[
"members"] = nlohmann::json::array_t{};
1177 for (
const auto& struct_member : struc->members) {
1178 auto& member = members.emplace_back(nlohmann::json::object_t{});
1179 member[
"name"] = struct_member.name;
1180 member[
"type"] = struct_member.type;
1181 member[
"base_type"] =
1183 member[
"offset"] = struct_member.offset;
1184 member[
"byte_length"] = struct_member.byte_length;
1185 if (struct_member.array_elements.has_value()) {
1186 member[
"array_elements"] = struct_member.array_elements.value();
1188 member[
"array_elements"] =
"std::nullopt";
1190 member[
"element_padding"] = struct_member.element_padding;
1203 const spirv_cross::Compiler& compiler,
1204 const spirv_cross::Resource* resource) {
1207 const auto&
type = compiler.get_type(resource->type_id);
1209 const auto total_size =
type.columns *
type.vecsize *
type.width / 8u;
1212 if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1213 type.columns == 1u &&
type.vecsize == 2u &&
1214 type.width ==
sizeof(
float) * 8u) {
1216 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1217 type.columns == 1u &&
type.vecsize == 4u &&
1218 type.width ==
sizeof(
float) * 8u) {
1220 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1221 type.columns == 1u &&
type.vecsize == 3u &&
1222 type.width ==
sizeof(
float) * 8u) {
1224 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1225 type.columns == 1u &&
type.vecsize == 1u &&
1226 type.width ==
sizeof(
float) * 8u) {
1228 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Int &&
1229 type.columns == 1u &&
type.vecsize == 1u &&
1230 type.width ==
sizeof(int32_t) * 8u) {
1240 std::optional<Reflector::StructDefinition>
1241 Reflector::ReflectPerVertexStructDefinition(
1242 const spirv_cross::SmallVector<spirv_cross::Resource>& stage_inputs)
const {
1245 if (stage_inputs.empty()) {
1246 return std::nullopt;
1250 std::set<uint32_t> locations;
1251 for (
const auto& input : stage_inputs) {
1252 auto location = compiler_->get_decoration(
1253 input.id, spv::Decoration::DecorationLocation);
1254 if (locations.count(location) != 0) {
1256 return std::nullopt;
1258 locations.insert(location);
1261 for (
size_t i = 0; i < locations.size(); i++) {
1262 if (locations.count(i) != 1) {
1267 return std::nullopt;
1271 auto input_for_location =
1272 [&](uint32_t queried_location) ->
const spirv_cross::Resource* {
1273 for (
const auto& input : stage_inputs) {
1274 auto location = compiler_->get_decoration(
1275 input.id, spv::Decoration::DecorationLocation);
1276 if (location == queried_location) {
1285 StructDefinition struc;
1286 struc.name =
"PerVertexData";
1287 struc.byte_length = 0u;
1288 for (
size_t i = 0; i < locations.size(); i++) {
1289 auto resource = input_for_location(i);
1290 if (resource ==
nullptr) {
1291 return std::nullopt;
1293 const auto vertex_type =
1296 auto member = StructMember{
1297 vertex_type.type_name,
1298 vertex_type.base_type,
1299 vertex_type.variable_name,
1301 vertex_type.byte_length,
1302 vertex_type.byte_length,
1306 struc.byte_length += vertex_type.byte_length;
1307 struc.members.emplace_back(std::move(member));
1312 std::optional<std::string> Reflector::GetMemberNameAtIndexIfExists(
1313 const spirv_cross::SPIRType& parent_type,
1314 size_t index)
const {
1315 if (parent_type.type_alias != 0) {
1316 return GetMemberNameAtIndexIfExists(
1317 compiler_->get_type(parent_type.type_alias), index);
1320 if (
auto found = ir_->meta.find(parent_type.self); found != ir_->meta.end()) {
1321 const auto& members = found->second.members;
1322 if (index < members.size() && !members[index].alias.empty()) {
1323 return members[index].alias;
1326 return std::nullopt;
1329 std::string Reflector::GetMemberNameAtIndex(
1330 const spirv_cross::SPIRType& parent_type,
1332 std::string suffix)
const {
1333 if (
auto name = GetMemberNameAtIndexIfExists(parent_type, index);
1335 return name.value();
1337 static std::atomic_size_t sUnnamedMembersID;
1338 std::stringstream stream;
1339 stream <<
"unnamed_" << sUnnamedMembersID++ << suffix;
1340 return stream.str();
1343 std::vector<Reflector::BindPrototype> Reflector::ReflectBindPrototypes(
1344 const spirv_cross::ShaderResources& resources,
1345 spv::ExecutionModel execution_model)
const {
1346 std::vector<BindPrototype> prototypes;
1347 for (
const auto& uniform_buffer : resources.uniform_buffers) {
1348 auto& proto = prototypes.emplace_back(BindPrototype{});
1349 proto.return_type =
"bool";
1351 proto.descriptor_type =
"DescriptorType::kUniformBuffer";
1353 std::stringstream stream;
1354 stream <<
"Bind uniform buffer for resource named " << uniform_buffer.name
1356 proto.docstring = stream.str();
1358 proto.args.push_back(BindPrototypeArgument{
1359 .type_name =
"ResourceBinder&",
1360 .argument_name =
"command",
1362 proto.args.push_back(BindPrototypeArgument{
1363 .type_name =
"BufferView",
1364 .argument_name =
"view",
1367 for (
const auto& storage_buffer : resources.storage_buffers) {
1368 auto& proto = prototypes.emplace_back(BindPrototype{});
1369 proto.return_type =
"bool";
1371 proto.descriptor_type =
"DescriptorType::kStorageBuffer";
1373 std::stringstream stream;
1374 stream <<
"Bind storage buffer for resource named " << storage_buffer.name
1376 proto.docstring = stream.str();
1378 proto.args.push_back(BindPrototypeArgument{
1379 .type_name =
"ResourceBinder&",
1380 .argument_name =
"command",
1382 proto.args.push_back(BindPrototypeArgument{
1383 .type_name =
"BufferView",
1384 .argument_name =
"view",
1387 for (
const auto& sampled_image : resources.sampled_images) {
1388 auto& proto = prototypes.emplace_back(BindPrototype{});
1389 proto.return_type =
"bool";
1391 proto.descriptor_type =
"DescriptorType::kSampledImage";
1393 std::stringstream stream;
1394 stream <<
"Bind combined image sampler for resource named "
1395 << sampled_image.name <<
".";
1396 proto.docstring = stream.str();
1398 proto.args.push_back(BindPrototypeArgument{
1399 .type_name =
"ResourceBinder&",
1400 .argument_name =
"command",
1402 proto.args.push_back(BindPrototypeArgument{
1403 .type_name =
"std::shared_ptr<const Texture>",
1404 .argument_name =
"texture",
1406 proto.args.push_back(BindPrototypeArgument{
1407 .type_name =
"raw_ptr<const Sampler>",
1408 .argument_name =
"sampler",
1411 for (
const auto& separate_image : resources.separate_images) {
1412 auto& proto = prototypes.emplace_back(BindPrototype{});
1413 proto.return_type =
"bool";
1415 proto.descriptor_type =
"DescriptorType::kImage";
1417 std::stringstream stream;
1418 stream <<
"Bind separate image for resource named " << separate_image.name
1420 proto.docstring = stream.str();
1422 proto.args.push_back(BindPrototypeArgument{
1423 .type_name =
"Command&",
1424 .argument_name =
"command",
1426 proto.args.push_back(BindPrototypeArgument{
1427 .type_name =
"std::shared_ptr<const Texture>",
1428 .argument_name =
"texture",
1431 for (
const auto& separate_sampler : resources.separate_samplers) {
1432 auto& proto = prototypes.emplace_back(BindPrototype{});
1433 proto.return_type =
"bool";
1435 proto.descriptor_type =
"DescriptorType::kSampler";
1437 std::stringstream stream;
1438 stream <<
"Bind separate sampler for resource named "
1439 << separate_sampler.name <<
".";
1440 proto.docstring = stream.str();
1442 proto.args.push_back(BindPrototypeArgument{
1443 .type_name =
"Command&",
1444 .argument_name =
"command",
1446 proto.args.push_back(BindPrototypeArgument{
1447 .type_name =
"std::shared_ptr<const Sampler>",
1448 .argument_name =
"sampler",
1454 nlohmann::json::array_t Reflector::EmitBindPrototypes(
1455 const spirv_cross::ShaderResources& resources,
1456 spv::ExecutionModel execution_model)
const {
1457 const auto prototypes = ReflectBindPrototypes(resources, execution_model);
1458 nlohmann::json::array_t result;
1459 for (
const auto& res : prototypes) {
1460 auto& item = result.emplace_back(nlohmann::json::object_t{});
1461 item[
"return_type"] = res.return_type;
1462 item[
"name"] = res.name;
1463 item[
"docstring"] = res.docstring;
1464 item[
"descriptor_type"] = res.descriptor_type;
1465 auto& args = item[
"args"] = nlohmann::json::array_t{};
1466 for (
const auto& arg : res.args) {
1467 auto& json_arg = args.emplace_back(nlohmann::json::object_t{});
1468 json_arg[
"type_name"] = arg.type_name;
1469 json_arg[
"argument_name"] = arg.argument_name;
static const char * kVulkanUBOName
Reflector(Options options, const std::shared_ptr< const spirv_cross::ParsedIR > &ir, const std::shared_ptr< fml::Mapping > &shader_data, const CompilerBackend &compiler)
std::shared_ptr< fml::Mapping > GetReflectionJSON() const
std::shared_ptr< fml::Mapping > GetReflectionCC() const
std::shared_ptr< RuntimeStageData::Shader > GetRuntimeStageShaderData() const
std::shared_ptr< ShaderBundleData > GetShaderBundleData() const
std::shared_ptr< fml::Mapping > GetReflectionHeader() const
Vector2 padding
The halo padding in source space.
static std::optional< KnownType > ReadKnownScalarType(spirv_cross::SPIRType::BaseType type)
static std::string TypeNameWithPaddingOfSize(size_t size)
static VertexType VertexTypeFromInputResource(const spirv_cross::Compiler &compiler, const spirv_cross::Resource *resource)
static std::string ToString(CompilerBackend::Type type)
static size_t GetReflectedStructSize(const std::vector< StructMember > &members)
Get the reflected struct size. In the vast majority of the cases, this is the same as the declared st...
static std::optional< RuntimeStageBackend > GetRuntimeStageBackend(TargetPlatform target_platform)
static std::string ExecutionModelToString(spv::ExecutionModel model)
static std::string StringToShaderStage(const std::string &str)
constexpr std::string_view kReflectionHeaderTemplate
std::string ToCamelCase(std::string_view string)
constexpr std::string_view kReflectionCCTemplate
bool StringStartsWith(const std::string &target, const std::string &prefix)
std::vector< spirv_cross::ID > SortUniforms(const spirv_cross::ParsedIR *ir, const spirv_cross::Compiler *compiler, std::optional< spirv_cross::SPIRType::BaseType > type_filter, bool include)
Sorts uniform declarations in an IR according to decoration order.
TPoint< int32_t > IPoint32
std::string SPrintF(const char *format,...)
TPoint< uint32_t > UintPoint32
A storage only class for half precision floating point.
spirv_cross::Compiler * GetCompiler()
uint32_t GetExtendedMSLResourceBinding(ExtendedResourceIndex index, spirv_cross::ID id) const
TargetPlatform target_platform
std::string entry_point_name
std::string header_file_name
static std::string BaseTypeToString(spirv_cross::SPIRType::BaseType type)
spirv_cross::SPIRType::BaseType base_type
std::string variable_name
std::shared_ptr< const fml::Mapping > data