14#include "flutter/fml/logging.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,
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_;
138std::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));
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);
293std::shared_ptr<fml::Mapping> Reflector::GenerateReflectionHeader()
const {
297std::shared_ptr<fml::Mapping> Reflector::GenerateReflectionCC()
const {
303 switch (target_platform) {
323std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
330 const auto& entrypoints = compiler_->get_entry_points_and_stages();
331 if (entrypoints.size() != 1u) {
335 auto data = std::make_unique<RuntimeStageData::Shader>();
337 data->stage = entrypoints.front().execution_model;
338 data->shader = shader_data_;
342 std::vector<spirv_cross::ID> uniforms =
344 for (
auto& sorted_id : uniforms) {
345 auto var = ir_->ids[sorted_id].get<spirv_cross::SPIRVariable>();
346 const auto spir_type = compiler_->get_type(var.basetype);
347 UniformDescription uniform_description;
348 uniform_description.name = compiler_->get_name(var.self);
349 uniform_description.location = compiler_->get_decoration(
350 var.self, spv::Decoration::DecorationLocation);
351 uniform_description.binding =
352 compiler_->get_decoration(var.self, spv::Decoration::DecorationBinding);
353 uniform_description.type = spir_type.basetype;
354 uniform_description.rows = spir_type.vecsize;
355 uniform_description.columns = spir_type.columns;
356 uniform_description.bit_width = spir_type.width;
357 uniform_description.array_elements = GetArrayElements(spir_type);
359 spir_type.basetype ==
360 spirv_cross::SPIRType::BaseType::SampledImage)
361 <<
"Vulkan runtime effect had unexpected uniforms outside of the "
362 "uniform buffer object.";
363 data->uniforms.emplace_back(std::move(uniform_description));
366 const auto ubos = compiler_->get_shader_resources().uniform_buffers;
373 "for Vulkan runtime stage backend.";
377 const auto& ubo = ubos[0];
379 auto members = ReadStructMembers(ubo.type_id);
380 std::vector<uint8_t> struct_layout;
381 size_t float_count = 0;
383 for (
size_t i = 0; i < members.size(); i += 1) {
384 const auto& member = members[i];
385 std::vector<int> bytes;
386 switch (member.underlying_type) {
388 size_t padding_count =
389 (member.size +
sizeof(
float) - 1) /
sizeof(
float);
390 while (padding_count > 0) {
391 struct_layout.push_back(0);
397 size_t member_float_count = member.byte_length /
sizeof(
float);
398 float_count += member_float_count;
399 while (member_float_count > 0) {
400 struct_layout.push_back(1);
401 member_float_count--;
406 VALIDATION_LOG <<
"Non-floating-type struct member " << member.name
407 <<
" is not supported.";
411 data->uniforms.emplace_back(UniformDescription{
416 .type = spirv_cross::SPIRType::Struct,
417 .struct_layout = std::move(struct_layout),
418 .struct_float_count = float_count,
423 if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
424 const auto inputs = compiler_->get_shader_resources().stage_inputs;
425 auto input_offsets = ComputeOffsets(inputs);
426 for (
const auto& input :
inputs) {
427 std::optional<size_t>
offset = GetOffset(input.id, input_offsets);
429 const auto type = compiler_->get_type(input.type_id);
431 InputDescription input_description;
432 input_description.name = input.name;
433 input_description.location = compiler_->get_decoration(
434 input.id, spv::Decoration::DecorationLocation);
435 input_description.set = compiler_->get_decoration(
436 input.id, spv::Decoration::DecorationDescriptorSet);
437 input_description.binding = compiler_->get_decoration(
438 input.id, spv::Decoration::DecorationBinding);
439 input_description.type =
type.basetype;
440 input_description.bit_width =
type.width;
441 input_description.vec_size =
type.vecsize;
442 input_description.columns =
type.columns;
443 input_description.offset =
offset.value_or(0u);
444 data->inputs.emplace_back(std::move(input_description));
451std::shared_ptr<ShaderBundleData> Reflector::GenerateShaderBundleData()
const {
452 const auto& entrypoints = compiler_->get_entry_points_and_stages();
453 if (entrypoints.size() != 1u) {
457 auto data = std::make_shared<ShaderBundleData>(
459 entrypoints.front().execution_model,
462 data->SetShaderData(shader_data_);
464 const auto uniforms = compiler_->get_shader_resources().uniform_buffers;
465 for (
const auto& uniform : uniforms) {
466 ShaderBundleData::ShaderUniformStruct uniform_struct;
467 uniform_struct.name = uniform.name;
470 uniform_struct.set = compiler_->get_decoration(
471 uniform.id, spv::Decoration::DecorationDescriptorSet);
472 uniform_struct.binding = compiler_->get_decoration(
473 uniform.id, spv::Decoration::DecorationBinding);
475 const auto type = compiler_->get_type(uniform.type_id);
476 if (
type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
477 std::cerr <<
"Error: Uniform \"" << uniform.name
478 <<
"\" is not a struct. All Flutter GPU shader uniforms must "
484 size_t size_in_bytes = 0;
485 for (
const auto& struct_member : ReadStructMembers(uniform.type_id)) {
486 size_in_bytes += struct_member.byte_length;
490 ShaderBundleData::ShaderUniformStructField uniform_struct_field;
491 uniform_struct_field.name = struct_member.name;
492 uniform_struct_field.type = struct_member.base_type;
493 uniform_struct_field.offset_in_bytes = struct_member.offset;
494 uniform_struct_field.element_size_in_bytes = struct_member.size;
495 uniform_struct_field.total_size_in_bytes = struct_member.byte_length;
496 uniform_struct_field.array_elements = struct_member.array_elements;
497 uniform_struct.fields.push_back(uniform_struct_field);
499 uniform_struct.size_in_bytes = size_in_bytes;
501 data->AddUniformStruct(uniform_struct);
504 const auto sampled_images = compiler_->get_shader_resources().sampled_images;
505 for (
const auto&
image : sampled_images) {
506 ShaderBundleData::ShaderUniformTexture uniform_texture;
507 uniform_texture.name =
image.name;
510 uniform_texture.set = compiler_->get_decoration(
511 image.id, spv::Decoration::DecorationDescriptorSet);
512 uniform_texture.binding =
513 compiler_->get_decoration(
image.id, spv::Decoration::DecorationBinding);
514 data->AddUniformTexture(uniform_texture);
518 if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
519 const auto inputs = compiler_->get_shader_resources().stage_inputs;
520 auto input_offsets = ComputeOffsets(inputs);
521 for (
const auto& input :
inputs) {
522 std::optional<size_t>
offset = GetOffset(input.id, input_offsets);
524 const auto type = compiler_->get_type(input.type_id);
526 InputDescription input_description;
527 input_description.name = input.name;
528 input_description.location = compiler_->get_decoration(
529 input.id, spv::Decoration::DecorationLocation);
530 input_description.set = compiler_->get_decoration(
531 input.id, spv::Decoration::DecorationDescriptorSet);
532 input_description.binding = compiler_->get_decoration(
533 input.id, spv::Decoration::DecorationBinding);
534 input_description.type =
type.basetype;
535 input_description.bit_width =
type.width;
536 input_description.vec_size =
type.vecsize;
537 input_description.columns =
type.columns;
538 input_description.offset =
offset.value_or(0u);
539 data->AddInputDescription(std::move(input_description));
546std::optional<uint32_t> Reflector::GetArrayElements(
547 const spirv_cross::SPIRType&
type)
const {
548 if (
type.array.empty()) {
552 <<
"Multi-dimensional arrays are not supported.";
554 <<
"Must use a literal for array sizes.";
555 return type.array.front();
561 return "Metal Shading Language";
563 return "OpenGL Shading Language";
565 return "OpenGL Shading Language (Relaxed Vulkan Semantics)";
567 return "SkSL Shading Language";
572std::shared_ptr<fml::Mapping> Reflector::InflateTemplate(
573 std::string_view tmpl)
const {
574 inja::Environment
env;
575 env.set_trim_blocks(
true);
576 env.set_lstrip_blocks(
true);
578 env.add_callback(
"camel_case", 1u, [](inja::Arguments&
args) {
582 env.add_callback(
"to_shader_stage", 1u, [](inja::Arguments&
args) {
586 env.add_callback(
"get_generator_name", 0u,
591 auto inflated_template =
592 std::make_shared<std::string>(
env.render(tmpl, *template_arguments_));
594 return std::make_shared<fml::NonOwnedMapping>(
595 reinterpret_cast<const uint8_t*
>(inflated_template->data()),
596 inflated_template->size(), [inflated_template](
auto,
auto) {});
599std::vector<size_t> Reflector::ComputeOffsets(
600 const spirv_cross::SmallVector<spirv_cross::Resource>& resources)
const {
601 std::vector<size_t>
offsets(resources.size(), 0);
602 if (resources.size() == 0) {
605 for (
const auto& resource : resources) {
606 const auto type = compiler_->get_type(resource.type_id);
607 auto location = compiler_->get_decoration(
608 resource.id, spv::Decoration::DecorationLocation);
610 if (location >= resources.size() || location < 0) {
615 for (
size_t i = 1; i < resources.size(); i++) {
618 for (
size_t i = resources.size() - 1; i > 0; i--) {
626std::optional<size_t> Reflector::GetOffset(
628 const std::vector<size_t>& offsets)
const {
630 compiler_->get_decoration(
id, spv::Decoration::DecorationLocation);
631 if (location >=
offsets.size()) {
637std::optional<nlohmann::json::object_t> Reflector::ReflectResource(
638 const spirv_cross::Resource& resource,
639 std::optional<size_t>
offset)
const {
640 nlohmann::json::object_t
result;
642 result[
"name"] = resource.name;
643 result[
"descriptor_set"] = compiler_->get_decoration(
644 resource.id, spv::Decoration::DecorationDescriptorSet);
645 result[
"binding"] = compiler_->get_decoration(
646 resource.id, spv::Decoration::DecorationBinding);
647 result[
"set"] = compiler_->get_decoration(
648 resource.id, spv::Decoration::DecorationDescriptorSet);
649 result[
"location"] = compiler_->get_decoration(
650 resource.id, spv::Decoration::DecorationLocation);
652 compiler_->get_decoration(resource.id, spv::Decoration::DecorationIndex);
657 auto type = ReflectType(resource.type_id);
658 if (!
type.has_value()) {
666std::optional<nlohmann::json::object_t> Reflector::ReflectType(
667 const spirv_cross::TypeID& type_id)
const {
668 nlohmann::json::object_t
result;
670 const auto type = compiler_->get_type(type_id);
676 auto& members =
result[
"members"] = nlohmann::json::array_t{};
677 if (
type.basetype == spirv_cross::SPIRType::BaseType::Struct) {
678 for (
const auto& struct_member : ReadStructMembers(type_id)) {
679 auto member = nlohmann::json::object_t{};
680 member[
"name"] = struct_member.name;
681 member[
"type"] = struct_member.type;
682 member[
"base_type"] =
684 member[
"offset"] = struct_member.offset;
685 member[
"size"] = struct_member.size;
686 member[
"byte_length"] = struct_member.byte_length;
687 if (struct_member.array_elements.has_value()) {
688 member[
"array_elements"] = struct_member.array_elements.value();
690 member[
"array_elements"] =
"std::nullopt";
692 members.emplace_back(std::move(member));
699std::optional<nlohmann::json::array_t> Reflector::ReflectResources(
700 const spirv_cross::SmallVector<spirv_cross::Resource>& resources,
701 bool compute_offsets)
const {
702 nlohmann::json::array_t
result;
703 result.reserve(resources.size());
705 if (compute_offsets) {
706 offsets = ComputeOffsets(resources);
708 for (
const auto& resource : resources) {
709 std::optional<size_t> maybe_offset = std::nullopt;
710 if (compute_offsets) {
711 maybe_offset = GetOffset(resource.id, offsets);
713 if (
auto reflected = ReflectResource(resource, maybe_offset);
714 reflected.has_value()) {
715 result.emplace_back(std::move(reflected.value()));
724 std::stringstream stream;
725 stream <<
"Padding<" << size <<
">";
735 spirv_cross::SPIRType::BaseType
type) {
737 case spirv_cross::SPIRType::BaseType::Boolean:
740 .byte_size =
sizeof(bool),
742 case spirv_cross::SPIRType::BaseType::Float:
745 .byte_size =
sizeof(
Scalar),
747 case spirv_cross::SPIRType::BaseType::Half:
750 .byte_size =
sizeof(
Half),
752 case spirv_cross::SPIRType::BaseType::UInt:
755 .byte_size =
sizeof(uint32_t),
757 case spirv_cross::SPIRType::BaseType::Int:
760 .byte_size =
sizeof(int32_t),
782 auto struct_size = 0u;
783 for (
const auto& member : members) {
784 struct_size += member.byte_length;
789std::vector<StructMember> Reflector::ReadStructMembers(
790 const spirv_cross::TypeID& type_id)
const {
791 const auto& struct_type = compiler_->get_type(type_id);
792 FML_CHECK(struct_type.basetype == spirv_cross::SPIRType::BaseType::Struct);
794 std::vector<StructMember>
result;
796 size_t current_byte_offset = 0;
797 size_t max_member_alignment = 0;
799 for (
size_t i = 0; i < struct_type.member_types.size(); i++) {
800 const auto& member = compiler_->get_type(struct_type.member_types[i]);
801 const auto struct_member_offset =
802 compiler_->type_struct_member_offset(struct_type, i);
803 auto array_elements = GetArrayElements(member);
805 if (struct_member_offset > current_byte_offset) {
806 const auto alignment_pad = struct_member_offset - current_byte_offset;
807 result.emplace_back(StructMember{
809 spirv_cross::SPIRType::BaseType::Void,
811 GetMemberNameAtIndex(struct_type, i).c_str()),
818 current_byte_offset += alignment_pad;
821 max_member_alignment =
822 std::max<size_t>(max_member_alignment,
823 (member.width / 8) * member.columns * member.vecsize);
825 FML_CHECK(current_byte_offset == struct_member_offset);
828 if (member.basetype == spirv_cross::SPIRType::BaseType::Struct) {
831 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
835 uint32_t element_padding = stride -
size;
836 result.emplace_back(StructMember{
837 compiler_->get_name(member.self),
839 GetMemberNameAtIndex(struct_type, i),
840 struct_member_offset,
842 stride * array_elements.value_or(1),
846 current_byte_offset += stride * array_elements.value_or(1);
852 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
853 member.width ==
sizeof(
Scalar) * 8 &&
854 member.columns == 4 &&
857 uint32_t stride = GetArrayStride<sizeof(Matrix)>(struct_type, member, i);
858 uint32_t element_padding = stride -
sizeof(Matrix);
859 result.emplace_back(StructMember{
862 GetMemberNameAtIndex(struct_type, i),
863 struct_member_offset,
865 stride * array_elements.value_or(1),
869 current_byte_offset += stride * array_elements.value_or(1);
874 if (member.basetype == spirv_cross::SPIRType::BaseType::UInt &&
875 member.width ==
sizeof(uint32_t) * 8 &&
876 member.columns == 1 &&
880 GetArrayStride<sizeof(UintPoint32)>(struct_type, member, i);
881 uint32_t element_padding = stride -
sizeof(
UintPoint32);
882 result.emplace_back(StructMember{
885 GetMemberNameAtIndex(struct_type, i),
886 struct_member_offset,
888 stride * array_elements.value_or(1),
892 current_byte_offset += stride * array_elements.value_or(1);
897 if (member.basetype == spirv_cross::SPIRType::BaseType::Int &&
898 member.width ==
sizeof(int32_t) * 8 &&
899 member.columns == 1 &&
903 GetArrayStride<sizeof(IPoint32)>(struct_type, member, i);
904 uint32_t element_padding = stride -
sizeof(
IPoint32);
905 result.emplace_back(StructMember{
908 GetMemberNameAtIndex(struct_type, i),
909 struct_member_offset,
911 stride * array_elements.value_or(1),
915 current_byte_offset += stride * array_elements.value_or(1);
920 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
921 member.width ==
sizeof(
float) * 8 &&
922 member.columns == 1 &&
925 uint32_t stride = GetArrayStride<sizeof(Point)>(struct_type, member, i);
926 uint32_t element_padding = stride -
sizeof(
Point);
927 result.emplace_back(StructMember{
930 GetMemberNameAtIndex(struct_type, i),
931 struct_member_offset,
933 stride * array_elements.value_or(1),
937 current_byte_offset += stride * array_elements.value_or(1);
942 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
943 member.width ==
sizeof(
float) * 8 &&
944 member.columns == 1 &&
947 uint32_t stride = GetArrayStride<sizeof(Vector3)>(struct_type, member, i);
948 uint32_t element_padding = stride -
sizeof(Vector3);
949 result.emplace_back(StructMember{
952 GetMemberNameAtIndex(struct_type, i),
953 struct_member_offset,
955 stride * array_elements.value_or(1),
959 current_byte_offset += stride * array_elements.value_or(1);
964 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
965 member.width ==
sizeof(
float) * 8 &&
966 member.columns == 1 &&
969 uint32_t stride = GetArrayStride<sizeof(Vector4)>(struct_type, member, i);
970 uint32_t element_padding = stride -
sizeof(Vector4);
971 result.emplace_back(StructMember{
974 GetMemberNameAtIndex(struct_type, i),
975 struct_member_offset,
977 stride * array_elements.value_or(1),
981 current_byte_offset += stride * array_elements.value_or(1);
986 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
987 member.width ==
sizeof(Half) * 8 &&
988 member.columns == 1 &&
992 GetArrayStride<sizeof(HalfVector2)>(struct_type, member, i);
993 uint32_t element_padding = stride -
sizeof(HalfVector2);
994 result.emplace_back(StructMember{
997 GetMemberNameAtIndex(struct_type, i),
998 struct_member_offset,
1000 stride * array_elements.value_or(1),
1004 current_byte_offset += stride * array_elements.value_or(1);
1009 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1010 member.width ==
sizeof(Half) * 8 &&
1011 member.columns == 1 &&
1015 GetArrayStride<sizeof(HalfVector3)>(struct_type, member, i);
1016 uint32_t element_padding = stride -
sizeof(HalfVector3);
1017 result.emplace_back(StructMember{
1020 GetMemberNameAtIndex(struct_type, i),
1021 struct_member_offset,
1022 sizeof(HalfVector3),
1023 stride * array_elements.value_or(1),
1027 current_byte_offset += stride * array_elements.value_or(1);
1032 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1033 member.width ==
sizeof(Half) * 8 &&
1034 member.columns == 1 &&
1038 GetArrayStride<sizeof(HalfVector4)>(struct_type, member, i);
1039 uint32_t element_padding = stride -
sizeof(HalfVector4);
1040 result.emplace_back(StructMember{
1043 GetMemberNameAtIndex(struct_type, i),
1044 struct_member_offset,
1045 sizeof(HalfVector4),
1046 stride * array_elements.value_or(1),
1050 current_byte_offset += stride * array_elements.value_or(1);
1057 if (maybe_known_type.has_value() &&
1058 member.columns == 1 &&
1061 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
1063 stride = maybe_known_type.value().byte_size;
1065 uint32_t element_padding = stride - maybe_known_type.value().byte_size;
1067 result.emplace_back(StructMember{
1068 maybe_known_type.value().
name,
1070 GetMemberNameAtIndex(struct_type, i),
1071 struct_member_offset,
1072 maybe_known_type.value().byte_size,
1073 stride * array_elements.value_or(1),
1077 current_byte_offset += stride * array_elements.value_or(1);
1085 const size_t size = (member.width * member.columns * member.vecsize) / 8u;
1086 uint32_t stride = GetArrayStride<0>(struct_type, member, i);
1090 auto element_padding = stride -
size;
1091 result.emplace_back(StructMember{
1094 GetMemberNameAtIndex(struct_type, i),
1095 struct_member_offset,
1097 stride * array_elements.value_or(1),
1101 current_byte_offset += stride * array_elements.value_or(1);
1106 if (max_member_alignment > 0u) {
1107 const auto struct_length = current_byte_offset;
1109 const auto excess = struct_length % max_member_alignment;
1111 const auto padding = max_member_alignment - excess;
1112 result.emplace_back(StructMember{
1114 spirv_cross::SPIRType::BaseType::Void,
1116 current_byte_offset,
1129std::optional<Reflector::StructDefinition> Reflector::ReflectStructDefinition(
1130 const spirv_cross::TypeID& type_id)
const {
1131 const auto&
type = compiler_->get_type(type_id);
1132 if (
type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
1133 return std::nullopt;
1136 const auto struct_name = compiler_->get_name(type_id);
1137 if (struct_name.find(
"_RESERVED_IDENTIFIER_") != std::string::npos) {
1138 return std::nullopt;
1141 auto struct_members = ReadStructMembers(type_id);
1144 StructDefinition struc;
1145 struc.name = struct_name;
1146 struc.byte_length = reflected_struct_size;
1147 struc.members = std::move(struct_members);
1151nlohmann::json::object_t Reflector::EmitStructDefinition(
1152 std::optional<Reflector::StructDefinition> struc)
const {
1153 nlohmann::json::object_t
result;
1154 result[
"name"] = struc->name;
1155 result[
"byte_length"] = struc->byte_length;
1156 auto& members =
result[
"members"] = nlohmann::json::array_t{};
1157 for (
const auto& struct_member : struc->members) {
1158 auto& member = members.emplace_back(nlohmann::json::object_t{});
1159 member[
"name"] = struct_member.name;
1160 member[
"type"] = struct_member.type;
1161 member[
"base_type"] =
1163 member[
"offset"] = struct_member.offset;
1164 member[
"byte_length"] = struct_member.byte_length;
1165 if (struct_member.array_elements.has_value()) {
1166 member[
"array_elements"] = struct_member.array_elements.value();
1168 member[
"array_elements"] =
"std::nullopt";
1170 member[
"element_padding"] = struct_member.element_padding;
1183 const spirv_cross::Compiler&
compiler,
1184 const spirv_cross::Resource* resource) {
1187 const auto&
type =
compiler.get_type(resource->type_id);
1192 if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1193 type.columns == 1u &&
type.vecsize == 2u &&
1194 type.width ==
sizeof(
float) * 8u) {
1195 result.type_name =
"Point";
1196 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1197 type.columns == 1u &&
type.vecsize == 4u &&
1198 type.width ==
sizeof(
float) * 8u) {
1199 result.type_name =
"Vector4";
1200 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1201 type.columns == 1u &&
type.vecsize == 3u &&
1202 type.width ==
sizeof(
float) * 8u) {
1203 result.type_name =
"Vector3";
1204 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1205 type.columns == 1u &&
type.vecsize == 1u &&
1206 type.width ==
sizeof(
float) * 8u) {
1207 result.type_name =
"Scalar";
1208 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Int &&
1209 type.columns == 1u &&
type.vecsize == 1u &&
1210 type.width ==
sizeof(int32_t) * 8u) {
1211 result.type_name =
"int32_t";
1220std::optional<Reflector::StructDefinition>
1221Reflector::ReflectPerVertexStructDefinition(
1222 const spirv_cross::SmallVector<spirv_cross::Resource>& stage_inputs)
const {
1225 if (stage_inputs.empty()) {
1226 return std::nullopt;
1230 std::set<uint32_t> locations;
1231 for (
const auto& input : stage_inputs) {
1232 auto location = compiler_->get_decoration(
1233 input.id, spv::Decoration::DecorationLocation);
1234 if (locations.count(location) != 0) {
1236 return std::nullopt;
1238 locations.insert(location);
1241 for (
size_t i = 0; i < locations.size(); i++) {
1242 if (locations.count(i) != 1) {
1247 return std::nullopt;
1251 auto input_for_location =
1252 [&](uint32_t queried_location) ->
const spirv_cross::Resource* {
1253 for (
const auto& input : stage_inputs) {
1254 auto location = compiler_->get_decoration(
1255 input.id, spv::Decoration::DecorationLocation);
1256 if (location == queried_location) {
1265 StructDefinition struc;
1266 struc.name =
"PerVertexData";
1267 struc.byte_length = 0u;
1268 for (
size_t i = 0; i < locations.size(); i++) {
1269 auto resource = input_for_location(i);
1270 if (resource ==
nullptr) {
1271 return std::nullopt;
1273 const auto vertex_type =
1276 auto member = StructMember{
1277 vertex_type.type_name,
1278 vertex_type.base_type,
1279 vertex_type.variable_name,
1281 vertex_type.byte_length,
1282 vertex_type.byte_length,
1286 struc.byte_length += vertex_type.byte_length;
1287 struc.members.emplace_back(std::move(member));
1292std::optional<std::string> Reflector::GetMemberNameAtIndexIfExists(
1293 const spirv_cross::SPIRType& parent_type,
1294 size_t index)
const {
1295 if (parent_type.type_alias != 0) {
1296 return GetMemberNameAtIndexIfExists(
1297 compiler_->get_type(parent_type.type_alias), index);
1300 if (
auto found = ir_->meta.find(parent_type.self); found != ir_->meta.end()) {
1301 const auto& members = found->second.members;
1302 if (index < members.size() && !members[index].alias.empty()) {
1303 return members[index].alias;
1306 return std::nullopt;
1309std::string Reflector::GetMemberNameAtIndex(
1310 const spirv_cross::SPIRType& parent_type,
1312 std::string suffix)
const {
1313 if (
auto name = GetMemberNameAtIndexIfExists(parent_type, index);
1315 return name.value();
1317 static std::atomic_size_t sUnnamedMembersID;
1318 std::stringstream
stream;
1323std::vector<Reflector::BindPrototype> Reflector::ReflectBindPrototypes(
1324 const spirv_cross::ShaderResources& resources,
1325 spv::ExecutionModel execution_model)
const {
1326 std::vector<BindPrototype> prototypes;
1327 for (
const auto& uniform_buffer : resources.uniform_buffers) {
1328 auto& proto = prototypes.emplace_back(BindPrototype{});
1329 proto.return_type =
"bool";
1331 proto.descriptor_type =
"DescriptorType::kUniformBuffer";
1333 std::stringstream
stream;
1334 stream <<
"Bind uniform buffer for resource named " << uniform_buffer.name
1336 proto.docstring =
stream.str();
1338 proto.args.push_back(BindPrototypeArgument{
1339 .type_name =
"ResourceBinder&",
1340 .argument_name =
"command",
1342 proto.args.push_back(BindPrototypeArgument{
1343 .type_name =
"BufferView",
1344 .argument_name =
"view",
1347 for (
const auto& storage_buffer : resources.storage_buffers) {
1348 auto& proto = prototypes.emplace_back(BindPrototype{});
1349 proto.return_type =
"bool";
1351 proto.descriptor_type =
"DescriptorType::kStorageBuffer";
1353 std::stringstream
stream;
1354 stream <<
"Bind storage buffer for resource named " << storage_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& sampled_image : resources.sampled_images) {
1368 auto& proto = prototypes.emplace_back(BindPrototype{});
1369 proto.return_type =
"bool";
1371 proto.descriptor_type =
"DescriptorType::kSampledImage";
1373 std::stringstream
stream;
1374 stream <<
"Bind combined image sampler for resource named "
1375 << sampled_image.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 =
"std::shared_ptr<const Texture>",
1384 .argument_name =
"texture",
1386 proto.args.push_back(BindPrototypeArgument{
1387 .type_name =
"const std::unique_ptr<const Sampler>&",
1388 .argument_name =
"sampler",
1391 for (
const auto& separate_image : resources.separate_images) {
1392 auto& proto = prototypes.emplace_back(BindPrototype{});
1393 proto.return_type =
"bool";
1395 proto.descriptor_type =
"DescriptorType::kImage";
1397 std::stringstream
stream;
1398 stream <<
"Bind separate image for resource named " << separate_image.name
1400 proto.docstring =
stream.str();
1402 proto.args.push_back(BindPrototypeArgument{
1403 .type_name =
"Command&",
1404 .argument_name =
"command",
1406 proto.args.push_back(BindPrototypeArgument{
1407 .type_name =
"std::shared_ptr<const Texture>",
1408 .argument_name =
"texture",
1411 for (
const auto& separate_sampler : resources.separate_samplers) {
1412 auto& proto = prototypes.emplace_back(BindPrototype{});
1413 proto.return_type =
"bool";
1415 proto.descriptor_type =
"DescriptorType::kSampler";
1417 std::stringstream
stream;
1418 stream <<
"Bind separate sampler for resource named "
1419 << separate_sampler.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 Sampler>",
1428 .argument_name =
"sampler",
1434nlohmann::json::array_t Reflector::EmitBindPrototypes(
1435 const spirv_cross::ShaderResources& resources,
1436 spv::ExecutionModel execution_model)
const {
1437 const auto prototypes = ReflectBindPrototypes(resources, execution_model);
1438 nlohmann::json::array_t
result;
1439 for (
const auto& res : prototypes) {
1440 auto& item =
result.emplace_back(nlohmann::json::object_t{});
1441 item[
"return_type"] = res.return_type;
1442 item[
"name"] = res.name;
1443 item[
"docstring"] = res.docstring;
1444 item[
"descriptor_type"] = res.descriptor_type;
1445 auto&
args = item[
"args"] = nlohmann::json::array_t{};
1446 for (
const auto& arg : res.
args) {
1447 auto& json_arg =
args.emplace_back(nlohmann::json::object_t{});
1448 json_arg[
"type_name"] = arg.type_name;
1449 json_arg[
"argument_name"] = arg.argument_name;
static size_t total_size(SkSBlockAllocator< N > &pool)
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
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
#define FML_CHECK(condition)
#define FML_UNREACHABLE()
std::array< MockImage, 3 > images
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
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 std::optional< RuntimeStageBackend > GetRuntimeStageBackend(TargetPlatform target_platform)
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::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
static std::optional< KnownType > ReadKnownScalarType(spirv_cross::SPIRType::BaseType type)
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