15 #include "flutter/fml/logging.h"
16 #include "fml/backtrace.h"
29 #include "spirv_common.hpp"
36 case spv::ExecutionModel::ExecutionModelVertex:
38 case spv::ExecutionModel::ExecutionModelFragment:
40 case spv::ExecutionModel::ExecutionModelGLCompute:
48 if (str ==
"vertex") {
49 return "ShaderStage::kVertex";
52 if (str ==
"fragment") {
53 return "ShaderStage::kFragment";
56 if (str ==
"compute") {
57 return "ShaderStage::kCompute";
60 return "ShaderStage::kUnknown";
64 const std::shared_ptr<const spirv_cross::ParsedIR>& ir,
65 const std::shared_ptr<fml::Mapping>& shader_data,
67 : options_(
std::move(options)),
69 shader_data_(shader_data),
71 if (!ir_ || !compiler_) {
75 if (
auto template_arguments = GenerateTemplateArguments();
76 template_arguments.has_value()) {
78 std::make_unique<nlohmann::json>(std::move(template_arguments.value()));
83 reflection_header_ = GenerateReflectionHeader();
84 if (!reflection_header_) {
88 reflection_cc_ = GenerateReflectionCC();
89 if (!reflection_cc_) {
93 runtime_stage_shader_ = GenerateRuntimeStageData();
95 shader_bundle_data_ = GenerateShaderBundleData();
96 if (!shader_bundle_data_) {
115 std::make_shared<std::string>(template_arguments_->dump(2u));
117 return std::make_shared<fml::NonOwnedMapping>(
118 reinterpret_cast<const uint8_t*
>(json_string->data()),
119 json_string->size(), [json_string](
auto,
auto) {});
123 return reflection_header_;
127 return reflection_cc_;
132 return runtime_stage_shader_;
136 return shader_bundle_data_;
139 std::optional<nlohmann::json> Reflector::GenerateTemplateArguments()
const {
142 const auto& entrypoints = compiler_->get_entry_points_and_stages();
143 if (entrypoints.size() != 1) {
144 VALIDATION_LOG <<
"Incorrect number of entrypoints in the shader. Found "
145 << entrypoints.size() <<
" but expected 1.";
149 auto execution_model = entrypoints.front().execution_model;
157 const auto shader_resources = compiler_->get_shader_resources();
161 auto& subpass_inputs = root[
"subpass_inputs"] = nlohmann::json::array_t{};
162 if (
auto subpass_inputs_json =
163 ReflectResources(shader_resources.subpass_inputs);
164 subpass_inputs_json.has_value()) {
165 for (
auto subpass_input : subpass_inputs_json.value()) {
166 subpass_input[
"descriptor_type"] =
"DescriptorType::kInputAttachment";
167 subpass_inputs.emplace_back(std::move(subpass_input));
176 auto& buffers = root[
"buffers"] = nlohmann::json::array_t{};
177 if (
auto uniform_buffers_json =
178 ReflectResources(shader_resources.uniform_buffers);
179 uniform_buffers_json.has_value()) {
180 for (
auto uniform_buffer : uniform_buffers_json.value()) {
181 uniform_buffer[
"descriptor_type"] =
"DescriptorType::kUniformBuffer";
182 buffers.emplace_back(std::move(uniform_buffer));
187 if (
auto storage_buffers_json =
188 ReflectResources(shader_resources.storage_buffers);
189 storage_buffers_json.has_value()) {
190 for (
auto uniform_buffer : storage_buffers_json.value()) {
191 uniform_buffer[
"descriptor_type"] =
"DescriptorType::kStorageBuffer";
192 buffers.emplace_back(std::move(uniform_buffer));
200 auto& uniforms = root[
"uniforms"] = nlohmann::json::array_t{};
201 if (
auto uniforms_json =
202 ReflectResources(shader_resources.gl_plain_uniforms);
203 uniforms_json.has_value()) {
204 for (
auto uniform : uniforms_json.value()) {
205 uniform[
"descriptor_type"] =
"DescriptorType::kUniform";
206 uniforms.emplace_back(std::move(uniform));
214 auto& stage_inputs = root[
"stage_inputs"] = nlohmann::json::array_t{};
215 if (
auto stage_inputs_json = ReflectResources(
216 shader_resources.stage_inputs,
217 execution_model == spv::ExecutionModelVertex);
218 stage_inputs_json.has_value()) {
219 stage_inputs = std::move(stage_inputs_json.value());
226 auto combined_sampled_images =
227 ReflectResources(shader_resources.sampled_images);
228 auto images = ReflectResources(shader_resources.separate_images);
229 auto samplers = ReflectResources(shader_resources.separate_samplers);
230 if (!combined_sampled_images.has_value() || !images.has_value() ||
231 !samplers.has_value()) {
234 auto& sampled_images = root[
"sampled_images"] = nlohmann::json::array_t{};
235 for (
auto value : combined_sampled_images.value()) {
236 value[
"descriptor_type"] =
"DescriptorType::kSampledImage";
237 sampled_images.emplace_back(std::move(
value));
239 for (
auto value : images.value()) {
240 value[
"descriptor_type"] =
"DescriptorType::kImage";
241 sampled_images.emplace_back(std::move(
value));
243 for (
auto value : samplers.value()) {
244 value[
"descriptor_type"] =
"DescriptorType::kSampledSampler";
245 sampled_images.emplace_back(std::move(
value));
249 if (
auto stage_outputs = ReflectResources(shader_resources.stage_outputs);
250 stage_outputs.has_value()) {
251 root[
"stage_outputs"] = std::move(stage_outputs.value());
257 auto& struct_definitions = root[
"struct_definitions"] =
258 nlohmann::json::array_t{};
259 if (entrypoints.front().execution_model ==
260 spv::ExecutionModel::ExecutionModelVertex &&
261 !shader_resources.stage_inputs.empty()) {
263 ReflectPerVertexStructDefinition(shader_resources.stage_inputs);
265 struct_definitions.emplace_back(EmitStructDefinition(struc.value()));
273 std::set<spirv_cross::ID> known_structs;
274 ir_->for_each_typed_id<spirv_cross::SPIRType>(
275 [&](uint32_t,
const spirv_cross::SPIRType&
type) {
276 if (
type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
282 for (
size_t i = 0; i <
type.member_types.size(); i++) {
283 if (!compiler_->has_member_decoration(
type.self, i,
284 spv::DecorationOffset)) {
288 if (known_structs.find(
type.self) != known_structs.end()) {
293 known_structs.insert(
type.self);
294 if (
auto struc = ReflectStructDefinition(
type.self);
296 struct_definitions.emplace_back(
297 EmitStructDefinition(struc.value()));
302 root[
"bind_prototypes"] =
303 EmitBindPrototypes(shader_resources, execution_model);
308 std::shared_ptr<fml::Mapping> Reflector::GenerateReflectionHeader()
const {
312 std::shared_ptr<fml::Mapping> Reflector::GenerateReflectionCC()
const {
318 switch (target_platform) {
340 std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
343 if (!backend.has_value()) {
347 const auto& entrypoints = compiler_->get_entry_points_and_stages();
348 if (entrypoints.size() != 1u) {
352 auto data = std::make_unique<RuntimeStageData::Shader>();
354 data->stage = entrypoints.front().execution_model;
355 data->shader = shader_data_;
356 data->backend = backend.value();
359 std::vector<spirv_cross::ID> uniforms =
361 for (
auto& sorted_id : uniforms) {
362 auto var = ir_->ids[sorted_id].get<spirv_cross::SPIRVariable>();
363 const auto spir_type = compiler_->get_type(var.basetype);
364 UniformDescription uniform_description;
365 uniform_description.name = compiler_->get_name(var.self);
366 uniform_description.location = compiler_->get_decoration(
367 var.self, spv::Decoration::DecorationLocation);
368 uniform_description.binding =
369 compiler_->get_decoration(var.self, spv::Decoration::DecorationBinding);
370 uniform_description.type = spir_type.basetype;
371 uniform_description.rows = spir_type.vecsize;
372 uniform_description.columns = spir_type.columns;
373 uniform_description.bit_width = spir_type.width;
374 uniform_description.array_elements = GetArrayElements(spir_type);
376 spir_type.basetype ==
377 spirv_cross::SPIRType::BaseType::SampledImage)
378 <<
"Vulkan runtime effect had unexpected uniforms outside of the "
379 "uniform buffer object.";
380 data->uniforms.emplace_back(std::move(uniform_description));
383 const auto ubos = compiler_->get_shader_resources().uniform_buffers;
390 "for Vulkan runtime stage backend.";
394 const auto& ubo = ubos[0];
397 compiler_->get_decoration(ubo.id, spv::Decoration::DecorationBinding);
398 auto members = ReadStructMembers(ubo.type_id);
399 std::vector<uint8_t> struct_layout;
400 size_t float_count = 0;
402 for (
size_t i = 0; i < members.size(); i += 1) {
403 const auto& member = members[i];
404 std::vector<int> bytes;
405 switch (member.underlying_type) {
407 size_t padding_count =
408 (member.size +
sizeof(float) - 1) /
sizeof(float);
409 while (padding_count > 0) {
410 struct_layout.push_back(0);
416 if (member.array_elements > 1) {
419 for (
auto i = 0; i < member.array_elements; i++) {
420 for (
auto j = 0u; j < member.size /
sizeof(float); j++) {
421 struct_layout.push_back(1);
423 for (
auto j = 0u; j < member.element_padding /
sizeof(float);
425 struct_layout.push_back(0);
429 size_t member_float_count = member.byte_length /
sizeof(float);
430 float_count += member_float_count;
431 while (member_float_count > 0) {
432 struct_layout.push_back(1);
433 member_float_count--;
439 VALIDATION_LOG <<
"Non-floating-type struct member " << member.name
440 <<
" is not supported.";
444 data->uniforms.emplace_back(UniformDescription{
448 .type = spirv_cross::SPIRType::Struct,
449 .struct_layout = std::move(struct_layout),
450 .struct_float_count = float_count,
455 if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
456 const auto inputs = compiler_->get_shader_resources().stage_inputs;
457 auto input_offsets = ComputeOffsets(inputs);
458 for (
const auto& input : inputs) {
459 std::optional<size_t> offset = GetOffset(input.id, input_offsets);
461 const auto type = compiler_->get_type(input.type_id);
463 InputDescription input_description;
464 input_description.name = input.name;
465 input_description.location = compiler_->get_decoration(
466 input.id, spv::Decoration::DecorationLocation);
467 input_description.set = compiler_->get_decoration(
468 input.id, spv::Decoration::DecorationDescriptorSet);
469 input_description.binding = compiler_->get_decoration(
470 input.id, spv::Decoration::DecorationBinding);
471 input_description.type =
type.basetype;
472 input_description.bit_width =
type.width;
473 input_description.vec_size =
type.vecsize;
474 input_description.columns =
type.columns;
475 input_description.offset = offset.value_or(0u);
476 data->inputs.emplace_back(std::move(input_description));
483 std::shared_ptr<ShaderBundleData> Reflector::GenerateShaderBundleData()
const {
484 const auto& entrypoints = compiler_->get_entry_points_and_stages();
485 if (entrypoints.size() != 1u) {
489 auto data = std::make_shared<ShaderBundleData>(
491 entrypoints.front().execution_model,
494 data->SetShaderData(shader_data_);
496 const auto uniforms = compiler_->get_shader_resources().uniform_buffers;
497 for (
const auto& uniform : uniforms) {
498 ShaderBundleData::ShaderUniformStruct uniform_struct;
499 uniform_struct.name = uniform.name;
502 uniform_struct.set = compiler_->get_decoration(
503 uniform.id, spv::Decoration::DecorationDescriptorSet);
504 uniform_struct.binding = compiler_->get_decoration(
505 uniform.id, spv::Decoration::DecorationBinding);
507 const auto type = compiler_->get_type(uniform.type_id);
508 if (
type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
509 std::cerr <<
"Error: Uniform \"" << uniform.name
510 <<
"\" is not a struct. All Flutter GPU shader uniforms must "
516 size_t size_in_bytes = 0;
517 for (
const auto& struct_member : ReadStructMembers(uniform.type_id)) {
518 size_in_bytes += struct_member.byte_length;
522 ShaderBundleData::ShaderUniformStructField uniform_struct_field;
523 uniform_struct_field.name = struct_member.name;
524 uniform_struct_field.type = struct_member.base_type;
525 uniform_struct_field.offset_in_bytes = struct_member.offset;
526 uniform_struct_field.element_size_in_bytes = struct_member.size;
527 uniform_struct_field.total_size_in_bytes = struct_member.byte_length;
528 uniform_struct_field.array_elements = struct_member.array_elements;
529 uniform_struct.fields.push_back(uniform_struct_field);
531 uniform_struct.size_in_bytes = size_in_bytes;
533 data->AddUniformStruct(uniform_struct);
536 const auto sampled_images = compiler_->get_shader_resources().sampled_images;
537 for (
const auto& image : sampled_images) {
538 ShaderBundleData::ShaderUniformTexture uniform_texture;
539 uniform_texture.name = image.name;
542 uniform_texture.set = compiler_->get_decoration(
543 image.id, spv::Decoration::DecorationDescriptorSet);
544 uniform_texture.binding =
545 compiler_->get_decoration(image.id, spv::Decoration::DecorationBinding);
546 data->AddUniformTexture(uniform_texture);
550 if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
551 const auto inputs = compiler_->get_shader_resources().stage_inputs;
552 auto input_offsets = ComputeOffsets(inputs);
553 for (
const auto& input : inputs) {
554 std::optional<size_t> offset = GetOffset(input.id, input_offsets);
556 const auto type = compiler_->get_type(input.type_id);
558 InputDescription input_description;
559 input_description.name = input.name;
560 input_description.location = compiler_->get_decoration(
561 input.id, spv::Decoration::DecorationLocation);
562 input_description.set = compiler_->get_decoration(
563 input.id, spv::Decoration::DecorationDescriptorSet);
564 input_description.binding = compiler_->get_decoration(
565 input.id, spv::Decoration::DecorationBinding);
566 input_description.type =
type.basetype;
567 input_description.bit_width =
type.width;
568 input_description.vec_size =
type.vecsize;
569 input_description.columns =
type.columns;
570 input_description.offset = offset.value_or(0u);
571 data->AddInputDescription(std::move(input_description));
578 std::optional<uint32_t> Reflector::GetArrayElements(
579 const spirv_cross::SPIRType&
type)
const {
580 if (
type.array.empty()) {
583 FML_CHECK(
type.array.size() == 1)
584 <<
"Multi-dimensional arrays are not supported.";
585 FML_CHECK(
type.array_size_literal.front())
586 <<
"Must use a literal for array sizes.";
587 return type.array.front();
593 return "Metal Shading Language";
595 return "OpenGL Shading Language";
597 return "OpenGL Shading Language (Relaxed Vulkan Semantics)";
599 return "SkSL Shading Language";
604 std::shared_ptr<fml::Mapping> Reflector::InflateTemplate(
605 std::string_view tmpl)
const {
606 inja::Environment env;
607 env.set_trim_blocks(
true);
608 env.set_lstrip_blocks(
true);
610 env.add_callback(
"camel_case", 1u, [](inja::Arguments& args) {
611 return ToCamelCase(args.at(0u)->get<std::string>());
614 env.add_callback(
"to_shader_stage", 1u, [](inja::Arguments& args) {
618 env.add_callback(
"get_generator_name", 0u,
619 [
type = compiler_.
GetType()](inja::Arguments& args) {
623 auto inflated_template =
624 std::make_shared<std::string>(env.render(tmpl, *template_arguments_));
626 return std::make_shared<fml::NonOwnedMapping>(
627 reinterpret_cast<const uint8_t*
>(inflated_template->data()),
628 inflated_template->size(), [inflated_template](
auto,
auto) {});
631 std::vector<size_t> Reflector::ComputeOffsets(
632 const spirv_cross::SmallVector<spirv_cross::Resource>& resources)
const {
633 std::vector<size_t> offsets(resources.size(), 0);
634 if (resources.size() == 0) {
637 for (
const auto& resource : resources) {
638 const auto type = compiler_->get_type(resource.type_id);
639 auto location = compiler_->get_decoration(
640 resource.id, spv::Decoration::DecorationLocation);
647 for (
size_t i = 1; i < resources.size(); i++) {
648 offsets[i] += offsets[i - 1];
650 for (
size_t i = resources.size() - 1; i > 0; i--) {
651 offsets[i] = offsets[i - 1];
658 std::optional<size_t> Reflector::GetOffset(
660 const std::vector<size_t>& offsets)
const {
662 compiler_->get_decoration(
id, spv::Decoration::DecorationLocation);
669 std::optional<nlohmann::json::object_t> Reflector::ReflectResource(
670 const spirv_cross::Resource& resource,
671 std::optional<size_t> offset)
const {
672 nlohmann::json::object_t result;
674 result[
"name"] = resource.name;
675 result[
"descriptor_set"] = compiler_->get_decoration(
676 resource.id, spv::Decoration::DecorationDescriptorSet);
677 result[
"binding"] = compiler_->get_decoration(
678 resource.id, spv::Decoration::DecorationBinding);
679 result[
"set"] = compiler_->get_decoration(
680 resource.id, spv::Decoration::DecorationDescriptorSet);
681 result[
"location"] = compiler_->get_decoration(
682 resource.id, spv::Decoration::DecorationLocation);
684 compiler_->get_decoration(resource.id, spv::Decoration::DecorationIndex);
689 result[
"relaxed_precision"] =
690 compiler_->get_decoration(
691 resource.id, spv::Decoration::DecorationRelaxedPrecision) == 1;
692 result[
"offset"] = offset.value_or(0u);
693 auto type = ReflectType(resource.type_id);
694 if (!
type.has_value()) {
697 result[
"type"] = std::move(
type.value());
701 std::optional<nlohmann::json::object_t> Reflector::ReflectType(
702 const spirv_cross::TypeID& type_id)
const {
703 nlohmann::json::object_t result;
705 const auto type = compiler_->get_type(type_id);
708 result[
"bit_width"] =
type.width;
709 result[
"vec_size"] =
type.vecsize;
710 result[
"columns"] =
type.columns;
711 auto& members = result[
"members"] = nlohmann::json::array_t{};
712 if (
type.basetype == spirv_cross::SPIRType::BaseType::Struct) {
713 for (
const auto& struct_member : ReadStructMembers(type_id)) {
714 auto member = nlohmann::json::object_t{};
715 member[
"name"] = struct_member.name;
716 member[
"type"] = struct_member.type;
717 member[
"base_type"] =
719 member[
"offset"] = struct_member.offset;
720 member[
"size"] = struct_member.size;
721 member[
"byte_length"] = struct_member.byte_length;
722 if (struct_member.array_elements.has_value()) {
723 member[
"array_elements"] = struct_member.array_elements.value();
725 member[
"array_elements"] =
"std::nullopt";
727 members.emplace_back(std::move(member));
734 std::optional<nlohmann::json::array_t> Reflector::ReflectResources(
735 const spirv_cross::SmallVector<spirv_cross::Resource>& resources,
736 bool compute_offsets)
const {
737 nlohmann::json::array_t result;
738 result.reserve(resources.size());
739 std::vector<size_t> offsets;
740 if (compute_offsets) {
741 offsets = ComputeOffsets(resources);
743 for (
const auto& resource : resources) {
744 std::optional<size_t> maybe_offset = std::nullopt;
745 if (compute_offsets) {
746 maybe_offset = GetOffset(resource.id, offsets);
748 if (
auto reflected = ReflectResource(resource, maybe_offset);
749 reflected.has_value()) {
750 result.emplace_back(std::move(reflected.value()));
759 std::stringstream stream;
760 stream <<
"Padding<" << size <<
">";
770 spirv_cross::SPIRType::BaseType
type) {
772 case spirv_cross::SPIRType::BaseType::Boolean:
775 .byte_size =
sizeof(bool),
777 case spirv_cross::SPIRType::BaseType::Float:
780 .byte_size =
sizeof(
Scalar),
782 case spirv_cross::SPIRType::BaseType::Half:
785 .byte_size =
sizeof(
Half),
787 case spirv_cross::SPIRType::BaseType::UInt:
790 .byte_size =
sizeof(uint32_t),
792 case spirv_cross::SPIRType::BaseType::Int:
795 .byte_size =
sizeof(int32_t),
817 auto struct_size = 0u;
818 for (
const auto& member : members) {
819 struct_size += member.byte_length;
824 std::vector<StructMember> Reflector::ReadStructMembers(
825 const spirv_cross::TypeID& type_id)
const {
826 const auto& struct_type = compiler_->get_type(type_id);
827 FML_CHECK(struct_type.basetype == spirv_cross::SPIRType::BaseType::Struct);
829 std::vector<StructMember> result;
831 size_t current_byte_offset = 0;
832 size_t max_member_alignment = 0;
834 for (
size_t i = 0; i < struct_type.member_types.size(); i++) {
835 const auto& member = compiler_->get_type(struct_type.member_types[i]);
836 const auto struct_member_offset =
837 compiler_->type_struct_member_offset(struct_type, i);
838 auto array_elements = GetArrayElements(member);
840 if (struct_member_offset > current_byte_offset) {
841 const auto alignment_pad = struct_member_offset - current_byte_offset;
842 result.emplace_back(StructMember{
844 spirv_cross::SPIRType::BaseType::Void,
845 std::format(
"_PADDING_{}_",
846 GetMemberNameAtIndex(struct_type, i)),
853 current_byte_offset += alignment_pad;
856 max_member_alignment =
857 std::max<size_t>(max_member_alignment,
858 (member.width / 8) * member.columns * member.vecsize);
860 FML_CHECK(current_byte_offset == struct_member_offset);
863 if (member.basetype == spirv_cross::SPIRType::BaseType::Struct) {
866 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
870 uint32_t element_padding = stride - size;
871 result.emplace_back(StructMember{
872 compiler_->get_name(member.self),
874 GetMemberNameAtIndex(struct_type, i),
875 struct_member_offset,
877 stride * array_elements.value_or(1),
881 current_byte_offset += stride * array_elements.value_or(1);
887 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
888 member.width ==
sizeof(
Scalar) * 8 &&
889 member.columns == 4 &&
892 uint32_t stride = GetArrayStride<sizeof(Matrix)>(struct_type, member, i);
893 uint32_t element_padding = stride -
sizeof(Matrix);
894 result.emplace_back(StructMember{
897 GetMemberNameAtIndex(struct_type, i),
898 struct_member_offset,
900 stride * array_elements.value_or(1),
904 current_byte_offset += stride * array_elements.value_or(1);
909 if (member.basetype == spirv_cross::SPIRType::BaseType::UInt &&
910 member.width ==
sizeof(uint32_t) * 8 &&
911 member.columns == 1 &&
915 GetArrayStride<sizeof(UintPoint32)>(struct_type, member, i);
916 uint32_t element_padding = stride -
sizeof(
UintPoint32);
917 result.emplace_back(StructMember{
920 GetMemberNameAtIndex(struct_type, i),
921 struct_member_offset,
923 stride * array_elements.value_or(1),
927 current_byte_offset += stride * array_elements.value_or(1);
932 if (member.basetype == spirv_cross::SPIRType::BaseType::Int &&
933 member.width ==
sizeof(int32_t) * 8 &&
934 member.columns == 1 &&
938 GetArrayStride<sizeof(IPoint32)>(struct_type, member, i);
939 uint32_t element_padding = stride -
sizeof(
IPoint32);
940 result.emplace_back(StructMember{
943 GetMemberNameAtIndex(struct_type, i),
944 struct_member_offset,
946 stride * array_elements.value_or(1),
950 current_byte_offset += stride * array_elements.value_or(1);
955 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
956 member.width ==
sizeof(
float) * 8 &&
957 member.columns == 1 &&
960 uint32_t stride = GetArrayStride<sizeof(Point)>(struct_type, member, i);
961 uint32_t element_padding = stride -
sizeof(
Point);
962 result.emplace_back(StructMember{
965 GetMemberNameAtIndex(struct_type, i),
966 struct_member_offset,
968 stride * array_elements.value_or(1),
972 current_byte_offset += stride * array_elements.value_or(1);
977 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
978 member.width ==
sizeof(
float) * 8 &&
979 member.columns == 1 &&
982 uint32_t stride = GetArrayStride<sizeof(Vector3)>(struct_type, member, i);
983 uint32_t element_padding = stride -
sizeof(Vector3);
984 result.emplace_back(StructMember{
987 GetMemberNameAtIndex(struct_type, i),
988 struct_member_offset,
990 stride * array_elements.value_or(1),
994 current_byte_offset += stride * array_elements.value_or(1);
999 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
1000 member.width ==
sizeof(
float) * 8 &&
1001 member.columns == 1 &&
1004 uint32_t stride = GetArrayStride<sizeof(Vector4)>(struct_type, member, i);
1005 uint32_t element_padding = stride -
sizeof(Vector4);
1006 result.emplace_back(StructMember{
1009 GetMemberNameAtIndex(struct_type, i),
1010 struct_member_offset,
1012 stride * array_elements.value_or(1),
1016 current_byte_offset += stride * array_elements.value_or(1);
1021 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1022 member.width ==
sizeof(Half) * 8 &&
1023 member.columns == 1 &&
1027 GetArrayStride<sizeof(HalfVector2)>(struct_type, member, i);
1028 uint32_t element_padding = stride -
sizeof(HalfVector2);
1029 result.emplace_back(StructMember{
1032 GetMemberNameAtIndex(struct_type, i),
1033 struct_member_offset,
1034 sizeof(HalfVector2),
1035 stride * array_elements.value_or(1),
1039 current_byte_offset += stride * array_elements.value_or(1);
1044 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1045 member.width ==
sizeof(Half) * 8 &&
1046 member.columns == 1 &&
1050 GetArrayStride<sizeof(HalfVector3)>(struct_type, member, i);
1051 uint32_t element_padding = stride -
sizeof(HalfVector3);
1052 result.emplace_back(StructMember{
1055 GetMemberNameAtIndex(struct_type, i),
1056 struct_member_offset,
1057 sizeof(HalfVector3),
1058 stride * array_elements.value_or(1),
1062 current_byte_offset += stride * array_elements.value_or(1);
1067 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1068 member.width ==
sizeof(Half) * 8 &&
1069 member.columns == 1 &&
1073 GetArrayStride<sizeof(HalfVector4)>(struct_type, member, i);
1074 uint32_t element_padding = stride -
sizeof(HalfVector4);
1075 result.emplace_back(StructMember{
1078 GetMemberNameAtIndex(struct_type, i),
1079 struct_member_offset,
1080 sizeof(HalfVector4),
1081 stride * array_elements.value_or(1),
1085 current_byte_offset += stride * array_elements.value_or(1);
1092 if (maybe_known_type.has_value() &&
1093 member.columns == 1 &&
1096 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
1098 stride = maybe_known_type.value().byte_size;
1100 uint32_t element_padding = stride - maybe_known_type.value().byte_size;
1102 result.emplace_back(StructMember{
1103 maybe_known_type.value().name,
1105 GetMemberNameAtIndex(struct_type, i),
1106 struct_member_offset,
1107 maybe_known_type.value().byte_size,
1108 stride * array_elements.value_or(1),
1112 current_byte_offset += stride * array_elements.value_or(1);
1120 const size_t size = (member.width * member.columns * member.vecsize) / 8u;
1121 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
1125 auto element_padding = stride - size;
1126 result.emplace_back(StructMember{
1129 GetMemberNameAtIndex(struct_type, i),
1130 struct_member_offset,
1132 stride * array_elements.value_or(1),
1136 current_byte_offset += stride * array_elements.value_or(1);
1141 if (max_member_alignment > 0u) {
1142 const auto struct_length = current_byte_offset;
1144 const auto excess = struct_length % max_member_alignment;
1146 const auto padding = max_member_alignment - excess;
1147 result.emplace_back(StructMember{
1149 spirv_cross::SPIRType::BaseType::Void,
1151 current_byte_offset,
1164 std::optional<Reflector::StructDefinition> Reflector::ReflectStructDefinition(
1165 const spirv_cross::TypeID& type_id)
const {
1166 const auto&
type = compiler_->get_type(type_id);
1167 if (
type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
1168 return std::nullopt;
1171 const auto struct_name = compiler_->get_name(type_id);
1172 if (struct_name.find(
"_RESERVED_IDENTIFIER_") != std::string::npos) {
1173 return std::nullopt;
1176 auto struct_members = ReadStructMembers(type_id);
1179 StructDefinition struc;
1180 struc.name = struct_name;
1181 struc.byte_length = reflected_struct_size;
1182 struc.members = std::move(struct_members);
1186 nlohmann::json::object_t Reflector::EmitStructDefinition(
1187 std::optional<Reflector::StructDefinition> struc)
const {
1188 nlohmann::json::object_t result;
1189 result[
"name"] = struc->name;
1190 result[
"byte_length"] = struc->byte_length;
1191 auto& members = result[
"members"] = nlohmann::json::array_t{};
1192 for (
const auto& struct_member : struc->members) {
1193 auto& member = members.emplace_back(nlohmann::json::object_t{});
1194 member[
"name"] = struct_member.name;
1195 member[
"type"] = struct_member.type;
1196 member[
"base_type"] =
1198 member[
"offset"] = struct_member.offset;
1199 member[
"byte_length"] = struct_member.byte_length;
1200 if (struct_member.array_elements.has_value()) {
1201 member[
"array_elements"] = struct_member.array_elements.value();
1203 member[
"array_elements"] =
"std::nullopt";
1205 member[
"element_padding"] = struct_member.element_padding;
1218 const spirv_cross::Compiler& compiler,
1219 const spirv_cross::Resource* resource) {
1222 const auto&
type = compiler.get_type(resource->type_id);
1224 const auto total_size =
type.columns *
type.vecsize *
type.width / 8u;
1227 if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1228 type.columns == 1u &&
type.vecsize == 2u &&
1229 type.width ==
sizeof(
float) * 8u) {
1231 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1232 type.columns == 1u &&
type.vecsize == 4u &&
1233 type.width ==
sizeof(
float) * 8u) {
1235 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1236 type.columns == 1u &&
type.vecsize == 3u &&
1237 type.width ==
sizeof(
float) * 8u) {
1239 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1240 type.columns == 1u &&
type.vecsize == 1u &&
1241 type.width ==
sizeof(
float) * 8u) {
1243 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Int &&
1244 type.columns == 1u &&
type.vecsize == 1u &&
1245 type.width ==
sizeof(int32_t) * 8u) {
1255 std::optional<Reflector::StructDefinition>
1256 Reflector::ReflectPerVertexStructDefinition(
1257 const spirv_cross::SmallVector<spirv_cross::Resource>& stage_inputs)
const {
1260 if (stage_inputs.empty()) {
1261 return std::nullopt;
1265 std::set<uint32_t> locations;
1266 for (
const auto& input : stage_inputs) {
1267 auto location = compiler_->get_decoration(
1268 input.id, spv::Decoration::DecorationLocation);
1269 if (locations.count(
location) != 0) {
1271 return std::nullopt;
1276 for (
size_t i = 0; i < locations.size(); i++) {
1277 if (locations.count(i) != 1) {
1282 return std::nullopt;
1286 auto input_for_location =
1287 [&](uint32_t queried_location) ->
const spirv_cross::Resource* {
1288 for (
const auto& input : stage_inputs) {
1289 auto location = compiler_->get_decoration(
1290 input.id, spv::Decoration::DecorationLocation);
1291 if (
location == queried_location) {
1300 StructDefinition struc;
1301 struc.name =
"PerVertexData";
1302 struc.byte_length = 0u;
1303 for (
size_t i = 0; i < locations.size(); i++) {
1304 auto resource = input_for_location(i);
1305 if (resource ==
nullptr) {
1306 return std::nullopt;
1308 const auto vertex_type =
1311 auto member = StructMember{
1312 vertex_type.type_name,
1313 vertex_type.base_type,
1314 vertex_type.variable_name,
1316 vertex_type.byte_length,
1317 vertex_type.byte_length,
1321 struc.byte_length += vertex_type.byte_length;
1322 struc.members.emplace_back(std::move(member));
1327 std::optional<std::string> Reflector::GetMemberNameAtIndexIfExists(
1328 const spirv_cross::SPIRType& parent_type,
1329 size_t index)
const {
1330 if (parent_type.type_alias != 0) {
1331 return GetMemberNameAtIndexIfExists(
1332 compiler_->get_type(parent_type.type_alias), index);
1335 if (
auto found = ir_->meta.find(parent_type.self); found != ir_->meta.end()) {
1336 const auto& members = found->second.members;
1337 if (index < members.size() && !members[index].alias.empty()) {
1338 return members[index].alias;
1341 return std::nullopt;
1344 std::string Reflector::GetMemberNameAtIndex(
1345 const spirv_cross::SPIRType& parent_type,
1347 std::string suffix)
const {
1348 if (
auto name = GetMemberNameAtIndexIfExists(parent_type, index);
1350 return name.value();
1352 static std::atomic_size_t sUnnamedMembersID;
1353 std::stringstream stream;
1354 stream <<
"unnamed_" << sUnnamedMembersID++ << suffix;
1355 return stream.str();
1358 std::vector<Reflector::BindPrototype> Reflector::ReflectBindPrototypes(
1359 const spirv_cross::ShaderResources& resources,
1360 spv::ExecutionModel execution_model)
const {
1361 std::vector<BindPrototype> prototypes;
1362 for (
const auto& uniform_buffer : resources.uniform_buffers) {
1363 auto& proto = prototypes.emplace_back(BindPrototype{});
1364 proto.return_type =
"bool";
1366 proto.descriptor_type =
"DescriptorType::kUniformBuffer";
1368 std::stringstream stream;
1369 stream <<
"Bind uniform buffer for resource named " << uniform_buffer.name
1371 proto.docstring = stream.str();
1373 proto.args.push_back(BindPrototypeArgument{
1374 .type_name =
"ResourceBinder&",
1375 .argument_name =
"command",
1377 proto.args.push_back(BindPrototypeArgument{
1378 .type_name =
"BufferView",
1379 .argument_name =
"view",
1382 for (
const auto& storage_buffer : resources.storage_buffers) {
1383 auto& proto = prototypes.emplace_back(BindPrototype{});
1384 proto.return_type =
"bool";
1386 proto.descriptor_type =
"DescriptorType::kStorageBuffer";
1388 std::stringstream stream;
1389 stream <<
"Bind storage buffer for resource named " << storage_buffer.name
1391 proto.docstring = stream.str();
1393 proto.args.push_back(BindPrototypeArgument{
1394 .type_name =
"ResourceBinder&",
1395 .argument_name =
"command",
1397 proto.args.push_back(BindPrototypeArgument{
1398 .type_name =
"BufferView",
1399 .argument_name =
"view",
1402 for (
const auto& sampled_image : resources.sampled_images) {
1403 auto& proto = prototypes.emplace_back(BindPrototype{});
1404 proto.return_type =
"bool";
1406 proto.descriptor_type =
"DescriptorType::kSampledImage";
1408 std::stringstream stream;
1409 stream <<
"Bind combined image sampler for resource named "
1410 << sampled_image.name <<
".";
1411 proto.docstring = stream.str();
1413 proto.args.push_back(BindPrototypeArgument{
1414 .type_name =
"ResourceBinder&",
1415 .argument_name =
"command",
1417 proto.args.push_back(BindPrototypeArgument{
1418 .type_name =
"std::shared_ptr<const Texture>",
1419 .argument_name =
"texture",
1421 proto.args.push_back(BindPrototypeArgument{
1422 .type_name =
"raw_ptr<const Sampler>",
1423 .argument_name =
"sampler",
1426 for (
const auto& separate_image : resources.separate_images) {
1427 auto& proto = prototypes.emplace_back(BindPrototype{});
1428 proto.return_type =
"bool";
1430 proto.descriptor_type =
"DescriptorType::kImage";
1432 std::stringstream stream;
1433 stream <<
"Bind separate image for resource named " << separate_image.name
1435 proto.docstring = stream.str();
1437 proto.args.push_back(BindPrototypeArgument{
1438 .type_name =
"Command&",
1439 .argument_name =
"command",
1441 proto.args.push_back(BindPrototypeArgument{
1442 .type_name =
"std::shared_ptr<const Texture>",
1443 .argument_name =
"texture",
1446 for (
const auto& separate_sampler : resources.separate_samplers) {
1447 auto& proto = prototypes.emplace_back(BindPrototype{});
1448 proto.return_type =
"bool";
1450 proto.descriptor_type =
"DescriptorType::kSampler";
1452 std::stringstream stream;
1453 stream <<
"Bind separate sampler for resource named "
1454 << separate_sampler.name <<
".";
1455 proto.docstring = stream.str();
1457 proto.args.push_back(BindPrototypeArgument{
1458 .type_name =
"Command&",
1459 .argument_name =
"command",
1461 proto.args.push_back(BindPrototypeArgument{
1462 .type_name =
"std::shared_ptr<const Sampler>",
1463 .argument_name =
"sampler",
1469 nlohmann::json::array_t Reflector::EmitBindPrototypes(
1470 const spirv_cross::ShaderResources& resources,
1471 spv::ExecutionModel execution_model)
const {
1472 const auto prototypes = ReflectBindPrototypes(resources, execution_model);
1473 nlohmann::json::array_t result;
1474 for (
const auto& res : prototypes) {
1475 auto& item = result.emplace_back(nlohmann::json::object_t{});
1476 item[
"return_type"] = res.return_type;
1477 item[
"name"] = res.name;
1478 item[
"docstring"] = res.docstring;
1479 item[
"descriptor_type"] = res.descriptor_type;
1480 auto& args = item[
"args"] = nlohmann::json::array_t{};
1481 for (
const auto& arg : res.args) {
1482 auto& json_arg = args.emplace_back(nlohmann::json::object_t{});
1483 json_arg[
"type_name"] = arg.type_name;
1484 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
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