diff options
| author | 2021-04-14 01:04:59 -0300 | |
|---|---|---|
| committer | 2021-07-22 21:51:27 -0400 | |
| commit | b126987c59964d81ae3705ad7ad6c0ace8714e19 (patch) | |
| tree | d6384469b9450431de5679dcd12fa2ce18e9a8dd /src/shader_recompiler/backend | |
| parent | shader: Implement early Z tests (diff) | |
| download | yuzu-b126987c59964d81ae3705ad7ad6c0ace8714e19.tar.gz yuzu-b126987c59964d81ae3705ad7ad6c0ace8714e19.tar.xz yuzu-b126987c59964d81ae3705ad7ad6c0ace8714e19.zip | |
shader: Implement transform feedbacks and define file format
Diffstat (limited to 'src/shader_recompiler/backend')
5 files changed, 97 insertions, 16 deletions
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp index df53e58a8..74c42233d 100644 --- a/src/shader_recompiler/backend/spirv/emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/emit_context.cpp | |||
| @@ -135,6 +135,45 @@ Id DefineOutput(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin = | |||
| 135 | return DefineVariable(ctx, type, builtin, spv::StorageClass::Output); | 135 | return DefineVariable(ctx, type, builtin, spv::StorageClass::Output); |
| 136 | } | 136 | } |
| 137 | 137 | ||
| 138 | void DefineGenericOutput(EmitContext& ctx, size_t index) { | ||
| 139 | static constexpr std::string_view swizzle{"xyzw"}; | ||
| 140 | const size_t base_attr_index{static_cast<size_t>(IR::Attribute::Generic0X) + index * 4}; | ||
| 141 | u32 element{0}; | ||
| 142 | while (element < 4) { | ||
| 143 | const u32 remainder{4 - element}; | ||
| 144 | const TransformFeedbackVarying* xfb_varying{}; | ||
| 145 | if (!ctx.profile.xfb_varyings.empty()) { | ||
| 146 | xfb_varying = &ctx.profile.xfb_varyings[base_attr_index + element]; | ||
| 147 | xfb_varying = xfb_varying && xfb_varying->components > 0 ? xfb_varying : nullptr; | ||
| 148 | } | ||
| 149 | const u32 num_components{xfb_varying ? xfb_varying->components : remainder}; | ||
| 150 | |||
| 151 | const Id id{DefineOutput(ctx, ctx.F32[num_components])}; | ||
| 152 | ctx.Decorate(id, spv::Decoration::Location, static_cast<u32>(index)); | ||
| 153 | if (element > 0) { | ||
| 154 | ctx.Decorate(id, spv::Decoration::Component, element); | ||
| 155 | } | ||
| 156 | if (xfb_varying) { | ||
| 157 | ctx.Decorate(id, spv::Decoration::XfbBuffer, xfb_varying->buffer); | ||
| 158 | ctx.Decorate(id, spv::Decoration::XfbStride, xfb_varying->stride); | ||
| 159 | ctx.Decorate(id, spv::Decoration::Offset, xfb_varying->offset); | ||
| 160 | } | ||
| 161 | if (num_components < 4 || element > 0) { | ||
| 162 | ctx.Name(id, fmt::format("out_attr{}", index)); | ||
| 163 | } else { | ||
| 164 | const std::string_view subswizzle{swizzle.substr(element, num_components)}; | ||
| 165 | ctx.Name(id, fmt::format("out_attr{}_{}", index, subswizzle)); | ||
| 166 | } | ||
| 167 | const GenericElementInfo info{ | ||
| 168 | .id = id, | ||
| 169 | .first_element = element, | ||
| 170 | .num_components = num_components, | ||
| 171 | }; | ||
| 172 | std::fill_n(ctx.output_generics[index].begin(), num_components, info); | ||
| 173 | element += num_components; | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 138 | Id GetAttributeType(EmitContext& ctx, AttributeType type) { | 177 | Id GetAttributeType(EmitContext& ctx, AttributeType type) { |
| 139 | switch (type) { | 178 | switch (type) { |
| 140 | case AttributeType::Float: | 179 | case AttributeType::Float: |
| @@ -663,12 +702,15 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) { | |||
| 663 | OpReturn(); | 702 | OpReturn(); |
| 664 | ++label_index; | 703 | ++label_index; |
| 665 | } | 704 | } |
| 666 | for (size_t i = 0; i < info.stores_generics.size(); i++) { | 705 | for (size_t i = 0; i < info.stores_generics.size(); ++i) { |
| 667 | if (!info.stores_generics[i]) { | 706 | if (!info.stores_generics[i]) { |
| 668 | continue; | 707 | continue; |
| 669 | } | 708 | } |
| 709 | if (output_generics[i][0].num_components != 4) { | ||
| 710 | throw NotImplementedException("Physical stores and transform feedbacks"); | ||
| 711 | } | ||
| 670 | AddLabel(labels[label_index]); | 712 | AddLabel(labels[label_index]); |
| 671 | const Id generic_id{output_generics.at(i)}; | 713 | const Id generic_id{output_generics[i][0].id}; |
| 672 | const Id pointer{OpAccessChain(output_f32, generic_id, masked_index)}; | 714 | const Id pointer{OpAccessChain(output_f32, generic_id, masked_index)}; |
| 673 | OpStore(pointer, store_value); | 715 | OpStore(pointer, store_value); |
| 674 | OpReturn(); | 716 | OpReturn(); |
| @@ -1015,11 +1057,9 @@ void EmitContext::DefineOutputs(const Info& info) { | |||
| 1015 | } | 1057 | } |
| 1016 | viewport_index = DefineOutput(*this, U32[1], spv::BuiltIn::ViewportIndex); | 1058 | viewport_index = DefineOutput(*this, U32[1], spv::BuiltIn::ViewportIndex); |
| 1017 | } | 1059 | } |
| 1018 | for (size_t i = 0; i < info.stores_generics.size(); ++i) { | 1060 | for (size_t index = 0; index < info.stores_generics.size(); ++index) { |
| 1019 | if (info.stores_generics[i]) { | 1061 | if (info.stores_generics[index]) { |
| 1020 | output_generics[i] = DefineOutput(*this, F32[4]); | 1062 | DefineGenericOutput(*this, index); |
| 1021 | Decorate(output_generics[i], spv::Decoration::Location, static_cast<u32>(i)); | ||
| 1022 | Name(output_generics[i], fmt::format("out_attr{}", i)); | ||
| 1023 | } | 1063 | } |
| 1024 | } | 1064 | } |
| 1025 | if (stage == Stage::Fragment) { | 1065 | if (stage == Stage::Fragment) { |
diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/emit_context.h index cade1fa0d..b27e5540c 100644 --- a/src/shader_recompiler/backend/spirv/emit_context.h +++ b/src/shader_recompiler/backend/spirv/emit_context.h | |||
| @@ -79,6 +79,12 @@ struct StorageDefinitions { | |||
| 79 | Id U32x4{}; | 79 | Id U32x4{}; |
| 80 | }; | 80 | }; |
| 81 | 81 | ||
| 82 | struct GenericElementInfo { | ||
| 83 | Id id{}; | ||
| 84 | u32 first_element{}; | ||
| 85 | u32 num_components{}; | ||
| 86 | }; | ||
| 87 | |||
| 82 | class EmitContext final : public Sirit::Module { | 88 | class EmitContext final : public Sirit::Module { |
| 83 | public: | 89 | public: |
| 84 | explicit EmitContext(const Profile& profile, IR::Program& program, u32& binding); | 90 | explicit EmitContext(const Profile& profile, IR::Program& program, u32& binding); |
| @@ -189,7 +195,7 @@ public: | |||
| 189 | 195 | ||
| 190 | Id output_point_size{}; | 196 | Id output_point_size{}; |
| 191 | Id output_position{}; | 197 | Id output_position{}; |
| 192 | std::array<Id, 32> output_generics{}; | 198 | std::array<std::array<GenericElementInfo, 4>, 32> output_generics{}; |
| 193 | 199 | ||
| 194 | std::array<Id, 8> frag_color{}; | 200 | std::array<Id, 8> frag_color{}; |
| 195 | Id frag_depth{}; | 201 | Id frag_depth{}; |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 7ad00c434..444ba276f 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp | |||
| @@ -288,6 +288,9 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct | |||
| 288 | if (info.uses_typeless_image_writes) { | 288 | if (info.uses_typeless_image_writes) { |
| 289 | ctx.AddCapability(spv::Capability::StorageImageWriteWithoutFormat); | 289 | ctx.AddCapability(spv::Capability::StorageImageWriteWithoutFormat); |
| 290 | } | 290 | } |
| 291 | if (!ctx.profile.xfb_varyings.empty()) { | ||
| 292 | ctx.AddCapability(spv::Capability::TransformFeedback); | ||
| 293 | } | ||
| 291 | // TODO: Track this usage | 294 | // TODO: Track this usage |
| 292 | ctx.AddCapability(spv::Capability::ImageGatherExtended); | 295 | ctx.AddCapability(spv::Capability::ImageGatherExtended); |
| 293 | ctx.AddCapability(spv::Capability::ImageQuery); | 296 | ctx.AddCapability(spv::Capability::ImageQuery); |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index a91b4c212..f9c151a5c 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp | |||
| @@ -40,11 +40,17 @@ Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&... | |||
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | std::optional<Id> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) { | 42 | std::optional<Id> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) { |
| 43 | const u32 element{static_cast<u32>(attr) % 4}; | ||
| 44 | const auto element_id{[&] { return ctx.Constant(ctx.U32[1], element); }}; | ||
| 45 | if (IR::IsGeneric(attr)) { | 43 | if (IR::IsGeneric(attr)) { |
| 46 | const u32 index{IR::GenericAttributeIndex(attr)}; | 44 | const u32 index{IR::GenericAttributeIndex(attr)}; |
| 47 | return ctx.OpAccessChain(ctx.output_f32, ctx.output_generics.at(index), element_id()); | 45 | const u32 element{IR::GenericAttributeElement(attr)}; |
| 46 | const GenericElementInfo& info{ctx.output_generics.at(index).at(element)}; | ||
| 47 | if (info.num_components == 1) { | ||
| 48 | return info.id; | ||
| 49 | } else { | ||
| 50 | const u32 index_element{element - info.first_element}; | ||
| 51 | const Id index_id{ctx.Constant(ctx.U32[1], index_element)}; | ||
| 52 | return ctx.OpAccessChain(ctx.output_f32, info.id, index_id); | ||
| 53 | } | ||
| 48 | } | 54 | } |
| 49 | switch (attr) { | 55 | switch (attr) { |
| 50 | case IR::Attribute::PointSize: | 56 | case IR::Attribute::PointSize: |
| @@ -52,8 +58,11 @@ std::optional<Id> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) { | |||
| 52 | case IR::Attribute::PositionX: | 58 | case IR::Attribute::PositionX: |
| 53 | case IR::Attribute::PositionY: | 59 | case IR::Attribute::PositionY: |
| 54 | case IR::Attribute::PositionZ: | 60 | case IR::Attribute::PositionZ: |
| 55 | case IR::Attribute::PositionW: | 61 | case IR::Attribute::PositionW: { |
| 56 | return ctx.OpAccessChain(ctx.output_f32, ctx.output_position, element_id()); | 62 | const u32 element{static_cast<u32>(attr) % 4}; |
| 63 | const Id element_id{ctx.Constant(ctx.U32[1], element)}; | ||
| 64 | return ctx.OpAccessChain(ctx.output_f32, ctx.output_position, element_id); | ||
| 65 | } | ||
| 57 | case IR::Attribute::ClipDistance0: | 66 | case IR::Attribute::ClipDistance0: |
| 58 | case IR::Attribute::ClipDistance1: | 67 | case IR::Attribute::ClipDistance1: |
| 59 | case IR::Attribute::ClipDistance2: | 68 | case IR::Attribute::ClipDistance2: |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp index fee740c08..7af29e4dd 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp | |||
| @@ -22,6 +22,21 @@ void SetFixedPipelinePointSize(EmitContext& ctx) { | |||
| 22 | ctx.OpStore(ctx.output_point_size, ctx.Constant(ctx.F32[1], point_size)); | 22 | ctx.OpStore(ctx.output_point_size, ctx.Constant(ctx.F32[1], point_size)); |
| 23 | } | 23 | } |
| 24 | } | 24 | } |
| 25 | |||
| 26 | Id DefaultVarying(EmitContext& ctx, u32 num_components, u32 element, Id zero, Id one, | ||
| 27 | Id default_vector) { | ||
| 28 | switch (num_components) { | ||
| 29 | case 1: | ||
| 30 | return element == 3 ? one : zero; | ||
| 31 | case 2: | ||
| 32 | return ctx.ConstantComposite(ctx.F32[2], zero, element + 1 == 3 ? one : zero); | ||
| 33 | case 3: | ||
| 34 | return ctx.ConstantComposite(ctx.F32[3], zero, zero, element + 2 == 3 ? one : zero); | ||
| 35 | case 4: | ||
| 36 | return default_vector; | ||
| 37 | } | ||
| 38 | throw InvalidArgument("Bad element"); | ||
| 39 | } | ||
| 25 | } // Anonymous namespace | 40 | } // Anonymous namespace |
| 26 | 41 | ||
| 27 | void EmitPrologue(EmitContext& ctx) { | 42 | void EmitPrologue(EmitContext& ctx) { |
| @@ -30,9 +45,17 @@ void EmitPrologue(EmitContext& ctx) { | |||
| 30 | const Id one{ctx.Constant(ctx.F32[1], 1.0f)}; | 45 | const Id one{ctx.Constant(ctx.F32[1], 1.0f)}; |
| 31 | const Id default_vector{ctx.ConstantComposite(ctx.F32[4], zero, zero, zero, one)}; | 46 | const Id default_vector{ctx.ConstantComposite(ctx.F32[4], zero, zero, zero, one)}; |
| 32 | ctx.OpStore(ctx.output_position, default_vector); | 47 | ctx.OpStore(ctx.output_position, default_vector); |
| 33 | for (const Id generic_id : ctx.output_generics) { | 48 | for (const auto& info : ctx.output_generics) { |
| 34 | if (Sirit::ValidId(generic_id)) { | 49 | if (info[0].num_components == 0) { |
| 35 | ctx.OpStore(generic_id, default_vector); | 50 | continue; |
| 51 | } | ||
| 52 | u32 element{0}; | ||
| 53 | while (element < 4) { | ||
| 54 | const auto& element_info{info[element]}; | ||
| 55 | const u32 num{element_info.num_components}; | ||
| 56 | const Id value{DefaultVarying(ctx, num, element, zero, one, default_vector)}; | ||
| 57 | ctx.OpStore(element_info.id, value); | ||
| 58 | element += num; | ||
| 36 | } | 59 | } |
| 37 | } | 60 | } |
| 38 | } | 61 | } |