summaryrefslogtreecommitdiffstats
path: root/src/shader_recompiler/backend/spirv
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.cpp160
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.h67
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.cpp189
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.h84
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp20
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp26
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp18
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp16
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp36
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp4
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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <array>
+#include <string_view>
+
+#include <fmt/format.h>
+
+#include "common/common_types.h"
+#include "shader_recompiler/backend/spirv/emit_context.h"
+
+namespace Shader::Backend::SPIRV {
+
+void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) {
+ defs[0] = sirit_ctx.Name(base_type, name);
+
+ std::array<char, 6> def_name;
+ for (int i = 1; i < 4; ++i) {
+ const std::string_view def_name_view(
+ def_name.data(),
+ fmt::format_to_n(def_name.data(), def_name.size(), "{}x{}", name, i + 1).size);
+ defs[i] = sirit_ctx.Name(sirit_ctx.TypeVector(base_type, i + 1), def_name_view);
+ }
+}
+
+EmitContext::EmitContext(IR::Program& program) : Sirit::Module(0x00010000) {
+ AddCapability(spv::Capability::Shader);
+ DefineCommonTypes(program.info);
+ DefineCommonConstants();
+ DefineSpecialVariables(program.info);
+ DefineConstantBuffers(program.info);
+ DefineStorageBuffers(program.info);
+ DefineLabels(program);
+}
+
+EmitContext::~EmitContext() = default;
+
+Id EmitContext::Def(const IR::Value& value) {
+ if (!value.IsImmediate()) {
+ return value.Inst()->Definition<Id>();
+ }
+ switch (value.Type()) {
+ case IR::Type::U1:
+ return value.U1() ? true_value : false_value;
+ case IR::Type::U32:
+ return Constant(U32[1], value.U32());
+ case IR::Type::F32:
+ return Constant(F32[1], value.F32());
+ default:
+ throw NotImplementedException("Immediate type {}", value.Type());
+ }
+}
+
+void EmitContext::DefineCommonTypes(const Info& info) {
+ void_id = TypeVoid();
+
+ U1 = Name(TypeBool(), "u1");
+
+ F32.Define(*this, TypeFloat(32), "f32");
+ U32.Define(*this, TypeInt(32, false), "u32");
+
+ if (info.uses_fp16) {
+ AddCapability(spv::Capability::Float16);
+ F16.Define(*this, TypeFloat(16), "f16");
+ }
+ if (info.uses_fp64) {
+ AddCapability(spv::Capability::Float64);
+ F64.Define(*this, TypeFloat(64), "f64");
+ }
+}
+
+void EmitContext::DefineCommonConstants() {
+ true_value = ConstantTrue(U1);
+ false_value = ConstantFalse(U1);
+ u32_zero_value = Constant(U32[1], 0U);
+}
+
+void EmitContext::DefineSpecialVariables(const Info& info) {
+ const auto define{[this](Id type, spv::BuiltIn builtin, spv::StorageClass storage_class) {
+ const Id pointer_type{TypePointer(storage_class, type)};
+ const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::Input)};
+ Decorate(id, spv::Decoration::BuiltIn, builtin);
+ return id;
+ }};
+ using namespace std::placeholders;
+ const auto define_input{std::bind(define, _1, _2, spv::StorageClass::Input)};
+
+ if (info.uses_workgroup_id) {
+ workgroup_id = define_input(U32[3], spv::BuiltIn::WorkgroupId);
+ }
+ if (info.uses_local_invocation_id) {
+ local_invocation_id = define_input(U32[3], spv::BuiltIn::LocalInvocationId);
+ }
+}
+
+void EmitContext::DefineConstantBuffers(const Info& info) {
+ if (info.constant_buffer_descriptors.empty()) {
+ return;
+ }
+ const Id array_type{TypeArray(U32[1], Constant(U32[1], 4096))};
+ Decorate(array_type, spv::Decoration::ArrayStride, 16U);
+
+ const Id struct_type{TypeStruct(array_type)};
+ Name(struct_type, "cbuf_block");
+ Decorate(struct_type, spv::Decoration::Block);
+ MemberName(struct_type, 0, "data");
+ MemberDecorate(struct_type, 0, spv::Decoration::Offset, 0U);
+
+ const Id uniform_type{TypePointer(spv::StorageClass::Uniform, struct_type)};
+ uniform_u32 = TypePointer(spv::StorageClass::Uniform, U32[1]);
+
+ u32 binding{};
+ for (const Info::ConstantBufferDescriptor& desc : info.constant_buffer_descriptors) {
+ const Id id{AddGlobalVariable(uniform_type, spv::StorageClass::Uniform)};
+ Decorate(id, spv::Decoration::Binding, binding);
+ Name(id, fmt::format("c{}", desc.index));
+ std::fill_n(cbufs.data() + desc.index, desc.count, id);
+ binding += desc.count;
+ }
+}
+
+void EmitContext::DefineStorageBuffers(const Info& info) {
+ if (info.storage_buffers_descriptors.empty()) {
+ return;
+ }
+ AddExtension("SPV_KHR_storage_buffer_storage_class");
+
+ const Id array_type{TypeRuntimeArray(U32[1])};
+ Decorate(array_type, spv::Decoration::ArrayStride, 4U);
+
+ const Id struct_type{TypeStruct(array_type)};
+ Name(struct_type, "ssbo_block");
+ Decorate(struct_type, spv::Decoration::Block);
+ MemberName(struct_type, 0, "data");
+ MemberDecorate(struct_type, 0, spv::Decoration::Offset, 0U);
+
+ const Id storage_type{TypePointer(spv::StorageClass::StorageBuffer, struct_type)};
+ storage_u32 = TypePointer(spv::StorageClass::StorageBuffer, U32[1]);
+
+ u32 binding{};
+ for (const Info::StorageBufferDescriptor& desc : info.storage_buffers_descriptors) {
+ const Id id{AddGlobalVariable(storage_type, spv::StorageClass::StorageBuffer)};
+ Decorate(id, spv::Decoration::Binding, binding);
+ Name(id, fmt::format("ssbo{}", binding));
+ std::fill_n(ssbos.data() + binding, desc.count, id);
+ binding += desc.count;
+ }
+}
+
+void EmitContext::DefineLabels(IR::Program& program) {
+ for (const IR::Function& function : program.functions) {
+ for (IR::Block* const block : function.blocks) {
+ block->SetDefinition(OpLabel());
+ }
+ }
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <string_view>
+
+#include <sirit/sirit.h>
+
+#include "shader_recompiler/frontend/ir/program.h"
+#include "shader_recompiler/shader_info.h"
+
+namespace Shader::Backend::SPIRV {
+
+using Sirit::Id;
+
+class VectorTypes {
+public:
+ void Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name);
+
+ [[nodiscard]] Id operator[](size_t size) const noexcept {
+ return defs[size - 1];
+ }
+
+private:
+ std::array<Id, 4> defs{};
+};
+
+class EmitContext final : public Sirit::Module {
+public:
+ explicit EmitContext(IR::Program& program);
+ ~EmitContext();
+
+ [[nodiscard]] Id Def(const IR::Value& value);
+
+ Id void_id{};
+ Id U1{};
+ VectorTypes F32;
+ VectorTypes U32;
+ VectorTypes F16;
+ VectorTypes F64;
+
+ Id true_value{};
+ Id false_value{};
+ Id u32_zero_value{};
+
+ Id uniform_u32{};
+ Id storage_u32{};
+
+ std::array<Id, Info::MAX_CBUFS> cbufs{};
+ std::array<Id, Info::MAX_SSBOS> ssbos{};
+
+ Id workgroup_id{};
+ Id local_invocation_id{};
+
+private:
+ void DefineCommonTypes(const Info& info);
+ void DefineCommonConstants();
+ void DefineSpecialVariables(const Info& info);
+ void DefineConstantBuffers(const Info& info);
+ void DefineStorageBuffers(const Info& info);
+ void DefineLabels(IR::Program& program);
+};
+
+} // 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 @@
#include "shader_recompiler/frontend/ir/program.h"
namespace Shader::Backend::SPIRV {
+namespace {
+template <class Func>
+struct FuncTraits : FuncTraits<decltype(&Func::operator())> {};
-EmitContext::EmitContext(IR::Program& program) {
- AddCapability(spv::Capability::Shader);
- AddCapability(spv::Capability::Float16);
- AddCapability(spv::Capability::Float64);
- void_id = TypeVoid();
+template <class ClassType, class ReturnType_, class... Args>
+struct FuncTraits<ReturnType_ (ClassType::*)(Args...)> {
+ using ReturnType = ReturnType_;
- u1 = Name(TypeBool(), "u1");
- f32.Define(*this, TypeFloat(32), "f32");
- u32.Define(*this, TypeInt(32, false), "u32");
- f16.Define(*this, TypeFloat(16), "f16");
- f64.Define(*this, TypeFloat(64), "f64");
+ static constexpr size_t NUM_ARGS = sizeof...(Args);
- true_value = ConstantTrue(u1);
- false_value = ConstantFalse(u1);
+ template <size_t I>
+ using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
+};
- for (const IR::Function& function : program.functions) {
- for (IR::Block* const block : function.blocks) {
- block_label_map.emplace_back(block, OpLabel());
+template <auto method, typename... Args>
+void SetDefinition(EmitSPIRV& emit, EmitContext& ctx, IR::Inst* inst, Args... args) {
+ const Id forward_id{inst->Definition<Id>()};
+ const bool has_forward_id{Sirit::ValidId(forward_id)};
+ Id current_id{};
+ if (has_forward_id) {
+ current_id = ctx.ExchangeCurrentId(forward_id);
+ }
+ const Id new_id{(emit.*method)(ctx, std::forward<Args>(args)...)};
+ if (has_forward_id) {
+ ctx.ExchangeCurrentId(current_id);
+ } else {
+ inst->SetDefinition<Id>(new_id);
+ }
+}
+
+template <typename ArgType>
+ArgType Arg(EmitContext& ctx, const IR::Value& arg) {
+ if constexpr (std::is_same_v<ArgType, Id>) {
+ return ctx.Def(arg);
+ } else if constexpr (std::is_same_v<ArgType, const IR::Value&>) {
+ return arg;
+ } else if constexpr (std::is_same_v<ArgType, u32>) {
+ return arg.U32();
+ } else if constexpr (std::is_same_v<ArgType, IR::Block*>) {
+ return arg.Label();
+ }
+}
+
+template <auto method, bool is_first_arg_inst, size_t... I>
+void Invoke(EmitSPIRV& emit, EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
+ using Traits = FuncTraits<decltype(method)>;
+ if constexpr (std::is_same_v<Traits::ReturnType, Id>) {
+ if constexpr (is_first_arg_inst) {
+ SetDefinition<method>(emit, ctx, inst, inst,
+ Arg<Traits::ArgType<I + 2>>(ctx, inst->Arg(I))...);
+ } else {
+ SetDefinition<method>(emit, ctx, inst,
+ Arg<Traits::ArgType<I + 1>>(ctx, inst->Arg(I))...);
+ }
+ } else {
+ if constexpr (is_first_arg_inst) {
+ (emit.*method)(ctx, inst, Arg<Traits::ArgType<I + 2>>(ctx, inst->Arg(I))...);
+ } else {
+ (emit.*method)(ctx, Arg<Traits::ArgType<I + 1>>(ctx, inst->Arg(I))...);
}
}
- std::ranges::sort(block_label_map, {}, &std::pair<IR::Block*, Id>::first);
}
-EmitContext::~EmitContext() = default;
+template <auto method>
+void Invoke(EmitSPIRV& emit, EmitContext& ctx, IR::Inst* inst) {
+ using Traits = FuncTraits<decltype(method)>;
+ static_assert(Traits::NUM_ARGS >= 1, "Insufficient arguments");
+ if constexpr (Traits::NUM_ARGS == 1) {
+ Invoke<method, false>(emit, ctx, inst, std::make_index_sequence<0>{});
+ } else {
+ using FirstArgType = typename Traits::template ArgType<1>;
+ static constexpr bool is_first_arg_inst = std::is_same_v<FirstArgType, IR::Inst*>;
+ using Indices = std::make_index_sequence<Traits::NUM_ARGS - (is_first_arg_inst ? 2 : 1)>;
+ Invoke<method, is_first_arg_inst>(emit, ctx, inst, Indices{});
+ }
+}
+} // Anonymous namespace
EmitSPIRV::EmitSPIRV(IR::Program& program) {
EmitContext ctx{program};
@@ -46,74 +98,32 @@ EmitSPIRV::EmitSPIRV(IR::Program& program) {
for (IR::Function& function : program.functions) {
func = ctx.OpFunction(ctx.void_id, spv::FunctionControlMask::MaskNone, void_function);
for (IR::Block* const block : function.blocks) {
- ctx.AddLabel(ctx.BlockLabel(block));
+ ctx.AddLabel(block->Definition<Id>());
for (IR::Inst& inst : block->Instructions()) {
EmitInst(ctx, &inst);
}
}
ctx.OpFunctionEnd();
}
- ctx.AddEntryPoint(spv::ExecutionModel::GLCompute, func, "main");
+ boost::container::small_vector<Id, 32> interfaces;
+ if (program.info.uses_workgroup_id) {
+ interfaces.push_back(ctx.workgroup_id);
+ }
+ if (program.info.uses_local_invocation_id) {
+ interfaces.push_back(ctx.local_invocation_id);
+ }
+
+ const std::span interfaces_span(interfaces.data(), interfaces.size());
+ ctx.AddEntryPoint(spv::ExecutionModel::Fragment, func, "main", interfaces_span);
+ ctx.AddExecutionMode(func, spv::ExecutionMode::OriginUpperLeft);
std::vector<u32> result{ctx.Assemble()};
- std::FILE* file{std::fopen("shader.spv", "wb")};
+ std::FILE* file{std::fopen("D:\\shader.spv", "wb")};
std::fwrite(result.data(), sizeof(u32), result.size(), file);
std::fclose(file);
- std::system("spirv-dis shader.spv");
- std::system("spirv-val shader.spv");
- std::system("spirv-cross shader.spv");
-}
-
-template <auto method, typename... Args>
-static void SetDefinition(EmitSPIRV& emit, EmitContext& ctx, IR::Inst* inst, Args... args) {
- const Id forward_id{inst->Definition<Id>()};
- const bool has_forward_id{Sirit::ValidId(forward_id)};
- Id current_id{};
- if (has_forward_id) {
- current_id = ctx.ExchangeCurrentId(forward_id);
- }
- const Id new_id{(emit.*method)(ctx, std::forward<Args>(args)...)};
- if (has_forward_id) {
- ctx.ExchangeCurrentId(current_id);
- } else {
- inst->SetDefinition<Id>(new_id);
- }
-}
-
-template <auto method>
-static void Invoke(EmitSPIRV& emit, EmitContext& ctx, IR::Inst* inst) {
- using M = decltype(method);
- using std::is_invocable_r_v;
- if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&>) {
- SetDefinition<method>(emit, ctx, inst);
- } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, Id>) {
- SetDefinition<method>(emit, ctx, inst, ctx.Def(inst->Arg(0)));
- } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, Id, Id>) {
- SetDefinition<method>(emit, ctx, inst, ctx.Def(inst->Arg(0)), ctx.Def(inst->Arg(1)));
- } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, Id, Id, Id>) {
- SetDefinition<method>(emit, ctx, inst, ctx.Def(inst->Arg(0)), ctx.Def(inst->Arg(1)),
- ctx.Def(inst->Arg(2)));
- } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, IR::Inst*>) {
- SetDefinition<method>(emit, ctx, inst, inst);
- } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, IR::Inst*, Id, Id>) {
- SetDefinition<method>(emit, ctx, inst, inst, ctx.Def(inst->Arg(0)), ctx.Def(inst->Arg(1)));
- } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, IR::Inst*, Id, Id, Id>) {
- SetDefinition<method>(emit, ctx, inst, inst, ctx.Def(inst->Arg(0)), ctx.Def(inst->Arg(1)),
- ctx.Def(inst->Arg(2)));
- } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, Id, u32>) {
- SetDefinition<method>(emit, ctx, inst, ctx.Def(inst->Arg(0)), inst->Arg(1).U32());
- } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, const IR::Value&>) {
- SetDefinition<method>(emit, ctx, inst, inst->Arg(0));
- } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, const IR::Value&,
- const IR::Value&>) {
- SetDefinition<method>(emit, ctx, inst, inst->Arg(0), inst->Arg(1));
- } else if constexpr (is_invocable_r_v<void, M, EmitSPIRV&, EmitContext&, IR::Inst*>) {
- (emit.*method)(ctx, inst);
- } else if constexpr (is_invocable_r_v<void, M, EmitSPIRV&, EmitContext&>) {
- (emit.*method)(ctx);
- } else {
- static_assert(false, "Bad format");
- }
+ std::system("spirv-dis D:\\shader.spv") == 0 &&
+ std::system("spirv-val --uniform-buffer-standard-layout D:\\shader.spv") == 0 &&
+ std::system("spirv-cross -V D:\\shader.spv") == 0;
}
void EmitSPIRV::EmitInst(EmitContext& ctx, IR::Inst* inst) {
@@ -130,9 +140,9 @@ void EmitSPIRV::EmitInst(EmitContext& ctx, IR::Inst* inst) {
static Id TypeId(const EmitContext& ctx, IR::Type type) {
switch (type) {
case IR::Type::U1:
- return ctx.u1;
+ return ctx.U1;
case IR::Type::U32:
- return ctx.u32[1];
+ return ctx.U32[1];
default:
throw NotImplementedException("Phi node type {}", type);
}
@@ -162,7 +172,7 @@ Id EmitSPIRV::EmitPhi(EmitContext& ctx, IR::Inst* inst) {
}
IR::Block* const phi_block{inst->PhiBlock(index)};
operands.push_back(def);
- operands.push_back(ctx.BlockLabel(phi_block));
+ operands.push_back(phi_block->Definition<Id>());
}
const Id result_type{TypeId(ctx, inst->Arg(0).Type())};
return ctx.OpPhi(result_type, std::span(operands.data(), operands.size()));
@@ -174,29 +184,6 @@ void EmitSPIRV::EmitIdentity(EmitContext&) {
throw NotImplementedException("SPIR-V Instruction");
}
-// FIXME: Move to its own file
-void EmitSPIRV::EmitBranch(EmitContext& ctx, IR::Inst* inst) {
- ctx.OpBranch(ctx.BlockLabel(inst->Arg(0).Label()));
-}
-
-void EmitSPIRV::EmitBranchConditional(EmitContext& ctx, IR::Inst* inst) {
- ctx.OpBranchConditional(ctx.Def(inst->Arg(0)), ctx.BlockLabel(inst->Arg(1).Label()),
- ctx.BlockLabel(inst->Arg(2).Label()));
-}
-
-void EmitSPIRV::EmitLoopMerge(EmitContext& ctx, IR::Inst* inst) {
- ctx.OpLoopMerge(ctx.BlockLabel(inst->Arg(0).Label()), ctx.BlockLabel(inst->Arg(1).Label()),
- spv::LoopControlMask::MaskNone);
-}
-
-void EmitSPIRV::EmitSelectionMerge(EmitContext& ctx, IR::Inst* inst) {
- ctx.OpSelectionMerge(ctx.BlockLabel(inst->Arg(0).Label()), spv::SelectionControlMask::MaskNone);
-}
-
-void EmitSPIRV::EmitReturn(EmitContext& ctx) {
- ctx.OpReturn();
-}
-
void EmitSPIRV::EmitGetZeroFromOp(EmitContext&) {
throw LogicError("Unreachable instruction");
}
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 @@
#include <sirit/sirit.h>
#include "common/common_types.h"
+#include "shader_recompiler/backend/spirv/emit_context.h"
#include "shader_recompiler/frontend/ir/microinstruction.h"
#include "shader_recompiler/frontend/ir/program.h"
namespace Shader::Backend::SPIRV {
-using Sirit::Id;
-
-class VectorTypes {
-public:
- void Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) {
- defs[0] = sirit_ctx.Name(base_type, name);
-
- std::array<char, 6> def_name;
- for (int i = 1; i < 4; ++i) {
- const std::string_view def_name_view(
- def_name.data(),
- fmt::format_to_n(def_name.data(), def_name.size(), "{}x{}", name, i + 1).size);
- defs[i] = sirit_ctx.Name(sirit_ctx.TypeVector(base_type, i + 1), def_name_view);
- }
- }
-
- [[nodiscard]] Id operator[](size_t size) const noexcept {
- return defs[size - 1];
- }
-
-private:
- std::array<Id, 4> defs;
-};
-
-class EmitContext final : public Sirit::Module {
-public:
- explicit EmitContext(IR::Program& program);
- ~EmitContext();
-
- [[nodiscard]] Id Def(const IR::Value& value) {
- if (!value.IsImmediate()) {
- return value.Inst()->Definition<Id>();
- }
- switch (value.Type()) {
- case IR::Type::U1:
- return value.U1() ? true_value : false_value;
- case IR::Type::U32:
- return Constant(u32[1], value.U32());
- case IR::Type::F32:
- return Constant(f32[1], value.F32());
- default:
- throw NotImplementedException("Immediate type {}", value.Type());
- }
- }
-
- [[nodiscard]] Id BlockLabel(IR::Block* block) const {
- const auto it{std::ranges::lower_bound(block_label_map, block, {},
- &std::pair<IR::Block*, Id>::first)};
- if (it == block_label_map.end()) {
- throw LogicError("Undefined block");
- }
- return it->second;
- }
-
- Id void_id{};
- Id u1{};
- VectorTypes f32;
- VectorTypes u32;
- VectorTypes f16;
- VectorTypes f64;
-
- Id true_value{};
- Id false_value{};
-
- Id workgroup_id{};
- Id local_invocation_id{};
-
-private:
- std::vector<std::pair<IR::Block*, Id>> block_label_map;
-};
-
class EmitSPIRV {
public:
explicit EmitSPIRV(IR::Program& program);
@@ -94,10 +24,11 @@ private:
Id EmitPhi(EmitContext& ctx, IR::Inst* inst);
void EmitVoid(EmitContext& ctx);
void EmitIdentity(EmitContext& ctx);
- void EmitBranch(EmitContext& ctx, IR::Inst* inst);
- void EmitBranchConditional(EmitContext& ctx, IR::Inst* inst);
- void EmitLoopMerge(EmitContext& ctx, IR::Inst* inst);
- void EmitSelectionMerge(EmitContext& ctx, IR::Inst* inst);
+ void EmitBranch(EmitContext& ctx, IR::Block* label);
+ void EmitBranchConditional(EmitContext& ctx, Id condition, IR::Block* true_label,
+ IR::Block* false_label);
+ void EmitLoopMerge(EmitContext& ctx, IR::Block* merge_label, IR::Block* continue_label);
+ void EmitSelectionMerge(EmitContext& ctx, IR::Block* merge_label);
void EmitReturn(EmitContext& ctx);
void EmitGetRegister(EmitContext& ctx);
void EmitSetRegister(EmitContext& ctx);
@@ -150,7 +81,8 @@ private:
void EmitWriteStorageS8(EmitContext& ctx);
void EmitWriteStorageU16(EmitContext& ctx);
void EmitWriteStorageS16(EmitContext& ctx);
- void EmitWriteStorage32(EmitContext& ctx);
+ void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
+ Id value);
void EmitWriteStorage64(EmitContext& ctx);
void EmitWriteStorage128(EmitContext& ctx);
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&) {
}
Id EmitSPIRV::EmitBitCastU32F32(EmitContext& ctx, Id value) {
- return ctx.OpBitcast(ctx.u32[1], value);
+ return ctx.OpBitcast(ctx.U32[1], value);
}
void EmitSPIRV::EmitBitCastU64F64(EmitContext&) {
@@ -23,7 +23,7 @@ void EmitSPIRV::EmitBitCastF16U16(EmitContext&) {
}
Id EmitSPIRV::EmitBitCastF32U32(EmitContext& ctx, Id value) {
- return ctx.OpBitcast(ctx.f32[1], value);
+ return ctx.OpBitcast(ctx.F32[1], value);
}
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&) {
}
Id EmitSPIRV::EmitCompositeExtractU32x3(EmitContext& ctx, Id vector, u32 index) {
- return ctx.OpCompositeExtract(ctx.u32[1], vector, index);
+ return ctx.OpCompositeExtract(ctx.U32[1], vector, index);
}
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::
if (!offset.IsImmediate()) {
throw NotImplementedException("Variable constant buffer offset");
}
- return ctx.Name(ctx.OpUndef(ctx.u32[1]), "unimplemented_cbuf");
+ const Id imm_offset{ctx.Constant(ctx.U32[1], offset.U32() / 4)};
+ const Id cbuf{ctx.cbufs[binding.U32()]};
+ const Id access_chain{ctx.OpAccessChain(ctx.uniform_u32, cbuf, ctx.u32_zero_value, imm_offset)};
+ return ctx.OpLoad(ctx.U32[1], access_chain);
}
void EmitSPIRV::EmitGetAttribute(EmitContext&) {
@@ -89,22 +92,11 @@ void EmitSPIRV::EmitSetOFlag(EmitContext&) {
}
Id EmitSPIRV::EmitWorkgroupId(EmitContext& ctx) {
- if (ctx.workgroup_id.value == 0) {
- ctx.workgroup_id = ctx.AddGlobalVariable(
- ctx.TypePointer(spv::StorageClass::Input, ctx.u32[3]), spv::StorageClass::Input);
- ctx.Decorate(ctx.workgroup_id, spv::Decoration::BuiltIn, spv::BuiltIn::WorkgroupId);
- }
- return ctx.OpLoad(ctx.u32[3], ctx.workgroup_id);
+ return ctx.OpLoad(ctx.U32[3], ctx.workgroup_id);
}
Id EmitSPIRV::EmitLocalInvocationId(EmitContext& ctx) {
- if (ctx.local_invocation_id.value == 0) {
- ctx.local_invocation_id = ctx.AddGlobalVariable(
- ctx.TypePointer(spv::StorageClass::Input, ctx.u32[3]), spv::StorageClass::Input);
- ctx.Decorate(ctx.local_invocation_id, spv::Decoration::BuiltIn,
- spv::BuiltIn::LocalInvocationId);
- }
- return ctx.OpLoad(ctx.u32[3], ctx.local_invocation_id);
+ return ctx.OpLoad(ctx.U32[3], ctx.local_invocation_id);
}
} // 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 @@
// Refer to the license.txt file included.
#include "shader_recompiler/backend/spirv/emit_spirv.h"
+
+namespace Shader::Backend::SPIRV {
+
+void EmitSPIRV::EmitBranch(EmitContext& ctx, IR::Block* label) {
+ ctx.OpBranch(label->Definition<Id>());
+}
+
+void EmitSPIRV::EmitBranchConditional(EmitContext& ctx, Id condition, IR::Block* true_label,
+ IR::Block* false_label) {
+ ctx.OpBranchConditional(condition, true_label->Definition<Id>(), false_label->Definition<Id>());
+}
+
+void EmitSPIRV::EmitLoopMerge(EmitContext& ctx, IR::Block* merge_label, IR::Block* continue_label) {
+ ctx.OpLoopMerge(merge_label->Definition<Id>(), continue_label->Definition<Id>(),
+ spv::LoopControlMask::MaskNone);
+}
+
+void EmitSPIRV::EmitSelectionMerge(EmitContext& ctx, IR::Block* merge_label) {
+ ctx.OpSelectionMerge(merge_label->Definition<Id>(), spv::SelectionControlMask::MaskNone);
+}
+
+void EmitSPIRV::EmitReturn(EmitContext& ctx) {
+ ctx.OpReturn();
+}
+
+} // 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&) {
}
Id EmitSPIRV::EmitFPAdd16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
- return Decorate(ctx, inst, ctx.OpFAdd(ctx.f16[1], a, b));
+ return Decorate(ctx, inst, ctx.OpFAdd(ctx.F16[1], a, b));
}
Id EmitSPIRV::EmitFPAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
- return Decorate(ctx, inst, ctx.OpFAdd(ctx.f32[1], a, b));
+ return Decorate(ctx, inst, ctx.OpFAdd(ctx.F32[1], a, b));
}
Id EmitSPIRV::EmitFPAdd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
- return Decorate(ctx, inst, ctx.OpFAdd(ctx.f64[1], a, b));
+ return Decorate(ctx, inst, ctx.OpFAdd(ctx.F64[1], a, b));
}
Id EmitSPIRV::EmitFPFma16(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
- return Decorate(ctx, inst, ctx.OpFma(ctx.f16[1], a, b, c));
+ return Decorate(ctx, inst, ctx.OpFma(ctx.F16[1], a, b, c));
}
Id EmitSPIRV::EmitFPFma32(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
- return Decorate(ctx, inst, ctx.OpFma(ctx.f32[1], a, b, c));
+ return Decorate(ctx, inst, ctx.OpFma(ctx.F32[1], a, b, c));
}
Id EmitSPIRV::EmitFPFma64(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
- return Decorate(ctx, inst, ctx.OpFma(ctx.f64[1], a, b, c));
+ return Decorate(ctx, inst, ctx.OpFma(ctx.F64[1], a, b, c));
}
void EmitSPIRV::EmitFPMax32(EmitContext&) {
@@ -86,15 +86,15 @@ void EmitSPIRV::EmitFPMin64(EmitContext&) {
}
Id EmitSPIRV::EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
- return Decorate(ctx, inst, ctx.OpFMul(ctx.f16[1], a, b));
+ return Decorate(ctx, inst, ctx.OpFMul(ctx.F16[1], a, b));
}
Id EmitSPIRV::EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
- return Decorate(ctx, inst, ctx.OpFMul(ctx.f32[1], a, b));
+ return Decorate(ctx, inst, ctx.OpFMul(ctx.F32[1], a, b));
}
Id EmitSPIRV::EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
- return Decorate(ctx, inst, ctx.OpFMul(ctx.f64[1], a, b));
+ return Decorate(ctx, inst, ctx.OpFMul(ctx.F64[1], a, b));
}
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) {
if (inst->HasAssociatedPseudoOperation()) {
throw NotImplementedException("Pseudo-operations on IAdd32");
}
- return ctx.OpIAdd(ctx.u32[1], a, b);
+ return ctx.OpIAdd(ctx.U32[1], a, b);
}
void EmitSPIRV::EmitIAdd64(EmitContext&) {
@@ -18,7 +18,7 @@ void EmitSPIRV::EmitIAdd64(EmitContext&) {
}
Id EmitSPIRV::EmitISub32(EmitContext& ctx, Id a, Id b) {
- return ctx.OpISub(ctx.u32[1], a, b);
+ return ctx.OpISub(ctx.U32[1], a, b);
}
void EmitSPIRV::EmitISub64(EmitContext&) {
@@ -26,7 +26,7 @@ void EmitSPIRV::EmitISub64(EmitContext&) {
}
Id EmitSPIRV::EmitIMul32(EmitContext& ctx, Id a, Id b) {
- return ctx.OpIMul(ctx.u32[1], a, b);
+ return ctx.OpIMul(ctx.U32[1], a, b);
}
void EmitSPIRV::EmitINeg32(EmitContext&) {
@@ -38,7 +38,7 @@ void EmitSPIRV::EmitIAbs32(EmitContext&) {
}
Id EmitSPIRV::EmitShiftLeftLogical32(EmitContext& ctx, Id base, Id shift) {
- return ctx.OpShiftLeftLogical(ctx.u32[1], base, shift);
+ return ctx.OpShiftLeftLogical(ctx.U32[1], base, shift);
}
void EmitSPIRV::EmitShiftRightLogical32(EmitContext&) {
@@ -70,11 +70,11 @@ void EmitSPIRV::EmitBitFieldSExtract(EmitContext&) {
}
Id EmitSPIRV::EmitBitFieldUExtract(EmitContext& ctx, Id base, Id offset, Id count) {
- return ctx.OpBitFieldUExtract(ctx.u32[1], base, offset, count);
+ return ctx.OpBitFieldUExtract(ctx.U32[1], base, offset, count);
}
Id EmitSPIRV::EmitSLessThan(EmitContext& ctx, Id lhs, Id rhs) {
- return ctx.OpSLessThan(ctx.u1, lhs, rhs);
+ return ctx.OpSLessThan(ctx.U1, lhs, rhs);
}
void EmitSPIRV::EmitULessThan(EmitContext&) {
@@ -94,7 +94,7 @@ void EmitSPIRV::EmitULessThanEqual(EmitContext&) {
}
Id EmitSPIRV::EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs) {
- return ctx.OpSGreaterThan(ctx.u1, lhs, rhs);
+ return ctx.OpSGreaterThan(ctx.U1, lhs, rhs);
}
void EmitSPIRV::EmitUGreaterThan(EmitContext&) {
@@ -110,7 +110,7 @@ void EmitSPIRV::EmitSGreaterThanEqual(EmitContext&) {
}
Id EmitSPIRV::EmitUGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
- return ctx.OpUGreaterThanEqual(ctx.u1, lhs, rhs);
+ return ctx.OpUGreaterThanEqual(ctx.U1, lhs, rhs);
}
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <bit>
+
#include "shader_recompiler/backend/spirv/emit_spirv.h"
namespace Shader::Backend::SPIRV {
+static Id StorageIndex(EmitContext& ctx, const IR::Value& offset, size_t element_size) {
+ if (offset.IsImmediate()) {
+ const u32 imm_offset{static_cast<u32>(offset.U32() / element_size)};
+ return ctx.Constant(ctx.U32[1], imm_offset);
+ }
+ const u32 shift{static_cast<u32>(std::countr_zero(element_size))};
+ const Id index{ctx.Def(offset)};
+ if (shift == 0) {
+ return index;
+ }
+ const Id shift_id{ctx.Constant(ctx.U32[1], shift)};
+ return ctx.OpShiftRightLogical(ctx.U32[1], index, shift_id);
+}
+
void EmitSPIRV::EmitLoadGlobalU8(EmitContext&) {
throw NotImplementedException("SPIR-V Instruction");
}
@@ -79,11 +95,14 @@ void EmitSPIRV::EmitLoadStorageS16(EmitContext&) {
}
Id EmitSPIRV::EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding,
- [[maybe_unused]] const IR::Value& offset) {
+ const IR::Value& offset) {
if (!binding.IsImmediate()) {
- throw NotImplementedException("Storage buffer indexing");
+ throw NotImplementedException("Dynamic storage buffer indexing");
}
- return ctx.Name(ctx.OpUndef(ctx.u32[1]), "unimplemented_sbuf");
+ const Id ssbo{ctx.ssbos[binding.U32()]};
+ const Id index{StorageIndex(ctx, offset, sizeof(u32))};
+ const Id pointer{ctx.OpAccessChain(ctx.storage_u32, ssbo, ctx.u32_zero_value, index)};
+ return ctx.OpLoad(ctx.U32[1], pointer);
}
void EmitSPIRV::EmitLoadStorage64(EmitContext&) {
@@ -110,8 +129,15 @@ void EmitSPIRV::EmitWriteStorageS16(EmitContext&) {
throw NotImplementedException("SPIR-V Instruction");
}
-void EmitSPIRV::EmitWriteStorage32(EmitContext& ctx) {
- ctx.Name(ctx.OpUndef(ctx.u32[1]), "unimplemented_sbuf_store");
+void EmitSPIRV::EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding,
+ const IR::Value& offset, Id value) {
+ if (!binding.IsImmediate()) {
+ throw NotImplementedException("Dynamic storage buffer indexing");
+ }
+ const Id ssbo{ctx.ssbos[binding.U32()]};
+ const Id index{StorageIndex(ctx, offset, sizeof(u32))};
+ const Id pointer{ctx.OpAccessChain(ctx.storage_u32, ssbo, ctx.u32_zero_value, index)};
+ ctx.OpStore(pointer, value);
}
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 @@
namespace Shader::Backend::SPIRV {
Id EmitSPIRV::EmitUndefU1(EmitContext& ctx) {
- return ctx.OpUndef(ctx.u1);
+ return ctx.OpUndef(ctx.U1);
}
Id EmitSPIRV::EmitUndefU8(EmitContext&) {
@@ -19,7 +19,7 @@ Id EmitSPIRV::EmitUndefU16(EmitContext&) {
}
Id EmitSPIRV::EmitUndefU32(EmitContext& ctx) {
- return ctx.OpUndef(ctx.u32[1]);
+ return ctx.OpUndef(ctx.U32[1]);
}
Id EmitSPIRV::EmitUndefU64(EmitContext&) {