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];
380 compiler_->get_decoration(ubo.id, spv::Decoration::DecorationBinding);
381 auto members = ReadStructMembers(ubo.type_id);
382 std::vector<uint8_t> struct_layout;
383 size_t float_count = 0;
385 for (
size_t i = 0;
i < members.size();
i += 1) {
386 const auto& member = members[
i];
387 std::vector<int> bytes;
388 switch (member.underlying_type) {
390 size_t padding_count =
391 (member.size +
sizeof(
float) - 1) /
sizeof(
float);
392 while (padding_count > 0) {
393 struct_layout.push_back(0);
399 size_t member_float_count = member.byte_length /
sizeof(
float);
400 float_count += member_float_count;
401 while (member_float_count > 0) {
402 struct_layout.push_back(1);
403 member_float_count--;
408 VALIDATION_LOG <<
"Non-floating-type struct member " << member.name
409 <<
" is not supported.";
413 data->uniforms.emplace_back(UniformDescription{
417 .type = spirv_cross::SPIRType::Struct,
418 .struct_layout = std::move(struct_layout),
419 .struct_float_count = float_count,
424 if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
425 const auto inputs = compiler_->get_shader_resources().stage_inputs;
426 auto input_offsets = ComputeOffsets(
inputs);
427 for (
const auto& input :
inputs) {
428 std::optional<size_t>
offset = GetOffset(input.id, input_offsets);
430 const auto type = compiler_->get_type(input.type_id);
432 InputDescription input_description;
433 input_description.name = input.name;
434 input_description.location = compiler_->get_decoration(
435 input.id, spv::Decoration::DecorationLocation);
436 input_description.set = compiler_->get_decoration(
437 input.id, spv::Decoration::DecorationDescriptorSet);
438 input_description.binding = compiler_->get_decoration(
439 input.id, spv::Decoration::DecorationBinding);
440 input_description.type =
type.basetype;
441 input_description.bit_width =
type.width;
442 input_description.vec_size =
type.vecsize;
443 input_description.columns =
type.columns;
444 input_description.offset =
offset.value_or(0u);
445 data->inputs.emplace_back(std::move(input_description));
452std::shared_ptr<ShaderBundleData> Reflector::GenerateShaderBundleData()
const {
453 const auto& entrypoints = compiler_->get_entry_points_and_stages();
454 if (entrypoints.size() != 1u) {
458 auto data = std::make_shared<ShaderBundleData>(
460 entrypoints.front().execution_model,
463 data->SetShaderData(shader_data_);
465 const auto uniforms = compiler_->get_shader_resources().uniform_buffers;
466 for (
const auto& uniform : uniforms) {
467 ShaderBundleData::ShaderUniformStruct uniform_struct;
468 uniform_struct.name = uniform.name;
471 uniform_struct.set = compiler_->get_decoration(
472 uniform.id, spv::Decoration::DecorationDescriptorSet);
473 uniform_struct.binding = compiler_->get_decoration(
474 uniform.id, spv::Decoration::DecorationBinding);
476 const auto type = compiler_->get_type(uniform.type_id);
477 if (
type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
478 std::cerr <<
"Error: Uniform \"" << uniform.name
479 <<
"\" is not a struct. All Flutter GPU shader uniforms must "
485 size_t size_in_bytes = 0;
486 for (
const auto& struct_member : ReadStructMembers(uniform.type_id)) {
487 size_in_bytes += struct_member.byte_length;
491 ShaderBundleData::ShaderUniformStructField uniform_struct_field;
492 uniform_struct_field.name = struct_member.name;
493 uniform_struct_field.type = struct_member.base_type;
494 uniform_struct_field.offset_in_bytes = struct_member.offset;
495 uniform_struct_field.element_size_in_bytes = struct_member.size;
496 uniform_struct_field.total_size_in_bytes = struct_member.byte_length;
497 uniform_struct_field.array_elements = struct_member.array_elements;
498 uniform_struct.fields.push_back(uniform_struct_field);
500 uniform_struct.size_in_bytes = size_in_bytes;
502 data->AddUniformStruct(uniform_struct);
505 const auto sampled_images = compiler_->get_shader_resources().sampled_images;
506 for (
const auto&
image : sampled_images) {
507 ShaderBundleData::ShaderUniformTexture uniform_texture;
508 uniform_texture.name =
image.name;
511 uniform_texture.set = compiler_->get_decoration(
512 image.id, spv::Decoration::DecorationDescriptorSet);
513 uniform_texture.binding =
514 compiler_->get_decoration(
image.id, spv::Decoration::DecorationBinding);
515 data->AddUniformTexture(uniform_texture);
519 if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
520 const auto inputs = compiler_->get_shader_resources().stage_inputs;
521 auto input_offsets = ComputeOffsets(
inputs);
522 for (
const auto& input :
inputs) {
523 std::optional<size_t>
offset = GetOffset(input.id, input_offsets);
525 const auto type = compiler_->get_type(input.type_id);
527 InputDescription input_description;
528 input_description.name = input.name;
529 input_description.location = compiler_->get_decoration(
530 input.id, spv::Decoration::DecorationLocation);
531 input_description.set = compiler_->get_decoration(
532 input.id, spv::Decoration::DecorationDescriptorSet);
533 input_description.binding = compiler_->get_decoration(
534 input.id, spv::Decoration::DecorationBinding);
535 input_description.type =
type.basetype;
536 input_description.bit_width =
type.width;
537 input_description.vec_size =
type.vecsize;
538 input_description.columns =
type.columns;
539 input_description.offset =
offset.value_or(0u);
540 data->AddInputDescription(std::move(input_description));
547std::optional<uint32_t> Reflector::GetArrayElements(
548 const spirv_cross::SPIRType&
type)
const {
549 if (
type.array.empty()) {
553 <<
"Multi-dimensional arrays are not supported.";
555 <<
"Must use a literal for array sizes.";
556 return type.array.front();
562 return "Metal Shading Language";
564 return "OpenGL Shading Language";
566 return "OpenGL Shading Language (Relaxed Vulkan Semantics)";
568 return "SkSL Shading Language";
573std::shared_ptr<fml::Mapping> Reflector::InflateTemplate(
574 std::string_view tmpl)
const {
575 inja::Environment
env;
576 env.set_trim_blocks(
true);
577 env.set_lstrip_blocks(
true);
579 env.add_callback(
"camel_case", 1u, [](inja::Arguments&
args) {
583 env.add_callback(
"to_shader_stage", 1u, [](inja::Arguments&
args) {
587 env.add_callback(
"get_generator_name", 0u,
592 auto inflated_template =
593 std::make_shared<std::string>(
env.render(tmpl, *template_arguments_));
595 return std::make_shared<fml::NonOwnedMapping>(
596 reinterpret_cast<const uint8_t*
>(inflated_template->data()),
597 inflated_template->size(), [inflated_template](
auto,
auto) {});
600std::vector<size_t> Reflector::ComputeOffsets(
601 const spirv_cross::SmallVector<spirv_cross::Resource>& resources)
const {
602 std::vector<size_t>
offsets(resources.size(), 0);
603 if (resources.size() == 0) {
606 for (
const auto&
resource : resources) {
607 const auto type = compiler_->get_type(
resource.type_id);
608 auto location = compiler_->get_decoration(
609 resource.id, spv::Decoration::DecorationLocation);
611 if (location >= resources.size() || location < 0) {
616 for (
size_t i = 1;
i < resources.size();
i++) {
619 for (
size_t i = resources.size() - 1;
i > 0;
i--) {
627std::optional<size_t> Reflector::GetOffset(
629 const std::vector<size_t>&
offsets)
const {
631 compiler_->get_decoration(
id, spv::Decoration::DecorationLocation);
632 if (location >=
offsets.size()) {
638std::optional<nlohmann::json::object_t> Reflector::ReflectResource(
639 const spirv_cross::Resource&
resource,
640 std::optional<size_t>
offset)
const {
641 nlohmann::json::object_t
result;
644 result[
"descriptor_set"] = compiler_->get_decoration(
645 resource.id, spv::Decoration::DecorationDescriptorSet);
646 result[
"binding"] = compiler_->get_decoration(
647 resource.id, spv::Decoration::DecorationBinding);
648 result[
"set"] = compiler_->get_decoration(
649 resource.id, spv::Decoration::DecorationDescriptorSet);
650 result[
"location"] = compiler_->get_decoration(
651 resource.id, spv::Decoration::DecorationLocation);
653 compiler_->get_decoration(
resource.id, spv::Decoration::DecorationIndex);
659 if (!
type.has_value()) {
667std::optional<nlohmann::json::object_t> Reflector::ReflectType(
668 const spirv_cross::TypeID& type_id)
const {
669 nlohmann::json::object_t
result;
671 const auto type = compiler_->get_type(type_id);
677 auto& members =
result[
"members"] = nlohmann::json::array_t{};
678 if (
type.basetype == spirv_cross::SPIRType::BaseType::Struct) {
679 for (
const auto& struct_member : ReadStructMembers(type_id)) {
680 auto member = nlohmann::json::object_t{};
681 member[
"name"] = struct_member.name;
682 member[
"type"] = struct_member.type;
683 member[
"base_type"] =
685 member[
"offset"] = struct_member.offset;
686 member[
"size"] = struct_member.size;
687 member[
"byte_length"] = struct_member.byte_length;
688 if (struct_member.array_elements.has_value()) {
689 member[
"array_elements"] = struct_member.array_elements.value();
691 member[
"array_elements"] =
"std::nullopt";
693 members.emplace_back(std::move(member));
700std::optional<nlohmann::json::array_t> Reflector::ReflectResources(
701 const spirv_cross::SmallVector<spirv_cross::Resource>& resources,
702 bool compute_offsets)
const {
703 nlohmann::json::array_t
result;
704 result.reserve(resources.size());
706 if (compute_offsets) {
707 offsets = ComputeOffsets(resources);
709 for (
const auto&
resource : resources) {
710 std::optional<size_t> maybe_offset = std::nullopt;
711 if (compute_offsets) {
714 if (
auto reflected = ReflectResource(
resource, maybe_offset);
715 reflected.has_value()) {
716 result.emplace_back(std::move(reflected.value()));
736 spirv_cross::SPIRType::BaseType
type) {
738 case spirv_cross::SPIRType::BaseType::Boolean:
741 .byte_size =
sizeof(bool),
743 case spirv_cross::SPIRType::BaseType::Float:
746 .byte_size =
sizeof(
Scalar),
748 case spirv_cross::SPIRType::BaseType::Half:
751 .byte_size =
sizeof(
Half),
753 case spirv_cross::SPIRType::BaseType::UInt:
756 .byte_size =
sizeof(uint32_t),
758 case spirv_cross::SPIRType::BaseType::Int:
761 .byte_size =
sizeof(int32_t),
783 auto struct_size = 0u;
784 for (
const auto& member : members) {
785 struct_size += member.byte_length;
790std::vector<StructMember> Reflector::ReadStructMembers(
791 const spirv_cross::TypeID& type_id)
const {
792 const auto& struct_type = compiler_->get_type(type_id);
793 FML_CHECK(struct_type.basetype == spirv_cross::SPIRType::BaseType::Struct);
795 std::vector<StructMember>
result;
797 size_t current_byte_offset = 0;
798 size_t max_member_alignment = 0;
800 for (
size_t i = 0;
i < struct_type.member_types.size();
i++) {
801 const auto& member = compiler_->get_type(struct_type.member_types[
i]);
802 const auto struct_member_offset =
803 compiler_->type_struct_member_offset(struct_type,
i);
804 auto array_elements = GetArrayElements(member);
806 if (struct_member_offset > current_byte_offset) {
807 const auto alignment_pad = struct_member_offset - current_byte_offset;
808 result.emplace_back(StructMember{
810 spirv_cross::SPIRType::BaseType::Void,
812 GetMemberNameAtIndex(struct_type,
i).c_str()),
819 current_byte_offset += alignment_pad;
822 max_member_alignment =
823 std::max<size_t>(max_member_alignment,
824 (member.width / 8) * member.columns * member.vecsize);
826 FML_CHECK(current_byte_offset == struct_member_offset);
829 if (member.basetype == spirv_cross::SPIRType::BaseType::Struct) {
832 uint32_t stride = GetArrayStride<0>(struct_type, member,
i);
836 uint32_t element_padding = stride -
size;
837 result.emplace_back(StructMember{
838 compiler_->get_name(member.self),
840 GetMemberNameAtIndex(struct_type,
i),
841 struct_member_offset,
843 stride * array_elements.value_or(1),
847 current_byte_offset += stride * array_elements.value_or(1);
853 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
854 member.width ==
sizeof(
Scalar) * 8 &&
855 member.columns == 4 &&
858 uint32_t stride = GetArrayStride<sizeof(Matrix)>(struct_type, member,
i);
859 uint32_t element_padding = stride -
sizeof(
Matrix);
860 result.emplace_back(StructMember{
863 GetMemberNameAtIndex(struct_type,
i),
864 struct_member_offset,
866 stride * array_elements.value_or(1),
870 current_byte_offset += stride * array_elements.value_or(1);
875 if (member.basetype == spirv_cross::SPIRType::BaseType::UInt &&
876 member.width ==
sizeof(uint32_t) * 8 &&
877 member.columns == 1 &&
881 GetArrayStride<sizeof(UintPoint32)>(struct_type, member,
i);
882 uint32_t element_padding = stride -
sizeof(
UintPoint32);
883 result.emplace_back(StructMember{
886 GetMemberNameAtIndex(struct_type,
i),
887 struct_member_offset,
889 stride * array_elements.value_or(1),
893 current_byte_offset += stride * array_elements.value_or(1);
898 if (member.basetype == spirv_cross::SPIRType::BaseType::Int &&
899 member.width ==
sizeof(int32_t) * 8 &&
900 member.columns == 1 &&
904 GetArrayStride<sizeof(IPoint32)>(struct_type, member,
i);
905 uint32_t element_padding = stride -
sizeof(
IPoint32);
906 result.emplace_back(StructMember{
909 GetMemberNameAtIndex(struct_type,
i),
910 struct_member_offset,
912 stride * array_elements.value_or(1),
916 current_byte_offset += stride * array_elements.value_or(1);
921 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
922 member.width ==
sizeof(
float) * 8 &&
923 member.columns == 1 &&
926 uint32_t stride = GetArrayStride<sizeof(Point)>(struct_type, member,
i);
927 uint32_t element_padding = stride -
sizeof(
Point);
928 result.emplace_back(StructMember{
931 GetMemberNameAtIndex(struct_type,
i),
932 struct_member_offset,
934 stride * array_elements.value_or(1),
938 current_byte_offset += stride * array_elements.value_or(1);
943 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
944 member.width ==
sizeof(
float) * 8 &&
945 member.columns == 1 &&
948 uint32_t stride = GetArrayStride<sizeof(Vector3)>(struct_type, member,
i);
949 uint32_t element_padding = stride -
sizeof(Vector3);
950 result.emplace_back(StructMember{
953 GetMemberNameAtIndex(struct_type,
i),
954 struct_member_offset,
956 stride * array_elements.value_or(1),
960 current_byte_offset += stride * array_elements.value_or(1);
965 if (member.basetype == spirv_cross::SPIRType::BaseType::Float &&
966 member.width ==
sizeof(
float) * 8 &&
967 member.columns == 1 &&
970 uint32_t stride = GetArrayStride<sizeof(Vector4)>(struct_type, member,
i);
971 uint32_t element_padding = stride -
sizeof(Vector4);
972 result.emplace_back(StructMember{
975 GetMemberNameAtIndex(struct_type,
i),
976 struct_member_offset,
978 stride * array_elements.value_or(1),
982 current_byte_offset += stride * array_elements.value_or(1);
987 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
988 member.width ==
sizeof(Half) * 8 &&
989 member.columns == 1 &&
993 GetArrayStride<sizeof(HalfVector2)>(struct_type, member,
i);
994 uint32_t element_padding = stride -
sizeof(HalfVector2);
995 result.emplace_back(StructMember{
998 GetMemberNameAtIndex(struct_type,
i),
999 struct_member_offset,
1000 sizeof(HalfVector2),
1001 stride * array_elements.value_or(1),
1005 current_byte_offset += stride * array_elements.value_or(1);
1010 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1011 member.width ==
sizeof(Half) * 8 &&
1012 member.columns == 1 &&
1016 GetArrayStride<sizeof(HalfVector3)>(struct_type, member,
i);
1017 uint32_t element_padding = stride -
sizeof(HalfVector3);
1018 result.emplace_back(StructMember{
1021 GetMemberNameAtIndex(struct_type,
i),
1022 struct_member_offset,
1023 sizeof(HalfVector3),
1024 stride * array_elements.value_or(1),
1028 current_byte_offset += stride * array_elements.value_or(1);
1033 if (member.basetype == spirv_cross::SPIRType::BaseType::Half &&
1034 member.width ==
sizeof(Half) * 8 &&
1035 member.columns == 1 &&
1039 GetArrayStride<sizeof(HalfVector4)>(struct_type, member,
i);
1040 uint32_t element_padding = stride -
sizeof(HalfVector4);
1041 result.emplace_back(StructMember{
1044 GetMemberNameAtIndex(struct_type,
i),
1045 struct_member_offset,
1046 sizeof(HalfVector4),
1047 stride * array_elements.value_or(1),
1051 current_byte_offset += stride * array_elements.value_or(1);
1058 if (maybe_known_type.has_value() &&
1059 member.columns == 1 &&
1062 uint32_t stride = GetArrayStride<0>(struct_type, member,
i);
1064 stride = maybe_known_type.value().byte_size;
1066 uint32_t element_padding = stride - maybe_known_type.value().byte_size;
1068 result.emplace_back(StructMember{
1069 maybe_known_type.value().
name,
1071 GetMemberNameAtIndex(struct_type,
i),
1072 struct_member_offset,
1073 maybe_known_type.value().byte_size,
1074 stride * array_elements.value_or(1),
1078 current_byte_offset += stride * array_elements.value_or(1);
1086 const size_t size = (member.width * member.columns * member.vecsize) / 8u;
1087 uint32_t stride = GetArrayStride<0>(struct_type, member,
i);
1091 auto element_padding = stride -
size;
1092 result.emplace_back(StructMember{
1095 GetMemberNameAtIndex(struct_type,
i),
1096 struct_member_offset,
1098 stride * array_elements.value_or(1),
1102 current_byte_offset += stride * array_elements.value_or(1);
1107 if (max_member_alignment > 0u) {
1108 const auto struct_length = current_byte_offset;
1110 const auto excess = struct_length % max_member_alignment;
1112 const auto padding = max_member_alignment - excess;
1113 result.emplace_back(StructMember{
1115 spirv_cross::SPIRType::BaseType::Void,
1117 current_byte_offset,
1130std::optional<Reflector::StructDefinition> Reflector::ReflectStructDefinition(
1131 const spirv_cross::TypeID& type_id)
const {
1132 const auto&
type = compiler_->get_type(type_id);
1133 if (
type.basetype != spirv_cross::SPIRType::BaseType::Struct) {
1134 return std::nullopt;
1137 const auto struct_name = compiler_->get_name(type_id);
1138 if (struct_name.find(
"_RESERVED_IDENTIFIER_") != std::string::npos) {
1139 return std::nullopt;
1142 auto struct_members = ReadStructMembers(type_id);
1145 StructDefinition struc;
1146 struc.name = struct_name;
1147 struc.byte_length = reflected_struct_size;
1148 struc.members = std::move(struct_members);
1152nlohmann::json::object_t Reflector::EmitStructDefinition(
1153 std::optional<Reflector::StructDefinition> struc)
const {
1154 nlohmann::json::object_t
result;
1155 result[
"name"] = struc->name;
1156 result[
"byte_length"] = struc->byte_length;
1157 auto& members =
result[
"members"] = nlohmann::json::array_t{};
1158 for (
const auto& struct_member : struc->members) {
1159 auto& member = members.emplace_back(nlohmann::json::object_t{});
1160 member[
"name"] = struct_member.name;
1161 member[
"type"] = struct_member.type;
1162 member[
"base_type"] =
1164 member[
"offset"] = struct_member.offset;
1165 member[
"byte_length"] = struct_member.byte_length;
1166 if (struct_member.array_elements.has_value()) {
1167 member[
"array_elements"] = struct_member.array_elements.value();
1169 member[
"array_elements"] =
"std::nullopt";
1171 member[
"element_padding"] = struct_member.element_padding;
1184 const spirv_cross::Compiler&
compiler,
1185 const spirv_cross::Resource*
resource) {
1193 if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1194 type.columns == 1u &&
type.vecsize == 2u &&
1195 type.width ==
sizeof(
float) * 8u) {
1196 result.type_name =
"Point";
1197 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1198 type.columns == 1u &&
type.vecsize == 4u &&
1199 type.width ==
sizeof(
float) * 8u) {
1200 result.type_name =
"Vector4";
1201 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1202 type.columns == 1u &&
type.vecsize == 3u &&
1203 type.width ==
sizeof(
float) * 8u) {
1204 result.type_name =
"Vector3";
1205 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Float &&
1206 type.columns == 1u &&
type.vecsize == 1u &&
1207 type.width ==
sizeof(
float) * 8u) {
1208 result.type_name =
"Scalar";
1209 }
else if (
type.basetype == spirv_cross::SPIRType::BaseType::Int &&
1210 type.columns == 1u &&
type.vecsize == 1u &&
1211 type.width ==
sizeof(int32_t) * 8u) {
1212 result.type_name =
"int32_t";
1221std::optional<Reflector::StructDefinition>
1222Reflector::ReflectPerVertexStructDefinition(
1223 const spirv_cross::SmallVector<spirv_cross::Resource>& stage_inputs)
const {
1226 if (stage_inputs.empty()) {
1227 return std::nullopt;
1231 std::set<uint32_t> locations;
1232 for (
const auto& input : stage_inputs) {
1233 auto location = compiler_->get_decoration(
1234 input.id, spv::Decoration::DecorationLocation);
1235 if (locations.count(location) != 0) {
1237 return std::nullopt;
1239 locations.insert(location);
1242 for (
size_t i = 0;
i < locations.size();
i++) {
1243 if (locations.count(
i) != 1) {
1248 return std::nullopt;
1252 auto input_for_location =
1253 [&](uint32_t queried_location) ->
const spirv_cross::Resource* {
1254 for (
const auto& input : stage_inputs) {
1255 auto location = compiler_->get_decoration(
1256 input.id, spv::Decoration::DecorationLocation);
1257 if (location == queried_location) {
1266 StructDefinition struc;
1267 struc.name =
"PerVertexData";
1268 struc.byte_length = 0u;
1269 for (
size_t i = 0;
i < locations.size();
i++) {
1272 return std::nullopt;
1274 const auto vertex_type =
1277 auto member = StructMember{
1278 vertex_type.type_name,
1279 vertex_type.base_type,
1280 vertex_type.variable_name,
1282 vertex_type.byte_length,
1283 vertex_type.byte_length,
1287 struc.byte_length += vertex_type.byte_length;
1288 struc.members.emplace_back(std::move(member));
1293std::optional<std::string> Reflector::GetMemberNameAtIndexIfExists(
1294 const spirv_cross::SPIRType& parent_type,
1295 size_t index)
const {
1296 if (parent_type.type_alias != 0) {
1297 return GetMemberNameAtIndexIfExists(
1298 compiler_->get_type(parent_type.type_alias), index);
1301 if (
auto found = ir_->meta.find(parent_type.self); found != ir_->meta.end()) {
1302 const auto& members = found->second.members;
1303 if (index < members.size() && !members[index].alias.empty()) {
1304 return members[index].alias;
1307 return std::nullopt;
1310std::string Reflector::GetMemberNameAtIndex(
1311 const spirv_cross::SPIRType& parent_type,
1313 std::string
suffix)
const {
1314 if (
auto name = GetMemberNameAtIndexIfExists(parent_type, index);
1316 return name.value();
1318 static std::atomic_size_t sUnnamedMembersID;
1319 std::stringstream
stream;
1324std::vector<Reflector::BindPrototype> Reflector::ReflectBindPrototypes(
1325 const spirv_cross::ShaderResources& resources,
1326 spv::ExecutionModel execution_model)
const {
1327 std::vector<BindPrototype> prototypes;
1328 for (
const auto& uniform_buffer : resources.uniform_buffers) {
1329 auto& proto = prototypes.emplace_back(BindPrototype{});
1330 proto.return_type =
"bool";
1332 proto.descriptor_type =
"DescriptorType::kUniformBuffer";
1334 std::stringstream
stream;
1335 stream <<
"Bind uniform buffer for resource named " << uniform_buffer.name
1337 proto.docstring =
stream.str();
1339 proto.args.push_back(BindPrototypeArgument{
1340 .type_name =
"ResourceBinder&",
1341 .argument_name =
"command",
1343 proto.args.push_back(BindPrototypeArgument{
1344 .type_name =
"BufferView",
1345 .argument_name =
"view",
1348 for (
const auto& storage_buffer : resources.storage_buffers) {
1349 auto& proto = prototypes.emplace_back(BindPrototype{});
1350 proto.return_type =
"bool";
1352 proto.descriptor_type =
"DescriptorType::kStorageBuffer";
1354 std::stringstream
stream;
1355 stream <<
"Bind storage buffer for resource named " << storage_buffer.name
1357 proto.docstring =
stream.str();
1359 proto.args.push_back(BindPrototypeArgument{
1360 .type_name =
"ResourceBinder&",
1361 .argument_name =
"command",
1363 proto.args.push_back(BindPrototypeArgument{
1364 .type_name =
"BufferView",
1365 .argument_name =
"view",
1368 for (
const auto& sampled_image : resources.sampled_images) {
1369 auto& proto = prototypes.emplace_back(BindPrototype{});
1370 proto.return_type =
"bool";
1372 proto.descriptor_type =
"DescriptorType::kSampledImage";
1374 std::stringstream
stream;
1375 stream <<
"Bind combined image sampler for resource named "
1376 << sampled_image.name <<
".";
1377 proto.docstring =
stream.str();
1379 proto.args.push_back(BindPrototypeArgument{
1380 .type_name =
"ResourceBinder&",
1381 .argument_name =
"command",
1383 proto.args.push_back(BindPrototypeArgument{
1384 .type_name =
"std::shared_ptr<const Texture>",
1385 .argument_name =
"texture",
1387 proto.args.push_back(BindPrototypeArgument{
1388 .type_name =
"const std::unique_ptr<const Sampler>&",
1389 .argument_name =
"sampler",
1392 for (
const auto& separate_image : resources.separate_images) {
1393 auto& proto = prototypes.emplace_back(BindPrototype{});
1394 proto.return_type =
"bool";
1396 proto.descriptor_type =
"DescriptorType::kImage";
1398 std::stringstream
stream;
1399 stream <<
"Bind separate image for resource named " << separate_image.name
1401 proto.docstring =
stream.str();
1403 proto.args.push_back(BindPrototypeArgument{
1404 .type_name =
"Command&",
1405 .argument_name =
"command",
1407 proto.args.push_back(BindPrototypeArgument{
1408 .type_name =
"std::shared_ptr<const Texture>",
1409 .argument_name =
"texture",
1412 for (
const auto& separate_sampler : resources.separate_samplers) {
1413 auto& proto = prototypes.emplace_back(BindPrototype{});
1414 proto.return_type =
"bool";
1416 proto.descriptor_type =
"DescriptorType::kSampler";
1418 std::stringstream
stream;
1419 stream <<
"Bind separate sampler for resource named "
1420 << separate_sampler.name <<
".";
1421 proto.docstring =
stream.str();
1423 proto.args.push_back(BindPrototypeArgument{
1424 .type_name =
"Command&",
1425 .argument_name =
"command",
1427 proto.args.push_back(BindPrototypeArgument{
1428 .type_name =
"std::shared_ptr<const Sampler>",
1429 .argument_name =
"sampler",
1435nlohmann::json::array_t Reflector::EmitBindPrototypes(
1436 const spirv_cross::ShaderResources& resources,
1437 spv::ExecutionModel execution_model)
const {
1438 const auto prototypes = ReflectBindPrototypes(resources, execution_model);
1439 nlohmann::json::array_t
result;
1440 for (
const auto& res : prototypes) {
1441 auto& item =
result.emplace_back(nlohmann::json::object_t{});
1442 item[
"return_type"] = res.return_type;
1443 item[
"name"] = res.name;
1444 item[
"docstring"] = res.docstring;
1445 item[
"descriptor_type"] = res.descriptor_type;
1446 auto&
args = item[
"args"] = nlohmann::json::array_t{};
1447 for (
const auto& arg : res.args) {
1448 auto& json_arg =
args.emplace_back(nlohmann::json::object_t{});
1449 json_arg[
"type_name"] = arg.type_name;
1450 json_arg[
"argument_name"] = arg.argument_name;
static size_t total_size(SkSBlockAllocator< N > &pool)
static SkString resource(SkPDFResourceType type, int index)
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
sk_sp< const SkImage > image
DEF_SWITCHES_START aot vmservice shared library name
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
SK_API sk_sp< PrecompileColorFilter > Matrix()
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