diff options
11 files changed, 272 insertions, 23 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 | } |
diff --git a/src/shader_recompiler/frontend/ir/attribute.cpp b/src/shader_recompiler/frontend/ir/attribute.cpp index 7993e5c43..4d0b8b8e5 100644 --- a/src/shader_recompiler/frontend/ir/attribute.cpp +++ b/src/shader_recompiler/frontend/ir/attribute.cpp | |||
| @@ -20,6 +20,13 @@ u32 GenericAttributeIndex(Attribute attribute) { | |||
| 20 | return (static_cast<u32>(attribute) - static_cast<u32>(Attribute::Generic0X)) / 4u; | 20 | return (static_cast<u32>(attribute) - static_cast<u32>(Attribute::Generic0X)) / 4u; |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | u32 GenericAttributeElement(Attribute attribute) { | ||
| 24 | if (!IsGeneric(attribute)) { | ||
| 25 | throw InvalidArgument("Attribute is not generic {}", attribute); | ||
| 26 | } | ||
| 27 | return static_cast<u32>(attribute) % 4; | ||
| 28 | } | ||
| 29 | |||
| 23 | std::string NameOf(Attribute attribute) { | 30 | std::string NameOf(Attribute attribute) { |
| 24 | switch (attribute) { | 31 | switch (attribute) { |
| 25 | case Attribute::PrimitiveId: | 32 | case Attribute::PrimitiveId: |
diff --git a/src/shader_recompiler/frontend/ir/attribute.h b/src/shader_recompiler/frontend/ir/attribute.h index 34ec7e0cd..8bf2ddf30 100644 --- a/src/shader_recompiler/frontend/ir/attribute.h +++ b/src/shader_recompiler/frontend/ir/attribute.h | |||
| @@ -226,6 +226,8 @@ enum class Attribute : u64 { | |||
| 226 | 226 | ||
| 227 | [[nodiscard]] u32 GenericAttributeIndex(Attribute attribute); | 227 | [[nodiscard]] u32 GenericAttributeIndex(Attribute attribute); |
| 228 | 228 | ||
| 229 | [[nodiscard]] u32 GenericAttributeElement(Attribute attribute); | ||
| 230 | |||
| 229 | [[nodiscard]] std::string NameOf(Attribute attribute); | 231 | [[nodiscard]] std::string NameOf(Attribute attribute); |
| 230 | 232 | ||
| 231 | } // namespace Shader::IR | 233 | } // namespace Shader::IR |
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index 919bec4e2..5ecae71b9 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <vector> | ||
| 8 | #include <optional> | 9 | #include <optional> |
| 9 | 10 | ||
| 10 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| @@ -26,6 +27,13 @@ enum class InputTopology { | |||
| 26 | TrianglesAdjacency, | 27 | TrianglesAdjacency, |
| 27 | }; | 28 | }; |
| 28 | 29 | ||
| 30 | struct TransformFeedbackVarying { | ||
| 31 | u32 buffer{}; | ||
| 32 | u32 stride{}; | ||
| 33 | u32 offset{}; | ||
| 34 | u32 components{}; | ||
| 35 | }; | ||
| 36 | |||
| 29 | struct Profile { | 37 | struct Profile { |
| 30 | u32 supported_spirv{0x00010000}; | 38 | u32 supported_spirv{0x00010000}; |
| 31 | 39 | ||
| @@ -58,6 +66,8 @@ struct Profile { | |||
| 58 | InputTopology input_topology{}; | 66 | InputTopology input_topology{}; |
| 59 | 67 | ||
| 60 | std::optional<float> fixed_state_point_size; | 68 | std::optional<float> fixed_state_point_size; |
| 69 | |||
| 70 | std::vector<TransformFeedbackVarying> xfb_varyings; | ||
| 61 | }; | 71 | }; |
| 62 | 72 | ||
| 63 | } // namespace Shader | 73 | } // namespace Shader |
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index d8f683907..6a3baf837 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp | |||
| @@ -52,6 +52,8 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, | |||
| 52 | const u32 topology_index = static_cast<u32>(regs.draw.topology.Value()); | 52 | const u32 topology_index = static_cast<u32>(regs.draw.topology.Value()); |
| 53 | 53 | ||
| 54 | raw1 = 0; | 54 | raw1 = 0; |
| 55 | no_extended_dynamic_state.Assign(has_extended_dynamic_state ? 0 : 1); | ||
| 56 | xfb_enabled.Assign(regs.tfb_enabled != 0); | ||
| 55 | primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0); | 57 | primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0); |
| 56 | depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0); | 58 | depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0); |
| 57 | depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value()); | 59 | depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value()); |
| @@ -113,10 +115,12 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, | |||
| 113 | return static_cast<u16>(viewport.swizzle.raw); | 115 | return static_cast<u16>(viewport.swizzle.raw); |
| 114 | }); | 116 | }); |
| 115 | } | 117 | } |
| 116 | if (!has_extended_dynamic_state) { | 118 | if (no_extended_dynamic_state != 0) { |
| 117 | no_extended_dynamic_state.Assign(1); | ||
| 118 | dynamic_state.Refresh(regs); | 119 | dynamic_state.Refresh(regs); |
| 119 | } | 120 | } |
| 121 | if (xfb_enabled != 0) { | ||
| 122 | xfb_state.Refresh(regs); | ||
| 123 | } | ||
| 120 | } | 124 | } |
| 121 | 125 | ||
| 122 | void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t index) { | 126 | void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t index) { |
| @@ -158,6 +162,17 @@ void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t | |||
| 158 | enable.Assign(1); | 162 | enable.Assign(1); |
| 159 | } | 163 | } |
| 160 | 164 | ||
| 165 | void FixedPipelineState::TransformFeedbackState::Refresh(const Maxwell& regs) { | ||
| 166 | std::ranges::transform(regs.tfb_layouts, layouts.begin(), [](const auto& layout) { | ||
| 167 | return Layout{ | ||
| 168 | .stream = layout.stream, | ||
| 169 | .varying_count = layout.varying_count, | ||
| 170 | .stride = layout.stride, | ||
| 171 | }; | ||
| 172 | }); | ||
| 173 | varyings = regs.tfb_varying_locs; | ||
| 174 | } | ||
| 175 | |||
| 161 | void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) { | 176 | void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) { |
| 162 | u32 packed_front_face = PackFrontFace(regs.front_face); | 177 | u32 packed_front_face = PackFrontFace(regs.front_face); |
| 163 | if (regs.screen_y_control.triangle_rast_flip != 0) { | 178 | if (regs.screen_y_control.triangle_rast_flip != 0) { |
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index 348f1d6ce..5568c4f72 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h | |||
| @@ -130,6 +130,18 @@ struct FixedPipelineState { | |||
| 130 | } | 130 | } |
| 131 | }; | 131 | }; |
| 132 | 132 | ||
| 133 | struct TransformFeedbackState { | ||
| 134 | struct Layout { | ||
| 135 | u32 stream; | ||
| 136 | u32 varying_count; | ||
| 137 | u32 stride; | ||
| 138 | }; | ||
| 139 | std::array<Layout, Maxwell::NumTransformFeedbackBuffers> layouts; | ||
| 140 | std::array<std::array<u8, 128>, Maxwell::NumTransformFeedbackBuffers> varyings; | ||
| 141 | |||
| 142 | void Refresh(const Maxwell& regs); | ||
| 143 | }; | ||
| 144 | |||
| 133 | struct DynamicState { | 145 | struct DynamicState { |
| 134 | union { | 146 | union { |
| 135 | u32 raw1; | 147 | u32 raw1; |
| @@ -168,6 +180,7 @@ struct FixedPipelineState { | |||
| 168 | union { | 180 | union { |
| 169 | u32 raw1; | 181 | u32 raw1; |
| 170 | BitField<0, 1, u32> no_extended_dynamic_state; | 182 | BitField<0, 1, u32> no_extended_dynamic_state; |
| 183 | BitField<1, 1, u32> xfb_enabled; | ||
| 171 | BitField<2, 1, u32> primitive_restart_enable; | 184 | BitField<2, 1, u32> primitive_restart_enable; |
| 172 | BitField<3, 1, u32> depth_bias_enable; | 185 | BitField<3, 1, u32> depth_bias_enable; |
| 173 | BitField<4, 1, u32> depth_clamp_disabled; | 186 | BitField<4, 1, u32> depth_clamp_disabled; |
| @@ -199,6 +212,7 @@ struct FixedPipelineState { | |||
| 199 | std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments; | 212 | std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments; |
| 200 | std::array<u16, Maxwell::NumViewports> viewport_swizzles; | 213 | std::array<u16, Maxwell::NumViewports> viewport_swizzles; |
| 201 | DynamicState dynamic_state; | 214 | DynamicState dynamic_state; |
| 215 | TransformFeedbackState xfb_state; | ||
| 202 | 216 | ||
| 203 | void Refresh(Tegra::Engines::Maxwell3D& maxwell3d, bool has_extended_dynamic_state); | 217 | void Refresh(Tegra::Engines::Maxwell3D& maxwell3d, bool has_extended_dynamic_state); |
| 204 | 218 | ||
| @@ -211,8 +225,16 @@ struct FixedPipelineState { | |||
| 211 | } | 225 | } |
| 212 | 226 | ||
| 213 | size_t Size() const noexcept { | 227 | size_t Size() const noexcept { |
| 214 | const size_t total_size = sizeof *this; | 228 | if (xfb_enabled != 0) { |
| 215 | return total_size - (no_extended_dynamic_state != 0 ? 0 : sizeof(DynamicState)); | 229 | // When transform feedback is enabled, use the whole struct |
| 230 | return sizeof(*this); | ||
| 231 | } else if (no_extended_dynamic_state != 0) { | ||
| 232 | // Dynamic state is enabled, we can enable more | ||
| 233 | return offsetof(FixedPipelineState, xfb_state); | ||
| 234 | } else { | ||
| 235 | // No XFB, extended dynamic state enabled | ||
| 236 | return offsetof(FixedPipelineState, dynamic_state); | ||
| 237 | } | ||
| 216 | } | 238 | } |
| 217 | }; | 239 | }; |
| 218 | static_assert(std::has_unique_object_representations_v<FixedPipelineState>); | 240 | static_assert(std::has_unique_object_representations_v<FixedPipelineState>); |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 8a59a2611..de52d0f30 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -248,6 +248,10 @@ namespace { | |||
| 248 | using Shader::Backend::SPIRV::EmitSPIRV; | 248 | using Shader::Backend::SPIRV::EmitSPIRV; |
| 249 | using Shader::Maxwell::TranslateProgram; | 249 | using Shader::Maxwell::TranslateProgram; |
| 250 | 250 | ||
| 251 | // TODO: Move this to a separate file | ||
| 252 | constexpr std::array<char, 8> MAGIC_NUMBER{'y', 'u', 'z', 'u', 'c', 'a', 'c', 'h'}; | ||
| 253 | constexpr u32 CACHE_VERSION{1}; | ||
| 254 | |||
| 251 | class GraphicsEnvironment final : public GenericEnvironment { | 255 | class GraphicsEnvironment final : public GenericEnvironment { |
| 252 | public: | 256 | public: |
| 253 | explicit GraphicsEnvironment() = default; | 257 | explicit GraphicsEnvironment() = default; |
| @@ -379,13 +383,14 @@ void SerializePipeline(const Key& key, const Envs& envs, const std::string& file | |||
| 379 | try { | 383 | try { |
| 380 | std::ofstream file; | 384 | std::ofstream file; |
| 381 | file.exceptions(std::ifstream::failbit); | 385 | file.exceptions(std::ifstream::failbit); |
| 382 | Common::FS::OpenFStream(file, filename, std::ios::binary | std::ios::app); | 386 | Common::FS::OpenFStream(file, filename, std::ios::binary | std::ios::ate | std::ios::app); |
| 383 | if (!file.is_open()) { | 387 | if (!file.is_open()) { |
| 384 | LOG_ERROR(Common_Filesystem, "Failed to open pipeline cache file {}", filename); | 388 | LOG_ERROR(Common_Filesystem, "Failed to open pipeline cache file {}", filename); |
| 385 | return; | 389 | return; |
| 386 | } | 390 | } |
| 387 | if (file.tellp() == 0) { | 391 | if (file.tellp() == 0) { |
| 388 | // Write header... | 392 | file.write(MAGIC_NUMBER.data(), MAGIC_NUMBER.size()) |
| 393 | .write(reinterpret_cast<const char*>(&CACHE_VERSION), sizeof(CACHE_VERSION)); | ||
| 389 | } | 394 | } |
| 390 | const std::span key_span(reinterpret_cast<const char*>(&key), sizeof(key)); | 395 | const std::span key_span(reinterpret_cast<const char*>(&key), sizeof(key)); |
| 391 | SerializePipeline(key_span, MakeSpan(envs), file); | 396 | SerializePipeline(key_span, MakeSpan(envs), file); |
| @@ -520,8 +525,27 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading | |||
| 520 | file.exceptions(std::ifstream::failbit); | 525 | file.exceptions(std::ifstream::failbit); |
| 521 | const auto end{file.tellg()}; | 526 | const auto end{file.tellg()}; |
| 522 | file.seekg(0, std::ios::beg); | 527 | file.seekg(0, std::ios::beg); |
| 523 | // Read header... | ||
| 524 | 528 | ||
| 529 | std::array<char, 8> magic_number; | ||
| 530 | u32 cache_version; | ||
| 531 | file.read(magic_number.data(), magic_number.size()) | ||
| 532 | .read(reinterpret_cast<char*>(&cache_version), sizeof(cache_version)); | ||
| 533 | if (magic_number != MAGIC_NUMBER || cache_version != CACHE_VERSION) { | ||
| 534 | file.close(); | ||
| 535 | if (Common::FS::Delete(pipeline_cache_filename)) { | ||
| 536 | if (magic_number != MAGIC_NUMBER) { | ||
| 537 | LOG_ERROR(Render_Vulkan, "Invalid pipeline cache file"); | ||
| 538 | } | ||
| 539 | if (cache_version != CACHE_VERSION) { | ||
| 540 | LOG_INFO(Render_Vulkan, "Deleting old pipeline cache"); | ||
| 541 | } | ||
| 542 | } else { | ||
| 543 | LOG_ERROR(Render_Vulkan, | ||
| 544 | "Invalid pipeline cache file and failed to delete it in \"{}\"", | ||
| 545 | pipeline_cache_filename); | ||
| 546 | } | ||
| 547 | return; | ||
| 548 | } | ||
| 525 | while (file.tellg() != end) { | 549 | while (file.tellg() != end) { |
| 526 | if (stop_loading) { | 550 | if (stop_loading) { |
| 527 | return; | 551 | return; |
| @@ -879,6 +903,88 @@ static Shader::AttributeType CastAttributeType(const FixedPipelineState::VertexA | |||
| 879 | return Shader::AttributeType::Float; | 903 | return Shader::AttributeType::Float; |
| 880 | } | 904 | } |
| 881 | 905 | ||
| 906 | static std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings( | ||
| 907 | const GraphicsPipelineCacheKey& key) { | ||
| 908 | static constexpr std::array VECTORS{ | ||
| 909 | 28, // gl_Position | ||
| 910 | 32, // Generic 0 | ||
| 911 | 36, // Generic 1 | ||
| 912 | 40, // Generic 2 | ||
| 913 | 44, // Generic 3 | ||
| 914 | 48, // Generic 4 | ||
| 915 | 52, // Generic 5 | ||
| 916 | 56, // Generic 6 | ||
| 917 | 60, // Generic 7 | ||
| 918 | 64, // Generic 8 | ||
| 919 | 68, // Generic 9 | ||
| 920 | 72, // Generic 10 | ||
| 921 | 76, // Generic 11 | ||
| 922 | 80, // Generic 12 | ||
| 923 | 84, // Generic 13 | ||
| 924 | 88, // Generic 14 | ||
| 925 | 92, // Generic 15 | ||
| 926 | 96, // Generic 16 | ||
| 927 | 100, // Generic 17 | ||
| 928 | 104, // Generic 18 | ||
| 929 | 108, // Generic 19 | ||
| 930 | 112, // Generic 20 | ||
| 931 | 116, // Generic 21 | ||
| 932 | 120, // Generic 22 | ||
| 933 | 124, // Generic 23 | ||
| 934 | 128, // Generic 24 | ||
| 935 | 132, // Generic 25 | ||
| 936 | 136, // Generic 26 | ||
| 937 | 140, // Generic 27 | ||
| 938 | 144, // Generic 28 | ||
| 939 | 148, // Generic 29 | ||
| 940 | 152, // Generic 30 | ||
| 941 | 156, // Generic 31 | ||
| 942 | 160, // gl_FrontColor | ||
| 943 | 164, // gl_FrontSecondaryColor | ||
| 944 | 160, // gl_BackColor | ||
| 945 | 164, // gl_BackSecondaryColor | ||
| 946 | 192, // gl_TexCoord[0] | ||
| 947 | 196, // gl_TexCoord[1] | ||
| 948 | 200, // gl_TexCoord[2] | ||
| 949 | 204, // gl_TexCoord[3] | ||
| 950 | 208, // gl_TexCoord[4] | ||
| 951 | 212, // gl_TexCoord[5] | ||
| 952 | 216, // gl_TexCoord[6] | ||
| 953 | 220, // gl_TexCoord[7] | ||
| 954 | }; | ||
| 955 | std::vector<Shader::TransformFeedbackVarying> xfb(256); | ||
| 956 | for (size_t buffer = 0; buffer < Maxwell::NumTransformFeedbackBuffers; ++buffer) { | ||
| 957 | const auto& locations = key.state.xfb_state.varyings[buffer]; | ||
| 958 | const auto& layout = key.state.xfb_state.layouts[buffer]; | ||
| 959 | const u32 varying_count = layout.varying_count; | ||
| 960 | u32 highest = 0; | ||
| 961 | for (u32 offset = 0; offset < varying_count; ++offset) { | ||
| 962 | const u32 base_offset = offset; | ||
| 963 | const u8 location = locations[offset]; | ||
| 964 | |||
| 965 | Shader::TransformFeedbackVarying varying; | ||
| 966 | varying.buffer = layout.stream; | ||
| 967 | varying.stride = layout.stride; | ||
| 968 | varying.offset = offset * 4; | ||
| 969 | varying.components = 1; | ||
| 970 | |||
| 971 | if (std::ranges::find(VECTORS, Common::AlignDown(location, 4)) != VECTORS.end()) { | ||
| 972 | UNIMPLEMENTED_IF_MSG(location % 4 != 0, "Unaligned TFB"); | ||
| 973 | |||
| 974 | const u8 base_index = location / 4; | ||
| 975 | while (offset + 1 < varying_count && base_index == locations[offset + 1] / 4) { | ||
| 976 | ++offset; | ||
| 977 | ++varying.components; | ||
| 978 | } | ||
| 979 | } | ||
| 980 | xfb[location] = varying; | ||
| 981 | highest = std::max(highest, (base_offset + varying.components) * 4); | ||
| 982 | } | ||
| 983 | UNIMPLEMENTED_IF(highest != layout.stride); | ||
| 984 | } | ||
| 985 | return xfb; | ||
| 986 | } | ||
| 987 | |||
| 882 | Shader::Profile PipelineCache::MakeProfile(const GraphicsPipelineCacheKey& key, | 988 | Shader::Profile PipelineCache::MakeProfile(const GraphicsPipelineCacheKey& key, |
| 883 | const Shader::IR::Program& program) { | 989 | const Shader::IR::Program& program) { |
| 884 | Shader::Profile profile{base_profile}; | 990 | Shader::Profile profile{base_profile}; |
| @@ -893,6 +999,9 @@ Shader::Profile PipelineCache::MakeProfile(const GraphicsPipelineCacheKey& key, | |||
| 893 | if (key.state.topology == Maxwell::PrimitiveTopology::Points) { | 999 | if (key.state.topology == Maxwell::PrimitiveTopology::Points) { |
| 894 | profile.fixed_state_point_size = point_size; | 1000 | profile.fixed_state_point_size = point_size; |
| 895 | } | 1001 | } |
| 1002 | if (key.state.xfb_enabled != 0) { | ||
| 1003 | profile.xfb_varyings = MakeTransformFeedbackVaryings(key); | ||
| 1004 | } | ||
| 896 | profile.convert_depth_mode = gl_ndc; | 1005 | profile.convert_depth_mode = gl_ndc; |
| 897 | } | 1006 | } |
| 898 | std::ranges::transform(key.state.attributes, profile.generic_input_types.begin(), | 1007 | std::ranges::transform(key.state.attributes, profile.generic_input_types.begin(), |
| @@ -902,6 +1011,9 @@ Shader::Profile PipelineCache::MakeProfile(const GraphicsPipelineCacheKey& key, | |||
| 902 | if (program.output_topology == Shader::OutputTopology::PointList) { | 1011 | if (program.output_topology == Shader::OutputTopology::PointList) { |
| 903 | profile.fixed_state_point_size = point_size; | 1012 | profile.fixed_state_point_size = point_size; |
| 904 | } | 1013 | } |
| 1014 | if (key.state.xfb_enabled != 0) { | ||
| 1015 | profile.xfb_varyings = MakeTransformFeedbackVaryings(key); | ||
| 1016 | } | ||
| 905 | profile.convert_depth_mode = gl_ndc; | 1017 | profile.convert_depth_mode = gl_ndc; |
| 906 | break; | 1018 | break; |
| 907 | default: | 1019 | default: |