diff options
Diffstat (limited to 'src/shader_recompiler/backend')
4 files changed, 99 insertions, 29 deletions
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h index b412957c7..2b360e073 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv.h | |||
| @@ -22,7 +22,7 @@ constexpr u32 NUM_TEXTURE_AND_IMAGE_SCALING_WORDS = | |||
| 22 | struct RescalingLayout { | 22 | struct RescalingLayout { |
| 23 | alignas(16) std::array<u32, NUM_TEXTURE_SCALING_WORDS> rescaling_textures; | 23 | alignas(16) std::array<u32, NUM_TEXTURE_SCALING_WORDS> rescaling_textures; |
| 24 | alignas(16) std::array<u32, NUM_IMAGE_SCALING_WORDS> rescaling_images; | 24 | alignas(16) std::array<u32, NUM_IMAGE_SCALING_WORDS> rescaling_images; |
| 25 | alignas(16) u32 down_factor; | 25 | u32 down_factor; |
| 26 | }; | 26 | }; |
| 27 | constexpr u32 RESCALING_LAYOUT_WORDS_OFFSET = offsetof(RescalingLayout, rescaling_textures); | 27 | constexpr u32 RESCALING_LAYOUT_WORDS_OFFSET = offsetof(RescalingLayout, rescaling_textures); |
| 28 | constexpr u32 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET = offsetof(RescalingLayout, down_factor); | 28 | constexpr u32 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET = offsetof(RescalingLayout, down_factor); |
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 8ea730c80..80b4bbd27 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 | |||
| @@ -123,34 +123,36 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) { | |||
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | Id GetCbuf(EmitContext& ctx, Id result_type, Id UniformDefinitions::*member_ptr, u32 element_size, | 125 | Id GetCbuf(EmitContext& ctx, Id result_type, Id UniformDefinitions::*member_ptr, u32 element_size, |
| 126 | const IR::Value& binding, const IR::Value& offset) { | 126 | const IR::Value& binding, const IR::Value& offset, const Id indirect_func) { |
| 127 | Id buffer_offset; | ||
| 128 | const Id uniform_type{ctx.uniform_types.*member_ptr}; | ||
| 129 | if (offset.IsImmediate()) { | ||
| 130 | // Hardware been proved to read the aligned offset (e.g. LDC.U32 at 6 will read offset 4) | ||
| 131 | const Id imm_offset{ctx.Const(offset.U32() / element_size)}; | ||
| 132 | buffer_offset = imm_offset; | ||
| 133 | } else if (element_size > 1) { | ||
| 134 | const u32 log2_element_size{static_cast<u32>(std::countr_zero(element_size))}; | ||
| 135 | const Id shift{ctx.Const(log2_element_size)}; | ||
| 136 | buffer_offset = ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.Def(offset), shift); | ||
| 137 | } else { | ||
| 138 | buffer_offset = ctx.Def(offset); | ||
| 139 | } | ||
| 127 | if (!binding.IsImmediate()) { | 140 | if (!binding.IsImmediate()) { |
| 128 | throw NotImplementedException("Constant buffer indexing"); | 141 | return ctx.OpFunctionCall(result_type, indirect_func, ctx.Def(binding), buffer_offset); |
| 129 | } | 142 | } |
| 130 | const Id cbuf{ctx.cbufs[binding.U32()].*member_ptr}; | 143 | const Id cbuf{ctx.cbufs[binding.U32()].*member_ptr}; |
| 131 | const Id uniform_type{ctx.uniform_types.*member_ptr}; | 144 | const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, buffer_offset)}; |
| 132 | if (!offset.IsImmediate()) { | ||
| 133 | Id index{ctx.Def(offset)}; | ||
| 134 | if (element_size > 1) { | ||
| 135 | const u32 log2_element_size{static_cast<u32>(std::countr_zero(element_size))}; | ||
| 136 | const Id shift{ctx.Const(log2_element_size)}; | ||
| 137 | index = ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.Def(offset), shift); | ||
| 138 | } | ||
| 139 | const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, index)}; | ||
| 140 | return ctx.OpLoad(result_type, access_chain); | ||
| 141 | } | ||
| 142 | // Hardware been proved to read the aligned offset (e.g. LDC.U32 at 6 will read offset 4) | ||
| 143 | const Id imm_offset{ctx.Const(offset.U32() / element_size)}; | ||
| 144 | const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, imm_offset)}; | ||
| 145 | return ctx.OpLoad(result_type, access_chain); | 145 | return ctx.OpLoad(result_type, access_chain); |
| 146 | } | 146 | } |
| 147 | 147 | ||
| 148 | Id GetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | 148 | Id GetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { |
| 149 | return GetCbuf(ctx, ctx.U32[1], &UniformDefinitions::U32, sizeof(u32), binding, offset); | 149 | return GetCbuf(ctx, ctx.U32[1], &UniformDefinitions::U32, sizeof(u32), binding, offset, |
| 150 | ctx.load_const_func_u32); | ||
| 150 | } | 151 | } |
| 151 | 152 | ||
| 152 | Id GetCbufU32x4(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | 153 | Id GetCbufU32x4(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { |
| 153 | return GetCbuf(ctx, ctx.U32[4], &UniformDefinitions::U32x4, sizeof(u32[4]), binding, offset); | 154 | return GetCbuf(ctx, ctx.U32[4], &UniformDefinitions::U32x4, sizeof(u32[4]), binding, offset, |
| 155 | ctx.load_const_func_u32x4); | ||
| 154 | } | 156 | } |
| 155 | 157 | ||
| 156 | Id GetCbufElement(EmitContext& ctx, Id vector, const IR::Value& offset, u32 index_offset) { | 158 | Id GetCbufElement(EmitContext& ctx, Id vector, const IR::Value& offset, u32 index_offset) { |
| @@ -201,7 +203,8 @@ void EmitGetIndirectBranchVariable(EmitContext&) { | |||
| 201 | 203 | ||
| 202 | Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | 204 | Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { |
| 203 | if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) { | 205 | if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) { |
| 204 | const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset)}; | 206 | const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset, |
| 207 | ctx.load_const_func_u8)}; | ||
| 205 | return ctx.OpUConvert(ctx.U32[1], load); | 208 | return ctx.OpUConvert(ctx.U32[1], load); |
| 206 | } | 209 | } |
| 207 | Id element{}; | 210 | Id element{}; |
| @@ -217,7 +220,8 @@ Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& of | |||
| 217 | 220 | ||
| 218 | Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | 221 | Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { |
| 219 | if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) { | 222 | if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) { |
| 220 | const Id load{GetCbuf(ctx, ctx.S8, &UniformDefinitions::S8, sizeof(s8), binding, offset)}; | 223 | const Id load{GetCbuf(ctx, ctx.S8, &UniformDefinitions::S8, sizeof(s8), binding, offset, |
| 224 | ctx.load_const_func_u8)}; | ||
| 221 | return ctx.OpSConvert(ctx.U32[1], load); | 225 | return ctx.OpSConvert(ctx.U32[1], load); |
| 222 | } | 226 | } |
| 223 | Id element{}; | 227 | Id element{}; |
| @@ -233,8 +237,8 @@ Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& of | |||
| 233 | 237 | ||
| 234 | Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | 238 | Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { |
| 235 | if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) { | 239 | if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) { |
| 236 | const Id load{ | 240 | const Id load{GetCbuf(ctx, ctx.U16, &UniformDefinitions::U16, sizeof(u16), binding, offset, |
| 237 | GetCbuf(ctx, ctx.U16, &UniformDefinitions::U16, sizeof(u16), binding, offset)}; | 241 | ctx.load_const_func_u16)}; |
| 238 | return ctx.OpUConvert(ctx.U32[1], load); | 242 | return ctx.OpUConvert(ctx.U32[1], load); |
| 239 | } | 243 | } |
| 240 | Id element{}; | 244 | Id element{}; |
| @@ -250,8 +254,8 @@ Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& o | |||
| 250 | 254 | ||
| 251 | Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | 255 | Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { |
| 252 | if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) { | 256 | if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) { |
| 253 | const Id load{ | 257 | const Id load{GetCbuf(ctx, ctx.S16, &UniformDefinitions::S16, sizeof(s16), binding, offset, |
| 254 | GetCbuf(ctx, ctx.S16, &UniformDefinitions::S16, sizeof(s16), binding, offset)}; | 258 | ctx.load_const_func_u16)}; |
| 255 | return ctx.OpSConvert(ctx.U32[1], load); | 259 | return ctx.OpSConvert(ctx.U32[1], load); |
| 256 | } | 260 | } |
| 257 | Id element{}; | 261 | Id element{}; |
| @@ -276,7 +280,8 @@ Id EmitGetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& o | |||
| 276 | 280 | ||
| 277 | Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | 281 | Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { |
| 278 | if (ctx.profile.support_descriptor_aliasing) { | 282 | if (ctx.profile.support_descriptor_aliasing) { |
| 279 | return GetCbuf(ctx, ctx.F32[1], &UniformDefinitions::F32, sizeof(f32), binding, offset); | 283 | return GetCbuf(ctx, ctx.F32[1], &UniformDefinitions::F32, sizeof(f32), binding, offset, |
| 284 | ctx.load_const_func_f32); | ||
| 280 | } else { | 285 | } else { |
| 281 | const Id vector{GetCbufU32x4(ctx, binding, offset)}; | 286 | const Id vector{GetCbufU32x4(ctx, binding, offset)}; |
| 282 | return ctx.OpBitcast(ctx.F32[1], GetCbufElement(ctx, vector, offset, 0u)); | 287 | return ctx.OpBitcast(ctx.F32[1], GetCbufElement(ctx, vector, offset, 0u)); |
| @@ -285,8 +290,8 @@ Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& o | |||
| 285 | 290 | ||
| 286 | Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | 291 | Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { |
| 287 | if (ctx.profile.support_descriptor_aliasing) { | 292 | if (ctx.profile.support_descriptor_aliasing) { |
| 288 | return GetCbuf(ctx, ctx.U32[2], &UniformDefinitions::U32x2, sizeof(u32[2]), binding, | 293 | return GetCbuf(ctx, ctx.U32[2], &UniformDefinitions::U32x2, sizeof(u32[2]), binding, offset, |
| 289 | offset); | 294 | ctx.load_const_func_u32x2); |
| 290 | } else { | 295 | } else { |
| 291 | const Id vector{GetCbufU32x4(ctx, binding, offset)}; | 296 | const Id vector{GetCbufU32x4(ctx, binding, offset)}; |
| 292 | return ctx.OpCompositeConstruct(ctx.U32[2], GetCbufElement(ctx, vector, offset, 0u), | 297 | return ctx.OpCompositeConstruct(ctx.U32[2], GetCbufElement(ctx, vector, offset, 0u), |
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index cd90c084a..aa5b6c9b7 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp | |||
| @@ -464,6 +464,7 @@ EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_inf | |||
| 464 | DefineSharedMemory(program); | 464 | DefineSharedMemory(program); |
| 465 | DefineSharedMemoryFunctions(program); | 465 | DefineSharedMemoryFunctions(program); |
| 466 | DefineConstantBuffers(program.info, uniform_binding); | 466 | DefineConstantBuffers(program.info, uniform_binding); |
| 467 | DefineConstantBufferIndirectFunctions(program.info); | ||
| 467 | DefineStorageBuffers(program.info, storage_binding); | 468 | DefineStorageBuffers(program.info, storage_binding); |
| 468 | DefineTextureBuffers(program.info, texture_binding); | 469 | DefineTextureBuffers(program.info, texture_binding); |
| 469 | DefineImageBuffers(program.info, image_binding); | 470 | DefineImageBuffers(program.info, image_binding); |
| @@ -993,7 +994,7 @@ void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) { | |||
| 993 | } | 994 | } |
| 994 | return; | 995 | return; |
| 995 | } | 996 | } |
| 996 | IR::Type types{info.used_constant_buffer_types}; | 997 | IR::Type types{info.used_constant_buffer_types | info.used_indirect_cbuf_types}; |
| 997 | if (True(types & IR::Type::U8)) { | 998 | if (True(types & IR::Type::U8)) { |
| 998 | if (profile.support_int8) { | 999 | if (profile.support_int8) { |
| 999 | DefineConstBuffers(*this, info, &UniformDefinitions::U8, binding, U8, 'u', sizeof(u8)); | 1000 | DefineConstBuffers(*this, info, &UniformDefinitions::U8, binding, U8, 'u', sizeof(u8)); |
| @@ -1027,6 +1028,62 @@ void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) { | |||
| 1027 | binding += static_cast<u32>(info.constant_buffer_descriptors.size()); | 1028 | binding += static_cast<u32>(info.constant_buffer_descriptors.size()); |
| 1028 | } | 1029 | } |
| 1029 | 1030 | ||
| 1031 | void EmitContext::DefineConstantBufferIndirectFunctions(const Info& info) { | ||
| 1032 | if (!info.uses_cbuf_indirect) { | ||
| 1033 | return; | ||
| 1034 | } | ||
| 1035 | const auto make_accessor{[&](Id buffer_type, Id UniformDefinitions::*member_ptr) { | ||
| 1036 | const Id func_type{TypeFunction(buffer_type, U32[1], U32[1])}; | ||
| 1037 | const Id func{OpFunction(buffer_type, spv::FunctionControlMask::MaskNone, func_type)}; | ||
| 1038 | const Id binding{OpFunctionParameter(U32[1])}; | ||
| 1039 | const Id offset{OpFunctionParameter(U32[1])}; | ||
| 1040 | |||
| 1041 | AddLabel(); | ||
| 1042 | |||
| 1043 | const Id merge_label{OpLabel()}; | ||
| 1044 | const Id uniform_type{uniform_types.*member_ptr}; | ||
| 1045 | |||
| 1046 | std::array<Id, Info::MAX_CBUFS> buf_labels; | ||
| 1047 | std::array<Sirit::Literal, Info::MAX_CBUFS> buf_literals; | ||
| 1048 | for (u32 i = 0; i < Info::MAX_CBUFS; i++) { | ||
| 1049 | buf_labels[i] = OpLabel(); | ||
| 1050 | buf_literals[i] = Sirit::Literal{i}; | ||
| 1051 | } | ||
| 1052 | OpSelectionMerge(merge_label, spv::SelectionControlMask::MaskNone); | ||
| 1053 | OpSwitch(binding, buf_labels[0], buf_literals, buf_labels); | ||
| 1054 | for (u32 i = 0; i < Info::MAX_CBUFS; i++) { | ||
| 1055 | AddLabel(buf_labels[i]); | ||
| 1056 | const Id cbuf{cbufs[i].*member_ptr}; | ||
| 1057 | const Id access_chain{OpAccessChain(uniform_type, cbuf, u32_zero_value, offset)}; | ||
| 1058 | const Id result{OpLoad(buffer_type, access_chain)}; | ||
| 1059 | OpReturnValue(result); | ||
| 1060 | } | ||
| 1061 | AddLabel(merge_label); | ||
| 1062 | OpUnreachable(); | ||
| 1063 | OpFunctionEnd(); | ||
| 1064 | return func; | ||
| 1065 | }}; | ||
| 1066 | IR::Type types{info.used_indirect_cbuf_types}; | ||
| 1067 | if (True(types & IR::Type::U8)) { | ||
| 1068 | load_const_func_u8 = make_accessor(U8, &UniformDefinitions::U8); | ||
| 1069 | } | ||
| 1070 | if (True(types & IR::Type::U16)) { | ||
| 1071 | load_const_func_u16 = make_accessor(U16, &UniformDefinitions::U16); | ||
| 1072 | } | ||
| 1073 | if (True(types & IR::Type::F32)) { | ||
| 1074 | load_const_func_f32 = make_accessor(F32[1], &UniformDefinitions::F32); | ||
| 1075 | } | ||
| 1076 | if (True(types & IR::Type::U32)) { | ||
| 1077 | load_const_func_u32 = make_accessor(U32[1], &UniformDefinitions::U32); | ||
| 1078 | } | ||
| 1079 | if (True(types & IR::Type::U32x2)) { | ||
| 1080 | load_const_func_u32x2 = make_accessor(U32[2], &UniformDefinitions::U32x2); | ||
| 1081 | } | ||
| 1082 | if (True(types & IR::Type::U32x4)) { | ||
| 1083 | load_const_func_u32x4 = make_accessor(U32[4], &UniformDefinitions::U32x4); | ||
| 1084 | } | ||
| 1085 | } | ||
| 1086 | |||
| 1030 | void EmitContext::DefineStorageBuffers(const Info& info, u32& binding) { | 1087 | void EmitContext::DefineStorageBuffers(const Info& info, u32& binding) { |
| 1031 | if (info.storage_buffers_descriptors.empty()) { | 1088 | if (info.storage_buffers_descriptors.empty()) { |
| 1032 | return; | 1089 | return; |
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index f87138f7e..906a1dc2c 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h | |||
| @@ -294,6 +294,13 @@ public: | |||
| 294 | 294 | ||
| 295 | std::vector<Id> interfaces; | 295 | std::vector<Id> interfaces; |
| 296 | 296 | ||
| 297 | Id load_const_func_u8{}; | ||
| 298 | Id load_const_func_u16{}; | ||
| 299 | Id load_const_func_u32{}; | ||
| 300 | Id load_const_func_f32{}; | ||
| 301 | Id load_const_func_u32x2{}; | ||
| 302 | Id load_const_func_u32x4{}; | ||
| 303 | |||
| 297 | private: | 304 | private: |
| 298 | void DefineCommonTypes(const Info& info); | 305 | void DefineCommonTypes(const Info& info); |
| 299 | void DefineCommonConstants(); | 306 | void DefineCommonConstants(); |
| @@ -302,6 +309,7 @@ private: | |||
| 302 | void DefineSharedMemory(const IR::Program& program); | 309 | void DefineSharedMemory(const IR::Program& program); |
| 303 | void DefineSharedMemoryFunctions(const IR::Program& program); | 310 | void DefineSharedMemoryFunctions(const IR::Program& program); |
| 304 | void DefineConstantBuffers(const Info& info, u32& binding); | 311 | void DefineConstantBuffers(const Info& info, u32& binding); |
| 312 | void DefineConstantBufferIndirectFunctions(const Info& info); | ||
| 305 | void DefineStorageBuffers(const Info& info, u32& binding); | 313 | void DefineStorageBuffers(const Info& info, u32& binding); |
| 306 | void DefineTextureBuffers(const Info& info, u32& binding); | 314 | void DefineTextureBuffers(const Info& info, u32& binding); |
| 307 | void DefineImageBuffers(const Info& info, u32& binding); | 315 | void DefineImageBuffers(const Info& info, u32& binding); |