diff options
Diffstat (limited to 'src/shader_recompiler/backend')
12 files changed, 408 insertions, 218 deletions
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp new file mode 100644 index 000000000..1c985aff8 --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_context.cpp | |||
| @@ -0,0 +1,160 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <array> | ||
| 7 | #include <string_view> | ||
| 8 | |||
| 9 | #include <fmt/format.h> | ||
| 10 | |||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "shader_recompiler/backend/spirv/emit_context.h" | ||
| 13 | |||
| 14 | namespace Shader::Backend::SPIRV { | ||
| 15 | |||
| 16 | void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) { | ||
| 17 | defs[0] = sirit_ctx.Name(base_type, name); | ||
| 18 | |||
| 19 | std::array<char, 6> def_name; | ||
| 20 | for (int i = 1; i < 4; ++i) { | ||
| 21 | const std::string_view def_name_view( | ||
| 22 | def_name.data(), | ||
| 23 | fmt::format_to_n(def_name.data(), def_name.size(), "{}x{}", name, i + 1).size); | ||
| 24 | defs[i] = sirit_ctx.Name(sirit_ctx.TypeVector(base_type, i + 1), def_name_view); | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | EmitContext::EmitContext(IR::Program& program) : Sirit::Module(0x00010000) { | ||
| 29 | AddCapability(spv::Capability::Shader); | ||
| 30 | DefineCommonTypes(program.info); | ||
| 31 | DefineCommonConstants(); | ||
| 32 | DefineSpecialVariables(program.info); | ||
| 33 | DefineConstantBuffers(program.info); | ||
| 34 | DefineStorageBuffers(program.info); | ||
| 35 | DefineLabels(program); | ||
| 36 | } | ||
| 37 | |||
| 38 | EmitContext::~EmitContext() = default; | ||
| 39 | |||
| 40 | Id EmitContext::Def(const IR::Value& value) { | ||
| 41 | if (!value.IsImmediate()) { | ||
| 42 | return value.Inst()->Definition<Id>(); | ||
| 43 | } | ||
| 44 | switch (value.Type()) { | ||
| 45 | case IR::Type::U1: | ||
| 46 | return value.U1() ? true_value : false_value; | ||
| 47 | case IR::Type::U32: | ||
| 48 | return Constant(U32[1], value.U32()); | ||
| 49 | case IR::Type::F32: | ||
| 50 | return Constant(F32[1], value.F32()); | ||
| 51 | default: | ||
| 52 | throw NotImplementedException("Immediate type {}", value.Type()); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | void EmitContext::DefineCommonTypes(const Info& info) { | ||
| 57 | void_id = TypeVoid(); | ||
| 58 | |||
| 59 | U1 = Name(TypeBool(), "u1"); | ||
| 60 | |||
| 61 | F32.Define(*this, TypeFloat(32), "f32"); | ||
| 62 | U32.Define(*this, TypeInt(32, false), "u32"); | ||
| 63 | |||
| 64 | if (info.uses_fp16) { | ||
| 65 | AddCapability(spv::Capability::Float16); | ||
| 66 | F16.Define(*this, TypeFloat(16), "f16"); | ||
| 67 | } | ||
| 68 | if (info.uses_fp64) { | ||
| 69 | AddCapability(spv::Capability::Float64); | ||
| 70 | F64.Define(*this, TypeFloat(64), "f64"); | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | void EmitContext::DefineCommonConstants() { | ||
| 75 | true_value = ConstantTrue(U1); | ||
| 76 | false_value = ConstantFalse(U1); | ||
| 77 | u32_zero_value = Constant(U32[1], 0U); | ||
| 78 | } | ||
| 79 | |||
| 80 | void EmitContext::DefineSpecialVariables(const Info& info) { | ||
| 81 | const auto define{[this](Id type, spv::BuiltIn builtin, spv::StorageClass storage_class) { | ||
| 82 | const Id pointer_type{TypePointer(storage_class, type)}; | ||
| 83 | const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::Input)}; | ||
| 84 | Decorate(id, spv::Decoration::BuiltIn, builtin); | ||
| 85 | return id; | ||
| 86 | }}; | ||
| 87 | using namespace std::placeholders; | ||
| 88 | const auto define_input{std::bind(define, _1, _2, spv::StorageClass::Input)}; | ||
| 89 | |||
| 90 | if (info.uses_workgroup_id) { | ||
| 91 | workgroup_id = define_input(U32[3], spv::BuiltIn::WorkgroupId); | ||
| 92 | } | ||
| 93 | if (info.uses_local_invocation_id) { | ||
| 94 | local_invocation_id = define_input(U32[3], spv::BuiltIn::LocalInvocationId); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | void EmitContext::DefineConstantBuffers(const Info& info) { | ||
| 99 | if (info.constant_buffer_descriptors.empty()) { | ||
| 100 | return; | ||
| 101 | } | ||
| 102 | const Id array_type{TypeArray(U32[1], Constant(U32[1], 4096))}; | ||
| 103 | Decorate(array_type, spv::Decoration::ArrayStride, 16U); | ||
| 104 | |||
| 105 | const Id struct_type{TypeStruct(array_type)}; | ||
| 106 | Name(struct_type, "cbuf_block"); | ||
| 107 | Decorate(struct_type, spv::Decoration::Block); | ||
| 108 | MemberName(struct_type, 0, "data"); | ||
| 109 | MemberDecorate(struct_type, 0, spv::Decoration::Offset, 0U); | ||
| 110 | |||
| 111 | const Id uniform_type{TypePointer(spv::StorageClass::Uniform, struct_type)}; | ||
| 112 | uniform_u32 = TypePointer(spv::StorageClass::Uniform, U32[1]); | ||
| 113 | |||
| 114 | u32 binding{}; | ||
| 115 | for (const Info::ConstantBufferDescriptor& desc : info.constant_buffer_descriptors) { | ||
| 116 | const Id id{AddGlobalVariable(uniform_type, spv::StorageClass::Uniform)}; | ||
| 117 | Decorate(id, spv::Decoration::Binding, binding); | ||
| 118 | Name(id, fmt::format("c{}", desc.index)); | ||
| 119 | std::fill_n(cbufs.data() + desc.index, desc.count, id); | ||
| 120 | binding += desc.count; | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | void EmitContext::DefineStorageBuffers(const Info& info) { | ||
| 125 | if (info.storage_buffers_descriptors.empty()) { | ||
| 126 | return; | ||
| 127 | } | ||
| 128 | AddExtension("SPV_KHR_storage_buffer_storage_class"); | ||
| 129 | |||
| 130 | const Id array_type{TypeRuntimeArray(U32[1])}; | ||
| 131 | Decorate(array_type, spv::Decoration::ArrayStride, 4U); | ||
| 132 | |||
| 133 | const Id struct_type{TypeStruct(array_type)}; | ||
| 134 | Name(struct_type, "ssbo_block"); | ||
| 135 | Decorate(struct_type, spv::Decoration::Block); | ||
| 136 | MemberName(struct_type, 0, "data"); | ||
| 137 | MemberDecorate(struct_type, 0, spv::Decoration::Offset, 0U); | ||
| 138 | |||
| 139 | const Id storage_type{TypePointer(spv::StorageClass::StorageBuffer, struct_type)}; | ||
| 140 | storage_u32 = TypePointer(spv::StorageClass::StorageBuffer, U32[1]); | ||
| 141 | |||
| 142 | u32 binding{}; | ||
| 143 | for (const Info::StorageBufferDescriptor& desc : info.storage_buffers_descriptors) { | ||
| 144 | const Id id{AddGlobalVariable(storage_type, spv::StorageClass::StorageBuffer)}; | ||
| 145 | Decorate(id, spv::Decoration::Binding, binding); | ||
| 146 | Name(id, fmt::format("ssbo{}", binding)); | ||
| 147 | std::fill_n(ssbos.data() + binding, desc.count, id); | ||
| 148 | binding += desc.count; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | void EmitContext::DefineLabels(IR::Program& program) { | ||
| 153 | for (const IR::Function& function : program.functions) { | ||
| 154 | for (IR::Block* const block : function.blocks) { | ||
| 155 | block->SetDefinition(OpLabel()); | ||
| 156 | } | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/emit_context.h new file mode 100644 index 000000000..c4b84759d --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_context.h | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <string_view> | ||
| 9 | |||
| 10 | #include <sirit/sirit.h> | ||
| 11 | |||
| 12 | #include "shader_recompiler/frontend/ir/program.h" | ||
| 13 | #include "shader_recompiler/shader_info.h" | ||
| 14 | |||
| 15 | namespace Shader::Backend::SPIRV { | ||
| 16 | |||
| 17 | using Sirit::Id; | ||
| 18 | |||
| 19 | class VectorTypes { | ||
| 20 | public: | ||
| 21 | void Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name); | ||
| 22 | |||
| 23 | [[nodiscard]] Id operator[](size_t size) const noexcept { | ||
| 24 | return defs[size - 1]; | ||
| 25 | } | ||
| 26 | |||
| 27 | private: | ||
| 28 | std::array<Id, 4> defs{}; | ||
| 29 | }; | ||
| 30 | |||
| 31 | class EmitContext final : public Sirit::Module { | ||
| 32 | public: | ||
| 33 | explicit EmitContext(IR::Program& program); | ||
| 34 | ~EmitContext(); | ||
| 35 | |||
| 36 | [[nodiscard]] Id Def(const IR::Value& value); | ||
| 37 | |||
| 38 | Id void_id{}; | ||
| 39 | Id U1{}; | ||
| 40 | VectorTypes F32; | ||
| 41 | VectorTypes U32; | ||
| 42 | VectorTypes F16; | ||
| 43 | VectorTypes F64; | ||
| 44 | |||
| 45 | Id true_value{}; | ||
| 46 | Id false_value{}; | ||
| 47 | Id u32_zero_value{}; | ||
| 48 | |||
| 49 | Id uniform_u32{}; | ||
| 50 | Id storage_u32{}; | ||
| 51 | |||
| 52 | std::array<Id, Info::MAX_CBUFS> cbufs{}; | ||
| 53 | std::array<Id, Info::MAX_SSBOS> ssbos{}; | ||
| 54 | |||
| 55 | Id workgroup_id{}; | ||
| 56 | Id local_invocation_id{}; | ||
| 57 | |||
| 58 | private: | ||
| 59 | void DefineCommonTypes(const Info& info); | ||
| 60 | void DefineCommonConstants(); | ||
| 61 | void DefineSpecialVariables(const Info& info); | ||
| 62 | void DefineConstantBuffers(const Info& info); | ||
| 63 | void DefineStorageBuffers(const Info& info); | ||
| 64 | void DefineLabels(IR::Program& program); | ||
| 65 | }; | ||
| 66 | |||
| 67 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 0895414b4..c79c09774 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp | |||
| @@ -12,31 +12,83 @@ | |||
| 12 | #include "shader_recompiler/frontend/ir/program.h" | 12 | #include "shader_recompiler/frontend/ir/program.h" |
| 13 | 13 | ||
| 14 | namespace Shader::Backend::SPIRV { | 14 | namespace Shader::Backend::SPIRV { |
| 15 | namespace { | ||
| 16 | template <class Func> | ||
| 17 | struct FuncTraits : FuncTraits<decltype(&Func::operator())> {}; | ||
| 15 | 18 | ||
| 16 | EmitContext::EmitContext(IR::Program& program) { | 19 | template <class ClassType, class ReturnType_, class... Args> |
| 17 | AddCapability(spv::Capability::Shader); | 20 | struct FuncTraits<ReturnType_ (ClassType::*)(Args...)> { |
| 18 | AddCapability(spv::Capability::Float16); | 21 | using ReturnType = ReturnType_; |
| 19 | AddCapability(spv::Capability::Float64); | ||
| 20 | void_id = TypeVoid(); | ||
| 21 | 22 | ||
| 22 | u1 = Name(TypeBool(), "u1"); | 23 | static constexpr size_t NUM_ARGS = sizeof...(Args); |
| 23 | f32.Define(*this, TypeFloat(32), "f32"); | ||
| 24 | u32.Define(*this, TypeInt(32, false), "u32"); | ||
| 25 | f16.Define(*this, TypeFloat(16), "f16"); | ||
| 26 | f64.Define(*this, TypeFloat(64), "f64"); | ||
| 27 | 24 | ||
| 28 | true_value = ConstantTrue(u1); | 25 | template <size_t I> |
| 29 | false_value = ConstantFalse(u1); | 26 | using ArgType = std::tuple_element_t<I, std::tuple<Args...>>; |
| 27 | }; | ||
| 30 | 28 | ||
| 31 | for (const IR::Function& function : program.functions) { | 29 | template <auto method, typename... Args> |
| 32 | for (IR::Block* const block : function.blocks) { | 30 | void SetDefinition(EmitSPIRV& emit, EmitContext& ctx, IR::Inst* inst, Args... args) { |
| 33 | block_label_map.emplace_back(block, OpLabel()); | 31 | const Id forward_id{inst->Definition<Id>()}; |
| 32 | const bool has_forward_id{Sirit::ValidId(forward_id)}; | ||
| 33 | Id current_id{}; | ||
| 34 | if (has_forward_id) { | ||
| 35 | current_id = ctx.ExchangeCurrentId(forward_id); | ||
| 36 | } | ||
| 37 | const Id new_id{(emit.*method)(ctx, std::forward<Args>(args)...)}; | ||
| 38 | if (has_forward_id) { | ||
| 39 | ctx.ExchangeCurrentId(current_id); | ||
| 40 | } else { | ||
| 41 | inst->SetDefinition<Id>(new_id); | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | template <typename ArgType> | ||
| 46 | ArgType Arg(EmitContext& ctx, const IR::Value& arg) { | ||
| 47 | if constexpr (std::is_same_v<ArgType, Id>) { | ||
| 48 | return ctx.Def(arg); | ||
| 49 | } else if constexpr (std::is_same_v<ArgType, const IR::Value&>) { | ||
| 50 | return arg; | ||
| 51 | } else if constexpr (std::is_same_v<ArgType, u32>) { | ||
| 52 | return arg.U32(); | ||
| 53 | } else if constexpr (std::is_same_v<ArgType, IR::Block*>) { | ||
| 54 | return arg.Label(); | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | template <auto method, bool is_first_arg_inst, size_t... I> | ||
| 59 | void Invoke(EmitSPIRV& emit, EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) { | ||
| 60 | using Traits = FuncTraits<decltype(method)>; | ||
| 61 | if constexpr (std::is_same_v<Traits::ReturnType, Id>) { | ||
| 62 | if constexpr (is_first_arg_inst) { | ||
| 63 | SetDefinition<method>(emit, ctx, inst, inst, | ||
| 64 | Arg<Traits::ArgType<I + 2>>(ctx, inst->Arg(I))...); | ||
| 65 | } else { | ||
| 66 | SetDefinition<method>(emit, ctx, inst, | ||
| 67 | Arg<Traits::ArgType<I + 1>>(ctx, inst->Arg(I))...); | ||
| 68 | } | ||
| 69 | } else { | ||
| 70 | if constexpr (is_first_arg_inst) { | ||
| 71 | (emit.*method)(ctx, inst, Arg<Traits::ArgType<I + 2>>(ctx, inst->Arg(I))...); | ||
| 72 | } else { | ||
| 73 | (emit.*method)(ctx, Arg<Traits::ArgType<I + 1>>(ctx, inst->Arg(I))...); | ||
| 34 | } | 74 | } |
| 35 | } | 75 | } |
| 36 | std::ranges::sort(block_label_map, {}, &std::pair<IR::Block*, Id>::first); | ||
| 37 | } | 76 | } |
| 38 | 77 | ||
| 39 | EmitContext::~EmitContext() = default; | 78 | template <auto method> |
| 79 | void Invoke(EmitSPIRV& emit, EmitContext& ctx, IR::Inst* inst) { | ||
| 80 | using Traits = FuncTraits<decltype(method)>; | ||
| 81 | static_assert(Traits::NUM_ARGS >= 1, "Insufficient arguments"); | ||
| 82 | if constexpr (Traits::NUM_ARGS == 1) { | ||
| 83 | Invoke<method, false>(emit, ctx, inst, std::make_index_sequence<0>{}); | ||
| 84 | } else { | ||
| 85 | using FirstArgType = typename Traits::template ArgType<1>; | ||
| 86 | static constexpr bool is_first_arg_inst = std::is_same_v<FirstArgType, IR::Inst*>; | ||
| 87 | using Indices = std::make_index_sequence<Traits::NUM_ARGS - (is_first_arg_inst ? 2 : 1)>; | ||
| 88 | Invoke<method, is_first_arg_inst>(emit, ctx, inst, Indices{}); | ||
| 89 | } | ||
| 90 | } | ||
| 91 | } // Anonymous namespace | ||
| 40 | 92 | ||
| 41 | EmitSPIRV::EmitSPIRV(IR::Program& program) { | 93 | EmitSPIRV::EmitSPIRV(IR::Program& program) { |
| 42 | EmitContext ctx{program}; | 94 | EmitContext ctx{program}; |
| @@ -46,74 +98,32 @@ EmitSPIRV::EmitSPIRV(IR::Program& program) { | |||
| 46 | for (IR::Function& function : program.functions) { | 98 | for (IR::Function& function : program.functions) { |
| 47 | func = ctx.OpFunction(ctx.void_id, spv::FunctionControlMask::MaskNone, void_function); | 99 | func = ctx.OpFunction(ctx.void_id, spv::FunctionControlMask::MaskNone, void_function); |
| 48 | for (IR::Block* const block : function.blocks) { | 100 | for (IR::Block* const block : function.blocks) { |
| 49 | ctx.AddLabel(ctx.BlockLabel(block)); | 101 | ctx.AddLabel(block->Definition<Id>()); |
| 50 | for (IR::Inst& inst : block->Instructions()) { | 102 | for (IR::Inst& inst : block->Instructions()) { |
| 51 | EmitInst(ctx, &inst); | 103 | EmitInst(ctx, &inst); |
| 52 | } | 104 | } |
| 53 | } | 105 | } |
| 54 | ctx.OpFunctionEnd(); | 106 | ctx.OpFunctionEnd(); |
| 55 | } | 107 | } |
| 56 | ctx.AddEntryPoint(spv::ExecutionModel::GLCompute, func, "main"); | 108 | boost::container::small_vector<Id, 32> interfaces; |
| 109 | if (program.info.uses_workgroup_id) { | ||
| 110 | interfaces.push_back(ctx.workgroup_id); | ||
| 111 | } | ||
| 112 | if (program.info.uses_local_invocation_id) { | ||
| 113 | interfaces.push_back(ctx.local_invocation_id); | ||
| 114 | } | ||
| 115 | |||
| 116 | const std::span interfaces_span(interfaces.data(), interfaces.size()); | ||
| 117 | ctx.AddEntryPoint(spv::ExecutionModel::Fragment, func, "main", interfaces_span); | ||
| 118 | ctx.AddExecutionMode(func, spv::ExecutionMode::OriginUpperLeft); | ||
| 57 | 119 | ||
| 58 | std::vector<u32> result{ctx.Assemble()}; | 120 | std::vector<u32> result{ctx.Assemble()}; |
| 59 | std::FILE* file{std::fopen("shader.spv", "wb")}; | 121 | std::FILE* file{std::fopen("D:\\shader.spv", "wb")}; |
| 60 | std::fwrite(result.data(), sizeof(u32), result.size(), file); | 122 | std::fwrite(result.data(), sizeof(u32), result.size(), file); |
| 61 | std::fclose(file); | 123 | std::fclose(file); |
| 62 | std::system("spirv-dis shader.spv"); | 124 | std::system("spirv-dis D:\\shader.spv") == 0 && |
| 63 | std::system("spirv-val shader.spv"); | 125 | std::system("spirv-val --uniform-buffer-standard-layout D:\\shader.spv") == 0 && |
| 64 | std::system("spirv-cross shader.spv"); | 126 | std::system("spirv-cross -V D:\\shader.spv") == 0; |
| 65 | } | ||
| 66 | |||
| 67 | template <auto method, typename... Args> | ||
| 68 | static void SetDefinition(EmitSPIRV& emit, EmitContext& ctx, IR::Inst* inst, Args... args) { | ||
| 69 | const Id forward_id{inst->Definition<Id>()}; | ||
| 70 | const bool has_forward_id{Sirit::ValidId(forward_id)}; | ||
| 71 | Id current_id{}; | ||
| 72 | if (has_forward_id) { | ||
| 73 | current_id = ctx.ExchangeCurrentId(forward_id); | ||
| 74 | } | ||
| 75 | const Id new_id{(emit.*method)(ctx, std::forward<Args>(args)...)}; | ||
| 76 | if (has_forward_id) { | ||
| 77 | ctx.ExchangeCurrentId(current_id); | ||
| 78 | } else { | ||
| 79 | inst->SetDefinition<Id>(new_id); | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | template <auto method> | ||
| 84 | static void Invoke(EmitSPIRV& emit, EmitContext& ctx, IR::Inst* inst) { | ||
| 85 | using M = decltype(method); | ||
| 86 | using std::is_invocable_r_v; | ||
| 87 | if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&>) { | ||
| 88 | SetDefinition<method>(emit, ctx, inst); | ||
| 89 | } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, Id>) { | ||
| 90 | SetDefinition<method>(emit, ctx, inst, ctx.Def(inst->Arg(0))); | ||
| 91 | } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, Id, Id>) { | ||
| 92 | SetDefinition<method>(emit, ctx, inst, ctx.Def(inst->Arg(0)), ctx.Def(inst->Arg(1))); | ||
| 93 | } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, Id, Id, Id>) { | ||
| 94 | SetDefinition<method>(emit, ctx, inst, ctx.Def(inst->Arg(0)), ctx.Def(inst->Arg(1)), | ||
| 95 | ctx.Def(inst->Arg(2))); | ||
| 96 | } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, IR::Inst*>) { | ||
| 97 | SetDefinition<method>(emit, ctx, inst, inst); | ||
| 98 | } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, IR::Inst*, Id, Id>) { | ||
| 99 | SetDefinition<method>(emit, ctx, inst, inst, ctx.Def(inst->Arg(0)), ctx.Def(inst->Arg(1))); | ||
| 100 | } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, IR::Inst*, Id, Id, Id>) { | ||
| 101 | SetDefinition<method>(emit, ctx, inst, inst, ctx.Def(inst->Arg(0)), ctx.Def(inst->Arg(1)), | ||
| 102 | ctx.Def(inst->Arg(2))); | ||
| 103 | } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, Id, u32>) { | ||
| 104 | SetDefinition<method>(emit, ctx, inst, ctx.Def(inst->Arg(0)), inst->Arg(1).U32()); | ||
| 105 | } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, const IR::Value&>) { | ||
| 106 | SetDefinition<method>(emit, ctx, inst, inst->Arg(0)); | ||
| 107 | } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, const IR::Value&, | ||
| 108 | const IR::Value&>) { | ||
| 109 | SetDefinition<method>(emit, ctx, inst, inst->Arg(0), inst->Arg(1)); | ||
| 110 | } else if constexpr (is_invocable_r_v<void, M, EmitSPIRV&, EmitContext&, IR::Inst*>) { | ||
| 111 | (emit.*method)(ctx, inst); | ||
| 112 | } else if constexpr (is_invocable_r_v<void, M, EmitSPIRV&, EmitContext&>) { | ||
| 113 | (emit.*method)(ctx); | ||
| 114 | } else { | ||
| 115 | static_assert(false, "Bad format"); | ||
| 116 | } | ||
| 117 | } | 127 | } |
| 118 | 128 | ||
| 119 | void EmitSPIRV::EmitInst(EmitContext& ctx, IR::Inst* inst) { | 129 | void EmitSPIRV::EmitInst(EmitContext& ctx, IR::Inst* inst) { |
| @@ -130,9 +140,9 @@ void EmitSPIRV::EmitInst(EmitContext& ctx, IR::Inst* inst) { | |||
| 130 | static Id TypeId(const EmitContext& ctx, IR::Type type) { | 140 | static Id TypeId(const EmitContext& ctx, IR::Type type) { |
| 131 | switch (type) { | 141 | switch (type) { |
| 132 | case IR::Type::U1: | 142 | case IR::Type::U1: |
| 133 | return ctx.u1; | 143 | return ctx.U1; |
| 134 | case IR::Type::U32: | 144 | case IR::Type::U32: |
| 135 | return ctx.u32[1]; | 145 | return ctx.U32[1]; |
| 136 | default: | 146 | default: |
| 137 | throw NotImplementedException("Phi node type {}", type); | 147 | throw NotImplementedException("Phi node type {}", type); |
| 138 | } | 148 | } |
| @@ -162,7 +172,7 @@ Id EmitSPIRV::EmitPhi(EmitContext& ctx, IR::Inst* inst) { | |||
| 162 | } | 172 | } |
| 163 | IR::Block* const phi_block{inst->PhiBlock(index)}; | 173 | IR::Block* const phi_block{inst->PhiBlock(index)}; |
| 164 | operands.push_back(def); | 174 | operands.push_back(def); |
| 165 | operands.push_back(ctx.BlockLabel(phi_block)); | 175 | operands.push_back(phi_block->Definition<Id>()); |
| 166 | } | 176 | } |
| 167 | const Id result_type{TypeId(ctx, inst->Arg(0).Type())}; | 177 | const Id result_type{TypeId(ctx, inst->Arg(0).Type())}; |
| 168 | return ctx.OpPhi(result_type, std::span(operands.data(), operands.size())); | 178 | return ctx.OpPhi(result_type, std::span(operands.data(), operands.size())); |
| @@ -174,29 +184,6 @@ void EmitSPIRV::EmitIdentity(EmitContext&) { | |||
| 174 | throw NotImplementedException("SPIR-V Instruction"); | 184 | throw NotImplementedException("SPIR-V Instruction"); |
| 175 | } | 185 | } |
| 176 | 186 | ||
| 177 | // FIXME: Move to its own file | ||
| 178 | void EmitSPIRV::EmitBranch(EmitContext& ctx, IR::Inst* inst) { | ||
| 179 | ctx.OpBranch(ctx.BlockLabel(inst->Arg(0).Label())); | ||
| 180 | } | ||
| 181 | |||
| 182 | void EmitSPIRV::EmitBranchConditional(EmitContext& ctx, IR::Inst* inst) { | ||
| 183 | ctx.OpBranchConditional(ctx.Def(inst->Arg(0)), ctx.BlockLabel(inst->Arg(1).Label()), | ||
| 184 | ctx.BlockLabel(inst->Arg(2).Label())); | ||
| 185 | } | ||
| 186 | |||
| 187 | void EmitSPIRV::EmitLoopMerge(EmitContext& ctx, IR::Inst* inst) { | ||
| 188 | ctx.OpLoopMerge(ctx.BlockLabel(inst->Arg(0).Label()), ctx.BlockLabel(inst->Arg(1).Label()), | ||
| 189 | spv::LoopControlMask::MaskNone); | ||
| 190 | } | ||
| 191 | |||
| 192 | void EmitSPIRV::EmitSelectionMerge(EmitContext& ctx, IR::Inst* inst) { | ||
| 193 | ctx.OpSelectionMerge(ctx.BlockLabel(inst->Arg(0).Label()), spv::SelectionControlMask::MaskNone); | ||
| 194 | } | ||
| 195 | |||
| 196 | void EmitSPIRV::EmitReturn(EmitContext& ctx) { | ||
| 197 | ctx.OpReturn(); | ||
| 198 | } | ||
| 199 | |||
| 200 | void EmitSPIRV::EmitGetZeroFromOp(EmitContext&) { | 187 | void EmitSPIRV::EmitGetZeroFromOp(EmitContext&) { |
| 201 | throw LogicError("Unreachable instruction"); | 188 | throw LogicError("Unreachable instruction"); |
| 202 | } | 189 | } |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h index 7d76377b5..a5d0e1ec0 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv.h | |||
| @@ -7,82 +7,12 @@ | |||
| 7 | #include <sirit/sirit.h> | 7 | #include <sirit/sirit.h> |
| 8 | 8 | ||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "shader_recompiler/backend/spirv/emit_context.h" | ||
| 10 | #include "shader_recompiler/frontend/ir/microinstruction.h" | 11 | #include "shader_recompiler/frontend/ir/microinstruction.h" |
| 11 | #include "shader_recompiler/frontend/ir/program.h" | 12 | #include "shader_recompiler/frontend/ir/program.h" |
| 12 | 13 | ||
| 13 | namespace Shader::Backend::SPIRV { | 14 | namespace Shader::Backend::SPIRV { |
| 14 | 15 | ||
| 15 | using Sirit::Id; | ||
| 16 | |||
| 17 | class VectorTypes { | ||
| 18 | public: | ||
| 19 | void Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) { | ||
| 20 | defs[0] = sirit_ctx.Name(base_type, name); | ||
| 21 | |||
| 22 | std::array<char, 6> def_name; | ||
| 23 | for (int i = 1; i < 4; ++i) { | ||
| 24 | const std::string_view def_name_view( | ||
| 25 | def_name.data(), | ||
| 26 | fmt::format_to_n(def_name.data(), def_name.size(), "{}x{}", name, i + 1).size); | ||
| 27 | defs[i] = sirit_ctx.Name(sirit_ctx.TypeVector(base_type, i + 1), def_name_view); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | [[nodiscard]] Id operator[](size_t size) const noexcept { | ||
| 32 | return defs[size - 1]; | ||
| 33 | } | ||
| 34 | |||
| 35 | private: | ||
| 36 | std::array<Id, 4> defs; | ||
| 37 | }; | ||
| 38 | |||
| 39 | class EmitContext final : public Sirit::Module { | ||
| 40 | public: | ||
| 41 | explicit EmitContext(IR::Program& program); | ||
| 42 | ~EmitContext(); | ||
| 43 | |||
| 44 | [[nodiscard]] Id Def(const IR::Value& value) { | ||
| 45 | if (!value.IsImmediate()) { | ||
| 46 | return value.Inst()->Definition<Id>(); | ||
| 47 | } | ||
| 48 | switch (value.Type()) { | ||
| 49 | case IR::Type::U1: | ||
| 50 | return value.U1() ? true_value : false_value; | ||
| 51 | case IR::Type::U32: | ||
| 52 | return Constant(u32[1], value.U32()); | ||
| 53 | case IR::Type::F32: | ||
| 54 | return Constant(f32[1], value.F32()); | ||
| 55 | default: | ||
| 56 | throw NotImplementedException("Immediate type {}", value.Type()); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | [[nodiscard]] Id BlockLabel(IR::Block* block) const { | ||
| 61 | const auto it{std::ranges::lower_bound(block_label_map, block, {}, | ||
| 62 | &std::pair<IR::Block*, Id>::first)}; | ||
| 63 | if (it == block_label_map.end()) { | ||
| 64 | throw LogicError("Undefined block"); | ||
| 65 | } | ||
| 66 | return it->second; | ||
| 67 | } | ||
| 68 | |||
| 69 | Id void_id{}; | ||
| 70 | Id u1{}; | ||
| 71 | VectorTypes f32; | ||
| 72 | VectorTypes u32; | ||
| 73 | VectorTypes f16; | ||
| 74 | VectorTypes f64; | ||
| 75 | |||
| 76 | Id true_value{}; | ||
| 77 | Id false_value{}; | ||
| 78 | |||
| 79 | Id workgroup_id{}; | ||
| 80 | Id local_invocation_id{}; | ||
| 81 | |||
| 82 | private: | ||
| 83 | std::vector<std::pair<IR::Block*, Id>> block_label_map; | ||
| 84 | }; | ||
| 85 | |||
| 86 | class EmitSPIRV { | 16 | class EmitSPIRV { |
| 87 | public: | 17 | public: |
| 88 | explicit EmitSPIRV(IR::Program& program); | 18 | explicit EmitSPIRV(IR::Program& program); |
| @@ -94,10 +24,11 @@ private: | |||
| 94 | Id EmitPhi(EmitContext& ctx, IR::Inst* inst); | 24 | Id EmitPhi(EmitContext& ctx, IR::Inst* inst); |
| 95 | void EmitVoid(EmitContext& ctx); | 25 | void EmitVoid(EmitContext& ctx); |
| 96 | void EmitIdentity(EmitContext& ctx); | 26 | void EmitIdentity(EmitContext& ctx); |
| 97 | void EmitBranch(EmitContext& ctx, IR::Inst* inst); | 27 | void EmitBranch(EmitContext& ctx, IR::Block* label); |
| 98 | void EmitBranchConditional(EmitContext& ctx, IR::Inst* inst); | 28 | void EmitBranchConditional(EmitContext& ctx, Id condition, IR::Block* true_label, |
| 99 | void EmitLoopMerge(EmitContext& ctx, IR::Inst* inst); | 29 | IR::Block* false_label); |
| 100 | void EmitSelectionMerge(EmitContext& ctx, IR::Inst* inst); | 30 | void EmitLoopMerge(EmitContext& ctx, IR::Block* merge_label, IR::Block* continue_label); |
| 31 | void EmitSelectionMerge(EmitContext& ctx, IR::Block* merge_label); | ||
| 101 | void EmitReturn(EmitContext& ctx); | 32 | void EmitReturn(EmitContext& ctx); |
| 102 | void EmitGetRegister(EmitContext& ctx); | 33 | void EmitGetRegister(EmitContext& ctx); |
| 103 | void EmitSetRegister(EmitContext& ctx); | 34 | void EmitSetRegister(EmitContext& ctx); |
| @@ -150,7 +81,8 @@ private: | |||
| 150 | void EmitWriteStorageS8(EmitContext& ctx); | 81 | void EmitWriteStorageS8(EmitContext& ctx); |
| 151 | void EmitWriteStorageU16(EmitContext& ctx); | 82 | void EmitWriteStorageU16(EmitContext& ctx); |
| 152 | void EmitWriteStorageS16(EmitContext& ctx); | 83 | void EmitWriteStorageS16(EmitContext& ctx); |
| 153 | void EmitWriteStorage32(EmitContext& ctx); | 84 | void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, |
| 85 | Id value); | ||
| 154 | void EmitWriteStorage64(EmitContext& ctx); | 86 | void EmitWriteStorage64(EmitContext& ctx); |
| 155 | void EmitWriteStorage128(EmitContext& ctx); | 87 | void EmitWriteStorage128(EmitContext& ctx); |
| 156 | void EmitCompositeConstructU32x2(EmitContext& ctx); | 88 | void EmitCompositeConstructU32x2(EmitContext& ctx); |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp index 447df5b8c..af82df99c 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp | |||
| @@ -11,7 +11,7 @@ void EmitSPIRV::EmitBitCastU16F16(EmitContext&) { | |||
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | Id EmitSPIRV::EmitBitCastU32F32(EmitContext& ctx, Id value) { | 13 | Id EmitSPIRV::EmitBitCastU32F32(EmitContext& ctx, Id value) { |
| 14 | return ctx.OpBitcast(ctx.u32[1], value); | 14 | return ctx.OpBitcast(ctx.U32[1], value); |
| 15 | } | 15 | } |
| 16 | 16 | ||
| 17 | void EmitSPIRV::EmitBitCastU64F64(EmitContext&) { | 17 | void EmitSPIRV::EmitBitCastU64F64(EmitContext&) { |
| @@ -23,7 +23,7 @@ void EmitSPIRV::EmitBitCastF16U16(EmitContext&) { | |||
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | Id EmitSPIRV::EmitBitCastF32U32(EmitContext& ctx, Id value) { | 25 | Id EmitSPIRV::EmitBitCastF32U32(EmitContext& ctx, Id value) { |
| 26 | return ctx.OpBitcast(ctx.f32[1], value); | 26 | return ctx.OpBitcast(ctx.F32[1], value); |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | void EmitSPIRV::EmitBitCastF64U64(EmitContext&) { | 29 | void EmitSPIRV::EmitBitCastF64U64(EmitContext&) { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp index b190cf876..a7374c89d 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp | |||
| @@ -23,7 +23,7 @@ void EmitSPIRV::EmitCompositeExtractU32x2(EmitContext&) { | |||
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | Id EmitSPIRV::EmitCompositeExtractU32x3(EmitContext& ctx, Id vector, u32 index) { | 25 | Id EmitSPIRV::EmitCompositeExtractU32x3(EmitContext& ctx, Id vector, u32 index) { |
| 26 | return ctx.OpCompositeExtract(ctx.u32[1], vector, index); | 26 | return ctx.OpCompositeExtract(ctx.U32[1], vector, index); |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | void EmitSPIRV::EmitCompositeExtractU32x4(EmitContext&) { | 29 | void EmitSPIRV::EmitCompositeExtractU32x4(EmitContext&) { |
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 1eab739ed..f4c9970eb 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 | |||
| @@ -37,7 +37,10 @@ Id EmitSPIRV::EmitGetCbuf(EmitContext& ctx, const IR::Value& binding, const IR:: | |||
| 37 | if (!offset.IsImmediate()) { | 37 | if (!offset.IsImmediate()) { |
| 38 | throw NotImplementedException("Variable constant buffer offset"); | 38 | throw NotImplementedException("Variable constant buffer offset"); |
| 39 | } | 39 | } |
| 40 | return ctx.Name(ctx.OpUndef(ctx.u32[1]), "unimplemented_cbuf"); | 40 | const Id imm_offset{ctx.Constant(ctx.U32[1], offset.U32() / 4)}; |
| 41 | const Id cbuf{ctx.cbufs[binding.U32()]}; | ||
| 42 | const Id access_chain{ctx.OpAccessChain(ctx.uniform_u32, cbuf, ctx.u32_zero_value, imm_offset)}; | ||
| 43 | return ctx.OpLoad(ctx.U32[1], access_chain); | ||
| 41 | } | 44 | } |
| 42 | 45 | ||
| 43 | void EmitSPIRV::EmitGetAttribute(EmitContext&) { | 46 | void EmitSPIRV::EmitGetAttribute(EmitContext&) { |
| @@ -89,22 +92,11 @@ void EmitSPIRV::EmitSetOFlag(EmitContext&) { | |||
| 89 | } | 92 | } |
| 90 | 93 | ||
| 91 | Id EmitSPIRV::EmitWorkgroupId(EmitContext& ctx) { | 94 | Id EmitSPIRV::EmitWorkgroupId(EmitContext& ctx) { |
| 92 | if (ctx.workgroup_id.value == 0) { | 95 | return ctx.OpLoad(ctx.U32[3], ctx.workgroup_id); |
| 93 | ctx.workgroup_id = ctx.AddGlobalVariable( | ||
| 94 | ctx.TypePointer(spv::StorageClass::Input, ctx.u32[3]), spv::StorageClass::Input); | ||
| 95 | ctx.Decorate(ctx.workgroup_id, spv::Decoration::BuiltIn, spv::BuiltIn::WorkgroupId); | ||
| 96 | } | ||
| 97 | return ctx.OpLoad(ctx.u32[3], ctx.workgroup_id); | ||
| 98 | } | 96 | } |
| 99 | 97 | ||
| 100 | Id EmitSPIRV::EmitLocalInvocationId(EmitContext& ctx) { | 98 | Id EmitSPIRV::EmitLocalInvocationId(EmitContext& ctx) { |
| 101 | if (ctx.local_invocation_id.value == 0) { | 99 | return ctx.OpLoad(ctx.U32[3], ctx.local_invocation_id); |
| 102 | ctx.local_invocation_id = ctx.AddGlobalVariable( | ||
| 103 | ctx.TypePointer(spv::StorageClass::Input, ctx.u32[3]), spv::StorageClass::Input); | ||
| 104 | ctx.Decorate(ctx.local_invocation_id, spv::Decoration::BuiltIn, | ||
| 105 | spv::BuiltIn::LocalInvocationId); | ||
| 106 | } | ||
| 107 | return ctx.OpLoad(ctx.u32[3], ctx.local_invocation_id); | ||
| 108 | } | 100 | } |
| 109 | 101 | ||
| 110 | } // namespace Shader::Backend::SPIRV | 102 | } // namespace Shader::Backend::SPIRV |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp index 66ce6c8c5..549c1907a 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp | |||
| @@ -3,3 +3,29 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 6 | |||
| 7 | namespace Shader::Backend::SPIRV { | ||
| 8 | |||
| 9 | void EmitSPIRV::EmitBranch(EmitContext& ctx, IR::Block* label) { | ||
| 10 | ctx.OpBranch(label->Definition<Id>()); | ||
| 11 | } | ||
| 12 | |||
| 13 | void EmitSPIRV::EmitBranchConditional(EmitContext& ctx, Id condition, IR::Block* true_label, | ||
| 14 | IR::Block* false_label) { | ||
| 15 | ctx.OpBranchConditional(condition, true_label->Definition<Id>(), false_label->Definition<Id>()); | ||
| 16 | } | ||
| 17 | |||
| 18 | void EmitSPIRV::EmitLoopMerge(EmitContext& ctx, IR::Block* merge_label, IR::Block* continue_label) { | ||
| 19 | ctx.OpLoopMerge(merge_label->Definition<Id>(), continue_label->Definition<Id>(), | ||
| 20 | spv::LoopControlMask::MaskNone); | ||
| 21 | } | ||
| 22 | |||
| 23 | void EmitSPIRV::EmitSelectionMerge(EmitContext& ctx, IR::Block* merge_label) { | ||
| 24 | ctx.OpSelectionMerge(merge_label->Definition<Id>(), spv::SelectionControlMask::MaskNone); | ||
| 25 | } | ||
| 26 | |||
| 27 | void EmitSPIRV::EmitReturn(EmitContext& ctx) { | ||
| 28 | ctx.OpReturn(); | ||
| 29 | } | ||
| 30 | |||
| 31 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp index 9c39537e2..c9bc121f8 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp | |||
| @@ -46,27 +46,27 @@ void EmitSPIRV::EmitFPAbs64(EmitContext&) { | |||
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | Id EmitSPIRV::EmitFPAdd16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { | 48 | Id EmitSPIRV::EmitFPAdd16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { |
| 49 | return Decorate(ctx, inst, ctx.OpFAdd(ctx.f16[1], a, b)); | 49 | return Decorate(ctx, inst, ctx.OpFAdd(ctx.F16[1], a, b)); |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | Id EmitSPIRV::EmitFPAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { | 52 | Id EmitSPIRV::EmitFPAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { |
| 53 | return Decorate(ctx, inst, ctx.OpFAdd(ctx.f32[1], a, b)); | 53 | return Decorate(ctx, inst, ctx.OpFAdd(ctx.F32[1], a, b)); |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | Id EmitSPIRV::EmitFPAdd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { | 56 | Id EmitSPIRV::EmitFPAdd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { |
| 57 | return Decorate(ctx, inst, ctx.OpFAdd(ctx.f64[1], a, b)); | 57 | return Decorate(ctx, inst, ctx.OpFAdd(ctx.F64[1], a, b)); |
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | Id EmitSPIRV::EmitFPFma16(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) { | 60 | Id EmitSPIRV::EmitFPFma16(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) { |
| 61 | return Decorate(ctx, inst, ctx.OpFma(ctx.f16[1], a, b, c)); | 61 | return Decorate(ctx, inst, ctx.OpFma(ctx.F16[1], a, b, c)); |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | Id EmitSPIRV::EmitFPFma32(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) { | 64 | Id EmitSPIRV::EmitFPFma32(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) { |
| 65 | return Decorate(ctx, inst, ctx.OpFma(ctx.f32[1], a, b, c)); | 65 | return Decorate(ctx, inst, ctx.OpFma(ctx.F32[1], a, b, c)); |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | Id EmitSPIRV::EmitFPFma64(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) { | 68 | Id EmitSPIRV::EmitFPFma64(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) { |
| 69 | return Decorate(ctx, inst, ctx.OpFma(ctx.f64[1], a, b, c)); | 69 | return Decorate(ctx, inst, ctx.OpFma(ctx.F64[1], a, b, c)); |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | void EmitSPIRV::EmitFPMax32(EmitContext&) { | 72 | void EmitSPIRV::EmitFPMax32(EmitContext&) { |
| @@ -86,15 +86,15 @@ void EmitSPIRV::EmitFPMin64(EmitContext&) { | |||
| 86 | } | 86 | } |
| 87 | 87 | ||
| 88 | Id EmitSPIRV::EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { | 88 | Id EmitSPIRV::EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { |
| 89 | return Decorate(ctx, inst, ctx.OpFMul(ctx.f16[1], a, b)); | 89 | return Decorate(ctx, inst, ctx.OpFMul(ctx.F16[1], a, b)); |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | Id EmitSPIRV::EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { | 92 | Id EmitSPIRV::EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { |
| 93 | return Decorate(ctx, inst, ctx.OpFMul(ctx.f32[1], a, b)); | 93 | return Decorate(ctx, inst, ctx.OpFMul(ctx.F32[1], a, b)); |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | Id EmitSPIRV::EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { | 96 | Id EmitSPIRV::EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { |
| 97 | return Decorate(ctx, inst, ctx.OpFMul(ctx.f64[1], a, b)); | 97 | return Decorate(ctx, inst, ctx.OpFMul(ctx.F64[1], a, b)); |
| 98 | } | 98 | } |
| 99 | 99 | ||
| 100 | void EmitSPIRV::EmitFPNeg16(EmitContext&) { | 100 | void EmitSPIRV::EmitFPNeg16(EmitContext&) { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp index e811a63ab..32af94a73 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp | |||
| @@ -10,7 +10,7 @@ Id EmitSPIRV::EmitIAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { | |||
| 10 | if (inst->HasAssociatedPseudoOperation()) { | 10 | if (inst->HasAssociatedPseudoOperation()) { |
| 11 | throw NotImplementedException("Pseudo-operations on IAdd32"); | 11 | throw NotImplementedException("Pseudo-operations on IAdd32"); |
| 12 | } | 12 | } |
| 13 | return ctx.OpIAdd(ctx.u32[1], a, b); | 13 | return ctx.OpIAdd(ctx.U32[1], a, b); |
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | void EmitSPIRV::EmitIAdd64(EmitContext&) { | 16 | void EmitSPIRV::EmitIAdd64(EmitContext&) { |
| @@ -18,7 +18,7 @@ void EmitSPIRV::EmitIAdd64(EmitContext&) { | |||
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | Id EmitSPIRV::EmitISub32(EmitContext& ctx, Id a, Id b) { | 20 | Id EmitSPIRV::EmitISub32(EmitContext& ctx, Id a, Id b) { |
| 21 | return ctx.OpISub(ctx.u32[1], a, b); | 21 | return ctx.OpISub(ctx.U32[1], a, b); |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | void EmitSPIRV::EmitISub64(EmitContext&) { | 24 | void EmitSPIRV::EmitISub64(EmitContext&) { |
| @@ -26,7 +26,7 @@ void EmitSPIRV::EmitISub64(EmitContext&) { | |||
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | Id EmitSPIRV::EmitIMul32(EmitContext& ctx, Id a, Id b) { | 28 | Id EmitSPIRV::EmitIMul32(EmitContext& ctx, Id a, Id b) { |
| 29 | return ctx.OpIMul(ctx.u32[1], a, b); | 29 | return ctx.OpIMul(ctx.U32[1], a, b); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | void EmitSPIRV::EmitINeg32(EmitContext&) { | 32 | void EmitSPIRV::EmitINeg32(EmitContext&) { |
| @@ -38,7 +38,7 @@ void EmitSPIRV::EmitIAbs32(EmitContext&) { | |||
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | Id EmitSPIRV::EmitShiftLeftLogical32(EmitContext& ctx, Id base, Id shift) { | 40 | Id EmitSPIRV::EmitShiftLeftLogical32(EmitContext& ctx, Id base, Id shift) { |
| 41 | return ctx.OpShiftLeftLogical(ctx.u32[1], base, shift); | 41 | return ctx.OpShiftLeftLogical(ctx.U32[1], base, shift); |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | void EmitSPIRV::EmitShiftRightLogical32(EmitContext&) { | 44 | void EmitSPIRV::EmitShiftRightLogical32(EmitContext&) { |
| @@ -70,11 +70,11 @@ void EmitSPIRV::EmitBitFieldSExtract(EmitContext&) { | |||
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | Id EmitSPIRV::EmitBitFieldUExtract(EmitContext& ctx, Id base, Id offset, Id count) { | 72 | Id EmitSPIRV::EmitBitFieldUExtract(EmitContext& ctx, Id base, Id offset, Id count) { |
| 73 | return ctx.OpBitFieldUExtract(ctx.u32[1], base, offset, count); | 73 | return ctx.OpBitFieldUExtract(ctx.U32[1], base, offset, count); |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | Id EmitSPIRV::EmitSLessThan(EmitContext& ctx, Id lhs, Id rhs) { | 76 | Id EmitSPIRV::EmitSLessThan(EmitContext& ctx, Id lhs, Id rhs) { |
| 77 | return ctx.OpSLessThan(ctx.u1, lhs, rhs); | 77 | return ctx.OpSLessThan(ctx.U1, lhs, rhs); |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | void EmitSPIRV::EmitULessThan(EmitContext&) { | 80 | void EmitSPIRV::EmitULessThan(EmitContext&) { |
| @@ -94,7 +94,7 @@ void EmitSPIRV::EmitULessThanEqual(EmitContext&) { | |||
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | Id EmitSPIRV::EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs) { | 96 | Id EmitSPIRV::EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs) { |
| 97 | return ctx.OpSGreaterThan(ctx.u1, lhs, rhs); | 97 | return ctx.OpSGreaterThan(ctx.U1, lhs, rhs); |
| 98 | } | 98 | } |
| 99 | 99 | ||
| 100 | void EmitSPIRV::EmitUGreaterThan(EmitContext&) { | 100 | void EmitSPIRV::EmitUGreaterThan(EmitContext&) { |
| @@ -110,7 +110,7 @@ void EmitSPIRV::EmitSGreaterThanEqual(EmitContext&) { | |||
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | Id EmitSPIRV::EmitUGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs) { | 112 | Id EmitSPIRV::EmitUGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs) { |
| 113 | return ctx.OpUGreaterThanEqual(ctx.u1, lhs, rhs); | 113 | return ctx.OpUGreaterThanEqual(ctx.U1, lhs, rhs); |
| 114 | } | 114 | } |
| 115 | 115 | ||
| 116 | void EmitSPIRV::EmitLogicalOr(EmitContext&) { | 116 | void EmitSPIRV::EmitLogicalOr(EmitContext&) { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp index 21a0d72fa..5769a3c95 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp | |||
| @@ -2,10 +2,26 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <bit> | ||
| 6 | |||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 7 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 6 | 8 | ||
| 7 | namespace Shader::Backend::SPIRV { | 9 | namespace Shader::Backend::SPIRV { |
| 8 | 10 | ||
| 11 | static Id StorageIndex(EmitContext& ctx, const IR::Value& offset, size_t element_size) { | ||
| 12 | if (offset.IsImmediate()) { | ||
| 13 | const u32 imm_offset{static_cast<u32>(offset.U32() / element_size)}; | ||
| 14 | return ctx.Constant(ctx.U32[1], imm_offset); | ||
| 15 | } | ||
| 16 | const u32 shift{static_cast<u32>(std::countr_zero(element_size))}; | ||
| 17 | const Id index{ctx.Def(offset)}; | ||
| 18 | if (shift == 0) { | ||
| 19 | return index; | ||
| 20 | } | ||
| 21 | const Id shift_id{ctx.Constant(ctx.U32[1], shift)}; | ||
| 22 | return ctx.OpShiftRightLogical(ctx.U32[1], index, shift_id); | ||
| 23 | } | ||
| 24 | |||
| 9 | void EmitSPIRV::EmitLoadGlobalU8(EmitContext&) { | 25 | void EmitSPIRV::EmitLoadGlobalU8(EmitContext&) { |
| 10 | throw NotImplementedException("SPIR-V Instruction"); | 26 | throw NotImplementedException("SPIR-V Instruction"); |
| 11 | } | 27 | } |
| @@ -79,11 +95,14 @@ void EmitSPIRV::EmitLoadStorageS16(EmitContext&) { | |||
| 79 | } | 95 | } |
| 80 | 96 | ||
| 81 | Id EmitSPIRV::EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding, | 97 | Id EmitSPIRV::EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding, |
| 82 | [[maybe_unused]] const IR::Value& offset) { | 98 | const IR::Value& offset) { |
| 83 | if (!binding.IsImmediate()) { | 99 | if (!binding.IsImmediate()) { |
| 84 | throw NotImplementedException("Storage buffer indexing"); | 100 | throw NotImplementedException("Dynamic storage buffer indexing"); |
| 85 | } | 101 | } |
| 86 | return ctx.Name(ctx.OpUndef(ctx.u32[1]), "unimplemented_sbuf"); | 102 | const Id ssbo{ctx.ssbos[binding.U32()]}; |
| 103 | const Id index{StorageIndex(ctx, offset, sizeof(u32))}; | ||
| 104 | const Id pointer{ctx.OpAccessChain(ctx.storage_u32, ssbo, ctx.u32_zero_value, index)}; | ||
| 105 | return ctx.OpLoad(ctx.U32[1], pointer); | ||
| 87 | } | 106 | } |
| 88 | 107 | ||
| 89 | void EmitSPIRV::EmitLoadStorage64(EmitContext&) { | 108 | void EmitSPIRV::EmitLoadStorage64(EmitContext&) { |
| @@ -110,8 +129,15 @@ void EmitSPIRV::EmitWriteStorageS16(EmitContext&) { | |||
| 110 | throw NotImplementedException("SPIR-V Instruction"); | 129 | throw NotImplementedException("SPIR-V Instruction"); |
| 111 | } | 130 | } |
| 112 | 131 | ||
| 113 | void EmitSPIRV::EmitWriteStorage32(EmitContext& ctx) { | 132 | void EmitSPIRV::EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, |
| 114 | ctx.Name(ctx.OpUndef(ctx.u32[1]), "unimplemented_sbuf_store"); | 133 | const IR::Value& offset, Id value) { |
| 134 | if (!binding.IsImmediate()) { | ||
| 135 | throw NotImplementedException("Dynamic storage buffer indexing"); | ||
| 136 | } | ||
| 137 | const Id ssbo{ctx.ssbos[binding.U32()]}; | ||
| 138 | const Id index{StorageIndex(ctx, offset, sizeof(u32))}; | ||
| 139 | const Id pointer{ctx.OpAccessChain(ctx.storage_u32, ssbo, ctx.u32_zero_value, index)}; | ||
| 140 | ctx.OpStore(pointer, value); | ||
| 115 | } | 141 | } |
| 116 | 142 | ||
| 117 | void EmitSPIRV::EmitWriteStorage64(EmitContext&) { | 143 | void EmitSPIRV::EmitWriteStorage64(EmitContext&) { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp index a6f542360..c1ed8f281 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | namespace Shader::Backend::SPIRV { | 7 | namespace Shader::Backend::SPIRV { |
| 8 | 8 | ||
| 9 | Id EmitSPIRV::EmitUndefU1(EmitContext& ctx) { | 9 | Id EmitSPIRV::EmitUndefU1(EmitContext& ctx) { |
| 10 | return ctx.OpUndef(ctx.u1); | 10 | return ctx.OpUndef(ctx.U1); |
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | Id EmitSPIRV::EmitUndefU8(EmitContext&) { | 13 | Id EmitSPIRV::EmitUndefU8(EmitContext&) { |
| @@ -19,7 +19,7 @@ Id EmitSPIRV::EmitUndefU16(EmitContext&) { | |||
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | Id EmitSPIRV::EmitUndefU32(EmitContext& ctx) { | 21 | Id EmitSPIRV::EmitUndefU32(EmitContext& ctx) { |
| 22 | return ctx.OpUndef(ctx.u32[1]); | 22 | return ctx.OpUndef(ctx.U32[1]); |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | Id EmitSPIRV::EmitUndefU64(EmitContext&) { | 25 | Id EmitSPIRV::EmitUndefU64(EmitContext&) { |