diff options
| author | 2021-04-19 16:33:23 -0300 | |
|---|---|---|
| committer | 2021-07-22 21:51:28 -0400 | |
| commit | 7018e524f5e6217b3259333acc4ea09ad036d331 (patch) | |
| tree | 58e750b08d48e018accc4de9a05cb483d825904c /src/shader_recompiler/backend/spirv | |
| parent | spirv: Fix ViewportMask (diff) | |
| download | yuzu-7018e524f5e6217b3259333acc4ea09ad036d331.tar.gz yuzu-7018e524f5e6217b3259333acc4ea09ad036d331.tar.xz yuzu-7018e524f5e6217b3259333acc4ea09ad036d331.zip | |
shader: Add NVN storage buffer fallbacks
When we can't track the SSBO origin of a global memory instruction,
leave it as a global memory operation and assume these pointers are in
the NVN storage buffer slots, then apply a linear search in the shader's
runtime.
Diffstat (limited to 'src/shader_recompiler/backend/spirv')
5 files changed, 109 insertions, 24 deletions
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp index 2ffa8c453..7f16cb0dc 100644 --- a/src/shader_recompiler/backend/spirv/emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/emit_context.cpp | |||
| @@ -411,6 +411,7 @@ EmitContext::EmitContext(const Profile& profile_, IR::Program& program, u32& bin | |||
| 411 | DefineTextures(program.info, binding); | 411 | DefineTextures(program.info, binding); |
| 412 | DefineImages(program.info, binding); | 412 | DefineImages(program.info, binding); |
| 413 | DefineAttributeMemAccess(program.info); | 413 | DefineAttributeMemAccess(program.info); |
| 414 | DefineGlobalMemoryFunctions(program.info); | ||
| 414 | DefineLabels(program); | 415 | DefineLabels(program); |
| 415 | } | 416 | } |
| 416 | 417 | ||
| @@ -762,6 +763,82 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) { | |||
| 762 | } | 763 | } |
| 763 | } | 764 | } |
| 764 | 765 | ||
| 766 | void EmitContext::DefineGlobalMemoryFunctions(const Info& info) { | ||
| 767 | if (!info.uses_global_memory) { | ||
| 768 | return; | ||
| 769 | } | ||
| 770 | using DefPtr = Id StorageDefinitions::*; | ||
| 771 | const Id zero{u32_zero_value}; | ||
| 772 | const auto define_body{[&](DefPtr ssbo_member, Id addr, Id element_pointer, u32 shift, | ||
| 773 | auto&& callback) { | ||
| 774 | AddLabel(); | ||
| 775 | const size_t num_buffers{info.storage_buffers_descriptors.size()}; | ||
| 776 | for (size_t index = 0; index < num_buffers; ++index) { | ||
| 777 | const auto& ssbo{info.storage_buffers_descriptors[index]}; | ||
| 778 | const Id ssbo_addr_cbuf_offset{Const(ssbo.cbuf_offset / 8)}; | ||
| 779 | const Id ssbo_size_cbuf_offset{Const(ssbo.cbuf_offset / 4 + 2)}; | ||
| 780 | const Id ssbo_addr_pointer{OpAccessChain( | ||
| 781 | uniform_types.U32x2, cbufs[ssbo.cbuf_index].U32x2, zero, ssbo_addr_cbuf_offset)}; | ||
| 782 | const Id ssbo_size_pointer{OpAccessChain(uniform_types.U32, cbufs[ssbo.cbuf_index].U32, | ||
| 783 | zero, ssbo_size_cbuf_offset)}; | ||
| 784 | |||
| 785 | const Id ssbo_addr{OpBitcast(U64, OpLoad(U32[2], ssbo_addr_pointer))}; | ||
| 786 | const Id ssbo_size{OpUConvert(U64, OpLoad(U32[1], ssbo_size_pointer))}; | ||
| 787 | const Id ssbo_end{OpIAdd(U64, ssbo_addr, ssbo_size)}; | ||
| 788 | const Id cond{OpLogicalAnd(U1, OpUGreaterThanEqual(U1, addr, ssbo_addr), | ||
| 789 | OpULessThan(U1, addr, ssbo_end))}; | ||
| 790 | const Id then_label{OpLabel()}; | ||
| 791 | const Id else_label{OpLabel()}; | ||
| 792 | OpSelectionMerge(else_label, spv::SelectionControlMask::MaskNone); | ||
| 793 | OpBranchConditional(cond, then_label, else_label); | ||
| 794 | AddLabel(then_label); | ||
| 795 | const Id ssbo_id{ssbos[index].*ssbo_member}; | ||
| 796 | const Id ssbo_offset{OpUConvert(U32[1], OpISub(U64, addr, ssbo_addr))}; | ||
| 797 | const Id ssbo_index{OpShiftRightLogical(U32[1], ssbo_offset, Const(shift))}; | ||
| 798 | const Id ssbo_pointer{OpAccessChain(element_pointer, ssbo_id, zero, ssbo_index)}; | ||
| 799 | callback(ssbo_pointer); | ||
| 800 | AddLabel(else_label); | ||
| 801 | } | ||
| 802 | }}; | ||
| 803 | const auto define_load{[&](DefPtr ssbo_member, Id element_pointer, Id type, u32 shift) { | ||
| 804 | const Id function_type{TypeFunction(type, U64)}; | ||
| 805 | const Id func_id{OpFunction(type, spv::FunctionControlMask::MaskNone, function_type)}; | ||
| 806 | const Id addr{OpFunctionParameter(U64)}; | ||
| 807 | define_body(ssbo_member, addr, element_pointer, shift, | ||
| 808 | [&](Id ssbo_pointer) { OpReturnValue(OpLoad(type, ssbo_pointer)); }); | ||
| 809 | OpReturnValue(ConstantNull(type)); | ||
| 810 | OpFunctionEnd(); | ||
| 811 | return func_id; | ||
| 812 | }}; | ||
| 813 | const auto define_write{[&](DefPtr ssbo_member, Id element_pointer, Id type, u32 shift) { | ||
| 814 | const Id function_type{TypeFunction(void_id, U64, type)}; | ||
| 815 | const Id func_id{OpFunction(void_id, spv::FunctionControlMask::MaskNone, function_type)}; | ||
| 816 | const Id addr{OpFunctionParameter(U64)}; | ||
| 817 | const Id data{OpFunctionParameter(type)}; | ||
| 818 | define_body(ssbo_member, addr, element_pointer, shift, [&](Id ssbo_pointer) { | ||
| 819 | OpStore(ssbo_pointer, data); | ||
| 820 | OpReturn(); | ||
| 821 | }); | ||
| 822 | OpReturn(); | ||
| 823 | OpFunctionEnd(); | ||
| 824 | return func_id; | ||
| 825 | }}; | ||
| 826 | const auto define{ | ||
| 827 | [&](DefPtr ssbo_member, const StorageTypeDefinition& type_def, Id type, size_t size) { | ||
| 828 | const Id element_type{type_def.element}; | ||
| 829 | const u32 shift{static_cast<u32>(std::countr_zero(size))}; | ||
| 830 | const Id load_func{define_load(ssbo_member, element_type, type, shift)}; | ||
| 831 | const Id write_func{define_write(ssbo_member, element_type, type, shift)}; | ||
| 832 | return std::make_pair(load_func, write_func); | ||
| 833 | }}; | ||
| 834 | std::tie(load_global_func_u32, write_global_func_u32) = | ||
| 835 | define(&StorageDefinitions::U32, storage_types.U32, U32[1], sizeof(u32)); | ||
| 836 | std::tie(load_global_func_u32x2, write_global_func_u32x2) = | ||
| 837 | define(&StorageDefinitions::U32x2, storage_types.U32x2, U32[2], sizeof(u32[2])); | ||
| 838 | std::tie(load_global_func_u32x4, write_global_func_u32x4) = | ||
| 839 | define(&StorageDefinitions::U32x4, storage_types.U32x4, U32[4], sizeof(u32[4])); | ||
| 840 | } | ||
| 841 | |||
| 765 | void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) { | 842 | void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) { |
| 766 | if (info.constant_buffer_descriptors.empty()) { | 843 | if (info.constant_buffer_descriptors.empty()) { |
| 767 | return; | 844 | return; |
diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/emit_context.h index ef8507367..a4503c7ab 100644 --- a/src/shader_recompiler/backend/spirv/emit_context.h +++ b/src/shader_recompiler/backend/spirv/emit_context.h | |||
| @@ -224,6 +224,13 @@ public: | |||
| 224 | Id f32x2_min_cas{}; | 224 | Id f32x2_min_cas{}; |
| 225 | Id f32x2_max_cas{}; | 225 | Id f32x2_max_cas{}; |
| 226 | 226 | ||
| 227 | Id load_global_func_u32{}; | ||
| 228 | Id load_global_func_u32x2{}; | ||
| 229 | Id load_global_func_u32x4{}; | ||
| 230 | Id write_global_func_u32{}; | ||
| 231 | Id write_global_func_u32x2{}; | ||
| 232 | Id write_global_func_u32x4{}; | ||
| 233 | |||
| 227 | Id input_position{}; | 234 | Id input_position{}; |
| 228 | std::array<Id, 32> input_generics{}; | 235 | std::array<Id, 32> input_generics{}; |
| 229 | 236 | ||
| @@ -255,6 +262,7 @@ private: | |||
| 255 | void DefineTextures(const Info& info, u32& binding); | 262 | void DefineTextures(const Info& info, u32& binding); |
| 256 | void DefineImages(const Info& info, u32& binding); | 263 | void DefineImages(const Info& info, u32& binding); |
| 257 | void DefineAttributeMemAccess(const Info& info); | 264 | void DefineAttributeMemAccess(const Info& info); |
| 265 | void DefineGlobalMemoryFunctions(const Info& info); | ||
| 258 | void DefineLabels(IR::Program& program); | 266 | void DefineLabels(IR::Program& program); |
| 259 | 267 | ||
| 260 | void DefineInputs(const Info& info); | 268 | void DefineInputs(const Info& info); |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h index 67d06faa0..89a82e858 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv.h | |||
| @@ -84,16 +84,16 @@ void EmitLoadGlobalU8(EmitContext& ctx); | |||
| 84 | void EmitLoadGlobalS8(EmitContext& ctx); | 84 | void EmitLoadGlobalS8(EmitContext& ctx); |
| 85 | void EmitLoadGlobalU16(EmitContext& ctx); | 85 | void EmitLoadGlobalU16(EmitContext& ctx); |
| 86 | void EmitLoadGlobalS16(EmitContext& ctx); | 86 | void EmitLoadGlobalS16(EmitContext& ctx); |
| 87 | void EmitLoadGlobal32(EmitContext& ctx); | 87 | Id EmitLoadGlobal32(EmitContext& ctx, Id address); |
| 88 | void EmitLoadGlobal64(EmitContext& ctx); | 88 | Id EmitLoadGlobal64(EmitContext& ctx, Id address); |
| 89 | void EmitLoadGlobal128(EmitContext& ctx); | 89 | Id EmitLoadGlobal128(EmitContext& ctx, Id address); |
| 90 | void EmitWriteGlobalU8(EmitContext& ctx); | 90 | void EmitWriteGlobalU8(EmitContext& ctx); |
| 91 | void EmitWriteGlobalS8(EmitContext& ctx); | 91 | void EmitWriteGlobalS8(EmitContext& ctx); |
| 92 | void EmitWriteGlobalU16(EmitContext& ctx); | 92 | void EmitWriteGlobalU16(EmitContext& ctx); |
| 93 | void EmitWriteGlobalS16(EmitContext& ctx); | 93 | void EmitWriteGlobalS16(EmitContext& ctx); |
| 94 | void EmitWriteGlobal32(EmitContext& ctx); | 94 | void EmitWriteGlobal32(EmitContext& ctx, Id address, Id value); |
| 95 | void EmitWriteGlobal64(EmitContext& ctx); | 95 | void EmitWriteGlobal64(EmitContext& ctx, Id address, Id value); |
| 96 | void EmitWriteGlobal128(EmitContext& ctx); | 96 | void EmitWriteGlobal128(EmitContext& ctx, Id address, Id value); |
| 97 | Id EmitLoadStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | 97 | Id EmitLoadStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); |
| 98 | Id EmitLoadStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | 98 | Id EmitLoadStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); |
| 99 | Id EmitLoadStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | 99 | Id EmitLoadStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); |
| @@ -277,9 +277,9 @@ Id EmitFPIsNan16(EmitContext& ctx, Id value); | |||
| 277 | Id EmitFPIsNan32(EmitContext& ctx, Id value); | 277 | Id EmitFPIsNan32(EmitContext& ctx, Id value); |
| 278 | Id EmitFPIsNan64(EmitContext& ctx, Id value); | 278 | Id EmitFPIsNan64(EmitContext& ctx, Id value); |
| 279 | Id EmitIAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b); | 279 | Id EmitIAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b); |
| 280 | void EmitIAdd64(EmitContext& ctx); | 280 | Id EmitIAdd64(EmitContext& ctx, Id a, Id b); |
| 281 | Id EmitISub32(EmitContext& ctx, Id a, Id b); | 281 | Id EmitISub32(EmitContext& ctx, Id a, Id b); |
| 282 | void EmitISub64(EmitContext& ctx); | 282 | Id EmitISub64(EmitContext& ctx, Id a, Id b); |
| 283 | Id EmitIMul32(EmitContext& ctx, Id a, Id b); | 283 | Id EmitIMul32(EmitContext& ctx, Id a, Id b); |
| 284 | Id EmitINeg32(EmitContext& ctx, Id value); | 284 | Id EmitINeg32(EmitContext& ctx, Id value); |
| 285 | Id EmitINeg64(EmitContext& ctx, Id value); | 285 | Id EmitINeg64(EmitContext& ctx, Id value); |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp index c12d0a513..cd5b1f42c 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp | |||
| @@ -55,16 +55,16 @@ Id EmitIAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { | |||
| 55 | return result; | 55 | return result; |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | void EmitIAdd64(EmitContext&) { | 58 | Id EmitIAdd64(EmitContext& ctx, Id a, Id b) { |
| 59 | throw NotImplementedException("SPIR-V Instruction"); | 59 | return ctx.OpIAdd(ctx.U64, a, b); |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | Id EmitISub32(EmitContext& ctx, Id a, Id b) { | 62 | Id EmitISub32(EmitContext& ctx, Id a, Id b) { |
| 63 | return ctx.OpISub(ctx.U32[1], a, b); | 63 | return ctx.OpISub(ctx.U32[1], a, b); |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | void EmitISub64(EmitContext&) { | 66 | Id EmitISub64(EmitContext& ctx, Id a, Id b) { |
| 67 | throw NotImplementedException("SPIR-V Instruction"); | 67 | return ctx.OpISub(ctx.U64, a, b); |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | Id EmitIMul32(EmitContext& ctx, Id a, Id b) { | 70 | Id EmitIMul32(EmitContext& ctx, Id a, Id b) { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp index 7bf828995..8849258e3 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp | |||
| @@ -64,16 +64,16 @@ void EmitLoadGlobalS16(EmitContext&) { | |||
| 64 | throw NotImplementedException("SPIR-V Instruction"); | 64 | throw NotImplementedException("SPIR-V Instruction"); |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | void EmitLoadGlobal32(EmitContext&) { | 67 | Id EmitLoadGlobal32(EmitContext& ctx, Id address) { |
| 68 | throw NotImplementedException("SPIR-V Instruction"); | 68 | return ctx.OpFunctionCall(ctx.U32[1], ctx.load_global_func_u32, address); |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | void EmitLoadGlobal64(EmitContext&) { | 71 | Id EmitLoadGlobal64(EmitContext& ctx, Id address) { |
| 72 | throw NotImplementedException("SPIR-V Instruction"); | 72 | return ctx.OpFunctionCall(ctx.U32[2], ctx.load_global_func_u32x2, address); |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | void EmitLoadGlobal128(EmitContext&) { | 75 | Id EmitLoadGlobal128(EmitContext& ctx, Id address) { |
| 76 | throw NotImplementedException("SPIR-V Instruction"); | 76 | return ctx.OpFunctionCall(ctx.U32[4], ctx.load_global_func_u32x4, address); |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | void EmitWriteGlobalU8(EmitContext&) { | 79 | void EmitWriteGlobalU8(EmitContext&) { |
| @@ -92,16 +92,16 @@ void EmitWriteGlobalS16(EmitContext&) { | |||
| 92 | throw NotImplementedException("SPIR-V Instruction"); | 92 | throw NotImplementedException("SPIR-V Instruction"); |
| 93 | } | 93 | } |
| 94 | 94 | ||
| 95 | void EmitWriteGlobal32(EmitContext&) { | 95 | void EmitWriteGlobal32(EmitContext& ctx, Id address, Id value) { |
| 96 | throw NotImplementedException("SPIR-V Instruction"); | 96 | ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32, address, value); |
| 97 | } | 97 | } |
| 98 | 98 | ||
| 99 | void EmitWriteGlobal64(EmitContext&) { | 99 | void EmitWriteGlobal64(EmitContext& ctx, Id address, Id value) { |
| 100 | throw NotImplementedException("SPIR-V Instruction"); | 100 | ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32x2, address, value); |
| 101 | } | 101 | } |
| 102 | 102 | ||
| 103 | void EmitWriteGlobal128(EmitContext&) { | 103 | void EmitWriteGlobal128(EmitContext& ctx, Id address, Id value) { |
| 104 | throw NotImplementedException("SPIR-V Instruction"); | 104 | ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32x4, address, value); |
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | Id EmitLoadStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | 107 | Id EmitLoadStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { |