summaryrefslogtreecommitdiff
path: root/src/shader_recompiler/backend
diff options
context:
space:
mode:
authorGravatar bunnei2021-07-25 11:39:04 -0700
committerGravatar GitHub2021-07-25 11:39:04 -0700
commit98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f (patch)
tree816faa96c2c4d291825063433331a8ea4b3d08f1 /src/shader_recompiler/backend
parentMerge pull request #6699 from lat9nq/common-threads (diff)
parentshader: Support out of bound local memory reads and immediate writes (diff)
downloadyuzu-98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f.tar.gz
yuzu-98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f.tar.xz
yuzu-98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f.zip
Merge pull request #6585 from ameerj/hades
Shader Decompiler Rewrite
Diffstat (limited to 'src/shader_recompiler/backend')
-rw-r--r--src/shader_recompiler/backend/bindings.h19
-rw-r--r--src/shader_recompiler/backend/glasm/emit_context.cpp154
-rw-r--r--src/shader_recompiler/backend/glasm/emit_context.h80
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm.cpp492
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm.h25
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_barriers.cpp0
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp91
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp244
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp346
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_control_flow.cpp0
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp231
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp414
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_image.cpp850
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_instructions.h625
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp294
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_logical.cpp0
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp568
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp273
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_select.cpp67
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_shared_memory.cpp58
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_special.cpp0
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_undefined.cpp0
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_warp.cpp150
-rw-r--r--src/shader_recompiler/backend/glasm/reg_alloc.cpp186
-rw-r--r--src/shader_recompiler/backend/glasm/reg_alloc.h303
-rw-r--r--src/shader_recompiler/backend/glsl/emit_context.cpp715
-rw-r--r--src/shader_recompiler/backend/glsl/emit_context.h174
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl.cpp252
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl.h24
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp418
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp21
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp94
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_composite.cpp219
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp456
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp21
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp230
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp456
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_image.cpp799
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_instructions.h702
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp253
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp28
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_memory.cpp202
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp105
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_select.cpp55
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp79
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_special.cpp111
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp32
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp217
-rw-r--r--src/shader_recompiler/backend/glsl/var_alloc.cpp308
-rw-r--r--src/shader_recompiler/backend/glsl/var_alloc.h105
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.cpp1368
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.h307
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.cpp541
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.h27
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp448
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp38
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp66
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp155
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp505
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp28
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp269
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp396
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp462
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp183
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_instructions.h579
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp270
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp26
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp275
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_select.cpp42
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp174
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_special.cpp150
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp30
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp203
73 files changed, 18088 insertions, 0 deletions
diff --git a/src/shader_recompiler/backend/bindings.h b/src/shader_recompiler/backend/bindings.h
new file mode 100644
index 000000000..35503000c
--- /dev/null
+++ b/src/shader_recompiler/backend/bindings.h
@@ -0,0 +1,19 @@
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 "common/common_types.h"
8
9namespace Shader::Backend {
10
11struct Bindings {
12 u32 unified{};
13 u32 uniform_buffer{};
14 u32 storage_buffer{};
15 u32 texture{};
16 u32 image{};
17};
18
19} // namespace Shader::Backend
diff --git a/src/shader_recompiler/backend/glasm/emit_context.cpp b/src/shader_recompiler/backend/glasm/emit_context.cpp
new file mode 100644
index 000000000..069c019ad
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/emit_context.cpp
@@ -0,0 +1,154 @@
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 <string_view>
6
7#include "shader_recompiler/backend/bindings.h"
8#include "shader_recompiler/backend/glasm/emit_context.h"
9#include "shader_recompiler/frontend/ir/program.h"
10#include "shader_recompiler/profile.h"
11#include "shader_recompiler/runtime_info.h"
12
13namespace Shader::Backend::GLASM {
14namespace {
15std::string_view InterpDecorator(Interpolation interp) {
16 switch (interp) {
17 case Interpolation::Smooth:
18 return "";
19 case Interpolation::Flat:
20 return "FLAT ";
21 case Interpolation::NoPerspective:
22 return "NOPERSPECTIVE ";
23 }
24 throw InvalidArgument("Invalid interpolation {}", interp);
25}
26
27bool IsInputArray(Stage stage) {
28 return stage == Stage::Geometry || stage == Stage::TessellationControl ||
29 stage == Stage::TessellationEval;
30}
31} // Anonymous namespace
32
33EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
34 const RuntimeInfo& runtime_info_)
35 : info{program.info}, profile{profile_}, runtime_info{runtime_info_} {
36 // FIXME: Temporary partial implementation
37 u32 cbuf_index{};
38 for (const auto& desc : info.constant_buffer_descriptors) {
39 if (desc.count != 1) {
40 throw NotImplementedException("Constant buffer descriptor array");
41 }
42 Add("CBUFFER c{}[]={{program.buffer[{}]}};", desc.index, cbuf_index);
43 ++cbuf_index;
44 }
45 u32 ssbo_index{};
46 for (const auto& desc : info.storage_buffers_descriptors) {
47 if (desc.count != 1) {
48 throw NotImplementedException("Storage buffer descriptor array");
49 }
50 if (runtime_info.glasm_use_storage_buffers) {
51 Add("STORAGE ssbo{}[]={{program.storage[{}]}};", ssbo_index, bindings.storage_buffer);
52 ++bindings.storage_buffer;
53 ++ssbo_index;
54 }
55 }
56 if (!runtime_info.glasm_use_storage_buffers) {
57 if (const size_t num = info.storage_buffers_descriptors.size(); num > 0) {
58 Add("PARAM c[{}]={{program.local[0..{}]}};", num, num - 1);
59 }
60 }
61 stage = program.stage;
62 switch (program.stage) {
63 case Stage::VertexA:
64 case Stage::VertexB:
65 stage_name = "vertex";
66 attrib_name = "vertex";
67 break;
68 case Stage::TessellationControl:
69 case Stage::TessellationEval:
70 stage_name = "primitive";
71 attrib_name = "primitive";
72 break;
73 case Stage::Geometry:
74 stage_name = "primitive";
75 attrib_name = "vertex";
76 break;
77 case Stage::Fragment:
78 stage_name = "fragment";
79 attrib_name = "fragment";
80 break;
81 case Stage::Compute:
82 stage_name = "invocation";
83 break;
84 }
85 const std::string_view attr_stage{stage == Stage::Fragment ? "fragment" : "vertex"};
86 const VaryingState loads{info.loads.mask | info.passthrough.mask};
87 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
88 if (loads.Generic(index)) {
89 Add("{}ATTRIB in_attr{}[]={{{}.attrib[{}..{}]}};",
90 InterpDecorator(info.interpolation[index]), index, attr_stage, index, index);
91 }
92 }
93 if (IsInputArray(stage) && loads.AnyComponent(IR::Attribute::PositionX)) {
94 Add("ATTRIB vertex_position=vertex.position;");
95 }
96 if (info.uses_invocation_id) {
97 Add("ATTRIB primitive_invocation=primitive.invocation;");
98 }
99 if (info.stores_tess_level_outer) {
100 Add("OUTPUT result_patch_tessouter[]={{result.patch.tessouter[0..3]}};");
101 }
102 if (info.stores_tess_level_inner) {
103 Add("OUTPUT result_patch_tessinner[]={{result.patch.tessinner[0..1]}};");
104 }
105 if (info.stores.ClipDistances()) {
106 Add("OUTPUT result_clip[]={{result.clip[0..7]}};");
107 }
108 for (size_t index = 0; index < info.uses_patches.size(); ++index) {
109 if (!info.uses_patches[index]) {
110 continue;
111 }
112 if (stage == Stage::TessellationControl) {
113 Add("OUTPUT result_patch_attrib{}[]={{result.patch.attrib[{}..{}]}};"
114 "ATTRIB primitive_out_patch_attrib{}[]={{primitive.out.patch.attrib[{}..{}]}};",
115 index, index, index, index, index, index);
116 } else {
117 Add("ATTRIB primitive_patch_attrib{}[]={{primitive.patch.attrib[{}..{}]}};", index,
118 index, index);
119 }
120 }
121 if (stage == Stage::Fragment) {
122 Add("OUTPUT frag_color0=result.color;");
123 for (size_t index = 1; index < info.stores_frag_color.size(); ++index) {
124 Add("OUTPUT frag_color{}=result.color[{}];", index, index);
125 }
126 }
127 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
128 if (info.stores.Generic(index)) {
129 Add("OUTPUT out_attr{}[]={{result.attrib[{}..{}]}};", index, index, index);
130 }
131 }
132 image_buffer_bindings.reserve(info.image_buffer_descriptors.size());
133 for (const auto& desc : info.image_buffer_descriptors) {
134 image_buffer_bindings.push_back(bindings.image);
135 bindings.image += desc.count;
136 }
137 image_bindings.reserve(info.image_descriptors.size());
138 for (const auto& desc : info.image_descriptors) {
139 image_bindings.push_back(bindings.image);
140 bindings.image += desc.count;
141 }
142 texture_buffer_bindings.reserve(info.texture_buffer_descriptors.size());
143 for (const auto& desc : info.texture_buffer_descriptors) {
144 texture_buffer_bindings.push_back(bindings.texture);
145 bindings.texture += desc.count;
146 }
147 texture_bindings.reserve(info.texture_descriptors.size());
148 for (const auto& desc : info.texture_descriptors) {
149 texture_bindings.push_back(bindings.texture);
150 bindings.texture += desc.count;
151 }
152}
153
154} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_context.h b/src/shader_recompiler/backend/glasm/emit_context.h
new file mode 100644
index 000000000..8433e5c00
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/emit_context.h
@@ -0,0 +1,80 @@
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 <string>
8#include <utility>
9#include <vector>
10
11#include <fmt/format.h>
12
13#include "shader_recompiler/backend/glasm/reg_alloc.h"
14#include "shader_recompiler/stage.h"
15
16namespace Shader {
17struct Info;
18struct Profile;
19struct RuntimeInfo;
20} // namespace Shader
21
22namespace Shader::Backend {
23struct Bindings;
24}
25
26namespace Shader::IR {
27class Inst;
28struct Program;
29} // namespace Shader::IR
30
31namespace Shader::Backend::GLASM {
32
33class EmitContext {
34public:
35 explicit EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
36 const RuntimeInfo& runtime_info_);
37
38 template <typename... Args>
39 void Add(const char* format_str, IR::Inst& inst, Args&&... args) {
40 code += fmt::format(fmt::runtime(format_str), reg_alloc.Define(inst),
41 std::forward<Args>(args)...);
42 // TODO: Remove this
43 code += '\n';
44 }
45
46 template <typename... Args>
47 void LongAdd(const char* format_str, IR::Inst& inst, Args&&... args) {
48 code += fmt::format(fmt::runtime(format_str), reg_alloc.LongDefine(inst),
49 std::forward<Args>(args)...);
50 // TODO: Remove this
51 code += '\n';
52 }
53
54 template <typename... Args>
55 void Add(const char* format_str, Args&&... args) {
56 code += fmt::format(fmt::runtime(format_str), std::forward<Args>(args)...);
57 // TODO: Remove this
58 code += '\n';
59 }
60
61 std::string code;
62 RegAlloc reg_alloc{};
63 const Info& info;
64 const Profile& profile;
65 const RuntimeInfo& runtime_info;
66
67 std::vector<u32> texture_buffer_bindings;
68 std::vector<u32> image_buffer_bindings;
69 std::vector<u32> texture_bindings;
70 std::vector<u32> image_bindings;
71
72 Stage stage{};
73 std::string_view stage_name = "invalid";
74 std::string_view attrib_name = "invalid";
75
76 u32 num_safety_loop_vars{};
77 bool uses_y_direction{};
78};
79
80} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
new file mode 100644
index 000000000..a5e8c9b6e
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
@@ -0,0 +1,492 @@
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 <string>
7#include <tuple>
8
9#include "common/div_ceil.h"
10#include "common/settings.h"
11#include "shader_recompiler/backend/bindings.h"
12#include "shader_recompiler/backend/glasm/emit_context.h"
13#include "shader_recompiler/backend/glasm/emit_glasm.h"
14#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
15#include "shader_recompiler/frontend/ir/ir_emitter.h"
16#include "shader_recompiler/frontend/ir/program.h"
17#include "shader_recompiler/profile.h"
18#include "shader_recompiler/runtime_info.h"
19
20namespace Shader::Backend::GLASM {
21namespace {
22template <class Func>
23struct FuncTraits {};
24
25template <class ReturnType_, class... Args>
26struct FuncTraits<ReturnType_ (*)(Args...)> {
27 using ReturnType = ReturnType_;
28
29 static constexpr size_t NUM_ARGS = sizeof...(Args);
30
31 template <size_t I>
32 using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
33};
34
35template <typename T>
36struct Identity {
37 Identity(T data_) : data{data_} {}
38
39 T Extract() {
40 return data;
41 }
42
43 T data;
44};
45
46template <bool scalar>
47class RegWrapper {
48public:
49 RegWrapper(EmitContext& ctx, const IR::Value& ir_value) : reg_alloc{ctx.reg_alloc} {
50 const Value value{reg_alloc.Peek(ir_value)};
51 if (value.type == Type::Register) {
52 inst = ir_value.InstRecursive();
53 reg = Register{value};
54 } else {
55 reg = value.type == Type::U64 ? reg_alloc.AllocLongReg() : reg_alloc.AllocReg();
56 }
57 switch (value.type) {
58 case Type::Register:
59 case Type::Void:
60 break;
61 case Type::U32:
62 ctx.Add("MOV.U {}.x,{};", reg, value.imm_u32);
63 break;
64 case Type::U64:
65 ctx.Add("MOV.U64 {}.x,{};", reg, value.imm_u64);
66 break;
67 }
68 }
69
70 auto Extract() {
71 if (inst) {
72 reg_alloc.Unref(*inst);
73 } else {
74 reg_alloc.FreeReg(reg);
75 }
76 return std::conditional_t<scalar, ScalarRegister, Register>{Value{reg}};
77 }
78
79private:
80 RegAlloc& reg_alloc;
81 IR::Inst* inst{};
82 Register reg{};
83};
84
85template <typename ArgType>
86class ValueWrapper {
87public:
88 ValueWrapper(EmitContext& ctx, const IR::Value& ir_value_)
89 : reg_alloc{ctx.reg_alloc}, ir_value{ir_value_}, value{reg_alloc.Peek(ir_value)} {}
90
91 ArgType Extract() {
92 if (!ir_value.IsImmediate()) {
93 reg_alloc.Unref(*ir_value.InstRecursive());
94 }
95 return value;
96 }
97
98private:
99 RegAlloc& reg_alloc;
100 const IR::Value& ir_value;
101 ArgType value;
102};
103
104template <typename ArgType>
105auto Arg(EmitContext& ctx, const IR::Value& arg) {
106 if constexpr (std::is_same_v<ArgType, Register>) {
107 return RegWrapper<false>{ctx, arg};
108 } else if constexpr (std::is_same_v<ArgType, ScalarRegister>) {
109 return RegWrapper<true>{ctx, arg};
110 } else if constexpr (std::is_base_of_v<Value, ArgType>) {
111 return ValueWrapper<ArgType>{ctx, arg};
112 } else if constexpr (std::is_same_v<ArgType, const IR::Value&>) {
113 return Identity<const IR::Value&>{arg};
114 } else if constexpr (std::is_same_v<ArgType, u32>) {
115 return Identity{arg.U32()};
116 } else if constexpr (std::is_same_v<ArgType, IR::Attribute>) {
117 return Identity{arg.Attribute()};
118 } else if constexpr (std::is_same_v<ArgType, IR::Patch>) {
119 return Identity{arg.Patch()};
120 } else if constexpr (std::is_same_v<ArgType, IR::Reg>) {
121 return Identity{arg.Reg()};
122 }
123}
124
125template <auto func, bool is_first_arg_inst>
126struct InvokeCall {
127 template <typename... Args>
128 InvokeCall(EmitContext& ctx, IR::Inst* inst, Args&&... args) {
129 if constexpr (is_first_arg_inst) {
130 func(ctx, *inst, args.Extract()...);
131 } else {
132 func(ctx, args.Extract()...);
133 }
134 }
135};
136
137template <auto func, bool is_first_arg_inst, size_t... I>
138void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
139 using Traits = FuncTraits<decltype(func)>;
140 if constexpr (is_first_arg_inst) {
141 InvokeCall<func, is_first_arg_inst>{
142 ctx, inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...};
143 } else {
144 InvokeCall<func, is_first_arg_inst>{
145 ctx, inst, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...};
146 }
147}
148
149template <auto func>
150void Invoke(EmitContext& ctx, IR::Inst* inst) {
151 using Traits = FuncTraits<decltype(func)>;
152 static_assert(Traits::NUM_ARGS >= 1, "Insufficient arguments");
153 if constexpr (Traits::NUM_ARGS == 1) {
154 Invoke<func, false>(ctx, inst, std::make_index_sequence<0>{});
155 } else {
156 using FirstArgType = typename Traits::template ArgType<1>;
157 static constexpr bool is_first_arg_inst = std::is_same_v<FirstArgType, IR::Inst&>;
158 using Indices = std::make_index_sequence<Traits::NUM_ARGS - (is_first_arg_inst ? 2 : 1)>;
159 Invoke<func, is_first_arg_inst>(ctx, inst, Indices{});
160 }
161}
162
163void EmitInst(EmitContext& ctx, IR::Inst* inst) {
164 switch (inst->GetOpcode()) {
165#define OPCODE(name, result_type, ...) \
166 case IR::Opcode::name: \
167 return Invoke<&Emit##name>(ctx, inst);
168#include "shader_recompiler/frontend/ir/opcodes.inc"
169#undef OPCODE
170 }
171 throw LogicError("Invalid opcode {}", inst->GetOpcode());
172}
173
174bool IsReference(IR::Inst& inst) {
175 return inst.GetOpcode() == IR::Opcode::Reference;
176}
177
178void PrecolorInst(IR::Inst& phi) {
179 // Insert phi moves before references to avoid overwritting other phis
180 const size_t num_args{phi.NumArgs()};
181 for (size_t i = 0; i < num_args; ++i) {
182 IR::Block& phi_block{*phi.PhiBlock(i)};
183 auto it{std::find_if_not(phi_block.rbegin(), phi_block.rend(), IsReference).base()};
184 IR::IREmitter ir{phi_block, it};
185 const IR::Value arg{phi.Arg(i)};
186 if (arg.IsImmediate()) {
187 ir.PhiMove(phi, arg);
188 } else {
189 ir.PhiMove(phi, IR::Value{&RegAlloc::AliasInst(*arg.Inst())});
190 }
191 }
192 for (size_t i = 0; i < num_args; ++i) {
193 IR::IREmitter{*phi.PhiBlock(i)}.Reference(IR::Value{&phi});
194 }
195}
196
197void Precolor(const IR::Program& program) {
198 for (IR::Block* const block : program.blocks) {
199 for (IR::Inst& phi : block->Instructions()) {
200 if (!IR::IsPhi(phi)) {
201 break;
202 }
203 PrecolorInst(phi);
204 }
205 }
206}
207
208void EmitCode(EmitContext& ctx, const IR::Program& program) {
209 const auto eval{
210 [&](const IR::U1& cond) { return ScalarS32{ctx.reg_alloc.Consume(IR::Value{cond})}; }};
211 for (const IR::AbstractSyntaxNode& node : program.syntax_list) {
212 switch (node.type) {
213 case IR::AbstractSyntaxNode::Type::Block:
214 for (IR::Inst& inst : node.data.block->Instructions()) {
215 EmitInst(ctx, &inst);
216 }
217 break;
218 case IR::AbstractSyntaxNode::Type::If:
219 ctx.Add("MOV.S.CC RC,{};"
220 "IF NE.x;",
221 eval(node.data.if_node.cond));
222 break;
223 case IR::AbstractSyntaxNode::Type::EndIf:
224 ctx.Add("ENDIF;");
225 break;
226 case IR::AbstractSyntaxNode::Type::Loop:
227 ctx.Add("REP;");
228 break;
229 case IR::AbstractSyntaxNode::Type::Repeat:
230 if (!Settings::values.disable_shader_loop_safety_checks) {
231 const u32 loop_index{ctx.num_safety_loop_vars++};
232 const u32 vector_index{loop_index / 4};
233 const char component{"xyzw"[loop_index % 4]};
234 ctx.Add("SUB.S.CC loop{}.{},loop{}.{},1;"
235 "BRK(LT.{});",
236 vector_index, component, vector_index, component, component);
237 }
238 if (node.data.repeat.cond.IsImmediate()) {
239 if (node.data.repeat.cond.U1()) {
240 ctx.Add("ENDREP;");
241 } else {
242 ctx.Add("BRK;"
243 "ENDREP;");
244 }
245 } else {
246 ctx.Add("MOV.S.CC RC,{};"
247 "BRK(EQ.x);"
248 "ENDREP;",
249 eval(node.data.repeat.cond));
250 }
251 break;
252 case IR::AbstractSyntaxNode::Type::Break:
253 if (node.data.break_node.cond.IsImmediate()) {
254 if (node.data.break_node.cond.U1()) {
255 ctx.Add("BRK;");
256 }
257 } else {
258 ctx.Add("MOV.S.CC RC,{};"
259 "BRK (NE.x);",
260 eval(node.data.break_node.cond));
261 }
262 break;
263 case IR::AbstractSyntaxNode::Type::Return:
264 case IR::AbstractSyntaxNode::Type::Unreachable:
265 ctx.Add("RET;");
266 break;
267 }
268 }
269 if (!ctx.reg_alloc.IsEmpty()) {
270 LOG_WARNING(Shader_GLASM, "Register leak after generating code");
271 }
272}
273
274void SetupOptions(const IR::Program& program, const Profile& profile,
275 const RuntimeInfo& runtime_info, std::string& header) {
276 const Info& info{program.info};
277 const Stage stage{program.stage};
278
279 // TODO: Track the shared atomic ops
280 header += "OPTION NV_internal;"
281 "OPTION NV_shader_storage_buffer;"
282 "OPTION NV_gpu_program_fp64;";
283 if (info.uses_int64_bit_atomics) {
284 header += "OPTION NV_shader_atomic_int64;";
285 }
286 if (info.uses_atomic_f32_add) {
287 header += "OPTION NV_shader_atomic_float;";
288 }
289 if (info.uses_atomic_f16x2_add || info.uses_atomic_f16x2_min || info.uses_atomic_f16x2_max) {
290 header += "OPTION NV_shader_atomic_fp16_vector;";
291 }
292 if (info.uses_subgroup_invocation_id || info.uses_subgroup_mask || info.uses_subgroup_vote ||
293 info.uses_fswzadd) {
294 header += "OPTION NV_shader_thread_group;";
295 }
296 if (info.uses_subgroup_shuffles) {
297 header += "OPTION NV_shader_thread_shuffle;";
298 }
299 if (info.uses_sparse_residency) {
300 header += "OPTION EXT_sparse_texture2;";
301 }
302 const bool stores_viewport_layer{info.stores[IR::Attribute::ViewportIndex] ||
303 info.stores[IR::Attribute::Layer]};
304 if ((stage != Stage::Geometry && stores_viewport_layer) ||
305 info.stores[IR::Attribute::ViewportMask]) {
306 if (profile.support_viewport_index_layer_non_geometry) {
307 header += "OPTION NV_viewport_array2;";
308 }
309 }
310 if (program.is_geometry_passthrough && profile.support_geometry_shader_passthrough) {
311 header += "OPTION NV_geometry_shader_passthrough;";
312 }
313 if (info.uses_typeless_image_reads && profile.support_typeless_image_loads) {
314 header += "OPTION EXT_shader_image_load_formatted;";
315 }
316 if (profile.support_derivative_control) {
317 header += "OPTION ARB_derivative_control;";
318 }
319 if (stage == Stage::Fragment && runtime_info.force_early_z != 0) {
320 header += "OPTION NV_early_fragment_tests;";
321 }
322 if (stage == Stage::Fragment) {
323 header += "OPTION ARB_draw_buffers;";
324 }
325}
326
327std::string_view StageHeader(Stage stage) {
328 switch (stage) {
329 case Stage::VertexA:
330 case Stage::VertexB:
331 return "!!NVvp5.0\n";
332 case Stage::TessellationControl:
333 return "!!NVtcp5.0\n";
334 case Stage::TessellationEval:
335 return "!!NVtep5.0\n";
336 case Stage::Geometry:
337 return "!!NVgp5.0\n";
338 case Stage::Fragment:
339 return "!!NVfp5.0\n";
340 case Stage::Compute:
341 return "!!NVcp5.0\n";
342 }
343 throw InvalidArgument("Invalid stage {}", stage);
344}
345
346std::string_view InputPrimitive(InputTopology topology) {
347 switch (topology) {
348 case InputTopology::Points:
349 return "POINTS";
350 case InputTopology::Lines:
351 return "LINES";
352 case InputTopology::LinesAdjacency:
353 return "LINESS_ADJACENCY";
354 case InputTopology::Triangles:
355 return "TRIANGLES";
356 case InputTopology::TrianglesAdjacency:
357 return "TRIANGLES_ADJACENCY";
358 }
359 throw InvalidArgument("Invalid input topology {}", topology);
360}
361
362std::string_view OutputPrimitive(OutputTopology topology) {
363 switch (topology) {
364 case OutputTopology::PointList:
365 return "POINTS";
366 case OutputTopology::LineStrip:
367 return "LINE_STRIP";
368 case OutputTopology::TriangleStrip:
369 return "TRIANGLE_STRIP";
370 }
371 throw InvalidArgument("Invalid output topology {}", topology);
372}
373
374std::string_view GetTessMode(TessPrimitive primitive) {
375 switch (primitive) {
376 case TessPrimitive::Triangles:
377 return "TRIANGLES";
378 case TessPrimitive::Quads:
379 return "QUADS";
380 case TessPrimitive::Isolines:
381 return "ISOLINES";
382 }
383 throw InvalidArgument("Invalid tessellation primitive {}", primitive);
384}
385
386std::string_view GetTessSpacing(TessSpacing spacing) {
387 switch (spacing) {
388 case TessSpacing::Equal:
389 return "EQUAL";
390 case TessSpacing::FractionalOdd:
391 return "FRACTIONAL_ODD";
392 case TessSpacing::FractionalEven:
393 return "FRACTIONAL_EVEN";
394 }
395 throw InvalidArgument("Invalid tessellation spacing {}", spacing);
396}
397} // Anonymous namespace
398
399std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program,
400 Bindings& bindings) {
401 EmitContext ctx{program, bindings, profile, runtime_info};
402 Precolor(program);
403 EmitCode(ctx, program);
404 std::string header{StageHeader(program.stage)};
405 SetupOptions(program, profile, runtime_info, header);
406 switch (program.stage) {
407 case Stage::TessellationControl:
408 header += fmt::format("VERTICES_OUT {};", program.invocations);
409 break;
410 case Stage::TessellationEval:
411 header += fmt::format("TESS_MODE {};"
412 "TESS_SPACING {};"
413 "TESS_VERTEX_ORDER {};",
414 GetTessMode(runtime_info.tess_primitive),
415 GetTessSpacing(runtime_info.tess_spacing),
416 runtime_info.tess_clockwise ? "CW" : "CCW");
417 break;
418 case Stage::Geometry:
419 header += fmt::format("PRIMITIVE_IN {};", InputPrimitive(runtime_info.input_topology));
420 if (program.is_geometry_passthrough) {
421 if (profile.support_geometry_shader_passthrough) {
422 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
423 if (program.info.passthrough.Generic(index)) {
424 header += fmt::format("PASSTHROUGH result.attrib[{}];", index);
425 }
426 }
427 if (program.info.passthrough.AnyComponent(IR::Attribute::PositionX)) {
428 header += "PASSTHROUGH result.position;";
429 }
430 } else {
431 LOG_WARNING(Shader_GLASM, "Passthrough geometry program used but not supported");
432 }
433 } else {
434 header +=
435 fmt::format("VERTICES_OUT {};"
436 "PRIMITIVE_OUT {};",
437 program.output_vertices, OutputPrimitive(program.output_topology));
438 }
439 break;
440 case Stage::Compute:
441 header += fmt::format("GROUP_SIZE {} {} {};", program.workgroup_size[0],
442 program.workgroup_size[1], program.workgroup_size[2]);
443 break;
444 default:
445 break;
446 }
447 if (program.shared_memory_size > 0) {
448 header += fmt::format("SHARED_MEMORY {};", program.shared_memory_size);
449 header += fmt::format("SHARED shared_mem[]={{program.sharedmem}};");
450 }
451 header += "TEMP ";
452 for (size_t index = 0; index < ctx.reg_alloc.NumUsedRegisters(); ++index) {
453 header += fmt::format("R{},", index);
454 }
455 if (program.local_memory_size > 0) {
456 header += fmt::format("lmem[{}],", program.local_memory_size);
457 }
458 if (program.info.uses_fswzadd) {
459 header += "FSWZA[4],FSWZB[4],";
460 }
461 const u32 num_safety_loop_vectors{Common::DivCeil(ctx.num_safety_loop_vars, 4u)};
462 for (u32 index = 0; index < num_safety_loop_vectors; ++index) {
463 header += fmt::format("loop{},", index);
464 }
465 header += "RC;"
466 "LONG TEMP ";
467 for (size_t index = 0; index < ctx.reg_alloc.NumUsedLongRegisters(); ++index) {
468 header += fmt::format("D{},", index);
469 }
470 header += "DC;";
471 if (program.info.uses_fswzadd) {
472 header += "MOV.F FSWZA[0],-1;"
473 "MOV.F FSWZA[1],1;"
474 "MOV.F FSWZA[2],-1;"
475 "MOV.F FSWZA[3],0;"
476 "MOV.F FSWZB[0],-1;"
477 "MOV.F FSWZB[1],-1;"
478 "MOV.F FSWZB[2],1;"
479 "MOV.F FSWZB[3],-1;";
480 }
481 for (u32 index = 0; index < num_safety_loop_vectors; ++index) {
482 header += fmt::format("MOV.S loop{},{{0x2000,0x2000,0x2000,0x2000}};", index);
483 }
484 if (ctx.uses_y_direction) {
485 header += "PARAM y_direction[1]={state.material.front.ambient};";
486 }
487 ctx.code.insert(0, header);
488 ctx.code += "END";
489 return ctx.code;
490}
491
492} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.h b/src/shader_recompiler/backend/glasm/emit_glasm.h
new file mode 100644
index 000000000..bcb55f062
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/emit_glasm.h
@@ -0,0 +1,25 @@
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 <string>
8
9#include "shader_recompiler/backend/bindings.h"
10#include "shader_recompiler/frontend/ir/program.h"
11#include "shader_recompiler/profile.h"
12#include "shader_recompiler/runtime_info.h"
13
14namespace Shader::Backend::GLASM {
15
16[[nodiscard]] std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info,
17 IR::Program& program, Bindings& bindings);
18
19[[nodiscard]] inline std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info,
20 IR::Program& program) {
21 Bindings binding;
22 return EmitGLASM(profile, runtime_info, program, binding);
23}
24
25} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_barriers.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_barriers.cpp
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_barriers.cpp
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp
new file mode 100644
index 000000000..9201ccd39
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp
@@ -0,0 +1,91 @@
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 "shader_recompiler/backend/glasm/emit_context.h"
6#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
7#include "shader_recompiler/frontend/ir/value.h"
8
9namespace Shader::Backend::GLASM {
10
11static void Alias(IR::Inst& inst, const IR::Value& value) {
12 if (value.IsImmediate()) {
13 return;
14 }
15 IR::Inst& value_inst{RegAlloc::AliasInst(*value.Inst())};
16 value_inst.DestructiveAddUsage(inst.UseCount());
17 value_inst.DestructiveRemoveUsage();
18 inst.SetDefinition(value_inst.Definition<Id>());
19}
20
21void EmitIdentity(EmitContext&, IR::Inst& inst, const IR::Value& value) {
22 Alias(inst, value);
23}
24
25void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value) {
26 // Fake one usage to get a real register out of the condition
27 inst.DestructiveAddUsage(1);
28 const Register ret{ctx.reg_alloc.Define(inst)};
29 const ScalarS32 input{ctx.reg_alloc.Consume(value)};
30 if (ret != input) {
31 ctx.Add("MOV.S {},{};", ret, input);
32 }
33}
34
35void EmitBitCastU16F16(EmitContext&, IR::Inst& inst, const IR::Value& value) {
36 Alias(inst, value);
37}
38
39void EmitBitCastU32F32(EmitContext&, IR::Inst& inst, const IR::Value& value) {
40 Alias(inst, value);
41}
42
43void EmitBitCastU64F64(EmitContext&, IR::Inst& inst, const IR::Value& value) {
44 Alias(inst, value);
45}
46
47void EmitBitCastF16U16(EmitContext&, IR::Inst& inst, const IR::Value& value) {
48 Alias(inst, value);
49}
50
51void EmitBitCastF32U32(EmitContext&, IR::Inst& inst, const IR::Value& value) {
52 Alias(inst, value);
53}
54
55void EmitBitCastF64U64(EmitContext&, IR::Inst& inst, const IR::Value& value) {
56 Alias(inst, value);
57}
58
59void EmitPackUint2x32(EmitContext& ctx, IR::Inst& inst, Register value) {
60 ctx.LongAdd("PK64.U {}.x,{};", inst, value);
61}
62
63void EmitUnpackUint2x32(EmitContext& ctx, IR::Inst& inst, Register value) {
64 ctx.Add("UP64.U {}.xy,{}.x;", inst, value);
65}
66
67void EmitPackFloat2x16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
68 throw NotImplementedException("GLASM instruction");
69}
70
71void EmitUnpackFloat2x16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
72 throw NotImplementedException("GLASM instruction");
73}
74
75void EmitPackHalf2x16(EmitContext& ctx, IR::Inst& inst, Register value) {
76 ctx.Add("PK2H {}.x,{};", inst, value);
77}
78
79void EmitUnpackHalf2x16(EmitContext& ctx, IR::Inst& inst, Register value) {
80 ctx.Add("UP2H {}.xy,{}.x;", inst, value);
81}
82
83void EmitPackDouble2x32(EmitContext& ctx, IR::Inst& inst, Register value) {
84 ctx.LongAdd("PK64 {}.x,{};", inst, value);
85}
86
87void EmitUnpackDouble2x32(EmitContext& ctx, IR::Inst& inst, Register value) {
88 ctx.Add("UP64 {}.xy,{}.x;", inst, value);
89}
90
91} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp
new file mode 100644
index 000000000..bff0b7c1c
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp
@@ -0,0 +1,244 @@
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 "shader_recompiler/backend/glasm/emit_context.h"
6#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
7#include "shader_recompiler/frontend/ir/value.h"
8
9namespace Shader::Backend::GLASM {
10namespace {
11template <auto read_imm, char type, typename... Values>
12void CompositeConstruct(EmitContext& ctx, IR::Inst& inst, Values&&... elements) {
13 const Register ret{ctx.reg_alloc.Define(inst)};
14 if (std::ranges::any_of(std::array{elements...},
15 [](const IR::Value& value) { return value.IsImmediate(); })) {
16 using Type = std::invoke_result_t<decltype(read_imm), IR::Value>;
17 const std::array<Type, 4> values{(elements.IsImmediate() ? (elements.*read_imm)() : 0)...};
18 ctx.Add("MOV.{} {},{{{},{},{},{}}};", type, ret, fmt::to_string(values[0]),
19 fmt::to_string(values[1]), fmt::to_string(values[2]), fmt::to_string(values[3]));
20 }
21 size_t index{};
22 for (const IR::Value& element : {elements...}) {
23 if (!element.IsImmediate()) {
24 const ScalarU32 value{ctx.reg_alloc.Consume(element)};
25 ctx.Add("MOV.{} {}.{},{};", type, ret, "xyzw"[index], value);
26 }
27 ++index;
28 }
29}
30
31void CompositeExtract(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index, char type) {
32 const Register ret{ctx.reg_alloc.Define(inst)};
33 if (ret == composite && index == 0) {
34 // No need to do anything here, the source and destination are the same register
35 return;
36 }
37 ctx.Add("MOV.{} {}.x,{}.{};", type, ret, composite, "xyzw"[index]);
38}
39
40template <typename ObjectType>
41void CompositeInsert(EmitContext& ctx, IR::Inst& inst, Register composite, ObjectType object,
42 u32 index, char type) {
43 const Register ret{ctx.reg_alloc.Define(inst)};
44 const char swizzle{"xyzw"[index]};
45 if (ret != composite && ret == object) {
46 // The object is aliased with the return value, so we have to use a temporary to insert
47 ctx.Add("MOV.{} RC,{};"
48 "MOV.{} RC.{},{};"
49 "MOV.{} {},RC;",
50 type, composite, type, swizzle, object, type, ret);
51 } else if (ret != composite) {
52 // The input composite is not aliased with the return value so we have to copy it before
53 // hand. But the insert object is not aliased with the return value, so we don't have to
54 // worry about that
55 ctx.Add("MOV.{} {},{};"
56 "MOV.{} {}.{},{};",
57 type, ret, composite, type, ret, swizzle, object);
58 } else {
59 // The return value is alised so we can just insert the object, it doesn't matter if it's
60 // aliased
61 ctx.Add("MOV.{} {}.{},{};", type, ret, swizzle, object);
62 }
63}
64} // Anonymous namespace
65
66void EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
67 const IR::Value& e2) {
68 CompositeConstruct<&IR::Value::U32, 'U'>(ctx, inst, e1, e2);
69}
70
71void EmitCompositeConstructU32x3(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
72 const IR::Value& e2, const IR::Value& e3) {
73 CompositeConstruct<&IR::Value::U32, 'U'>(ctx, inst, e1, e2, e3);
74}
75
76void EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
77 const IR::Value& e2, const IR::Value& e3, const IR::Value& e4) {
78 CompositeConstruct<&IR::Value::U32, 'U'>(ctx, inst, e1, e2, e3, e4);
79}
80
81void EmitCompositeExtractU32x2(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
82 CompositeExtract(ctx, inst, composite, index, 'U');
83}
84
85void EmitCompositeExtractU32x3(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
86 CompositeExtract(ctx, inst, composite, index, 'U');
87}
88
89void EmitCompositeExtractU32x4(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
90 CompositeExtract(ctx, inst, composite, index, 'U');
91}
92
93void EmitCompositeInsertU32x2([[maybe_unused]] EmitContext& ctx,
94 [[maybe_unused]] Register composite,
95 [[maybe_unused]] ScalarU32 object, [[maybe_unused]] u32 index) {
96 throw NotImplementedException("GLASM instruction");
97}
98
99void EmitCompositeInsertU32x3([[maybe_unused]] EmitContext& ctx,
100 [[maybe_unused]] Register composite,
101 [[maybe_unused]] ScalarU32 object, [[maybe_unused]] u32 index) {
102 throw NotImplementedException("GLASM instruction");
103}
104
105void EmitCompositeInsertU32x4([[maybe_unused]] EmitContext& ctx,
106 [[maybe_unused]] Register composite,
107 [[maybe_unused]] ScalarU32 object, [[maybe_unused]] u32 index) {
108 throw NotImplementedException("GLASM instruction");
109}
110
111void EmitCompositeConstructF16x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register e1,
112 [[maybe_unused]] Register e2) {
113 throw NotImplementedException("GLASM instruction");
114}
115
116void EmitCompositeConstructF16x3([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register e1,
117 [[maybe_unused]] Register e2, [[maybe_unused]] Register e3) {
118 throw NotImplementedException("GLASM instruction");
119}
120
121void EmitCompositeConstructF16x4([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register e1,
122 [[maybe_unused]] Register e2, [[maybe_unused]] Register e3,
123 [[maybe_unused]] Register e4) {
124 throw NotImplementedException("GLASM instruction");
125}
126
127void EmitCompositeExtractF16x2([[maybe_unused]] EmitContext& ctx,
128 [[maybe_unused]] Register composite, [[maybe_unused]] u32 index) {
129 throw NotImplementedException("GLASM instruction");
130}
131
132void EmitCompositeExtractF16x3([[maybe_unused]] EmitContext& ctx,
133 [[maybe_unused]] Register composite, [[maybe_unused]] u32 index) {
134 throw NotImplementedException("GLASM instruction");
135}
136
137void EmitCompositeExtractF16x4([[maybe_unused]] EmitContext& ctx,
138 [[maybe_unused]] Register composite, [[maybe_unused]] u32 index) {
139 throw NotImplementedException("GLASM instruction");
140}
141
142void EmitCompositeInsertF16x2([[maybe_unused]] EmitContext& ctx,
143 [[maybe_unused]] Register composite, [[maybe_unused]] Register object,
144 [[maybe_unused]] u32 index) {
145 throw NotImplementedException("GLASM instruction");
146}
147
148void EmitCompositeInsertF16x3([[maybe_unused]] EmitContext& ctx,
149 [[maybe_unused]] Register composite, [[maybe_unused]] Register object,
150 [[maybe_unused]] u32 index) {
151 throw NotImplementedException("GLASM instruction");
152}
153
154void EmitCompositeInsertF16x4([[maybe_unused]] EmitContext& ctx,
155 [[maybe_unused]] Register composite, [[maybe_unused]] Register object,
156 [[maybe_unused]] u32 index) {
157 throw NotImplementedException("GLASM instruction");
158}
159
160void EmitCompositeConstructF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
161 const IR::Value& e2) {
162 CompositeConstruct<&IR::Value::F32, 'F'>(ctx, inst, e1, e2);
163}
164
165void EmitCompositeConstructF32x3(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
166 const IR::Value& e2, const IR::Value& e3) {
167 CompositeConstruct<&IR::Value::F32, 'F'>(ctx, inst, e1, e2, e3);
168}
169
170void EmitCompositeConstructF32x4(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
171 const IR::Value& e2, const IR::Value& e3, const IR::Value& e4) {
172 CompositeConstruct<&IR::Value::F32, 'F'>(ctx, inst, e1, e2, e3, e4);
173}
174
175void EmitCompositeExtractF32x2(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
176 CompositeExtract(ctx, inst, composite, index, 'F');
177}
178
179void EmitCompositeExtractF32x3(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
180 CompositeExtract(ctx, inst, composite, index, 'F');
181}
182
183void EmitCompositeExtractF32x4(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
184 CompositeExtract(ctx, inst, composite, index, 'F');
185}
186
187void EmitCompositeInsertF32x2(EmitContext& ctx, IR::Inst& inst, Register composite,
188 ScalarF32 object, u32 index) {
189 CompositeInsert(ctx, inst, composite, object, index, 'F');
190}
191
192void EmitCompositeInsertF32x3(EmitContext& ctx, IR::Inst& inst, Register composite,
193 ScalarF32 object, u32 index) {
194 CompositeInsert(ctx, inst, composite, object, index, 'F');
195}
196
197void EmitCompositeInsertF32x4(EmitContext& ctx, IR::Inst& inst, Register composite,
198 ScalarF32 object, u32 index) {
199 CompositeInsert(ctx, inst, composite, object, index, 'F');
200}
201
202void EmitCompositeConstructF64x2([[maybe_unused]] EmitContext& ctx) {
203 throw NotImplementedException("GLASM instruction");
204}
205
206void EmitCompositeConstructF64x3([[maybe_unused]] EmitContext& ctx) {
207 throw NotImplementedException("GLASM instruction");
208}
209
210void EmitCompositeConstructF64x4([[maybe_unused]] EmitContext& ctx) {
211 throw NotImplementedException("GLASM instruction");
212}
213
214void EmitCompositeExtractF64x2([[maybe_unused]] EmitContext& ctx) {
215 throw NotImplementedException("GLASM instruction");
216}
217
218void EmitCompositeExtractF64x3([[maybe_unused]] EmitContext& ctx) {
219 throw NotImplementedException("GLASM instruction");
220}
221
222void EmitCompositeExtractF64x4([[maybe_unused]] EmitContext& ctx) {
223 throw NotImplementedException("GLASM instruction");
224}
225
226void EmitCompositeInsertF64x2([[maybe_unused]] EmitContext& ctx,
227 [[maybe_unused]] Register composite, [[maybe_unused]] Register object,
228 [[maybe_unused]] u32 index) {
229 throw NotImplementedException("GLASM instruction");
230}
231
232void EmitCompositeInsertF64x3([[maybe_unused]] EmitContext& ctx,
233 [[maybe_unused]] Register composite, [[maybe_unused]] Register object,
234 [[maybe_unused]] u32 index) {
235 throw NotImplementedException("GLASM instruction");
236}
237
238void EmitCompositeInsertF64x4([[maybe_unused]] EmitContext& ctx,
239 [[maybe_unused]] Register composite, [[maybe_unused]] Register object,
240 [[maybe_unused]] u32 index) {
241 throw NotImplementedException("GLASM instruction");
242}
243
244} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
new file mode 100644
index 000000000..02c9dc6d7
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
@@ -0,0 +1,346 @@
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 <string_view>
6
7#include "shader_recompiler/backend/glasm/emit_context.h"
8#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10#include "shader_recompiler/profile.h"
11#include "shader_recompiler/shader_info.h"
12
13namespace Shader::Backend::GLASM {
14namespace {
15void GetCbuf(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset,
16 std::string_view size) {
17 if (!binding.IsImmediate()) {
18 throw NotImplementedException("Indirect constant buffer loading");
19 }
20 const Register ret{ctx.reg_alloc.Define(inst)};
21 if (offset.type == Type::U32) {
22 // Avoid reading arrays out of bounds, matching hardware's behavior
23 if (offset.imm_u32 >= 0x10'000) {
24 ctx.Add("MOV.S {},0;", ret);
25 return;
26 }
27 }
28 ctx.Add("LDC.{} {},c{}[{}];", size, ret, binding.U32(), offset);
29}
30
31bool IsInputArray(Stage stage) {
32 return stage == Stage::Geometry || stage == Stage::TessellationControl ||
33 stage == Stage::TessellationEval;
34}
35
36std::string VertexIndex(EmitContext& ctx, ScalarU32 vertex) {
37 return IsInputArray(ctx.stage) ? fmt::format("[{}]", vertex) : "";
38}
39
40u32 TexCoordIndex(IR::Attribute attr) {
41 return (static_cast<u32>(attr) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
42}
43} // Anonymous namespace
44
45void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
46 GetCbuf(ctx, inst, binding, offset, "U8");
47}
48
49void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
50 GetCbuf(ctx, inst, binding, offset, "S8");
51}
52
53void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
54 GetCbuf(ctx, inst, binding, offset, "U16");
55}
56
57void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
58 GetCbuf(ctx, inst, binding, offset, "S16");
59}
60
61void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
62 GetCbuf(ctx, inst, binding, offset, "U32");
63}
64
65void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
66 GetCbuf(ctx, inst, binding, offset, "F32");
67}
68
69void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
70 ScalarU32 offset) {
71 GetCbuf(ctx, inst, binding, offset, "U32X2");
72}
73
74void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32 vertex) {
75 const u32 element{static_cast<u32>(attr) % 4};
76 const char swizzle{"xyzw"[element]};
77 if (IR::IsGeneric(attr)) {
78 const u32 index{IR::GenericAttributeIndex(attr)};
79 ctx.Add("MOV.F {}.x,in_attr{}{}[0].{};", inst, index, VertexIndex(ctx, vertex), swizzle);
80 return;
81 }
82 if (attr >= IR::Attribute::FixedFncTexture0S && attr <= IR::Attribute::FixedFncTexture9Q) {
83 const u32 index{TexCoordIndex(attr)};
84 ctx.Add("MOV.F {}.x,{}.texcoord[{}].{};", inst, ctx.attrib_name, index, swizzle);
85 return;
86 }
87 switch (attr) {
88 case IR::Attribute::PrimitiveId:
89 ctx.Add("MOV.S {}.x,primitive.id;", inst);
90 break;
91 case IR::Attribute::PositionX:
92 case IR::Attribute::PositionY:
93 case IR::Attribute::PositionZ:
94 case IR::Attribute::PositionW:
95 if (IsInputArray(ctx.stage)) {
96 ctx.Add("MOV.F {}.x,vertex_position{}.{};", inst, VertexIndex(ctx, vertex), swizzle);
97 } else {
98 ctx.Add("MOV.F {}.x,{}.position.{};", inst, ctx.attrib_name, swizzle);
99 }
100 break;
101 case IR::Attribute::ColorFrontDiffuseR:
102 case IR::Attribute::ColorFrontDiffuseG:
103 case IR::Attribute::ColorFrontDiffuseB:
104 case IR::Attribute::ColorFrontDiffuseA:
105 ctx.Add("MOV.F {}.x,{}.color.{};", inst, ctx.attrib_name, swizzle);
106 break;
107 case IR::Attribute::PointSpriteS:
108 case IR::Attribute::PointSpriteT:
109 ctx.Add("MOV.F {}.x,{}.pointcoord.{};", inst, ctx.attrib_name, swizzle);
110 break;
111 case IR::Attribute::TessellationEvaluationPointU:
112 case IR::Attribute::TessellationEvaluationPointV:
113 ctx.Add("MOV.F {}.x,vertex.tesscoord.{};", inst, swizzle);
114 break;
115 case IR::Attribute::InstanceId:
116 ctx.Add("MOV.S {}.x,{}.instance;", inst, ctx.attrib_name);
117 break;
118 case IR::Attribute::VertexId:
119 ctx.Add("MOV.S {}.x,{}.id;", inst, ctx.attrib_name);
120 break;
121 case IR::Attribute::FrontFace:
122 ctx.Add("CMP.S {}.x,{}.facing.x,0,-1;", inst, ctx.attrib_name);
123 break;
124 default:
125 throw NotImplementedException("Get attribute {}", attr);
126 }
127}
128
129void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, ScalarF32 value,
130 [[maybe_unused]] ScalarU32 vertex) {
131 const u32 element{static_cast<u32>(attr) % 4};
132 const char swizzle{"xyzw"[element]};
133 if (IR::IsGeneric(attr)) {
134 const u32 index{IR::GenericAttributeIndex(attr)};
135 ctx.Add("MOV.F out_attr{}[0].{},{};", index, swizzle, value);
136 return;
137 }
138 if (attr >= IR::Attribute::FixedFncTexture0S && attr <= IR::Attribute::FixedFncTexture9R) {
139 const u32 index{TexCoordIndex(attr)};
140 ctx.Add("MOV.F result.texcoord[{}].{},{};", index, swizzle, value);
141 return;
142 }
143 switch (attr) {
144 case IR::Attribute::Layer:
145 if (ctx.stage == Stage::Geometry || ctx.profile.support_viewport_index_layer_non_geometry) {
146 ctx.Add("MOV.F result.layer.x,{};", value);
147 } else {
148 LOG_WARNING(Shader_GLASM,
149 "Layer stored outside of geometry shader not supported by device");
150 }
151 break;
152 case IR::Attribute::ViewportIndex:
153 if (ctx.stage == Stage::Geometry || ctx.profile.support_viewport_index_layer_non_geometry) {
154 ctx.Add("MOV.F result.viewport.x,{};", value);
155 } else {
156 LOG_WARNING(Shader_GLASM,
157 "Viewport stored outside of geometry shader not supported by device");
158 }
159 break;
160 case IR::Attribute::ViewportMask:
161 // NV_viewport_array2 is required to access result.viewportmask, regardless of shader stage.
162 if (ctx.profile.support_viewport_index_layer_non_geometry) {
163 ctx.Add("MOV.F result.viewportmask[0].x,{};", value);
164 } else {
165 LOG_WARNING(Shader_GLASM, "Device does not support storing to ViewportMask");
166 }
167 break;
168 case IR::Attribute::PointSize:
169 ctx.Add("MOV.F result.pointsize.x,{};", value);
170 break;
171 case IR::Attribute::PositionX:
172 case IR::Attribute::PositionY:
173 case IR::Attribute::PositionZ:
174 case IR::Attribute::PositionW:
175 ctx.Add("MOV.F result.position.{},{};", swizzle, value);
176 break;
177 case IR::Attribute::ColorFrontDiffuseR:
178 case IR::Attribute::ColorFrontDiffuseG:
179 case IR::Attribute::ColorFrontDiffuseB:
180 case IR::Attribute::ColorFrontDiffuseA:
181 ctx.Add("MOV.F result.color.{},{};", swizzle, value);
182 break;
183 case IR::Attribute::ColorFrontSpecularR:
184 case IR::Attribute::ColorFrontSpecularG:
185 case IR::Attribute::ColorFrontSpecularB:
186 case IR::Attribute::ColorFrontSpecularA:
187 ctx.Add("MOV.F result.color.secondary.{},{};", swizzle, value);
188 break;
189 case IR::Attribute::ColorBackDiffuseR:
190 case IR::Attribute::ColorBackDiffuseG:
191 case IR::Attribute::ColorBackDiffuseB:
192 case IR::Attribute::ColorBackDiffuseA:
193 ctx.Add("MOV.F result.color.back.{},{};", swizzle, value);
194 break;
195 case IR::Attribute::ColorBackSpecularR:
196 case IR::Attribute::ColorBackSpecularG:
197 case IR::Attribute::ColorBackSpecularB:
198 case IR::Attribute::ColorBackSpecularA:
199 ctx.Add("MOV.F result.color.back.secondary.{},{};", swizzle, value);
200 break;
201 case IR::Attribute::FogCoordinate:
202 ctx.Add("MOV.F result.fogcoord.x,{};", value);
203 break;
204 case IR::Attribute::ClipDistance0:
205 case IR::Attribute::ClipDistance1:
206 case IR::Attribute::ClipDistance2:
207 case IR::Attribute::ClipDistance3:
208 case IR::Attribute::ClipDistance4:
209 case IR::Attribute::ClipDistance5:
210 case IR::Attribute::ClipDistance6:
211 case IR::Attribute::ClipDistance7: {
212 const u32 index{static_cast<u32>(attr) - static_cast<u32>(IR::Attribute::ClipDistance0)};
213 ctx.Add("MOV.F result.clip[{}].x,{};", index, value);
214 break;
215 }
216 default:
217 throw NotImplementedException("Set attribute {}", attr);
218 }
219}
220
221void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, ScalarS32 offset, ScalarU32 vertex) {
222 // RC.x = base_index
223 // RC.y = masked_index
224 // RC.z = compare_index
225 ctx.Add("SHR.S RC.x,{},2;"
226 "AND.S RC.y,RC.x,3;"
227 "SHR.S RC.z,{},4;",
228 offset, offset);
229
230 const Register ret{ctx.reg_alloc.Define(inst)};
231 u32 num_endifs{};
232 const auto read{[&](u32 compare_index, const std::array<std::string, 4>& values) {
233 ++num_endifs;
234 ctx.Add("SEQ.S.CC RC.w,RC.z,{};" // compare_index
235 "IF NE.w;"
236 // X
237 "SEQ.S.CC RC.w,RC.y,0;"
238 "IF NE.w;"
239 "MOV {}.x,{};"
240 "ELSE;"
241 // Y
242 "SEQ.S.CC RC.w,RC.y,1;"
243 "IF NE.w;"
244 "MOV {}.x,{};"
245 "ELSE;"
246 // Z
247 "SEQ.S.CC RC.w,RC.y,2;"
248 "IF NE.w;"
249 "MOV {}.x,{};"
250 "ELSE;"
251 // W
252 "MOV {}.x,{};"
253 "ENDIF;"
254 "ENDIF;"
255 "ENDIF;"
256 "ELSE;",
257 compare_index, ret, values[0], ret, values[1], ret, values[2], ret, values[3]);
258 }};
259 const auto read_swizzled{[&](u32 compare_index, std::string_view value) {
260 const std::array values{fmt::format("{}.x", value), fmt::format("{}.y", value),
261 fmt::format("{}.z", value), fmt::format("{}.w", value)};
262 read(compare_index, values);
263 }};
264 if (ctx.info.loads.AnyComponent(IR::Attribute::PositionX)) {
265 const u32 index{static_cast<u32>(IR::Attribute::PositionX)};
266 if (IsInputArray(ctx.stage)) {
267 read_swizzled(index, fmt::format("vertex_position{}", VertexIndex(ctx, vertex)));
268 } else {
269 read_swizzled(index, fmt::format("{}.position", ctx.attrib_name));
270 }
271 }
272 for (u32 index = 0; index < static_cast<u32>(IR::NUM_GENERICS); ++index) {
273 if (!ctx.info.loads.Generic(index)) {
274 continue;
275 }
276 read_swizzled(index, fmt::format("in_attr{}{}[0]", index, VertexIndex(ctx, vertex)));
277 }
278 for (u32 i = 0; i < num_endifs; ++i) {
279 ctx.Add("ENDIF;");
280 }
281}
282
283void EmitSetAttributeIndexed([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] ScalarU32 offset,
284 [[maybe_unused]] ScalarF32 value, [[maybe_unused]] ScalarU32 vertex) {
285 throw NotImplementedException("GLASM instruction");
286}
287
288void EmitGetPatch(EmitContext& ctx, IR::Inst& inst, IR::Patch patch) {
289 if (!IR::IsGeneric(patch)) {
290 throw NotImplementedException("Non-generic patch load");
291 }
292 const u32 index{IR::GenericPatchIndex(patch)};
293 const u32 element{IR::GenericPatchElement(patch)};
294 const char swizzle{"xyzw"[element]};
295 const std::string_view out{ctx.stage == Stage::TessellationControl ? ".out" : ""};
296 ctx.Add("MOV.F {},primitive{}.patch.attrib[{}].{};", inst, out, index, swizzle);
297}
298
299void EmitSetPatch(EmitContext& ctx, IR::Patch patch, ScalarF32 value) {
300 if (IR::IsGeneric(patch)) {
301 const u32 index{IR::GenericPatchIndex(patch)};
302 const u32 element{IR::GenericPatchElement(patch)};
303 ctx.Add("MOV.F result.patch.attrib[{}].{},{};", index, "xyzw"[element], value);
304 return;
305 }
306 switch (patch) {
307 case IR::Patch::TessellationLodLeft:
308 case IR::Patch::TessellationLodRight:
309 case IR::Patch::TessellationLodTop:
310 case IR::Patch::TessellationLodBottom: {
311 const u32 index{static_cast<u32>(patch) - u32(IR::Patch::TessellationLodLeft)};
312 ctx.Add("MOV.F result.patch.tessouter[{}].x,{};", index, value);
313 break;
314 }
315 case IR::Patch::TessellationLodInteriorU:
316 ctx.Add("MOV.F result.patch.tessinner[0].x,{};", value);
317 break;
318 case IR::Patch::TessellationLodInteriorV:
319 ctx.Add("MOV.F result.patch.tessinner[1].x,{};", value);
320 break;
321 default:
322 throw NotImplementedException("Patch {}", patch);
323 }
324}
325
326void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, ScalarF32 value) {
327 ctx.Add("MOV.F frag_color{}.{},{};", index, "xyzw"[component], value);
328}
329
330void EmitSetSampleMask(EmitContext& ctx, ScalarS32 value) {
331 ctx.Add("MOV.S result.samplemask.x,{};", value);
332}
333
334void EmitSetFragDepth(EmitContext& ctx, ScalarF32 value) {
335 ctx.Add("MOV.F result.depth.z,{};", value);
336}
337
338void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset) {
339 ctx.Add("MOV.U {},lmem[{}].x;", inst, word_offset);
340}
341
342void EmitWriteLocal(EmitContext& ctx, ScalarU32 word_offset, ScalarU32 value) {
343 ctx.Add("MOV.U lmem[{}].x,{};", word_offset, value);
344}
345
346} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_control_flow.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_control_flow.cpp
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_control_flow.cpp
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp
new file mode 100644
index 000000000..ccdf1cbc8
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp
@@ -0,0 +1,231 @@
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 <string_view>
6
7#include "shader_recompiler/backend/glasm/emit_context.h"
8#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
9#include "shader_recompiler/frontend/ir/modifiers.h"
10#include "shader_recompiler/frontend/ir/value.h"
11
12namespace Shader::Backend::GLASM {
13namespace {
14std::string_view FpRounding(IR::FpRounding fp_rounding) {
15 switch (fp_rounding) {
16 case IR::FpRounding::DontCare:
17 return "";
18 case IR::FpRounding::RN:
19 return ".ROUND";
20 case IR::FpRounding::RZ:
21 return ".TRUNC";
22 case IR::FpRounding::RM:
23 return ".FLR";
24 case IR::FpRounding::RP:
25 return ".CEIL";
26 }
27 throw InvalidArgument("Invalid floating-point rounding {}", fp_rounding);
28}
29
30template <typename InputType>
31void Convert(EmitContext& ctx, IR::Inst& inst, InputType value, std::string_view dest,
32 std::string_view src, bool is_long_result) {
33 const std::string_view fp_rounding{FpRounding(inst.Flags<IR::FpControl>().rounding)};
34 const auto ret{is_long_result ? ctx.reg_alloc.LongDefine(inst) : ctx.reg_alloc.Define(inst)};
35 ctx.Add("CVT.{}.{}{} {}.x,{};", dest, src, fp_rounding, ret, value);
36}
37} // Anonymous namespace
38
39void EmitConvertS16F16(EmitContext& ctx, IR::Inst& inst, Register value) {
40 Convert(ctx, inst, value, "S16", "F16", false);
41}
42
43void EmitConvertS16F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
44 Convert(ctx, inst, value, "S16", "F32", false);
45}
46
47void EmitConvertS16F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
48 Convert(ctx, inst, value, "S16", "F64", false);
49}
50
51void EmitConvertS32F16(EmitContext& ctx, IR::Inst& inst, Register value) {
52 Convert(ctx, inst, value, "S32", "F16", false);
53}
54
55void EmitConvertS32F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
56 Convert(ctx, inst, value, "S32", "F32", false);
57}
58
59void EmitConvertS32F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
60 Convert(ctx, inst, value, "S32", "F64", false);
61}
62
63void EmitConvertS64F16(EmitContext& ctx, IR::Inst& inst, Register value) {
64 Convert(ctx, inst, value, "S64", "F16", true);
65}
66
67void EmitConvertS64F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
68 Convert(ctx, inst, value, "S64", "F32", true);
69}
70
71void EmitConvertS64F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
72 Convert(ctx, inst, value, "S64", "F64", true);
73}
74
75void EmitConvertU16F16(EmitContext& ctx, IR::Inst& inst, Register value) {
76 Convert(ctx, inst, value, "U16", "F16", false);
77}
78
79void EmitConvertU16F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
80 Convert(ctx, inst, value, "U16", "F32", false);
81}
82
83void EmitConvertU16F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
84 Convert(ctx, inst, value, "U16", "F64", false);
85}
86
87void EmitConvertU32F16(EmitContext& ctx, IR::Inst& inst, Register value) {
88 Convert(ctx, inst, value, "U32", "F16", false);
89}
90
91void EmitConvertU32F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
92 Convert(ctx, inst, value, "U32", "F32", false);
93}
94
95void EmitConvertU32F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
96 Convert(ctx, inst, value, "U32", "F64", false);
97}
98
99void EmitConvertU64F16(EmitContext& ctx, IR::Inst& inst, Register value) {
100 Convert(ctx, inst, value, "U64", "F16", true);
101}
102
103void EmitConvertU64F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
104 Convert(ctx, inst, value, "U64", "F32", true);
105}
106
107void EmitConvertU64F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
108 Convert(ctx, inst, value, "U64", "F64", true);
109}
110
111void EmitConvertU64U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value) {
112 Convert(ctx, inst, value, "U64", "U32", true);
113}
114
115void EmitConvertU32U64(EmitContext& ctx, IR::Inst& inst, Register value) {
116 Convert(ctx, inst, value, "U32", "U64", false);
117}
118
119void EmitConvertF16F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
120 Convert(ctx, inst, value, "F16", "F32", false);
121}
122
123void EmitConvertF32F16(EmitContext& ctx, IR::Inst& inst, Register value) {
124 Convert(ctx, inst, value, "F32", "F16", false);
125}
126
127void EmitConvertF32F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
128 Convert(ctx, inst, value, "F32", "F64", false);
129}
130
131void EmitConvertF64F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
132 Convert(ctx, inst, value, "F64", "F32", true);
133}
134
135void EmitConvertF16S8(EmitContext& ctx, IR::Inst& inst, Register value) {
136 Convert(ctx, inst, value, "F16", "S8", false);
137}
138
139void EmitConvertF16S16(EmitContext& ctx, IR::Inst& inst, Register value) {
140 Convert(ctx, inst, value, "F16", "S16", false);
141}
142
143void EmitConvertF16S32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
144 Convert(ctx, inst, value, "F16", "S32", false);
145}
146
147void EmitConvertF16S64(EmitContext& ctx, IR::Inst& inst, Register value) {
148 Convert(ctx, inst, value, "F16", "S64", false);
149}
150
151void EmitConvertF16U8(EmitContext& ctx, IR::Inst& inst, Register value) {
152 Convert(ctx, inst, value, "F16", "U8", false);
153}
154
155void EmitConvertF16U16(EmitContext& ctx, IR::Inst& inst, Register value) {
156 Convert(ctx, inst, value, "F16", "U16", false);
157}
158
159void EmitConvertF16U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value) {
160 Convert(ctx, inst, value, "F16", "U32", false);
161}
162
163void EmitConvertF16U64(EmitContext& ctx, IR::Inst& inst, Register value) {
164 Convert(ctx, inst, value, "F16", "U64", false);
165}
166
167void EmitConvertF32S8(EmitContext& ctx, IR::Inst& inst, Register value) {
168 Convert(ctx, inst, value, "F32", "S8", false);
169}
170
171void EmitConvertF32S16(EmitContext& ctx, IR::Inst& inst, Register value) {
172 Convert(ctx, inst, value, "F32", "S16", false);
173}
174
175void EmitConvertF32S32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
176 Convert(ctx, inst, value, "F32", "S32", false);
177}
178
179void EmitConvertF32S64(EmitContext& ctx, IR::Inst& inst, Register value) {
180 Convert(ctx, inst, value, "F32", "S64", false);
181}
182
183void EmitConvertF32U8(EmitContext& ctx, IR::Inst& inst, Register value) {
184 Convert(ctx, inst, value, "F32", "U8", false);
185}
186
187void EmitConvertF32U16(EmitContext& ctx, IR::Inst& inst, Register value) {
188 Convert(ctx, inst, value, "F32", "U16", false);
189}
190
191void EmitConvertF32U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value) {
192 Convert(ctx, inst, value, "F32", "U32", false);
193}
194
195void EmitConvertF32U64(EmitContext& ctx, IR::Inst& inst, Register value) {
196 Convert(ctx, inst, value, "F32", "U64", false);
197}
198
199void EmitConvertF64S8(EmitContext& ctx, IR::Inst& inst, Register value) {
200 Convert(ctx, inst, value, "F64", "S8", true);
201}
202
203void EmitConvertF64S16(EmitContext& ctx, IR::Inst& inst, Register value) {
204 Convert(ctx, inst, value, "F64", "S16", true);
205}
206
207void EmitConvertF64S32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
208 Convert(ctx, inst, value, "F64", "S32", true);
209}
210
211void EmitConvertF64S64(EmitContext& ctx, IR::Inst& inst, Register value) {
212 Convert(ctx, inst, value, "F64", "S64", true);
213}
214
215void EmitConvertF64U8(EmitContext& ctx, IR::Inst& inst, Register value) {
216 Convert(ctx, inst, value, "F64", "U8", true);
217}
218
219void EmitConvertF64U16(EmitContext& ctx, IR::Inst& inst, Register value) {
220 Convert(ctx, inst, value, "F64", "U16", true);
221}
222
223void EmitConvertF64U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value) {
224 Convert(ctx, inst, value, "F64", "U32", true);
225}
226
227void EmitConvertF64U64(EmitContext& ctx, IR::Inst& inst, Register value) {
228 Convert(ctx, inst, value, "F64", "U64", true);
229}
230
231} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp
new file mode 100644
index 000000000..4ed58619d
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp
@@ -0,0 +1,414 @@
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 <string_view>
6
7#include "shader_recompiler/backend/glasm/emit_context.h"
8#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
9#include "shader_recompiler/frontend/ir/modifiers.h"
10#include "shader_recompiler/frontend/ir/value.h"
11
12namespace Shader::Backend::GLASM {
13namespace {
14template <typename InputType>
15void Compare(EmitContext& ctx, IR::Inst& inst, InputType lhs, InputType rhs, std::string_view op,
16 std::string_view type, bool ordered, bool inequality = false) {
17 const Register ret{ctx.reg_alloc.Define(inst)};
18 ctx.Add("{}.{} RC.x,{},{};", op, type, lhs, rhs);
19 if (ordered && inequality) {
20 ctx.Add("SEQ.{} RC.y,{},{};"
21 "SEQ.{} RC.z,{},{};"
22 "AND.U RC.x,RC.x,RC.y;"
23 "AND.U RC.x,RC.x,RC.z;"
24 "SNE.S {}.x,RC.x,0;",
25 type, lhs, lhs, type, rhs, rhs, ret);
26 } else if (ordered) {
27 ctx.Add("SNE.S {}.x,RC.x,0;", ret);
28 } else {
29 ctx.Add("SNE.{} RC.y,{},{};"
30 "SNE.{} RC.z,{},{};"
31 "OR.U RC.x,RC.x,RC.y;"
32 "OR.U RC.x,RC.x,RC.z;"
33 "SNE.S {}.x,RC.x,0;",
34 type, lhs, lhs, type, rhs, rhs, ret);
35 }
36}
37
38template <typename InputType>
39void Clamp(EmitContext& ctx, Register ret, InputType value, InputType min_value,
40 InputType max_value, std::string_view type) {
41 // Call MAX first to properly clamp nan to min_value instead
42 ctx.Add("MAX.{} RC.x,{},{};"
43 "MIN.{} {}.x,RC.x,{};",
44 type, min_value, value, type, ret, max_value);
45}
46
47std::string_view Precise(IR::Inst& inst) {
48 const bool precise{inst.Flags<IR::FpControl>().no_contraction};
49 return precise ? ".PREC" : "";
50}
51} // Anonymous namespace
52
53void EmitFPAbs16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
54 [[maybe_unused]] Register value) {
55 throw NotImplementedException("GLASM instruction");
56}
57
58void EmitFPAbs32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
59 ctx.Add("MOV.F {}.x,|{}|;", inst, value);
60}
61
62void EmitFPAbs64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
63 ctx.LongAdd("MOV.F64 {}.x,|{}|;", inst, value);
64}
65
66void EmitFPAdd16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
67 [[maybe_unused]] Register a, [[maybe_unused]] Register b) {
68 throw NotImplementedException("GLASM instruction");
69}
70
71void EmitFPAdd32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b) {
72 ctx.Add("ADD.F{} {}.x,{},{};", Precise(inst), ctx.reg_alloc.Define(inst), a, b);
73}
74
75void EmitFPAdd64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b) {
76 ctx.Add("ADD.F64{} {}.x,{},{};", Precise(inst), ctx.reg_alloc.LongDefine(inst), a, b);
77}
78
79void EmitFPFma16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
80 [[maybe_unused]] Register a, [[maybe_unused]] Register b,
81 [[maybe_unused]] Register c) {
82 throw NotImplementedException("GLASM instruction");
83}
84
85void EmitFPFma32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b, ScalarF32 c) {
86 ctx.Add("MAD.F{} {}.x,{},{},{};", Precise(inst), ctx.reg_alloc.Define(inst), a, b, c);
87}
88
89void EmitFPFma64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b, ScalarF64 c) {
90 ctx.Add("MAD.F64{} {}.x,{},{},{};", Precise(inst), ctx.reg_alloc.LongDefine(inst), a, b, c);
91}
92
93void EmitFPMax32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b) {
94 ctx.Add("MAX.F {}.x,{},{};", inst, a, b);
95}
96
97void EmitFPMax64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b) {
98 ctx.LongAdd("MAX.F64 {}.x,{},{};", inst, a, b);
99}
100
101void EmitFPMin32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b) {
102 ctx.Add("MIN.F {}.x,{},{};", inst, a, b);
103}
104
105void EmitFPMin64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b) {
106 ctx.LongAdd("MIN.F64 {}.x,{},{};", inst, a, b);
107}
108
109void EmitFPMul16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
110 [[maybe_unused]] Register a, [[maybe_unused]] Register b) {
111 throw NotImplementedException("GLASM instruction");
112}
113
114void EmitFPMul32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b) {
115 ctx.Add("MUL.F{} {}.x,{},{};", Precise(inst), ctx.reg_alloc.Define(inst), a, b);
116}
117
118void EmitFPMul64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b) {
119 ctx.Add("MUL.F64{} {}.x,{},{};", Precise(inst), ctx.reg_alloc.LongDefine(inst), a, b);
120}
121
122void EmitFPNeg16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
123 throw NotImplementedException("GLASM instruction");
124}
125
126void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, ScalarRegister value) {
127 ctx.Add("MOV.F {}.x,-{};", inst, value);
128}
129
130void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, Register value) {
131 ctx.LongAdd("MOV.F64 {}.x,-{};", inst, value);
132}
133
134void EmitFPSin(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
135 ctx.Add("SIN {}.x,{};", inst, value);
136}
137
138void EmitFPCos(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
139 ctx.Add("COS {}.x,{};", inst, value);
140}
141
142void EmitFPExp2(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
143 ctx.Add("EX2 {}.x,{};", inst, value);
144}
145
146void EmitFPLog2(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
147 ctx.Add("LG2 {}.x,{};", inst, value);
148}
149
150void EmitFPRecip32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
151 ctx.Add("RCP {}.x,{};", inst, value);
152}
153
154void EmitFPRecip64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
155 throw NotImplementedException("GLASM instruction");
156}
157
158void EmitFPRecipSqrt32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
159 ctx.Add("RSQ {}.x,{};", inst, value);
160}
161
162void EmitFPRecipSqrt64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
163 throw NotImplementedException("GLASM instruction");
164}
165
166void EmitFPSqrt(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
167 const Register ret{ctx.reg_alloc.Define(inst)};
168 ctx.Add("RSQ RC.x,{};RCP {}.x,RC.x;", value, ret);
169}
170
171void EmitFPSaturate16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
172 throw NotImplementedException("GLASM instruction");
173}
174
175void EmitFPSaturate32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
176 ctx.Add("MOV.F.SAT {}.x,{};", inst, value);
177}
178
179void EmitFPSaturate64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
180 throw NotImplementedException("GLASM instruction");
181}
182
183void EmitFPClamp16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value,
184 [[maybe_unused]] Register min_value, [[maybe_unused]] Register max_value) {
185 throw NotImplementedException("GLASM instruction");
186}
187
188void EmitFPClamp32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value, ScalarF32 min_value,
189 ScalarF32 max_value) {
190 Clamp(ctx, ctx.reg_alloc.Define(inst), value, min_value, max_value, "F");
191}
192
193void EmitFPClamp64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value, ScalarF64 min_value,
194 ScalarF64 max_value) {
195 Clamp(ctx, ctx.reg_alloc.LongDefine(inst), value, min_value, max_value, "F64");
196}
197
198void EmitFPRoundEven16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
199 throw NotImplementedException("GLASM instruction");
200}
201
202void EmitFPRoundEven32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
203 ctx.Add("ROUND.F {}.x,{};", inst, value);
204}
205
206void EmitFPRoundEven64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
207 ctx.LongAdd("ROUND.F64 {}.x,{};", inst, value);
208}
209
210void EmitFPFloor16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
211 throw NotImplementedException("GLASM instruction");
212}
213
214void EmitFPFloor32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
215 ctx.Add("FLR.F {}.x,{};", inst, value);
216}
217
218void EmitFPFloor64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
219 ctx.LongAdd("FLR.F64 {}.x,{};", inst, value);
220}
221
222void EmitFPCeil16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
223 throw NotImplementedException("GLASM instruction");
224}
225
226void EmitFPCeil32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
227 ctx.Add("CEIL.F {}.x,{};", inst, value);
228}
229
230void EmitFPCeil64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
231 ctx.LongAdd("CEIL.F64 {}.x,{};", inst, value);
232}
233
234void EmitFPTrunc16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
235 throw NotImplementedException("GLASM instruction");
236}
237
238void EmitFPTrunc32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
239 ctx.Add("TRUNC.F {}.x,{};", inst, value);
240}
241
242void EmitFPTrunc64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
243 ctx.LongAdd("TRUNC.F64 {}.x,{};", inst, value);
244}
245
246void EmitFPOrdEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
247 [[maybe_unused]] Register rhs) {
248 throw NotImplementedException("GLASM instruction");
249}
250
251void EmitFPOrdEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
252 Compare(ctx, inst, lhs, rhs, "SEQ", "F", true);
253}
254
255void EmitFPOrdEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
256 Compare(ctx, inst, lhs, rhs, "SEQ", "F64", true);
257}
258
259void EmitFPUnordEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
260 [[maybe_unused]] Register rhs) {
261 throw NotImplementedException("GLASM instruction");
262}
263
264void EmitFPUnordEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
265 Compare(ctx, inst, lhs, rhs, "SEQ", "F", false);
266}
267
268void EmitFPUnordEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
269 Compare(ctx, inst, lhs, rhs, "SEQ", "F64", false);
270}
271
272void EmitFPOrdNotEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
273 [[maybe_unused]] Register rhs) {
274 throw NotImplementedException("GLASM instruction");
275}
276
277void EmitFPOrdNotEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
278 Compare(ctx, inst, lhs, rhs, "SNE", "F", true, true);
279}
280
281void EmitFPOrdNotEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
282 Compare(ctx, inst, lhs, rhs, "SNE", "F64", true, true);
283}
284
285void EmitFPUnordNotEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
286 [[maybe_unused]] Register rhs) {
287 throw NotImplementedException("GLASM instruction");
288}
289
290void EmitFPUnordNotEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
291 Compare(ctx, inst, lhs, rhs, "SNE", "F", false, true);
292}
293
294void EmitFPUnordNotEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
295 Compare(ctx, inst, lhs, rhs, "SNE", "F64", false, true);
296}
297
298void EmitFPOrdLessThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
299 [[maybe_unused]] Register rhs) {
300 throw NotImplementedException("GLASM instruction");
301}
302
303void EmitFPOrdLessThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
304 Compare(ctx, inst, lhs, rhs, "SLT", "F", true);
305}
306
307void EmitFPOrdLessThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
308 Compare(ctx, inst, lhs, rhs, "SLT", "F64", true);
309}
310
311void EmitFPUnordLessThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
312 [[maybe_unused]] Register rhs) {
313 throw NotImplementedException("GLASM instruction");
314}
315
316void EmitFPUnordLessThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
317 Compare(ctx, inst, lhs, rhs, "SLT", "F", false);
318}
319
320void EmitFPUnordLessThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
321 Compare(ctx, inst, lhs, rhs, "SLT", "F64", false);
322}
323
324void EmitFPOrdGreaterThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
325 [[maybe_unused]] Register rhs) {
326 throw NotImplementedException("GLASM instruction");
327}
328
329void EmitFPOrdGreaterThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
330 Compare(ctx, inst, lhs, rhs, "SGT", "F", true);
331}
332
333void EmitFPOrdGreaterThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
334 Compare(ctx, inst, lhs, rhs, "SGT", "F64", true);
335}
336
337void EmitFPUnordGreaterThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
338 [[maybe_unused]] Register rhs) {
339 throw NotImplementedException("GLASM instruction");
340}
341
342void EmitFPUnordGreaterThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
343 Compare(ctx, inst, lhs, rhs, "SGT", "F", false);
344}
345
346void EmitFPUnordGreaterThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
347 Compare(ctx, inst, lhs, rhs, "SGT", "F64", false);
348}
349
350void EmitFPOrdLessThanEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
351 [[maybe_unused]] Register rhs) {
352 throw NotImplementedException("GLASM instruction");
353}
354
355void EmitFPOrdLessThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
356 Compare(ctx, inst, lhs, rhs, "SLE", "F", true);
357}
358
359void EmitFPOrdLessThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
360 Compare(ctx, inst, lhs, rhs, "SLE", "F64", true);
361}
362
363void EmitFPUnordLessThanEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
364 [[maybe_unused]] Register rhs) {
365 throw NotImplementedException("GLASM instruction");
366}
367
368void EmitFPUnordLessThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
369 Compare(ctx, inst, lhs, rhs, "SLE", "F", false);
370}
371
372void EmitFPUnordLessThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
373 Compare(ctx, inst, lhs, rhs, "SLE", "F64", false);
374}
375
376void EmitFPOrdGreaterThanEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
377 [[maybe_unused]] Register rhs) {
378 throw NotImplementedException("GLASM instruction");
379}
380
381void EmitFPOrdGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
382 Compare(ctx, inst, lhs, rhs, "SGE", "F", true);
383}
384
385void EmitFPOrdGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
386 Compare(ctx, inst, lhs, rhs, "SGE", "F64", true);
387}
388
389void EmitFPUnordGreaterThanEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
390 [[maybe_unused]] Register rhs) {
391 throw NotImplementedException("GLASM instruction");
392}
393
394void EmitFPUnordGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
395 Compare(ctx, inst, lhs, rhs, "SGE", "F", false);
396}
397
398void EmitFPUnordGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
399 Compare(ctx, inst, lhs, rhs, "SGE", "F64", false);
400}
401
402void EmitFPIsNan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
403 throw NotImplementedException("GLASM instruction");
404}
405
406void EmitFPIsNan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
407 Compare(ctx, inst, value, value, "SNE", "F", true, false);
408}
409
410void EmitFPIsNan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
411 Compare(ctx, inst, value, value, "SNE", "F64", true, false);
412}
413
414} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
new file mode 100644
index 000000000..09e3a9b82
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
@@ -0,0 +1,850 @@
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 <utility>
6
7#include "shader_recompiler/backend/glasm/emit_context.h"
8#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
9#include "shader_recompiler/frontend/ir/modifiers.h"
10#include "shader_recompiler/frontend/ir/value.h"
11
12namespace Shader::Backend::GLASM {
13namespace {
14struct ScopedRegister {
15 ScopedRegister() = default;
16 ScopedRegister(RegAlloc& reg_alloc_) : reg_alloc{&reg_alloc_}, reg{reg_alloc->AllocReg()} {}
17
18 ~ScopedRegister() {
19 if (reg_alloc) {
20 reg_alloc->FreeReg(reg);
21 }
22 }
23
24 ScopedRegister& operator=(ScopedRegister&& rhs) noexcept {
25 if (reg_alloc) {
26 reg_alloc->FreeReg(reg);
27 }
28 reg_alloc = std::exchange(rhs.reg_alloc, nullptr);
29 reg = rhs.reg;
30 return *this;
31 }
32
33 ScopedRegister(ScopedRegister&& rhs) noexcept
34 : reg_alloc{std::exchange(rhs.reg_alloc, nullptr)}, reg{rhs.reg} {}
35
36 ScopedRegister& operator=(const ScopedRegister&) = delete;
37 ScopedRegister(const ScopedRegister&) = delete;
38
39 RegAlloc* reg_alloc{};
40 Register reg;
41};
42
43std::string Texture(EmitContext& ctx, IR::TextureInstInfo info,
44 [[maybe_unused]] const IR::Value& index) {
45 // FIXME: indexed reads
46 if (info.type == TextureType::Buffer) {
47 return fmt::format("texture[{}]", ctx.texture_buffer_bindings.at(info.descriptor_index));
48 } else {
49 return fmt::format("texture[{}]", ctx.texture_bindings.at(info.descriptor_index));
50 }
51}
52
53std::string Image(EmitContext& ctx, IR::TextureInstInfo info,
54 [[maybe_unused]] const IR::Value& index) {
55 // FIXME: indexed reads
56 if (info.type == TextureType::Buffer) {
57 return fmt::format("image[{}]", ctx.image_buffer_bindings.at(info.descriptor_index));
58 } else {
59 return fmt::format("image[{}]", ctx.image_bindings.at(info.descriptor_index));
60 }
61}
62
63std::string_view TextureType(IR::TextureInstInfo info) {
64 if (info.is_depth) {
65 switch (info.type) {
66 case TextureType::Color1D:
67 return "SHADOW1D";
68 case TextureType::ColorArray1D:
69 return "SHADOWARRAY1D";
70 case TextureType::Color2D:
71 return "SHADOW2D";
72 case TextureType::ColorArray2D:
73 return "SHADOWARRAY2D";
74 case TextureType::Color3D:
75 return "SHADOW3D";
76 case TextureType::ColorCube:
77 return "SHADOWCUBE";
78 case TextureType::ColorArrayCube:
79 return "SHADOWARRAYCUBE";
80 case TextureType::Buffer:
81 return "SHADOWBUFFER";
82 }
83 } else {
84 switch (info.type) {
85 case TextureType::Color1D:
86 return "1D";
87 case TextureType::ColorArray1D:
88 return "ARRAY1D";
89 case TextureType::Color2D:
90 return "2D";
91 case TextureType::ColorArray2D:
92 return "ARRAY2D";
93 case TextureType::Color3D:
94 return "3D";
95 case TextureType::ColorCube:
96 return "CUBE";
97 case TextureType::ColorArrayCube:
98 return "ARRAYCUBE";
99 case TextureType::Buffer:
100 return "BUFFER";
101 }
102 }
103 throw InvalidArgument("Invalid texture type {}", info.type.Value());
104}
105
106std::string Offset(EmitContext& ctx, const IR::Value& offset) {
107 if (offset.IsEmpty()) {
108 return "";
109 }
110 return fmt::format(",offset({})", Register{ctx.reg_alloc.Consume(offset)});
111}
112
113std::pair<ScopedRegister, ScopedRegister> AllocOffsetsRegs(EmitContext& ctx,
114 const IR::Value& offset2) {
115 if (offset2.IsEmpty()) {
116 return {};
117 } else {
118 return {ctx.reg_alloc, ctx.reg_alloc};
119 }
120}
121
122void SwizzleOffsets(EmitContext& ctx, Register off_x, Register off_y, const IR::Value& offset1,
123 const IR::Value& offset2) {
124 const Register offsets_a{ctx.reg_alloc.Consume(offset1)};
125 const Register offsets_b{ctx.reg_alloc.Consume(offset2)};
126 // Input swizzle: [XYXY] [XYXY]
127 // Output swizzle: [XXXX] [YYYY]
128 ctx.Add("MOV {}.x,{}.x;"
129 "MOV {}.y,{}.z;"
130 "MOV {}.z,{}.x;"
131 "MOV {}.w,{}.z;"
132 "MOV {}.x,{}.y;"
133 "MOV {}.y,{}.w;"
134 "MOV {}.z,{}.y;"
135 "MOV {}.w,{}.w;",
136 off_x, offsets_a, off_x, offsets_a, off_x, offsets_b, off_x, offsets_b, off_y,
137 offsets_a, off_y, offsets_a, off_y, offsets_b, off_y, offsets_b);
138}
139
140std::string GradOffset(const IR::Value& offset) {
141 if (offset.IsImmediate()) {
142 LOG_WARNING(Shader_GLASM, "Gradient offset is a scalar immediate");
143 return "";
144 }
145 IR::Inst* const vector{offset.InstRecursive()};
146 if (!vector->AreAllArgsImmediates()) {
147 LOG_WARNING(Shader_GLASM, "Gradient offset vector is not immediate");
148 return "";
149 }
150 switch (vector->NumArgs()) {
151 case 1:
152 return fmt::format(",({})", static_cast<s32>(vector->Arg(0).U32()));
153 case 2:
154 return fmt::format(",({},{})", static_cast<s32>(vector->Arg(0).U32()),
155 static_cast<s32>(vector->Arg(1).U32()));
156 default:
157 throw LogicError("Invalid number of gradient offsets {}", vector->NumArgs());
158 }
159}
160
161std::pair<std::string, ScopedRegister> Coord(EmitContext& ctx, const IR::Value& coord) {
162 if (coord.IsImmediate()) {
163 ScopedRegister scoped_reg(ctx.reg_alloc);
164 ctx.Add("MOV.U {}.x,{};", scoped_reg.reg, ScalarU32{ctx.reg_alloc.Consume(coord)});
165 return {fmt::to_string(scoped_reg.reg), std::move(scoped_reg)};
166 }
167 std::string coord_vec{fmt::to_string(Register{ctx.reg_alloc.Consume(coord)})};
168 if (coord.InstRecursive()->HasUses()) {
169 // Move non-dead coords to a separate register, although this should never happen because
170 // vectors are only assembled for immediate texture instructions
171 ctx.Add("MOV.F RC,{};", coord_vec);
172 coord_vec = "RC";
173 }
174 return {std::move(coord_vec), ScopedRegister{}};
175}
176
177void StoreSparse(EmitContext& ctx, IR::Inst* sparse_inst) {
178 if (!sparse_inst) {
179 return;
180 }
181 const Register sparse_ret{ctx.reg_alloc.Define(*sparse_inst)};
182 ctx.Add("MOV.S {},-1;"
183 "MOV.S {}(NONRESIDENT),0;",
184 sparse_ret, sparse_ret);
185}
186
187std::string_view FormatStorage(ImageFormat format) {
188 switch (format) {
189 case ImageFormat::Typeless:
190 return "U";
191 case ImageFormat::R8_UINT:
192 return "U8";
193 case ImageFormat::R8_SINT:
194 return "S8";
195 case ImageFormat::R16_UINT:
196 return "U16";
197 case ImageFormat::R16_SINT:
198 return "S16";
199 case ImageFormat::R32_UINT:
200 return "U32";
201 case ImageFormat::R32G32_UINT:
202 return "U32X2";
203 case ImageFormat::R32G32B32A32_UINT:
204 return "U32X4";
205 }
206 throw InvalidArgument("Invalid image format {}", format);
207}
208
209template <typename T>
210void ImageAtomic(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord, T value,
211 std::string_view op) {
212 const auto info{inst.Flags<IR::TextureInstInfo>()};
213 const std::string_view type{TextureType(info)};
214 const std::string image{Image(ctx, info, index)};
215 const Register ret{ctx.reg_alloc.Define(inst)};
216 ctx.Add("ATOMIM.{} {},{},{},{},{};", op, ret, value, coord, image, type);
217}
218
219IR::Inst* PrepareSparse(IR::Inst& inst) {
220 const auto sparse_inst{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
221 if (sparse_inst) {
222 sparse_inst->Invalidate();
223 }
224 return sparse_inst;
225}
226} // Anonymous namespace
227
228void EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
229 const IR::Value& coord, Register bias_lc, const IR::Value& offset) {
230 const auto info{inst.Flags<IR::TextureInstInfo>()};
231 const auto sparse_inst{PrepareSparse(inst)};
232 const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
233 const std::string_view lod_clamp_mod{info.has_lod_clamp ? ".LODCLAMP" : ""};
234 const std::string_view type{TextureType(info)};
235 const std::string texture{Texture(ctx, info, index)};
236 const std::string offset_vec{Offset(ctx, offset)};
237 const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
238 const Register ret{ctx.reg_alloc.Define(inst)};
239 if (info.has_bias) {
240 if (info.type == TextureType::ColorArrayCube) {
241 ctx.Add("TXB.F{}{} {},{},{},{},ARRAYCUBE{};", lod_clamp_mod, sparse_mod, ret, coord_vec,
242 bias_lc, texture, offset_vec);
243 } else {
244 if (info.has_lod_clamp) {
245 ctx.Add("MOV.F {}.w,{}.x;"
246 "TXB.F.LODCLAMP{} {},{},{}.y,{},{}{};",
247 coord_vec, bias_lc, sparse_mod, ret, coord_vec, bias_lc, texture, type,
248 offset_vec);
249 } else {
250 ctx.Add("MOV.F {}.w,{}.x;"
251 "TXB.F{} {},{},{},{}{};",
252 coord_vec, bias_lc, sparse_mod, ret, coord_vec, texture, type, offset_vec);
253 }
254 }
255 } else {
256 if (info.has_lod_clamp && info.type == TextureType::ColorArrayCube) {
257 ctx.Add("TEX.F.LODCLAMP{} {},{},{},{},ARRAYCUBE{};", sparse_mod, ret, coord_vec,
258 bias_lc, texture, offset_vec);
259 } else {
260 ctx.Add("TEX.F{}{} {},{},{},{}{};", lod_clamp_mod, sparse_mod, ret, coord_vec, texture,
261 type, offset_vec);
262 }
263 }
264 StoreSparse(ctx, sparse_inst);
265}
266
267void EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
268 const IR::Value& coord, ScalarF32 lod, const IR::Value& offset) {
269 const auto info{inst.Flags<IR::TextureInstInfo>()};
270 const auto sparse_inst{PrepareSparse(inst)};
271 const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
272 const std::string_view type{TextureType(info)};
273 const std::string texture{Texture(ctx, info, index)};
274 const std::string offset_vec{Offset(ctx, offset)};
275 const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
276 const Register ret{ctx.reg_alloc.Define(inst)};
277 if (info.type == TextureType::ColorArrayCube) {
278 ctx.Add("TXL.F{} {},{},{},{},ARRAYCUBE{};", sparse_mod, ret, coord_vec, lod, texture,
279 offset_vec);
280 } else {
281 ctx.Add("MOV.F {}.w,{};"
282 "TXL.F{} {},{},{},{}{};",
283 coord_vec, lod, sparse_mod, ret, coord_vec, texture, type, offset_vec);
284 }
285 StoreSparse(ctx, sparse_inst);
286}
287
288void EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
289 const IR::Value& coord, const IR::Value& dref,
290 const IR::Value& bias_lc, const IR::Value& offset) {
291 // Allocate early to avoid aliases
292 const auto info{inst.Flags<IR::TextureInstInfo>()};
293 ScopedRegister staging;
294 if (info.type == TextureType::ColorArrayCube) {
295 staging = ScopedRegister{ctx.reg_alloc};
296 }
297 const ScalarF32 dref_val{ctx.reg_alloc.Consume(dref)};
298 const Register bias_lc_vec{ctx.reg_alloc.Consume(bias_lc)};
299 const auto sparse_inst{PrepareSparse(inst)};
300 const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
301 const std::string_view type{TextureType(info)};
302 const std::string texture{Texture(ctx, info, index)};
303 const std::string offset_vec{Offset(ctx, offset)};
304 const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
305 const Register ret{ctx.reg_alloc.Define(inst)};
306 if (info.has_bias) {
307 if (info.has_lod_clamp) {
308 switch (info.type) {
309 case TextureType::Color1D:
310 case TextureType::ColorArray1D:
311 case TextureType::Color2D:
312 ctx.Add("MOV.F {}.z,{};"
313 "MOV.F {}.w,{}.x;"
314 "TXB.F.LODCLAMP{} {},{},{}.y,{},{}{};",
315 coord_vec, dref_val, coord_vec, bias_lc_vec, sparse_mod, ret, coord_vec,
316 bias_lc_vec, texture, type, offset_vec);
317 break;
318 case TextureType::ColorArray2D:
319 case TextureType::ColorCube:
320 ctx.Add("MOV.F {}.w,{};"
321 "TXB.F.LODCLAMP{} {},{},{},{},{}{};",
322 coord_vec, dref_val, sparse_mod, ret, coord_vec, bias_lc_vec, texture, type,
323 offset_vec);
324 break;
325 default:
326 throw NotImplementedException("Invalid type {} with bias and lod clamp",
327 info.type.Value());
328 }
329 } else {
330 switch (info.type) {
331 case TextureType::Color1D:
332 case TextureType::ColorArray1D:
333 case TextureType::Color2D:
334 ctx.Add("MOV.F {}.z,{};"
335 "MOV.F {}.w,{}.x;"
336 "TXB.F{} {},{},{},{}{};",
337 coord_vec, dref_val, coord_vec, bias_lc_vec, sparse_mod, ret, coord_vec,
338 texture, type, offset_vec);
339 break;
340 case TextureType::ColorArray2D:
341 case TextureType::ColorCube:
342 ctx.Add("MOV.F {}.w,{};"
343 "TXB.F{} {},{},{},{},{}{};",
344 coord_vec, dref_val, sparse_mod, ret, coord_vec, bias_lc_vec, texture, type,
345 offset_vec);
346 break;
347 case TextureType::ColorArrayCube:
348 ctx.Add("MOV.F {}.x,{};"
349 "MOV.F {}.y,{}.x;"
350 "TXB.F{} {},{},{},{},{}{};",
351 staging.reg, dref_val, staging.reg, bias_lc_vec, sparse_mod, ret, coord_vec,
352 staging.reg, texture, type, offset_vec);
353 break;
354 default:
355 throw NotImplementedException("Invalid type {}", info.type.Value());
356 }
357 }
358 } else {
359 if (info.has_lod_clamp) {
360 if (info.type != TextureType::ColorArrayCube) {
361 const bool w_swizzle{info.type == TextureType::ColorArray2D ||
362 info.type == TextureType::ColorCube};
363 const char dref_swizzle{w_swizzle ? 'w' : 'z'};
364 ctx.Add("MOV.F {}.{},{};"
365 "TEX.F.LODCLAMP{} {},{},{},{},{}{};",
366 coord_vec, dref_swizzle, dref_val, sparse_mod, ret, coord_vec, bias_lc_vec,
367 texture, type, offset_vec);
368 } else {
369 ctx.Add("MOV.F {}.x,{};"
370 "MOV.F {}.y,{};"
371 "TEX.F.LODCLAMP{} {},{},{},{},{}{};",
372 staging.reg, dref_val, staging.reg, bias_lc_vec, sparse_mod, ret, coord_vec,
373 staging.reg, texture, type, offset_vec);
374 }
375 } else {
376 if (info.type != TextureType::ColorArrayCube) {
377 const bool w_swizzle{info.type == TextureType::ColorArray2D ||
378 info.type == TextureType::ColorCube};
379 const char dref_swizzle{w_swizzle ? 'w' : 'z'};
380 ctx.Add("MOV.F {}.{},{};"
381 "TEX.F{} {},{},{},{}{};",
382 coord_vec, dref_swizzle, dref_val, sparse_mod, ret, coord_vec, texture,
383 type, offset_vec);
384 } else {
385 ctx.Add("TEX.F{} {},{},{},{},{}{};", sparse_mod, ret, coord_vec, dref_val, texture,
386 type, offset_vec);
387 }
388 }
389 }
390 StoreSparse(ctx, sparse_inst);
391}
392
393void EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
394 const IR::Value& coord, const IR::Value& dref,
395 const IR::Value& lod, const IR::Value& offset) {
396 // Allocate early to avoid aliases
397 const auto info{inst.Flags<IR::TextureInstInfo>()};
398 ScopedRegister staging;
399 if (info.type == TextureType::ColorArrayCube) {
400 staging = ScopedRegister{ctx.reg_alloc};
401 }
402 const ScalarF32 dref_val{ctx.reg_alloc.Consume(dref)};
403 const ScalarF32 lod_val{ctx.reg_alloc.Consume(lod)};
404 const auto sparse_inst{PrepareSparse(inst)};
405 const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
406 const std::string_view type{TextureType(info)};
407 const std::string texture{Texture(ctx, info, index)};
408 const std::string offset_vec{Offset(ctx, offset)};
409 const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
410 const Register ret{ctx.reg_alloc.Define(inst)};
411 switch (info.type) {
412 case TextureType::Color1D:
413 case TextureType::ColorArray1D:
414 case TextureType::Color2D:
415 ctx.Add("MOV.F {}.z,{};"
416 "MOV.F {}.w,{};"
417 "TXL.F{} {},{},{},{}{};",
418 coord_vec, dref_val, coord_vec, lod_val, sparse_mod, ret, coord_vec, texture, type,
419 offset_vec);
420 break;
421 case TextureType::ColorArray2D:
422 case TextureType::ColorCube:
423 ctx.Add("MOV.F {}.w,{};"
424 "TXL.F{} {},{},{},{},{}{};",
425 coord_vec, dref_val, sparse_mod, ret, coord_vec, lod_val, texture, type,
426 offset_vec);
427 break;
428 case TextureType::ColorArrayCube:
429 ctx.Add("MOV.F {}.x,{};"
430 "MOV.F {}.y,{};"
431 "TXL.F{} {},{},{},{},{}{};",
432 staging.reg, dref_val, staging.reg, lod_val, sparse_mod, ret, coord_vec,
433 staging.reg, texture, type, offset_vec);
434 break;
435 default:
436 throw NotImplementedException("Invalid type {}", info.type.Value());
437 }
438 StoreSparse(ctx, sparse_inst);
439}
440
441void EmitImageGather(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
442 const IR::Value& coord, const IR::Value& offset, const IR::Value& offset2) {
443 // Allocate offsets early so they don't overwrite any consumed register
444 const auto [off_x, off_y]{AllocOffsetsRegs(ctx, offset2)};
445 const auto info{inst.Flags<IR::TextureInstInfo>()};
446 const char comp{"xyzw"[info.gather_component]};
447 const auto sparse_inst{PrepareSparse(inst)};
448 const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
449 const std::string_view type{TextureType(info)};
450 const std::string texture{Texture(ctx, info, index)};
451 const Register coord_vec{ctx.reg_alloc.Consume(coord)};
452 const Register ret{ctx.reg_alloc.Define(inst)};
453 if (offset2.IsEmpty()) {
454 const std::string offset_vec{Offset(ctx, offset)};
455 ctx.Add("TXG.F{} {},{},{}.{},{}{};", sparse_mod, ret, coord_vec, texture, comp, type,
456 offset_vec);
457 } else {
458 SwizzleOffsets(ctx, off_x.reg, off_y.reg, offset, offset2);
459 ctx.Add("TXGO.F{} {},{},{},{},{}.{},{};", sparse_mod, ret, coord_vec, off_x.reg, off_y.reg,
460 texture, comp, type);
461 }
462 StoreSparse(ctx, sparse_inst);
463}
464
465void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
466 const IR::Value& coord, const IR::Value& offset, const IR::Value& offset2,
467 const IR::Value& dref) {
468 // FIXME: This instruction is not working as expected
469
470 // Allocate offsets early so they don't overwrite any consumed register
471 const auto [off_x, off_y]{AllocOffsetsRegs(ctx, offset2)};
472 const auto info{inst.Flags<IR::TextureInstInfo>()};
473 const auto sparse_inst{PrepareSparse(inst)};
474 const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
475 const std::string_view type{TextureType(info)};
476 const std::string texture{Texture(ctx, info, index)};
477 const Register coord_vec{ctx.reg_alloc.Consume(coord)};
478 const ScalarF32 dref_value{ctx.reg_alloc.Consume(dref)};
479 const Register ret{ctx.reg_alloc.Define(inst)};
480 std::string args;
481 switch (info.type) {
482 case TextureType::Color2D:
483 ctx.Add("MOV.F {}.z,{};", coord_vec, dref_value);
484 args = fmt::to_string(coord_vec);
485 break;
486 case TextureType::ColorArray2D:
487 case TextureType::ColorCube:
488 ctx.Add("MOV.F {}.w,{};", coord_vec, dref_value);
489 args = fmt::to_string(coord_vec);
490 break;
491 case TextureType::ColorArrayCube:
492 args = fmt::format("{},{}", coord_vec, dref_value);
493 break;
494 default:
495 throw NotImplementedException("Invalid type {}", info.type.Value());
496 }
497 if (offset2.IsEmpty()) {
498 const std::string offset_vec{Offset(ctx, offset)};
499 ctx.Add("TXG.F{} {},{},{},{}{};", sparse_mod, ret, args, texture, type, offset_vec);
500 } else {
501 SwizzleOffsets(ctx, off_x.reg, off_y.reg, offset, offset2);
502 ctx.Add("TXGO.F{} {},{},{},{},{},{};", sparse_mod, ret, args, off_x.reg, off_y.reg, texture,
503 type);
504 }
505 StoreSparse(ctx, sparse_inst);
506}
507
508void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
509 const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms) {
510 const auto info{inst.Flags<IR::TextureInstInfo>()};
511 const auto sparse_inst{PrepareSparse(inst)};
512 const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
513 const std::string_view type{TextureType(info)};
514 const std::string texture{Texture(ctx, info, index)};
515 const std::string offset_vec{Offset(ctx, offset)};
516 const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
517 const Register ret{ctx.reg_alloc.Define(inst)};
518 if (info.type == TextureType::Buffer) {
519 ctx.Add("TXF.F{} {},{},{},{}{};", sparse_mod, ret, coord_vec, texture, type, offset_vec);
520 } else if (ms.type != Type::Void) {
521 ctx.Add("MOV.S {}.w,{};"
522 "TXFMS.F{} {},{},{},{}{};",
523 coord_vec, ms, sparse_mod, ret, coord_vec, texture, type, offset_vec);
524 } else {
525 ctx.Add("MOV.S {}.w,{};"
526 "TXF.F{} {},{},{},{}{};",
527 coord_vec, lod, sparse_mod, ret, coord_vec, texture, type, offset_vec);
528 }
529 StoreSparse(ctx, sparse_inst);
530}
531
532void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
533 ScalarS32 lod) {
534 const auto info{inst.Flags<IR::TextureInstInfo>()};
535 const std::string texture{Texture(ctx, info, index)};
536 const std::string_view type{TextureType(info)};
537 ctx.Add("TXQ {},{},{},{};", inst, lod, texture, type);
538}
539
540void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord) {
541 const auto info{inst.Flags<IR::TextureInstInfo>()};
542 const std::string texture{Texture(ctx, info, index)};
543 const std::string_view type{TextureType(info)};
544 ctx.Add("LOD.F {},{},{},{};", inst, coord, texture, type);
545}
546
547void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
548 const IR::Value& coord, const IR::Value& derivatives,
549 const IR::Value& offset, const IR::Value& lod_clamp) {
550 const auto info{inst.Flags<IR::TextureInstInfo>()};
551 ScopedRegister dpdx, dpdy;
552 const bool multi_component{info.num_derivates > 1 || info.has_lod_clamp};
553 if (multi_component) {
554 // Allocate this early to avoid aliasing other registers
555 dpdx = ScopedRegister{ctx.reg_alloc};
556 dpdy = ScopedRegister{ctx.reg_alloc};
557 }
558 const auto sparse_inst{PrepareSparse(inst)};
559 const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
560 const std::string_view type{TextureType(info)};
561 const std::string texture{Texture(ctx, info, index)};
562 const std::string offset_vec{GradOffset(offset)};
563 const Register coord_vec{ctx.reg_alloc.Consume(coord)};
564 const Register derivatives_vec{ctx.reg_alloc.Consume(derivatives)};
565 const Register ret{ctx.reg_alloc.Define(inst)};
566 if (multi_component) {
567 ctx.Add("MOV.F {}.x,{}.x;"
568 "MOV.F {}.y,{}.z;"
569 "MOV.F {}.x,{}.y;"
570 "MOV.F {}.y,{}.w;",
571 dpdx.reg, derivatives_vec, dpdx.reg, derivatives_vec, dpdy.reg, derivatives_vec,
572 dpdy.reg, derivatives_vec);
573 if (info.has_lod_clamp) {
574 const ScalarF32 lod_clamp_value{ctx.reg_alloc.Consume(lod_clamp)};
575 ctx.Add("MOV.F {}.w,{};"
576 "TXD.F.LODCLAMP{} {},{},{},{},{},{}{};",
577 dpdy.reg, lod_clamp_value, sparse_mod, ret, coord_vec, dpdx.reg, dpdy.reg,
578 texture, type, offset_vec);
579 } else {
580 ctx.Add("TXD.F{} {},{},{},{},{},{}{};", sparse_mod, ret, coord_vec, dpdx.reg, dpdy.reg,
581 texture, type, offset_vec);
582 }
583 } else {
584 ctx.Add("TXD.F{} {},{},{}.x,{}.y,{},{}{};", sparse_mod, ret, coord_vec, derivatives_vec,
585 derivatives_vec, texture, type, offset_vec);
586 }
587 StoreSparse(ctx, sparse_inst);
588}
589
590void EmitImageRead(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord) {
591 const auto info{inst.Flags<IR::TextureInstInfo>()};
592 const auto sparse_inst{PrepareSparse(inst)};
593 const std::string_view format{FormatStorage(info.image_format)};
594 const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
595 const std::string_view type{TextureType(info)};
596 const std::string image{Image(ctx, info, index)};
597 const Register ret{ctx.reg_alloc.Define(inst)};
598 ctx.Add("LOADIM.{}{} {},{},{},{};", format, sparse_mod, ret, coord, image, type);
599 StoreSparse(ctx, sparse_inst);
600}
601
602void EmitImageWrite(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
603 Register color) {
604 const auto info{inst.Flags<IR::TextureInstInfo>()};
605 const std::string_view format{FormatStorage(info.image_format)};
606 const std::string_view type{TextureType(info)};
607 const std::string image{Image(ctx, info, index)};
608 ctx.Add("STOREIM.{} {},{},{},{};", format, image, color, coord, type);
609}
610
611void EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
612 ScalarU32 value) {
613 ImageAtomic(ctx, inst, index, coord, value, "ADD.U32");
614}
615
616void EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
617 ScalarS32 value) {
618 ImageAtomic(ctx, inst, index, coord, value, "MIN.S32");
619}
620
621void EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
622 ScalarU32 value) {
623 ImageAtomic(ctx, inst, index, coord, value, "MIN.U32");
624}
625
626void EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
627 ScalarS32 value) {
628 ImageAtomic(ctx, inst, index, coord, value, "MAX.S32");
629}
630
631void EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
632 ScalarU32 value) {
633 ImageAtomic(ctx, inst, index, coord, value, "MAX.U32");
634}
635
636void EmitImageAtomicInc32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
637 ScalarU32 value) {
638 ImageAtomic(ctx, inst, index, coord, value, "IWRAP.U32");
639}
640
641void EmitImageAtomicDec32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
642 ScalarU32 value) {
643 ImageAtomic(ctx, inst, index, coord, value, "DWRAP.U32");
644}
645
646void EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
647 ScalarU32 value) {
648 ImageAtomic(ctx, inst, index, coord, value, "AND.U32");
649}
650
651void EmitImageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
652 ScalarU32 value) {
653 ImageAtomic(ctx, inst, index, coord, value, "OR.U32");
654}
655
656void EmitImageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
657 ScalarU32 value) {
658 ImageAtomic(ctx, inst, index, coord, value, "XOR.U32");
659}
660
661void EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
662 Register coord, ScalarU32 value) {
663 ImageAtomic(ctx, inst, index, coord, value, "EXCH.U32");
664}
665
666void EmitBindlessImageSampleImplicitLod(EmitContext&) {
667 throw LogicError("Unreachable instruction");
668}
669
670void EmitBindlessImageSampleExplicitLod(EmitContext&) {
671 throw LogicError("Unreachable instruction");
672}
673
674void EmitBindlessImageSampleDrefImplicitLod(EmitContext&) {
675 throw LogicError("Unreachable instruction");
676}
677
678void EmitBindlessImageSampleDrefExplicitLod(EmitContext&) {
679 throw LogicError("Unreachable instruction");
680}
681
682void EmitBindlessImageGather(EmitContext&) {
683 throw LogicError("Unreachable instruction");
684}
685
686void EmitBindlessImageGatherDref(EmitContext&) {
687 throw LogicError("Unreachable instruction");
688}
689
690void EmitBindlessImageFetch(EmitContext&) {
691 throw LogicError("Unreachable instruction");
692}
693
694void EmitBindlessImageQueryDimensions(EmitContext&) {
695 throw LogicError("Unreachable instruction");
696}
697
698void EmitBindlessImageQueryLod(EmitContext&) {
699 throw LogicError("Unreachable instruction");
700}
701
702void EmitBindlessImageGradient(EmitContext&) {
703 throw LogicError("Unreachable instruction");
704}
705
706void EmitBindlessImageRead(EmitContext&) {
707 throw LogicError("Unreachable instruction");
708}
709
710void EmitBindlessImageWrite(EmitContext&) {
711 throw LogicError("Unreachable instruction");
712}
713
714void EmitBoundImageSampleImplicitLod(EmitContext&) {
715 throw LogicError("Unreachable instruction");
716}
717
718void EmitBoundImageSampleExplicitLod(EmitContext&) {
719 throw LogicError("Unreachable instruction");
720}
721
722void EmitBoundImageSampleDrefImplicitLod(EmitContext&) {
723 throw LogicError("Unreachable instruction");
724}
725
726void EmitBoundImageSampleDrefExplicitLod(EmitContext&) {
727 throw LogicError("Unreachable instruction");
728}
729
730void EmitBoundImageGather(EmitContext&) {
731 throw LogicError("Unreachable instruction");
732}
733
734void EmitBoundImageGatherDref(EmitContext&) {
735 throw LogicError("Unreachable instruction");
736}
737
738void EmitBoundImageFetch(EmitContext&) {
739 throw LogicError("Unreachable instruction");
740}
741
742void EmitBoundImageQueryDimensions(EmitContext&) {
743 throw LogicError("Unreachable instruction");
744}
745
746void EmitBoundImageQueryLod(EmitContext&) {
747 throw LogicError("Unreachable instruction");
748}
749
750void EmitBoundImageGradient(EmitContext&) {
751 throw LogicError("Unreachable instruction");
752}
753
754void EmitBoundImageRead(EmitContext&) {
755 throw LogicError("Unreachable instruction");
756}
757
758void EmitBoundImageWrite(EmitContext&) {
759 throw LogicError("Unreachable instruction");
760}
761
762void EmitBindlessImageAtomicIAdd32(EmitContext&) {
763 throw LogicError("Unreachable instruction");
764}
765
766void EmitBindlessImageAtomicSMin32(EmitContext&) {
767 throw LogicError("Unreachable instruction");
768}
769
770void EmitBindlessImageAtomicUMin32(EmitContext&) {
771 throw LogicError("Unreachable instruction");
772}
773
774void EmitBindlessImageAtomicSMax32(EmitContext&) {
775 throw LogicError("Unreachable instruction");
776}
777
778void EmitBindlessImageAtomicUMax32(EmitContext&) {
779 throw LogicError("Unreachable instruction");
780}
781
782void EmitBindlessImageAtomicInc32(EmitContext&) {
783 throw LogicError("Unreachable instruction");
784}
785
786void EmitBindlessImageAtomicDec32(EmitContext&) {
787 throw LogicError("Unreachable instruction");
788}
789
790void EmitBindlessImageAtomicAnd32(EmitContext&) {
791 throw LogicError("Unreachable instruction");
792}
793
794void EmitBindlessImageAtomicOr32(EmitContext&) {
795 throw LogicError("Unreachable instruction");
796}
797
798void EmitBindlessImageAtomicXor32(EmitContext&) {
799 throw LogicError("Unreachable instruction");
800}
801
802void EmitBindlessImageAtomicExchange32(EmitContext&) {
803 throw LogicError("Unreachable instruction");
804}
805
806void EmitBoundImageAtomicIAdd32(EmitContext&) {
807 throw LogicError("Unreachable instruction");
808}
809
810void EmitBoundImageAtomicSMin32(EmitContext&) {
811 throw LogicError("Unreachable instruction");
812}
813
814void EmitBoundImageAtomicUMin32(EmitContext&) {
815 throw LogicError("Unreachable instruction");
816}
817
818void EmitBoundImageAtomicSMax32(EmitContext&) {
819 throw LogicError("Unreachable instruction");
820}
821
822void EmitBoundImageAtomicUMax32(EmitContext&) {
823 throw LogicError("Unreachable instruction");
824}
825
826void EmitBoundImageAtomicInc32(EmitContext&) {
827 throw LogicError("Unreachable instruction");
828}
829
830void EmitBoundImageAtomicDec32(EmitContext&) {
831 throw LogicError("Unreachable instruction");
832}
833
834void EmitBoundImageAtomicAnd32(EmitContext&) {
835 throw LogicError("Unreachable instruction");
836}
837
838void EmitBoundImageAtomicOr32(EmitContext&) {
839 throw LogicError("Unreachable instruction");
840}
841
842void EmitBoundImageAtomicXor32(EmitContext&) {
843 throw LogicError("Unreachable instruction");
844}
845
846void EmitBoundImageAtomicExchange32(EmitContext&) {
847 throw LogicError("Unreachable instruction");
848}
849
850} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
new file mode 100644
index 000000000..12afda43b
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
@@ -0,0 +1,625 @@
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 "common/common_types.h"
8#include "shader_recompiler/backend/glasm/reg_alloc.h"
9
10namespace Shader::IR {
11enum class Attribute : u64;
12enum class Patch : u64;
13class Inst;
14class Value;
15} // namespace Shader::IR
16
17namespace Shader::Backend::GLASM {
18
19class EmitContext;
20
21// Microinstruction emitters
22void EmitPhi(EmitContext& ctx, IR::Inst& inst);
23void EmitVoid(EmitContext& ctx);
24void EmitIdentity(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
25void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
26void EmitReference(EmitContext&, const IR::Value& value);
27void EmitPhiMove(EmitContext& ctx, const IR::Value& phi, const IR::Value& value);
28void EmitJoin(EmitContext& ctx);
29void EmitDemoteToHelperInvocation(EmitContext& ctx);
30void EmitBarrier(EmitContext& ctx);
31void EmitWorkgroupMemoryBarrier(EmitContext& ctx);
32void EmitDeviceMemoryBarrier(EmitContext& ctx);
33void EmitPrologue(EmitContext& ctx);
34void EmitEpilogue(EmitContext& ctx);
35void EmitEmitVertex(EmitContext& ctx, ScalarS32 stream);
36void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream);
37void EmitGetRegister(EmitContext& ctx);
38void EmitSetRegister(EmitContext& ctx);
39void EmitGetPred(EmitContext& ctx);
40void EmitSetPred(EmitContext& ctx);
41void EmitSetGotoVariable(EmitContext& ctx);
42void EmitGetGotoVariable(EmitContext& ctx);
43void EmitSetIndirectBranchVariable(EmitContext& ctx);
44void EmitGetIndirectBranchVariable(EmitContext& ctx);
45void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
46void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
47void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
48void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
49void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
50void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
51void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
52void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32 vertex);
53void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, ScalarF32 value, ScalarU32 vertex);
54void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, ScalarS32 offset, ScalarU32 vertex);
55void EmitSetAttributeIndexed(EmitContext& ctx, ScalarU32 offset, ScalarF32 value, ScalarU32 vertex);
56void EmitGetPatch(EmitContext& ctx, IR::Inst& inst, IR::Patch patch);
57void EmitSetPatch(EmitContext& ctx, IR::Patch patch, ScalarF32 value);
58void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, ScalarF32 value);
59void EmitSetSampleMask(EmitContext& ctx, ScalarS32 value);
60void EmitSetFragDepth(EmitContext& ctx, ScalarF32 value);
61void EmitGetZFlag(EmitContext& ctx);
62void EmitGetSFlag(EmitContext& ctx);
63void EmitGetCFlag(EmitContext& ctx);
64void EmitGetOFlag(EmitContext& ctx);
65void EmitSetZFlag(EmitContext& ctx);
66void EmitSetSFlag(EmitContext& ctx);
67void EmitSetCFlag(EmitContext& ctx);
68void EmitSetOFlag(EmitContext& ctx);
69void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst);
70void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst);
71void EmitInvocationId(EmitContext& ctx, IR::Inst& inst);
72void EmitSampleId(EmitContext& ctx, IR::Inst& inst);
73void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst);
74void EmitYDirection(EmitContext& ctx, IR::Inst& inst);
75void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset);
76void EmitWriteLocal(EmitContext& ctx, ScalarU32 word_offset, ScalarU32 value);
77void EmitUndefU1(EmitContext& ctx, IR::Inst& inst);
78void EmitUndefU8(EmitContext& ctx, IR::Inst& inst);
79void EmitUndefU16(EmitContext& ctx, IR::Inst& inst);
80void EmitUndefU32(EmitContext& ctx, IR::Inst& inst);
81void EmitUndefU64(EmitContext& ctx, IR::Inst& inst);
82void EmitLoadGlobalU8(EmitContext& ctx, IR::Inst& inst, Register address);
83void EmitLoadGlobalS8(EmitContext& ctx, IR::Inst& inst, Register address);
84void EmitLoadGlobalU16(EmitContext& ctx, IR::Inst& inst, Register address);
85void EmitLoadGlobalS16(EmitContext& ctx, IR::Inst& inst, Register address);
86void EmitLoadGlobal32(EmitContext& ctx, IR::Inst& inst, Register address);
87void EmitLoadGlobal64(EmitContext& ctx, IR::Inst& inst, Register address);
88void EmitLoadGlobal128(EmitContext& ctx, IR::Inst& inst, Register address);
89void EmitWriteGlobalU8(EmitContext& ctx, Register address, Register value);
90void EmitWriteGlobalS8(EmitContext& ctx, Register address, Register value);
91void EmitWriteGlobalU16(EmitContext& ctx, Register address, Register value);
92void EmitWriteGlobalS16(EmitContext& ctx, Register address, Register value);
93void EmitWriteGlobal32(EmitContext& ctx, Register address, ScalarU32 value);
94void EmitWriteGlobal64(EmitContext& ctx, Register address, Register value);
95void EmitWriteGlobal128(EmitContext& ctx, Register address, Register value);
96void EmitLoadStorageU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
97 ScalarU32 offset);
98void EmitLoadStorageS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
99 ScalarU32 offset);
100void EmitLoadStorageU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
101 ScalarU32 offset);
102void EmitLoadStorageS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
103 ScalarU32 offset);
104void EmitLoadStorage32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
105 ScalarU32 offset);
106void EmitLoadStorage64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
107 ScalarU32 offset);
108void EmitLoadStorage128(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
109 ScalarU32 offset);
110void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
111 ScalarU32 value);
112void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
113 ScalarS32 value);
114void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
115 ScalarU32 value);
116void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
117 ScalarS32 value);
118void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
119 ScalarU32 value);
120void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
121 Register value);
122void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
123 Register value);
124void EmitLoadSharedU8(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset);
125void EmitLoadSharedS8(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset);
126void EmitLoadSharedU16(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset);
127void EmitLoadSharedS16(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset);
128void EmitLoadSharedU32(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset);
129void EmitLoadSharedU64(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset);
130void EmitLoadSharedU128(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset);
131void EmitWriteSharedU8(EmitContext& ctx, ScalarU32 offset, ScalarU32 value);
132void EmitWriteSharedU16(EmitContext& ctx, ScalarU32 offset, ScalarU32 value);
133void EmitWriteSharedU32(EmitContext& ctx, ScalarU32 offset, ScalarU32 value);
134void EmitWriteSharedU64(EmitContext& ctx, ScalarU32 offset, Register value);
135void EmitWriteSharedU128(EmitContext& ctx, ScalarU32 offset, Register value);
136void EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
137 const IR::Value& e2);
138void EmitCompositeConstructU32x3(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
139 const IR::Value& e2, const IR::Value& e3);
140void EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
141 const IR::Value& e2, const IR::Value& e3, const IR::Value& e4);
142void EmitCompositeExtractU32x2(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index);
143void EmitCompositeExtractU32x3(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index);
144void EmitCompositeExtractU32x4(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index);
145void EmitCompositeInsertU32x2(EmitContext& ctx, Register composite, ScalarU32 object, u32 index);
146void EmitCompositeInsertU32x3(EmitContext& ctx, Register composite, ScalarU32 object, u32 index);
147void EmitCompositeInsertU32x4(EmitContext& ctx, Register composite, ScalarU32 object, u32 index);
148void EmitCompositeConstructF16x2(EmitContext& ctx, Register e1, Register e2);
149void EmitCompositeConstructF16x3(EmitContext& ctx, Register e1, Register e2, Register e3);
150void EmitCompositeConstructF16x4(EmitContext& ctx, Register e1, Register e2, Register e3,
151 Register e4);
152void EmitCompositeExtractF16x2(EmitContext& ctx, Register composite, u32 index);
153void EmitCompositeExtractF16x3(EmitContext& ctx, Register composite, u32 index);
154void EmitCompositeExtractF16x4(EmitContext& ctx, Register composite, u32 index);
155void EmitCompositeInsertF16x2(EmitContext& ctx, Register composite, Register object, u32 index);
156void EmitCompositeInsertF16x3(EmitContext& ctx, Register composite, Register object, u32 index);
157void EmitCompositeInsertF16x4(EmitContext& ctx, Register composite, Register object, u32 index);
158void EmitCompositeConstructF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
159 const IR::Value& e2);
160void EmitCompositeConstructF32x3(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
161 const IR::Value& e2, const IR::Value& e3);
162void EmitCompositeConstructF32x4(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
163 const IR::Value& e2, const IR::Value& e3, const IR::Value& e4);
164void EmitCompositeExtractF32x2(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index);
165void EmitCompositeExtractF32x3(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index);
166void EmitCompositeExtractF32x4(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index);
167void EmitCompositeInsertF32x2(EmitContext& ctx, IR::Inst& inst, Register composite,
168 ScalarF32 object, u32 index);
169void EmitCompositeInsertF32x3(EmitContext& ctx, IR::Inst& inst, Register composite,
170 ScalarF32 object, u32 index);
171void EmitCompositeInsertF32x4(EmitContext& ctx, IR::Inst& inst, Register composite,
172 ScalarF32 object, u32 index);
173void EmitCompositeConstructF64x2(EmitContext& ctx);
174void EmitCompositeConstructF64x3(EmitContext& ctx);
175void EmitCompositeConstructF64x4(EmitContext& ctx);
176void EmitCompositeExtractF64x2(EmitContext& ctx);
177void EmitCompositeExtractF64x3(EmitContext& ctx);
178void EmitCompositeExtractF64x4(EmitContext& ctx);
179void EmitCompositeInsertF64x2(EmitContext& ctx, Register composite, Register object, u32 index);
180void EmitCompositeInsertF64x3(EmitContext& ctx, Register composite, Register object, u32 index);
181void EmitCompositeInsertF64x4(EmitContext& ctx, Register composite, Register object, u32 index);
182void EmitSelectU1(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, ScalarS32 true_value,
183 ScalarS32 false_value);
184void EmitSelectU8(EmitContext& ctx, ScalarS32 cond, ScalarS32 true_value, ScalarS32 false_value);
185void EmitSelectU16(EmitContext& ctx, ScalarS32 cond, ScalarS32 true_value, ScalarS32 false_value);
186void EmitSelectU32(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, ScalarS32 true_value,
187 ScalarS32 false_value);
188void EmitSelectU64(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, Register true_value,
189 Register false_value);
190void EmitSelectF16(EmitContext& ctx, ScalarS32 cond, Register true_value, Register false_value);
191void EmitSelectF32(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, ScalarS32 true_value,
192 ScalarS32 false_value);
193void EmitSelectF64(EmitContext& ctx, ScalarS32 cond, Register true_value, Register false_value);
194void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
195void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
196void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
197void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
198void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
199void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
200void EmitPackUint2x32(EmitContext& ctx, IR::Inst& inst, Register value);
201void EmitUnpackUint2x32(EmitContext& ctx, IR::Inst& inst, Register value);
202void EmitPackFloat2x16(EmitContext& ctx, Register value);
203void EmitUnpackFloat2x16(EmitContext& ctx, Register value);
204void EmitPackHalf2x16(EmitContext& ctx, IR::Inst& inst, Register value);
205void EmitUnpackHalf2x16(EmitContext& ctx, IR::Inst& inst, Register value);
206void EmitPackDouble2x32(EmitContext& ctx, IR::Inst& inst, Register value);
207void EmitUnpackDouble2x32(EmitContext& ctx, IR::Inst& inst, Register value);
208void EmitGetZeroFromOp(EmitContext& ctx);
209void EmitGetSignFromOp(EmitContext& ctx);
210void EmitGetCarryFromOp(EmitContext& ctx);
211void EmitGetOverflowFromOp(EmitContext& ctx);
212void EmitGetSparseFromOp(EmitContext& ctx);
213void EmitGetInBoundsFromOp(EmitContext& ctx);
214void EmitFPAbs16(EmitContext& ctx, IR::Inst& inst, Register value);
215void EmitFPAbs32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
216void EmitFPAbs64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
217void EmitFPAdd16(EmitContext& ctx, IR::Inst& inst, Register a, Register b);
218void EmitFPAdd32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b);
219void EmitFPAdd64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b);
220void EmitFPFma16(EmitContext& ctx, IR::Inst& inst, Register a, Register b, Register c);
221void EmitFPFma32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b, ScalarF32 c);
222void EmitFPFma64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b, ScalarF64 c);
223void EmitFPMax32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b);
224void EmitFPMax64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b);
225void EmitFPMin32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b);
226void EmitFPMin64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b);
227void EmitFPMul16(EmitContext& ctx, IR::Inst& inst, Register a, Register b);
228void EmitFPMul32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b);
229void EmitFPMul64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b);
230void EmitFPNeg16(EmitContext& ctx, Register value);
231void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, ScalarRegister value);
232void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, Register value);
233void EmitFPSin(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
234void EmitFPCos(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
235void EmitFPExp2(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
236void EmitFPLog2(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
237void EmitFPRecip32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
238void EmitFPRecip64(EmitContext& ctx, Register value);
239void EmitFPRecipSqrt32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
240void EmitFPRecipSqrt64(EmitContext& ctx, Register value);
241void EmitFPSqrt(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
242void EmitFPSaturate16(EmitContext& ctx, Register value);
243void EmitFPSaturate32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
244void EmitFPSaturate64(EmitContext& ctx, Register value);
245void EmitFPClamp16(EmitContext& ctx, Register value, Register min_value, Register max_value);
246void EmitFPClamp32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value, ScalarF32 min_value,
247 ScalarF32 max_value);
248void EmitFPClamp64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value, ScalarF64 min_value,
249 ScalarF64 max_value);
250void EmitFPRoundEven16(EmitContext& ctx, Register value);
251void EmitFPRoundEven32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
252void EmitFPRoundEven64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
253void EmitFPFloor16(EmitContext& ctx, Register value);
254void EmitFPFloor32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
255void EmitFPFloor64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
256void EmitFPCeil16(EmitContext& ctx, Register value);
257void EmitFPCeil32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
258void EmitFPCeil64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
259void EmitFPTrunc16(EmitContext& ctx, Register value);
260void EmitFPTrunc32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
261void EmitFPTrunc64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
262void EmitFPOrdEqual16(EmitContext& ctx, Register lhs, Register rhs);
263void EmitFPOrdEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
264void EmitFPOrdEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
265void EmitFPUnordEqual16(EmitContext& ctx, Register lhs, Register rhs);
266void EmitFPUnordEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
267void EmitFPUnordEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
268void EmitFPOrdNotEqual16(EmitContext& ctx, Register lhs, Register rhs);
269void EmitFPOrdNotEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
270void EmitFPOrdNotEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
271void EmitFPUnordNotEqual16(EmitContext& ctx, Register lhs, Register rhs);
272void EmitFPUnordNotEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
273void EmitFPUnordNotEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
274void EmitFPOrdLessThan16(EmitContext& ctx, Register lhs, Register rhs);
275void EmitFPOrdLessThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
276void EmitFPOrdLessThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
277void EmitFPUnordLessThan16(EmitContext& ctx, Register lhs, Register rhs);
278void EmitFPUnordLessThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
279void EmitFPUnordLessThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
280void EmitFPOrdGreaterThan16(EmitContext& ctx, Register lhs, Register rhs);
281void EmitFPOrdGreaterThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
282void EmitFPOrdGreaterThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
283void EmitFPUnordGreaterThan16(EmitContext& ctx, Register lhs, Register rhs);
284void EmitFPUnordGreaterThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
285void EmitFPUnordGreaterThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
286void EmitFPOrdLessThanEqual16(EmitContext& ctx, Register lhs, Register rhs);
287void EmitFPOrdLessThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
288void EmitFPOrdLessThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
289void EmitFPUnordLessThanEqual16(EmitContext& ctx, Register lhs, Register rhs);
290void EmitFPUnordLessThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
291void EmitFPUnordLessThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
292void EmitFPOrdGreaterThanEqual16(EmitContext& ctx, Register lhs, Register rhs);
293void EmitFPOrdGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
294void EmitFPOrdGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
295void EmitFPUnordGreaterThanEqual16(EmitContext& ctx, Register lhs, Register rhs);
296void EmitFPUnordGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
297void EmitFPUnordGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
298void EmitFPIsNan16(EmitContext& ctx, Register value);
299void EmitFPIsNan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
300void EmitFPIsNan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
301void EmitIAdd32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
302void EmitIAdd64(EmitContext& ctx, IR::Inst& inst, Register a, Register b);
303void EmitISub32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
304void EmitISub64(EmitContext& ctx, IR::Inst& inst, Register a, Register b);
305void EmitIMul32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
306void EmitINeg32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
307void EmitINeg64(EmitContext& ctx, IR::Inst& inst, Register value);
308void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
309void EmitShiftLeftLogical32(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 shift);
310void EmitShiftLeftLogical64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base, ScalarU32 shift);
311void EmitShiftRightLogical32(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 shift);
312void EmitShiftRightLogical64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base,
313 ScalarU32 shift);
314void EmitShiftRightArithmetic32(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 shift);
315void EmitShiftRightArithmetic64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base,
316 ScalarS32 shift);
317void EmitBitwiseAnd32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
318void EmitBitwiseOr32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
319void EmitBitwiseXor32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
320void EmitBitFieldInsert(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 insert,
321 ScalarS32 offset, ScalarS32 count);
322void EmitBitFieldSExtract(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 offset,
323 ScalarS32 count);
324void EmitBitFieldUExtract(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 offset,
325 ScalarU32 count);
326void EmitBitReverse32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
327void EmitBitCount32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
328void EmitBitwiseNot32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
329void EmitFindSMsb32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
330void EmitFindUMsb32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value);
331void EmitSMin32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
332void EmitUMin32(EmitContext& ctx, IR::Inst& inst, ScalarU32 a, ScalarU32 b);
333void EmitSMax32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
334void EmitUMax32(EmitContext& ctx, IR::Inst& inst, ScalarU32 a, ScalarU32 b);
335void EmitSClamp32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value, ScalarS32 min, ScalarS32 max);
336void EmitUClamp32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 min, ScalarU32 max);
337void EmitSLessThan(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs);
338void EmitULessThan(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs);
339void EmitIEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs);
340void EmitSLessThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs);
341void EmitULessThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs);
342void EmitSGreaterThan(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs);
343void EmitUGreaterThan(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs);
344void EmitINotEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs);
345void EmitSGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs);
346void EmitUGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs);
347void EmitSharedAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
348 ScalarU32 value);
349void EmitSharedAtomicSMin32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
350 ScalarS32 value);
351void EmitSharedAtomicUMin32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
352 ScalarU32 value);
353void EmitSharedAtomicSMax32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
354 ScalarS32 value);
355void EmitSharedAtomicUMax32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
356 ScalarU32 value);
357void EmitSharedAtomicInc32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
358 ScalarU32 value);
359void EmitSharedAtomicDec32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
360 ScalarU32 value);
361void EmitSharedAtomicAnd32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
362 ScalarU32 value);
363void EmitSharedAtomicOr32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
364 ScalarU32 value);
365void EmitSharedAtomicXor32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
366 ScalarU32 value);
367void EmitSharedAtomicExchange32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
368 ScalarU32 value);
369void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
370 Register value);
371void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
372 ScalarU32 offset, ScalarU32 value);
373void EmitStorageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
374 ScalarU32 offset, ScalarS32 value);
375void EmitStorageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
376 ScalarU32 offset, ScalarU32 value);
377void EmitStorageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
378 ScalarU32 offset, ScalarS32 value);
379void EmitStorageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
380 ScalarU32 offset, ScalarU32 value);
381void EmitStorageAtomicInc32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
382 ScalarU32 offset, ScalarU32 value);
383void EmitStorageAtomicDec32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
384 ScalarU32 offset, ScalarU32 value);
385void EmitStorageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
386 ScalarU32 offset, ScalarU32 value);
387void EmitStorageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
388 ScalarU32 offset, ScalarU32 value);
389void EmitStorageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
390 ScalarU32 offset, ScalarU32 value);
391void EmitStorageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
392 ScalarU32 offset, ScalarU32 value);
393void EmitStorageAtomicIAdd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
394 ScalarU32 offset, Register value);
395void EmitStorageAtomicSMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
396 ScalarU32 offset, Register value);
397void EmitStorageAtomicUMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
398 ScalarU32 offset, Register value);
399void EmitStorageAtomicSMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
400 ScalarU32 offset, Register value);
401void EmitStorageAtomicUMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
402 ScalarU32 offset, Register value);
403void EmitStorageAtomicAnd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
404 ScalarU32 offset, Register value);
405void EmitStorageAtomicOr64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
406 ScalarU32 offset, Register value);
407void EmitStorageAtomicXor64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
408 ScalarU32 offset, Register value);
409void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
410 ScalarU32 offset, Register value);
411void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
412 ScalarU32 offset, ScalarF32 value);
413void EmitStorageAtomicAddF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
414 ScalarU32 offset, Register value);
415void EmitStorageAtomicAddF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
416 ScalarU32 offset, Register value);
417void EmitStorageAtomicMinF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
418 ScalarU32 offset, Register value);
419void EmitStorageAtomicMinF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
420 ScalarU32 offset, Register value);
421void EmitStorageAtomicMaxF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
422 ScalarU32 offset, Register value);
423void EmitStorageAtomicMaxF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
424 ScalarU32 offset, Register value);
425void EmitGlobalAtomicIAdd32(EmitContext& ctx);
426void EmitGlobalAtomicSMin32(EmitContext& ctx);
427void EmitGlobalAtomicUMin32(EmitContext& ctx);
428void EmitGlobalAtomicSMax32(EmitContext& ctx);
429void EmitGlobalAtomicUMax32(EmitContext& ctx);
430void EmitGlobalAtomicInc32(EmitContext& ctx);
431void EmitGlobalAtomicDec32(EmitContext& ctx);
432void EmitGlobalAtomicAnd32(EmitContext& ctx);
433void EmitGlobalAtomicOr32(EmitContext& ctx);
434void EmitGlobalAtomicXor32(EmitContext& ctx);
435void EmitGlobalAtomicExchange32(EmitContext& ctx);
436void EmitGlobalAtomicIAdd64(EmitContext& ctx);
437void EmitGlobalAtomicSMin64(EmitContext& ctx);
438void EmitGlobalAtomicUMin64(EmitContext& ctx);
439void EmitGlobalAtomicSMax64(EmitContext& ctx);
440void EmitGlobalAtomicUMax64(EmitContext& ctx);
441void EmitGlobalAtomicInc64(EmitContext& ctx);
442void EmitGlobalAtomicDec64(EmitContext& ctx);
443void EmitGlobalAtomicAnd64(EmitContext& ctx);
444void EmitGlobalAtomicOr64(EmitContext& ctx);
445void EmitGlobalAtomicXor64(EmitContext& ctx);
446void EmitGlobalAtomicExchange64(EmitContext& ctx);
447void EmitGlobalAtomicAddF32(EmitContext& ctx);
448void EmitGlobalAtomicAddF16x2(EmitContext& ctx);
449void EmitGlobalAtomicAddF32x2(EmitContext& ctx);
450void EmitGlobalAtomicMinF16x2(EmitContext& ctx);
451void EmitGlobalAtomicMinF32x2(EmitContext& ctx);
452void EmitGlobalAtomicMaxF16x2(EmitContext& ctx);
453void EmitGlobalAtomicMaxF32x2(EmitContext& ctx);
454void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
455void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
456void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
457void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
458void EmitConvertS16F16(EmitContext& ctx, IR::Inst& inst, Register value);
459void EmitConvertS16F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
460void EmitConvertS16F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
461void EmitConvertS32F16(EmitContext& ctx, IR::Inst& inst, Register value);
462void EmitConvertS32F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
463void EmitConvertS32F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
464void EmitConvertS64F16(EmitContext& ctx, IR::Inst& inst, Register value);
465void EmitConvertS64F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
466void EmitConvertS64F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
467void EmitConvertU16F16(EmitContext& ctx, IR::Inst& inst, Register value);
468void EmitConvertU16F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
469void EmitConvertU16F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
470void EmitConvertU32F16(EmitContext& ctx, IR::Inst& inst, Register value);
471void EmitConvertU32F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
472void EmitConvertU32F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
473void EmitConvertU64F16(EmitContext& ctx, IR::Inst& inst, Register value);
474void EmitConvertU64F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
475void EmitConvertU64F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
476void EmitConvertU64U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value);
477void EmitConvertU32U64(EmitContext& ctx, IR::Inst& inst, Register value);
478void EmitConvertF16F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
479void EmitConvertF32F16(EmitContext& ctx, IR::Inst& inst, Register value);
480void EmitConvertF32F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
481void EmitConvertF64F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
482void EmitConvertF16S8(EmitContext& ctx, IR::Inst& inst, Register value);
483void EmitConvertF16S16(EmitContext& ctx, IR::Inst& inst, Register value);
484void EmitConvertF16S32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
485void EmitConvertF16S64(EmitContext& ctx, IR::Inst& inst, Register value);
486void EmitConvertF16U8(EmitContext& ctx, IR::Inst& inst, Register value);
487void EmitConvertF16U16(EmitContext& ctx, IR::Inst& inst, Register value);
488void EmitConvertF16U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value);
489void EmitConvertF16U64(EmitContext& ctx, IR::Inst& inst, Register value);
490void EmitConvertF32S8(EmitContext& ctx, IR::Inst& inst, Register value);
491void EmitConvertF32S16(EmitContext& ctx, IR::Inst& inst, Register value);
492void EmitConvertF32S32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
493void EmitConvertF32S64(EmitContext& ctx, IR::Inst& inst, Register value);
494void EmitConvertF32U8(EmitContext& ctx, IR::Inst& inst, Register value);
495void EmitConvertF32U16(EmitContext& ctx, IR::Inst& inst, Register value);
496void EmitConvertF32U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value);
497void EmitConvertF32U64(EmitContext& ctx, IR::Inst& inst, Register value);
498void EmitConvertF64S8(EmitContext& ctx, IR::Inst& inst, Register value);
499void EmitConvertF64S16(EmitContext& ctx, IR::Inst& inst, Register value);
500void EmitConvertF64S32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
501void EmitConvertF64S64(EmitContext& ctx, IR::Inst& inst, Register value);
502void EmitConvertF64U8(EmitContext& ctx, IR::Inst& inst, Register value);
503void EmitConvertF64U16(EmitContext& ctx, IR::Inst& inst, Register value);
504void EmitConvertF64U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value);
505void EmitConvertF64U64(EmitContext& ctx, IR::Inst& inst, Register value);
506void EmitBindlessImageSampleImplicitLod(EmitContext&);
507void EmitBindlessImageSampleExplicitLod(EmitContext&);
508void EmitBindlessImageSampleDrefImplicitLod(EmitContext&);
509void EmitBindlessImageSampleDrefExplicitLod(EmitContext&);
510void EmitBindlessImageGather(EmitContext&);
511void EmitBindlessImageGatherDref(EmitContext&);
512void EmitBindlessImageFetch(EmitContext&);
513void EmitBindlessImageQueryDimensions(EmitContext&);
514void EmitBindlessImageQueryLod(EmitContext&);
515void EmitBindlessImageGradient(EmitContext&);
516void EmitBindlessImageRead(EmitContext&);
517void EmitBindlessImageWrite(EmitContext&);
518void EmitBoundImageSampleImplicitLod(EmitContext&);
519void EmitBoundImageSampleExplicitLod(EmitContext&);
520void EmitBoundImageSampleDrefImplicitLod(EmitContext&);
521void EmitBoundImageSampleDrefExplicitLod(EmitContext&);
522void EmitBoundImageGather(EmitContext&);
523void EmitBoundImageGatherDref(EmitContext&);
524void EmitBoundImageFetch(EmitContext&);
525void EmitBoundImageQueryDimensions(EmitContext&);
526void EmitBoundImageQueryLod(EmitContext&);
527void EmitBoundImageGradient(EmitContext&);
528void EmitBoundImageRead(EmitContext&);
529void EmitBoundImageWrite(EmitContext&);
530void EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
531 const IR::Value& coord, Register bias_lc, const IR::Value& offset);
532void EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
533 const IR::Value& coord, ScalarF32 lod, const IR::Value& offset);
534void EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
535 const IR::Value& coord, const IR::Value& dref,
536 const IR::Value& bias_lc, const IR::Value& offset);
537void EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
538 const IR::Value& coord, const IR::Value& dref,
539 const IR::Value& lod, const IR::Value& offset);
540void EmitImageGather(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
541 const IR::Value& coord, const IR::Value& offset, const IR::Value& offset2);
542void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
543 const IR::Value& coord, const IR::Value& offset, const IR::Value& offset2,
544 const IR::Value& dref);
545void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
546 const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms);
547void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
548 ScalarS32 lod);
549void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord);
550void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
551 const IR::Value& coord, const IR::Value& derivatives,
552 const IR::Value& offset, const IR::Value& lod_clamp);
553void EmitImageRead(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord);
554void EmitImageWrite(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
555 Register color);
556void EmitBindlessImageAtomicIAdd32(EmitContext&);
557void EmitBindlessImageAtomicSMin32(EmitContext&);
558void EmitBindlessImageAtomicUMin32(EmitContext&);
559void EmitBindlessImageAtomicSMax32(EmitContext&);
560void EmitBindlessImageAtomicUMax32(EmitContext&);
561void EmitBindlessImageAtomicInc32(EmitContext&);
562void EmitBindlessImageAtomicDec32(EmitContext&);
563void EmitBindlessImageAtomicAnd32(EmitContext&);
564void EmitBindlessImageAtomicOr32(EmitContext&);
565void EmitBindlessImageAtomicXor32(EmitContext&);
566void EmitBindlessImageAtomicExchange32(EmitContext&);
567void EmitBoundImageAtomicIAdd32(EmitContext&);
568void EmitBoundImageAtomicSMin32(EmitContext&);
569void EmitBoundImageAtomicUMin32(EmitContext&);
570void EmitBoundImageAtomicSMax32(EmitContext&);
571void EmitBoundImageAtomicUMax32(EmitContext&);
572void EmitBoundImageAtomicInc32(EmitContext&);
573void EmitBoundImageAtomicDec32(EmitContext&);
574void EmitBoundImageAtomicAnd32(EmitContext&);
575void EmitBoundImageAtomicOr32(EmitContext&);
576void EmitBoundImageAtomicXor32(EmitContext&);
577void EmitBoundImageAtomicExchange32(EmitContext&);
578void EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
579 ScalarU32 value);
580void EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
581 ScalarS32 value);
582void EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
583 ScalarU32 value);
584void EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
585 ScalarS32 value);
586void EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
587 ScalarU32 value);
588void EmitImageAtomicInc32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
589 ScalarU32 value);
590void EmitImageAtomicDec32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
591 ScalarU32 value);
592void EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
593 ScalarU32 value);
594void EmitImageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
595 ScalarU32 value);
596void EmitImageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
597 ScalarU32 value);
598void EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
599 Register coord, ScalarU32 value);
600void EmitLaneId(EmitContext& ctx, IR::Inst& inst);
601void EmitVoteAll(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred);
602void EmitVoteAny(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred);
603void EmitVoteEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred);
604void EmitSubgroupBallot(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred);
605void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst);
606void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst);
607void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst);
608void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst);
609void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst);
610void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
611 const IR::Value& clamp, const IR::Value& segmentation_mask);
612void EmitShuffleUp(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
613 const IR::Value& clamp, const IR::Value& segmentation_mask);
614void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
615 const IR::Value& clamp, const IR::Value& segmentation_mask);
616void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
617 const IR::Value& clamp, const IR::Value& segmentation_mask);
618void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a, ScalarF32 op_b,
619 ScalarU32 swizzle);
620void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a);
621void EmitDPdyFine(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a);
622void EmitDPdxCoarse(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a);
623void EmitDPdyCoarse(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a);
624
625} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp
new file mode 100644
index 000000000..f55c26b76
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp
@@ -0,0 +1,294 @@
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 "shader_recompiler/backend/glasm/emit_context.h"
6#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
7#include "shader_recompiler/frontend/ir/value.h"
8
9namespace Shader::Backend::GLASM {
10namespace {
11void BitwiseLogicalOp(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b,
12 std::string_view lop) {
13 const auto zero = inst.GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp);
14 const auto sign = inst.GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp);
15 if (zero) {
16 zero->Invalidate();
17 }
18 if (sign) {
19 sign->Invalidate();
20 }
21 if (zero || sign) {
22 ctx.reg_alloc.InvalidateConditionCodes();
23 }
24 const auto ret{ctx.reg_alloc.Define(inst)};
25 ctx.Add("{}.S {}.x,{},{};", lop, ret, a, b);
26 if (zero) {
27 ctx.Add("SEQ.S {},{},0;", *zero, ret);
28 }
29 if (sign) {
30 ctx.Add("SLT.S {},{},0;", *sign, ret);
31 }
32}
33} // Anonymous namespace
34
35void EmitIAdd32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
36 const std::array flags{
37 inst.GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp),
38 inst.GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp),
39 inst.GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp),
40 inst.GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp),
41 };
42 for (IR::Inst* const flag_inst : flags) {
43 if (flag_inst) {
44 flag_inst->Invalidate();
45 }
46 }
47 const bool cc{inst.HasAssociatedPseudoOperation()};
48 const std::string_view cc_mod{cc ? ".CC" : ""};
49 if (cc) {
50 ctx.reg_alloc.InvalidateConditionCodes();
51 }
52 const auto ret{ctx.reg_alloc.Define(inst)};
53 ctx.Add("ADD.S{} {}.x,{},{};", cc_mod, ret, a, b);
54 if (!cc) {
55 return;
56 }
57 static constexpr std::array<std::string_view, 4> masks{"", "SF", "CF", "OF"};
58 for (size_t flag_index = 0; flag_index < flags.size(); ++flag_index) {
59 if (!flags[flag_index]) {
60 continue;
61 }
62 const auto flag_ret{ctx.reg_alloc.Define(*flags[flag_index])};
63 if (flag_index == 0) {
64 ctx.Add("SEQ.S {}.x,{}.x,0;", flag_ret, ret);
65 } else {
66 // We could use conditional execution here, but it's broken on Nvidia's compiler
67 ctx.Add("IF {}.x;"
68 "MOV.S {}.x,-1;"
69 "ELSE;"
70 "MOV.S {}.x,0;"
71 "ENDIF;",
72 masks[flag_index], flag_ret, flag_ret);
73 }
74 }
75}
76
77void EmitIAdd64(EmitContext& ctx, IR::Inst& inst, Register a, Register b) {
78 ctx.LongAdd("ADD.S64 {}.x,{}.x,{}.x;", inst, a, b);
79}
80
81void EmitISub32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
82 ctx.Add("SUB.S {}.x,{},{};", inst, a, b);
83}
84
85void EmitISub64(EmitContext& ctx, IR::Inst& inst, Register a, Register b) {
86 ctx.LongAdd("SUB.S64 {}.x,{}.x,{}.x;", inst, a, b);
87}
88
89void EmitIMul32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
90 ctx.Add("MUL.S {}.x,{},{};", inst, a, b);
91}
92
93void EmitINeg32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
94 if (value.type != Type::Register && static_cast<s32>(value.imm_u32) < 0) {
95 ctx.Add("MOV.S {},{};", inst, -static_cast<s32>(value.imm_u32));
96 } else {
97 ctx.Add("MOV.S {},-{};", inst, value);
98 }
99}
100
101void EmitINeg64(EmitContext& ctx, IR::Inst& inst, Register value) {
102 ctx.LongAdd("MOV.S64 {},-{};", inst, value);
103}
104
105void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
106 ctx.Add("ABS.S {},{};", inst, value);
107}
108
109void EmitShiftLeftLogical32(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 shift) {
110 ctx.Add("SHL.U {}.x,{},{};", inst, base, shift);
111}
112
113void EmitShiftLeftLogical64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base,
114 ScalarU32 shift) {
115 ctx.LongAdd("SHL.U64 {}.x,{},{};", inst, base, shift);
116}
117
118void EmitShiftRightLogical32(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 shift) {
119 ctx.Add("SHR.U {}.x,{},{};", inst, base, shift);
120}
121
122void EmitShiftRightLogical64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base,
123 ScalarU32 shift) {
124 ctx.LongAdd("SHR.U64 {}.x,{},{};", inst, base, shift);
125}
126
127void EmitShiftRightArithmetic32(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 shift) {
128 ctx.Add("SHR.S {}.x,{},{};", inst, base, shift);
129}
130
131void EmitShiftRightArithmetic64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base,
132 ScalarS32 shift) {
133 ctx.LongAdd("SHR.S64 {}.x,{},{};", inst, base, shift);
134}
135
136void EmitBitwiseAnd32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
137 BitwiseLogicalOp(ctx, inst, a, b, "AND");
138}
139
140void EmitBitwiseOr32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
141 BitwiseLogicalOp(ctx, inst, a, b, "OR");
142}
143
144void EmitBitwiseXor32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
145 BitwiseLogicalOp(ctx, inst, a, b, "XOR");
146}
147
148void EmitBitFieldInsert(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 insert,
149 ScalarS32 offset, ScalarS32 count) {
150 const Register ret{ctx.reg_alloc.Define(inst)};
151 if (count.type != Type::Register && offset.type != Type::Register) {
152 ctx.Add("BFI.S {},{{{},{},0,0}},{},{};", ret, count, offset, insert, base);
153 } else {
154 ctx.Add("MOV.S RC.x,{};"
155 "MOV.S RC.y,{};"
156 "BFI.S {},RC,{},{};",
157 count, offset, ret, insert, base);
158 }
159}
160
161void EmitBitFieldSExtract(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 offset,
162 ScalarS32 count) {
163 const Register ret{ctx.reg_alloc.Define(inst)};
164 if (count.type != Type::Register && offset.type != Type::Register) {
165 ctx.Add("BFE.S {},{{{},{},0,0}},{};", ret, count, offset, base);
166 } else {
167 ctx.Add("MOV.S RC.x,{};"
168 "MOV.S RC.y,{};"
169 "BFE.S {},RC,{};",
170 count, offset, ret, base);
171 }
172}
173
174void EmitBitFieldUExtract(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 offset,
175 ScalarU32 count) {
176 const auto zero = inst.GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp);
177 const auto sign = inst.GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp);
178 if (zero) {
179 zero->Invalidate();
180 }
181 if (sign) {
182 sign->Invalidate();
183 }
184 if (zero || sign) {
185 ctx.reg_alloc.InvalidateConditionCodes();
186 }
187 const Register ret{ctx.reg_alloc.Define(inst)};
188 if (count.type != Type::Register && offset.type != Type::Register) {
189 ctx.Add("BFE.U {},{{{},{},0,0}},{};", ret, count, offset, base);
190 } else {
191 ctx.Add("MOV.U RC.x,{};"
192 "MOV.U RC.y,{};"
193 "BFE.U {},RC,{};",
194 count, offset, ret, base);
195 }
196 if (zero) {
197 ctx.Add("SEQ.S {},{},0;", *zero, ret);
198 }
199 if (sign) {
200 ctx.Add("SLT.S {},{},0;", *sign, ret);
201 }
202}
203
204void EmitBitReverse32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
205 ctx.Add("BFR {},{};", inst, value);
206}
207
208void EmitBitCount32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
209 ctx.Add("BTC {},{};", inst, value);
210}
211
212void EmitBitwiseNot32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
213 ctx.Add("NOT.S {},{};", inst, value);
214}
215
216void EmitFindSMsb32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
217 ctx.Add("BTFM.S {},{};", inst, value);
218}
219
220void EmitFindUMsb32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value) {
221 ctx.Add("BTFM.U {},{};", inst, value);
222}
223
224void EmitSMin32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
225 ctx.Add("MIN.S {},{},{};", inst, a, b);
226}
227
228void EmitUMin32(EmitContext& ctx, IR::Inst& inst, ScalarU32 a, ScalarU32 b) {
229 ctx.Add("MIN.U {},{},{};", inst, a, b);
230}
231
232void EmitSMax32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
233 ctx.Add("MAX.S {},{},{};", inst, a, b);
234}
235
236void EmitUMax32(EmitContext& ctx, IR::Inst& inst, ScalarU32 a, ScalarU32 b) {
237 ctx.Add("MAX.U {},{},{};", inst, a, b);
238}
239
240void EmitSClamp32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value, ScalarS32 min, ScalarS32 max) {
241 const Register ret{ctx.reg_alloc.Define(inst)};
242 ctx.Add("MIN.S RC.x,{},{};"
243 "MAX.S {}.x,RC.x,{};",
244 max, value, ret, min);
245}
246
247void EmitUClamp32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 min, ScalarU32 max) {
248 const Register ret{ctx.reg_alloc.Define(inst)};
249 ctx.Add("MIN.U RC.x,{},{};"
250 "MAX.U {}.x,RC.x,{};",
251 max, value, ret, min);
252}
253
254void EmitSLessThan(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
255 ctx.Add("SLT.S {}.x,{},{};", inst, lhs, rhs);
256}
257
258void EmitULessThan(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs) {
259 ctx.Add("SLT.U {}.x,{},{};", inst, lhs, rhs);
260}
261
262void EmitIEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
263 ctx.Add("SEQ.S {}.x,{},{};", inst, lhs, rhs);
264}
265
266void EmitSLessThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
267 ctx.Add("SLE.S {}.x,{},{};", inst, lhs, rhs);
268}
269
270void EmitULessThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs) {
271 ctx.Add("SLE.U {}.x,{},{};", inst, lhs, rhs);
272}
273
274void EmitSGreaterThan(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
275 ctx.Add("SGT.S {}.x,{},{};", inst, lhs, rhs);
276}
277
278void EmitUGreaterThan(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs) {
279 ctx.Add("SGT.U {}.x,{},{};", inst, lhs, rhs);
280}
281
282void EmitINotEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
283 ctx.Add("SNE.U {}.x,{},{};", inst, lhs, rhs);
284}
285
286void EmitSGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
287 ctx.Add("SGE.S {}.x,{},{};", inst, lhs, rhs);
288}
289
290void EmitUGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs) {
291 ctx.Add("SGE.U {}.x,{},{};", inst, lhs, rhs);
292}
293
294} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_logical.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_logical.cpp
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_logical.cpp
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp
new file mode 100644
index 000000000..af9fac7c1
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp
@@ -0,0 +1,568 @@
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 <string_view>
6
7#include "shader_recompiler/backend/glasm/emit_context.h"
8#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
9#include "shader_recompiler/frontend/ir/program.h"
10#include "shader_recompiler/frontend/ir/value.h"
11#include "shader_recompiler/runtime_info.h"
12
13namespace Shader::Backend::GLASM {
14namespace {
15void StorageOp(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
16 std::string_view then_expr, std::string_view else_expr = {}) {
17 // Operate on bindless SSBO, call the expression with bounds checking
18 // address = c[binding].xy
19 // length = c[binding].z
20 const u32 sb_binding{binding.U32()};
21 ctx.Add("PK64.U DC,c[{}];" // pointer = address
22 "CVT.U64.U32 DC.z,{};" // offset = uint64_t(offset)
23 "ADD.U64 DC.x,DC.x,DC.z;" // pointer += offset
24 "SLT.U.CC RC.x,{},c[{}].z;", // cc = offset < length
25 sb_binding, offset, offset, sb_binding);
26 if (else_expr.empty()) {
27 ctx.Add("IF NE.x;{}ENDIF;", then_expr);
28 } else {
29 ctx.Add("IF NE.x;{}ELSE;{}ENDIF;", then_expr, else_expr);
30 }
31}
32
33void GlobalStorageOp(EmitContext& ctx, Register address, bool pointer_based, std::string_view expr,
34 std::string_view else_expr = {}) {
35 const size_t num_buffers{ctx.info.storage_buffers_descriptors.size()};
36 for (size_t index = 0; index < num_buffers; ++index) {
37 if (!ctx.info.nvn_buffer_used[index]) {
38 continue;
39 }
40 const auto& ssbo{ctx.info.storage_buffers_descriptors[index]};
41 ctx.Add("LDC.U64 DC.x,c{}[{}];" // ssbo_addr
42 "LDC.U32 RC.x,c{}[{}];" // ssbo_size_u32
43 "CVT.U64.U32 DC.y,RC.x;" // ssbo_size = ssbo_size_u32
44 "ADD.U64 DC.y,DC.y,DC.x;" // ssbo_end = ssbo_addr + ssbo_size
45 "SGE.U64 RC.x,{}.x,DC.x;" // a = input_addr >= ssbo_addr ? -1 : 0
46 "SLT.U64 RC.y,{}.x,DC.y;" // b = input_addr < ssbo_end ? -1 : 0
47 "AND.U.CC RC.x,RC.x,RC.y;" // cond = a && b
48 "IF NE.x;" // if cond
49 "SUB.U64 DC.x,{}.x,DC.x;", // offset = input_addr - ssbo_addr
50 ssbo.cbuf_index, ssbo.cbuf_offset, ssbo.cbuf_index, ssbo.cbuf_offset + 8, address,
51 address, address);
52 if (pointer_based) {
53 ctx.Add("PK64.U DC.y,c[{}];" // host_ssbo = cbuf
54 "ADD.U64 DC.x,DC.x,DC.y;" // host_addr = host_ssbo + offset
55 "{}"
56 "ELSE;",
57 index, expr);
58 } else {
59 ctx.Add("CVT.U32.U64 RC.x,DC.x;"
60 "{},ssbo{}[RC.x];"
61 "ELSE;",
62 expr, index);
63 }
64 }
65 if (!else_expr.empty()) {
66 ctx.Add("{}", else_expr);
67 }
68 const size_t num_used_buffers{ctx.info.nvn_buffer_used.count()};
69 for (size_t index = 0; index < num_used_buffers; ++index) {
70 ctx.Add("ENDIF;");
71 }
72}
73
74template <typename ValueType>
75void Write(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset, ValueType value,
76 std::string_view size) {
77 if (ctx.runtime_info.glasm_use_storage_buffers) {
78 ctx.Add("STB.{} {},ssbo{}[{}];", size, value, binding.U32(), offset);
79 } else {
80 StorageOp(ctx, binding, offset, fmt::format("STORE.{} {},DC.x;", size, value));
81 }
82}
83
84void Load(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset,
85 std::string_view size) {
86 const Register ret{ctx.reg_alloc.Define(inst)};
87 if (ctx.runtime_info.glasm_use_storage_buffers) {
88 ctx.Add("LDB.{} {},ssbo{}[{}];", size, ret, binding.U32(), offset);
89 } else {
90 StorageOp(ctx, binding, offset, fmt::format("LOAD.{} {},DC.x;", size, ret),
91 fmt::format("MOV.U {},{{0,0,0,0}};", ret));
92 }
93}
94
95template <typename ValueType>
96void GlobalWrite(EmitContext& ctx, Register address, ValueType value, std::string_view size) {
97 if (ctx.runtime_info.glasm_use_storage_buffers) {
98 GlobalStorageOp(ctx, address, false, fmt::format("STB.{} {}", size, value));
99 } else {
100 GlobalStorageOp(ctx, address, true, fmt::format("STORE.{} {},DC.x;", size, value));
101 }
102}
103
104void GlobalLoad(EmitContext& ctx, IR::Inst& inst, Register address, std::string_view size) {
105 const Register ret{ctx.reg_alloc.Define(inst)};
106 if (ctx.runtime_info.glasm_use_storage_buffers) {
107 GlobalStorageOp(ctx, address, false, fmt::format("LDB.{} {}", size, ret));
108 } else {
109 GlobalStorageOp(ctx, address, true, fmt::format("LOAD.{} {},DC.x;", size, ret),
110 fmt::format("MOV.S {},0;", ret));
111 }
112}
113
114template <typename ValueType>
115void Atom(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset,
116 ValueType value, std::string_view operation, std::string_view size) {
117 const Register ret{ctx.reg_alloc.Define(inst)};
118 if (ctx.runtime_info.glasm_use_storage_buffers) {
119 ctx.Add("ATOMB.{}.{} {},{},ssbo{}[{}];", operation, size, ret, value, binding.U32(),
120 offset);
121 } else {
122 StorageOp(ctx, binding, offset,
123 fmt::format("ATOM.{}.{} {},{},DC.x;", operation, size, ret, value));
124 }
125}
126} // Anonymous namespace
127
128void EmitLoadGlobalU8(EmitContext& ctx, IR::Inst& inst, Register address) {
129 GlobalLoad(ctx, inst, address, "U8");
130}
131
132void EmitLoadGlobalS8(EmitContext& ctx, IR::Inst& inst, Register address) {
133 GlobalLoad(ctx, inst, address, "S8");
134}
135
136void EmitLoadGlobalU16(EmitContext& ctx, IR::Inst& inst, Register address) {
137 GlobalLoad(ctx, inst, address, "U16");
138}
139
140void EmitLoadGlobalS16(EmitContext& ctx, IR::Inst& inst, Register address) {
141 GlobalLoad(ctx, inst, address, "S16");
142}
143
144void EmitLoadGlobal32(EmitContext& ctx, IR::Inst& inst, Register address) {
145 GlobalLoad(ctx, inst, address, "U32");
146}
147
148void EmitLoadGlobal64(EmitContext& ctx, IR::Inst& inst, Register address) {
149 GlobalLoad(ctx, inst, address, "U32X2");
150}
151
152void EmitLoadGlobal128(EmitContext& ctx, IR::Inst& inst, Register address) {
153 GlobalLoad(ctx, inst, address, "U32X4");
154}
155
156void EmitWriteGlobalU8(EmitContext& ctx, Register address, Register value) {
157 GlobalWrite(ctx, address, value, "U8");
158}
159
160void EmitWriteGlobalS8(EmitContext& ctx, Register address, Register value) {
161 GlobalWrite(ctx, address, value, "S8");
162}
163
164void EmitWriteGlobalU16(EmitContext& ctx, Register address, Register value) {
165 GlobalWrite(ctx, address, value, "U16");
166}
167
168void EmitWriteGlobalS16(EmitContext& ctx, Register address, Register value) {
169 GlobalWrite(ctx, address, value, "S16");
170}
171
172void EmitWriteGlobal32(EmitContext& ctx, Register address, ScalarU32 value) {
173 GlobalWrite(ctx, address, value, "U32");
174}
175
176void EmitWriteGlobal64(EmitContext& ctx, Register address, Register value) {
177 GlobalWrite(ctx, address, value, "U32X2");
178}
179
180void EmitWriteGlobal128(EmitContext& ctx, Register address, Register value) {
181 GlobalWrite(ctx, address, value, "U32X4");
182}
183
184void EmitLoadStorageU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
185 ScalarU32 offset) {
186 Load(ctx, inst, binding, offset, "U8");
187}
188
189void EmitLoadStorageS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
190 ScalarU32 offset) {
191 Load(ctx, inst, binding, offset, "S8");
192}
193
194void EmitLoadStorageU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
195 ScalarU32 offset) {
196 Load(ctx, inst, binding, offset, "U16");
197}
198
199void EmitLoadStorageS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
200 ScalarU32 offset) {
201 Load(ctx, inst, binding, offset, "S16");
202}
203
204void EmitLoadStorage32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
205 ScalarU32 offset) {
206 Load(ctx, inst, binding, offset, "U32");
207}
208
209void EmitLoadStorage64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
210 ScalarU32 offset) {
211 Load(ctx, inst, binding, offset, "U32X2");
212}
213
214void EmitLoadStorage128(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
215 ScalarU32 offset) {
216 Load(ctx, inst, binding, offset, "U32X4");
217}
218
219void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
220 ScalarU32 value) {
221 Write(ctx, binding, offset, value, "U8");
222}
223
224void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
225 ScalarS32 value) {
226 Write(ctx, binding, offset, value, "S8");
227}
228
229void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
230 ScalarU32 value) {
231 Write(ctx, binding, offset, value, "U16");
232}
233
234void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
235 ScalarS32 value) {
236 Write(ctx, binding, offset, value, "S16");
237}
238
239void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
240 ScalarU32 value) {
241 Write(ctx, binding, offset, value, "U32");
242}
243
244void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
245 Register value) {
246 Write(ctx, binding, offset, value, "U32X2");
247}
248
249void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
250 Register value) {
251 Write(ctx, binding, offset, value, "U32X4");
252}
253
254void EmitSharedAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
255 ScalarU32 value) {
256 ctx.Add("ATOMS.ADD.U32 {},{},shared_mem[{}];", inst, value, pointer_offset);
257}
258
259void EmitSharedAtomicSMin32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
260 ScalarS32 value) {
261 ctx.Add("ATOMS.MIN.S32 {},{},shared_mem[{}];", inst, value, pointer_offset);
262}
263
264void EmitSharedAtomicUMin32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
265 ScalarU32 value) {
266 ctx.Add("ATOMS.MIN.U32 {},{},shared_mem[{}];", inst, value, pointer_offset);
267}
268
269void EmitSharedAtomicSMax32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
270 ScalarS32 value) {
271 ctx.Add("ATOMS.MAX.S32 {},{},shared_mem[{}];", inst, value, pointer_offset);
272}
273
274void EmitSharedAtomicUMax32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
275 ScalarU32 value) {
276 ctx.Add("ATOMS.MAX.U32 {},{},shared_mem[{}];", inst, value, pointer_offset);
277}
278
279void EmitSharedAtomicInc32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
280 ScalarU32 value) {
281 ctx.Add("ATOMS.IWRAP.U32 {},{},shared_mem[{}];", inst, value, pointer_offset);
282}
283
284void EmitSharedAtomicDec32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
285 ScalarU32 value) {
286 ctx.Add("ATOMS.DWRAP.U32 {},{},shared_mem[{}];", inst, value, pointer_offset);
287}
288
289void EmitSharedAtomicAnd32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
290 ScalarU32 value) {
291 ctx.Add("ATOMS.AND.U32 {},{},shared_mem[{}];", inst, value, pointer_offset);
292}
293
294void EmitSharedAtomicOr32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
295 ScalarU32 value) {
296 ctx.Add("ATOMS.OR.U32 {},{},shared_mem[{}];", inst, value, pointer_offset);
297}
298
299void EmitSharedAtomicXor32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
300 ScalarU32 value) {
301 ctx.Add("ATOMS.XOR.U32 {},{},shared_mem[{}];", inst, value, pointer_offset);
302}
303
304void EmitSharedAtomicExchange32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
305 ScalarU32 value) {
306 ctx.Add("ATOMS.EXCH.U32 {},{},shared_mem[{}];", inst, value, pointer_offset);
307}
308
309void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
310 Register value) {
311 ctx.LongAdd("ATOMS.EXCH.U64 {}.x,{},shared_mem[{}];", inst, value, pointer_offset);
312}
313
314void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
315 ScalarU32 offset, ScalarU32 value) {
316 Atom(ctx, inst, binding, offset, value, "ADD", "U32");
317}
318
319void EmitStorageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
320 ScalarU32 offset, ScalarS32 value) {
321 Atom(ctx, inst, binding, offset, value, "MIN", "S32");
322}
323
324void EmitStorageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
325 ScalarU32 offset, ScalarU32 value) {
326 Atom(ctx, inst, binding, offset, value, "MIN", "U32");
327}
328
329void EmitStorageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
330 ScalarU32 offset, ScalarS32 value) {
331 Atom(ctx, inst, binding, offset, value, "MAX", "S32");
332}
333
334void EmitStorageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
335 ScalarU32 offset, ScalarU32 value) {
336 Atom(ctx, inst, binding, offset, value, "MAX", "U32");
337}
338
339void EmitStorageAtomicInc32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
340 ScalarU32 offset, ScalarU32 value) {
341 Atom(ctx, inst, binding, offset, value, "IWRAP", "U32");
342}
343
344void EmitStorageAtomicDec32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
345 ScalarU32 offset, ScalarU32 value) {
346 Atom(ctx, inst, binding, offset, value, "DWRAP", "U32");
347}
348
349void EmitStorageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
350 ScalarU32 offset, ScalarU32 value) {
351 Atom(ctx, inst, binding, offset, value, "AND", "U32");
352}
353
354void EmitStorageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
355 ScalarU32 offset, ScalarU32 value) {
356 Atom(ctx, inst, binding, offset, value, "OR", "U32");
357}
358
359void EmitStorageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
360 ScalarU32 offset, ScalarU32 value) {
361 Atom(ctx, inst, binding, offset, value, "XOR", "U32");
362}
363
364void EmitStorageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
365 ScalarU32 offset, ScalarU32 value) {
366 Atom(ctx, inst, binding, offset, value, "EXCH", "U32");
367}
368
369void EmitStorageAtomicIAdd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
370 ScalarU32 offset, Register value) {
371 Atom(ctx, inst, binding, offset, value, "ADD", "U64");
372}
373
374void EmitStorageAtomicSMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
375 ScalarU32 offset, Register value) {
376 Atom(ctx, inst, binding, offset, value, "MIN", "S64");
377}
378
379void EmitStorageAtomicUMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
380 ScalarU32 offset, Register value) {
381 Atom(ctx, inst, binding, offset, value, "MIN", "U64");
382}
383
384void EmitStorageAtomicSMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
385 ScalarU32 offset, Register value) {
386 Atom(ctx, inst, binding, offset, value, "MAX", "S64");
387}
388
389void EmitStorageAtomicUMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
390 ScalarU32 offset, Register value) {
391 Atom(ctx, inst, binding, offset, value, "MAX", "U64");
392}
393
394void EmitStorageAtomicAnd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
395 ScalarU32 offset, Register value) {
396 Atom(ctx, inst, binding, offset, value, "AND", "U64");
397}
398
399void EmitStorageAtomicOr64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
400 ScalarU32 offset, Register value) {
401 Atom(ctx, inst, binding, offset, value, "OR", "U64");
402}
403
404void EmitStorageAtomicXor64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
405 ScalarU32 offset, Register value) {
406 Atom(ctx, inst, binding, offset, value, "XOR", "U64");
407}
408
409void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
410 ScalarU32 offset, Register value) {
411 Atom(ctx, inst, binding, offset, value, "EXCH", "U64");
412}
413
414void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
415 ScalarU32 offset, ScalarF32 value) {
416 Atom(ctx, inst, binding, offset, value, "ADD", "F32");
417}
418
419void EmitStorageAtomicAddF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
420 ScalarU32 offset, Register value) {
421 Atom(ctx, inst, binding, offset, value, "ADD", "F16x2");
422}
423
424void EmitStorageAtomicAddF32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
425 [[maybe_unused]] const IR::Value& binding,
426 [[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) {
427 throw NotImplementedException("GLASM instruction");
428}
429
430void EmitStorageAtomicMinF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
431 ScalarU32 offset, Register value) {
432 Atom(ctx, inst, binding, offset, value, "MIN", "F16x2");
433}
434
435void EmitStorageAtomicMinF32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
436 [[maybe_unused]] const IR::Value& binding,
437 [[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) {
438 throw NotImplementedException("GLASM instruction");
439}
440
441void EmitStorageAtomicMaxF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
442 ScalarU32 offset, Register value) {
443 Atom(ctx, inst, binding, offset, value, "MAX", "F16x2");
444}
445
446void EmitStorageAtomicMaxF32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
447 [[maybe_unused]] const IR::Value& binding,
448 [[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) {
449 throw NotImplementedException("GLASM instruction");
450}
451
452void EmitGlobalAtomicIAdd32(EmitContext&) {
453 throw NotImplementedException("GLASM instruction");
454}
455
456void EmitGlobalAtomicSMin32(EmitContext&) {
457 throw NotImplementedException("GLASM instruction");
458}
459
460void EmitGlobalAtomicUMin32(EmitContext&) {
461 throw NotImplementedException("GLASM instruction");
462}
463
464void EmitGlobalAtomicSMax32(EmitContext&) {
465 throw NotImplementedException("GLASM instruction");
466}
467
468void EmitGlobalAtomicUMax32(EmitContext&) {
469 throw NotImplementedException("GLASM instruction");
470}
471
472void EmitGlobalAtomicInc32(EmitContext&) {
473 throw NotImplementedException("GLASM instruction");
474}
475
476void EmitGlobalAtomicDec32(EmitContext&) {
477 throw NotImplementedException("GLASM instruction");
478}
479
480void EmitGlobalAtomicAnd32(EmitContext&) {
481 throw NotImplementedException("GLASM instruction");
482}
483
484void EmitGlobalAtomicOr32(EmitContext&) {
485 throw NotImplementedException("GLASM instruction");
486}
487
488void EmitGlobalAtomicXor32(EmitContext&) {
489 throw NotImplementedException("GLASM instruction");
490}
491
492void EmitGlobalAtomicExchange32(EmitContext&) {
493 throw NotImplementedException("GLASM instruction");
494}
495
496void EmitGlobalAtomicIAdd64(EmitContext&) {
497 throw NotImplementedException("GLASM instruction");
498}
499
500void EmitGlobalAtomicSMin64(EmitContext&) {
501 throw NotImplementedException("GLASM instruction");
502}
503
504void EmitGlobalAtomicUMin64(EmitContext&) {
505 throw NotImplementedException("GLASM instruction");
506}
507
508void EmitGlobalAtomicSMax64(EmitContext&) {
509 throw NotImplementedException("GLASM instruction");
510}
511
512void EmitGlobalAtomicUMax64(EmitContext&) {
513 throw NotImplementedException("GLASM instruction");
514}
515
516void EmitGlobalAtomicInc64(EmitContext&) {
517 throw NotImplementedException("GLASM instruction");
518}
519
520void EmitGlobalAtomicDec64(EmitContext&) {
521 throw NotImplementedException("GLASM instruction");
522}
523
524void EmitGlobalAtomicAnd64(EmitContext&) {
525 throw NotImplementedException("GLASM instruction");
526}
527
528void EmitGlobalAtomicOr64(EmitContext&) {
529 throw NotImplementedException("GLASM instruction");
530}
531
532void EmitGlobalAtomicXor64(EmitContext&) {
533 throw NotImplementedException("GLASM instruction");
534}
535
536void EmitGlobalAtomicExchange64(EmitContext&) {
537 throw NotImplementedException("GLASM instruction");
538}
539
540void EmitGlobalAtomicAddF32(EmitContext&) {
541 throw NotImplementedException("GLASM instruction");
542}
543
544void EmitGlobalAtomicAddF16x2(EmitContext&) {
545 throw NotImplementedException("GLASM instruction");
546}
547
548void EmitGlobalAtomicAddF32x2(EmitContext&) {
549 throw NotImplementedException("GLASM instruction");
550}
551
552void EmitGlobalAtomicMinF16x2(EmitContext&) {
553 throw NotImplementedException("GLASM instruction");
554}
555
556void EmitGlobalAtomicMinF32x2(EmitContext&) {
557 throw NotImplementedException("GLASM instruction");
558}
559
560void EmitGlobalAtomicMaxF16x2(EmitContext&) {
561 throw NotImplementedException("GLASM instruction");
562}
563
564void EmitGlobalAtomicMaxF32x2(EmitContext&) {
565 throw NotImplementedException("GLASM instruction");
566}
567
568} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
new file mode 100644
index 000000000..ff64c6924
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
@@ -0,0 +1,273 @@
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 <string_view>
6
7#include "shader_recompiler/backend/glasm/emit_context.h"
8#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
9#include "shader_recompiler/frontend/ir/program.h"
10#include "shader_recompiler/frontend/ir/value.h"
11
12#ifdef _MSC_VER
13#pragma warning(disable : 4100)
14#endif
15
16namespace Shader::Backend::GLASM {
17
18#define NotImplemented() throw NotImplementedException("GLASM instruction {}", __LINE__)
19
20static void DefinePhi(EmitContext& ctx, IR::Inst& phi) {
21 switch (phi.Arg(0).Type()) {
22 case IR::Type::U1:
23 case IR::Type::U32:
24 case IR::Type::F32:
25 ctx.reg_alloc.Define(phi);
26 break;
27 case IR::Type::U64:
28 case IR::Type::F64:
29 ctx.reg_alloc.LongDefine(phi);
30 break;
31 default:
32 throw NotImplementedException("Phi node type {}", phi.Type());
33 }
34}
35
36void EmitPhi(EmitContext& ctx, IR::Inst& phi) {
37 const size_t num_args{phi.NumArgs()};
38 for (size_t i = 0; i < num_args; ++i) {
39 ctx.reg_alloc.Consume(phi.Arg(i));
40 }
41 if (!phi.Definition<Id>().is_valid) {
42 // The phi node wasn't forward defined
43 DefinePhi(ctx, phi);
44 }
45}
46
47void EmitVoid(EmitContext&) {}
48
49void EmitReference(EmitContext& ctx, const IR::Value& value) {
50 ctx.reg_alloc.Consume(value);
51}
52
53void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value& value) {
54 IR::Inst& phi{RegAlloc::AliasInst(*phi_value.Inst())};
55 if (!phi.Definition<Id>().is_valid) {
56 // The phi node wasn't forward defined
57 DefinePhi(ctx, phi);
58 }
59 const Register phi_reg{ctx.reg_alloc.Consume(IR::Value{&phi})};
60 const Value eval_value{ctx.reg_alloc.Consume(value)};
61
62 if (phi_reg == eval_value) {
63 return;
64 }
65 switch (phi.Flags<IR::Type>()) {
66 case IR::Type::U1:
67 case IR::Type::U32:
68 case IR::Type::F32:
69 ctx.Add("MOV.S {}.x,{};", phi_reg, ScalarS32{eval_value});
70 break;
71 case IR::Type::U64:
72 case IR::Type::F64:
73 ctx.Add("MOV.U64 {}.x,{};", phi_reg, ScalarRegister{eval_value});
74 break;
75 default:
76 throw NotImplementedException("Phi node type {}", phi.Type());
77 }
78}
79
80void EmitJoin(EmitContext& ctx) {
81 NotImplemented();
82}
83
84void EmitDemoteToHelperInvocation(EmitContext& ctx) {
85 ctx.Add("KIL TR.x;");
86}
87
88void EmitBarrier(EmitContext& ctx) {
89 ctx.Add("BAR;");
90}
91
92void EmitWorkgroupMemoryBarrier(EmitContext& ctx) {
93 ctx.Add("MEMBAR.CTA;");
94}
95
96void EmitDeviceMemoryBarrier(EmitContext& ctx) {
97 ctx.Add("MEMBAR;");
98}
99
100void EmitPrologue(EmitContext& ctx) {
101 // TODO
102}
103
104void EmitEpilogue(EmitContext& ctx) {
105 // TODO
106}
107
108void EmitEmitVertex(EmitContext& ctx, ScalarS32 stream) {
109 if (stream.type == Type::U32 && stream.imm_u32 == 0) {
110 ctx.Add("EMIT;");
111 } else {
112 ctx.Add("EMITS {};", stream);
113 }
114}
115
116void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
117 if (!stream.IsImmediate()) {
118 LOG_WARNING(Shader_GLASM, "Stream is not immediate");
119 }
120 ctx.reg_alloc.Consume(stream);
121 ctx.Add("ENDPRIM;");
122}
123
124void EmitGetRegister(EmitContext& ctx) {
125 NotImplemented();
126}
127
128void EmitSetRegister(EmitContext& ctx) {
129 NotImplemented();
130}
131
132void EmitGetPred(EmitContext& ctx) {
133 NotImplemented();
134}
135
136void EmitSetPred(EmitContext& ctx) {
137 NotImplemented();
138}
139
140void EmitSetGotoVariable(EmitContext& ctx) {
141 NotImplemented();
142}
143
144void EmitGetGotoVariable(EmitContext& ctx) {
145 NotImplemented();
146}
147
148void EmitSetIndirectBranchVariable(EmitContext& ctx) {
149 NotImplemented();
150}
151
152void EmitGetIndirectBranchVariable(EmitContext& ctx) {
153 NotImplemented();
154}
155
156void EmitGetZFlag(EmitContext& ctx) {
157 NotImplemented();
158}
159
160void EmitGetSFlag(EmitContext& ctx) {
161 NotImplemented();
162}
163
164void EmitGetCFlag(EmitContext& ctx) {
165 NotImplemented();
166}
167
168void EmitGetOFlag(EmitContext& ctx) {
169 NotImplemented();
170}
171
172void EmitSetZFlag(EmitContext& ctx) {
173 NotImplemented();
174}
175
176void EmitSetSFlag(EmitContext& ctx) {
177 NotImplemented();
178}
179
180void EmitSetCFlag(EmitContext& ctx) {
181 NotImplemented();
182}
183
184void EmitSetOFlag(EmitContext& ctx) {
185 NotImplemented();
186}
187
188void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst) {
189 ctx.Add("MOV.S {},invocation.groupid;", inst);
190}
191
192void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst) {
193 ctx.Add("MOV.S {},invocation.localid;", inst);
194}
195
196void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
197 ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst);
198}
199
200void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
201 ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst);
202}
203
204void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst) {
205 ctx.Add("MOV.S {}.x,fragment.helperthread.x;", inst);
206}
207
208void EmitYDirection(EmitContext& ctx, IR::Inst& inst) {
209 ctx.uses_y_direction = true;
210 ctx.Add("MOV.F {}.x,y_direction[0].w;", inst);
211}
212
213void EmitUndefU1(EmitContext& ctx, IR::Inst& inst) {
214 ctx.Add("MOV.S {}.x,0;", inst);
215}
216
217void EmitUndefU8(EmitContext& ctx, IR::Inst& inst) {
218 ctx.Add("MOV.S {}.x,0;", inst);
219}
220
221void EmitUndefU16(EmitContext& ctx, IR::Inst& inst) {
222 ctx.Add("MOV.S {}.x,0;", inst);
223}
224
225void EmitUndefU32(EmitContext& ctx, IR::Inst& inst) {
226 ctx.Add("MOV.S {}.x,0;", inst);
227}
228
229void EmitUndefU64(EmitContext& ctx, IR::Inst& inst) {
230 ctx.LongAdd("MOV.S64 {}.x,0;", inst);
231}
232
233void EmitGetZeroFromOp(EmitContext& ctx) {
234 NotImplemented();
235}
236
237void EmitGetSignFromOp(EmitContext& ctx) {
238 NotImplemented();
239}
240
241void EmitGetCarryFromOp(EmitContext& ctx) {
242 NotImplemented();
243}
244
245void EmitGetOverflowFromOp(EmitContext& ctx) {
246 NotImplemented();
247}
248
249void EmitGetSparseFromOp(EmitContext& ctx) {
250 NotImplemented();
251}
252
253void EmitGetInBoundsFromOp(EmitContext& ctx) {
254 NotImplemented();
255}
256
257void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
258 ctx.Add("OR.S {},{},{};", inst, a, b);
259}
260
261void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
262 ctx.Add("AND.S {},{},{};", inst, a, b);
263}
264
265void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
266 ctx.Add("XOR.S {},{},{};", inst, a, b);
267}
268
269void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
270 ctx.Add("SEQ.S {},{},0;", inst, value);
271}
272
273} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_select.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_select.cpp
new file mode 100644
index 000000000..68fff613c
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_select.cpp
@@ -0,0 +1,67 @@
1
2// Copyright 2021 yuzu Emulator Project
3// Licensed under GPLv2 or any later version
4// Refer to the license.txt file included.
5
6#include "shader_recompiler/backend/glasm/emit_context.h"
7#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
8#include "shader_recompiler/frontend/ir/value.h"
9
10namespace Shader::Backend::GLASM {
11
12void EmitSelectU1(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, ScalarS32 true_value,
13 ScalarS32 false_value) {
14 ctx.Add("CMP.S {},{},{},{};", inst, cond, true_value, false_value);
15}
16
17void EmitSelectU8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] ScalarS32 cond,
18 [[maybe_unused]] ScalarS32 true_value, [[maybe_unused]] ScalarS32 false_value) {
19 throw NotImplementedException("GLASM instruction");
20}
21
22void EmitSelectU16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] ScalarS32 cond,
23 [[maybe_unused]] ScalarS32 true_value, [[maybe_unused]] ScalarS32 false_value) {
24 throw NotImplementedException("GLASM instruction");
25}
26
27void EmitSelectU32(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, ScalarS32 true_value,
28 ScalarS32 false_value) {
29 ctx.Add("CMP.S {},{},{},{};", inst, cond, true_value, false_value);
30}
31
32void EmitSelectU64(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, Register true_value,
33 Register false_value) {
34 ctx.reg_alloc.InvalidateConditionCodes();
35 const Register ret{ctx.reg_alloc.LongDefine(inst)};
36 if (ret == true_value) {
37 ctx.Add("MOV.S.CC RC.x,{};"
38 "MOV.U64 {}.x(EQ.x),{};",
39 cond, ret, false_value);
40 } else if (ret == false_value) {
41 ctx.Add("MOV.S.CC RC.x,{};"
42 "MOV.U64 {}.x(NE.x),{};",
43 cond, ret, true_value);
44 } else {
45 ctx.Add("MOV.S.CC RC.x,{};"
46 "MOV.U64 {}.x,{};"
47 "MOV.U64 {}.x(NE.x),{};",
48 cond, ret, false_value, ret, true_value);
49 }
50}
51
52void EmitSelectF16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] ScalarS32 cond,
53 [[maybe_unused]] Register true_value, [[maybe_unused]] Register false_value) {
54 throw NotImplementedException("GLASM instruction");
55}
56
57void EmitSelectF32(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, ScalarS32 true_value,
58 ScalarS32 false_value) {
59 ctx.Add("CMP.S {},{},{},{};", inst, cond, true_value, false_value);
60}
61
62void EmitSelectF64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] ScalarS32 cond,
63 [[maybe_unused]] Register true_value, [[maybe_unused]] Register false_value) {
64 throw NotImplementedException("GLASM instruction");
65}
66
67} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_shared_memory.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_shared_memory.cpp
new file mode 100644
index 000000000..c1498f449
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_shared_memory.cpp
@@ -0,0 +1,58 @@
1
2// Copyright 2021 yuzu Emulator Project
3// Licensed under GPLv2 or any later version
4// Refer to the license.txt file included.
5
6#include "shader_recompiler/backend/glasm/emit_context.h"
7#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
8#include "shader_recompiler/frontend/ir/value.h"
9
10namespace Shader::Backend::GLASM {
11void EmitLoadSharedU8(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
12 ctx.Add("LDS.U8 {},shared_mem[{}];", inst, offset);
13}
14
15void EmitLoadSharedS8(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
16 ctx.Add("LDS.S8 {},shared_mem[{}];", inst, offset);
17}
18
19void EmitLoadSharedU16(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
20 ctx.Add("LDS.U16 {},shared_mem[{}];", inst, offset);
21}
22
23void EmitLoadSharedS16(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
24 ctx.Add("LDS.S16 {},shared_mem[{}];", inst, offset);
25}
26
27void EmitLoadSharedU32(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
28 ctx.Add("LDS.U32 {},shared_mem[{}];", inst, offset);
29}
30
31void EmitLoadSharedU64(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
32 ctx.Add("LDS.U32X2 {},shared_mem[{}];", inst, offset);
33}
34
35void EmitLoadSharedU128(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
36 ctx.Add("LDS.U32X4 {},shared_mem[{}];", inst, offset);
37}
38
39void EmitWriteSharedU8(EmitContext& ctx, ScalarU32 offset, ScalarU32 value) {
40 ctx.Add("STS.U8 {},shared_mem[{}];", value, offset);
41}
42
43void EmitWriteSharedU16(EmitContext& ctx, ScalarU32 offset, ScalarU32 value) {
44 ctx.Add("STS.U16 {},shared_mem[{}];", value, offset);
45}
46
47void EmitWriteSharedU32(EmitContext& ctx, ScalarU32 offset, ScalarU32 value) {
48 ctx.Add("STS.U32 {},shared_mem[{}];", value, offset);
49}
50
51void EmitWriteSharedU64(EmitContext& ctx, ScalarU32 offset, Register value) {
52 ctx.Add("STS.U32X2 {},shared_mem[{}];", value, offset);
53}
54
55void EmitWriteSharedU128(EmitContext& ctx, ScalarU32 offset, Register value) {
56 ctx.Add("STS.U32X4 {},shared_mem[{}];", value, offset);
57}
58} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_special.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_special.cpp
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_special.cpp
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_undefined.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_undefined.cpp
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_undefined.cpp
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_warp.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_warp.cpp
new file mode 100644
index 000000000..544d475b4
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_warp.cpp
@@ -0,0 +1,150 @@
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 "shader_recompiler/backend/glasm/emit_context.h"
6#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
7#include "shader_recompiler/frontend/ir/value.h"
8#include "shader_recompiler/profile.h"
9
10namespace Shader::Backend::GLASM {
11
12void EmitLaneId(EmitContext& ctx, IR::Inst& inst) {
13 ctx.Add("MOV.S {}.x,{}.threadid;", inst, ctx.stage_name);
14}
15
16void EmitVoteAll(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred) {
17 ctx.Add("TGALL.S {}.x,{};", inst, pred);
18}
19
20void EmitVoteAny(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred) {
21 ctx.Add("TGANY.S {}.x,{};", inst, pred);
22}
23
24void EmitVoteEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred) {
25 ctx.Add("TGEQ.S {}.x,{};", inst, pred);
26}
27
28void EmitSubgroupBallot(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred) {
29 ctx.Add("TGBALLOT {}.x,{};", inst, pred);
30}
31
32void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst) {
33 ctx.Add("MOV.U {},{}.threadeqmask;", inst, ctx.stage_name);
34}
35
36void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst) {
37 ctx.Add("MOV.U {},{}.threadltmask;", inst, ctx.stage_name);
38}
39
40void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst) {
41 ctx.Add("MOV.U {},{}.threadlemask;", inst, ctx.stage_name);
42}
43
44void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst) {
45 ctx.Add("MOV.U {},{}.threadgtmask;", inst, ctx.stage_name);
46}
47
48void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst) {
49 ctx.Add("MOV.U {},{}.threadgemask;", inst, ctx.stage_name);
50}
51
52static void Shuffle(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
53 const IR::Value& clamp, const IR::Value& segmentation_mask,
54 std::string_view op) {
55 IR::Inst* const in_bounds{inst.GetAssociatedPseudoOperation(IR::Opcode::GetInBoundsFromOp)};
56 if (in_bounds) {
57 in_bounds->Invalidate();
58 }
59 std::string mask;
60 if (clamp.IsImmediate() && segmentation_mask.IsImmediate()) {
61 mask = fmt::to_string(clamp.U32() | (segmentation_mask.U32() << 8));
62 } else {
63 mask = "RC";
64 ctx.Add("BFI.U RC.x,{{5,8,0,0}},{},{};",
65 ScalarU32{ctx.reg_alloc.Consume(segmentation_mask)},
66 ScalarU32{ctx.reg_alloc.Consume(clamp)});
67 }
68 const Register value_ret{ctx.reg_alloc.Define(inst)};
69 if (in_bounds) {
70 const Register bounds_ret{ctx.reg_alloc.Define(*in_bounds)};
71 ctx.Add("SHF{}.U {},{},{},{};"
72 "MOV.U {}.x,{}.y;",
73 op, bounds_ret, value, index, mask, value_ret, bounds_ret);
74 } else {
75 ctx.Add("SHF{}.U {},{},{},{};"
76 "MOV.U {}.x,{}.y;",
77 op, value_ret, value, index, mask, value_ret, value_ret);
78 }
79}
80
81void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
82 const IR::Value& clamp, const IR::Value& segmentation_mask) {
83 Shuffle(ctx, inst, value, index, clamp, segmentation_mask, "IDX");
84}
85
86void EmitShuffleUp(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
87 const IR::Value& clamp, const IR::Value& segmentation_mask) {
88 Shuffle(ctx, inst, value, index, clamp, segmentation_mask, "UP");
89}
90
91void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
92 const IR::Value& clamp, const IR::Value& segmentation_mask) {
93 Shuffle(ctx, inst, value, index, clamp, segmentation_mask, "DOWN");
94}
95
96void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
97 const IR::Value& clamp, const IR::Value& segmentation_mask) {
98 Shuffle(ctx, inst, value, index, clamp, segmentation_mask, "XOR");
99}
100
101void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a, ScalarF32 op_b,
102 ScalarU32 swizzle) {
103 const auto ret{ctx.reg_alloc.Define(inst)};
104 ctx.Add("AND.U RC.z,{}.threadid,3;"
105 "SHL.U RC.z,RC.z,1;"
106 "SHR.U RC.z,{},RC.z;"
107 "AND.U RC.z,RC.z,3;"
108 "MUL.F RC.x,{},FSWZA[RC.z];"
109 "MUL.F RC.y,{},FSWZB[RC.z];"
110 "ADD.F {}.x,RC.x,RC.y;",
111 ctx.stage_name, swizzle, op_a, op_b, ret);
112}
113
114void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, ScalarF32 p) {
115 if (ctx.profile.support_derivative_control) {
116 ctx.Add("DDX.FINE {}.x,{};", inst, p);
117 } else {
118 LOG_WARNING(Shader_GLASM, "Fine derivatives not supported by device");
119 ctx.Add("DDX {}.x,{};", inst, p);
120 }
121}
122
123void EmitDPdyFine(EmitContext& ctx, IR::Inst& inst, ScalarF32 p) {
124 if (ctx.profile.support_derivative_control) {
125 ctx.Add("DDY.FINE {}.x,{};", inst, p);
126 } else {
127 LOG_WARNING(Shader_GLASM, "Fine derivatives not supported by device");
128 ctx.Add("DDY {}.x,{};", inst, p);
129 }
130}
131
132void EmitDPdxCoarse(EmitContext& ctx, IR::Inst& inst, ScalarF32 p) {
133 if (ctx.profile.support_derivative_control) {
134 ctx.Add("DDX.COARSE {}.x,{};", inst, p);
135 } else {
136 LOG_WARNING(Shader_GLASM, "Coarse derivatives not supported by device");
137 ctx.Add("DDX {}.x,{};", inst, p);
138 }
139}
140
141void EmitDPdyCoarse(EmitContext& ctx, IR::Inst& inst, ScalarF32 p) {
142 if (ctx.profile.support_derivative_control) {
143 ctx.Add("DDY.COARSE {}.x,{};", inst, p);
144 } else {
145 LOG_WARNING(Shader_GLASM, "Coarse derivatives not supported by device");
146 ctx.Add("DDY {}.x,{};", inst, p);
147 }
148}
149
150} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/reg_alloc.cpp b/src/shader_recompiler/backend/glasm/reg_alloc.cpp
new file mode 100644
index 000000000..4c046db6e
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/reg_alloc.cpp
@@ -0,0 +1,186 @@
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 <string>
6
7#include <fmt/format.h>
8
9#include "shader_recompiler/backend/glasm/emit_context.h"
10#include "shader_recompiler/backend/glasm/reg_alloc.h"
11#include "shader_recompiler/exception.h"
12#include "shader_recompiler/frontend/ir/value.h"
13
14namespace Shader::Backend::GLASM {
15
16Register RegAlloc::Define(IR::Inst& inst) {
17 return Define(inst, false);
18}
19
20Register RegAlloc::LongDefine(IR::Inst& inst) {
21 return Define(inst, true);
22}
23
24Value RegAlloc::Peek(const IR::Value& value) {
25 if (value.IsImmediate()) {
26 return MakeImm(value);
27 } else {
28 return PeekInst(*value.Inst());
29 }
30}
31
32Value RegAlloc::Consume(const IR::Value& value) {
33 if (value.IsImmediate()) {
34 return MakeImm(value);
35 } else {
36 return ConsumeInst(*value.Inst());
37 }
38}
39
40void RegAlloc::Unref(IR::Inst& inst) {
41 IR::Inst& value_inst{AliasInst(inst)};
42 value_inst.DestructiveRemoveUsage();
43 if (!value_inst.HasUses()) {
44 Free(value_inst.Definition<Id>());
45 }
46}
47
48Register RegAlloc::AllocReg() {
49 Register ret;
50 ret.type = Type::Register;
51 ret.id = Alloc(false);
52 return ret;
53}
54
55Register RegAlloc::AllocLongReg() {
56 Register ret;
57 ret.type = Type::Register;
58 ret.id = Alloc(true);
59 return ret;
60}
61
62void RegAlloc::FreeReg(Register reg) {
63 Free(reg.id);
64}
65
66Value RegAlloc::MakeImm(const IR::Value& value) {
67 Value ret;
68 switch (value.Type()) {
69 case IR::Type::Void:
70 ret.type = Type::Void;
71 break;
72 case IR::Type::U1:
73 ret.type = Type::U32;
74 ret.imm_u32 = value.U1() ? 0xffffffff : 0;
75 break;
76 case IR::Type::U32:
77 ret.type = Type::U32;
78 ret.imm_u32 = value.U32();
79 break;
80 case IR::Type::F32:
81 ret.type = Type::U32;
82 ret.imm_u32 = Common::BitCast<u32>(value.F32());
83 break;
84 case IR::Type::U64:
85 ret.type = Type::U64;
86 ret.imm_u64 = value.U64();
87 break;
88 case IR::Type::F64:
89 ret.type = Type::U64;
90 ret.imm_u64 = Common::BitCast<u64>(value.F64());
91 break;
92 default:
93 throw NotImplementedException("Immediate type {}", value.Type());
94 }
95 return ret;
96}
97
98Register RegAlloc::Define(IR::Inst& inst, bool is_long) {
99 if (inst.HasUses()) {
100 inst.SetDefinition<Id>(Alloc(is_long));
101 } else {
102 Id id{};
103 id.is_long.Assign(is_long ? 1 : 0);
104 id.is_null.Assign(1);
105 inst.SetDefinition<Id>(id);
106 }
107 return Register{PeekInst(inst)};
108}
109
110Value RegAlloc::PeekInst(IR::Inst& inst) {
111 Value ret;
112 ret.type = Type::Register;
113 ret.id = inst.Definition<Id>();
114 return ret;
115}
116
117Value RegAlloc::ConsumeInst(IR::Inst& inst) {
118 Unref(inst);
119 return PeekInst(inst);
120}
121
122Id RegAlloc::Alloc(bool is_long) {
123 size_t& num_regs{is_long ? num_used_long_registers : num_used_registers};
124 std::bitset<NUM_REGS>& use{is_long ? long_register_use : register_use};
125 if (num_used_registers + num_used_long_registers < NUM_REGS) {
126 for (size_t reg = 0; reg < NUM_REGS; ++reg) {
127 if (use[reg]) {
128 continue;
129 }
130 num_regs = std::max(num_regs, reg + 1);
131 use[reg] = true;
132 Id ret{};
133 ret.is_valid.Assign(1);
134 ret.is_long.Assign(is_long ? 1 : 0);
135 ret.is_spill.Assign(0);
136 ret.is_condition_code.Assign(0);
137 ret.is_null.Assign(0);
138 ret.index.Assign(static_cast<u32>(reg));
139 return ret;
140 }
141 }
142 throw NotImplementedException("Register spilling");
143}
144
145void RegAlloc::Free(Id id) {
146 if (id.is_valid == 0) {
147 throw LogicError("Freeing invalid register");
148 }
149 if (id.is_spill != 0) {
150 throw NotImplementedException("Free spill");
151 }
152 if (id.is_long != 0) {
153 long_register_use[id.index] = false;
154 } else {
155 register_use[id.index] = false;
156 }
157}
158
159/*static*/ bool RegAlloc::IsAliased(const IR::Inst& inst) {
160 switch (inst.GetOpcode()) {
161 case IR::Opcode::Identity:
162 case IR::Opcode::BitCastU16F16:
163 case IR::Opcode::BitCastU32F32:
164 case IR::Opcode::BitCastU64F64:
165 case IR::Opcode::BitCastF16U16:
166 case IR::Opcode::BitCastF32U32:
167 case IR::Opcode::BitCastF64U64:
168 return true;
169 default:
170 return false;
171 }
172}
173
174/*static*/ IR::Inst& RegAlloc::AliasInst(IR::Inst& inst) {
175 IR::Inst* it{&inst};
176 while (IsAliased(*it)) {
177 const IR::Value arg{it->Arg(0)};
178 if (arg.IsImmediate()) {
179 break;
180 }
181 it = arg.InstRecursive();
182 }
183 return *it;
184}
185
186} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/reg_alloc.h b/src/shader_recompiler/backend/glasm/reg_alloc.h
new file mode 100644
index 000000000..82aec66c6
--- /dev/null
+++ b/src/shader_recompiler/backend/glasm/reg_alloc.h
@@ -0,0 +1,303 @@
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 <bitset>
8
9#include <fmt/format.h>
10
11#include "common/bit_cast.h"
12#include "common/bit_field.h"
13#include "common/common_types.h"
14#include "shader_recompiler/exception.h"
15
16namespace Shader::IR {
17class Inst;
18class Value;
19} // namespace Shader::IR
20
21namespace Shader::Backend::GLASM {
22
23class EmitContext;
24
25enum class Type : u32 {
26 Void,
27 Register,
28 U32,
29 U64,
30};
31
32struct Id {
33 union {
34 u32 raw;
35 BitField<0, 1, u32> is_valid;
36 BitField<1, 1, u32> is_long;
37 BitField<2, 1, u32> is_spill;
38 BitField<3, 1, u32> is_condition_code;
39 BitField<4, 1, u32> is_null;
40 BitField<5, 27, u32> index;
41 };
42
43 bool operator==(Id rhs) const noexcept {
44 return raw == rhs.raw;
45 }
46 bool operator!=(Id rhs) const noexcept {
47 return !operator==(rhs);
48 }
49};
50static_assert(sizeof(Id) == sizeof(u32));
51
52struct Value {
53 Type type;
54 union {
55 Id id;
56 u32 imm_u32;
57 u64 imm_u64;
58 };
59
60 bool operator==(const Value& rhs) const noexcept {
61 if (type != rhs.type) {
62 return false;
63 }
64 switch (type) {
65 case Type::Void:
66 return true;
67 case Type::Register:
68 return id == rhs.id;
69 case Type::U32:
70 return imm_u32 == rhs.imm_u32;
71 case Type::U64:
72 return imm_u64 == rhs.imm_u64;
73 }
74 return false;
75 }
76 bool operator!=(const Value& rhs) const noexcept {
77 return !operator==(rhs);
78 }
79};
80struct Register : Value {};
81struct ScalarRegister : Value {};
82struct ScalarU32 : Value {};
83struct ScalarS32 : Value {};
84struct ScalarF32 : Value {};
85struct ScalarF64 : Value {};
86
87class RegAlloc {
88public:
89 RegAlloc() = default;
90
91 Register Define(IR::Inst& inst);
92
93 Register LongDefine(IR::Inst& inst);
94
95 [[nodiscard]] Value Peek(const IR::Value& value);
96
97 Value Consume(const IR::Value& value);
98
99 void Unref(IR::Inst& inst);
100
101 [[nodiscard]] Register AllocReg();
102
103 [[nodiscard]] Register AllocLongReg();
104
105 void FreeReg(Register reg);
106
107 void InvalidateConditionCodes() {
108 // This does nothing for now
109 }
110
111 [[nodiscard]] size_t NumUsedRegisters() const noexcept {
112 return num_used_registers;
113 }
114
115 [[nodiscard]] size_t NumUsedLongRegisters() const noexcept {
116 return num_used_long_registers;
117 }
118
119 [[nodiscard]] bool IsEmpty() const noexcept {
120 return register_use.none() && long_register_use.none();
121 }
122
123 /// Returns true if the instruction is expected to be aliased to another
124 static bool IsAliased(const IR::Inst& inst);
125
126 /// Returns the underlying value out of an alias sequence
127 static IR::Inst& AliasInst(IR::Inst& inst);
128
129private:
130 static constexpr size_t NUM_REGS = 4096;
131 static constexpr size_t NUM_ELEMENTS = 4;
132
133 Value MakeImm(const IR::Value& value);
134
135 Register Define(IR::Inst& inst, bool is_long);
136
137 Value PeekInst(IR::Inst& inst);
138
139 Value ConsumeInst(IR::Inst& inst);
140
141 Id Alloc(bool is_long);
142
143 void Free(Id id);
144
145 size_t num_used_registers{};
146 size_t num_used_long_registers{};
147 std::bitset<NUM_REGS> register_use{};
148 std::bitset<NUM_REGS> long_register_use{};
149};
150
151template <bool scalar, typename FormatContext>
152auto FormatTo(FormatContext& ctx, Id id) {
153 if (id.is_condition_code != 0) {
154 throw NotImplementedException("Condition code emission");
155 }
156 if (id.is_spill != 0) {
157 throw NotImplementedException("Spill emission");
158 }
159 if constexpr (scalar) {
160 if (id.is_null != 0) {
161 return fmt::format_to(ctx.out(), "{}", id.is_long != 0 ? "DC.x" : "RC.x");
162 }
163 if (id.is_long != 0) {
164 return fmt::format_to(ctx.out(), "D{}.x", id.index.Value());
165 } else {
166 return fmt::format_to(ctx.out(), "R{}.x", id.index.Value());
167 }
168 } else {
169 if (id.is_null != 0) {
170 return fmt::format_to(ctx.out(), "{}", id.is_long != 0 ? "DC" : "RC");
171 }
172 if (id.is_long != 0) {
173 return fmt::format_to(ctx.out(), "D{}", id.index.Value());
174 } else {
175 return fmt::format_to(ctx.out(), "R{}", id.index.Value());
176 }
177 }
178}
179
180} // namespace Shader::Backend::GLASM
181
182template <>
183struct fmt::formatter<Shader::Backend::GLASM::Id> {
184 constexpr auto parse(format_parse_context& ctx) {
185 return ctx.begin();
186 }
187 template <typename FormatContext>
188 auto format(Shader::Backend::GLASM::Id id, FormatContext& ctx) {
189 return Shader::Backend::GLASM::FormatTo<true>(ctx, id);
190 }
191};
192
193template <>
194struct fmt::formatter<Shader::Backend::GLASM::Register> {
195 constexpr auto parse(format_parse_context& ctx) {
196 return ctx.begin();
197 }
198 template <typename FormatContext>
199 auto format(const Shader::Backend::GLASM::Register& value, FormatContext& ctx) {
200 if (value.type != Shader::Backend::GLASM::Type::Register) {
201 throw Shader::InvalidArgument("Register value type is not register");
202 }
203 return Shader::Backend::GLASM::FormatTo<false>(ctx, value.id);
204 }
205};
206
207template <>
208struct fmt::formatter<Shader::Backend::GLASM::ScalarRegister> {
209 constexpr auto parse(format_parse_context& ctx) {
210 return ctx.begin();
211 }
212 template <typename FormatContext>
213 auto format(const Shader::Backend::GLASM::ScalarRegister& value, FormatContext& ctx) {
214 if (value.type != Shader::Backend::GLASM::Type::Register) {
215 throw Shader::InvalidArgument("Register value type is not register");
216 }
217 return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id);
218 }
219};
220
221template <>
222struct fmt::formatter<Shader::Backend::GLASM::ScalarU32> {
223 constexpr auto parse(format_parse_context& ctx) {
224 return ctx.begin();
225 }
226 template <typename FormatContext>
227 auto format(const Shader::Backend::GLASM::ScalarU32& value, FormatContext& ctx) {
228 switch (value.type) {
229 case Shader::Backend::GLASM::Type::Void:
230 break;
231 case Shader::Backend::GLASM::Type::Register:
232 return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id);
233 case Shader::Backend::GLASM::Type::U32:
234 return fmt::format_to(ctx.out(), "{}", value.imm_u32);
235 case Shader::Backend::GLASM::Type::U64:
236 break;
237 }
238 throw Shader::InvalidArgument("Invalid value type {}", value.type);
239 }
240};
241
242template <>
243struct fmt::formatter<Shader::Backend::GLASM::ScalarS32> {
244 constexpr auto parse(format_parse_context& ctx) {
245 return ctx.begin();
246 }
247 template <typename FormatContext>
248 auto format(const Shader::Backend::GLASM::ScalarS32& value, FormatContext& ctx) {
249 switch (value.type) {
250 case Shader::Backend::GLASM::Type::Void:
251 break;
252 case Shader::Backend::GLASM::Type::Register:
253 return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id);
254 case Shader::Backend::GLASM::Type::U32:
255 return fmt::format_to(ctx.out(), "{}", static_cast<s32>(value.imm_u32));
256 case Shader::Backend::GLASM::Type::U64:
257 break;
258 }
259 throw Shader::InvalidArgument("Invalid value type {}", value.type);
260 }
261};
262
263template <>
264struct fmt::formatter<Shader::Backend::GLASM::ScalarF32> {
265 constexpr auto parse(format_parse_context& ctx) {
266 return ctx.begin();
267 }
268 template <typename FormatContext>
269 auto format(const Shader::Backend::GLASM::ScalarF32& value, FormatContext& ctx) {
270 switch (value.type) {
271 case Shader::Backend::GLASM::Type::Void:
272 break;
273 case Shader::Backend::GLASM::Type::Register:
274 return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id);
275 case Shader::Backend::GLASM::Type::U32:
276 return fmt::format_to(ctx.out(), "{}", Common::BitCast<f32>(value.imm_u32));
277 case Shader::Backend::GLASM::Type::U64:
278 break;
279 }
280 throw Shader::InvalidArgument("Invalid value type {}", value.type);
281 }
282};
283
284template <>
285struct fmt::formatter<Shader::Backend::GLASM::ScalarF64> {
286 constexpr auto parse(format_parse_context& ctx) {
287 return ctx.begin();
288 }
289 template <typename FormatContext>
290 auto format(const Shader::Backend::GLASM::ScalarF64& value, FormatContext& ctx) {
291 switch (value.type) {
292 case Shader::Backend::GLASM::Type::Void:
293 break;
294 case Shader::Backend::GLASM::Type::Register:
295 return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id);
296 case Shader::Backend::GLASM::Type::U32:
297 break;
298 case Shader::Backend::GLASM::Type::U64:
299 return fmt::format_to(ctx.out(), "{}", Common::BitCast<f64>(value.imm_u64));
300 }
301 throw Shader::InvalidArgument("Invalid value type {}", value.type);
302 }
303};
diff --git a/src/shader_recompiler/backend/glsl/emit_context.cpp b/src/shader_recompiler/backend/glsl/emit_context.cpp
new file mode 100644
index 000000000..4e6f2c0fe
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_context.cpp
@@ -0,0 +1,715 @@
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 "shader_recompiler/backend/bindings.h"
6#include "shader_recompiler/backend/glsl/emit_context.h"
7#include "shader_recompiler/frontend/ir/program.h"
8#include "shader_recompiler/profile.h"
9#include "shader_recompiler/runtime_info.h"
10
11namespace Shader::Backend::GLSL {
12namespace {
13u32 CbufIndex(size_t offset) {
14 return (offset / 4) % 4;
15}
16
17char Swizzle(size_t offset) {
18 return "xyzw"[CbufIndex(offset)];
19}
20
21std::string_view InterpDecorator(Interpolation interp) {
22 switch (interp) {
23 case Interpolation::Smooth:
24 return "";
25 case Interpolation::Flat:
26 return "flat ";
27 case Interpolation::NoPerspective:
28 return "noperspective ";
29 }
30 throw InvalidArgument("Invalid interpolation {}", interp);
31}
32
33std::string_view InputArrayDecorator(Stage stage) {
34 switch (stage) {
35 case Stage::Geometry:
36 case Stage::TessellationControl:
37 case Stage::TessellationEval:
38 return "[]";
39 default:
40 return "";
41 }
42}
43
44bool StoresPerVertexAttributes(Stage stage) {
45 switch (stage) {
46 case Stage::VertexA:
47 case Stage::VertexB:
48 case Stage::Geometry:
49 case Stage::TessellationEval:
50 return true;
51 default:
52 return false;
53 }
54}
55
56std::string OutputDecorator(Stage stage, u32 size) {
57 switch (stage) {
58 case Stage::TessellationControl:
59 return fmt::format("[{}]", size);
60 default:
61 return "";
62 }
63}
64
65std::string_view SamplerType(TextureType type, bool is_depth) {
66 if (is_depth) {
67 switch (type) {
68 case TextureType::Color1D:
69 return "sampler1DShadow";
70 case TextureType::ColorArray1D:
71 return "sampler1DArrayShadow";
72 case TextureType::Color2D:
73 return "sampler2DShadow";
74 case TextureType::ColorArray2D:
75 return "sampler2DArrayShadow";
76 case TextureType::ColorCube:
77 return "samplerCubeShadow";
78 case TextureType::ColorArrayCube:
79 return "samplerCubeArrayShadow";
80 default:
81 throw NotImplementedException("Texture type: {}", type);
82 }
83 }
84 switch (type) {
85 case TextureType::Color1D:
86 return "sampler1D";
87 case TextureType::ColorArray1D:
88 return "sampler1DArray";
89 case TextureType::Color2D:
90 return "sampler2D";
91 case TextureType::ColorArray2D:
92 return "sampler2DArray";
93 case TextureType::Color3D:
94 return "sampler3D";
95 case TextureType::ColorCube:
96 return "samplerCube";
97 case TextureType::ColorArrayCube:
98 return "samplerCubeArray";
99 case TextureType::Buffer:
100 return "samplerBuffer";
101 default:
102 throw NotImplementedException("Texture type: {}", type);
103 }
104}
105
106std::string_view ImageType(TextureType type) {
107 switch (type) {
108 case TextureType::Color1D:
109 return "uimage1D";
110 case TextureType::ColorArray1D:
111 return "uimage1DArray";
112 case TextureType::Color2D:
113 return "uimage2D";
114 case TextureType::ColorArray2D:
115 return "uimage2DArray";
116 case TextureType::Color3D:
117 return "uimage3D";
118 case TextureType::ColorCube:
119 return "uimageCube";
120 case TextureType::ColorArrayCube:
121 return "uimageCubeArray";
122 case TextureType::Buffer:
123 return "uimageBuffer";
124 default:
125 throw NotImplementedException("Image type: {}", type);
126 }
127}
128
129std::string_view ImageFormatString(ImageFormat format) {
130 switch (format) {
131 case ImageFormat::Typeless:
132 return "";
133 case ImageFormat::R8_UINT:
134 return ",r8ui";
135 case ImageFormat::R8_SINT:
136 return ",r8i";
137 case ImageFormat::R16_UINT:
138 return ",r16ui";
139 case ImageFormat::R16_SINT:
140 return ",r16i";
141 case ImageFormat::R32_UINT:
142 return ",r32ui";
143 case ImageFormat::R32G32_UINT:
144 return ",rg32ui";
145 case ImageFormat::R32G32B32A32_UINT:
146 return ",rgba32ui";
147 default:
148 throw NotImplementedException("Image format: {}", format);
149 }
150}
151
152std::string_view ImageAccessQualifier(bool is_written, bool is_read) {
153 if (is_written && !is_read) {
154 return "writeonly ";
155 }
156 if (is_read && !is_written) {
157 return "readonly ";
158 }
159 return "";
160}
161
162std::string_view GetTessMode(TessPrimitive primitive) {
163 switch (primitive) {
164 case TessPrimitive::Triangles:
165 return "triangles";
166 case TessPrimitive::Quads:
167 return "quads";
168 case TessPrimitive::Isolines:
169 return "isolines";
170 }
171 throw InvalidArgument("Invalid tessellation primitive {}", primitive);
172}
173
174std::string_view GetTessSpacing(TessSpacing spacing) {
175 switch (spacing) {
176 case TessSpacing::Equal:
177 return "equal_spacing";
178 case TessSpacing::FractionalOdd:
179 return "fractional_odd_spacing";
180 case TessSpacing::FractionalEven:
181 return "fractional_even_spacing";
182 }
183 throw InvalidArgument("Invalid tessellation spacing {}", spacing);
184}
185
186std::string_view InputPrimitive(InputTopology topology) {
187 switch (topology) {
188 case InputTopology::Points:
189 return "points";
190 case InputTopology::Lines:
191 return "lines";
192 case InputTopology::LinesAdjacency:
193 return "lines_adjacency";
194 case InputTopology::Triangles:
195 return "triangles";
196 case InputTopology::TrianglesAdjacency:
197 return "triangles_adjacency";
198 }
199 throw InvalidArgument("Invalid input topology {}", topology);
200}
201
202std::string_view OutputPrimitive(OutputTopology topology) {
203 switch (topology) {
204 case OutputTopology::PointList:
205 return "points";
206 case OutputTopology::LineStrip:
207 return "line_strip";
208 case OutputTopology::TriangleStrip:
209 return "triangle_strip";
210 }
211 throw InvalidArgument("Invalid output topology {}", topology);
212}
213
214void SetupLegacyOutPerVertex(EmitContext& ctx, std::string& header) {
215 if (!ctx.info.stores.Legacy()) {
216 return;
217 }
218 if (ctx.info.stores.FixedFunctionTexture()) {
219 header += "vec4 gl_TexCoord[8];";
220 }
221 if (ctx.info.stores.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
222 header += "vec4 gl_FrontColor;";
223 }
224 if (ctx.info.stores.AnyComponent(IR::Attribute::ColorFrontSpecularR)) {
225 header += "vec4 gl_FrontSecondaryColor;";
226 }
227 if (ctx.info.stores.AnyComponent(IR::Attribute::ColorBackDiffuseR)) {
228 header += "vec4 gl_BackColor;";
229 }
230 if (ctx.info.stores.AnyComponent(IR::Attribute::ColorBackSpecularR)) {
231 header += "vec4 gl_BackSecondaryColor;";
232 }
233}
234
235void SetupOutPerVertex(EmitContext& ctx, std::string& header) {
236 if (!StoresPerVertexAttributes(ctx.stage)) {
237 return;
238 }
239 if (ctx.uses_geometry_passthrough) {
240 return;
241 }
242 header += "out gl_PerVertex{vec4 gl_Position;";
243 if (ctx.info.stores[IR::Attribute::PointSize]) {
244 header += "float gl_PointSize;";
245 }
246 if (ctx.info.stores.ClipDistances()) {
247 header += "float gl_ClipDistance[];";
248 }
249 if (ctx.info.stores[IR::Attribute::ViewportIndex] &&
250 ctx.profile.support_viewport_index_layer_non_geometry && ctx.stage != Stage::Geometry) {
251 header += "int gl_ViewportIndex;";
252 }
253 SetupLegacyOutPerVertex(ctx, header);
254 header += "};";
255 if (ctx.info.stores[IR::Attribute::ViewportIndex] && ctx.stage == Stage::Geometry) {
256 header += "out int gl_ViewportIndex;";
257 }
258}
259
260void SetupInPerVertex(EmitContext& ctx, std::string& header) {
261 // Currently only required for TessellationControl to adhere to
262 // ARB_separate_shader_objects requirements
263 if (ctx.stage != Stage::TessellationControl) {
264 return;
265 }
266 const bool loads_position{ctx.info.loads.AnyComponent(IR::Attribute::PositionX)};
267 const bool loads_point_size{ctx.info.loads[IR::Attribute::PointSize]};
268 const bool loads_clip_distance{ctx.info.loads.ClipDistances()};
269 const bool loads_per_vertex{loads_position || loads_point_size || loads_clip_distance};
270 if (!loads_per_vertex) {
271 return;
272 }
273 header += "in gl_PerVertex{";
274 if (loads_position) {
275 header += "vec4 gl_Position;";
276 }
277 if (loads_point_size) {
278 header += "float gl_PointSize;";
279 }
280 if (loads_clip_distance) {
281 header += "float gl_ClipDistance[];";
282 }
283 header += "}gl_in[gl_MaxPatchVertices];";
284}
285
286void SetupLegacyInPerFragment(EmitContext& ctx, std::string& header) {
287 if (!ctx.info.loads.Legacy()) {
288 return;
289 }
290 header += "in gl_PerFragment{";
291 if (ctx.info.loads.FixedFunctionTexture()) {
292 header += "vec4 gl_TexCoord[8];";
293 }
294 if (ctx.info.loads.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
295 header += "vec4 gl_Color;";
296 }
297 header += "};";
298}
299
300} // Anonymous namespace
301
302EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
303 const RuntimeInfo& runtime_info_)
304 : info{program.info}, profile{profile_}, runtime_info{runtime_info_}, stage{program.stage},
305 uses_geometry_passthrough{program.is_geometry_passthrough &&
306 profile.support_geometry_shader_passthrough} {
307 if (profile.need_fastmath_off) {
308 header += "#pragma optionNV(fastmath off)\n";
309 }
310 SetupExtensions();
311 switch (program.stage) {
312 case Stage::VertexA:
313 case Stage::VertexB:
314 stage_name = "vs";
315 break;
316 case Stage::TessellationControl:
317 stage_name = "tcs";
318 header += fmt::format("layout(vertices={})out;", program.invocations);
319 break;
320 case Stage::TessellationEval:
321 stage_name = "tes";
322 header += fmt::format("layout({},{},{})in;", GetTessMode(runtime_info.tess_primitive),
323 GetTessSpacing(runtime_info.tess_spacing),
324 runtime_info.tess_clockwise ? "cw" : "ccw");
325 break;
326 case Stage::Geometry:
327 stage_name = "gs";
328 header += fmt::format("layout({})in;", InputPrimitive(runtime_info.input_topology));
329 if (uses_geometry_passthrough) {
330 header += "layout(passthrough)in gl_PerVertex{vec4 gl_Position;};";
331 break;
332 } else if (program.is_geometry_passthrough &&
333 !profile.support_geometry_shader_passthrough) {
334 LOG_WARNING(Shader_GLSL, "Passthrough geometry program used but not supported");
335 }
336 header += fmt::format(
337 "layout({},max_vertices={})out;in gl_PerVertex{{vec4 gl_Position;}}gl_in[];",
338 OutputPrimitive(program.output_topology), program.output_vertices);
339 break;
340 case Stage::Fragment:
341 stage_name = "fs";
342 position_name = "gl_FragCoord";
343 if (runtime_info.force_early_z) {
344 header += "layout(early_fragment_tests)in;";
345 }
346 if (info.uses_sample_id) {
347 header += "in int gl_SampleID;";
348 }
349 if (info.stores_sample_mask) {
350 header += "out int gl_SampleMask[];";
351 }
352 break;
353 case Stage::Compute:
354 stage_name = "cs";
355 const u32 local_x{std::max(program.workgroup_size[0], 1u)};
356 const u32 local_y{std::max(program.workgroup_size[1], 1u)};
357 const u32 local_z{std::max(program.workgroup_size[2], 1u)};
358 header += fmt::format("layout(local_size_x={},local_size_y={},local_size_z={}) in;",
359 local_x, local_y, local_z);
360 break;
361 }
362 SetupOutPerVertex(*this, header);
363 SetupInPerVertex(*this, header);
364 SetupLegacyInPerFragment(*this, header);
365
366 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
367 if (!info.loads.Generic(index) || !runtime_info.previous_stage_stores.Generic(index)) {
368 continue;
369 }
370 const auto qualifier{uses_geometry_passthrough ? "passthrough"
371 : fmt::format("location={}", index)};
372 header += fmt::format("layout({}){}in vec4 in_attr{}{};", qualifier,
373 InterpDecorator(info.interpolation[index]), index,
374 InputArrayDecorator(stage));
375 }
376 for (size_t index = 0; index < info.uses_patches.size(); ++index) {
377 if (!info.uses_patches[index]) {
378 continue;
379 }
380 const auto qualifier{stage == Stage::TessellationControl ? "out" : "in"};
381 header += fmt::format("layout(location={})patch {} vec4 patch{};", index, qualifier, index);
382 }
383 if (stage == Stage::Fragment) {
384 for (size_t index = 0; index < info.stores_frag_color.size(); ++index) {
385 if (!info.stores_frag_color[index] && !profile.need_declared_frag_colors) {
386 continue;
387 }
388 header += fmt::format("layout(location={})out vec4 frag_color{};", index, index);
389 }
390 }
391 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
392 if (info.stores.Generic(index)) {
393 DefineGenericOutput(index, program.invocations);
394 }
395 }
396 DefineConstantBuffers(bindings);
397 DefineStorageBuffers(bindings);
398 SetupImages(bindings);
399 SetupTextures(bindings);
400 DefineHelperFunctions();
401 DefineConstants();
402}
403
404void EmitContext::SetupExtensions() {
405 header += "#extension GL_ARB_separate_shader_objects : enable\n";
406 if (info.uses_shadow_lod && profile.support_gl_texture_shadow_lod) {
407 header += "#extension GL_EXT_texture_shadow_lod : enable\n";
408 }
409 if (info.uses_int64 && profile.support_int64) {
410 header += "#extension GL_ARB_gpu_shader_int64 : enable\n";
411 }
412 if (info.uses_int64_bit_atomics) {
413 header += "#extension GL_NV_shader_atomic_int64 : enable\n";
414 }
415 if (info.uses_atomic_f32_add) {
416 header += "#extension GL_NV_shader_atomic_float : enable\n";
417 }
418 if (info.uses_atomic_f16x2_add || info.uses_atomic_f16x2_min || info.uses_atomic_f16x2_max) {
419 header += "#extension GL_NV_shader_atomic_fp16_vector : enable\n";
420 }
421 if (info.uses_fp16) {
422 if (profile.support_gl_nv_gpu_shader_5) {
423 header += "#extension GL_NV_gpu_shader5 : enable\n";
424 }
425 if (profile.support_gl_amd_gpu_shader_half_float) {
426 header += "#extension GL_AMD_gpu_shader_half_float : enable\n";
427 }
428 }
429 if (info.uses_subgroup_invocation_id || info.uses_subgroup_mask || info.uses_subgroup_vote ||
430 info.uses_subgroup_shuffles || info.uses_fswzadd) {
431 header += "#extension GL_ARB_shader_ballot : enable\n"
432 "#extension GL_ARB_shader_group_vote : enable\n";
433 if (!info.uses_int64 && profile.support_int64) {
434 header += "#extension GL_ARB_gpu_shader_int64 : enable\n";
435 }
436 if (profile.support_gl_warp_intrinsics) {
437 header += "#extension GL_NV_shader_thread_shuffle : enable\n";
438 }
439 }
440 if ((info.stores[IR::Attribute::ViewportIndex] || info.stores[IR::Attribute::Layer]) &&
441 profile.support_viewport_index_layer_non_geometry && stage != Stage::Geometry) {
442 header += "#extension GL_ARB_shader_viewport_layer_array : enable\n";
443 }
444 if (info.uses_sparse_residency && profile.support_gl_sparse_textures) {
445 header += "#extension GL_ARB_sparse_texture2 : enable\n";
446 }
447 if (info.stores[IR::Attribute::ViewportMask] && profile.support_viewport_mask) {
448 header += "#extension GL_NV_viewport_array2 : enable\n";
449 }
450 if (info.uses_typeless_image_reads) {
451 header += "#extension GL_EXT_shader_image_load_formatted : enable\n";
452 }
453 if (info.uses_derivatives && profile.support_gl_derivative_control) {
454 header += "#extension GL_ARB_derivative_control : enable\n";
455 }
456 if (uses_geometry_passthrough) {
457 header += "#extension GL_NV_geometry_shader_passthrough : enable\n";
458 }
459}
460
461void EmitContext::DefineConstantBuffers(Bindings& bindings) {
462 if (info.constant_buffer_descriptors.empty()) {
463 return;
464 }
465 for (const auto& desc : info.constant_buffer_descriptors) {
466 header += fmt::format(
467 "layout(std140,binding={}) uniform {}_cbuf_{}{{vec4 {}_cbuf{}[{}];}};",
468 bindings.uniform_buffer, stage_name, desc.index, stage_name, desc.index, 4 * 1024);
469 bindings.uniform_buffer += desc.count;
470 }
471}
472
473void EmitContext::DefineStorageBuffers(Bindings& bindings) {
474 if (info.storage_buffers_descriptors.empty()) {
475 return;
476 }
477 u32 index{};
478 for (const auto& desc : info.storage_buffers_descriptors) {
479 header += fmt::format("layout(std430,binding={}) buffer {}_ssbo_{}{{uint {}_ssbo{}[];}};",
480 bindings.storage_buffer, stage_name, bindings.storage_buffer,
481 stage_name, index);
482 bindings.storage_buffer += desc.count;
483 index += desc.count;
484 }
485}
486
487void EmitContext::DefineGenericOutput(size_t index, u32 invocations) {
488 static constexpr std::string_view swizzle{"xyzw"};
489 const size_t base_index{static_cast<size_t>(IR::Attribute::Generic0X) + index * 4};
490 u32 element{0};
491 while (element < 4) {
492 std::string definition{fmt::format("layout(location={}", index)};
493 const u32 remainder{4 - element};
494 const TransformFeedbackVarying* xfb_varying{};
495 if (!runtime_info.xfb_varyings.empty()) {
496 xfb_varying = &runtime_info.xfb_varyings[base_index + element];
497 xfb_varying = xfb_varying && xfb_varying->components > 0 ? xfb_varying : nullptr;
498 }
499 const u32 num_components{xfb_varying ? xfb_varying->components : remainder};
500 if (element > 0) {
501 definition += fmt::format(",component={}", element);
502 }
503 if (xfb_varying) {
504 definition +=
505 fmt::format(",xfb_buffer={},xfb_stride={},xfb_offset={}", xfb_varying->buffer,
506 xfb_varying->stride, xfb_varying->offset);
507 }
508 std::string name{fmt::format("out_attr{}", index)};
509 if (num_components < 4 || element > 0) {
510 name += fmt::format("_{}", swizzle.substr(element, num_components));
511 }
512 const auto type{num_components == 1 ? "float" : fmt::format("vec{}", num_components)};
513 definition += fmt::format(")out {} {}{};", type, name, OutputDecorator(stage, invocations));
514 header += definition;
515
516 const GenericElementInfo element_info{
517 .name = name,
518 .first_element = element,
519 .num_components = num_components,
520 };
521 std::fill_n(output_generics[index].begin() + element, num_components, element_info);
522 element += num_components;
523 }
524}
525
526void EmitContext::DefineHelperFunctions() {
527 header += "\n#define ftoi floatBitsToInt\n#define ftou floatBitsToUint\n"
528 "#define itof intBitsToFloat\n#define utof uintBitsToFloat\n";
529 if (info.uses_global_increment || info.uses_shared_increment) {
530 header += "uint CasIncrement(uint op_a,uint op_b){return op_a>=op_b?0u:(op_a+1u);}";
531 }
532 if (info.uses_global_decrement || info.uses_shared_decrement) {
533 header += "uint CasDecrement(uint op_a,uint op_b){"
534 "return op_a==0||op_a>op_b?op_b:(op_a-1u);}";
535 }
536 if (info.uses_atomic_f32_add) {
537 header += "uint CasFloatAdd(uint op_a,float op_b){"
538 "return ftou(utof(op_a)+op_b);}";
539 }
540 if (info.uses_atomic_f32x2_add) {
541 header += "uint CasFloatAdd32x2(uint op_a,vec2 op_b){"
542 "return packHalf2x16(unpackHalf2x16(op_a)+op_b);}";
543 }
544 if (info.uses_atomic_f32x2_min) {
545 header += "uint CasFloatMin32x2(uint op_a,vec2 op_b){return "
546 "packHalf2x16(min(unpackHalf2x16(op_a),op_b));}";
547 }
548 if (info.uses_atomic_f32x2_max) {
549 header += "uint CasFloatMax32x2(uint op_a,vec2 op_b){return "
550 "packHalf2x16(max(unpackHalf2x16(op_a),op_b));}";
551 }
552 if (info.uses_atomic_f16x2_add) {
553 header += "uint CasFloatAdd16x2(uint op_a,f16vec2 op_b){return "
554 "packFloat2x16(unpackFloat2x16(op_a)+op_b);}";
555 }
556 if (info.uses_atomic_f16x2_min) {
557 header += "uint CasFloatMin16x2(uint op_a,f16vec2 op_b){return "
558 "packFloat2x16(min(unpackFloat2x16(op_a),op_b));}";
559 }
560 if (info.uses_atomic_f16x2_max) {
561 header += "uint CasFloatMax16x2(uint op_a,f16vec2 op_b){return "
562 "packFloat2x16(max(unpackFloat2x16(op_a),op_b));}";
563 }
564 if (info.uses_atomic_s32_min) {
565 header += "uint CasMinS32(uint op_a,uint op_b){return uint(min(int(op_a),int(op_b)));}";
566 }
567 if (info.uses_atomic_s32_max) {
568 header += "uint CasMaxS32(uint op_a,uint op_b){return uint(max(int(op_a),int(op_b)));}";
569 }
570 if (info.uses_global_memory && profile.support_int64) {
571 header += DefineGlobalMemoryFunctions();
572 }
573 if (info.loads_indexed_attributes) {
574 const bool is_array{stage == Stage::Geometry};
575 const auto vertex_arg{is_array ? ",uint vertex" : ""};
576 std::string func{
577 fmt::format("float IndexedAttrLoad(int offset{}){{int base_index=offset>>2;uint "
578 "masked_index=uint(base_index)&3u;switch(base_index>>2){{",
579 vertex_arg)};
580 if (info.loads.AnyComponent(IR::Attribute::PositionX)) {
581 const auto position_idx{is_array ? "gl_in[vertex]." : ""};
582 func += fmt::format("case {}:return {}{}[masked_index];",
583 static_cast<u32>(IR::Attribute::PositionX) >> 2, position_idx,
584 position_name);
585 }
586 const u32 base_attribute_value = static_cast<u32>(IR::Attribute::Generic0X) >> 2;
587 for (u32 index = 0; index < IR::NUM_GENERICS; ++index) {
588 if (!info.loads.Generic(index)) {
589 continue;
590 }
591 const auto vertex_idx{is_array ? "[vertex]" : ""};
592 func += fmt::format("case {}:return in_attr{}{}[masked_index];",
593 base_attribute_value + index, index, vertex_idx);
594 }
595 func += "default: return 0.0;}}";
596 header += func;
597 }
598 if (info.stores_indexed_attributes) {
599 // TODO
600 }
601}
602
603std::string EmitContext::DefineGlobalMemoryFunctions() {
604 const auto define_body{[&](std::string& func, size_t index, std::string_view return_statement) {
605 const auto& ssbo{info.storage_buffers_descriptors[index]};
606 const u32 size_cbuf_offset{ssbo.cbuf_offset + 8};
607 const auto ssbo_addr{fmt::format("ssbo_addr{}", index)};
608 const auto cbuf{fmt::format("{}_cbuf{}", stage_name, ssbo.cbuf_index)};
609 std::array<std::string, 2> addr_xy;
610 std::array<std::string, 2> size_xy;
611 for (size_t i = 0; i < addr_xy.size(); ++i) {
612 const auto addr_loc{ssbo.cbuf_offset + 4 * i};
613 const auto size_loc{size_cbuf_offset + 4 * i};
614 addr_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, addr_loc / 16, Swizzle(addr_loc));
615 size_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, size_loc / 16, Swizzle(size_loc));
616 }
617 const auto addr_pack{fmt::format("packUint2x32(uvec2({},{}))", addr_xy[0], addr_xy[1])};
618 const auto addr_statment{fmt::format("uint64_t {}={};", ssbo_addr, addr_pack)};
619 func += addr_statment;
620
621 const auto size_vec{fmt::format("uvec2({},{})", size_xy[0], size_xy[1])};
622 const auto comp_lhs{fmt::format("(addr>={})", ssbo_addr)};
623 const auto comp_rhs{fmt::format("(addr<({}+uint64_t({})))", ssbo_addr, size_vec)};
624 const auto comparison{fmt::format("if({}&&{}){{", comp_lhs, comp_rhs)};
625 func += comparison;
626
627 const auto ssbo_name{fmt::format("{}_ssbo{}", stage_name, index)};
628 func += fmt::format(fmt::runtime(return_statement), ssbo_name, ssbo_addr);
629 }};
630 std::string write_func{"void WriteGlobal32(uint64_t addr,uint data){"};
631 std::string write_func_64{"void WriteGlobal64(uint64_t addr,uvec2 data){"};
632 std::string write_func_128{"void WriteGlobal128(uint64_t addr,uvec4 data){"};
633 std::string load_func{"uint LoadGlobal32(uint64_t addr){"};
634 std::string load_func_64{"uvec2 LoadGlobal64(uint64_t addr){"};
635 std::string load_func_128{"uvec4 LoadGlobal128(uint64_t addr){"};
636 const size_t num_buffers{info.storage_buffers_descriptors.size()};
637 for (size_t index = 0; index < num_buffers; ++index) {
638 if (!info.nvn_buffer_used[index]) {
639 continue;
640 }
641 define_body(write_func, index, "{0}[uint(addr-{1})>>2]=data;return;}}");
642 define_body(write_func_64, index,
643 "{0}[uint(addr-{1})>>2]=data.x;{0}[uint(addr-{1}+4)>>2]=data.y;return;}}");
644 define_body(write_func_128, index,
645 "{0}[uint(addr-{1})>>2]=data.x;{0}[uint(addr-{1}+4)>>2]=data.y;{0}[uint("
646 "addr-{1}+8)>>2]=data.z;{0}[uint(addr-{1}+12)>>2]=data.w;return;}}");
647 define_body(load_func, index, "return {0}[uint(addr-{1})>>2];}}");
648 define_body(load_func_64, index,
649 "return uvec2({0}[uint(addr-{1})>>2],{0}[uint(addr-{1}+4)>>2]);}}");
650 define_body(load_func_128, index,
651 "return uvec4({0}[uint(addr-{1})>>2],{0}[uint(addr-{1}+4)>>2],{0}["
652 "uint(addr-{1}+8)>>2],{0}[uint(addr-{1}+12)>>2]);}}");
653 }
654 write_func += '}';
655 write_func_64 += '}';
656 write_func_128 += '}';
657 load_func += "return 0u;}";
658 load_func_64 += "return uvec2(0);}";
659 load_func_128 += "return uvec4(0);}";
660 return write_func + write_func_64 + write_func_128 + load_func + load_func_64 + load_func_128;
661}
662
663void EmitContext::SetupImages(Bindings& bindings) {
664 image_buffers.reserve(info.image_buffer_descriptors.size());
665 for (const auto& desc : info.image_buffer_descriptors) {
666 image_buffers.push_back({bindings.image, desc.count});
667 const auto format{ImageFormatString(desc.format)};
668 const auto qualifier{ImageAccessQualifier(desc.is_written, desc.is_read)};
669 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""};
670 header += fmt::format("layout(binding={}{}) uniform {}uimageBuffer img{}{};",
671 bindings.image, format, qualifier, bindings.image, array_decorator);
672 bindings.image += desc.count;
673 }
674 images.reserve(info.image_descriptors.size());
675 for (const auto& desc : info.image_descriptors) {
676 images.push_back({bindings.image, desc.count});
677 const auto format{ImageFormatString(desc.format)};
678 const auto image_type{ImageType(desc.type)};
679 const auto qualifier{ImageAccessQualifier(desc.is_written, desc.is_read)};
680 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""};
681 header += fmt::format("layout(binding={}{})uniform {}{} img{}{};", bindings.image, format,
682 qualifier, image_type, bindings.image, array_decorator);
683 bindings.image += desc.count;
684 }
685}
686
687void EmitContext::SetupTextures(Bindings& bindings) {
688 texture_buffers.reserve(info.texture_buffer_descriptors.size());
689 for (const auto& desc : info.texture_buffer_descriptors) {
690 texture_buffers.push_back({bindings.texture, desc.count});
691 const auto sampler_type{SamplerType(TextureType::Buffer, false)};
692 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""};
693 header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture,
694 sampler_type, bindings.texture, array_decorator);
695 bindings.texture += desc.count;
696 }
697 textures.reserve(info.texture_descriptors.size());
698 for (const auto& desc : info.texture_descriptors) {
699 textures.push_back({bindings.texture, desc.count});
700 const auto sampler_type{SamplerType(desc.type, desc.is_depth)};
701 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""};
702 header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture,
703 sampler_type, bindings.texture, array_decorator);
704 bindings.texture += desc.count;
705 }
706}
707
708void EmitContext::DefineConstants() {
709 if (info.uses_fswzadd) {
710 header += "const float FSWZ_A[]=float[4](-1.f,1.f,-1.f,0.f);"
711 "const float FSWZ_B[]=float[4](-1.f,-1.f,1.f,-1.f);";
712 }
713}
714
715} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_context.h b/src/shader_recompiler/backend/glsl/emit_context.h
new file mode 100644
index 000000000..d9b639d29
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_context.h
@@ -0,0 +1,174 @@
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 <string>
8#include <utility>
9#include <vector>
10
11#include <fmt/format.h>
12
13#include "shader_recompiler/backend/glsl/var_alloc.h"
14#include "shader_recompiler/stage.h"
15
16namespace Shader {
17struct Info;
18struct Profile;
19struct RuntimeInfo;
20} // namespace Shader
21
22namespace Shader::Backend {
23struct Bindings;
24}
25
26namespace Shader::IR {
27class Inst;
28struct Program;
29} // namespace Shader::IR
30
31namespace Shader::Backend::GLSL {
32
33struct GenericElementInfo {
34 std::string name;
35 u32 first_element{};
36 u32 num_components{};
37};
38
39struct TextureImageDefinition {
40 u32 binding;
41 u32 count;
42};
43
44class EmitContext {
45public:
46 explicit EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
47 const RuntimeInfo& runtime_info_);
48
49 template <GlslVarType type, typename... Args>
50 void Add(const char* format_str, IR::Inst& inst, Args&&... args) {
51 const auto var_def{var_alloc.AddDefine(inst, type)};
52 if (var_def.empty()) {
53 // skip assigment.
54 code += fmt::format(fmt::runtime(format_str + 3), std::forward<Args>(args)...);
55 } else {
56 code += fmt::format(fmt::runtime(format_str), var_def, std::forward<Args>(args)...);
57 }
58 // TODO: Remove this
59 code += '\n';
60 }
61
62 template <typename... Args>
63 void AddU1(const char* format_str, IR::Inst& inst, Args&&... args) {
64 Add<GlslVarType::U1>(format_str, inst, args...);
65 }
66
67 template <typename... Args>
68 void AddF16x2(const char* format_str, IR::Inst& inst, Args&&... args) {
69 Add<GlslVarType::F16x2>(format_str, inst, args...);
70 }
71
72 template <typename... Args>
73 void AddU32(const char* format_str, IR::Inst& inst, Args&&... args) {
74 Add<GlslVarType::U32>(format_str, inst, args...);
75 }
76
77 template <typename... Args>
78 void AddF32(const char* format_str, IR::Inst& inst, Args&&... args) {
79 Add<GlslVarType::F32>(format_str, inst, args...);
80 }
81
82 template <typename... Args>
83 void AddU64(const char* format_str, IR::Inst& inst, Args&&... args) {
84 Add<GlslVarType::U64>(format_str, inst, args...);
85 }
86
87 template <typename... Args>
88 void AddF64(const char* format_str, IR::Inst& inst, Args&&... args) {
89 Add<GlslVarType::F64>(format_str, inst, args...);
90 }
91
92 template <typename... Args>
93 void AddU32x2(const char* format_str, IR::Inst& inst, Args&&... args) {
94 Add<GlslVarType::U32x2>(format_str, inst, args...);
95 }
96
97 template <typename... Args>
98 void AddF32x2(const char* format_str, IR::Inst& inst, Args&&... args) {
99 Add<GlslVarType::F32x2>(format_str, inst, args...);
100 }
101
102 template <typename... Args>
103 void AddU32x3(const char* format_str, IR::Inst& inst, Args&&... args) {
104 Add<GlslVarType::U32x3>(format_str, inst, args...);
105 }
106
107 template <typename... Args>
108 void AddF32x3(const char* format_str, IR::Inst& inst, Args&&... args) {
109 Add<GlslVarType::F32x3>(format_str, inst, args...);
110 }
111
112 template <typename... Args>
113 void AddU32x4(const char* format_str, IR::Inst& inst, Args&&... args) {
114 Add<GlslVarType::U32x4>(format_str, inst, args...);
115 }
116
117 template <typename... Args>
118 void AddF32x4(const char* format_str, IR::Inst& inst, Args&&... args) {
119 Add<GlslVarType::F32x4>(format_str, inst, args...);
120 }
121
122 template <typename... Args>
123 void AddPrecF32(const char* format_str, IR::Inst& inst, Args&&... args) {
124 Add<GlslVarType::PrecF32>(format_str, inst, args...);
125 }
126
127 template <typename... Args>
128 void AddPrecF64(const char* format_str, IR::Inst& inst, Args&&... args) {
129 Add<GlslVarType::PrecF64>(format_str, inst, args...);
130 }
131
132 template <typename... Args>
133 void Add(const char* format_str, Args&&... args) {
134 code += fmt::format(fmt::runtime(format_str), std::forward<Args>(args)...);
135 // TODO: Remove this
136 code += '\n';
137 }
138
139 std::string header;
140 std::string code;
141 VarAlloc var_alloc;
142 const Info& info;
143 const Profile& profile;
144 const RuntimeInfo& runtime_info;
145
146 Stage stage{};
147 std::string_view stage_name = "invalid";
148 std::string_view position_name = "gl_Position";
149
150 std::vector<TextureImageDefinition> texture_buffers;
151 std::vector<TextureImageDefinition> image_buffers;
152 std::vector<TextureImageDefinition> textures;
153 std::vector<TextureImageDefinition> images;
154 std::array<std::array<GenericElementInfo, 4>, 32> output_generics{};
155
156 u32 num_safety_loop_vars{};
157
158 bool uses_y_direction{};
159 bool uses_cc_carry{};
160 bool uses_geometry_passthrough{};
161
162private:
163 void SetupExtensions();
164 void DefineConstantBuffers(Bindings& bindings);
165 void DefineStorageBuffers(Bindings& bindings);
166 void DefineGenericOutput(size_t index, u32 invocations);
167 void DefineHelperFunctions();
168 void DefineConstants();
169 std::string DefineGlobalMemoryFunctions();
170 void SetupImages(Bindings& bindings);
171 void SetupTextures(Bindings& bindings);
172};
173
174} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl.cpp b/src/shader_recompiler/backend/glsl/emit_glsl.cpp
new file mode 100644
index 000000000..8a430d573
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl.cpp
@@ -0,0 +1,252 @@
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 <string>
7#include <tuple>
8#include <type_traits>
9
10#include "common/div_ceil.h"
11#include "common/settings.h"
12#include "shader_recompiler/backend/glsl/emit_context.h"
13#include "shader_recompiler/backend/glsl/emit_glsl.h"
14#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
15#include "shader_recompiler/frontend/ir/ir_emitter.h"
16
17namespace Shader::Backend::GLSL {
18namespace {
19template <class Func>
20struct FuncTraits {};
21
22template <class ReturnType_, class... Args>
23struct FuncTraits<ReturnType_ (*)(Args...)> {
24 using ReturnType = ReturnType_;
25
26 static constexpr size_t NUM_ARGS = sizeof...(Args);
27
28 template <size_t I>
29 using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
30};
31
32template <auto func, typename... Args>
33void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) {
34 inst->SetDefinition<Id>(func(ctx, std::forward<Args>(args)...));
35}
36
37template <typename ArgType>
38auto Arg(EmitContext& ctx, const IR::Value& arg) {
39 if constexpr (std::is_same_v<ArgType, std::string_view>) {
40 return ctx.var_alloc.Consume(arg);
41 } else if constexpr (std::is_same_v<ArgType, const IR::Value&>) {
42 return arg;
43 } else if constexpr (std::is_same_v<ArgType, u32>) {
44 return arg.U32();
45 } else if constexpr (std::is_same_v<ArgType, IR::Attribute>) {
46 return arg.Attribute();
47 } else if constexpr (std::is_same_v<ArgType, IR::Patch>) {
48 return arg.Patch();
49 } else if constexpr (std::is_same_v<ArgType, IR::Reg>) {
50 return arg.Reg();
51 }
52}
53
54template <auto func, bool is_first_arg_inst, size_t... I>
55void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
56 using Traits = FuncTraits<decltype(func)>;
57 if constexpr (std::is_same_v<typename Traits::ReturnType, Id>) {
58 if constexpr (is_first_arg_inst) {
59 SetDefinition<func>(
60 ctx, inst, *inst,
61 Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
62 } else {
63 SetDefinition<func>(
64 ctx, inst, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
65 }
66 } else {
67 if constexpr (is_first_arg_inst) {
68 func(ctx, *inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
69 } else {
70 func(ctx, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
71 }
72 }
73}
74
75template <auto func>
76void Invoke(EmitContext& ctx, IR::Inst* inst) {
77 using Traits = FuncTraits<decltype(func)>;
78 static_assert(Traits::NUM_ARGS >= 1, "Insufficient arguments");
79 if constexpr (Traits::NUM_ARGS == 1) {
80 Invoke<func, false>(ctx, inst, std::make_index_sequence<0>{});
81 } else {
82 using FirstArgType = typename Traits::template ArgType<1>;
83 static constexpr bool is_first_arg_inst = std::is_same_v<FirstArgType, IR::Inst&>;
84 using Indices = std::make_index_sequence<Traits::NUM_ARGS - (is_first_arg_inst ? 2 : 1)>;
85 Invoke<func, is_first_arg_inst>(ctx, inst, Indices{});
86 }
87}
88
89void EmitInst(EmitContext& ctx, IR::Inst* inst) {
90 switch (inst->GetOpcode()) {
91#define OPCODE(name, result_type, ...) \
92 case IR::Opcode::name: \
93 return Invoke<&Emit##name>(ctx, inst);
94#include "shader_recompiler/frontend/ir/opcodes.inc"
95#undef OPCODE
96 }
97 throw LogicError("Invalid opcode {}", inst->GetOpcode());
98}
99
100bool IsReference(IR::Inst& inst) {
101 return inst.GetOpcode() == IR::Opcode::Reference;
102}
103
104void PrecolorInst(IR::Inst& phi) {
105 // Insert phi moves before references to avoid overwritting other phis
106 const size_t num_args{phi.NumArgs()};
107 for (size_t i = 0; i < num_args; ++i) {
108 IR::Block& phi_block{*phi.PhiBlock(i)};
109 auto it{std::find_if_not(phi_block.rbegin(), phi_block.rend(), IsReference).base()};
110 IR::IREmitter ir{phi_block, it};
111 const IR::Value arg{phi.Arg(i)};
112 if (arg.IsImmediate()) {
113 ir.PhiMove(phi, arg);
114 } else {
115 ir.PhiMove(phi, IR::Value{arg.InstRecursive()});
116 }
117 }
118 for (size_t i = 0; i < num_args; ++i) {
119 IR::IREmitter{*phi.PhiBlock(i)}.Reference(IR::Value{&phi});
120 }
121}
122
123void Precolor(const IR::Program& program) {
124 for (IR::Block* const block : program.blocks) {
125 for (IR::Inst& phi : block->Instructions()) {
126 if (!IR::IsPhi(phi)) {
127 break;
128 }
129 PrecolorInst(phi);
130 }
131 }
132}
133
134void EmitCode(EmitContext& ctx, const IR::Program& program) {
135 for (const IR::AbstractSyntaxNode& node : program.syntax_list) {
136 switch (node.type) {
137 case IR::AbstractSyntaxNode::Type::Block:
138 for (IR::Inst& inst : node.data.block->Instructions()) {
139 EmitInst(ctx, &inst);
140 }
141 break;
142 case IR::AbstractSyntaxNode::Type::If:
143 ctx.Add("if({}){{", ctx.var_alloc.Consume(node.data.if_node.cond));
144 break;
145 case IR::AbstractSyntaxNode::Type::EndIf:
146 ctx.Add("}}");
147 break;
148 case IR::AbstractSyntaxNode::Type::Break:
149 if (node.data.break_node.cond.IsImmediate()) {
150 if (node.data.break_node.cond.U1()) {
151 ctx.Add("break;");
152 }
153 } else {
154 ctx.Add("if({}){{break;}}", ctx.var_alloc.Consume(node.data.break_node.cond));
155 }
156 break;
157 case IR::AbstractSyntaxNode::Type::Return:
158 case IR::AbstractSyntaxNode::Type::Unreachable:
159 ctx.Add("return;");
160 break;
161 case IR::AbstractSyntaxNode::Type::Loop:
162 ctx.Add("for(;;){{");
163 break;
164 case IR::AbstractSyntaxNode::Type::Repeat:
165 if (Settings::values.disable_shader_loop_safety_checks) {
166 ctx.Add("if(!{}){{break;}}}}", ctx.var_alloc.Consume(node.data.repeat.cond));
167 } else {
168 ctx.Add("if(--loop{}<0 || !{}){{break;}}}}", ctx.num_safety_loop_vars++,
169 ctx.var_alloc.Consume(node.data.repeat.cond));
170 }
171 break;
172 default:
173 throw NotImplementedException("AbstractSyntaxNode Type {}", node.type);
174 }
175 }
176}
177
178std::string GlslVersionSpecifier(const EmitContext& ctx) {
179 if (ctx.uses_y_direction || ctx.info.stores.Legacy() || ctx.info.loads.Legacy()) {
180 return " compatibility";
181 }
182 return "";
183}
184
185bool IsPreciseType(GlslVarType type) {
186 switch (type) {
187 case GlslVarType::PrecF32:
188 case GlslVarType::PrecF64:
189 return true;
190 default:
191 return false;
192 }
193}
194
195void DefineVariables(const EmitContext& ctx, std::string& header) {
196 for (u32 i = 0; i < static_cast<u32>(GlslVarType::Void); ++i) {
197 const auto type{static_cast<GlslVarType>(i)};
198 const auto& tracker{ctx.var_alloc.GetUseTracker(type)};
199 const auto type_name{ctx.var_alloc.GetGlslType(type)};
200 const bool has_precise_bug{ctx.stage == Stage::Fragment && ctx.profile.has_gl_precise_bug};
201 const auto precise{!has_precise_bug && IsPreciseType(type) ? "precise " : ""};
202 // Temps/return types that are never used are stored at index 0
203 if (tracker.uses_temp) {
204 header += fmt::format("{}{} t{}={}(0);", precise, type_name,
205 ctx.var_alloc.Representation(0, type), type_name);
206 }
207 for (u32 index = 0; index < tracker.num_used; ++index) {
208 header += fmt::format("{}{} {}={}(0);", precise, type_name,
209 ctx.var_alloc.Representation(index, type), type_name);
210 }
211 }
212 for (u32 i = 0; i < ctx.num_safety_loop_vars; ++i) {
213 header += fmt::format("int loop{}=0x2000;", i);
214 }
215}
216} // Anonymous namespace
217
218std::string EmitGLSL(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program,
219 Bindings& bindings) {
220 EmitContext ctx{program, bindings, profile, runtime_info};
221 Precolor(program);
222 EmitCode(ctx, program);
223 const std::string version{fmt::format("#version 450{}\n", GlslVersionSpecifier(ctx))};
224 ctx.header.insert(0, version);
225 if (program.shared_memory_size > 0) {
226 const auto requested_size{program.shared_memory_size};
227 const auto max_size{profile.gl_max_compute_smem_size};
228 const bool needs_clamp{requested_size > max_size};
229 if (needs_clamp) {
230 LOG_WARNING(Shader_GLSL, "Requested shared memory size ({}) exceeds device limit ({})",
231 requested_size, max_size);
232 }
233 const auto smem_size{needs_clamp ? max_size : requested_size};
234 ctx.header += fmt::format("shared uint smem[{}];", Common::DivCeil(smem_size, 4U));
235 }
236 ctx.header += "void main(){\n";
237 if (program.local_memory_size > 0) {
238 ctx.header += fmt::format("uint lmem[{}];", Common::DivCeil(program.local_memory_size, 4U));
239 }
240 DefineVariables(ctx, ctx.header);
241 if (ctx.uses_cc_carry) {
242 ctx.header += "uint carry;";
243 }
244 if (program.info.uses_subgroup_shuffles) {
245 ctx.header += "bool shfl_in_bounds;";
246 }
247 ctx.code.insert(0, ctx.header);
248 ctx.code += '}';
249 return ctx.code;
250}
251
252} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl.h b/src/shader_recompiler/backend/glsl/emit_glsl.h
new file mode 100644
index 000000000..20e5719e6
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl.h
@@ -0,0 +1,24 @@
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 <string>
8
9#include "shader_recompiler/backend/bindings.h"
10#include "shader_recompiler/frontend/ir/program.h"
11#include "shader_recompiler/profile.h"
12#include "shader_recompiler/runtime_info.h"
13
14namespace Shader::Backend::GLSL {
15
16[[nodiscard]] std::string EmitGLSL(const Profile& profile, const RuntimeInfo& runtime_info,
17 IR::Program& program, Bindings& bindings);
18
19[[nodiscard]] inline std::string EmitGLSL(const Profile& profile, IR::Program& program) {
20 Bindings binding;
21 return EmitGLSL(profile, {}, program, binding);
22}
23
24} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp
new file mode 100644
index 000000000..772acc5a4
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp
@@ -0,0 +1,418 @@
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 <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10
11namespace Shader::Backend::GLSL {
12namespace {
13constexpr char cas_loop[]{
14 "for (;;){{uint old={};{}=atomicCompSwap({},old,{}({},{}));if({}==old){{break;}}}}"};
15
16void SharedCasFunction(EmitContext& ctx, IR::Inst& inst, std::string_view offset,
17 std::string_view value, std::string_view function) {
18 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
19 const std::string smem{fmt::format("smem[{}>>2]", offset)};
20 ctx.Add(cas_loop, smem, ret, smem, function, smem, value, ret);
21}
22
23void SsboCasFunction(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
24 const IR::Value& offset, std::string_view value, std::string_view function) {
25 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
26 const std::string ssbo{fmt::format("{}_ssbo{}[{}>>2]", ctx.stage_name, binding.U32(),
27 ctx.var_alloc.Consume(offset))};
28 ctx.Add(cas_loop, ssbo, ret, ssbo, function, ssbo, value, ret);
29}
30
31void SsboCasFunctionF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
32 const IR::Value& offset, std::string_view value,
33 std::string_view function) {
34 const std::string ssbo{fmt::format("{}_ssbo{}[{}>>2]", ctx.stage_name, binding.U32(),
35 ctx.var_alloc.Consume(offset))};
36 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
37 ctx.Add(cas_loop, ssbo, ret, ssbo, function, ssbo, value, ret);
38 ctx.AddF32("{}=utof({});", inst, ret);
39}
40} // Anonymous namespace
41
42void EmitSharedAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
43 std::string_view value) {
44 ctx.AddU32("{}=atomicAdd(smem[{}>>2],{});", inst, pointer_offset, value);
45}
46
47void EmitSharedAtomicSMin32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
48 std::string_view value) {
49 const std::string u32_value{fmt::format("uint({})", value)};
50 SharedCasFunction(ctx, inst, pointer_offset, u32_value, "CasMinS32");
51}
52
53void EmitSharedAtomicUMin32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
54 std::string_view value) {
55 ctx.AddU32("{}=atomicMin(smem[{}>>2],{});", inst, pointer_offset, value);
56}
57
58void EmitSharedAtomicSMax32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
59 std::string_view value) {
60 const std::string u32_value{fmt::format("uint({})", value)};
61 SharedCasFunction(ctx, inst, pointer_offset, u32_value, "CasMaxS32");
62}
63
64void EmitSharedAtomicUMax32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
65 std::string_view value) {
66 ctx.AddU32("{}=atomicMax(smem[{}>>2],{});", inst, pointer_offset, value);
67}
68
69void EmitSharedAtomicInc32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
70 std::string_view value) {
71 SharedCasFunction(ctx, inst, pointer_offset, value, "CasIncrement");
72}
73
74void EmitSharedAtomicDec32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
75 std::string_view value) {
76 SharedCasFunction(ctx, inst, pointer_offset, value, "CasDecrement");
77}
78
79void EmitSharedAtomicAnd32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
80 std::string_view value) {
81 ctx.AddU32("{}=atomicAnd(smem[{}>>2],{});", inst, pointer_offset, value);
82}
83
84void EmitSharedAtomicOr32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
85 std::string_view value) {
86 ctx.AddU32("{}=atomicOr(smem[{}>>2],{});", inst, pointer_offset, value);
87}
88
89void EmitSharedAtomicXor32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
90 std::string_view value) {
91 ctx.AddU32("{}=atomicXor(smem[{}>>2],{});", inst, pointer_offset, value);
92}
93
94void EmitSharedAtomicExchange32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
95 std::string_view value) {
96 ctx.AddU32("{}=atomicExchange(smem[{}>>2],{});", inst, pointer_offset, value);
97}
98
99void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
100 std::string_view value) {
101 LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
102 ctx.AddU64("{}=packUint2x32(uvec2(smem[{}>>2],smem[({}+4)>>2]));", inst, pointer_offset,
103 pointer_offset);
104 ctx.Add("smem[{}>>2]=unpackUint2x32({}).x;smem[({}+4)>>2]=unpackUint2x32({}).y;",
105 pointer_offset, value, pointer_offset, value);
106}
107
108void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
109 const IR::Value& offset, std::string_view value) {
110 ctx.AddU32("{}=atomicAdd({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
111 ctx.var_alloc.Consume(offset), value);
112}
113
114void EmitStorageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
115 const IR::Value& offset, std::string_view value) {
116 const std::string u32_value{fmt::format("uint({})", value)};
117 SsboCasFunction(ctx, inst, binding, offset, u32_value, "CasMinS32");
118}
119
120void EmitStorageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
121 const IR::Value& offset, std::string_view value) {
122 ctx.AddU32("{}=atomicMin({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
123 ctx.var_alloc.Consume(offset), value);
124}
125
126void EmitStorageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
127 const IR::Value& offset, std::string_view value) {
128 const std::string u32_value{fmt::format("uint({})", value)};
129 SsboCasFunction(ctx, inst, binding, offset, u32_value, "CasMaxS32");
130}
131
132void EmitStorageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
133 const IR::Value& offset, std::string_view value) {
134 ctx.AddU32("{}=atomicMax({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
135 ctx.var_alloc.Consume(offset), value);
136}
137
138void EmitStorageAtomicInc32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
139 const IR::Value& offset, std::string_view value) {
140 SsboCasFunction(ctx, inst, binding, offset, value, "CasIncrement");
141}
142
143void EmitStorageAtomicDec32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
144 const IR::Value& offset, std::string_view value) {
145 SsboCasFunction(ctx, inst, binding, offset, value, "CasDecrement");
146}
147
148void EmitStorageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
149 const IR::Value& offset, std::string_view value) {
150 ctx.AddU32("{}=atomicAnd({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
151 ctx.var_alloc.Consume(offset), value);
152}
153
154void EmitStorageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
155 const IR::Value& offset, std::string_view value) {
156 ctx.AddU32("{}=atomicOr({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
157 ctx.var_alloc.Consume(offset), value);
158}
159
160void EmitStorageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
161 const IR::Value& offset, std::string_view value) {
162 ctx.AddU32("{}=atomicXor({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
163 ctx.var_alloc.Consume(offset), value);
164}
165
166void EmitStorageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
167 const IR::Value& offset, std::string_view value) {
168 ctx.AddU32("{}=atomicExchange({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
169 ctx.var_alloc.Consume(offset), value);
170}
171
172void EmitStorageAtomicIAdd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
173 const IR::Value& offset, std::string_view value) {
174 LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
175 ctx.AddU64("{}=packUint2x32(uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]));", inst,
176 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
177 binding.U32(), ctx.var_alloc.Consume(offset));
178 ctx.Add("{}_ssbo{}[{}>>2]+=unpackUint2x32({}).x;{}_ssbo{}[({}>>2)+1]+=unpackUint2x32({}).y;",
179 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value, ctx.stage_name,
180 binding.U32(), ctx.var_alloc.Consume(offset), value);
181}
182
183void EmitStorageAtomicSMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
184 const IR::Value& offset, std::string_view value) {
185 LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
186 ctx.AddU64("{}=packInt2x32(ivec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]));", inst,
187 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
188 binding.U32(), ctx.var_alloc.Consume(offset));
189 ctx.Add("for(int i=0;i<2;++i){{ "
190 "{}_ssbo{}[({}>>2)+i]=uint(min(int({}_ssbo{}[({}>>2)+i]),unpackInt2x32(int64_t({}))[i])"
191 ");}}",
192 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
193 binding.U32(), ctx.var_alloc.Consume(offset), value);
194}
195
196void EmitStorageAtomicUMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
197 const IR::Value& offset, std::string_view value) {
198 LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
199 ctx.AddU64("{}=packUint2x32(uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]));", inst,
200 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
201 binding.U32(), ctx.var_alloc.Consume(offset));
202 ctx.Add("for(int i=0;i<2;++i){{ "
203 "{}_ssbo{}[({}>>2)+i]=min({}_ssbo{}[({}>>2)+i],unpackUint2x32(uint64_t({}))[i]);}}",
204 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
205 binding.U32(), ctx.var_alloc.Consume(offset), value);
206}
207
208void EmitStorageAtomicSMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
209 const IR::Value& offset, std::string_view value) {
210 LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
211 ctx.AddU64("{}=packInt2x32(ivec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]));", inst,
212 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
213 binding.U32(), ctx.var_alloc.Consume(offset));
214 ctx.Add("for(int i=0;i<2;++i){{ "
215 "{}_ssbo{}[({}>>2)+i]=uint(max(int({}_ssbo{}[({}>>2)+i]),unpackInt2x32(int64_t({}))[i])"
216 ");}}",
217 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
218 binding.U32(), ctx.var_alloc.Consume(offset), value);
219}
220
221void EmitStorageAtomicUMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
222 const IR::Value& offset, std::string_view value) {
223 LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
224 ctx.AddU64("{}=packUint2x32(uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]));", inst,
225 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
226 binding.U32(), ctx.var_alloc.Consume(offset));
227 ctx.Add("for(int "
228 "i=0;i<2;++i){{{}_ssbo{}[({}>>2)+i]=max({}_ssbo{}[({}>>2)+i],unpackUint2x32(uint64_t({}"
229 "))[i]);}}",
230 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
231 binding.U32(), ctx.var_alloc.Consume(offset), value);
232}
233
234void EmitStorageAtomicAnd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
235 const IR::Value& offset, std::string_view value) {
236 ctx.AddU64(
237 "{}=packUint2x32(uvec2(atomicAnd({}_ssbo{}[{}>>2],unpackUint2x32({}).x),atomicAnd({}_"
238 "ssbo{}[({}>>2)+1],unpackUint2x32({}).y)));",
239 inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value, ctx.stage_name,
240 binding.U32(), ctx.var_alloc.Consume(offset), value);
241}
242
243void EmitStorageAtomicOr64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
244 const IR::Value& offset, std::string_view value) {
245 ctx.AddU64("{}=packUint2x32(uvec2(atomicOr({}_ssbo{}[{}>>2],unpackUint2x32({}).x),atomicOr({}_"
246 "ssbo{}[({}>>2)+1],unpackUint2x32({}).y)));",
247 inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value,
248 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value);
249}
250
251void EmitStorageAtomicXor64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
252 const IR::Value& offset, std::string_view value) {
253 ctx.AddU64(
254 "{}=packUint2x32(uvec2(atomicXor({}_ssbo{}[{}>>2],unpackUint2x32({}).x),atomicXor({}_"
255 "ssbo{}[({}>>2)+1],unpackUint2x32({}).y)));",
256 inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value, ctx.stage_name,
257 binding.U32(), ctx.var_alloc.Consume(offset), value);
258}
259
260void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
261 const IR::Value& offset, std::string_view value) {
262 ctx.AddU64("{}=packUint2x32(uvec2(atomicExchange({}_ssbo{}[{}>>2],unpackUint2x32({}).x),"
263 "atomicExchange({}_ssbo{}[({}>>2)+1],unpackUint2x32({}).y)));",
264 inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value,
265 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value);
266}
267
268void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
269 const IR::Value& offset, std::string_view value) {
270 SsboCasFunctionF32(ctx, inst, binding, offset, value, "CasFloatAdd");
271}
272
273void EmitStorageAtomicAddF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
274 const IR::Value& offset, std::string_view value) {
275 SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatAdd16x2");
276}
277
278void EmitStorageAtomicAddF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
279 const IR::Value& offset, std::string_view value) {
280 SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatAdd32x2");
281}
282
283void EmitStorageAtomicMinF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
284 const IR::Value& offset, std::string_view value) {
285 SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatMin16x2");
286}
287
288void EmitStorageAtomicMinF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
289 const IR::Value& offset, std::string_view value) {
290 SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatMin32x2");
291}
292
293void EmitStorageAtomicMaxF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
294 const IR::Value& offset, std::string_view value) {
295 SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatMax16x2");
296}
297
298void EmitStorageAtomicMaxF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
299 const IR::Value& offset, std::string_view value) {
300 SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatMax32x2");
301}
302
303void EmitGlobalAtomicIAdd32(EmitContext&) {
304 throw NotImplementedException("GLSL Instrucion");
305}
306
307void EmitGlobalAtomicSMin32(EmitContext&) {
308 throw NotImplementedException("GLSL Instrucion");
309}
310
311void EmitGlobalAtomicUMin32(EmitContext&) {
312 throw NotImplementedException("GLSL Instrucion");
313}
314
315void EmitGlobalAtomicSMax32(EmitContext&) {
316 throw NotImplementedException("GLSL Instrucion");
317}
318
319void EmitGlobalAtomicUMax32(EmitContext&) {
320 throw NotImplementedException("GLSL Instrucion");
321}
322
323void EmitGlobalAtomicInc32(EmitContext&) {
324 throw NotImplementedException("GLSL Instrucion");
325}
326
327void EmitGlobalAtomicDec32(EmitContext&) {
328 throw NotImplementedException("GLSL Instrucion");
329}
330
331void EmitGlobalAtomicAnd32(EmitContext&) {
332 throw NotImplementedException("GLSL Instrucion");
333}
334
335void EmitGlobalAtomicOr32(EmitContext&) {
336 throw NotImplementedException("GLSL Instrucion");
337}
338
339void EmitGlobalAtomicXor32(EmitContext&) {
340 throw NotImplementedException("GLSL Instrucion");
341}
342
343void EmitGlobalAtomicExchange32(EmitContext&) {
344 throw NotImplementedException("GLSL Instrucion");
345}
346
347void EmitGlobalAtomicIAdd64(EmitContext&) {
348 throw NotImplementedException("GLSL Instrucion");
349}
350
351void EmitGlobalAtomicSMin64(EmitContext&) {
352 throw NotImplementedException("GLSL Instrucion");
353}
354
355void EmitGlobalAtomicUMin64(EmitContext&) {
356 throw NotImplementedException("GLSL Instrucion");
357}
358
359void EmitGlobalAtomicSMax64(EmitContext&) {
360 throw NotImplementedException("GLSL Instrucion");
361}
362
363void EmitGlobalAtomicUMax64(EmitContext&) {
364 throw NotImplementedException("GLSL Instrucion");
365}
366
367void EmitGlobalAtomicInc64(EmitContext&) {
368 throw NotImplementedException("GLSL Instrucion");
369}
370
371void EmitGlobalAtomicDec64(EmitContext&) {
372 throw NotImplementedException("GLSL Instrucion");
373}
374
375void EmitGlobalAtomicAnd64(EmitContext&) {
376 throw NotImplementedException("GLSL Instrucion");
377}
378
379void EmitGlobalAtomicOr64(EmitContext&) {
380 throw NotImplementedException("GLSL Instrucion");
381}
382
383void EmitGlobalAtomicXor64(EmitContext&) {
384 throw NotImplementedException("GLSL Instrucion");
385}
386
387void EmitGlobalAtomicExchange64(EmitContext&) {
388 throw NotImplementedException("GLSL Instrucion");
389}
390
391void EmitGlobalAtomicAddF32(EmitContext&) {
392 throw NotImplementedException("GLSL Instrucion");
393}
394
395void EmitGlobalAtomicAddF16x2(EmitContext&) {
396 throw NotImplementedException("GLSL Instrucion");
397}
398
399void EmitGlobalAtomicAddF32x2(EmitContext&) {
400 throw NotImplementedException("GLSL Instrucion");
401}
402
403void EmitGlobalAtomicMinF16x2(EmitContext&) {
404 throw NotImplementedException("GLSL Instrucion");
405}
406
407void EmitGlobalAtomicMinF32x2(EmitContext&) {
408 throw NotImplementedException("GLSL Instrucion");
409}
410
411void EmitGlobalAtomicMaxF16x2(EmitContext&) {
412 throw NotImplementedException("GLSL Instrucion");
413}
414
415void EmitGlobalAtomicMaxF32x2(EmitContext&) {
416 throw NotImplementedException("GLSL Instrucion");
417}
418} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp
new file mode 100644
index 000000000..e1d1b558e
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp
@@ -0,0 +1,21 @@
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 "shader_recompiler/backend/glsl/emit_context.h"
6#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
7#include "shader_recompiler/frontend/ir/value.h"
8
9namespace Shader::Backend::GLSL {
10void EmitBarrier(EmitContext& ctx) {
11 ctx.Add("barrier();");
12}
13
14void EmitWorkgroupMemoryBarrier(EmitContext& ctx) {
15 ctx.Add("groupMemoryBarrier();");
16}
17
18void EmitDeviceMemoryBarrier(EmitContext& ctx) {
19 ctx.Add("memoryBarrier();");
20}
21} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
new file mode 100644
index 000000000..3c1714e89
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
@@ -0,0 +1,94 @@
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 <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10
11namespace Shader::Backend::GLSL {
12namespace {
13void Alias(IR::Inst& inst, const IR::Value& value) {
14 if (value.IsImmediate()) {
15 return;
16 }
17 IR::Inst& value_inst{*value.InstRecursive()};
18 value_inst.DestructiveAddUsage(inst.UseCount());
19 value_inst.DestructiveRemoveUsage();
20 inst.SetDefinition(value_inst.Definition<Id>());
21}
22} // Anonymous namespace
23
24void EmitIdentity(EmitContext&, IR::Inst& inst, const IR::Value& value) {
25 Alias(inst, value);
26}
27
28void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value) {
29 // Fake one usage to get a real variable out of the condition
30 inst.DestructiveAddUsage(1);
31 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U1)};
32 const auto input{ctx.var_alloc.Consume(value)};
33 if (ret != input) {
34 ctx.Add("{}={};", ret, input);
35 }
36}
37
38void EmitBitCastU16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) {
39 NotImplemented();
40}
41
42void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
43 ctx.AddU32("{}=ftou({});", inst, value);
44}
45
46void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
47 ctx.AddU64("{}=doubleBitsToUint64({});", inst, value);
48}
49
50void EmitBitCastF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) {
51 NotImplemented();
52}
53
54void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
55 ctx.AddF32("{}=utof({});", inst, value);
56}
57
58void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
59 ctx.AddF64("{}=uint64BitsToDouble({});", inst, value);
60}
61
62void EmitPackUint2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
63 ctx.AddU64("{}=packUint2x32({});", inst, value);
64}
65
66void EmitUnpackUint2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
67 ctx.AddU32x2("{}=unpackUint2x32({});", inst, value);
68}
69
70void EmitPackFloat2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
71 ctx.AddU32("{}=packFloat2x16({});", inst, value);
72}
73
74void EmitUnpackFloat2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
75 ctx.AddF16x2("{}=unpackFloat2x16({});", inst, value);
76}
77
78void EmitPackHalf2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
79 ctx.AddU32("{}=packHalf2x16({});", inst, value);
80}
81
82void EmitUnpackHalf2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
83 ctx.AddF32x2("{}=unpackHalf2x16({});", inst, value);
84}
85
86void EmitPackDouble2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
87 ctx.AddF64("{}=packDouble2x32({});", inst, value);
88}
89
90void EmitUnpackDouble2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
91 ctx.AddU32x2("{}=unpackDouble2x32({});", inst, value);
92}
93
94} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_composite.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_composite.cpp
new file mode 100644
index 000000000..49a66e3ec
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_composite.cpp
@@ -0,0 +1,219 @@
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 <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10
11namespace Shader::Backend::GLSL {
12namespace {
13constexpr std::string_view SWIZZLE{"xyzw"};
14void CompositeInsert(EmitContext& ctx, std::string_view result, std::string_view composite,
15 std::string_view object, u32 index) {
16 if (result == composite) {
17 // The result is aliased with the composite
18 ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
19 } else {
20 ctx.Add("{}={};{}.{}={};", result, composite, result, SWIZZLE[index], object);
21 }
22}
23} // Anonymous namespace
24
25void EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
26 std::string_view e2) {
27 ctx.AddU32x2("{}=uvec2({},{});", inst, e1, e2);
28}
29
30void EmitCompositeConstructU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
31 std::string_view e2, std::string_view e3) {
32 ctx.AddU32x3("{}=uvec3({},{},{});", inst, e1, e2, e3);
33}
34
35void EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
36 std::string_view e2, std::string_view e3, std::string_view e4) {
37 ctx.AddU32x4("{}=uvec4({},{},{},{});", inst, e1, e2, e3, e4);
38}
39
40void EmitCompositeExtractU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
41 u32 index) {
42 ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]);
43}
44
45void EmitCompositeExtractU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
46 u32 index) {
47 ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]);
48}
49
50void EmitCompositeExtractU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
51 u32 index) {
52 ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]);
53}
54
55void EmitCompositeInsertU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
56 std::string_view object, u32 index) {
57 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)};
58 CompositeInsert(ctx, ret, composite, object, index);
59}
60
61void EmitCompositeInsertU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
62 std::string_view object, u32 index) {
63 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x3)};
64 CompositeInsert(ctx, ret, composite, object, index);
65}
66
67void EmitCompositeInsertU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
68 std::string_view object, u32 index) {
69 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x4)};
70 CompositeInsert(ctx, ret, composite, object, index);
71}
72
73void EmitCompositeConstructF16x2([[maybe_unused]] EmitContext& ctx,
74 [[maybe_unused]] std::string_view e1,
75 [[maybe_unused]] std::string_view e2) {
76 NotImplemented();
77}
78
79void EmitCompositeConstructF16x3([[maybe_unused]] EmitContext& ctx,
80 [[maybe_unused]] std::string_view e1,
81 [[maybe_unused]] std::string_view e2,
82 [[maybe_unused]] std::string_view e3) {
83 NotImplemented();
84}
85
86void EmitCompositeConstructF16x4([[maybe_unused]] EmitContext& ctx,
87 [[maybe_unused]] std::string_view e1,
88 [[maybe_unused]] std::string_view e2,
89 [[maybe_unused]] std::string_view e3,
90 [[maybe_unused]] std::string_view e4) {
91 NotImplemented();
92}
93
94void EmitCompositeExtractF16x2([[maybe_unused]] EmitContext& ctx,
95 [[maybe_unused]] std::string_view composite,
96 [[maybe_unused]] u32 index) {
97 NotImplemented();
98}
99
100void EmitCompositeExtractF16x3([[maybe_unused]] EmitContext& ctx,
101 [[maybe_unused]] std::string_view composite,
102 [[maybe_unused]] u32 index) {
103 NotImplemented();
104}
105
106void EmitCompositeExtractF16x4([[maybe_unused]] EmitContext& ctx,
107 [[maybe_unused]] std::string_view composite,
108 [[maybe_unused]] u32 index) {
109 NotImplemented();
110}
111
112void EmitCompositeInsertF16x2([[maybe_unused]] EmitContext& ctx,
113 [[maybe_unused]] std::string_view composite,
114 [[maybe_unused]] std::string_view object,
115 [[maybe_unused]] u32 index) {
116 NotImplemented();
117}
118
119void EmitCompositeInsertF16x3([[maybe_unused]] EmitContext& ctx,
120 [[maybe_unused]] std::string_view composite,
121 [[maybe_unused]] std::string_view object,
122 [[maybe_unused]] u32 index) {
123 NotImplemented();
124}
125
126void EmitCompositeInsertF16x4([[maybe_unused]] EmitContext& ctx,
127 [[maybe_unused]] std::string_view composite,
128 [[maybe_unused]] std::string_view object,
129 [[maybe_unused]] u32 index) {
130 NotImplemented();
131}
132
133void EmitCompositeConstructF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
134 std::string_view e2) {
135 ctx.AddF32x2("{}=vec2({},{});", inst, e1, e2);
136}
137
138void EmitCompositeConstructF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
139 std::string_view e2, std::string_view e3) {
140 ctx.AddF32x3("{}=vec3({},{},{});", inst, e1, e2, e3);
141}
142
143void EmitCompositeConstructF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
144 std::string_view e2, std::string_view e3, std::string_view e4) {
145 ctx.AddF32x4("{}=vec4({},{},{},{});", inst, e1, e2, e3, e4);
146}
147
148void EmitCompositeExtractF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
149 u32 index) {
150 ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]);
151}
152
153void EmitCompositeExtractF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
154 u32 index) {
155 ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]);
156}
157
158void EmitCompositeExtractF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
159 u32 index) {
160 ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]);
161}
162
163void EmitCompositeInsertF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
164 std::string_view object, u32 index) {
165 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32x2)};
166 CompositeInsert(ctx, ret, composite, object, index);
167}
168
169void EmitCompositeInsertF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
170 std::string_view object, u32 index) {
171 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32x3)};
172 CompositeInsert(ctx, ret, composite, object, index);
173}
174
175void EmitCompositeInsertF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
176 std::string_view object, u32 index) {
177 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32x4)};
178 CompositeInsert(ctx, ret, composite, object, index);
179}
180
181void EmitCompositeConstructF64x2([[maybe_unused]] EmitContext& ctx) {
182 NotImplemented();
183}
184
185void EmitCompositeConstructF64x3([[maybe_unused]] EmitContext& ctx) {
186 NotImplemented();
187}
188
189void EmitCompositeConstructF64x4([[maybe_unused]] EmitContext& ctx) {
190 NotImplemented();
191}
192
193void EmitCompositeExtractF64x2([[maybe_unused]] EmitContext& ctx) {
194 NotImplemented();
195}
196
197void EmitCompositeExtractF64x3([[maybe_unused]] EmitContext& ctx) {
198 NotImplemented();
199}
200
201void EmitCompositeExtractF64x4([[maybe_unused]] EmitContext& ctx) {
202 NotImplemented();
203}
204
205void EmitCompositeInsertF64x2(EmitContext& ctx, std::string_view composite, std::string_view object,
206 u32 index) {
207 ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
208}
209
210void EmitCompositeInsertF64x3(EmitContext& ctx, std::string_view composite, std::string_view object,
211 u32 index) {
212 ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
213}
214
215void EmitCompositeInsertF64x4(EmitContext& ctx, std::string_view composite, std::string_view object,
216 u32 index) {
217 ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
218}
219} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
new file mode 100644
index 000000000..580063fa9
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
@@ -0,0 +1,456 @@
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 <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10#include "shader_recompiler/profile.h"
11#include "shader_recompiler/runtime_info.h"
12
13namespace Shader::Backend::GLSL {
14namespace {
15constexpr char SWIZZLE[]{"xyzw"};
16
17u32 CbufIndex(u32 offset) {
18 return (offset / 4) % 4;
19}
20
21char OffsetSwizzle(u32 offset) {
22 return SWIZZLE[CbufIndex(offset)];
23}
24
25bool IsInputArray(Stage stage) {
26 return stage == Stage::Geometry || stage == Stage::TessellationControl ||
27 stage == Stage::TessellationEval;
28}
29
30std::string InputVertexIndex(EmitContext& ctx, std::string_view vertex) {
31 return IsInputArray(ctx.stage) ? fmt::format("[{}]", vertex) : "";
32}
33
34std::string_view OutputVertexIndex(EmitContext& ctx) {
35 return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : "";
36}
37
38void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding,
39 const IR::Value& offset, u32 num_bits, std::string_view cast = {},
40 std::string_view bit_offset = {}) {
41 const bool is_immediate{offset.IsImmediate()};
42 const bool component_indexing_bug{!is_immediate && ctx.profile.has_gl_component_indexing_bug};
43 if (is_immediate) {
44 const s32 signed_offset{static_cast<s32>(offset.U32())};
45 static constexpr u32 cbuf_size{0x10000};
46 if (signed_offset < 0 || offset.U32() > cbuf_size) {
47 LOG_WARNING(Shader_GLSL, "Immediate constant buffer offset is out of bounds");
48 ctx.Add("{}=0u;", ret);
49 return;
50 }
51 }
52 const auto offset_var{ctx.var_alloc.Consume(offset)};
53 const auto index{is_immediate ? fmt::format("{}", offset.U32() / 16)
54 : fmt::format("{}>>4", offset_var)};
55 const auto swizzle{is_immediate ? fmt::format(".{}", OffsetSwizzle(offset.U32()))
56 : fmt::format("[({}>>2)%4]", offset_var)};
57
58 const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
59 const auto cbuf_cast{fmt::format("{}({}[{}]{{}})", cast, cbuf, index)};
60 const auto extraction{num_bits == 32 ? cbuf_cast
61 : fmt ::format("bitfieldExtract({},int({}),{})", cbuf_cast,
62 bit_offset, num_bits)};
63 if (!component_indexing_bug) {
64 const auto result{fmt::format(fmt::runtime(extraction), swizzle)};
65 ctx.Add("{}={};", ret, result);
66 return;
67 }
68 const auto cbuf_offset{fmt::format("{}>>2", offset_var)};
69 for (u32 i = 0; i < 4; ++i) {
70 const auto swizzle_string{fmt::format(".{}", "xyzw"[i])};
71 const auto result{fmt::format(fmt::runtime(extraction), swizzle_string)};
72 ctx.Add("if(({}&3)=={}){}={};", cbuf_offset, i, ret, result);
73 }
74}
75
76void GetCbuf8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, const IR::Value& offset,
77 std::string_view cast) {
78 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
79 if (offset.IsImmediate()) {
80 const auto bit_offset{fmt::format("{}", (offset.U32() % 4) * 8)};
81 GetCbuf(ctx, ret, binding, offset, 8, cast, bit_offset);
82 } else {
83 const auto offset_var{ctx.var_alloc.Consume(offset)};
84 const auto bit_offset{fmt::format("({}%4)*8", offset_var)};
85 GetCbuf(ctx, ret, binding, offset, 8, cast, bit_offset);
86 }
87}
88
89void GetCbuf16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, const IR::Value& offset,
90 std::string_view cast) {
91 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
92 if (offset.IsImmediate()) {
93 const auto bit_offset{fmt::format("{}", ((offset.U32() / 2) % 2) * 16)};
94 GetCbuf(ctx, ret, binding, offset, 16, cast, bit_offset);
95 } else {
96 const auto offset_var{ctx.var_alloc.Consume(offset)};
97 const auto bit_offset{fmt::format("(({}>>1)%2)*16", offset_var)};
98 GetCbuf(ctx, ret, binding, offset, 16, cast, bit_offset);
99 }
100}
101
102u32 TexCoordIndex(IR::Attribute attr) {
103 return (static_cast<u32>(attr) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
104}
105} // Anonymous namespace
106
107void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
108 const IR::Value& offset) {
109 GetCbuf8(ctx, inst, binding, offset, "ftou");
110}
111
112void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
113 const IR::Value& offset) {
114 GetCbuf8(ctx, inst, binding, offset, "ftoi");
115}
116
117void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
118 const IR::Value& offset) {
119 GetCbuf16(ctx, inst, binding, offset, "ftou");
120}
121
122void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
123 const IR::Value& offset) {
124 GetCbuf16(ctx, inst, binding, offset, "ftoi");
125}
126
127void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
128 const IR::Value& offset) {
129 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
130 GetCbuf(ctx, ret, binding, offset, 32, "ftou");
131}
132
133void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
134 const IR::Value& offset) {
135 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32)};
136 GetCbuf(ctx, ret, binding, offset, 32);
137}
138
139void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
140 const IR::Value& offset) {
141 const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
142 if (offset.IsImmediate()) {
143 static constexpr u32 cbuf_size{0x10000};
144 const u32 u32_offset{offset.U32()};
145 const s32 signed_offset{static_cast<s32>(offset.U32())};
146 if (signed_offset < 0 || u32_offset > cbuf_size) {
147 LOG_WARNING(Shader_GLSL, "Immediate constant buffer offset is out of bounds");
148 ctx.AddU32x2("{}=uvec2(0u);", inst);
149 return;
150 }
151 if (u32_offset % 2 == 0) {
152 ctx.AddU32x2("{}=ftou({}[{}].{}{});", inst, cbuf, u32_offset / 16,
153 OffsetSwizzle(u32_offset), OffsetSwizzle(u32_offset + 4));
154 } else {
155 ctx.AddU32x2("{}=uvec2(ftou({}[{}].{}),ftou({}[{}].{}));", inst, cbuf, u32_offset / 16,
156 OffsetSwizzle(u32_offset), cbuf, (u32_offset + 4) / 16,
157 OffsetSwizzle(u32_offset + 4));
158 }
159 return;
160 }
161 const auto offset_var{ctx.var_alloc.Consume(offset)};
162 if (!ctx.profile.has_gl_component_indexing_bug) {
163 ctx.AddU32x2("{}=uvec2(ftou({}[{}>>4][({}>>2)%4]),ftou({}[({}+4)>>4][(({}+4)>>2)%4]));",
164 inst, cbuf, offset_var, offset_var, cbuf, offset_var, offset_var);
165 return;
166 }
167 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)};
168 const auto cbuf_offset{fmt::format("{}>>2", offset_var)};
169 for (u32 swizzle = 0; swizzle < 4; ++swizzle) {
170 ctx.Add("if(({}&3)=={}){}=uvec2(ftou({}[{}>>4].{}),ftou({}[({}+4)>>4].{}));", cbuf_offset,
171 swizzle, ret, cbuf, offset_var, "xyzw"[swizzle], cbuf, offset_var,
172 "xyzw"[(swizzle + 1) % 4]);
173 }
174}
175
176void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
177 std::string_view vertex) {
178 const u32 element{static_cast<u32>(attr) % 4};
179 const char swizzle{"xyzw"[element]};
180 if (IR::IsGeneric(attr)) {
181 const u32 index{IR::GenericAttributeIndex(attr)};
182 if (!ctx.runtime_info.previous_stage_stores.Generic(index, element)) {
183 if (element == 3) {
184 ctx.AddF32("{}=1.f;", inst, attr);
185 } else {
186 ctx.AddF32("{}=0.f;", inst, attr);
187 }
188 return;
189 }
190 ctx.AddF32("{}=in_attr{}{}.{};", inst, index, InputVertexIndex(ctx, vertex), swizzle);
191 return;
192 }
193 // GLSL only exposes 8 legacy texcoords
194 if (attr >= IR::Attribute::FixedFncTexture8S && attr <= IR::Attribute::FixedFncTexture9Q) {
195 LOG_WARNING(Shader_GLSL, "GLSL does not allow access to gl_TexCoord[{}]",
196 TexCoordIndex(attr));
197 ctx.AddF32("{}=0.f;", inst);
198 return;
199 }
200 if (attr >= IR::Attribute::FixedFncTexture0S && attr <= IR::Attribute::FixedFncTexture7Q) {
201 const u32 index{TexCoordIndex(attr)};
202 ctx.AddF32("{}=gl_TexCoord[{}].{};", inst, index, swizzle);
203 return;
204 }
205 switch (attr) {
206 case IR::Attribute::PrimitiveId:
207 ctx.AddF32("{}=itof(gl_PrimitiveID);", inst);
208 break;
209 case IR::Attribute::PositionX:
210 case IR::Attribute::PositionY:
211 case IR::Attribute::PositionZ:
212 case IR::Attribute::PositionW: {
213 const bool is_array{IsInputArray(ctx.stage)};
214 const auto input_decorator{is_array ? fmt::format("gl_in[{}].", vertex) : ""};
215 ctx.AddF32("{}={}{}.{};", inst, input_decorator, ctx.position_name, swizzle);
216 break;
217 }
218 case IR::Attribute::ColorFrontDiffuseR:
219 case IR::Attribute::ColorFrontDiffuseG:
220 case IR::Attribute::ColorFrontDiffuseB:
221 case IR::Attribute::ColorFrontDiffuseA:
222 if (ctx.stage == Stage::Fragment) {
223 ctx.AddF32("{}=gl_Color.{};", inst, swizzle);
224 } else {
225 ctx.AddF32("{}=gl_FrontColor.{};", inst, swizzle);
226 }
227 break;
228 case IR::Attribute::PointSpriteS:
229 case IR::Attribute::PointSpriteT:
230 ctx.AddF32("{}=gl_PointCoord.{};", inst, swizzle);
231 break;
232 case IR::Attribute::TessellationEvaluationPointU:
233 case IR::Attribute::TessellationEvaluationPointV:
234 ctx.AddF32("{}=gl_TessCoord.{};", inst, swizzle);
235 break;
236 case IR::Attribute::InstanceId:
237 ctx.AddF32("{}=itof(gl_InstanceID);", inst);
238 break;
239 case IR::Attribute::VertexId:
240 ctx.AddF32("{}=itof(gl_VertexID);", inst);
241 break;
242 case IR::Attribute::FrontFace:
243 ctx.AddF32("{}=itof(gl_FrontFacing?-1:0);", inst);
244 break;
245 default:
246 throw NotImplementedException("Get attribute {}", attr);
247 }
248}
249
250void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view value,
251 [[maybe_unused]] std::string_view vertex) {
252 if (IR::IsGeneric(attr)) {
253 const u32 index{IR::GenericAttributeIndex(attr)};
254 const u32 attr_element{IR::GenericAttributeElement(attr)};
255 const GenericElementInfo& info{ctx.output_generics.at(index).at(attr_element)};
256 const auto output_decorator{OutputVertexIndex(ctx)};
257 if (info.num_components == 1) {
258 ctx.Add("{}{}={};", info.name, output_decorator, value);
259 } else {
260 const u32 index_element{attr_element - info.first_element};
261 ctx.Add("{}{}.{}={};", info.name, output_decorator, "xyzw"[index_element], value);
262 }
263 return;
264 }
265 const u32 element{static_cast<u32>(attr) % 4};
266 const char swizzle{"xyzw"[element]};
267 // GLSL only exposes 8 legacy texcoords
268 if (attr >= IR::Attribute::FixedFncTexture8S && attr <= IR::Attribute::FixedFncTexture9Q) {
269 LOG_WARNING(Shader_GLSL, "GLSL does not allow access to gl_TexCoord[{}]",
270 TexCoordIndex(attr));
271 return;
272 }
273 if (attr >= IR::Attribute::FixedFncTexture0S && attr <= IR::Attribute::FixedFncTexture7Q) {
274 const u32 index{TexCoordIndex(attr)};
275 ctx.Add("gl_TexCoord[{}].{}={};", index, swizzle, value);
276 return;
277 }
278 switch (attr) {
279 case IR::Attribute::Layer:
280 if (ctx.stage != Stage::Geometry &&
281 !ctx.profile.support_viewport_index_layer_non_geometry) {
282 LOG_WARNING(Shader_GLSL, "Shader stores viewport layer but device does not support "
283 "viewport layer extension");
284 break;
285 }
286 ctx.Add("gl_Layer=ftoi({});", value);
287 break;
288 case IR::Attribute::ViewportIndex:
289 if (ctx.stage != Stage::Geometry &&
290 !ctx.profile.support_viewport_index_layer_non_geometry) {
291 LOG_WARNING(Shader_GLSL, "Shader stores viewport index but device does not support "
292 "viewport layer extension");
293 break;
294 }
295 ctx.Add("gl_ViewportIndex=ftoi({});", value);
296 break;
297 case IR::Attribute::ViewportMask:
298 if (ctx.stage != Stage::Geometry && !ctx.profile.support_viewport_mask) {
299 LOG_WARNING(
300 Shader_GLSL,
301 "Shader stores viewport mask but device does not support viewport mask extension");
302 break;
303 }
304 ctx.Add("gl_ViewportMask[0]=ftoi({});", value);
305 break;
306 case IR::Attribute::PointSize:
307 ctx.Add("gl_PointSize={};", value);
308 break;
309 case IR::Attribute::PositionX:
310 case IR::Attribute::PositionY:
311 case IR::Attribute::PositionZ:
312 case IR::Attribute::PositionW:
313 ctx.Add("gl_Position.{}={};", swizzle, value);
314 break;
315 case IR::Attribute::ColorFrontDiffuseR:
316 case IR::Attribute::ColorFrontDiffuseG:
317 case IR::Attribute::ColorFrontDiffuseB:
318 case IR::Attribute::ColorFrontDiffuseA:
319 ctx.Add("gl_FrontColor.{}={};", swizzle, value);
320 break;
321 case IR::Attribute::ColorFrontSpecularR:
322 case IR::Attribute::ColorFrontSpecularG:
323 case IR::Attribute::ColorFrontSpecularB:
324 case IR::Attribute::ColorFrontSpecularA:
325 ctx.Add("gl_FrontSecondaryColor.{}={};", swizzle, value);
326 break;
327 case IR::Attribute::ColorBackDiffuseR:
328 case IR::Attribute::ColorBackDiffuseG:
329 case IR::Attribute::ColorBackDiffuseB:
330 case IR::Attribute::ColorBackDiffuseA:
331 ctx.Add("gl_BackColor.{}={};", swizzle, value);
332 break;
333 case IR::Attribute::ColorBackSpecularR:
334 case IR::Attribute::ColorBackSpecularG:
335 case IR::Attribute::ColorBackSpecularB:
336 case IR::Attribute::ColorBackSpecularA:
337 ctx.Add("gl_BackSecondaryColor.{}={};", swizzle, value);
338 break;
339 case IR::Attribute::FogCoordinate:
340 ctx.Add("gl_FogFragCoord={};", value);
341 break;
342 case IR::Attribute::ClipDistance0:
343 case IR::Attribute::ClipDistance1:
344 case IR::Attribute::ClipDistance2:
345 case IR::Attribute::ClipDistance3:
346 case IR::Attribute::ClipDistance4:
347 case IR::Attribute::ClipDistance5:
348 case IR::Attribute::ClipDistance6:
349 case IR::Attribute::ClipDistance7: {
350 const u32 index{static_cast<u32>(attr) - static_cast<u32>(IR::Attribute::ClipDistance0)};
351 ctx.Add("gl_ClipDistance[{}]={};", index, value);
352 break;
353 }
354 default:
355 throw NotImplementedException("Set attribute {}", attr);
356 }
357}
358
359void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, std::string_view offset,
360 std::string_view vertex) {
361 const bool is_array{ctx.stage == Stage::Geometry};
362 const auto vertex_arg{is_array ? fmt::format(",{}", vertex) : ""};
363 ctx.AddF32("{}=IndexedAttrLoad(int({}){});", inst, offset, vertex_arg);
364}
365
366void EmitSetAttributeIndexed([[maybe_unused]] EmitContext& ctx,
367 [[maybe_unused]] std::string_view offset,
368 [[maybe_unused]] std::string_view value,
369 [[maybe_unused]] std::string_view vertex) {
370 NotImplemented();
371}
372
373void EmitGetPatch(EmitContext& ctx, IR::Inst& inst, IR::Patch patch) {
374 if (!IR::IsGeneric(patch)) {
375 throw NotImplementedException("Non-generic patch load");
376 }
377 const u32 index{IR::GenericPatchIndex(patch)};
378 const u32 element{IR::GenericPatchElement(patch)};
379 const char swizzle{"xyzw"[element]};
380 ctx.AddF32("{}=patch{}.{};", inst, index, swizzle);
381}
382
383void EmitSetPatch(EmitContext& ctx, IR::Patch patch, std::string_view value) {
384 if (IR::IsGeneric(patch)) {
385 const u32 index{IR::GenericPatchIndex(patch)};
386 const u32 element{IR::GenericPatchElement(patch)};
387 ctx.Add("patch{}.{}={};", index, "xyzw"[element], value);
388 return;
389 }
390 switch (patch) {
391 case IR::Patch::TessellationLodLeft:
392 case IR::Patch::TessellationLodRight:
393 case IR::Patch::TessellationLodTop:
394 case IR::Patch::TessellationLodBottom: {
395 const u32 index{static_cast<u32>(patch) - u32(IR::Patch::TessellationLodLeft)};
396 ctx.Add("gl_TessLevelOuter[{}]={};", index, value);
397 break;
398 }
399 case IR::Patch::TessellationLodInteriorU:
400 ctx.Add("gl_TessLevelInner[0]={};", value);
401 break;
402 case IR::Patch::TessellationLodInteriorV:
403 ctx.Add("gl_TessLevelInner[1]={};", value);
404 break;
405 default:
406 throw NotImplementedException("Patch {}", patch);
407 }
408}
409
410void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, std::string_view value) {
411 const char swizzle{"xyzw"[component]};
412 ctx.Add("frag_color{}.{}={};", index, swizzle, value);
413}
414
415void EmitSetSampleMask(EmitContext& ctx, std::string_view value) {
416 ctx.Add("gl_SampleMask[0]=int({});", value);
417}
418
419void EmitSetFragDepth(EmitContext& ctx, std::string_view value) {
420 ctx.Add("gl_FragDepth={};", value);
421}
422
423void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst) {
424 ctx.AddU32x3("{}=gl_LocalInvocationID;", inst);
425}
426
427void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst) {
428 ctx.AddU32x3("{}=gl_WorkGroupID;", inst);
429}
430
431void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
432 ctx.AddU32("{}=uint(gl_InvocationID);", inst);
433}
434
435void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
436 ctx.AddU32("{}=uint(gl_SampleID);", inst);
437}
438
439void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst) {
440 ctx.AddU1("{}=gl_HelperInvocation;", inst);
441}
442
443void EmitYDirection(EmitContext& ctx, IR::Inst& inst) {
444 ctx.uses_y_direction = true;
445 ctx.AddF32("{}=gl_FrontMaterial.ambient.a;", inst);
446}
447
448void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset) {
449 ctx.AddU32("{}=lmem[{}];", inst, word_offset);
450}
451
452void EmitWriteLocal(EmitContext& ctx, std::string_view word_offset, std::string_view value) {
453 ctx.Add("lmem[{}]={};", word_offset, value);
454}
455
456} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp
new file mode 100644
index 000000000..53f8896be
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp
@@ -0,0 +1,21 @@
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 <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/exception.h"
10
11namespace Shader::Backend::GLSL {
12
13void EmitJoin(EmitContext&) {
14 throw NotImplementedException("Join shouldn't be emitted");
15}
16
17void EmitDemoteToHelperInvocation(EmitContext& ctx) {
18 ctx.Add("discard;");
19}
20
21} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp
new file mode 100644
index 000000000..eeae6562c
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp
@@ -0,0 +1,230 @@
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 <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10
11namespace Shader::Backend::GLSL {
12void EmitConvertS16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
13 [[maybe_unused]] std::string_view value) {
14 NotImplemented();
15}
16
17void EmitConvertS16F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
18 ctx.AddU32("{}=(int({})&0xffff)|(bitfieldExtract(int({}),31,1)<<15);", inst, value, value);
19}
20
21void EmitConvertS16F64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
22 [[maybe_unused]] std::string_view value) {
23 NotImplemented();
24}
25
26void EmitConvertS32F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
27 [[maybe_unused]] std::string_view value) {
28 NotImplemented();
29}
30
31void EmitConvertS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
32 ctx.AddU32("{}=int({});", inst, value);
33}
34
35void EmitConvertS32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
36 ctx.AddU32("{}=int({});", inst, value);
37}
38
39void EmitConvertS64F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
40 [[maybe_unused]] std::string_view value) {
41 NotImplemented();
42}
43
44void EmitConvertS64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
45 ctx.AddU64("{}=int64_t({});", inst, value);
46}
47
48void EmitConvertS64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
49 ctx.AddU64("{}=int64_t({});", inst, value);
50}
51
52void EmitConvertU16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
53 [[maybe_unused]] std::string_view value) {
54 NotImplemented();
55}
56
57void EmitConvertU16F32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
58 [[maybe_unused]] std::string_view value) {
59 NotImplemented();
60}
61
62void EmitConvertU16F64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
63 [[maybe_unused]] std::string_view value) {
64 NotImplemented();
65}
66
67void EmitConvertU32F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
68 [[maybe_unused]] std::string_view value) {
69 NotImplemented();
70}
71
72void EmitConvertU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
73 ctx.AddU32("{}=uint({});", inst, value);
74}
75
76void EmitConvertU32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
77 ctx.AddU32("{}=uint({});", inst, value);
78}
79
80void EmitConvertU64F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
81 [[maybe_unused]] std::string_view value) {
82 NotImplemented();
83}
84
85void EmitConvertU64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
86 ctx.AddU64("{}=uint64_t({});", inst, value);
87}
88
89void EmitConvertU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
90 ctx.AddU64("{}=uint64_t({});", inst, value);
91}
92
93void EmitConvertU64U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
94 ctx.AddU64("{}=uint64_t({});", inst, value);
95}
96
97void EmitConvertU32U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
98 ctx.AddU32("{}=uint({});", inst, value);
99}
100
101void EmitConvertF16F32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
102 [[maybe_unused]] std::string_view value) {
103 NotImplemented();
104}
105
106void EmitConvertF32F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
107 [[maybe_unused]] std::string_view value) {
108 NotImplemented();
109}
110
111void EmitConvertF32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
112 ctx.AddF32("{}=float({});", inst, value);
113}
114
115void EmitConvertF64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
116 ctx.AddF64("{}=double({});", inst, value);
117}
118
119void EmitConvertF16S8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
120 [[maybe_unused]] std::string_view value) {
121 NotImplemented();
122}
123
124void EmitConvertF16S16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
125 [[maybe_unused]] std::string_view value) {
126 NotImplemented();
127}
128
129void EmitConvertF16S32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
130 [[maybe_unused]] std::string_view value) {
131 NotImplemented();
132}
133
134void EmitConvertF16S64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
135 [[maybe_unused]] std::string_view value) {
136 NotImplemented();
137}
138
139void EmitConvertF16U8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
140 [[maybe_unused]] std::string_view value) {
141 NotImplemented();
142}
143
144void EmitConvertF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
145 [[maybe_unused]] std::string_view value) {
146 NotImplemented();
147}
148
149void EmitConvertF16U32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
150 [[maybe_unused]] std::string_view value) {
151 NotImplemented();
152}
153
154void EmitConvertF16U64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
155 [[maybe_unused]] std::string_view value) {
156 NotImplemented();
157}
158
159void EmitConvertF32S8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
160 [[maybe_unused]] std::string_view value) {
161 NotImplemented();
162}
163
164void EmitConvertF32S16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
165 [[maybe_unused]] std::string_view value) {
166 NotImplemented();
167}
168
169void EmitConvertF32S32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
170 ctx.AddF32("{}=float(int({}));", inst, value);
171}
172
173void EmitConvertF32S64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
174 ctx.AddF32("{}=float(int64_t({}));", inst, value);
175}
176
177void EmitConvertF32U8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
178 [[maybe_unused]] std::string_view value) {
179 NotImplemented();
180}
181
182void EmitConvertF32U16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
183 ctx.AddF32("{}=float({}&0xffff);", inst, value);
184}
185
186void EmitConvertF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
187 ctx.AddF32("{}=float({});", inst, value);
188}
189
190void EmitConvertF32U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
191 ctx.AddF32("{}=float({});", inst, value);
192}
193
194void EmitConvertF64S8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
195 [[maybe_unused]] std::string_view value) {
196 NotImplemented();
197}
198
199void EmitConvertF64S16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
200 [[maybe_unused]] std::string_view value) {
201 NotImplemented();
202}
203
204void EmitConvertF64S32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
205 ctx.AddF64("{}=double(int({}));", inst, value);
206}
207
208void EmitConvertF64S64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
209 ctx.AddF64("{}=double(int64_t({}));", inst, value);
210}
211
212void EmitConvertF64U8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
213 [[maybe_unused]] std::string_view value) {
214 NotImplemented();
215}
216
217void EmitConvertF64U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
218 [[maybe_unused]] std::string_view value) {
219 NotImplemented();
220}
221
222void EmitConvertF64U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
223 ctx.AddF64("{}=double({});", inst, value);
224}
225
226void EmitConvertF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
227 ctx.AddF64("{}=double({});", inst, value);
228}
229
230} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp
new file mode 100644
index 000000000..d423bfb1b
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp
@@ -0,0 +1,456 @@
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 <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/modifiers.h"
10#include "shader_recompiler/frontend/ir/value.h"
11
12namespace Shader::Backend::GLSL {
13namespace {
14void Compare(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs,
15 std::string_view op, bool ordered) {
16 const auto nan_op{ordered ? "&&!" : "||"};
17 ctx.AddU1("{}={}{}{}"
18 "{}isnan({}){}isnan({});",
19 inst, lhs, op, rhs, nan_op, lhs, nan_op, rhs);
20}
21
22bool IsPrecise(const IR::Inst& inst) {
23 return inst.Flags<IR::FpControl>().no_contraction;
24}
25} // Anonymous namespace
26
27void EmitFPAbs16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
28 [[maybe_unused]] std::string_view value) {
29 NotImplemented();
30}
31
32void EmitFPAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
33 ctx.AddF32("{}=abs({});", inst, value);
34}
35
36void EmitFPAbs64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
37 ctx.AddF64("{}=abs({});", inst, value);
38}
39
40void EmitFPAdd16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
41 [[maybe_unused]] std::string_view a, [[maybe_unused]] std::string_view b) {
42 NotImplemented();
43}
44
45void EmitFPAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
46 if (IsPrecise(inst)) {
47 ctx.AddPrecF32("{}={}+{};", inst, a, b);
48 } else {
49 ctx.AddF32("{}={}+{};", inst, a, b);
50 }
51}
52
53void EmitFPAdd64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
54 if (IsPrecise(inst)) {
55 ctx.AddPrecF64("{}={}+{};", inst, a, b);
56 } else {
57 ctx.AddF64("{}={}+{};", inst, a, b);
58 }
59}
60
61void EmitFPFma16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
62 [[maybe_unused]] std::string_view a, [[maybe_unused]] std::string_view b,
63 [[maybe_unused]] std::string_view c) {
64 NotImplemented();
65}
66
67void EmitFPFma32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
68 std::string_view c) {
69 if (IsPrecise(inst)) {
70 ctx.AddPrecF32("{}=fma({},{},{});", inst, a, b, c);
71 } else {
72 ctx.AddF32("{}=fma({},{},{});", inst, a, b, c);
73 }
74}
75
76void EmitFPFma64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
77 std::string_view c) {
78 if (IsPrecise(inst)) {
79 ctx.AddPrecF64("{}=fma({},{},{});", inst, a, b, c);
80 } else {
81 ctx.AddF64("{}=fma({},{},{});", inst, a, b, c);
82 }
83}
84
85void EmitFPMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
86 ctx.AddF32("{}=max({},{});", inst, a, b);
87}
88
89void EmitFPMax64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
90 ctx.AddF64("{}=max({},{});", inst, a, b);
91}
92
93void EmitFPMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
94 ctx.AddF32("{}=min({},{});", inst, a, b);
95}
96
97void EmitFPMin64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
98 ctx.AddF64("{}=min({},{});", inst, a, b);
99}
100
101void EmitFPMul16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
102 [[maybe_unused]] std::string_view a, [[maybe_unused]] std::string_view b) {
103 NotImplemented();
104}
105
106void EmitFPMul32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
107 if (IsPrecise(inst)) {
108 ctx.AddPrecF32("{}={}*{};", inst, a, b);
109 } else {
110 ctx.AddF32("{}={}*{};", inst, a, b);
111 }
112}
113
114void EmitFPMul64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
115 if (IsPrecise(inst)) {
116 ctx.AddPrecF64("{}={}*{};", inst, a, b);
117 } else {
118 ctx.AddF64("{}={}*{};", inst, a, b);
119 }
120}
121
122void EmitFPNeg16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
123 [[maybe_unused]] std::string_view value) {
124 NotImplemented();
125}
126
127void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
128 ctx.AddF32("{}=-({});", inst, value);
129}
130
131void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
132 ctx.AddF64("{}=-({});", inst, value);
133}
134
135void EmitFPSin(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
136 ctx.AddF32("{}=sin({});", inst, value);
137}
138
139void EmitFPCos(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
140 ctx.AddF32("{}=cos({});", inst, value);
141}
142
143void EmitFPExp2(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
144 ctx.AddF32("{}=exp2({});", inst, value);
145}
146
147void EmitFPLog2(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
148 ctx.AddF32("{}=log2({});", inst, value);
149}
150
151void EmitFPRecip32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
152 ctx.AddF32("{}=(1.0f)/{};", inst, value);
153}
154
155void EmitFPRecip64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
156 ctx.AddF64("{}=1.0/{};", inst, value);
157}
158
159void EmitFPRecipSqrt32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
160 [[maybe_unused]] std::string_view value) {
161 ctx.AddF32("{}=inversesqrt({});", inst, value);
162}
163
164void EmitFPRecipSqrt64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
165 [[maybe_unused]] std::string_view value) {
166 NotImplemented();
167}
168
169void EmitFPSqrt(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
170 ctx.AddF32("{}=sqrt({});", inst, value);
171}
172
173void EmitFPSaturate16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
174 [[maybe_unused]] std::string_view value) {
175 NotImplemented();
176}
177
178void EmitFPSaturate32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
179 ctx.AddF32("{}=min(max({},0.0),1.0);", inst, value);
180}
181
182void EmitFPSaturate64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
183 ctx.AddF64("{}=min(max({},0.0),1.0);", inst, value);
184}
185
186void EmitFPClamp16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
187 [[maybe_unused]] std::string_view value,
188 [[maybe_unused]] std::string_view min_value,
189 [[maybe_unused]] std::string_view max_value) {
190 NotImplemented();
191}
192
193void EmitFPClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value,
194 std::string_view min_value, std::string_view max_value) {
195 // GLSL's clamp does not produce desirable results
196 ctx.AddF32("{}=min(max({},float({})),float({}));", inst, value, min_value, max_value);
197}
198
199void EmitFPClamp64(EmitContext& ctx, IR::Inst& inst, std::string_view value,
200 std::string_view min_value, std::string_view max_value) {
201 // GLSL's clamp does not produce desirable results
202 ctx.AddF64("{}=min(max({},double({})),double({}));", inst, value, min_value, max_value);
203}
204
205void EmitFPRoundEven16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
206 [[maybe_unused]] std::string_view value) {
207 NotImplemented();
208}
209
210void EmitFPRoundEven32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
211 ctx.AddF32("{}=roundEven({});", inst, value);
212}
213
214void EmitFPRoundEven64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
215 ctx.AddF64("{}=roundEven({});", inst, value);
216}
217
218void EmitFPFloor16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
219 [[maybe_unused]] std::string_view value) {
220 NotImplemented();
221}
222
223void EmitFPFloor32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
224 ctx.AddF32("{}=floor({});", inst, value);
225}
226
227void EmitFPFloor64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
228 ctx.AddF64("{}=floor({});", inst, value);
229}
230
231void EmitFPCeil16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
232 [[maybe_unused]] std::string_view value) {
233 NotImplemented();
234}
235
236void EmitFPCeil32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
237 ctx.AddF32("{}=ceil({});", inst, value);
238}
239
240void EmitFPCeil64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
241 ctx.AddF64("{}=ceil({});", inst, value);
242}
243
244void EmitFPTrunc16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
245 [[maybe_unused]] std::string_view value) {
246 NotImplemented();
247}
248
249void EmitFPTrunc32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
250 ctx.AddF32("{}=trunc({});", inst, value);
251}
252
253void EmitFPTrunc64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
254 ctx.AddF64("{}=trunc({});", inst, value);
255}
256
257void EmitFPOrdEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
258 [[maybe_unused]] std::string_view rhs) {
259 NotImplemented();
260}
261
262void EmitFPOrdEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
263 std::string_view rhs) {
264 Compare(ctx, inst, lhs, rhs, "==", true);
265}
266
267void EmitFPOrdEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
268 std::string_view rhs) {
269 Compare(ctx, inst, lhs, rhs, "==", true);
270}
271
272void EmitFPUnordEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
273 [[maybe_unused]] std::string_view rhs) {
274 NotImplemented();
275}
276
277void EmitFPUnordEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
278 std::string_view rhs) {
279 Compare(ctx, inst, lhs, rhs, "==", false);
280}
281
282void EmitFPUnordEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
283 std::string_view rhs) {
284 Compare(ctx, inst, lhs, rhs, "==", false);
285}
286
287void EmitFPOrdNotEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
288 [[maybe_unused]] std::string_view rhs) {
289 NotImplemented();
290}
291
292void EmitFPOrdNotEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
293 std::string_view rhs) {
294 Compare(ctx, inst, lhs, rhs, "!=", true);
295}
296
297void EmitFPOrdNotEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
298 std::string_view rhs) {
299 Compare(ctx, inst, lhs, rhs, "!=", true);
300}
301
302void EmitFPUnordNotEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
303 [[maybe_unused]] std::string_view rhs) {
304 NotImplemented();
305}
306
307void EmitFPUnordNotEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
308 std::string_view rhs) {
309 Compare(ctx, inst, lhs, rhs, "!=", false);
310}
311
312void EmitFPUnordNotEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
313 std::string_view rhs) {
314 Compare(ctx, inst, lhs, rhs, "!=", false);
315}
316
317void EmitFPOrdLessThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
318 [[maybe_unused]] std::string_view rhs) {
319 NotImplemented();
320}
321
322void EmitFPOrdLessThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
323 std::string_view rhs) {
324 Compare(ctx, inst, lhs, rhs, "<", true);
325}
326
327void EmitFPOrdLessThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
328 std::string_view rhs) {
329 Compare(ctx, inst, lhs, rhs, "<", true);
330}
331
332void EmitFPUnordLessThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
333 [[maybe_unused]] std::string_view rhs) {
334 NotImplemented();
335}
336
337void EmitFPUnordLessThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
338 std::string_view rhs) {
339 Compare(ctx, inst, lhs, rhs, "<", false);
340}
341
342void EmitFPUnordLessThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
343 std::string_view rhs) {
344 Compare(ctx, inst, lhs, rhs, "<", false);
345}
346
347void EmitFPOrdGreaterThan16([[maybe_unused]] EmitContext& ctx,
348 [[maybe_unused]] std::string_view lhs,
349 [[maybe_unused]] std::string_view rhs) {
350 NotImplemented();
351}
352
353void EmitFPOrdGreaterThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
354 std::string_view rhs) {
355 Compare(ctx, inst, lhs, rhs, ">", true);
356}
357
358void EmitFPOrdGreaterThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
359 std::string_view rhs) {
360 Compare(ctx, inst, lhs, rhs, ">", true);
361}
362
363void EmitFPUnordGreaterThan16([[maybe_unused]] EmitContext& ctx,
364 [[maybe_unused]] std::string_view lhs,
365 [[maybe_unused]] std::string_view rhs) {
366 NotImplemented();
367}
368
369void EmitFPUnordGreaterThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
370 std::string_view rhs) {
371 Compare(ctx, inst, lhs, rhs, ">", false);
372}
373
374void EmitFPUnordGreaterThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
375 std::string_view rhs) {
376 Compare(ctx, inst, lhs, rhs, ">", false);
377}
378
379void EmitFPOrdLessThanEqual16([[maybe_unused]] EmitContext& ctx,
380 [[maybe_unused]] std::string_view lhs,
381 [[maybe_unused]] std::string_view rhs) {
382 NotImplemented();
383}
384
385void EmitFPOrdLessThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
386 std::string_view rhs) {
387 Compare(ctx, inst, lhs, rhs, "<=", true);
388}
389
390void EmitFPOrdLessThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
391 std::string_view rhs) {
392 Compare(ctx, inst, lhs, rhs, "<=", true);
393}
394
395void EmitFPUnordLessThanEqual16([[maybe_unused]] EmitContext& ctx,
396 [[maybe_unused]] std::string_view lhs,
397 [[maybe_unused]] std::string_view rhs) {
398 NotImplemented();
399}
400
401void EmitFPUnordLessThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
402 std::string_view rhs) {
403 Compare(ctx, inst, lhs, rhs, "<=", false);
404}
405
406void EmitFPUnordLessThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
407 std::string_view rhs) {
408 Compare(ctx, inst, lhs, rhs, "<=", false);
409}
410
411void EmitFPOrdGreaterThanEqual16([[maybe_unused]] EmitContext& ctx,
412 [[maybe_unused]] std::string_view lhs,
413 [[maybe_unused]] std::string_view rhs) {
414 NotImplemented();
415}
416
417void EmitFPOrdGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
418 std::string_view rhs) {
419 Compare(ctx, inst, lhs, rhs, ">=", true);
420}
421
422void EmitFPOrdGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
423 std::string_view rhs) {
424 Compare(ctx, inst, lhs, rhs, ">=", true);
425}
426
427void EmitFPUnordGreaterThanEqual16([[maybe_unused]] EmitContext& ctx,
428 [[maybe_unused]] std::string_view lhs,
429 [[maybe_unused]] std::string_view rhs) {
430 NotImplemented();
431}
432
433void EmitFPUnordGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
434 std::string_view rhs) {
435 Compare(ctx, inst, lhs, rhs, ">=", false);
436}
437
438void EmitFPUnordGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
439 std::string_view rhs) {
440 Compare(ctx, inst, lhs, rhs, ">=", false);
441}
442
443void EmitFPIsNan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
444 [[maybe_unused]] std::string_view value) {
445 NotImplemented();
446}
447
448void EmitFPIsNan32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
449 ctx.AddU1("{}=isnan({});", inst, value);
450}
451
452void EmitFPIsNan64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
453 ctx.AddU1("{}=isnan({});", inst, value);
454}
455
456} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
new file mode 100644
index 000000000..447eb8e0a
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
@@ -0,0 +1,799 @@
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 <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/modifiers.h"
10#include "shader_recompiler/frontend/ir/value.h"
11#include "shader_recompiler/profile.h"
12
13namespace Shader::Backend::GLSL {
14namespace {
15std::string Texture(EmitContext& ctx, const IR::TextureInstInfo& info, const IR::Value& index) {
16 const auto def{info.type == TextureType::Buffer ? ctx.texture_buffers.at(info.descriptor_index)
17 : ctx.textures.at(info.descriptor_index)};
18 const auto index_offset{def.count > 1 ? fmt::format("[{}]", ctx.var_alloc.Consume(index)) : ""};
19 return fmt::format("tex{}{}", def.binding, index_offset);
20}
21
22std::string Image(EmitContext& ctx, const IR::TextureInstInfo& info, const IR::Value& index) {
23 const auto def{info.type == TextureType::Buffer ? ctx.image_buffers.at(info.descriptor_index)
24 : ctx.images.at(info.descriptor_index)};
25 const auto index_offset{def.count > 1 ? fmt::format("[{}]", ctx.var_alloc.Consume(index)) : ""};
26 return fmt::format("img{}{}", def.binding, index_offset);
27}
28
29std::string CastToIntVec(std::string_view value, const IR::TextureInstInfo& info) {
30 switch (info.type) {
31 case TextureType::Color1D:
32 case TextureType::Buffer:
33 return fmt::format("int({})", value);
34 case TextureType::ColorArray1D:
35 case TextureType::Color2D:
36 case TextureType::ColorArray2D:
37 return fmt::format("ivec2({})", value);
38 case TextureType::Color3D:
39 case TextureType::ColorCube:
40 return fmt::format("ivec3({})", value);
41 case TextureType::ColorArrayCube:
42 return fmt::format("ivec4({})", value);
43 default:
44 throw NotImplementedException("Integer cast for TextureType {}", info.type.Value());
45 }
46}
47
48std::string CoordsCastToInt(std::string_view value, const IR::TextureInstInfo& info) {
49 switch (info.type) {
50 case TextureType::Color1D:
51 case TextureType::Buffer:
52 return fmt::format("int({})", value);
53 case TextureType::ColorArray1D:
54 case TextureType::Color2D:
55 return fmt::format("ivec2({})", value);
56 case TextureType::ColorArray2D:
57 case TextureType::Color3D:
58 case TextureType::ColorCube:
59 return fmt::format("ivec3({})", value);
60 case TextureType::ColorArrayCube:
61 return fmt::format("ivec4({})", value);
62 default:
63 throw NotImplementedException("TexelFetchCast type {}", info.type.Value());
64 }
65}
66
67bool NeedsShadowLodExt(TextureType type) {
68 switch (type) {
69 case TextureType::ColorArray2D:
70 case TextureType::ColorCube:
71 case TextureType::ColorArrayCube:
72 return true;
73 default:
74 return false;
75 }
76}
77
78std::string GetOffsetVec(EmitContext& ctx, const IR::Value& offset) {
79 if (offset.IsImmediate()) {
80 return fmt::format("int({})", offset.U32());
81 }
82 IR::Inst* const inst{offset.InstRecursive()};
83 if (inst->AreAllArgsImmediates()) {
84 switch (inst->GetOpcode()) {
85 case IR::Opcode::CompositeConstructU32x2:
86 return fmt::format("ivec2({},{})", inst->Arg(0).U32(), inst->Arg(1).U32());
87 case IR::Opcode::CompositeConstructU32x3:
88 return fmt::format("ivec3({},{},{})", inst->Arg(0).U32(), inst->Arg(1).U32(),
89 inst->Arg(2).U32());
90 case IR::Opcode::CompositeConstructU32x4:
91 return fmt::format("ivec4({},{},{},{})", inst->Arg(0).U32(), inst->Arg(1).U32(),
92 inst->Arg(2).U32(), inst->Arg(3).U32());
93 default:
94 break;
95 }
96 }
97 const bool has_var_aoffi{ctx.profile.support_gl_variable_aoffi};
98 if (!has_var_aoffi) {
99 LOG_WARNING(Shader_GLSL, "Device does not support variable texture offsets, STUBBING");
100 }
101 const auto offset_str{has_var_aoffi ? ctx.var_alloc.Consume(offset) : "0"};
102 switch (offset.Type()) {
103 case IR::Type::U32:
104 return fmt::format("int({})", offset_str);
105 case IR::Type::U32x2:
106 return fmt::format("ivec2({})", offset_str);
107 case IR::Type::U32x3:
108 return fmt::format("ivec3({})", offset_str);
109 case IR::Type::U32x4:
110 return fmt::format("ivec4({})", offset_str);
111 default:
112 throw NotImplementedException("Offset type {}", offset.Type());
113 }
114}
115
116std::string PtpOffsets(const IR::Value& offset, const IR::Value& offset2) {
117 const std::array values{offset.InstRecursive(), offset2.InstRecursive()};
118 if (!values[0]->AreAllArgsImmediates() || !values[1]->AreAllArgsImmediates()) {
119 LOG_WARNING(Shader_GLSL, "Not all arguments in PTP are immediate, STUBBING");
120 return "ivec2[](ivec2(0), ivec2(1), ivec2(2), ivec2(3))";
121 }
122 const IR::Opcode opcode{values[0]->GetOpcode()};
123 if (opcode != values[1]->GetOpcode() || opcode != IR::Opcode::CompositeConstructU32x4) {
124 throw LogicError("Invalid PTP arguments");
125 }
126 auto read{[&](unsigned int a, unsigned int b) { return values[a]->Arg(b).U32(); }};
127
128 return fmt::format("ivec2[](ivec2({},{}),ivec2({},{}),ivec2({},{}),ivec2({},{}))", read(0, 0),
129 read(0, 1), read(0, 2), read(0, 3), read(1, 0), read(1, 1), read(1, 2),
130 read(1, 3));
131}
132
133IR::Inst* PrepareSparse(IR::Inst& inst) {
134 const auto sparse_inst{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
135 if (sparse_inst) {
136 sparse_inst->Invalidate();
137 }
138 return sparse_inst;
139}
140} // Anonymous namespace
141
142void EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
143 std::string_view coords, std::string_view bias_lc,
144 const IR::Value& offset) {
145 const auto info{inst.Flags<IR::TextureInstInfo>()};
146 if (info.has_lod_clamp) {
147 throw NotImplementedException("EmitImageSampleImplicitLod Lod clamp samples");
148 }
149 const auto texture{Texture(ctx, info, index)};
150 const auto bias{info.has_bias ? fmt::format(",{}", bias_lc) : ""};
151 const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)};
152 const auto sparse_inst{PrepareSparse(inst)};
153 const bool supports_sparse{ctx.profile.support_gl_sparse_textures};
154 if (sparse_inst && !supports_sparse) {
155 LOG_WARNING(Shader_GLSL, "Device does not support sparse texture queries. STUBBING");
156 ctx.AddU1("{}=true;", *sparse_inst);
157 }
158 if (!sparse_inst || !supports_sparse) {
159 if (!offset.IsEmpty()) {
160 const auto offset_str{GetOffsetVec(ctx, offset)};
161 if (ctx.stage == Stage::Fragment) {
162 ctx.Add("{}=textureOffset({},{},{}{});", texel, texture, coords, offset_str, bias);
163 } else {
164 ctx.Add("{}=textureLodOffset({},{},0.0,{});", texel, texture, coords, offset_str);
165 }
166 } else {
167 if (ctx.stage == Stage::Fragment) {
168 ctx.Add("{}=texture({},{}{});", texel, texture, coords, bias);
169 } else {
170 ctx.Add("{}=textureLod({},{},0.0);", texel, texture, coords);
171 }
172 }
173 return;
174 }
175 if (!offset.IsEmpty()) {
176 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureOffsetARB({},{},{},{}{}));",
177 *sparse_inst, texture, coords, GetOffsetVec(ctx, offset), texel, bias);
178 } else {
179 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureARB({},{},{}{}));", *sparse_inst,
180 texture, coords, texel, bias);
181 }
182}
183
184void EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
185 std::string_view coords, std::string_view lod_lc,
186 const IR::Value& offset) {
187 const auto info{inst.Flags<IR::TextureInstInfo>()};
188 if (info.has_bias) {
189 throw NotImplementedException("EmitImageSampleExplicitLod Bias texture samples");
190 }
191 if (info.has_lod_clamp) {
192 throw NotImplementedException("EmitImageSampleExplicitLod Lod clamp samples");
193 }
194 const auto texture{Texture(ctx, info, index)};
195 const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)};
196 const auto sparse_inst{PrepareSparse(inst)};
197 const bool supports_sparse{ctx.profile.support_gl_sparse_textures};
198 if (sparse_inst && !supports_sparse) {
199 LOG_WARNING(Shader_GLSL, "Device does not support sparse texture queries. STUBBING");
200 ctx.AddU1("{}=true;", *sparse_inst);
201 }
202 if (!sparse_inst || !supports_sparse) {
203 if (!offset.IsEmpty()) {
204 ctx.Add("{}=textureLodOffset({},{},{},{});", texel, texture, coords, lod_lc,
205 GetOffsetVec(ctx, offset));
206 } else {
207 ctx.Add("{}=textureLod({},{},{});", texel, texture, coords, lod_lc);
208 }
209 return;
210 }
211 if (!offset.IsEmpty()) {
212 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTexelFetchOffsetARB({},{},int({}),{},{}));",
213 *sparse_inst, texture, CastToIntVec(coords, info), lod_lc,
214 GetOffsetVec(ctx, offset), texel);
215 } else {
216 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureLodARB({},{},{},{}));", *sparse_inst,
217 texture, coords, lod_lc, texel);
218 }
219}
220
221void EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
222 std::string_view coords, std::string_view dref,
223 std::string_view bias_lc, const IR::Value& offset) {
224 const auto info{inst.Flags<IR::TextureInstInfo>()};
225 const auto sparse_inst{PrepareSparse(inst)};
226 if (sparse_inst) {
227 throw NotImplementedException("EmitImageSampleDrefImplicitLod Sparse texture samples");
228 }
229 if (info.has_bias) {
230 throw NotImplementedException("EmitImageSampleDrefImplicitLod Bias texture samples");
231 }
232 if (info.has_lod_clamp) {
233 throw NotImplementedException("EmitImageSampleDrefImplicitLod Lod clamp samples");
234 }
235 const auto texture{Texture(ctx, info, index)};
236 const auto bias{info.has_bias ? fmt::format(",{}", bias_lc) : ""};
237 const bool needs_shadow_ext{NeedsShadowLodExt(info.type)};
238 const auto cast{needs_shadow_ext ? "vec4" : "vec3"};
239 const bool use_grad{!ctx.profile.support_gl_texture_shadow_lod &&
240 ctx.stage != Stage::Fragment && needs_shadow_ext};
241 if (use_grad) {
242 LOG_WARNING(Shader_GLSL,
243 "Device lacks GL_EXT_texture_shadow_lod. Using textureGrad fallback");
244 if (info.type == TextureType::ColorArrayCube) {
245 LOG_WARNING(Shader_GLSL, "textureGrad does not support ColorArrayCube. Stubbing");
246 ctx.AddF32("{}=0.0f;", inst);
247 return;
248 }
249 const auto d_cast{info.type == TextureType::ColorArray2D ? "vec2" : "vec3"};
250 ctx.AddF32("{}=textureGrad({},{}({},{}),{}(0),{}(0));", inst, texture, cast, coords, dref,
251 d_cast, d_cast);
252 return;
253 }
254 if (!offset.IsEmpty()) {
255 const auto offset_str{GetOffsetVec(ctx, offset)};
256 if (ctx.stage == Stage::Fragment) {
257 ctx.AddF32("{}=textureOffset({},{}({},{}),{}{});", inst, texture, cast, coords, dref,
258 offset_str, bias);
259 } else {
260 ctx.AddF32("{}=textureLodOffset({},{}({},{}),0.0,{});", inst, texture, cast, coords,
261 dref, offset_str);
262 }
263 } else {
264 if (ctx.stage == Stage::Fragment) {
265 if (info.type == TextureType::ColorArrayCube) {
266 ctx.AddF32("{}=texture({},vec4({}),{});", inst, texture, coords, dref);
267 } else {
268 ctx.AddF32("{}=texture({},{}({},{}){});", inst, texture, cast, coords, dref, bias);
269 }
270 } else {
271 ctx.AddF32("{}=textureLod({},{}({},{}),0.0);", inst, texture, cast, coords, dref);
272 }
273 }
274}
275
276void EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
277 std::string_view coords, std::string_view dref,
278 std::string_view lod_lc, const IR::Value& offset) {
279 const auto info{inst.Flags<IR::TextureInstInfo>()};
280 const auto sparse_inst{PrepareSparse(inst)};
281 if (sparse_inst) {
282 throw NotImplementedException("EmitImageSampleDrefExplicitLod Sparse texture samples");
283 }
284 if (info.has_bias) {
285 throw NotImplementedException("EmitImageSampleDrefExplicitLod Bias texture samples");
286 }
287 if (info.has_lod_clamp) {
288 throw NotImplementedException("EmitImageSampleDrefExplicitLod Lod clamp samples");
289 }
290 const auto texture{Texture(ctx, info, index)};
291 const bool needs_shadow_ext{NeedsShadowLodExt(info.type)};
292 const bool use_grad{!ctx.profile.support_gl_texture_shadow_lod && needs_shadow_ext};
293 const auto cast{needs_shadow_ext ? "vec4" : "vec3"};
294 if (use_grad) {
295 LOG_WARNING(Shader_GLSL,
296 "Device lacks GL_EXT_texture_shadow_lod. Using textureGrad fallback");
297 if (info.type == TextureType::ColorArrayCube) {
298 LOG_WARNING(Shader_GLSL, "textureGrad does not support ColorArrayCube. Stubbing");
299 ctx.AddF32("{}=0.0f;", inst);
300 return;
301 }
302 const auto d_cast{info.type == TextureType::ColorArray2D ? "vec2" : "vec3"};
303 ctx.AddF32("{}=textureGrad({},{}({},{}),{}(0),{}(0));", inst, texture, cast, coords, dref,
304 d_cast, d_cast);
305 return;
306 }
307 if (!offset.IsEmpty()) {
308 const auto offset_str{GetOffsetVec(ctx, offset)};
309 if (info.type == TextureType::ColorArrayCube) {
310 ctx.AddF32("{}=textureLodOffset({},{},{},{},{});", inst, texture, coords, dref, lod_lc,
311 offset_str);
312 } else {
313 ctx.AddF32("{}=textureLodOffset({},{}({},{}),{},{});", inst, texture, cast, coords,
314 dref, lod_lc, offset_str);
315 }
316 } else {
317 if (info.type == TextureType::ColorArrayCube) {
318 ctx.AddF32("{}=textureLod({},{},{},{});", inst, texture, coords, dref, lod_lc);
319 } else {
320 ctx.AddF32("{}=textureLod({},{}({},{}),{});", inst, texture, cast, coords, dref,
321 lod_lc);
322 }
323 }
324}
325
326void EmitImageGather(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
327 std::string_view coords, const IR::Value& offset, const IR::Value& offset2) {
328 const auto info{inst.Flags<IR::TextureInstInfo>()};
329 const auto texture{Texture(ctx, info, index)};
330 const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)};
331 const auto sparse_inst{PrepareSparse(inst)};
332 const bool supports_sparse{ctx.profile.support_gl_sparse_textures};
333 if (sparse_inst && !supports_sparse) {
334 LOG_WARNING(Shader_GLSL, "Device does not support sparse texture queries. STUBBING");
335 ctx.AddU1("{}=true;", *sparse_inst);
336 }
337 if (!sparse_inst || !supports_sparse) {
338 if (offset.IsEmpty()) {
339 ctx.Add("{}=textureGather({},{},int({}));", texel, texture, coords,
340 info.gather_component);
341 return;
342 }
343 if (offset2.IsEmpty()) {
344 ctx.Add("{}=textureGatherOffset({},{},{},int({}));", texel, texture, coords,
345 GetOffsetVec(ctx, offset), info.gather_component);
346 return;
347 }
348 // PTP
349 const auto offsets{PtpOffsets(offset, offset2)};
350 ctx.Add("{}=textureGatherOffsets({},{},{},int({}));", texel, texture, coords, offsets,
351 info.gather_component);
352 return;
353 }
354 if (offset.IsEmpty()) {
355 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureGatherARB({},{},{},int({})));",
356 *sparse_inst, texture, coords, texel, info.gather_component);
357 return;
358 }
359 if (offset2.IsEmpty()) {
360 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureGatherOffsetARB({},{},{},{},int({})));",
361 *sparse_inst, texture, CastToIntVec(coords, info), GetOffsetVec(ctx, offset),
362 texel, info.gather_component);
363 return;
364 }
365 // PTP
366 const auto offsets{PtpOffsets(offset, offset2)};
367 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureGatherOffsetARB({},{},{},{},int({})));",
368 *sparse_inst, texture, CastToIntVec(coords, info), offsets, texel,
369 info.gather_component);
370}
371
372void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
373 std::string_view coords, const IR::Value& offset, const IR::Value& offset2,
374 std::string_view dref) {
375 const auto info{inst.Flags<IR::TextureInstInfo>()};
376 const auto texture{Texture(ctx, info, index)};
377 const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)};
378 const auto sparse_inst{PrepareSparse(inst)};
379 const bool supports_sparse{ctx.profile.support_gl_sparse_textures};
380 if (sparse_inst && !supports_sparse) {
381 LOG_WARNING(Shader_GLSL, "Device does not support sparse texture queries. STUBBING");
382 ctx.AddU1("{}=true;", *sparse_inst);
383 }
384 if (!sparse_inst || !supports_sparse) {
385 if (offset.IsEmpty()) {
386 ctx.Add("{}=textureGather({},{},{});", texel, texture, coords, dref);
387 return;
388 }
389 if (offset2.IsEmpty()) {
390 ctx.Add("{}=textureGatherOffset({},{},{},{});", texel, texture, coords, dref,
391 GetOffsetVec(ctx, offset));
392 return;
393 }
394 // PTP
395 const auto offsets{PtpOffsets(offset, offset2)};
396 ctx.Add("{}=textureGatherOffsets({},{},{},{});", texel, texture, coords, dref, offsets);
397 return;
398 }
399 if (offset.IsEmpty()) {
400 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureGatherARB({},{},{},{}));", *sparse_inst,
401 texture, coords, dref, texel);
402 return;
403 }
404 if (offset2.IsEmpty()) {
405 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureGatherOffsetARB({},{},{},,{},{}));",
406 *sparse_inst, texture, CastToIntVec(coords, info), dref,
407 GetOffsetVec(ctx, offset), texel);
408 return;
409 }
410 // PTP
411 const auto offsets{PtpOffsets(offset, offset2)};
412 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureGatherOffsetARB({},{},{},,{},{}));",
413 *sparse_inst, texture, CastToIntVec(coords, info), dref, offsets, texel);
414}
415
416void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
417 std::string_view coords, std::string_view offset, std::string_view lod,
418 [[maybe_unused]] std::string_view ms) {
419 const auto info{inst.Flags<IR::TextureInstInfo>()};
420 if (info.has_bias) {
421 throw NotImplementedException("EmitImageFetch Bias texture samples");
422 }
423 if (info.has_lod_clamp) {
424 throw NotImplementedException("EmitImageFetch Lod clamp samples");
425 }
426 const auto texture{Texture(ctx, info, index)};
427 const auto sparse_inst{PrepareSparse(inst)};
428 const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)};
429 const bool supports_sparse{ctx.profile.support_gl_sparse_textures};
430 if (sparse_inst && !supports_sparse) {
431 LOG_WARNING(Shader_GLSL, "Device does not support sparse texture queries. STUBBING");
432 ctx.AddU1("{}=true;", *sparse_inst);
433 }
434 if (!sparse_inst || !supports_sparse) {
435 if (!offset.empty()) {
436 ctx.Add("{}=texelFetchOffset({},{},int({}),{});", texel, texture,
437 CoordsCastToInt(coords, info), lod, CoordsCastToInt(offset, info));
438 } else {
439 if (info.type == TextureType::Buffer) {
440 ctx.Add("{}=texelFetch({},int({}));", texel, texture, coords);
441 } else {
442 ctx.Add("{}=texelFetch({},{},int({}));", texel, texture,
443 CoordsCastToInt(coords, info), lod);
444 }
445 }
446 return;
447 }
448 if (!offset.empty()) {
449 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTexelFetchOffsetARB({},{},int({}),{},{}));",
450 *sparse_inst, texture, CastToIntVec(coords, info), lod,
451 CastToIntVec(offset, info), texel);
452 } else {
453 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTexelFetchARB({},{},int({}),{}));",
454 *sparse_inst, texture, CastToIntVec(coords, info), lod, texel);
455 }
456}
457
458void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
459 std::string_view lod) {
460 const auto info{inst.Flags<IR::TextureInstInfo>()};
461 const auto texture{Texture(ctx, info, index)};
462 switch (info.type) {
463 case TextureType::Color1D:
464 return ctx.AddU32x4(
465 "{}=uvec4(uint(textureSize({},int({}))),0u,0u,uint(textureQueryLevels({})));", inst,
466 texture, lod, texture);
467 case TextureType::ColorArray1D:
468 case TextureType::Color2D:
469 case TextureType::ColorCube:
470 return ctx.AddU32x4(
471 "{}=uvec4(uvec2(textureSize({},int({}))),0u,uint(textureQueryLevels({})));", inst,
472 texture, lod, texture);
473 case TextureType::ColorArray2D:
474 case TextureType::Color3D:
475 case TextureType::ColorArrayCube:
476 return ctx.AddU32x4(
477 "{}=uvec4(uvec3(textureSize({},int({}))),uint(textureQueryLevels({})));", inst, texture,
478 lod, texture);
479 case TextureType::Buffer:
480 throw NotImplementedException("EmitImageQueryDimensions Texture buffers");
481 }
482 throw LogicError("Unspecified image type {}", info.type.Value());
483}
484
485void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
486 std::string_view coords) {
487 const auto info{inst.Flags<IR::TextureInstInfo>()};
488 const auto texture{Texture(ctx, info, index)};
489 return ctx.AddF32x4("{}=vec4(textureQueryLod({},{}),0.0,0.0);", inst, texture, coords);
490}
491
492void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
493 std::string_view coords, const IR::Value& derivatives,
494 const IR::Value& offset, [[maybe_unused]] const IR::Value& lod_clamp) {
495 const auto info{inst.Flags<IR::TextureInstInfo>()};
496 if (info.has_lod_clamp) {
497 throw NotImplementedException("EmitImageGradient Lod clamp samples");
498 }
499 const auto sparse_inst{PrepareSparse(inst)};
500 if (sparse_inst) {
501 throw NotImplementedException("EmitImageGradient Sparse");
502 }
503 if (!offset.IsEmpty()) {
504 throw NotImplementedException("EmitImageGradient offset");
505 }
506 const auto texture{Texture(ctx, info, index)};
507 const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)};
508 const bool multi_component{info.num_derivates > 1 || info.has_lod_clamp};
509 const auto derivatives_vec{ctx.var_alloc.Consume(derivatives)};
510 if (multi_component) {
511 ctx.Add("{}=textureGrad({},{},vec2({}.xz),vec2({}.yz));", texel, texture, coords,
512 derivatives_vec, derivatives_vec);
513 } else {
514 ctx.Add("{}=textureGrad({},{},float({}.x),float({}.y));", texel, texture, coords,
515 derivatives_vec, derivatives_vec);
516 }
517}
518
519void EmitImageRead(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
520 std::string_view coords) {
521 const auto info{inst.Flags<IR::TextureInstInfo>()};
522 const auto sparse_inst{PrepareSparse(inst)};
523 if (sparse_inst) {
524 throw NotImplementedException("EmitImageRead Sparse");
525 }
526 const auto image{Image(ctx, info, index)};
527 ctx.AddU32x4("{}=uvec4(imageLoad({},{}));", inst, image, CoordsCastToInt(coords, info));
528}
529
530void EmitImageWrite(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
531 std::string_view coords, std::string_view color) {
532 const auto info{inst.Flags<IR::TextureInstInfo>()};
533 const auto image{Image(ctx, info, index)};
534 ctx.Add("imageStore({},{},{});", image, CoordsCastToInt(coords, info), color);
535}
536
537void EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
538 std::string_view coords, std::string_view value) {
539 const auto info{inst.Flags<IR::TextureInstInfo>()};
540 const auto image{Image(ctx, info, index)};
541 ctx.AddU32("{}=imageAtomicAdd({},{},{});", inst, image, CoordsCastToInt(coords, info), value);
542}
543
544void EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
545 std::string_view coords, std::string_view value) {
546 const auto info{inst.Flags<IR::TextureInstInfo>()};
547 const auto image{Image(ctx, info, index)};
548 ctx.AddU32("{}=imageAtomicMin({},{},int({}));", inst, image, CoordsCastToInt(coords, info),
549 value);
550}
551
552void EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
553 std::string_view coords, std::string_view value) {
554 const auto info{inst.Flags<IR::TextureInstInfo>()};
555 const auto image{Image(ctx, info, index)};
556 ctx.AddU32("{}=imageAtomicMin({},{},uint({}));", inst, image, CoordsCastToInt(coords, info),
557 value);
558}
559
560void EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
561 std::string_view coords, std::string_view value) {
562 const auto info{inst.Flags<IR::TextureInstInfo>()};
563 const auto image{Image(ctx, info, index)};
564 ctx.AddU32("{}=imageAtomicMax({},{},int({}));", inst, image, CoordsCastToInt(coords, info),
565 value);
566}
567
568void EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
569 std::string_view coords, std::string_view value) {
570 const auto info{inst.Flags<IR::TextureInstInfo>()};
571 const auto image{Image(ctx, info, index)};
572 ctx.AddU32("{}=imageAtomicMax({},{},uint({}));", inst, image, CoordsCastToInt(coords, info),
573 value);
574}
575
576void EmitImageAtomicInc32(EmitContext&, IR::Inst&, const IR::Value&, std::string_view,
577 std::string_view) {
578 NotImplemented();
579}
580
581void EmitImageAtomicDec32(EmitContext&, IR::Inst&, const IR::Value&, std::string_view,
582 std::string_view) {
583 NotImplemented();
584}
585
586void EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
587 std::string_view coords, std::string_view value) {
588 const auto info{inst.Flags<IR::TextureInstInfo>()};
589 const auto image{Image(ctx, info, index)};
590 ctx.AddU32("{}=imageAtomicAnd({},{},{});", inst, image, CoordsCastToInt(coords, info), value);
591}
592
593void EmitImageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
594 std::string_view coords, std::string_view value) {
595 const auto info{inst.Flags<IR::TextureInstInfo>()};
596 const auto image{Image(ctx, info, index)};
597 ctx.AddU32("{}=imageAtomicOr({},{},{});", inst, image, CoordsCastToInt(coords, info), value);
598}
599
600void EmitImageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
601 std::string_view coords, std::string_view value) {
602 const auto info{inst.Flags<IR::TextureInstInfo>()};
603 const auto image{Image(ctx, info, index)};
604 ctx.AddU32("{}=imageAtomicXor({},{},{});", inst, image, CoordsCastToInt(coords, info), value);
605}
606
607void EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
608 std::string_view coords, std::string_view value) {
609 const auto info{inst.Flags<IR::TextureInstInfo>()};
610 const auto image{Image(ctx, info, index)};
611 ctx.AddU32("{}=imageAtomicExchange({},{},{});", inst, image, CoordsCastToInt(coords, info),
612 value);
613}
614
615void EmitBindlessImageSampleImplicitLod(EmitContext&) {
616 NotImplemented();
617}
618
619void EmitBindlessImageSampleExplicitLod(EmitContext&) {
620 NotImplemented();
621}
622
623void EmitBindlessImageSampleDrefImplicitLod(EmitContext&) {
624 NotImplemented();
625}
626
627void EmitBindlessImageSampleDrefExplicitLod(EmitContext&) {
628 NotImplemented();
629}
630
631void EmitBindlessImageGather(EmitContext&) {
632 NotImplemented();
633}
634
635void EmitBindlessImageGatherDref(EmitContext&) {
636 NotImplemented();
637}
638
639void EmitBindlessImageFetch(EmitContext&) {
640 NotImplemented();
641}
642
643void EmitBindlessImageQueryDimensions(EmitContext&) {
644 NotImplemented();
645}
646
647void EmitBindlessImageQueryLod(EmitContext&) {
648 NotImplemented();
649}
650
651void EmitBindlessImageGradient(EmitContext&) {
652 NotImplemented();
653}
654
655void EmitBindlessImageRead(EmitContext&) {
656 NotImplemented();
657}
658
659void EmitBindlessImageWrite(EmitContext&) {
660 NotImplemented();
661}
662
663void EmitBoundImageSampleImplicitLod(EmitContext&) {
664 NotImplemented();
665}
666
667void EmitBoundImageSampleExplicitLod(EmitContext&) {
668 NotImplemented();
669}
670
671void EmitBoundImageSampleDrefImplicitLod(EmitContext&) {
672 NotImplemented();
673}
674
675void EmitBoundImageSampleDrefExplicitLod(EmitContext&) {
676 NotImplemented();
677}
678
679void EmitBoundImageGather(EmitContext&) {
680 NotImplemented();
681}
682
683void EmitBoundImageGatherDref(EmitContext&) {
684 NotImplemented();
685}
686
687void EmitBoundImageFetch(EmitContext&) {
688 NotImplemented();
689}
690
691void EmitBoundImageQueryDimensions(EmitContext&) {
692 NotImplemented();
693}
694
695void EmitBoundImageQueryLod(EmitContext&) {
696 NotImplemented();
697}
698
699void EmitBoundImageGradient(EmitContext&) {
700 NotImplemented();
701}
702
703void EmitBoundImageRead(EmitContext&) {
704 NotImplemented();
705}
706
707void EmitBoundImageWrite(EmitContext&) {
708 NotImplemented();
709}
710
711void EmitBindlessImageAtomicIAdd32(EmitContext&) {
712 NotImplemented();
713}
714
715void EmitBindlessImageAtomicSMin32(EmitContext&) {
716 NotImplemented();
717}
718
719void EmitBindlessImageAtomicUMin32(EmitContext&) {
720 NotImplemented();
721}
722
723void EmitBindlessImageAtomicSMax32(EmitContext&) {
724 NotImplemented();
725}
726
727void EmitBindlessImageAtomicUMax32(EmitContext&) {
728 NotImplemented();
729}
730
731void EmitBindlessImageAtomicInc32(EmitContext&) {
732 NotImplemented();
733}
734
735void EmitBindlessImageAtomicDec32(EmitContext&) {
736 NotImplemented();
737}
738
739void EmitBindlessImageAtomicAnd32(EmitContext&) {
740 NotImplemented();
741}
742
743void EmitBindlessImageAtomicOr32(EmitContext&) {
744 NotImplemented();
745}
746
747void EmitBindlessImageAtomicXor32(EmitContext&) {
748 NotImplemented();
749}
750
751void EmitBindlessImageAtomicExchange32(EmitContext&) {
752 NotImplemented();
753}
754
755void EmitBoundImageAtomicIAdd32(EmitContext&) {
756 NotImplemented();
757}
758
759void EmitBoundImageAtomicSMin32(EmitContext&) {
760 NotImplemented();
761}
762
763void EmitBoundImageAtomicUMin32(EmitContext&) {
764 NotImplemented();
765}
766
767void EmitBoundImageAtomicSMax32(EmitContext&) {
768 NotImplemented();
769}
770
771void EmitBoundImageAtomicUMax32(EmitContext&) {
772 NotImplemented();
773}
774
775void EmitBoundImageAtomicInc32(EmitContext&) {
776 NotImplemented();
777}
778
779void EmitBoundImageAtomicDec32(EmitContext&) {
780 NotImplemented();
781}
782
783void EmitBoundImageAtomicAnd32(EmitContext&) {
784 NotImplemented();
785}
786
787void EmitBoundImageAtomicOr32(EmitContext&) {
788 NotImplemented();
789}
790
791void EmitBoundImageAtomicXor32(EmitContext&) {
792 NotImplemented();
793}
794
795void EmitBoundImageAtomicExchange32(EmitContext&) {
796 NotImplemented();
797}
798
799} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
new file mode 100644
index 000000000..5936d086f
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
@@ -0,0 +1,702 @@
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 <string_view>
8
9#include "common/common_types.h"
10
11namespace Shader::IR {
12enum class Attribute : u64;
13enum class Patch : u64;
14class Inst;
15class Value;
16} // namespace Shader::IR
17
18namespace Shader::Backend::GLSL {
19class EmitContext;
20
21#define NotImplemented() throw NotImplementedException("GLSL instruction {}", __func__)
22
23// Microinstruction emitters
24void EmitPhi(EmitContext& ctx, IR::Inst& inst);
25void EmitVoid(EmitContext& ctx);
26void EmitIdentity(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
27void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
28void EmitReference(EmitContext& ctx, const IR::Value& value);
29void EmitPhiMove(EmitContext& ctx, const IR::Value& phi, const IR::Value& value);
30void EmitJoin(EmitContext& ctx);
31void EmitDemoteToHelperInvocation(EmitContext& ctx);
32void EmitBarrier(EmitContext& ctx);
33void EmitWorkgroupMemoryBarrier(EmitContext& ctx);
34void EmitDeviceMemoryBarrier(EmitContext& ctx);
35void EmitPrologue(EmitContext& ctx);
36void EmitEpilogue(EmitContext& ctx);
37void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream);
38void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream);
39void EmitGetRegister(EmitContext& ctx);
40void EmitSetRegister(EmitContext& ctx);
41void EmitGetPred(EmitContext& ctx);
42void EmitSetPred(EmitContext& ctx);
43void EmitSetGotoVariable(EmitContext& ctx);
44void EmitGetGotoVariable(EmitContext& ctx);
45void EmitSetIndirectBranchVariable(EmitContext& ctx);
46void EmitGetIndirectBranchVariable(EmitContext& ctx);
47void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
48 const IR::Value& offset);
49void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
50 const IR::Value& offset);
51void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
52 const IR::Value& offset);
53void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
54 const IR::Value& offset);
55void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
56 const IR::Value& offset);
57void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
58 const IR::Value& offset);
59void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
60 const IR::Value& offset);
61void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
62 std::string_view vertex);
63void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view value,
64 std::string_view vertex);
65void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, std::string_view offset,
66 std::string_view vertex);
67void EmitSetAttributeIndexed(EmitContext& ctx, std::string_view offset, std::string_view value,
68 std::string_view vertex);
69void EmitGetPatch(EmitContext& ctx, IR::Inst& inst, IR::Patch patch);
70void EmitSetPatch(EmitContext& ctx, IR::Patch patch, std::string_view value);
71void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, std::string_view value);
72void EmitSetSampleMask(EmitContext& ctx, std::string_view value);
73void EmitSetFragDepth(EmitContext& ctx, std::string_view value);
74void EmitGetZFlag(EmitContext& ctx);
75void EmitGetSFlag(EmitContext& ctx);
76void EmitGetCFlag(EmitContext& ctx);
77void EmitGetOFlag(EmitContext& ctx);
78void EmitSetZFlag(EmitContext& ctx);
79void EmitSetSFlag(EmitContext& ctx);
80void EmitSetCFlag(EmitContext& ctx);
81void EmitSetOFlag(EmitContext& ctx);
82void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst);
83void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst);
84void EmitInvocationId(EmitContext& ctx, IR::Inst& inst);
85void EmitSampleId(EmitContext& ctx, IR::Inst& inst);
86void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst);
87void EmitYDirection(EmitContext& ctx, IR::Inst& inst);
88void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset);
89void EmitWriteLocal(EmitContext& ctx, std::string_view word_offset, std::string_view value);
90void EmitUndefU1(EmitContext& ctx, IR::Inst& inst);
91void EmitUndefU8(EmitContext& ctx, IR::Inst& inst);
92void EmitUndefU16(EmitContext& ctx, IR::Inst& inst);
93void EmitUndefU32(EmitContext& ctx, IR::Inst& inst);
94void EmitUndefU64(EmitContext& ctx, IR::Inst& inst);
95void EmitLoadGlobalU8(EmitContext& ctx);
96void EmitLoadGlobalS8(EmitContext& ctx);
97void EmitLoadGlobalU16(EmitContext& ctx);
98void EmitLoadGlobalS16(EmitContext& ctx);
99void EmitLoadGlobal32(EmitContext& ctx, IR::Inst& inst, std::string_view address);
100void EmitLoadGlobal64(EmitContext& ctx, IR::Inst& inst, std::string_view address);
101void EmitLoadGlobal128(EmitContext& ctx, IR::Inst& inst, std::string_view address);
102void EmitWriteGlobalU8(EmitContext& ctx);
103void EmitWriteGlobalS8(EmitContext& ctx);
104void EmitWriteGlobalU16(EmitContext& ctx);
105void EmitWriteGlobalS16(EmitContext& ctx);
106void EmitWriteGlobal32(EmitContext& ctx, std::string_view address, std::string_view value);
107void EmitWriteGlobal64(EmitContext& ctx, std::string_view address, std::string_view value);
108void EmitWriteGlobal128(EmitContext& ctx, std::string_view address, std::string_view value);
109void EmitLoadStorageU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
110 const IR::Value& offset);
111void EmitLoadStorageS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
112 const IR::Value& offset);
113void EmitLoadStorageU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
114 const IR::Value& offset);
115void EmitLoadStorageS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
116 const IR::Value& offset);
117void EmitLoadStorage32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
118 const IR::Value& offset);
119void EmitLoadStorage64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
120 const IR::Value& offset);
121void EmitLoadStorage128(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
122 const IR::Value& offset);
123void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
124 std::string_view value);
125void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
126 std::string_view value);
127void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
128 std::string_view value);
129void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
130 std::string_view value);
131void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
132 std::string_view value);
133void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
134 std::string_view value);
135void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
136 std::string_view value);
137void EmitLoadSharedU8(EmitContext& ctx, IR::Inst& inst, std::string_view offset);
138void EmitLoadSharedS8(EmitContext& ctx, IR::Inst& inst, std::string_view offset);
139void EmitLoadSharedU16(EmitContext& ctx, IR::Inst& inst, std::string_view offset);
140void EmitLoadSharedS16(EmitContext& ctx, IR::Inst& inst, std::string_view offset);
141void EmitLoadSharedU32(EmitContext& ctx, IR::Inst& inst, std::string_view offset);
142void EmitLoadSharedU64(EmitContext& ctx, IR::Inst& inst, std::string_view offset);
143void EmitLoadSharedU128(EmitContext& ctx, IR::Inst& inst, std::string_view offset);
144void EmitWriteSharedU8(EmitContext& ctx, std::string_view offset, std::string_view value);
145void EmitWriteSharedU16(EmitContext& ctx, std::string_view offset, std::string_view value);
146void EmitWriteSharedU32(EmitContext& ctx, std::string_view offset, std::string_view value);
147void EmitWriteSharedU64(EmitContext& ctx, std::string_view offset, std::string_view value);
148void EmitWriteSharedU128(EmitContext& ctx, std::string_view offset, std::string_view value);
149void EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
150 std::string_view e2);
151void EmitCompositeConstructU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
152 std::string_view e2, std::string_view e3);
153void EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
154 std::string_view e2, std::string_view e3, std::string_view e4);
155void EmitCompositeExtractU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
156 u32 index);
157void EmitCompositeExtractU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
158 u32 index);
159void EmitCompositeExtractU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
160 u32 index);
161void EmitCompositeInsertU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
162 std::string_view object, u32 index);
163void EmitCompositeInsertU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
164 std::string_view object, u32 index);
165void EmitCompositeInsertU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
166 std::string_view object, u32 index);
167void EmitCompositeConstructF16x2(EmitContext& ctx, std::string_view e1, std::string_view e2);
168void EmitCompositeConstructF16x3(EmitContext& ctx, std::string_view e1, std::string_view e2,
169 std::string_view e3);
170void EmitCompositeConstructF16x4(EmitContext& ctx, std::string_view e1, std::string_view e2,
171 std::string_view e3, std::string_view e4);
172void EmitCompositeExtractF16x2(EmitContext& ctx, std::string_view composite, u32 index);
173void EmitCompositeExtractF16x3(EmitContext& ctx, std::string_view composite, u32 index);
174void EmitCompositeExtractF16x4(EmitContext& ctx, std::string_view composite, u32 index);
175void EmitCompositeInsertF16x2(EmitContext& ctx, std::string_view composite, std::string_view object,
176 u32 index);
177void EmitCompositeInsertF16x3(EmitContext& ctx, std::string_view composite, std::string_view object,
178 u32 index);
179void EmitCompositeInsertF16x4(EmitContext& ctx, std::string_view composite, std::string_view object,
180 u32 index);
181void EmitCompositeConstructF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
182 std::string_view e2);
183void EmitCompositeConstructF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
184 std::string_view e2, std::string_view e3);
185void EmitCompositeConstructF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
186 std::string_view e2, std::string_view e3, std::string_view e4);
187void EmitCompositeExtractF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
188 u32 index);
189void EmitCompositeExtractF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
190 u32 index);
191void EmitCompositeExtractF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
192 u32 index);
193void EmitCompositeInsertF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
194 std::string_view object, u32 index);
195void EmitCompositeInsertF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
196 std::string_view object, u32 index);
197void EmitCompositeInsertF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
198 std::string_view object, u32 index);
199void EmitCompositeConstructF64x2(EmitContext& ctx);
200void EmitCompositeConstructF64x3(EmitContext& ctx);
201void EmitCompositeConstructF64x4(EmitContext& ctx);
202void EmitCompositeExtractF64x2(EmitContext& ctx);
203void EmitCompositeExtractF64x3(EmitContext& ctx);
204void EmitCompositeExtractF64x4(EmitContext& ctx);
205void EmitCompositeInsertF64x2(EmitContext& ctx, std::string_view composite, std::string_view object,
206 u32 index);
207void EmitCompositeInsertF64x3(EmitContext& ctx, std::string_view composite, std::string_view object,
208 u32 index);
209void EmitCompositeInsertF64x4(EmitContext& ctx, std::string_view composite, std::string_view object,
210 u32 index);
211void EmitSelectU1(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
212 std::string_view true_value, std::string_view false_value);
213void EmitSelectU8(EmitContext& ctx, std::string_view cond, std::string_view true_value,
214 std::string_view false_value);
215void EmitSelectU16(EmitContext& ctx, std::string_view cond, std::string_view true_value,
216 std::string_view false_value);
217void EmitSelectU32(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
218 std::string_view true_value, std::string_view false_value);
219void EmitSelectU64(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
220 std::string_view true_value, std::string_view false_value);
221void EmitSelectF16(EmitContext& ctx, std::string_view cond, std::string_view true_value,
222 std::string_view false_value);
223void EmitSelectF32(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
224 std::string_view true_value, std::string_view false_value);
225void EmitSelectF64(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
226 std::string_view true_value, std::string_view false_value);
227void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst);
228void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
229void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
230void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst);
231void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
232void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
233void EmitPackUint2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
234void EmitUnpackUint2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
235void EmitPackFloat2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
236void EmitUnpackFloat2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
237void EmitPackHalf2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
238void EmitUnpackHalf2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
239void EmitPackDouble2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
240void EmitUnpackDouble2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
241void EmitGetZeroFromOp(EmitContext& ctx);
242void EmitGetSignFromOp(EmitContext& ctx);
243void EmitGetCarryFromOp(EmitContext& ctx);
244void EmitGetOverflowFromOp(EmitContext& ctx);
245void EmitGetSparseFromOp(EmitContext& ctx);
246void EmitGetInBoundsFromOp(EmitContext& ctx);
247void EmitFPAbs16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
248void EmitFPAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
249void EmitFPAbs64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
250void EmitFPAdd16(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
251void EmitFPAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
252void EmitFPAdd64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
253void EmitFPFma16(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
254 std::string_view c);
255void EmitFPFma32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
256 std::string_view c);
257void EmitFPFma64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
258 std::string_view c);
259void EmitFPMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
260void EmitFPMax64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
261void EmitFPMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
262void EmitFPMin64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
263void EmitFPMul16(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
264void EmitFPMul32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
265void EmitFPMul64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
266void EmitFPNeg16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
267void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
268void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
269void EmitFPSin(EmitContext& ctx, IR::Inst& inst, std::string_view value);
270void EmitFPCos(EmitContext& ctx, IR::Inst& inst, std::string_view value);
271void EmitFPExp2(EmitContext& ctx, IR::Inst& inst, std::string_view value);
272void EmitFPLog2(EmitContext& ctx, IR::Inst& inst, std::string_view value);
273void EmitFPRecip32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
274void EmitFPRecip64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
275void EmitFPRecipSqrt32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
276void EmitFPRecipSqrt64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
277void EmitFPSqrt(EmitContext& ctx, IR::Inst& inst, std::string_view value);
278void EmitFPSaturate16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
279void EmitFPSaturate32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
280void EmitFPSaturate64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
281void EmitFPClamp16(EmitContext& ctx, IR::Inst& inst, std::string_view value,
282 std::string_view min_value, std::string_view max_value);
283void EmitFPClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value,
284 std::string_view min_value, std::string_view max_value);
285void EmitFPClamp64(EmitContext& ctx, IR::Inst& inst, std::string_view value,
286 std::string_view min_value, std::string_view max_value);
287void EmitFPRoundEven16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
288void EmitFPRoundEven32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
289void EmitFPRoundEven64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
290void EmitFPFloor16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
291void EmitFPFloor32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
292void EmitFPFloor64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
293void EmitFPCeil16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
294void EmitFPCeil32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
295void EmitFPCeil64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
296void EmitFPTrunc16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
297void EmitFPTrunc32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
298void EmitFPTrunc64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
299void EmitFPOrdEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
300void EmitFPOrdEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs);
301void EmitFPOrdEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs);
302void EmitFPUnordEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
303void EmitFPUnordEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
304 std::string_view rhs);
305void EmitFPUnordEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
306 std::string_view rhs);
307void EmitFPOrdNotEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
308void EmitFPOrdNotEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
309 std::string_view rhs);
310void EmitFPOrdNotEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
311 std::string_view rhs);
312void EmitFPUnordNotEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
313void EmitFPUnordNotEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
314 std::string_view rhs);
315void EmitFPUnordNotEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
316 std::string_view rhs);
317void EmitFPOrdLessThan16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
318void EmitFPOrdLessThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
319 std::string_view rhs);
320void EmitFPOrdLessThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
321 std::string_view rhs);
322void EmitFPUnordLessThan16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
323void EmitFPUnordLessThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
324 std::string_view rhs);
325void EmitFPUnordLessThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
326 std::string_view rhs);
327void EmitFPOrdGreaterThan16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
328void EmitFPOrdGreaterThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
329 std::string_view rhs);
330void EmitFPOrdGreaterThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
331 std::string_view rhs);
332void EmitFPUnordGreaterThan16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
333void EmitFPUnordGreaterThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
334 std::string_view rhs);
335void EmitFPUnordGreaterThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
336 std::string_view rhs);
337void EmitFPOrdLessThanEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
338void EmitFPOrdLessThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
339 std::string_view rhs);
340void EmitFPOrdLessThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
341 std::string_view rhs);
342void EmitFPUnordLessThanEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
343void EmitFPUnordLessThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
344 std::string_view rhs);
345void EmitFPUnordLessThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
346 std::string_view rhs);
347void EmitFPOrdGreaterThanEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
348void EmitFPOrdGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
349 std::string_view rhs);
350void EmitFPOrdGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
351 std::string_view rhs);
352void EmitFPUnordGreaterThanEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
353void EmitFPUnordGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
354 std::string_view rhs);
355void EmitFPUnordGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
356 std::string_view rhs);
357void EmitFPIsNan16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
358void EmitFPIsNan32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
359void EmitFPIsNan64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
360void EmitIAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
361void EmitIAdd64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
362void EmitISub32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
363void EmitISub64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
364void EmitIMul32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
365void EmitINeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
366void EmitINeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
367void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
368void EmitShiftLeftLogical32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
369 std::string_view shift);
370void EmitShiftLeftLogical64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
371 std::string_view shift);
372void EmitShiftRightLogical32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
373 std::string_view shift);
374void EmitShiftRightLogical64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
375 std::string_view shift);
376void EmitShiftRightArithmetic32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
377 std::string_view shift);
378void EmitShiftRightArithmetic64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
379 std::string_view shift);
380void EmitBitwiseAnd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
381void EmitBitwiseOr32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
382void EmitBitwiseXor32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
383void EmitBitFieldInsert(EmitContext& ctx, IR::Inst& inst, std::string_view base,
384 std::string_view insert, std::string_view offset, std::string_view count);
385void EmitBitFieldSExtract(EmitContext& ctx, IR::Inst& inst, std::string_view base,
386 std::string_view offset, std::string_view count);
387void EmitBitFieldUExtract(EmitContext& ctx, IR::Inst& inst, std::string_view base,
388 std::string_view offset, std::string_view count);
389void EmitBitReverse32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
390void EmitBitCount32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
391void EmitBitwiseNot32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
392void EmitFindSMsb32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
393void EmitFindUMsb32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
394void EmitSMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
395void EmitUMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
396void EmitSMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
397void EmitUMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
398void EmitSClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view min,
399 std::string_view max);
400void EmitUClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view min,
401 std::string_view max);
402void EmitSLessThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs);
403void EmitULessThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs);
404void EmitIEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs);
405void EmitSLessThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
406 std::string_view rhs);
407void EmitULessThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
408 std::string_view rhs);
409void EmitSGreaterThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs);
410void EmitUGreaterThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs);
411void EmitINotEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs);
412void EmitSGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
413 std::string_view rhs);
414void EmitUGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
415 std::string_view rhs);
416void EmitSharedAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
417 std::string_view value);
418void EmitSharedAtomicSMin32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
419 std::string_view value);
420void EmitSharedAtomicUMin32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
421 std::string_view value);
422void EmitSharedAtomicSMax32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
423 std::string_view value);
424void EmitSharedAtomicUMax32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
425 std::string_view value);
426void EmitSharedAtomicInc32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
427 std::string_view value);
428void EmitSharedAtomicDec32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
429 std::string_view value);
430void EmitSharedAtomicAnd32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
431 std::string_view value);
432void EmitSharedAtomicOr32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
433 std::string_view value);
434void EmitSharedAtomicXor32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
435 std::string_view value);
436void EmitSharedAtomicExchange32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
437 std::string_view value);
438void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
439 std::string_view value);
440void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
441 const IR::Value& offset, std::string_view value);
442void EmitStorageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
443 const IR::Value& offset, std::string_view value);
444void EmitStorageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
445 const IR::Value& offset, std::string_view value);
446void EmitStorageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
447 const IR::Value& offset, std::string_view value);
448void EmitStorageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
449 const IR::Value& offset, std::string_view value);
450void EmitStorageAtomicInc32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
451 const IR::Value& offset, std::string_view value);
452void EmitStorageAtomicDec32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
453 const IR::Value& offset, std::string_view value);
454void EmitStorageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
455 const IR::Value& offset, std::string_view value);
456void EmitStorageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
457 const IR::Value& offset, std::string_view value);
458void EmitStorageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
459 const IR::Value& offset, std::string_view value);
460void EmitStorageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
461 const IR::Value& offset, std::string_view value);
462void EmitStorageAtomicIAdd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
463 const IR::Value& offset, std::string_view value);
464void EmitStorageAtomicSMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
465 const IR::Value& offset, std::string_view value);
466void EmitStorageAtomicUMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
467 const IR::Value& offset, std::string_view value);
468void EmitStorageAtomicSMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
469 const IR::Value& offset, std::string_view value);
470void EmitStorageAtomicUMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
471 const IR::Value& offset, std::string_view value);
472void EmitStorageAtomicAnd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
473 const IR::Value& offset, std::string_view value);
474void EmitStorageAtomicOr64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
475 const IR::Value& offset, std::string_view value);
476void EmitStorageAtomicXor64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
477 const IR::Value& offset, std::string_view value);
478void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
479 const IR::Value& offset, std::string_view value);
480void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
481 const IR::Value& offset, std::string_view value);
482void EmitStorageAtomicAddF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
483 const IR::Value& offset, std::string_view value);
484void EmitStorageAtomicAddF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
485 const IR::Value& offset, std::string_view value);
486void EmitStorageAtomicMinF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
487 const IR::Value& offset, std::string_view value);
488void EmitStorageAtomicMinF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
489 const IR::Value& offset, std::string_view value);
490void EmitStorageAtomicMaxF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
491 const IR::Value& offset, std::string_view value);
492void EmitStorageAtomicMaxF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
493 const IR::Value& offset, std::string_view value);
494void EmitGlobalAtomicIAdd32(EmitContext& ctx);
495void EmitGlobalAtomicSMin32(EmitContext& ctx);
496void EmitGlobalAtomicUMin32(EmitContext& ctx);
497void EmitGlobalAtomicSMax32(EmitContext& ctx);
498void EmitGlobalAtomicUMax32(EmitContext& ctx);
499void EmitGlobalAtomicInc32(EmitContext& ctx);
500void EmitGlobalAtomicDec32(EmitContext& ctx);
501void EmitGlobalAtomicAnd32(EmitContext& ctx);
502void EmitGlobalAtomicOr32(EmitContext& ctx);
503void EmitGlobalAtomicXor32(EmitContext& ctx);
504void EmitGlobalAtomicExchange32(EmitContext& ctx);
505void EmitGlobalAtomicIAdd64(EmitContext& ctx);
506void EmitGlobalAtomicSMin64(EmitContext& ctx);
507void EmitGlobalAtomicUMin64(EmitContext& ctx);
508void EmitGlobalAtomicSMax64(EmitContext& ctx);
509void EmitGlobalAtomicUMax64(EmitContext& ctx);
510void EmitGlobalAtomicInc64(EmitContext& ctx);
511void EmitGlobalAtomicDec64(EmitContext& ctx);
512void EmitGlobalAtomicAnd64(EmitContext& ctx);
513void EmitGlobalAtomicOr64(EmitContext& ctx);
514void EmitGlobalAtomicXor64(EmitContext& ctx);
515void EmitGlobalAtomicExchange64(EmitContext& ctx);
516void EmitGlobalAtomicAddF32(EmitContext& ctx);
517void EmitGlobalAtomicAddF16x2(EmitContext& ctx);
518void EmitGlobalAtomicAddF32x2(EmitContext& ctx);
519void EmitGlobalAtomicMinF16x2(EmitContext& ctx);
520void EmitGlobalAtomicMinF32x2(EmitContext& ctx);
521void EmitGlobalAtomicMaxF16x2(EmitContext& ctx);
522void EmitGlobalAtomicMaxF32x2(EmitContext& ctx);
523void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
524void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
525void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
526void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, std::string_view value);
527void EmitConvertS16F16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
528void EmitConvertS16F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
529void EmitConvertS16F64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
530void EmitConvertS32F16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
531void EmitConvertS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
532void EmitConvertS32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
533void EmitConvertS64F16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
534void EmitConvertS64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
535void EmitConvertS64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
536void EmitConvertU16F16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
537void EmitConvertU16F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
538void EmitConvertU16F64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
539void EmitConvertU32F16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
540void EmitConvertU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
541void EmitConvertU32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
542void EmitConvertU64F16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
543void EmitConvertU64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
544void EmitConvertU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
545void EmitConvertU64U32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
546void EmitConvertU32U64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
547void EmitConvertF16F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
548void EmitConvertF32F16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
549void EmitConvertF32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
550void EmitConvertF64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
551void EmitConvertF16S8(EmitContext& ctx, IR::Inst& inst, std::string_view value);
552void EmitConvertF16S16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
553void EmitConvertF16S32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
554void EmitConvertF16S64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
555void EmitConvertF16U8(EmitContext& ctx, IR::Inst& inst, std::string_view value);
556void EmitConvertF16U16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
557void EmitConvertF16U32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
558void EmitConvertF16U64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
559void EmitConvertF32S8(EmitContext& ctx, IR::Inst& inst, std::string_view value);
560void EmitConvertF32S16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
561void EmitConvertF32S32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
562void EmitConvertF32S64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
563void EmitConvertF32U8(EmitContext& ctx, IR::Inst& inst, std::string_view value);
564void EmitConvertF32U16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
565void EmitConvertF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
566void EmitConvertF32U64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
567void EmitConvertF64S8(EmitContext& ctx, IR::Inst& inst, std::string_view value);
568void EmitConvertF64S16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
569void EmitConvertF64S32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
570void EmitConvertF64S64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
571void EmitConvertF64U8(EmitContext& ctx, IR::Inst& inst, std::string_view value);
572void EmitConvertF64U16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
573void EmitConvertF64U32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
574void EmitConvertF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
575void EmitBindlessImageSampleImplicitLod(EmitContext&);
576void EmitBindlessImageSampleExplicitLod(EmitContext&);
577void EmitBindlessImageSampleDrefImplicitLod(EmitContext&);
578void EmitBindlessImageSampleDrefExplicitLod(EmitContext&);
579void EmitBindlessImageGather(EmitContext&);
580void EmitBindlessImageGatherDref(EmitContext&);
581void EmitBindlessImageFetch(EmitContext&);
582void EmitBindlessImageQueryDimensions(EmitContext&);
583void EmitBindlessImageQueryLod(EmitContext&);
584void EmitBindlessImageGradient(EmitContext&);
585void EmitBindlessImageRead(EmitContext&);
586void EmitBindlessImageWrite(EmitContext&);
587void EmitBoundImageSampleImplicitLod(EmitContext&);
588void EmitBoundImageSampleExplicitLod(EmitContext&);
589void EmitBoundImageSampleDrefImplicitLod(EmitContext&);
590void EmitBoundImageSampleDrefExplicitLod(EmitContext&);
591void EmitBoundImageGather(EmitContext&);
592void EmitBoundImageGatherDref(EmitContext&);
593void EmitBoundImageFetch(EmitContext&);
594void EmitBoundImageQueryDimensions(EmitContext&);
595void EmitBoundImageQueryLod(EmitContext&);
596void EmitBoundImageGradient(EmitContext&);
597void EmitBoundImageRead(EmitContext&);
598void EmitBoundImageWrite(EmitContext&);
599void EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
600 std::string_view coords, std::string_view bias_lc,
601 const IR::Value& offset);
602void EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
603 std::string_view coords, std::string_view lod_lc,
604 const IR::Value& offset);
605void EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
606 std::string_view coords, std::string_view dref,
607 std::string_view bias_lc, const IR::Value& offset);
608void EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
609 std::string_view coords, std::string_view dref,
610 std::string_view lod_lc, const IR::Value& offset);
611void EmitImageGather(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
612 std::string_view coords, const IR::Value& offset, const IR::Value& offset2);
613void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
614 std::string_view coords, const IR::Value& offset, const IR::Value& offset2,
615 std::string_view dref);
616void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
617 std::string_view coords, std::string_view offset, std::string_view lod,
618 std::string_view ms);
619void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
620 std::string_view lod);
621void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
622 std::string_view coords);
623void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
624 std::string_view coords, const IR::Value& derivatives,
625 const IR::Value& offset, const IR::Value& lod_clamp);
626void EmitImageRead(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
627 std::string_view coords);
628void EmitImageWrite(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
629 std::string_view coords, std::string_view color);
630void EmitBindlessImageAtomicIAdd32(EmitContext&);
631void EmitBindlessImageAtomicSMin32(EmitContext&);
632void EmitBindlessImageAtomicUMin32(EmitContext&);
633void EmitBindlessImageAtomicSMax32(EmitContext&);
634void EmitBindlessImageAtomicUMax32(EmitContext&);
635void EmitBindlessImageAtomicInc32(EmitContext&);
636void EmitBindlessImageAtomicDec32(EmitContext&);
637void EmitBindlessImageAtomicAnd32(EmitContext&);
638void EmitBindlessImageAtomicOr32(EmitContext&);
639void EmitBindlessImageAtomicXor32(EmitContext&);
640void EmitBindlessImageAtomicExchange32(EmitContext&);
641void EmitBoundImageAtomicIAdd32(EmitContext&);
642void EmitBoundImageAtomicSMin32(EmitContext&);
643void EmitBoundImageAtomicUMin32(EmitContext&);
644void EmitBoundImageAtomicSMax32(EmitContext&);
645void EmitBoundImageAtomicUMax32(EmitContext&);
646void EmitBoundImageAtomicInc32(EmitContext&);
647void EmitBoundImageAtomicDec32(EmitContext&);
648void EmitBoundImageAtomicAnd32(EmitContext&);
649void EmitBoundImageAtomicOr32(EmitContext&);
650void EmitBoundImageAtomicXor32(EmitContext&);
651void EmitBoundImageAtomicExchange32(EmitContext&);
652void EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
653 std::string_view coords, std::string_view value);
654void EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
655 std::string_view coords, std::string_view value);
656void EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
657 std::string_view coords, std::string_view value);
658void EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
659 std::string_view coords, std::string_view value);
660void EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
661 std::string_view coords, std::string_view value);
662void EmitImageAtomicInc32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
663 std::string_view coords, std::string_view value);
664void EmitImageAtomicDec32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
665 std::string_view coords, std::string_view value);
666void EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
667 std::string_view coords, std::string_view value);
668void EmitImageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
669 std::string_view coords, std::string_view value);
670void EmitImageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
671 std::string_view coords, std::string_view value);
672void EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
673 std::string_view coords, std::string_view value);
674void EmitLaneId(EmitContext& ctx, IR::Inst& inst);
675void EmitVoteAll(EmitContext& ctx, IR::Inst& inst, std::string_view pred);
676void EmitVoteAny(EmitContext& ctx, IR::Inst& inst, std::string_view pred);
677void EmitVoteEqual(EmitContext& ctx, IR::Inst& inst, std::string_view pred);
678void EmitSubgroupBallot(EmitContext& ctx, IR::Inst& inst, std::string_view pred);
679void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst);
680void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst);
681void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst);
682void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst);
683void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst);
684void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, std::string_view value,
685 std::string_view index, std::string_view clamp,
686 std::string_view segmentation_mask);
687void EmitShuffleUp(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view index,
688 std::string_view clamp, std::string_view segmentation_mask);
689void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, std::string_view value,
690 std::string_view index, std::string_view clamp,
691 std::string_view segmentation_mask);
692void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view value,
693 std::string_view index, std::string_view clamp,
694 std::string_view segmentation_mask);
695void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, std::string_view op_a, std::string_view op_b,
696 std::string_view swizzle);
697void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a);
698void EmitDPdyFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a);
699void EmitDPdxCoarse(EmitContext& ctx, IR::Inst& inst, std::string_view op_a);
700void EmitDPdyCoarse(EmitContext& ctx, IR::Inst& inst, std::string_view op_a);
701
702} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp
new file mode 100644
index 000000000..38419f88f
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp
@@ -0,0 +1,253 @@
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 <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10
11namespace Shader::Backend::GLSL {
12namespace {
13void SetZeroFlag(EmitContext& ctx, IR::Inst& inst, std::string_view result) {
14 IR::Inst* const zero{inst.GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp)};
15 if (!zero) {
16 return;
17 }
18 ctx.AddU1("{}={}==0;", *zero, result);
19 zero->Invalidate();
20}
21
22void SetSignFlag(EmitContext& ctx, IR::Inst& inst, std::string_view result) {
23 IR::Inst* const sign{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp)};
24 if (!sign) {
25 return;
26 }
27 ctx.AddU1("{}=int({})<0;", *sign, result);
28 sign->Invalidate();
29}
30
31void BitwiseLogicalOp(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
32 char lop) {
33 const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
34 ctx.Add("{}={}{}{};", result, a, lop, b);
35 SetZeroFlag(ctx, inst, result);
36 SetSignFlag(ctx, inst, result);
37}
38} // Anonymous namespace
39
40void EmitIAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
41 // Compute the overflow CC first as it requires the original operand values,
42 // which may be overwritten by the result of the addition
43 if (IR::Inst * overflow{inst.GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp)}) {
44 // https://stackoverflow.com/questions/55468823/how-to-detect-integer-overflow-in-c
45 constexpr u32 s32_max{static_cast<u32>(std::numeric_limits<s32>::max())};
46 const auto sub_a{fmt::format("{}u-{}", s32_max, a)};
47 const auto positive_result{fmt::format("int({})>int({})", b, sub_a)};
48 const auto negative_result{fmt::format("int({})<int({})", b, sub_a)};
49 ctx.AddU1("{}=int({})>=0?{}:{};", *overflow, a, positive_result, negative_result);
50 overflow->Invalidate();
51 }
52 const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
53 if (IR::Inst* const carry{inst.GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp)}) {
54 ctx.uses_cc_carry = true;
55 ctx.Add("{}=uaddCarry({},{},carry);", result, a, b);
56 ctx.AddU1("{}=carry!=0;", *carry);
57 carry->Invalidate();
58 } else {
59 ctx.Add("{}={}+{};", result, a, b);
60 }
61 SetZeroFlag(ctx, inst, result);
62 SetSignFlag(ctx, inst, result);
63}
64
65void EmitIAdd64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
66 ctx.AddU64("{}={}+{};", inst, a, b);
67}
68
69void EmitISub32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
70 ctx.AddU32("{}={}-{};", inst, a, b);
71}
72
73void EmitISub64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
74 ctx.AddU64("{}={}-{};", inst, a, b);
75}
76
77void EmitIMul32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
78 ctx.AddU32("{}=uint({}*{});", inst, a, b);
79}
80
81void EmitINeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
82 ctx.AddU32("{}=uint(-({}));", inst, value);
83}
84
85void EmitINeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
86 ctx.AddU64("{}=-({});", inst, value);
87}
88
89void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
90 ctx.AddU32("{}=abs(int({}));", inst, value);
91}
92
93void EmitShiftLeftLogical32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
94 std::string_view shift) {
95 ctx.AddU32("{}={}<<{};", inst, base, shift);
96}
97
98void EmitShiftLeftLogical64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
99 std::string_view shift) {
100 ctx.AddU64("{}={}<<{};", inst, base, shift);
101}
102
103void EmitShiftRightLogical32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
104 std::string_view shift) {
105 ctx.AddU32("{}={}>>{};", inst, base, shift);
106}
107
108void EmitShiftRightLogical64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
109 std::string_view shift) {
110 ctx.AddU64("{}={}>>{};", inst, base, shift);
111}
112
113void EmitShiftRightArithmetic32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
114 std::string_view shift) {
115 ctx.AddU32("{}=int({})>>{};", inst, base, shift);
116}
117
118void EmitShiftRightArithmetic64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
119 std::string_view shift) {
120 ctx.AddU64("{}=int64_t({})>>{};", inst, base, shift);
121}
122
123void EmitBitwiseAnd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
124 BitwiseLogicalOp(ctx, inst, a, b, '&');
125}
126
127void EmitBitwiseOr32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
128 BitwiseLogicalOp(ctx, inst, a, b, '|');
129}
130
131void EmitBitwiseXor32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
132 BitwiseLogicalOp(ctx, inst, a, b, '^');
133}
134
135void EmitBitFieldInsert(EmitContext& ctx, IR::Inst& inst, std::string_view base,
136 std::string_view insert, std::string_view offset, std::string_view count) {
137 ctx.AddU32("{}=bitfieldInsert({},{},int({}),int({}));", inst, base, insert, offset, count);
138}
139
140void EmitBitFieldSExtract(EmitContext& ctx, IR::Inst& inst, std::string_view base,
141 std::string_view offset, std::string_view count) {
142 const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
143 ctx.Add("{}=uint(bitfieldExtract(int({}),int({}),int({})));", result, base, offset, count);
144 SetZeroFlag(ctx, inst, result);
145 SetSignFlag(ctx, inst, result);
146}
147
148void EmitBitFieldUExtract(EmitContext& ctx, IR::Inst& inst, std::string_view base,
149 std::string_view offset, std::string_view count) {
150 const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
151 ctx.Add("{}=uint(bitfieldExtract(uint({}),int({}),int({})));", result, base, offset, count);
152 SetZeroFlag(ctx, inst, result);
153 SetSignFlag(ctx, inst, result);
154}
155
156void EmitBitReverse32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
157 ctx.AddU32("{}=bitfieldReverse({});", inst, value);
158}
159
160void EmitBitCount32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
161 ctx.AddU32("{}=bitCount({});", inst, value);
162}
163
164void EmitBitwiseNot32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
165 ctx.AddU32("{}=~{};", inst, value);
166}
167
168void EmitFindSMsb32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
169 ctx.AddU32("{}=findMSB(int({}));", inst, value);
170}
171
172void EmitFindUMsb32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
173 ctx.AddU32("{}=findMSB(uint({}));", inst, value);
174}
175
176void EmitSMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
177 ctx.AddU32("{}=min(int({}),int({}));", inst, a, b);
178}
179
180void EmitUMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
181 ctx.AddU32("{}=min(uint({}),uint({}));", inst, a, b);
182}
183
184void EmitSMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
185 ctx.AddU32("{}=max(int({}),int({}));", inst, a, b);
186}
187
188void EmitUMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
189 ctx.AddU32("{}=max(uint({}),uint({}));", inst, a, b);
190}
191
192void EmitSClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view min,
193 std::string_view max) {
194 const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
195 ctx.Add("{}=clamp(int({}),int({}),int({}));", result, value, min, max);
196 SetZeroFlag(ctx, inst, result);
197 SetSignFlag(ctx, inst, result);
198}
199
200void EmitUClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view min,
201 std::string_view max) {
202 const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
203 ctx.Add("{}=clamp(uint({}),uint({}),uint({}));", result, value, min, max);
204 SetZeroFlag(ctx, inst, result);
205 SetSignFlag(ctx, inst, result);
206}
207
208void EmitSLessThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
209 ctx.AddU1("{}=int({})<int({});", inst, lhs, rhs);
210}
211
212void EmitULessThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
213 ctx.AddU1("{}=uint({})<uint({});", inst, lhs, rhs);
214}
215
216void EmitIEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
217 ctx.AddU1("{}={}=={};", inst, lhs, rhs);
218}
219
220void EmitSLessThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
221 std::string_view rhs) {
222 ctx.AddU1("{}=int({})<=int({});", inst, lhs, rhs);
223}
224
225void EmitULessThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
226 std::string_view rhs) {
227 ctx.AddU1("{}=uint({})<=uint({});", inst, lhs, rhs);
228}
229
230void EmitSGreaterThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
231 std::string_view rhs) {
232 ctx.AddU1("{}=int({})>int({});", inst, lhs, rhs);
233}
234
235void EmitUGreaterThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
236 std::string_view rhs) {
237 ctx.AddU1("{}=uint({})>uint({});", inst, lhs, rhs);
238}
239
240void EmitINotEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
241 ctx.AddU1("{}={}!={};", inst, lhs, rhs);
242}
243
244void EmitSGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
245 std::string_view rhs) {
246 ctx.AddU1("{}=int({})>=int({});", inst, lhs, rhs);
247}
248
249void EmitUGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
250 std::string_view rhs) {
251 ctx.AddU1("{}=uint({})>=uint({});", inst, lhs, rhs);
252}
253} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp
new file mode 100644
index 000000000..338ff4bd6
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp
@@ -0,0 +1,28 @@
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 <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10
11namespace Shader::Backend::GLSL {
12
13void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
14 ctx.AddU1("{}={}||{};", inst, a, b);
15}
16
17void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
18 ctx.AddU1("{}={}&&{};", inst, a, b);
19}
20
21void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
22 ctx.AddU1("{}={}^^{};", inst, a, b);
23}
24
25void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
26 ctx.AddU1("{}=!{};", inst, value);
27}
28} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_memory.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_memory.cpp
new file mode 100644
index 000000000..e3957491f
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_memory.cpp
@@ -0,0 +1,202 @@
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 <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10#include "shader_recompiler/profile.h"
11
12namespace Shader::Backend::GLSL {
13namespace {
14constexpr char cas_loop[]{"for(;;){{uint old_value={};uint "
15 "cas_result=atomicCompSwap({},old_value,bitfieldInsert({},{},{},{}));"
16 "if(cas_result==old_value){{break;}}}}"};
17
18void SsboWriteCas(EmitContext& ctx, const IR::Value& binding, std::string_view offset_var,
19 std::string_view value, std::string_view bit_offset, u32 num_bits) {
20 const auto ssbo{fmt::format("{}_ssbo{}[{}>>2]", ctx.stage_name, binding.U32(), offset_var)};
21 ctx.Add(cas_loop, ssbo, ssbo, ssbo, value, bit_offset, num_bits);
22}
23} // Anonymous namespace
24
25void EmitLoadGlobalU8(EmitContext&) {
26 NotImplemented();
27}
28
29void EmitLoadGlobalS8(EmitContext&) {
30 NotImplemented();
31}
32
33void EmitLoadGlobalU16(EmitContext&) {
34 NotImplemented();
35}
36
37void EmitLoadGlobalS16(EmitContext&) {
38 NotImplemented();
39}
40
41void EmitLoadGlobal32(EmitContext& ctx, IR::Inst& inst, std::string_view address) {
42 if (ctx.profile.support_int64) {
43 return ctx.AddU32("{}=LoadGlobal32({});", inst, address);
44 }
45 LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
46 ctx.AddU32("{}=0u;", inst);
47}
48
49void EmitLoadGlobal64(EmitContext& ctx, IR::Inst& inst, std::string_view address) {
50 if (ctx.profile.support_int64) {
51 return ctx.AddU32x2("{}=LoadGlobal64({});", inst, address);
52 }
53 LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
54 ctx.AddU32x2("{}=uvec2(0);", inst);
55}
56
57void EmitLoadGlobal128(EmitContext& ctx, IR::Inst& inst, std::string_view address) {
58 if (ctx.profile.support_int64) {
59 return ctx.AddU32x4("{}=LoadGlobal128({});", inst, address);
60 }
61 LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
62 ctx.AddU32x4("{}=uvec4(0);", inst);
63}
64
65void EmitWriteGlobalU8(EmitContext&) {
66 NotImplemented();
67}
68
69void EmitWriteGlobalS8(EmitContext&) {
70 NotImplemented();
71}
72
73void EmitWriteGlobalU16(EmitContext&) {
74 NotImplemented();
75}
76
77void EmitWriteGlobalS16(EmitContext&) {
78 NotImplemented();
79}
80
81void EmitWriteGlobal32(EmitContext& ctx, std::string_view address, std::string_view value) {
82 if (ctx.profile.support_int64) {
83 return ctx.Add("WriteGlobal32({},{});", address, value);
84 }
85 LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
86}
87
88void EmitWriteGlobal64(EmitContext& ctx, std::string_view address, std::string_view value) {
89 if (ctx.profile.support_int64) {
90 return ctx.Add("WriteGlobal64({},{});", address, value);
91 }
92 LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
93}
94
95void EmitWriteGlobal128(EmitContext& ctx, std::string_view address, std::string_view value) {
96 if (ctx.profile.support_int64) {
97 return ctx.Add("WriteGlobal128({},{});", address, value);
98 }
99 LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
100}
101
102void EmitLoadStorageU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
103 const IR::Value& offset) {
104 const auto offset_var{ctx.var_alloc.Consume(offset)};
105 ctx.AddU32("{}=bitfieldExtract({}_ssbo{}[{}>>2],int({}%4)*8,8);", inst, ctx.stage_name,
106 binding.U32(), offset_var, offset_var);
107}
108
109void EmitLoadStorageS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
110 const IR::Value& offset) {
111 const auto offset_var{ctx.var_alloc.Consume(offset)};
112 ctx.AddU32("{}=bitfieldExtract(int({}_ssbo{}[{}>>2]),int({}%4)*8,8);", inst, ctx.stage_name,
113 binding.U32(), offset_var, offset_var);
114}
115
116void EmitLoadStorageU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
117 const IR::Value& offset) {
118 const auto offset_var{ctx.var_alloc.Consume(offset)};
119 ctx.AddU32("{}=bitfieldExtract({}_ssbo{}[{}>>2],int(({}>>1)%2)*16,16);", inst, ctx.stage_name,
120 binding.U32(), offset_var, offset_var);
121}
122
123void EmitLoadStorageS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
124 const IR::Value& offset) {
125 const auto offset_var{ctx.var_alloc.Consume(offset)};
126 ctx.AddU32("{}=bitfieldExtract(int({}_ssbo{}[{}>>2]),int(({}>>1)%2)*16,16);", inst,
127 ctx.stage_name, binding.U32(), offset_var, offset_var);
128}
129
130void EmitLoadStorage32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
131 const IR::Value& offset) {
132 const auto offset_var{ctx.var_alloc.Consume(offset)};
133 ctx.AddU32("{}={}_ssbo{}[{}>>2];", inst, ctx.stage_name, binding.U32(), offset_var);
134}
135
136void EmitLoadStorage64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
137 const IR::Value& offset) {
138 const auto offset_var{ctx.var_alloc.Consume(offset)};
139 ctx.AddU32x2("{}=uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}+4)>>2]);", inst, ctx.stage_name,
140 binding.U32(), offset_var, ctx.stage_name, binding.U32(), offset_var);
141}
142
143void EmitLoadStorage128(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
144 const IR::Value& offset) {
145 const auto offset_var{ctx.var_alloc.Consume(offset)};
146 ctx.AddU32x4("{}=uvec4({}_ssbo{}[{}>>2],{}_ssbo{}[({}+4)>>2],{}_ssbo{}[({}+8)>>2],{}_ssbo{}[({}"
147 "+12)>>2]);",
148 inst, ctx.stage_name, binding.U32(), offset_var, ctx.stage_name, binding.U32(),
149 offset_var, ctx.stage_name, binding.U32(), offset_var, ctx.stage_name,
150 binding.U32(), offset_var);
151}
152
153void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
154 std::string_view value) {
155 const auto offset_var{ctx.var_alloc.Consume(offset)};
156 const auto bit_offset{fmt::format("int({}%4)*8", offset_var)};
157 SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 8);
158}
159
160void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
161 std::string_view value) {
162 const auto offset_var{ctx.var_alloc.Consume(offset)};
163 const auto bit_offset{fmt::format("int({}%4)*8", offset_var)};
164 SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 8);
165}
166
167void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
168 std::string_view value) {
169 const auto offset_var{ctx.var_alloc.Consume(offset)};
170 const auto bit_offset{fmt::format("int(({}>>1)%2)*16", offset_var)};
171 SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 16);
172}
173
174void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
175 std::string_view value) {
176 const auto offset_var{ctx.var_alloc.Consume(offset)};
177 const auto bit_offset{fmt::format("int(({}>>1)%2)*16", offset_var)};
178 SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 16);
179}
180
181void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
182 std::string_view value) {
183 const auto offset_var{ctx.var_alloc.Consume(offset)};
184 ctx.Add("{}_ssbo{}[{}>>2]={};", ctx.stage_name, binding.U32(), offset_var, value);
185}
186
187void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
188 std::string_view value) {
189 const auto offset_var{ctx.var_alloc.Consume(offset)};
190 ctx.Add("{}_ssbo{}[{}>>2]={}.x;", ctx.stage_name, binding.U32(), offset_var, value);
191 ctx.Add("{}_ssbo{}[({}+4)>>2]={}.y;", ctx.stage_name, binding.U32(), offset_var, value);
192}
193
194void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
195 std::string_view value) {
196 const auto offset_var{ctx.var_alloc.Consume(offset)};
197 ctx.Add("{}_ssbo{}[{}>>2]={}.x;", ctx.stage_name, binding.U32(), offset_var, value);
198 ctx.Add("{}_ssbo{}[({}+4)>>2]={}.y;", ctx.stage_name, binding.U32(), offset_var, value);
199 ctx.Add("{}_ssbo{}[({}+8)>>2]={}.z;", ctx.stage_name, binding.U32(), offset_var, value);
200 ctx.Add("{}_ssbo{}[({}+12)>>2]={}.w;", ctx.stage_name, binding.U32(), offset_var, value);
201}
202} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp
new file mode 100644
index 000000000..f420fe388
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp
@@ -0,0 +1,105 @@
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 <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10
11#ifdef _MSC_VER
12#pragma warning(disable : 4100)
13#endif
14
15namespace Shader::Backend::GLSL {
16
17void EmitGetRegister(EmitContext& ctx) {
18 NotImplemented();
19}
20
21void EmitSetRegister(EmitContext& ctx) {
22 NotImplemented();
23}
24
25void EmitGetPred(EmitContext& ctx) {
26 NotImplemented();
27}
28
29void EmitSetPred(EmitContext& ctx) {
30 NotImplemented();
31}
32
33void EmitSetGotoVariable(EmitContext& ctx) {
34 NotImplemented();
35}
36
37void EmitGetGotoVariable(EmitContext& ctx) {
38 NotImplemented();
39}
40
41void EmitSetIndirectBranchVariable(EmitContext& ctx) {
42 NotImplemented();
43}
44
45void EmitGetIndirectBranchVariable(EmitContext& ctx) {
46 NotImplemented();
47}
48
49void EmitGetZFlag(EmitContext& ctx) {
50 NotImplemented();
51}
52
53void EmitGetSFlag(EmitContext& ctx) {
54 NotImplemented();
55}
56
57void EmitGetCFlag(EmitContext& ctx) {
58 NotImplemented();
59}
60
61void EmitGetOFlag(EmitContext& ctx) {
62 NotImplemented();
63}
64
65void EmitSetZFlag(EmitContext& ctx) {
66 NotImplemented();
67}
68
69void EmitSetSFlag(EmitContext& ctx) {
70 NotImplemented();
71}
72
73void EmitSetCFlag(EmitContext& ctx) {
74 NotImplemented();
75}
76
77void EmitSetOFlag(EmitContext& ctx) {
78 NotImplemented();
79}
80
81void EmitGetZeroFromOp(EmitContext& ctx) {
82 NotImplemented();
83}
84
85void EmitGetSignFromOp(EmitContext& ctx) {
86 NotImplemented();
87}
88
89void EmitGetCarryFromOp(EmitContext& ctx) {
90 NotImplemented();
91}
92
93void EmitGetOverflowFromOp(EmitContext& ctx) {
94 NotImplemented();
95}
96
97void EmitGetSparseFromOp(EmitContext& ctx) {
98 NotImplemented();
99}
100
101void EmitGetInBoundsFromOp(EmitContext& ctx) {
102 NotImplemented();
103}
104
105} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_select.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_select.cpp
new file mode 100644
index 000000000..49fba9073
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_select.cpp
@@ -0,0 +1,55 @@
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 <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10
11namespace Shader::Backend::GLSL {
12void EmitSelectU1(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
13 std::string_view true_value, std::string_view false_value) {
14 ctx.AddU1("{}={}?{}:{};", inst, cond, true_value, false_value);
15}
16
17void EmitSelectU8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view cond,
18 [[maybe_unused]] std::string_view true_value,
19 [[maybe_unused]] std::string_view false_value) {
20 NotImplemented();
21}
22
23void EmitSelectU16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view cond,
24 [[maybe_unused]] std::string_view true_value,
25 [[maybe_unused]] std::string_view false_value) {
26 NotImplemented();
27}
28
29void EmitSelectU32(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
30 std::string_view true_value, std::string_view false_value) {
31 ctx.AddU32("{}={}?{}:{};", inst, cond, true_value, false_value);
32}
33
34void EmitSelectU64(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
35 std::string_view true_value, std::string_view false_value) {
36 ctx.AddU64("{}={}?{}:{};", inst, cond, true_value, false_value);
37}
38
39void EmitSelectF16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view cond,
40 [[maybe_unused]] std::string_view true_value,
41 [[maybe_unused]] std::string_view false_value) {
42 NotImplemented();
43}
44
45void EmitSelectF32(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
46 std::string_view true_value, std::string_view false_value) {
47 ctx.AddF32("{}={}?{}:{};", inst, cond, true_value, false_value);
48}
49
50void EmitSelectF64(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
51 std::string_view true_value, std::string_view false_value) {
52 ctx.AddF64("{}={}?{}:{};", inst, cond, true_value, false_value);
53}
54
55} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp
new file mode 100644
index 000000000..518b78f06
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp
@@ -0,0 +1,79 @@
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 <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10
11namespace Shader::Backend::GLSL {
12namespace {
13constexpr char cas_loop[]{"for(;;){{uint old_value={};uint "
14 "cas_result=atomicCompSwap({},old_value,bitfieldInsert({},{},{},{}));"
15 "if(cas_result==old_value){{break;}}}}"};
16
17void SharedWriteCas(EmitContext& ctx, std::string_view offset, std::string_view value,
18 std::string_view bit_offset, u32 num_bits) {
19 const auto smem{fmt::format("smem[{}>>2]", offset)};
20 ctx.Add(cas_loop, smem, smem, smem, value, bit_offset, num_bits);
21}
22} // Anonymous namespace
23
24void EmitLoadSharedU8(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
25 ctx.AddU32("{}=bitfieldExtract(smem[{}>>2],int({}%4)*8,8);", inst, offset, offset);
26}
27
28void EmitLoadSharedS8(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
29 ctx.AddU32("{}=bitfieldExtract(int(smem[{}>>2]),int({}%4)*8,8);", inst, offset, offset);
30}
31
32void EmitLoadSharedU16(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
33 ctx.AddU32("{}=bitfieldExtract(smem[{}>>2],int(({}>>1)%2)*16,16);", inst, offset, offset);
34}
35
36void EmitLoadSharedS16(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
37 ctx.AddU32("{}=bitfieldExtract(int(smem[{}>>2]),int(({}>>1)%2)*16,16);", inst, offset, offset);
38}
39
40void EmitLoadSharedU32(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
41 ctx.AddU32("{}=smem[{}>>2];", inst, offset);
42}
43
44void EmitLoadSharedU64(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
45 ctx.AddU32x2("{}=uvec2(smem[{}>>2],smem[({}+4)>>2]);", inst, offset, offset);
46}
47
48void EmitLoadSharedU128(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
49 ctx.AddU32x4("{}=uvec4(smem[{}>>2],smem[({}+4)>>2],smem[({}+8)>>2],smem[({}+12)>>2]);", inst,
50 offset, offset, offset, offset);
51}
52
53void EmitWriteSharedU8(EmitContext& ctx, std::string_view offset, std::string_view value) {
54 const auto bit_offset{fmt::format("int({}%4)*8", offset)};
55 SharedWriteCas(ctx, offset, value, bit_offset, 8);
56}
57
58void EmitWriteSharedU16(EmitContext& ctx, std::string_view offset, std::string_view value) {
59 const auto bit_offset{fmt::format("int(({}>>1)%2)*16", offset)};
60 SharedWriteCas(ctx, offset, value, bit_offset, 16);
61}
62
63void EmitWriteSharedU32(EmitContext& ctx, std::string_view offset, std::string_view value) {
64 ctx.Add("smem[{}>>2]={};", offset, value);
65}
66
67void EmitWriteSharedU64(EmitContext& ctx, std::string_view offset, std::string_view value) {
68 ctx.Add("smem[{}>>2]={}.x;", offset, value);
69 ctx.Add("smem[({}+4)>>2]={}.y;", offset, value);
70}
71
72void EmitWriteSharedU128(EmitContext& ctx, std::string_view offset, std::string_view value) {
73 ctx.Add("smem[{}>>2]={}.x;", offset, value);
74 ctx.Add("smem[({}+4)>>2]={}.y;", offset, value);
75 ctx.Add("smem[({}+8)>>2]={}.z;", offset, value);
76 ctx.Add("smem[({}+12)>>2]={}.w;", offset, value);
77}
78
79} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp
new file mode 100644
index 000000000..9b866f889
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp
@@ -0,0 +1,111 @@
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 <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/program.h"
10#include "shader_recompiler/frontend/ir/value.h"
11#include "shader_recompiler/profile.h"
12
13namespace Shader::Backend::GLSL {
14namespace {
15std::string_view OutputVertexIndex(EmitContext& ctx) {
16 return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : "";
17}
18
19void InitializeOutputVaryings(EmitContext& ctx) {
20 if (ctx.uses_geometry_passthrough) {
21 return;
22 }
23 if (ctx.stage == Stage::VertexB || ctx.stage == Stage::Geometry) {
24 ctx.Add("gl_Position=vec4(0,0,0,1);");
25 }
26 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
27 if (!ctx.info.stores.Generic(index)) {
28 continue;
29 }
30 const auto& info_array{ctx.output_generics.at(index)};
31 const auto output_decorator{OutputVertexIndex(ctx)};
32 size_t element{};
33 while (element < info_array.size()) {
34 const auto& info{info_array.at(element)};
35 const auto varying_name{fmt::format("{}{}", info.name, output_decorator)};
36 switch (info.num_components) {
37 case 1: {
38 const char value{element == 3 ? '1' : '0'};
39 ctx.Add("{}={}.f;", varying_name, value);
40 break;
41 }
42 case 2:
43 case 3:
44 if (element + info.num_components < 4) {
45 ctx.Add("{}=vec{}(0);", varying_name, info.num_components);
46 } else {
47 // last element is the w component, must be initialized to 1
48 const auto zeros{info.num_components == 3 ? "0,0," : "0,"};
49 ctx.Add("{}=vec{}({}1);", varying_name, info.num_components, zeros);
50 }
51 break;
52 case 4:
53 ctx.Add("{}=vec4(0,0,0,1);", varying_name);
54 break;
55 default:
56 break;
57 }
58 element += info.num_components;
59 }
60 }
61}
62} // Anonymous namespace
63
64void EmitPhi(EmitContext& ctx, IR::Inst& phi) {
65 const size_t num_args{phi.NumArgs()};
66 for (size_t i = 0; i < num_args; ++i) {
67 ctx.var_alloc.Consume(phi.Arg(i));
68 }
69 if (!phi.Definition<Id>().is_valid) {
70 // The phi node wasn't forward defined
71 ctx.var_alloc.PhiDefine(phi, phi.Arg(0).Type());
72 }
73}
74
75void EmitVoid(EmitContext&) {}
76
77void EmitReference(EmitContext& ctx, const IR::Value& value) {
78 ctx.var_alloc.Consume(value);
79}
80
81void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value& value) {
82 IR::Inst& phi{*phi_value.InstRecursive()};
83 const auto phi_type{phi.Arg(0).Type()};
84 if (!phi.Definition<Id>().is_valid) {
85 // The phi node wasn't forward defined
86 ctx.var_alloc.PhiDefine(phi, phi_type);
87 }
88 const auto phi_reg{ctx.var_alloc.Consume(IR::Value{&phi})};
89 const auto val_reg{ctx.var_alloc.Consume(value)};
90 if (phi_reg == val_reg) {
91 return;
92 }
93 ctx.Add("{}={};", phi_reg, val_reg);
94}
95
96void EmitPrologue(EmitContext& ctx) {
97 InitializeOutputVaryings(ctx);
98}
99
100void EmitEpilogue(EmitContext&) {}
101
102void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
103 ctx.Add("EmitStreamVertex(int({}));", ctx.var_alloc.Consume(stream));
104 InitializeOutputVaryings(ctx);
105}
106
107void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
108 ctx.Add("EndStreamPrimitive(int({}));", ctx.var_alloc.Consume(stream));
109}
110
111} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp
new file mode 100644
index 000000000..15bf02dd6
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp
@@ -0,0 +1,32 @@
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 <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9
10namespace Shader::Backend::GLSL {
11
12void EmitUndefU1(EmitContext& ctx, IR::Inst& inst) {
13 ctx.AddU1("{}=false;", inst);
14}
15
16void EmitUndefU8(EmitContext& ctx, IR::Inst& inst) {
17 ctx.AddU32("{}=0u;", inst);
18}
19
20void EmitUndefU16(EmitContext& ctx, IR::Inst& inst) {
21 ctx.AddU32("{}=0u;", inst);
22}
23
24void EmitUndefU32(EmitContext& ctx, IR::Inst& inst) {
25 ctx.AddU32("{}=0u;", inst);
26}
27
28void EmitUndefU64(EmitContext& ctx, IR::Inst& inst) {
29 ctx.AddU64("{}=0u;", inst);
30}
31
32} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp
new file mode 100644
index 000000000..a982dd8a2
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp
@@ -0,0 +1,217 @@
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 <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10#include "shader_recompiler/profile.h"
11
12namespace Shader::Backend::GLSL {
13namespace {
14void SetInBoundsFlag(EmitContext& ctx, IR::Inst& inst) {
15 IR::Inst* const in_bounds{inst.GetAssociatedPseudoOperation(IR::Opcode::GetInBoundsFromOp)};
16 if (!in_bounds) {
17 return;
18 }
19 ctx.AddU1("{}=shfl_in_bounds;", *in_bounds);
20 in_bounds->Invalidate();
21}
22
23std::string ComputeMinThreadId(std::string_view thread_id, std::string_view segmentation_mask) {
24 return fmt::format("({}&{})", thread_id, segmentation_mask);
25}
26
27std::string ComputeMaxThreadId(std::string_view min_thread_id, std::string_view clamp,
28 std::string_view not_seg_mask) {
29 return fmt::format("({})|({}&{})", min_thread_id, clamp, not_seg_mask);
30}
31
32std::string GetMaxThreadId(std::string_view thread_id, std::string_view clamp,
33 std::string_view segmentation_mask) {
34 const auto not_seg_mask{fmt::format("(~{})", segmentation_mask)};
35 const auto min_thread_id{ComputeMinThreadId(thread_id, segmentation_mask)};
36 return ComputeMaxThreadId(min_thread_id, clamp, not_seg_mask);
37}
38
39void UseShuffleNv(EmitContext& ctx, IR::Inst& inst, std::string_view shfl_op,
40 std::string_view value, std::string_view index,
41 [[maybe_unused]] std::string_view clamp, std::string_view segmentation_mask) {
42 const auto width{fmt::format("32u>>(bitCount({}&31u))", segmentation_mask)};
43 ctx.AddU32("{}={}({},{},{},shfl_in_bounds);", inst, shfl_op, value, index, width);
44 SetInBoundsFlag(ctx, inst);
45}
46} // Anonymous namespace
47
48void EmitLaneId(EmitContext& ctx, IR::Inst& inst) {
49 ctx.AddU32("{}=gl_SubGroupInvocationARB&31u;", inst);
50}
51
52void EmitVoteAll(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
53 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
54 ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred);
55 } else {
56 const auto active_mask{fmt::format("uvec2(ballotARB(true))[gl_SubGroupInvocationARB]")};
57 const auto ballot{fmt::format("uvec2(ballotARB({}))[gl_SubGroupInvocationARB]", pred)};
58 ctx.AddU1("{}=({}&{})=={};", inst, ballot, active_mask, active_mask);
59 }
60}
61
62void EmitVoteAny(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
63 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
64 ctx.AddU1("{}=anyInvocationARB({});", inst, pred);
65 } else {
66 const auto active_mask{fmt::format("uvec2(ballotARB(true))[gl_SubGroupInvocationARB]")};
67 const auto ballot{fmt::format("uvec2(ballotARB({}))[gl_SubGroupInvocationARB]", pred)};
68 ctx.AddU1("{}=({}&{})!=0u;", inst, ballot, active_mask, active_mask);
69 }
70}
71
72void EmitVoteEqual(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
73 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
74 ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred);
75 } else {
76 const auto active_mask{fmt::format("uvec2(ballotARB(true))[gl_SubGroupInvocationARB]")};
77 const auto ballot{fmt::format("uvec2(ballotARB({}))[gl_SubGroupInvocationARB]", pred)};
78 const auto value{fmt::format("({}^{})", ballot, active_mask)};
79 ctx.AddU1("{}=({}==0)||({}=={});", inst, value, value, active_mask);
80 }
81}
82
83void EmitSubgroupBallot(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
84 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
85 ctx.AddU32("{}=uvec2(ballotARB({})).x;", inst, pred);
86 } else {
87 ctx.AddU32("{}=uvec2(ballotARB({}))[gl_SubGroupInvocationARB];", inst, pred);
88 }
89}
90
91void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst) {
92 ctx.AddU32("{}=uint(gl_SubGroupEqMaskARB.x);", inst);
93}
94
95void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst) {
96 ctx.AddU32("{}=uint(gl_SubGroupLtMaskARB.x);", inst);
97}
98
99void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst) {
100 ctx.AddU32("{}=uint(gl_SubGroupLeMaskARB.x);", inst);
101}
102
103void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst) {
104 ctx.AddU32("{}=uint(gl_SubGroupGtMaskARB.x);", inst);
105}
106
107void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst) {
108 ctx.AddU32("{}=uint(gl_SubGroupGeMaskARB.x);", inst);
109}
110
111void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, std::string_view value,
112 std::string_view index, std::string_view clamp,
113 std::string_view segmentation_mask) {
114 if (ctx.profile.support_gl_warp_intrinsics) {
115 UseShuffleNv(ctx, inst, "shuffleNV", value, index, clamp, segmentation_mask);
116 return;
117 }
118 const auto not_seg_mask{fmt::format("(~{})", segmentation_mask)};
119 const auto thread_id{"gl_SubGroupInvocationARB"};
120 const auto min_thread_id{ComputeMinThreadId(thread_id, segmentation_mask)};
121 const auto max_thread_id{ComputeMaxThreadId(min_thread_id, clamp, not_seg_mask)};
122
123 const auto lhs{fmt::format("({}&{})", index, not_seg_mask)};
124 const auto src_thread_id{fmt::format("({})|({})", lhs, min_thread_id)};
125 ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
126 SetInBoundsFlag(ctx, inst);
127 ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
128}
129
130void EmitShuffleUp(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view index,
131 std::string_view clamp, std::string_view segmentation_mask) {
132 if (ctx.profile.support_gl_warp_intrinsics) {
133 UseShuffleNv(ctx, inst, "shuffleUpNV", value, index, clamp, segmentation_mask);
134 return;
135 }
136 const auto thread_id{"gl_SubGroupInvocationARB"};
137 const auto max_thread_id{GetMaxThreadId(thread_id, clamp, segmentation_mask)};
138 const auto src_thread_id{fmt::format("({}-{})", thread_id, index)};
139 ctx.Add("shfl_in_bounds=int({})>=int({});", src_thread_id, max_thread_id);
140 SetInBoundsFlag(ctx, inst);
141 ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
142}
143
144void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, std::string_view value,
145 std::string_view index, std::string_view clamp,
146 std::string_view segmentation_mask) {
147 if (ctx.profile.support_gl_warp_intrinsics) {
148 UseShuffleNv(ctx, inst, "shuffleDownNV", value, index, clamp, segmentation_mask);
149 return;
150 }
151 const auto thread_id{"gl_SubGroupInvocationARB"};
152 const auto max_thread_id{GetMaxThreadId(thread_id, clamp, segmentation_mask)};
153 const auto src_thread_id{fmt::format("({}+{})", thread_id, index)};
154 ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
155 SetInBoundsFlag(ctx, inst);
156 ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
157}
158
159void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view value,
160 std::string_view index, std::string_view clamp,
161 std::string_view segmentation_mask) {
162 if (ctx.profile.support_gl_warp_intrinsics) {
163 UseShuffleNv(ctx, inst, "shuffleXorNV", value, index, clamp, segmentation_mask);
164 return;
165 }
166 const auto thread_id{"gl_SubGroupInvocationARB"};
167 const auto max_thread_id{GetMaxThreadId(thread_id, clamp, segmentation_mask)};
168 const auto src_thread_id{fmt::format("({}^{})", thread_id, index)};
169 ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
170 SetInBoundsFlag(ctx, inst);
171 ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
172}
173
174void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, std::string_view op_a, std::string_view op_b,
175 std::string_view swizzle) {
176 const auto mask{fmt::format("({}>>((gl_SubGroupInvocationARB&3)<<1))&3", swizzle)};
177 const std::string modifier_a = fmt::format("FSWZ_A[{}]", mask);
178 const std::string modifier_b = fmt::format("FSWZ_B[{}]", mask);
179 ctx.AddF32("{}=({}*{})+({}*{});", inst, op_a, modifier_a, op_b, modifier_b);
180}
181
182void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
183 if (ctx.profile.support_gl_derivative_control) {
184 ctx.AddF32("{}=dFdxFine({});", inst, op_a);
185 } else {
186 LOG_WARNING(Shader_GLSL, "Device does not support dFdxFine, fallback to dFdx");
187 ctx.AddF32("{}=dFdx({});", inst, op_a);
188 }
189}
190
191void EmitDPdyFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
192 if (ctx.profile.support_gl_derivative_control) {
193 ctx.AddF32("{}=dFdyFine({});", inst, op_a);
194 } else {
195 LOG_WARNING(Shader_GLSL, "Device does not support dFdyFine, fallback to dFdy");
196 ctx.AddF32("{}=dFdy({});", inst, op_a);
197 }
198}
199
200void EmitDPdxCoarse(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
201 if (ctx.profile.support_gl_derivative_control) {
202 ctx.AddF32("{}=dFdxCoarse({});", inst, op_a);
203 } else {
204 LOG_WARNING(Shader_GLSL, "Device does not support dFdxCoarse, fallback to dFdx");
205 ctx.AddF32("{}=dFdx({});", inst, op_a);
206 }
207}
208
209void EmitDPdyCoarse(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
210 if (ctx.profile.support_gl_derivative_control) {
211 ctx.AddF32("{}=dFdyCoarse({});", inst, op_a);
212 } else {
213 LOG_WARNING(Shader_GLSL, "Device does not support dFdyCoarse, fallback to dFdy");
214 ctx.AddF32("{}=dFdy({});", inst, op_a);
215 }
216}
217} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/var_alloc.cpp b/src/shader_recompiler/backend/glsl/var_alloc.cpp
new file mode 100644
index 000000000..194f926ca
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/var_alloc.cpp
@@ -0,0 +1,308 @@
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 <string>
6#include <string_view>
7
8#include <fmt/format.h>
9
10#include "shader_recompiler/backend/glsl/var_alloc.h"
11#include "shader_recompiler/exception.h"
12#include "shader_recompiler/frontend/ir/value.h"
13
14namespace Shader::Backend::GLSL {
15namespace {
16std::string TypePrefix(GlslVarType type) {
17 switch (type) {
18 case GlslVarType::U1:
19 return "b_";
20 case GlslVarType::F16x2:
21 return "f16x2_";
22 case GlslVarType::U32:
23 return "u_";
24 case GlslVarType::F32:
25 return "f_";
26 case GlslVarType::U64:
27 return "u64_";
28 case GlslVarType::F64:
29 return "d_";
30 case GlslVarType::U32x2:
31 return "u2_";
32 case GlslVarType::F32x2:
33 return "f2_";
34 case GlslVarType::U32x3:
35 return "u3_";
36 case GlslVarType::F32x3:
37 return "f3_";
38 case GlslVarType::U32x4:
39 return "u4_";
40 case GlslVarType::F32x4:
41 return "f4_";
42 case GlslVarType::PrecF32:
43 return "pf_";
44 case GlslVarType::PrecF64:
45 return "pd_";
46 case GlslVarType::Void:
47 return "";
48 default:
49 throw NotImplementedException("Type {}", type);
50 }
51}
52
53std::string FormatFloat(std::string_view value, IR::Type type) {
54 // TODO: Confirm FP64 nan/inf
55 if (type == IR::Type::F32) {
56 if (value == "nan") {
57 return "utof(0x7fc00000)";
58 }
59 if (value == "inf") {
60 return "utof(0x7f800000)";
61 }
62 if (value == "-inf") {
63 return "utof(0xff800000)";
64 }
65 }
66 if (value.find_first_of('e') != std::string_view::npos) {
67 // scientific notation
68 const auto cast{type == IR::Type::F32 ? "float" : "double"};
69 return fmt::format("{}({})", cast, value);
70 }
71 const bool needs_dot{value.find_first_of('.') == std::string_view::npos};
72 const bool needs_suffix{!value.ends_with('f')};
73 const auto suffix{type == IR::Type::F32 ? "f" : "lf"};
74 return fmt::format("{}{}{}", value, needs_dot ? "." : "", needs_suffix ? suffix : "");
75}
76
77std::string MakeImm(const IR::Value& value) {
78 switch (value.Type()) {
79 case IR::Type::U1:
80 return fmt::format("{}", value.U1() ? "true" : "false");
81 case IR::Type::U32:
82 return fmt::format("{}u", value.U32());
83 case IR::Type::F32:
84 return FormatFloat(fmt::format("{}", value.F32()), IR::Type::F32);
85 case IR::Type::U64:
86 return fmt::format("{}ul", value.U64());
87 case IR::Type::F64:
88 return FormatFloat(fmt::format("{}", value.F64()), IR::Type::F64);
89 case IR::Type::Void:
90 return "";
91 default:
92 throw NotImplementedException("Immediate type {}", value.Type());
93 }
94}
95} // Anonymous namespace
96
97std::string VarAlloc::Representation(u32 index, GlslVarType type) const {
98 const auto prefix{TypePrefix(type)};
99 return fmt::format("{}{}", prefix, index);
100}
101
102std::string VarAlloc::Representation(Id id) const {
103 return Representation(id.index, id.type);
104}
105
106std::string VarAlloc::Define(IR::Inst& inst, GlslVarType type) {
107 if (inst.HasUses()) {
108 inst.SetDefinition<Id>(Alloc(type));
109 return Representation(inst.Definition<Id>());
110 } else {
111 Id id{};
112 id.type.Assign(type);
113 GetUseTracker(type).uses_temp = true;
114 inst.SetDefinition<Id>(id);
115 return 't' + Representation(inst.Definition<Id>());
116 }
117}
118
119std::string VarAlloc::Define(IR::Inst& inst, IR::Type type) {
120 return Define(inst, RegType(type));
121}
122
123std::string VarAlloc::PhiDefine(IR::Inst& inst, IR::Type type) {
124 return AddDefine(inst, RegType(type));
125}
126
127std::string VarAlloc::AddDefine(IR::Inst& inst, GlslVarType type) {
128 if (inst.HasUses()) {
129 inst.SetDefinition<Id>(Alloc(type));
130 return Representation(inst.Definition<Id>());
131 } else {
132 return "";
133 }
134 return Representation(inst.Definition<Id>());
135}
136
137std::string VarAlloc::Consume(const IR::Value& value) {
138 return value.IsImmediate() ? MakeImm(value) : ConsumeInst(*value.InstRecursive());
139}
140
141std::string VarAlloc::ConsumeInst(IR::Inst& inst) {
142 inst.DestructiveRemoveUsage();
143 if (!inst.HasUses()) {
144 Free(inst.Definition<Id>());
145 }
146 return Representation(inst.Definition<Id>());
147}
148
149std::string VarAlloc::GetGlslType(IR::Type type) const {
150 return GetGlslType(RegType(type));
151}
152
153Id VarAlloc::Alloc(GlslVarType type) {
154 auto& use_tracker{GetUseTracker(type)};
155 const auto num_vars{use_tracker.var_use.size()};
156 for (size_t var = 0; var < num_vars; ++var) {
157 if (use_tracker.var_use[var]) {
158 continue;
159 }
160 use_tracker.num_used = std::max(use_tracker.num_used, var + 1);
161 use_tracker.var_use[var] = true;
162 Id ret{};
163 ret.is_valid.Assign(1);
164 ret.type.Assign(type);
165 ret.index.Assign(static_cast<u32>(var));
166 return ret;
167 }
168 // Allocate a new variable
169 use_tracker.var_use.push_back(true);
170 Id ret{};
171 ret.is_valid.Assign(1);
172 ret.type.Assign(type);
173 ret.index.Assign(static_cast<u32>(use_tracker.num_used));
174 ++use_tracker.num_used;
175 return ret;
176}
177
178void VarAlloc::Free(Id id) {
179 if (id.is_valid == 0) {
180 throw LogicError("Freeing invalid variable");
181 }
182 auto& use_tracker{GetUseTracker(id.type)};
183 use_tracker.var_use[id.index] = false;
184}
185
186GlslVarType VarAlloc::RegType(IR::Type type) const {
187 switch (type) {
188 case IR::Type::U1:
189 return GlslVarType::U1;
190 case IR::Type::U32:
191 return GlslVarType::U32;
192 case IR::Type::F32:
193 return GlslVarType::F32;
194 case IR::Type::U64:
195 return GlslVarType::U64;
196 case IR::Type::F64:
197 return GlslVarType::F64;
198 default:
199 throw NotImplementedException("IR type {}", type);
200 }
201}
202
203std::string VarAlloc::GetGlslType(GlslVarType type) const {
204 switch (type) {
205 case GlslVarType::U1:
206 return "bool";
207 case GlslVarType::F16x2:
208 return "f16vec2";
209 case GlslVarType::U32:
210 return "uint";
211 case GlslVarType::F32:
212 case GlslVarType::PrecF32:
213 return "float";
214 case GlslVarType::U64:
215 return "uint64_t";
216 case GlslVarType::F64:
217 case GlslVarType::PrecF64:
218 return "double";
219 case GlslVarType::U32x2:
220 return "uvec2";
221 case GlslVarType::F32x2:
222 return "vec2";
223 case GlslVarType::U32x3:
224 return "uvec3";
225 case GlslVarType::F32x3:
226 return "vec3";
227 case GlslVarType::U32x4:
228 return "uvec4";
229 case GlslVarType::F32x4:
230 return "vec4";
231 case GlslVarType::Void:
232 return "";
233 default:
234 throw NotImplementedException("Type {}", type);
235 }
236}
237
238VarAlloc::UseTracker& VarAlloc::GetUseTracker(GlslVarType type) {
239 switch (type) {
240 case GlslVarType::U1:
241 return var_bool;
242 case GlslVarType::F16x2:
243 return var_f16x2;
244 case GlslVarType::U32:
245 return var_u32;
246 case GlslVarType::F32:
247 return var_f32;
248 case GlslVarType::U64:
249 return var_u64;
250 case GlslVarType::F64:
251 return var_f64;
252 case GlslVarType::U32x2:
253 return var_u32x2;
254 case GlslVarType::F32x2:
255 return var_f32x2;
256 case GlslVarType::U32x3:
257 return var_u32x3;
258 case GlslVarType::F32x3:
259 return var_f32x3;
260 case GlslVarType::U32x4:
261 return var_u32x4;
262 case GlslVarType::F32x4:
263 return var_f32x4;
264 case GlslVarType::PrecF32:
265 return var_precf32;
266 case GlslVarType::PrecF64:
267 return var_precf64;
268 default:
269 throw NotImplementedException("Type {}", type);
270 }
271}
272
273const VarAlloc::UseTracker& VarAlloc::GetUseTracker(GlslVarType type) const {
274 switch (type) {
275 case GlslVarType::U1:
276 return var_bool;
277 case GlslVarType::F16x2:
278 return var_f16x2;
279 case GlslVarType::U32:
280 return var_u32;
281 case GlslVarType::F32:
282 return var_f32;
283 case GlslVarType::U64:
284 return var_u64;
285 case GlslVarType::F64:
286 return var_f64;
287 case GlslVarType::U32x2:
288 return var_u32x2;
289 case GlslVarType::F32x2:
290 return var_f32x2;
291 case GlslVarType::U32x3:
292 return var_u32x3;
293 case GlslVarType::F32x3:
294 return var_f32x3;
295 case GlslVarType::U32x4:
296 return var_u32x4;
297 case GlslVarType::F32x4:
298 return var_f32x4;
299 case GlslVarType::PrecF32:
300 return var_precf32;
301 case GlslVarType::PrecF64:
302 return var_precf64;
303 default:
304 throw NotImplementedException("Type {}", type);
305 }
306}
307
308} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/var_alloc.h b/src/shader_recompiler/backend/glsl/var_alloc.h
new file mode 100644
index 000000000..8b49f32a6
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/var_alloc.h
@@ -0,0 +1,105 @@
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 <bitset>
8#include <string>
9#include <vector>
10
11#include "common/bit_field.h"
12#include "common/common_types.h"
13
14namespace Shader::IR {
15class Inst;
16class Value;
17enum class Type;
18} // namespace Shader::IR
19
20namespace Shader::Backend::GLSL {
21enum class GlslVarType : u32 {
22 U1,
23 F16x2,
24 U32,
25 F32,
26 U64,
27 F64,
28 U32x2,
29 F32x2,
30 U32x3,
31 F32x3,
32 U32x4,
33 F32x4,
34 PrecF32,
35 PrecF64,
36 Void,
37};
38
39struct Id {
40 union {
41 u32 raw;
42 BitField<0, 1, u32> is_valid;
43 BitField<1, 4, GlslVarType> type;
44 BitField<6, 26, u32> index;
45 };
46
47 bool operator==(Id rhs) const noexcept {
48 return raw == rhs.raw;
49 }
50 bool operator!=(Id rhs) const noexcept {
51 return !operator==(rhs);
52 }
53};
54static_assert(sizeof(Id) == sizeof(u32));
55
56class VarAlloc {
57public:
58 struct UseTracker {
59 bool uses_temp{};
60 size_t num_used{};
61 std::vector<bool> var_use;
62 };
63
64 /// Used for explicit usages of variables, may revert to temporaries
65 std::string Define(IR::Inst& inst, GlslVarType type);
66 std::string Define(IR::Inst& inst, IR::Type type);
67
68 /// Used to assign variables used by the IR. May return a blank string if
69 /// the instruction's result is unused in the IR.
70 std::string AddDefine(IR::Inst& inst, GlslVarType type);
71 std::string PhiDefine(IR::Inst& inst, IR::Type type);
72
73 std::string Consume(const IR::Value& value);
74 std::string ConsumeInst(IR::Inst& inst);
75
76 std::string GetGlslType(GlslVarType type) const;
77 std::string GetGlslType(IR::Type type) const;
78
79 const UseTracker& GetUseTracker(GlslVarType type) const;
80 std::string Representation(u32 index, GlslVarType type) const;
81
82private:
83 GlslVarType RegType(IR::Type type) const;
84 Id Alloc(GlslVarType type);
85 void Free(Id id);
86 UseTracker& GetUseTracker(GlslVarType type);
87 std::string Representation(Id id) const;
88
89 UseTracker var_bool{};
90 UseTracker var_f16x2{};
91 UseTracker var_u32{};
92 UseTracker var_u32x2{};
93 UseTracker var_u32x3{};
94 UseTracker var_u32x4{};
95 UseTracker var_f32{};
96 UseTracker var_f32x2{};
97 UseTracker var_f32x3{};
98 UseTracker var_f32x4{};
99 UseTracker var_u64{};
100 UseTracker var_f64{};
101 UseTracker var_precf32{};
102 UseTracker var_precf64{};
103};
104
105} // namespace Shader::Backend::GLSL
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..2d29d8c14
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_context.cpp
@@ -0,0 +1,1368 @@
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 <climits>
8#include <string_view>
9
10#include <fmt/format.h>
11
12#include "common/common_types.h"
13#include "common/div_ceil.h"
14#include "shader_recompiler/backend/spirv/emit_context.h"
15
16namespace Shader::Backend::SPIRV {
17namespace {
18enum class Operation {
19 Increment,
20 Decrement,
21 FPAdd,
22 FPMin,
23 FPMax,
24};
25
26struct AttrInfo {
27 Id pointer;
28 Id id;
29 bool needs_cast;
30};
31
32Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
33 const spv::ImageFormat format{spv::ImageFormat::Unknown};
34 const Id type{ctx.F32[1]};
35 const bool depth{desc.is_depth};
36 switch (desc.type) {
37 case TextureType::Color1D:
38 return ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format);
39 case TextureType::ColorArray1D:
40 return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format);
41 case TextureType::Color2D:
42 return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, false, 1, format);
43 case TextureType::ColorArray2D:
44 return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, false, 1, format);
45 case TextureType::Color3D:
46 return ctx.TypeImage(type, spv::Dim::Dim3D, depth, false, false, 1, format);
47 case TextureType::ColorCube:
48 return ctx.TypeImage(type, spv::Dim::Cube, depth, false, false, 1, format);
49 case TextureType::ColorArrayCube:
50 return ctx.TypeImage(type, spv::Dim::Cube, depth, true, false, 1, format);
51 case TextureType::Buffer:
52 break;
53 }
54 throw InvalidArgument("Invalid texture type {}", desc.type);
55}
56
57spv::ImageFormat GetImageFormat(ImageFormat format) {
58 switch (format) {
59 case ImageFormat::Typeless:
60 return spv::ImageFormat::Unknown;
61 case ImageFormat::R8_UINT:
62 return spv::ImageFormat::R8ui;
63 case ImageFormat::R8_SINT:
64 return spv::ImageFormat::R8i;
65 case ImageFormat::R16_UINT:
66 return spv::ImageFormat::R16ui;
67 case ImageFormat::R16_SINT:
68 return spv::ImageFormat::R16i;
69 case ImageFormat::R32_UINT:
70 return spv::ImageFormat::R32ui;
71 case ImageFormat::R32G32_UINT:
72 return spv::ImageFormat::Rg32ui;
73 case ImageFormat::R32G32B32A32_UINT:
74 return spv::ImageFormat::Rgba32ui;
75 }
76 throw InvalidArgument("Invalid image format {}", format);
77}
78
79Id ImageType(EmitContext& ctx, const ImageDescriptor& desc) {
80 const spv::ImageFormat format{GetImageFormat(desc.format)};
81 const Id type{ctx.U32[1]};
82 switch (desc.type) {
83 case TextureType::Color1D:
84 return ctx.TypeImage(type, spv::Dim::Dim1D, false, false, false, 2, format);
85 case TextureType::ColorArray1D:
86 return ctx.TypeImage(type, spv::Dim::Dim1D, false, true, false, 2, format);
87 case TextureType::Color2D:
88 return ctx.TypeImage(type, spv::Dim::Dim2D, false, false, false, 2, format);
89 case TextureType::ColorArray2D:
90 return ctx.TypeImage(type, spv::Dim::Dim2D, false, true, false, 2, format);
91 case TextureType::Color3D:
92 return ctx.TypeImage(type, spv::Dim::Dim3D, false, false, false, 2, format);
93 case TextureType::Buffer:
94 throw NotImplementedException("Image buffer");
95 default:
96 break;
97 }
98 throw InvalidArgument("Invalid texture type {}", desc.type);
99}
100
101Id DefineVariable(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin,
102 spv::StorageClass storage_class) {
103 const Id pointer_type{ctx.TypePointer(storage_class, type)};
104 const Id id{ctx.AddGlobalVariable(pointer_type, storage_class)};
105 if (builtin) {
106 ctx.Decorate(id, spv::Decoration::BuiltIn, *builtin);
107 }
108 ctx.interfaces.push_back(id);
109 return id;
110}
111
112u32 NumVertices(InputTopology input_topology) {
113 switch (input_topology) {
114 case InputTopology::Points:
115 return 1;
116 case InputTopology::Lines:
117 return 2;
118 case InputTopology::LinesAdjacency:
119 return 4;
120 case InputTopology::Triangles:
121 return 3;
122 case InputTopology::TrianglesAdjacency:
123 return 6;
124 }
125 throw InvalidArgument("Invalid input topology {}", input_topology);
126}
127
128Id DefineInput(EmitContext& ctx, Id type, bool per_invocation,
129 std::optional<spv::BuiltIn> builtin = std::nullopt) {
130 switch (ctx.stage) {
131 case Stage::TessellationControl:
132 case Stage::TessellationEval:
133 if (per_invocation) {
134 type = ctx.TypeArray(type, ctx.Const(32u));
135 }
136 break;
137 case Stage::Geometry:
138 if (per_invocation) {
139 const u32 num_vertices{NumVertices(ctx.runtime_info.input_topology)};
140 type = ctx.TypeArray(type, ctx.Const(num_vertices));
141 }
142 break;
143 default:
144 break;
145 }
146 return DefineVariable(ctx, type, builtin, spv::StorageClass::Input);
147}
148
149Id DefineOutput(EmitContext& ctx, Id type, std::optional<u32> invocations,
150 std::optional<spv::BuiltIn> builtin = std::nullopt) {
151 if (invocations && ctx.stage == Stage::TessellationControl) {
152 type = ctx.TypeArray(type, ctx.Const(*invocations));
153 }
154 return DefineVariable(ctx, type, builtin, spv::StorageClass::Output);
155}
156
157void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invocations) {
158 static constexpr std::string_view swizzle{"xyzw"};
159 const size_t base_attr_index{static_cast<size_t>(IR::Attribute::Generic0X) + index * 4};
160 u32 element{0};
161 while (element < 4) {
162 const u32 remainder{4 - element};
163 const TransformFeedbackVarying* xfb_varying{};
164 if (!ctx.runtime_info.xfb_varyings.empty()) {
165 xfb_varying = &ctx.runtime_info.xfb_varyings[base_attr_index + element];
166 xfb_varying = xfb_varying && xfb_varying->components > 0 ? xfb_varying : nullptr;
167 }
168 const u32 num_components{xfb_varying ? xfb_varying->components : remainder};
169
170 const Id id{DefineOutput(ctx, ctx.F32[num_components], invocations)};
171 ctx.Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
172 if (element > 0) {
173 ctx.Decorate(id, spv::Decoration::Component, element);
174 }
175 if (xfb_varying) {
176 ctx.Decorate(id, spv::Decoration::XfbBuffer, xfb_varying->buffer);
177 ctx.Decorate(id, spv::Decoration::XfbStride, xfb_varying->stride);
178 ctx.Decorate(id, spv::Decoration::Offset, xfb_varying->offset);
179 }
180 if (num_components < 4 || element > 0) {
181 const std::string_view subswizzle{swizzle.substr(element, num_components)};
182 ctx.Name(id, fmt::format("out_attr{}_{}", index, subswizzle));
183 } else {
184 ctx.Name(id, fmt::format("out_attr{}", index));
185 }
186 const GenericElementInfo info{
187 .id = id,
188 .first_element = element,
189 .num_components = num_components,
190 };
191 std::fill_n(ctx.output_generics[index].begin() + element, num_components, info);
192 element += num_components;
193 }
194}
195
196Id GetAttributeType(EmitContext& ctx, AttributeType type) {
197 switch (type) {
198 case AttributeType::Float:
199 return ctx.F32[4];
200 case AttributeType::SignedInt:
201 return ctx.TypeVector(ctx.TypeInt(32, true), 4);
202 case AttributeType::UnsignedInt:
203 return ctx.U32[4];
204 case AttributeType::Disabled:
205 break;
206 }
207 throw InvalidArgument("Invalid attribute type {}", type);
208}
209
210std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) {
211 const AttributeType type{ctx.runtime_info.generic_input_types.at(index)};
212 switch (type) {
213 case AttributeType::Float:
214 return AttrInfo{ctx.input_f32, ctx.F32[1], false};
215 case AttributeType::UnsignedInt:
216 return AttrInfo{ctx.input_u32, ctx.U32[1], true};
217 case AttributeType::SignedInt:
218 return AttrInfo{ctx.input_s32, ctx.TypeInt(32, true), true};
219 case AttributeType::Disabled:
220 return std::nullopt;
221 }
222 throw InvalidArgument("Invalid attribute type {}", type);
223}
224
225std::string_view StageName(Stage stage) {
226 switch (stage) {
227 case Stage::VertexA:
228 return "vs_a";
229 case Stage::VertexB:
230 return "vs";
231 case Stage::TessellationControl:
232 return "tcs";
233 case Stage::TessellationEval:
234 return "tes";
235 case Stage::Geometry:
236 return "gs";
237 case Stage::Fragment:
238 return "fs";
239 case Stage::Compute:
240 return "cs";
241 }
242 throw InvalidArgument("Invalid stage {}", stage);
243}
244
245template <typename... Args>
246void Name(EmitContext& ctx, Id object, std::string_view format_str, Args&&... args) {
247 ctx.Name(object, fmt::format(fmt::runtime(format_str), StageName(ctx.stage),
248 std::forward<Args>(args)...)
249 .c_str());
250}
251
252void DefineConstBuffers(EmitContext& ctx, const Info& info, Id UniformDefinitions::*member_type,
253 u32 binding, Id type, char type_char, u32 element_size) {
254 const Id array_type{ctx.TypeArray(type, ctx.Const(65536U / element_size))};
255 ctx.Decorate(array_type, spv::Decoration::ArrayStride, element_size);
256
257 const Id struct_type{ctx.TypeStruct(array_type)};
258 Name(ctx, struct_type, "{}_cbuf_block_{}{}", ctx.stage, type_char, element_size * CHAR_BIT);
259 ctx.Decorate(struct_type, spv::Decoration::Block);
260 ctx.MemberName(struct_type, 0, "data");
261 ctx.MemberDecorate(struct_type, 0, spv::Decoration::Offset, 0U);
262
263 const Id struct_pointer_type{ctx.TypePointer(spv::StorageClass::Uniform, struct_type)};
264 const Id uniform_type{ctx.TypePointer(spv::StorageClass::Uniform, type)};
265 ctx.uniform_types.*member_type = uniform_type;
266
267 for (const ConstantBufferDescriptor& desc : info.constant_buffer_descriptors) {
268 const Id id{ctx.AddGlobalVariable(struct_pointer_type, spv::StorageClass::Uniform)};
269 ctx.Decorate(id, spv::Decoration::Binding, binding);
270 ctx.Decorate(id, spv::Decoration::DescriptorSet, 0U);
271 ctx.Name(id, fmt::format("c{}", desc.index));
272 for (size_t i = 0; i < desc.count; ++i) {
273 ctx.cbufs[desc.index + i].*member_type = id;
274 }
275 if (ctx.profile.supported_spirv >= 0x00010400) {
276 ctx.interfaces.push_back(id);
277 }
278 binding += desc.count;
279 }
280}
281
282void DefineSsbos(EmitContext& ctx, StorageTypeDefinition& type_def,
283 Id StorageDefinitions::*member_type, const Info& info, u32 binding, Id type,
284 u32 stride) {
285 const Id array_type{ctx.TypeRuntimeArray(type)};
286 ctx.Decorate(array_type, spv::Decoration::ArrayStride, stride);
287
288 const Id struct_type{ctx.TypeStruct(array_type)};
289 ctx.Decorate(struct_type, spv::Decoration::Block);
290 ctx.MemberDecorate(struct_type, 0, spv::Decoration::Offset, 0U);
291
292 const Id struct_pointer{ctx.TypePointer(spv::StorageClass::StorageBuffer, struct_type)};
293 type_def.array = struct_pointer;
294 type_def.element = ctx.TypePointer(spv::StorageClass::StorageBuffer, type);
295
296 u32 index{};
297 for (const StorageBufferDescriptor& desc : info.storage_buffers_descriptors) {
298 const Id id{ctx.AddGlobalVariable(struct_pointer, spv::StorageClass::StorageBuffer)};
299 ctx.Decorate(id, spv::Decoration::Binding, binding);
300 ctx.Decorate(id, spv::Decoration::DescriptorSet, 0U);
301 ctx.Name(id, fmt::format("ssbo{}", index));
302 if (ctx.profile.supported_spirv >= 0x00010400) {
303 ctx.interfaces.push_back(id);
304 }
305 for (size_t i = 0; i < desc.count; ++i) {
306 ctx.ssbos[index + i].*member_type = id;
307 }
308 index += desc.count;
309 binding += desc.count;
310 }
311}
312
313Id CasFunction(EmitContext& ctx, Operation operation, Id value_type) {
314 const Id func_type{ctx.TypeFunction(value_type, value_type, value_type)};
315 const Id func{ctx.OpFunction(value_type, spv::FunctionControlMask::MaskNone, func_type)};
316 const Id op_a{ctx.OpFunctionParameter(value_type)};
317 const Id op_b{ctx.OpFunctionParameter(value_type)};
318 ctx.AddLabel();
319 Id result{};
320 switch (operation) {
321 case Operation::Increment: {
322 const Id pred{ctx.OpUGreaterThanEqual(ctx.U1, op_a, op_b)};
323 const Id incr{ctx.OpIAdd(value_type, op_a, ctx.Constant(value_type, 1))};
324 result = ctx.OpSelect(value_type, pred, ctx.u32_zero_value, incr);
325 break;
326 }
327 case Operation::Decrement: {
328 const Id lhs{ctx.OpIEqual(ctx.U1, op_a, ctx.Constant(value_type, 0u))};
329 const Id rhs{ctx.OpUGreaterThan(ctx.U1, op_a, op_b)};
330 const Id pred{ctx.OpLogicalOr(ctx.U1, lhs, rhs)};
331 const Id decr{ctx.OpISub(value_type, op_a, ctx.Constant(value_type, 1))};
332 result = ctx.OpSelect(value_type, pred, op_b, decr);
333 break;
334 }
335 case Operation::FPAdd:
336 result = ctx.OpFAdd(value_type, op_a, op_b);
337 break;
338 case Operation::FPMin:
339 result = ctx.OpFMin(value_type, op_a, op_b);
340 break;
341 case Operation::FPMax:
342 result = ctx.OpFMax(value_type, op_a, op_b);
343 break;
344 default:
345 break;
346 }
347 ctx.OpReturnValue(result);
348 ctx.OpFunctionEnd();
349 return func;
350}
351
352Id CasLoop(EmitContext& ctx, Operation operation, Id array_pointer, Id element_pointer,
353 Id value_type, Id memory_type, spv::Scope scope) {
354 const bool is_shared{scope == spv::Scope::Workgroup};
355 const bool is_struct{!is_shared || ctx.profile.support_explicit_workgroup_layout};
356 const Id cas_func{CasFunction(ctx, operation, value_type)};
357 const Id zero{ctx.u32_zero_value};
358 const Id scope_id{ctx.Const(static_cast<u32>(scope))};
359
360 const Id loop_header{ctx.OpLabel()};
361 const Id continue_block{ctx.OpLabel()};
362 const Id merge_block{ctx.OpLabel()};
363 const Id func_type{is_shared
364 ? ctx.TypeFunction(value_type, ctx.U32[1], value_type)
365 : ctx.TypeFunction(value_type, ctx.U32[1], value_type, array_pointer)};
366
367 const Id func{ctx.OpFunction(value_type, spv::FunctionControlMask::MaskNone, func_type)};
368 const Id index{ctx.OpFunctionParameter(ctx.U32[1])};
369 const Id op_b{ctx.OpFunctionParameter(value_type)};
370 const Id base{is_shared ? ctx.shared_memory_u32 : ctx.OpFunctionParameter(array_pointer)};
371 ctx.AddLabel();
372 ctx.OpBranch(loop_header);
373 ctx.AddLabel(loop_header);
374
375 ctx.OpLoopMerge(merge_block, continue_block, spv::LoopControlMask::MaskNone);
376 ctx.OpBranch(continue_block);
377
378 ctx.AddLabel(continue_block);
379 const Id word_pointer{is_struct ? ctx.OpAccessChain(element_pointer, base, zero, index)
380 : ctx.OpAccessChain(element_pointer, base, index)};
381 if (value_type.value == ctx.F32[2].value) {
382 const Id u32_value{ctx.OpLoad(ctx.U32[1], word_pointer)};
383 const Id value{ctx.OpUnpackHalf2x16(ctx.F32[2], u32_value)};
384 const Id new_value{ctx.OpFunctionCall(value_type, cas_func, value, op_b)};
385 const Id u32_new_value{ctx.OpPackHalf2x16(ctx.U32[1], new_value)};
386 const Id atomic_res{ctx.OpAtomicCompareExchange(ctx.U32[1], word_pointer, scope_id, zero,
387 zero, u32_new_value, u32_value)};
388 const Id success{ctx.OpIEqual(ctx.U1, atomic_res, u32_value)};
389 ctx.OpBranchConditional(success, merge_block, loop_header);
390
391 ctx.AddLabel(merge_block);
392 ctx.OpReturnValue(ctx.OpUnpackHalf2x16(ctx.F32[2], atomic_res));
393 } else {
394 const Id value{ctx.OpLoad(memory_type, word_pointer)};
395 const bool matching_type{value_type.value == memory_type.value};
396 const Id bitcast_value{matching_type ? value : ctx.OpBitcast(value_type, value)};
397 const Id cal_res{ctx.OpFunctionCall(value_type, cas_func, bitcast_value, op_b)};
398 const Id new_value{matching_type ? cal_res : ctx.OpBitcast(memory_type, cal_res)};
399 const Id atomic_res{ctx.OpAtomicCompareExchange(ctx.U32[1], word_pointer, scope_id, zero,
400 zero, new_value, value)};
401 const Id success{ctx.OpIEqual(ctx.U1, atomic_res, value)};
402 ctx.OpBranchConditional(success, merge_block, loop_header);
403
404 ctx.AddLabel(merge_block);
405 ctx.OpReturnValue(ctx.OpBitcast(value_type, atomic_res));
406 }
407 ctx.OpFunctionEnd();
408 return func;
409}
410
411template <typename Desc>
412std::string NameOf(Stage stage, const Desc& desc, std::string_view prefix) {
413 if (desc.count > 1) {
414 return fmt::format("{}_{}{}_{:02x}x{}", StageName(stage), prefix, desc.cbuf_index,
415 desc.cbuf_offset, desc.count);
416 } else {
417 return fmt::format("{}_{}{}_{:02x}", StageName(stage), prefix, desc.cbuf_index,
418 desc.cbuf_offset);
419 }
420}
421
422Id DescType(EmitContext& ctx, Id sampled_type, Id pointer_type, u32 count) {
423 if (count > 1) {
424 const Id array_type{ctx.TypeArray(sampled_type, ctx.Const(count))};
425 return ctx.TypePointer(spv::StorageClass::UniformConstant, array_type);
426 } else {
427 return pointer_type;
428 }
429}
430} // Anonymous namespace
431
432void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) {
433 defs[0] = sirit_ctx.Name(base_type, name);
434
435 std::array<char, 6> def_name;
436 for (int i = 1; i < 4; ++i) {
437 const std::string_view def_name_view(
438 def_name.data(),
439 fmt::format_to_n(def_name.data(), def_name.size(), "{}x{}", name, i + 1).size);
440 defs[static_cast<size_t>(i)] =
441 sirit_ctx.Name(sirit_ctx.TypeVector(base_type, i + 1), def_name_view);
442 }
443}
444
445EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_info_,
446 IR::Program& program, Bindings& bindings)
447 : Sirit::Module(profile_.supported_spirv), profile{profile_},
448 runtime_info{runtime_info_}, stage{program.stage} {
449 const bool is_unified{profile.unified_descriptor_binding};
450 u32& uniform_binding{is_unified ? bindings.unified : bindings.uniform_buffer};
451 u32& storage_binding{is_unified ? bindings.unified : bindings.storage_buffer};
452 u32& texture_binding{is_unified ? bindings.unified : bindings.texture};
453 u32& image_binding{is_unified ? bindings.unified : bindings.image};
454 AddCapability(spv::Capability::Shader);
455 DefineCommonTypes(program.info);
456 DefineCommonConstants();
457 DefineInterfaces(program);
458 DefineLocalMemory(program);
459 DefineSharedMemory(program);
460 DefineSharedMemoryFunctions(program);
461 DefineConstantBuffers(program.info, uniform_binding);
462 DefineStorageBuffers(program.info, storage_binding);
463 DefineTextureBuffers(program.info, texture_binding);
464 DefineImageBuffers(program.info, image_binding);
465 DefineTextures(program.info, texture_binding);
466 DefineImages(program.info, image_binding);
467 DefineAttributeMemAccess(program.info);
468 DefineGlobalMemoryFunctions(program.info);
469}
470
471EmitContext::~EmitContext() = default;
472
473Id EmitContext::Def(const IR::Value& value) {
474 if (!value.IsImmediate()) {
475 return value.InstRecursive()->Definition<Id>();
476 }
477 switch (value.Type()) {
478 case IR::Type::Void:
479 // Void instructions are used for optional arguments (e.g. texture offsets)
480 // They are not meant to be used in the SPIR-V module
481 return Id{};
482 case IR::Type::U1:
483 return value.U1() ? true_value : false_value;
484 case IR::Type::U32:
485 return Const(value.U32());
486 case IR::Type::U64:
487 return Constant(U64, value.U64());
488 case IR::Type::F32:
489 return Const(value.F32());
490 case IR::Type::F64:
491 return Constant(F64[1], value.F64());
492 default:
493 throw NotImplementedException("Immediate type {}", value.Type());
494 }
495}
496
497Id EmitContext::BitOffset8(const IR::Value& offset) {
498 if (offset.IsImmediate()) {
499 return Const((offset.U32() % 4) * 8);
500 }
501 return OpBitwiseAnd(U32[1], OpShiftLeftLogical(U32[1], Def(offset), Const(3u)), Const(24u));
502}
503
504Id EmitContext::BitOffset16(const IR::Value& offset) {
505 if (offset.IsImmediate()) {
506 return Const(((offset.U32() / 2) % 2) * 16);
507 }
508 return OpBitwiseAnd(U32[1], OpShiftLeftLogical(U32[1], Def(offset), Const(3u)), Const(16u));
509}
510
511void EmitContext::DefineCommonTypes(const Info& info) {
512 void_id = TypeVoid();
513
514 U1 = Name(TypeBool(), "u1");
515
516 F32.Define(*this, TypeFloat(32), "f32");
517 U32.Define(*this, TypeInt(32, false), "u32");
518 S32.Define(*this, TypeInt(32, true), "s32");
519
520 private_u32 = Name(TypePointer(spv::StorageClass::Private, U32[1]), "private_u32");
521
522 input_f32 = Name(TypePointer(spv::StorageClass::Input, F32[1]), "input_f32");
523 input_u32 = Name(TypePointer(spv::StorageClass::Input, U32[1]), "input_u32");
524 input_s32 = Name(TypePointer(spv::StorageClass::Input, TypeInt(32, true)), "input_s32");
525
526 output_f32 = Name(TypePointer(spv::StorageClass::Output, F32[1]), "output_f32");
527 output_u32 = Name(TypePointer(spv::StorageClass::Output, U32[1]), "output_u32");
528
529 if (info.uses_int8 && profile.support_int8) {
530 AddCapability(spv::Capability::Int8);
531 U8 = Name(TypeInt(8, false), "u8");
532 S8 = Name(TypeInt(8, true), "s8");
533 }
534 if (info.uses_int16 && profile.support_int16) {
535 AddCapability(spv::Capability::Int16);
536 U16 = Name(TypeInt(16, false), "u16");
537 S16 = Name(TypeInt(16, true), "s16");
538 }
539 if (info.uses_int64) {
540 AddCapability(spv::Capability::Int64);
541 U64 = Name(TypeInt(64, false), "u64");
542 }
543 if (info.uses_fp16) {
544 AddCapability(spv::Capability::Float16);
545 F16.Define(*this, TypeFloat(16), "f16");
546 }
547 if (info.uses_fp64) {
548 AddCapability(spv::Capability::Float64);
549 F64.Define(*this, TypeFloat(64), "f64");
550 }
551}
552
553void EmitContext::DefineCommonConstants() {
554 true_value = ConstantTrue(U1);
555 false_value = ConstantFalse(U1);
556 u32_zero_value = Const(0U);
557 f32_zero_value = Const(0.0f);
558}
559
560void EmitContext::DefineInterfaces(const IR::Program& program) {
561 DefineInputs(program);
562 DefineOutputs(program);
563}
564
565void EmitContext::DefineLocalMemory(const IR::Program& program) {
566 if (program.local_memory_size == 0) {
567 return;
568 }
569 const u32 num_elements{Common::DivCeil(program.local_memory_size, 4U)};
570 const Id type{TypeArray(U32[1], Const(num_elements))};
571 const Id pointer{TypePointer(spv::StorageClass::Private, type)};
572 local_memory = AddGlobalVariable(pointer, spv::StorageClass::Private);
573 if (profile.supported_spirv >= 0x00010400) {
574 interfaces.push_back(local_memory);
575 }
576}
577
578void EmitContext::DefineSharedMemory(const IR::Program& program) {
579 if (program.shared_memory_size == 0) {
580 return;
581 }
582 const auto make{[&](Id element_type, u32 element_size) {
583 const u32 num_elements{Common::DivCeil(program.shared_memory_size, element_size)};
584 const Id array_type{TypeArray(element_type, Const(num_elements))};
585 Decorate(array_type, spv::Decoration::ArrayStride, element_size);
586
587 const Id struct_type{TypeStruct(array_type)};
588 MemberDecorate(struct_type, 0U, spv::Decoration::Offset, 0U);
589 Decorate(struct_type, spv::Decoration::Block);
590
591 const Id pointer{TypePointer(spv::StorageClass::Workgroup, struct_type)};
592 const Id element_pointer{TypePointer(spv::StorageClass::Workgroup, element_type)};
593 const Id variable{AddGlobalVariable(pointer, spv::StorageClass::Workgroup)};
594 Decorate(variable, spv::Decoration::Aliased);
595 interfaces.push_back(variable);
596
597 return std::make_tuple(variable, element_pointer, pointer);
598 }};
599 if (profile.support_explicit_workgroup_layout) {
600 AddExtension("SPV_KHR_workgroup_memory_explicit_layout");
601 AddCapability(spv::Capability::WorkgroupMemoryExplicitLayoutKHR);
602 if (program.info.uses_int8) {
603 AddCapability(spv::Capability::WorkgroupMemoryExplicitLayout8BitAccessKHR);
604 std::tie(shared_memory_u8, shared_u8, std::ignore) = make(U8, 1);
605 }
606 if (program.info.uses_int16) {
607 AddCapability(spv::Capability::WorkgroupMemoryExplicitLayout16BitAccessKHR);
608 std::tie(shared_memory_u16, shared_u16, std::ignore) = make(U16, 2);
609 }
610 if (program.info.uses_int64) {
611 std::tie(shared_memory_u64, shared_u64, std::ignore) = make(U64, 8);
612 }
613 std::tie(shared_memory_u32, shared_u32, shared_memory_u32_type) = make(U32[1], 4);
614 std::tie(shared_memory_u32x2, shared_u32x2, std::ignore) = make(U32[2], 8);
615 std::tie(shared_memory_u32x4, shared_u32x4, std::ignore) = make(U32[4], 16);
616 return;
617 }
618 const u32 num_elements{Common::DivCeil(program.shared_memory_size, 4U)};
619 const Id type{TypeArray(U32[1], Const(num_elements))};
620 shared_memory_u32_type = TypePointer(spv::StorageClass::Workgroup, type);
621
622 shared_u32 = TypePointer(spv::StorageClass::Workgroup, U32[1]);
623 shared_memory_u32 = AddGlobalVariable(shared_memory_u32_type, spv::StorageClass::Workgroup);
624 interfaces.push_back(shared_memory_u32);
625
626 const Id func_type{TypeFunction(void_id, U32[1], U32[1])};
627 const auto make_function{[&](u32 mask, u32 size) {
628 const Id loop_header{OpLabel()};
629 const Id continue_block{OpLabel()};
630 const Id merge_block{OpLabel()};
631
632 const Id func{OpFunction(void_id, spv::FunctionControlMask::MaskNone, func_type)};
633 const Id offset{OpFunctionParameter(U32[1])};
634 const Id insert_value{OpFunctionParameter(U32[1])};
635 AddLabel();
636 OpBranch(loop_header);
637
638 AddLabel(loop_header);
639 const Id word_offset{OpShiftRightArithmetic(U32[1], offset, Const(2U))};
640 const Id shift_offset{OpShiftLeftLogical(U32[1], offset, Const(3U))};
641 const Id bit_offset{OpBitwiseAnd(U32[1], shift_offset, Const(mask))};
642 const Id count{Const(size)};
643 OpLoopMerge(merge_block, continue_block, spv::LoopControlMask::MaskNone);
644 OpBranch(continue_block);
645
646 AddLabel(continue_block);
647 const Id word_pointer{OpAccessChain(shared_u32, shared_memory_u32, word_offset)};
648 const Id old_value{OpLoad(U32[1], word_pointer)};
649 const Id new_value{OpBitFieldInsert(U32[1], old_value, insert_value, bit_offset, count)};
650 const Id atomic_res{OpAtomicCompareExchange(U32[1], word_pointer, Const(1U), u32_zero_value,
651 u32_zero_value, new_value, old_value)};
652 const Id success{OpIEqual(U1, atomic_res, old_value)};
653 OpBranchConditional(success, merge_block, loop_header);
654
655 AddLabel(merge_block);
656 OpReturn();
657 OpFunctionEnd();
658 return func;
659 }};
660 if (program.info.uses_int8) {
661 shared_store_u8_func = make_function(24, 8);
662 }
663 if (program.info.uses_int16) {
664 shared_store_u16_func = make_function(16, 16);
665 }
666}
667
668void EmitContext::DefineSharedMemoryFunctions(const IR::Program& program) {
669 if (program.info.uses_shared_increment) {
670 increment_cas_shared = CasLoop(*this, Operation::Increment, shared_memory_u32_type,
671 shared_u32, U32[1], U32[1], spv::Scope::Workgroup);
672 }
673 if (program.info.uses_shared_decrement) {
674 decrement_cas_shared = CasLoop(*this, Operation::Decrement, shared_memory_u32_type,
675 shared_u32, U32[1], U32[1], spv::Scope::Workgroup);
676 }
677}
678
679void EmitContext::DefineAttributeMemAccess(const Info& info) {
680 const auto make_load{[&] {
681 const bool is_array{stage == Stage::Geometry};
682 const Id end_block{OpLabel()};
683 const Id default_label{OpLabel()};
684
685 const Id func_type_load{is_array ? TypeFunction(F32[1], U32[1], U32[1])
686 : TypeFunction(F32[1], U32[1])};
687 const Id func{OpFunction(F32[1], spv::FunctionControlMask::MaskNone, func_type_load)};
688 const Id offset{OpFunctionParameter(U32[1])};
689 const Id vertex{is_array ? OpFunctionParameter(U32[1]) : Id{}};
690
691 AddLabel();
692 const Id base_index{OpShiftRightArithmetic(U32[1], offset, Const(2U))};
693 const Id masked_index{OpBitwiseAnd(U32[1], base_index, Const(3U))};
694 const Id compare_index{OpShiftRightArithmetic(U32[1], base_index, Const(2U))};
695 std::vector<Sirit::Literal> literals;
696 std::vector<Id> labels;
697 if (info.loads.AnyComponent(IR::Attribute::PositionX)) {
698 literals.push_back(static_cast<u32>(IR::Attribute::PositionX) >> 2);
699 labels.push_back(OpLabel());
700 }
701 const u32 base_attribute_value = static_cast<u32>(IR::Attribute::Generic0X) >> 2;
702 for (u32 index = 0; index < static_cast<u32>(IR::NUM_GENERICS); ++index) {
703 if (!info.loads.Generic(index)) {
704 continue;
705 }
706 literals.push_back(base_attribute_value + index);
707 labels.push_back(OpLabel());
708 }
709 OpSelectionMerge(end_block, spv::SelectionControlMask::MaskNone);
710 OpSwitch(compare_index, default_label, literals, labels);
711 AddLabel(default_label);
712 OpReturnValue(Const(0.0f));
713 size_t label_index{0};
714 if (info.loads.AnyComponent(IR::Attribute::PositionX)) {
715 AddLabel(labels[label_index]);
716 const Id pointer{is_array
717 ? OpAccessChain(input_f32, input_position, vertex, masked_index)
718 : OpAccessChain(input_f32, input_position, masked_index)};
719 const Id result{OpLoad(F32[1], pointer)};
720 OpReturnValue(result);
721 ++label_index;
722 }
723 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
724 if (!info.loads.Generic(index)) {
725 continue;
726 }
727 AddLabel(labels[label_index]);
728 const auto type{AttrTypes(*this, static_cast<u32>(index))};
729 if (!type) {
730 OpReturnValue(Const(0.0f));
731 ++label_index;
732 continue;
733 }
734 const Id generic_id{input_generics.at(index)};
735 const Id pointer{is_array
736 ? OpAccessChain(type->pointer, generic_id, vertex, masked_index)
737 : OpAccessChain(type->pointer, generic_id, masked_index)};
738 const Id value{OpLoad(type->id, pointer)};
739 const Id result{type->needs_cast ? OpBitcast(F32[1], value) : value};
740 OpReturnValue(result);
741 ++label_index;
742 }
743 AddLabel(end_block);
744 OpUnreachable();
745 OpFunctionEnd();
746 return func;
747 }};
748 const auto make_store{[&] {
749 const Id end_block{OpLabel()};
750 const Id default_label{OpLabel()};
751
752 const Id func_type_store{TypeFunction(void_id, U32[1], F32[1])};
753 const Id func{OpFunction(void_id, spv::FunctionControlMask::MaskNone, func_type_store)};
754 const Id offset{OpFunctionParameter(U32[1])};
755 const Id store_value{OpFunctionParameter(F32[1])};
756 AddLabel();
757 const Id base_index{OpShiftRightArithmetic(U32[1], offset, Const(2U))};
758 const Id masked_index{OpBitwiseAnd(U32[1], base_index, Const(3U))};
759 const Id compare_index{OpShiftRightArithmetic(U32[1], base_index, Const(2U))};
760 std::vector<Sirit::Literal> literals;
761 std::vector<Id> labels;
762 if (info.stores.AnyComponent(IR::Attribute::PositionX)) {
763 literals.push_back(static_cast<u32>(IR::Attribute::PositionX) >> 2);
764 labels.push_back(OpLabel());
765 }
766 const u32 base_attribute_value = static_cast<u32>(IR::Attribute::Generic0X) >> 2;
767 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
768 if (!info.stores.Generic(index)) {
769 continue;
770 }
771 literals.push_back(base_attribute_value + static_cast<u32>(index));
772 labels.push_back(OpLabel());
773 }
774 if (info.stores.ClipDistances()) {
775 literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance0) >> 2);
776 labels.push_back(OpLabel());
777 literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance4) >> 2);
778 labels.push_back(OpLabel());
779 }
780 OpSelectionMerge(end_block, spv::SelectionControlMask::MaskNone);
781 OpSwitch(compare_index, default_label, literals, labels);
782 AddLabel(default_label);
783 OpReturn();
784 size_t label_index{0};
785 if (info.stores.AnyComponent(IR::Attribute::PositionX)) {
786 AddLabel(labels[label_index]);
787 const Id pointer{OpAccessChain(output_f32, output_position, masked_index)};
788 OpStore(pointer, store_value);
789 OpReturn();
790 ++label_index;
791 }
792 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
793 if (!info.stores.Generic(index)) {
794 continue;
795 }
796 if (output_generics[index][0].num_components != 4) {
797 throw NotImplementedException("Physical stores and transform feedbacks");
798 }
799 AddLabel(labels[label_index]);
800 const Id generic_id{output_generics[index][0].id};
801 const Id pointer{OpAccessChain(output_f32, generic_id, masked_index)};
802 OpStore(pointer, store_value);
803 OpReturn();
804 ++label_index;
805 }
806 if (info.stores.ClipDistances()) {
807 AddLabel(labels[label_index]);
808 const Id pointer{OpAccessChain(output_f32, clip_distances, masked_index)};
809 OpStore(pointer, store_value);
810 OpReturn();
811 ++label_index;
812 AddLabel(labels[label_index]);
813 const Id fixed_index{OpIAdd(U32[1], masked_index, Const(4U))};
814 const Id pointer2{OpAccessChain(output_f32, clip_distances, fixed_index)};
815 OpStore(pointer2, store_value);
816 OpReturn();
817 ++label_index;
818 }
819 AddLabel(end_block);
820 OpUnreachable();
821 OpFunctionEnd();
822 return func;
823 }};
824 if (info.loads_indexed_attributes) {
825 indexed_load_func = make_load();
826 }
827 if (info.stores_indexed_attributes) {
828 indexed_store_func = make_store();
829 }
830}
831
832void EmitContext::DefineGlobalMemoryFunctions(const Info& info) {
833 if (!info.uses_global_memory || !profile.support_int64) {
834 return;
835 }
836 using DefPtr = Id StorageDefinitions::*;
837 const Id zero{u32_zero_value};
838 const auto define_body{[&](DefPtr ssbo_member, Id addr, Id element_pointer, u32 shift,
839 auto&& callback) {
840 AddLabel();
841 const size_t num_buffers{info.storage_buffers_descriptors.size()};
842 for (size_t index = 0; index < num_buffers; ++index) {
843 if (!info.nvn_buffer_used[index]) {
844 continue;
845 }
846 const auto& ssbo{info.storage_buffers_descriptors[index]};
847 const Id ssbo_addr_cbuf_offset{Const(ssbo.cbuf_offset / 8)};
848 const Id ssbo_size_cbuf_offset{Const(ssbo.cbuf_offset / 4 + 2)};
849 const Id ssbo_addr_pointer{OpAccessChain(
850 uniform_types.U32x2, cbufs[ssbo.cbuf_index].U32x2, zero, ssbo_addr_cbuf_offset)};
851 const Id ssbo_size_pointer{OpAccessChain(uniform_types.U32, cbufs[ssbo.cbuf_index].U32,
852 zero, ssbo_size_cbuf_offset)};
853
854 const Id ssbo_addr{OpBitcast(U64, OpLoad(U32[2], ssbo_addr_pointer))};
855 const Id ssbo_size{OpUConvert(U64, OpLoad(U32[1], ssbo_size_pointer))};
856 const Id ssbo_end{OpIAdd(U64, ssbo_addr, ssbo_size)};
857 const Id cond{OpLogicalAnd(U1, OpUGreaterThanEqual(U1, addr, ssbo_addr),
858 OpULessThan(U1, addr, ssbo_end))};
859 const Id then_label{OpLabel()};
860 const Id else_label{OpLabel()};
861 OpSelectionMerge(else_label, spv::SelectionControlMask::MaskNone);
862 OpBranchConditional(cond, then_label, else_label);
863 AddLabel(then_label);
864 const Id ssbo_id{ssbos[index].*ssbo_member};
865 const Id ssbo_offset{OpUConvert(U32[1], OpISub(U64, addr, ssbo_addr))};
866 const Id ssbo_index{OpShiftRightLogical(U32[1], ssbo_offset, Const(shift))};
867 const Id ssbo_pointer{OpAccessChain(element_pointer, ssbo_id, zero, ssbo_index)};
868 callback(ssbo_pointer);
869 AddLabel(else_label);
870 }
871 }};
872 const auto define_load{[&](DefPtr ssbo_member, Id element_pointer, Id type, u32 shift) {
873 const Id function_type{TypeFunction(type, U64)};
874 const Id func_id{OpFunction(type, spv::FunctionControlMask::MaskNone, function_type)};
875 const Id addr{OpFunctionParameter(U64)};
876 define_body(ssbo_member, addr, element_pointer, shift,
877 [&](Id ssbo_pointer) { OpReturnValue(OpLoad(type, ssbo_pointer)); });
878 OpReturnValue(ConstantNull(type));
879 OpFunctionEnd();
880 return func_id;
881 }};
882 const auto define_write{[&](DefPtr ssbo_member, Id element_pointer, Id type, u32 shift) {
883 const Id function_type{TypeFunction(void_id, U64, type)};
884 const Id func_id{OpFunction(void_id, spv::FunctionControlMask::MaskNone, function_type)};
885 const Id addr{OpFunctionParameter(U64)};
886 const Id data{OpFunctionParameter(type)};
887 define_body(ssbo_member, addr, element_pointer, shift, [&](Id ssbo_pointer) {
888 OpStore(ssbo_pointer, data);
889 OpReturn();
890 });
891 OpReturn();
892 OpFunctionEnd();
893 return func_id;
894 }};
895 const auto define{
896 [&](DefPtr ssbo_member, const StorageTypeDefinition& type_def, Id type, size_t size) {
897 const Id element_type{type_def.element};
898 const u32 shift{static_cast<u32>(std::countr_zero(size))};
899 const Id load_func{define_load(ssbo_member, element_type, type, shift)};
900 const Id write_func{define_write(ssbo_member, element_type, type, shift)};
901 return std::make_pair(load_func, write_func);
902 }};
903 std::tie(load_global_func_u32, write_global_func_u32) =
904 define(&StorageDefinitions::U32, storage_types.U32, U32[1], sizeof(u32));
905 std::tie(load_global_func_u32x2, write_global_func_u32x2) =
906 define(&StorageDefinitions::U32x2, storage_types.U32x2, U32[2], sizeof(u32[2]));
907 std::tie(load_global_func_u32x4, write_global_func_u32x4) =
908 define(&StorageDefinitions::U32x4, storage_types.U32x4, U32[4], sizeof(u32[4]));
909}
910
911void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
912 if (info.constant_buffer_descriptors.empty()) {
913 return;
914 }
915 if (!profile.support_descriptor_aliasing) {
916 DefineConstBuffers(*this, info, &UniformDefinitions::U32x4, binding, U32[4], 'u',
917 sizeof(u32[4]));
918 for (const ConstantBufferDescriptor& desc : info.constant_buffer_descriptors) {
919 binding += desc.count;
920 }
921 return;
922 }
923 IR::Type types{info.used_constant_buffer_types};
924 if (True(types & IR::Type::U8)) {
925 if (profile.support_int8) {
926 DefineConstBuffers(*this, info, &UniformDefinitions::U8, binding, U8, 'u', sizeof(u8));
927 DefineConstBuffers(*this, info, &UniformDefinitions::S8, binding, S8, 's', sizeof(s8));
928 } else {
929 types |= IR::Type::U32;
930 }
931 }
932 if (True(types & IR::Type::U16)) {
933 if (profile.support_int16) {
934 DefineConstBuffers(*this, info, &UniformDefinitions::U16, binding, U16, 'u',
935 sizeof(u16));
936 DefineConstBuffers(*this, info, &UniformDefinitions::S16, binding, S16, 's',
937 sizeof(s16));
938 } else {
939 types |= IR::Type::U32;
940 }
941 }
942 if (True(types & IR::Type::U32)) {
943 DefineConstBuffers(*this, info, &UniformDefinitions::U32, binding, U32[1], 'u',
944 sizeof(u32));
945 }
946 if (True(types & IR::Type::F32)) {
947 DefineConstBuffers(*this, info, &UniformDefinitions::F32, binding, F32[1], 'f',
948 sizeof(f32));
949 }
950 if (True(types & IR::Type::U32x2)) {
951 DefineConstBuffers(*this, info, &UniformDefinitions::U32x2, binding, U32[2], 'u',
952 sizeof(u32[2]));
953 }
954 binding += static_cast<u32>(info.constant_buffer_descriptors.size());
955}
956
957void EmitContext::DefineStorageBuffers(const Info& info, u32& binding) {
958 if (info.storage_buffers_descriptors.empty()) {
959 return;
960 }
961 AddExtension("SPV_KHR_storage_buffer_storage_class");
962
963 const IR::Type used_types{profile.support_descriptor_aliasing ? info.used_storage_buffer_types
964 : IR::Type::U32};
965 if (profile.support_int8 && True(used_types & IR::Type::U8)) {
966 DefineSsbos(*this, storage_types.U8, &StorageDefinitions::U8, info, binding, U8,
967 sizeof(u8));
968 DefineSsbos(*this, storage_types.S8, &StorageDefinitions::S8, info, binding, S8,
969 sizeof(u8));
970 }
971 if (profile.support_int16 && True(used_types & IR::Type::U16)) {
972 DefineSsbos(*this, storage_types.U16, &StorageDefinitions::U16, info, binding, U16,
973 sizeof(u16));
974 DefineSsbos(*this, storage_types.S16, &StorageDefinitions::S16, info, binding, S16,
975 sizeof(u16));
976 }
977 if (True(used_types & IR::Type::U32)) {
978 DefineSsbos(*this, storage_types.U32, &StorageDefinitions::U32, info, binding, U32[1],
979 sizeof(u32));
980 }
981 if (True(used_types & IR::Type::F32)) {
982 DefineSsbos(*this, storage_types.F32, &StorageDefinitions::F32, info, binding, F32[1],
983 sizeof(f32));
984 }
985 if (True(used_types & IR::Type::U64)) {
986 DefineSsbos(*this, storage_types.U64, &StorageDefinitions::U64, info, binding, U64,
987 sizeof(u64));
988 }
989 if (True(used_types & IR::Type::U32x2)) {
990 DefineSsbos(*this, storage_types.U32x2, &StorageDefinitions::U32x2, info, binding, U32[2],
991 sizeof(u32[2]));
992 }
993 if (True(used_types & IR::Type::U32x4)) {
994 DefineSsbos(*this, storage_types.U32x4, &StorageDefinitions::U32x4, info, binding, U32[4],
995 sizeof(u32[4]));
996 }
997 for (const StorageBufferDescriptor& desc : info.storage_buffers_descriptors) {
998 binding += desc.count;
999 }
1000 const bool needs_function{
1001 info.uses_global_increment || info.uses_global_decrement || info.uses_atomic_f32_add ||
1002 info.uses_atomic_f16x2_add || info.uses_atomic_f16x2_min || info.uses_atomic_f16x2_max ||
1003 info.uses_atomic_f32x2_add || info.uses_atomic_f32x2_min || info.uses_atomic_f32x2_max};
1004 if (needs_function) {
1005 AddCapability(spv::Capability::VariablePointersStorageBuffer);
1006 }
1007 if (info.uses_global_increment) {
1008 increment_cas_ssbo = CasLoop(*this, Operation::Increment, storage_types.U32.array,
1009 storage_types.U32.element, U32[1], U32[1], spv::Scope::Device);
1010 }
1011 if (info.uses_global_decrement) {
1012 decrement_cas_ssbo = CasLoop(*this, Operation::Decrement, storage_types.U32.array,
1013 storage_types.U32.element, U32[1], U32[1], spv::Scope::Device);
1014 }
1015 if (info.uses_atomic_f32_add) {
1016 f32_add_cas = CasLoop(*this, Operation::FPAdd, storage_types.U32.array,
1017 storage_types.U32.element, F32[1], U32[1], spv::Scope::Device);
1018 }
1019 if (info.uses_atomic_f16x2_add) {
1020 f16x2_add_cas = CasLoop(*this, Operation::FPAdd, storage_types.U32.array,
1021 storage_types.U32.element, F16[2], F16[2], spv::Scope::Device);
1022 }
1023 if (info.uses_atomic_f16x2_min) {
1024 f16x2_min_cas = CasLoop(*this, Operation::FPMin, storage_types.U32.array,
1025 storage_types.U32.element, F16[2], F16[2], spv::Scope::Device);
1026 }
1027 if (info.uses_atomic_f16x2_max) {
1028 f16x2_max_cas = CasLoop(*this, Operation::FPMax, storage_types.U32.array,
1029 storage_types.U32.element, F16[2], F16[2], spv::Scope::Device);
1030 }
1031 if (info.uses_atomic_f32x2_add) {
1032 f32x2_add_cas = CasLoop(*this, Operation::FPAdd, storage_types.U32.array,
1033 storage_types.U32.element, F32[2], F32[2], spv::Scope::Device);
1034 }
1035 if (info.uses_atomic_f32x2_min) {
1036 f32x2_min_cas = CasLoop(*this, Operation::FPMin, storage_types.U32.array,
1037 storage_types.U32.element, F32[2], F32[2], spv::Scope::Device);
1038 }
1039 if (info.uses_atomic_f32x2_max) {
1040 f32x2_max_cas = CasLoop(*this, Operation::FPMax, storage_types.U32.array,
1041 storage_types.U32.element, F32[2], F32[2], spv::Scope::Device);
1042 }
1043}
1044
1045void EmitContext::DefineTextureBuffers(const Info& info, u32& binding) {
1046 if (info.texture_buffer_descriptors.empty()) {
1047 return;
1048 }
1049 const spv::ImageFormat format{spv::ImageFormat::Unknown};
1050 image_buffer_type = TypeImage(F32[1], spv::Dim::Buffer, 0U, false, false, 1, format);
1051 sampled_texture_buffer_type = TypeSampledImage(image_buffer_type);
1052
1053 const Id type{TypePointer(spv::StorageClass::UniformConstant, sampled_texture_buffer_type)};
1054 texture_buffers.reserve(info.texture_buffer_descriptors.size());
1055 for (const TextureBufferDescriptor& desc : info.texture_buffer_descriptors) {
1056 if (desc.count != 1) {
1057 throw NotImplementedException("Array of texture buffers");
1058 }
1059 const Id id{AddGlobalVariable(type, spv::StorageClass::UniformConstant)};
1060 Decorate(id, spv::Decoration::Binding, binding);
1061 Decorate(id, spv::Decoration::DescriptorSet, 0U);
1062 Name(id, NameOf(stage, desc, "texbuf"));
1063 texture_buffers.push_back({
1064 .id = id,
1065 .count = desc.count,
1066 });
1067 if (profile.supported_spirv >= 0x00010400) {
1068 interfaces.push_back(id);
1069 }
1070 ++binding;
1071 }
1072}
1073
1074void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
1075 image_buffers.reserve(info.image_buffer_descriptors.size());
1076 for (const ImageBufferDescriptor& desc : info.image_buffer_descriptors) {
1077 if (desc.count != 1) {
1078 throw NotImplementedException("Array of image buffers");
1079 }
1080 const spv::ImageFormat format{GetImageFormat(desc.format)};
1081 const Id image_type{TypeImage(U32[1], spv::Dim::Buffer, false, false, false, 2, format)};
1082 const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)};
1083 const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)};
1084 Decorate(id, spv::Decoration::Binding, binding);
1085 Decorate(id, spv::Decoration::DescriptorSet, 0U);
1086 Name(id, NameOf(stage, desc, "imgbuf"));
1087 image_buffers.push_back({
1088 .id = id,
1089 .image_type = image_type,
1090 .count = desc.count,
1091 });
1092 if (profile.supported_spirv >= 0x00010400) {
1093 interfaces.push_back(id);
1094 }
1095 ++binding;
1096 }
1097}
1098
1099void EmitContext::DefineTextures(const Info& info, u32& binding) {
1100 textures.reserve(info.texture_descriptors.size());
1101 for (const TextureDescriptor& desc : info.texture_descriptors) {
1102 const Id image_type{ImageType(*this, desc)};
1103 const Id sampled_type{TypeSampledImage(image_type)};
1104 const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, sampled_type)};
1105 const Id desc_type{DescType(*this, sampled_type, pointer_type, desc.count)};
1106 const Id id{AddGlobalVariable(desc_type, spv::StorageClass::UniformConstant)};
1107 Decorate(id, spv::Decoration::Binding, binding);
1108 Decorate(id, spv::Decoration::DescriptorSet, 0U);
1109 Name(id, NameOf(stage, desc, "tex"));
1110 textures.push_back({
1111 .id = id,
1112 .sampled_type = sampled_type,
1113 .pointer_type = pointer_type,
1114 .image_type = image_type,
1115 .count = desc.count,
1116 });
1117 if (profile.supported_spirv >= 0x00010400) {
1118 interfaces.push_back(id);
1119 }
1120 ++binding;
1121 }
1122 if (info.uses_atomic_image_u32) {
1123 image_u32 = TypePointer(spv::StorageClass::Image, U32[1]);
1124 }
1125}
1126
1127void EmitContext::DefineImages(const Info& info, u32& binding) {
1128 images.reserve(info.image_descriptors.size());
1129 for (const ImageDescriptor& desc : info.image_descriptors) {
1130 if (desc.count != 1) {
1131 throw NotImplementedException("Array of images");
1132 }
1133 const Id image_type{ImageType(*this, desc)};
1134 const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)};
1135 const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)};
1136 Decorate(id, spv::Decoration::Binding, binding);
1137 Decorate(id, spv::Decoration::DescriptorSet, 0U);
1138 Name(id, NameOf(stage, desc, "img"));
1139 images.push_back({
1140 .id = id,
1141 .image_type = image_type,
1142 .count = desc.count,
1143 });
1144 if (profile.supported_spirv >= 0x00010400) {
1145 interfaces.push_back(id);
1146 }
1147 ++binding;
1148 }
1149}
1150
1151void EmitContext::DefineInputs(const IR::Program& program) {
1152 const Info& info{program.info};
1153 const VaryingState loads{info.loads.mask | info.passthrough.mask};
1154
1155 if (info.uses_workgroup_id) {
1156 workgroup_id = DefineInput(*this, U32[3], false, spv::BuiltIn::WorkgroupId);
1157 }
1158 if (info.uses_local_invocation_id) {
1159 local_invocation_id = DefineInput(*this, U32[3], false, spv::BuiltIn::LocalInvocationId);
1160 }
1161 if (info.uses_invocation_id) {
1162 invocation_id = DefineInput(*this, U32[1], false, spv::BuiltIn::InvocationId);
1163 }
1164 if (info.uses_sample_id) {
1165 sample_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SampleId);
1166 }
1167 if (info.uses_is_helper_invocation) {
1168 is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation);
1169 }
1170 if (info.uses_subgroup_mask) {
1171 subgroup_mask_eq = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupEqMaskKHR);
1172 subgroup_mask_lt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLtMaskKHR);
1173 subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR);
1174 subgroup_mask_gt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGtMaskKHR);
1175 subgroup_mask_ge = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGeMaskKHR);
1176 }
1177 if (info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles ||
1178 (profile.warp_size_potentially_larger_than_guest &&
1179 (info.uses_subgroup_vote || info.uses_subgroup_mask))) {
1180 subgroup_local_invocation_id =
1181 DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId);
1182 }
1183 if (info.uses_fswzadd) {
1184 const Id f32_one{Const(1.0f)};
1185 const Id f32_minus_one{Const(-1.0f)};
1186 const Id f32_zero{Const(0.0f)};
1187 fswzadd_lut_a = ConstantComposite(F32[4], f32_minus_one, f32_one, f32_minus_one, f32_zero);
1188 fswzadd_lut_b =
1189 ConstantComposite(F32[4], f32_minus_one, f32_minus_one, f32_one, f32_minus_one);
1190 }
1191 if (loads[IR::Attribute::PrimitiveId]) {
1192 primitive_id = DefineInput(*this, U32[1], false, spv::BuiltIn::PrimitiveId);
1193 }
1194 if (loads.AnyComponent(IR::Attribute::PositionX)) {
1195 const bool is_fragment{stage != Stage::Fragment};
1196 const spv::BuiltIn built_in{is_fragment ? spv::BuiltIn::Position : spv::BuiltIn::FragCoord};
1197 input_position = DefineInput(*this, F32[4], true, built_in);
1198 if (profile.support_geometry_shader_passthrough) {
1199 if (info.passthrough.AnyComponent(IR::Attribute::PositionX)) {
1200 Decorate(input_position, spv::Decoration::PassthroughNV);
1201 }
1202 }
1203 }
1204 if (loads[IR::Attribute::InstanceId]) {
1205 if (profile.support_vertex_instance_id) {
1206 instance_id = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceId);
1207 } else {
1208 instance_index = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceIndex);
1209 base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseInstance);
1210 }
1211 }
1212 if (loads[IR::Attribute::VertexId]) {
1213 if (profile.support_vertex_instance_id) {
1214 vertex_id = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexId);
1215 } else {
1216 vertex_index = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexIndex);
1217 base_vertex = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex);
1218 }
1219 }
1220 if (loads[IR::Attribute::FrontFace]) {
1221 front_face = DefineInput(*this, U1, true, spv::BuiltIn::FrontFacing);
1222 }
1223 if (loads[IR::Attribute::PointSpriteS] || loads[IR::Attribute::PointSpriteT]) {
1224 point_coord = DefineInput(*this, F32[2], true, spv::BuiltIn::PointCoord);
1225 }
1226 if (loads[IR::Attribute::TessellationEvaluationPointU] ||
1227 loads[IR::Attribute::TessellationEvaluationPointV]) {
1228 tess_coord = DefineInput(*this, F32[3], false, spv::BuiltIn::TessCoord);
1229 }
1230 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
1231 const AttributeType input_type{runtime_info.generic_input_types[index]};
1232 if (!runtime_info.previous_stage_stores.Generic(index)) {
1233 continue;
1234 }
1235 if (!loads.Generic(index)) {
1236 continue;
1237 }
1238 if (input_type == AttributeType::Disabled) {
1239 continue;
1240 }
1241 const Id type{GetAttributeType(*this, input_type)};
1242 const Id id{DefineInput(*this, type, true)};
1243 Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
1244 Name(id, fmt::format("in_attr{}", index));
1245 input_generics[index] = id;
1246
1247 if (info.passthrough.Generic(index) && profile.support_geometry_shader_passthrough) {
1248 Decorate(id, spv::Decoration::PassthroughNV);
1249 }
1250 if (stage != Stage::Fragment) {
1251 continue;
1252 }
1253 switch (info.interpolation[index]) {
1254 case Interpolation::Smooth:
1255 // Default
1256 // Decorate(id, spv::Decoration::Smooth);
1257 break;
1258 case Interpolation::NoPerspective:
1259 Decorate(id, spv::Decoration::NoPerspective);
1260 break;
1261 case Interpolation::Flat:
1262 Decorate(id, spv::Decoration::Flat);
1263 break;
1264 }
1265 }
1266 if (stage == Stage::TessellationEval) {
1267 for (size_t index = 0; index < info.uses_patches.size(); ++index) {
1268 if (!info.uses_patches[index]) {
1269 continue;
1270 }
1271 const Id id{DefineInput(*this, F32[4], false)};
1272 Decorate(id, spv::Decoration::Patch);
1273 Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
1274 patches[index] = id;
1275 }
1276 }
1277}
1278
1279void EmitContext::DefineOutputs(const IR::Program& program) {
1280 const Info& info{program.info};
1281 const std::optional<u32> invocations{program.invocations};
1282 if (info.stores.AnyComponent(IR::Attribute::PositionX) || stage == Stage::VertexB) {
1283 output_position = DefineOutput(*this, F32[4], invocations, spv::BuiltIn::Position);
1284 }
1285 if (info.stores[IR::Attribute::PointSize] || runtime_info.fixed_state_point_size) {
1286 if (stage == Stage::Fragment) {
1287 throw NotImplementedException("Storing PointSize in fragment stage");
1288 }
1289 output_point_size = DefineOutput(*this, F32[1], invocations, spv::BuiltIn::PointSize);
1290 }
1291 if (info.stores.ClipDistances()) {
1292 if (stage == Stage::Fragment) {
1293 throw NotImplementedException("Storing ClipDistance in fragment stage");
1294 }
1295 const Id type{TypeArray(F32[1], Const(8U))};
1296 clip_distances = DefineOutput(*this, type, invocations, spv::BuiltIn::ClipDistance);
1297 }
1298 if (info.stores[IR::Attribute::Layer] &&
1299 (profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) {
1300 if (stage == Stage::Fragment) {
1301 throw NotImplementedException("Storing Layer in fragment stage");
1302 }
1303 layer = DefineOutput(*this, U32[1], invocations, spv::BuiltIn::Layer);
1304 }
1305 if (info.stores[IR::Attribute::ViewportIndex] &&
1306 (profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) {
1307 if (stage == Stage::Fragment) {
1308 throw NotImplementedException("Storing ViewportIndex in fragment stage");
1309 }
1310 viewport_index = DefineOutput(*this, U32[1], invocations, spv::BuiltIn::ViewportIndex);
1311 }
1312 if (info.stores[IR::Attribute::ViewportMask] && profile.support_viewport_mask) {
1313 viewport_mask = DefineOutput(*this, TypeArray(U32[1], Const(1u)), std::nullopt,
1314 spv::BuiltIn::ViewportMaskNV);
1315 }
1316 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
1317 if (info.stores.Generic(index)) {
1318 DefineGenericOutput(*this, index, invocations);
1319 }
1320 }
1321 switch (stage) {
1322 case Stage::TessellationControl:
1323 if (info.stores_tess_level_outer) {
1324 const Id type{TypeArray(F32[1], Const(4U))};
1325 output_tess_level_outer =
1326 DefineOutput(*this, type, std::nullopt, spv::BuiltIn::TessLevelOuter);
1327 Decorate(output_tess_level_outer, spv::Decoration::Patch);
1328 }
1329 if (info.stores_tess_level_inner) {
1330 const Id type{TypeArray(F32[1], Const(2U))};
1331 output_tess_level_inner =
1332 DefineOutput(*this, type, std::nullopt, spv::BuiltIn::TessLevelInner);
1333 Decorate(output_tess_level_inner, spv::Decoration::Patch);
1334 }
1335 for (size_t index = 0; index < info.uses_patches.size(); ++index) {
1336 if (!info.uses_patches[index]) {
1337 continue;
1338 }
1339 const Id id{DefineOutput(*this, F32[4], std::nullopt)};
1340 Decorate(id, spv::Decoration::Patch);
1341 Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
1342 patches[index] = id;
1343 }
1344 break;
1345 case Stage::Fragment:
1346 for (u32 index = 0; index < 8; ++index) {
1347 if (!info.stores_frag_color[index] && !profile.need_declared_frag_colors) {
1348 continue;
1349 }
1350 frag_color[index] = DefineOutput(*this, F32[4], std::nullopt);
1351 Decorate(frag_color[index], spv::Decoration::Location, index);
1352 Name(frag_color[index], fmt::format("frag_color{}", index));
1353 }
1354 if (info.stores_frag_depth) {
1355 frag_depth = DefineOutput(*this, F32[1], std::nullopt);
1356 Decorate(frag_depth, spv::Decoration::BuiltIn, spv::BuiltIn::FragDepth);
1357 }
1358 if (info.stores_sample_mask) {
1359 sample_mask = DefineOutput(*this, U32[1], std::nullopt);
1360 Decorate(sample_mask, spv::Decoration::BuiltIn, spv::BuiltIn::SampleMask);
1361 }
1362 break;
1363 default:
1364 break;
1365 }
1366}
1367
1368} // 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..e277bc358
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_context.h
@@ -0,0 +1,307 @@
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/backend/bindings.h"
13#include "shader_recompiler/frontend/ir/program.h"
14#include "shader_recompiler/profile.h"
15#include "shader_recompiler/runtime_info.h"
16#include "shader_recompiler/shader_info.h"
17
18namespace Shader::Backend::SPIRV {
19
20using Sirit::Id;
21
22class VectorTypes {
23public:
24 void Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name);
25
26 [[nodiscard]] Id operator[](size_t size) const noexcept {
27 return defs[size - 1];
28 }
29
30private:
31 std::array<Id, 4> defs{};
32};
33
34struct TextureDefinition {
35 Id id;
36 Id sampled_type;
37 Id pointer_type;
38 Id image_type;
39 u32 count;
40};
41
42struct TextureBufferDefinition {
43 Id id;
44 u32 count;
45};
46
47struct ImageBufferDefinition {
48 Id id;
49 Id image_type;
50 u32 count;
51};
52
53struct ImageDefinition {
54 Id id;
55 Id image_type;
56 u32 count;
57};
58
59struct UniformDefinitions {
60 Id U8{};
61 Id S8{};
62 Id U16{};
63 Id S16{};
64 Id U32{};
65 Id F32{};
66 Id U32x2{};
67 Id U32x4{};
68};
69
70struct StorageTypeDefinition {
71 Id array{};
72 Id element{};
73};
74
75struct StorageTypeDefinitions {
76 StorageTypeDefinition U8{};
77 StorageTypeDefinition S8{};
78 StorageTypeDefinition U16{};
79 StorageTypeDefinition S16{};
80 StorageTypeDefinition U32{};
81 StorageTypeDefinition U64{};
82 StorageTypeDefinition F32{};
83 StorageTypeDefinition U32x2{};
84 StorageTypeDefinition U32x4{};
85};
86
87struct StorageDefinitions {
88 Id U8{};
89 Id S8{};
90 Id U16{};
91 Id S16{};
92 Id U32{};
93 Id F32{};
94 Id U64{};
95 Id U32x2{};
96 Id U32x4{};
97};
98
99struct GenericElementInfo {
100 Id id{};
101 u32 first_element{};
102 u32 num_components{};
103};
104
105class EmitContext final : public Sirit::Module {
106public:
107 explicit EmitContext(const Profile& profile, const RuntimeInfo& runtime_info,
108 IR::Program& program, Bindings& binding);
109 ~EmitContext();
110
111 [[nodiscard]] Id Def(const IR::Value& value);
112
113 [[nodiscard]] Id BitOffset8(const IR::Value& offset);
114 [[nodiscard]] Id BitOffset16(const IR::Value& offset);
115
116 Id Const(u32 value) {
117 return Constant(U32[1], value);
118 }
119
120 Id Const(u32 element_1, u32 element_2) {
121 return ConstantComposite(U32[2], Const(element_1), Const(element_2));
122 }
123
124 Id Const(u32 element_1, u32 element_2, u32 element_3) {
125 return ConstantComposite(U32[3], Const(element_1), Const(element_2), Const(element_3));
126 }
127
128 Id Const(u32 element_1, u32 element_2, u32 element_3, u32 element_4) {
129 return ConstantComposite(U32[4], Const(element_1), Const(element_2), Const(element_3),
130 Const(element_4));
131 }
132
133 Id SConst(s32 value) {
134 return Constant(S32[1], value);
135 }
136
137 Id SConst(s32 element_1, s32 element_2) {
138 return ConstantComposite(S32[2], SConst(element_1), SConst(element_2));
139 }
140
141 Id SConst(s32 element_1, s32 element_2, s32 element_3) {
142 return ConstantComposite(S32[3], SConst(element_1), SConst(element_2), SConst(element_3));
143 }
144
145 Id SConst(s32 element_1, s32 element_2, s32 element_3, s32 element_4) {
146 return ConstantComposite(S32[4], SConst(element_1), SConst(element_2), SConst(element_3),
147 SConst(element_4));
148 }
149
150 Id Const(f32 value) {
151 return Constant(F32[1], value);
152 }
153
154 const Profile& profile;
155 const RuntimeInfo& runtime_info;
156 Stage stage{};
157
158 Id void_id{};
159 Id U1{};
160 Id U8{};
161 Id S8{};
162 Id U16{};
163 Id S16{};
164 Id U64{};
165 VectorTypes F32;
166 VectorTypes U32;
167 VectorTypes S32;
168 VectorTypes F16;
169 VectorTypes F64;
170
171 Id true_value{};
172 Id false_value{};
173 Id u32_zero_value{};
174 Id f32_zero_value{};
175
176 UniformDefinitions uniform_types;
177 StorageTypeDefinitions storage_types;
178
179 Id private_u32{};
180
181 Id shared_u8{};
182 Id shared_u16{};
183 Id shared_u32{};
184 Id shared_u64{};
185 Id shared_u32x2{};
186 Id shared_u32x4{};
187
188 Id input_f32{};
189 Id input_u32{};
190 Id input_s32{};
191
192 Id output_f32{};
193 Id output_u32{};
194
195 Id image_buffer_type{};
196 Id sampled_texture_buffer_type{};
197 Id image_u32{};
198
199 std::array<UniformDefinitions, Info::MAX_CBUFS> cbufs{};
200 std::array<StorageDefinitions, Info::MAX_SSBOS> ssbos{};
201 std::vector<TextureBufferDefinition> texture_buffers;
202 std::vector<ImageBufferDefinition> image_buffers;
203 std::vector<TextureDefinition> textures;
204 std::vector<ImageDefinition> images;
205
206 Id workgroup_id{};
207 Id local_invocation_id{};
208 Id invocation_id{};
209 Id sample_id{};
210 Id is_helper_invocation{};
211 Id subgroup_local_invocation_id{};
212 Id subgroup_mask_eq{};
213 Id subgroup_mask_lt{};
214 Id subgroup_mask_le{};
215 Id subgroup_mask_gt{};
216 Id subgroup_mask_ge{};
217 Id instance_id{};
218 Id instance_index{};
219 Id base_instance{};
220 Id vertex_id{};
221 Id vertex_index{};
222 Id base_vertex{};
223 Id front_face{};
224 Id point_coord{};
225 Id tess_coord{};
226 Id clip_distances{};
227 Id layer{};
228 Id viewport_index{};
229 Id viewport_mask{};
230 Id primitive_id{};
231
232 Id fswzadd_lut_a{};
233 Id fswzadd_lut_b{};
234
235 Id indexed_load_func{};
236 Id indexed_store_func{};
237
238 Id local_memory{};
239
240 Id shared_memory_u8{};
241 Id shared_memory_u16{};
242 Id shared_memory_u32{};
243 Id shared_memory_u64{};
244 Id shared_memory_u32x2{};
245 Id shared_memory_u32x4{};
246
247 Id shared_memory_u32_type{};
248
249 Id shared_store_u8_func{};
250 Id shared_store_u16_func{};
251 Id increment_cas_shared{};
252 Id increment_cas_ssbo{};
253 Id decrement_cas_shared{};
254 Id decrement_cas_ssbo{};
255 Id f32_add_cas{};
256 Id f16x2_add_cas{};
257 Id f16x2_min_cas{};
258 Id f16x2_max_cas{};
259 Id f32x2_add_cas{};
260 Id f32x2_min_cas{};
261 Id f32x2_max_cas{};
262
263 Id load_global_func_u32{};
264 Id load_global_func_u32x2{};
265 Id load_global_func_u32x4{};
266 Id write_global_func_u32{};
267 Id write_global_func_u32x2{};
268 Id write_global_func_u32x4{};
269
270 Id input_position{};
271 std::array<Id, 32> input_generics{};
272
273 Id output_point_size{};
274 Id output_position{};
275 std::array<std::array<GenericElementInfo, 4>, 32> output_generics{};
276
277 Id output_tess_level_outer{};
278 Id output_tess_level_inner{};
279 std::array<Id, 30> patches{};
280
281 std::array<Id, 8> frag_color{};
282 Id sample_mask{};
283 Id frag_depth{};
284
285 std::vector<Id> interfaces;
286
287private:
288 void DefineCommonTypes(const Info& info);
289 void DefineCommonConstants();
290 void DefineInterfaces(const IR::Program& program);
291 void DefineLocalMemory(const IR::Program& program);
292 void DefineSharedMemory(const IR::Program& program);
293 void DefineSharedMemoryFunctions(const IR::Program& program);
294 void DefineConstantBuffers(const Info& info, u32& binding);
295 void DefineStorageBuffers(const Info& info, u32& binding);
296 void DefineTextureBuffers(const Info& info, u32& binding);
297 void DefineImageBuffers(const Info& info, u32& binding);
298 void DefineTextures(const Info& info, u32& binding);
299 void DefineImages(const Info& info, u32& binding);
300 void DefineAttributeMemAccess(const Info& info);
301 void DefineGlobalMemoryFunctions(const Info& info);
302
303 void DefineInputs(const IR::Program& program);
304 void DefineOutputs(const IR::Program& program);
305};
306
307} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
new file mode 100644
index 000000000..d7a86e270
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
@@ -0,0 +1,541 @@
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 <span>
6#include <tuple>
7#include <type_traits>
8#include <utility>
9#include <vector>
10
11#include "common/settings.h"
12#include "shader_recompiler/backend/spirv/emit_spirv.h"
13#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
14#include "shader_recompiler/frontend/ir/basic_block.h"
15#include "shader_recompiler/frontend/ir/program.h"
16
17namespace Shader::Backend::SPIRV {
18namespace {
19template <class Func>
20struct FuncTraits {};
21
22template <class ReturnType_, class... Args>
23struct FuncTraits<ReturnType_ (*)(Args...)> {
24 using ReturnType = ReturnType_;
25
26 static constexpr size_t NUM_ARGS = sizeof...(Args);
27
28 template <size_t I>
29 using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
30};
31
32template <auto func, typename... Args>
33void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) {
34 inst->SetDefinition<Id>(func(ctx, std::forward<Args>(args)...));
35}
36
37template <typename ArgType>
38ArgType Arg(EmitContext& ctx, const IR::Value& arg) {
39 if constexpr (std::is_same_v<ArgType, Id>) {
40 return ctx.Def(arg);
41 } else if constexpr (std::is_same_v<ArgType, const IR::Value&>) {
42 return arg;
43 } else if constexpr (std::is_same_v<ArgType, u32>) {
44 return arg.U32();
45 } else if constexpr (std::is_same_v<ArgType, IR::Attribute>) {
46 return arg.Attribute();
47 } else if constexpr (std::is_same_v<ArgType, IR::Patch>) {
48 return arg.Patch();
49 } else if constexpr (std::is_same_v<ArgType, IR::Reg>) {
50 return arg.Reg();
51 }
52}
53
54template <auto func, bool is_first_arg_inst, size_t... I>
55void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
56 using Traits = FuncTraits<decltype(func)>;
57 if constexpr (std::is_same_v<typename Traits::ReturnType, Id>) {
58 if constexpr (is_first_arg_inst) {
59 SetDefinition<func>(
60 ctx, inst, inst,
61 Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
62 } else {
63 SetDefinition<func>(
64 ctx, inst, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
65 }
66 } else {
67 if constexpr (is_first_arg_inst) {
68 func(ctx, inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
69 } else {
70 func(ctx, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
71 }
72 }
73}
74
75template <auto func>
76void Invoke(EmitContext& ctx, IR::Inst* inst) {
77 using Traits = FuncTraits<decltype(func)>;
78 static_assert(Traits::NUM_ARGS >= 1, "Insufficient arguments");
79 if constexpr (Traits::NUM_ARGS == 1) {
80 Invoke<func, false>(ctx, inst, std::make_index_sequence<0>{});
81 } else {
82 using FirstArgType = typename Traits::template ArgType<1>;
83 static constexpr bool is_first_arg_inst = std::is_same_v<FirstArgType, IR::Inst*>;
84 using Indices = std::make_index_sequence<Traits::NUM_ARGS - (is_first_arg_inst ? 2 : 1)>;
85 Invoke<func, is_first_arg_inst>(ctx, inst, Indices{});
86 }
87}
88
89void EmitInst(EmitContext& ctx, IR::Inst* inst) {
90 switch (inst->GetOpcode()) {
91#define OPCODE(name, result_type, ...) \
92 case IR::Opcode::name: \
93 return Invoke<&Emit##name>(ctx, inst);
94#include "shader_recompiler/frontend/ir/opcodes.inc"
95#undef OPCODE
96 }
97 throw LogicError("Invalid opcode {}", inst->GetOpcode());
98}
99
100Id TypeId(const EmitContext& ctx, IR::Type type) {
101 switch (type) {
102 case IR::Type::U1:
103 return ctx.U1;
104 case IR::Type::U32:
105 return ctx.U32[1];
106 default:
107 throw NotImplementedException("Phi node type {}", type);
108 }
109}
110
111void Traverse(EmitContext& ctx, IR::Program& program) {
112 IR::Block* current_block{};
113 for (const IR::AbstractSyntaxNode& node : program.syntax_list) {
114 switch (node.type) {
115 case IR::AbstractSyntaxNode::Type::Block: {
116 const Id label{node.data.block->Definition<Id>()};
117 if (current_block) {
118 ctx.OpBranch(label);
119 }
120 current_block = node.data.block;
121 ctx.AddLabel(label);
122 for (IR::Inst& inst : node.data.block->Instructions()) {
123 EmitInst(ctx, &inst);
124 }
125 break;
126 }
127 case IR::AbstractSyntaxNode::Type::If: {
128 const Id if_label{node.data.if_node.body->Definition<Id>()};
129 const Id endif_label{node.data.if_node.merge->Definition<Id>()};
130 ctx.OpSelectionMerge(endif_label, spv::SelectionControlMask::MaskNone);
131 ctx.OpBranchConditional(ctx.Def(node.data.if_node.cond), if_label, endif_label);
132 break;
133 }
134 case IR::AbstractSyntaxNode::Type::Loop: {
135 const Id body_label{node.data.loop.body->Definition<Id>()};
136 const Id continue_label{node.data.loop.continue_block->Definition<Id>()};
137 const Id endloop_label{node.data.loop.merge->Definition<Id>()};
138
139 ctx.OpLoopMerge(endloop_label, continue_label, spv::LoopControlMask::MaskNone);
140 ctx.OpBranch(body_label);
141 break;
142 }
143 case IR::AbstractSyntaxNode::Type::Break: {
144 const Id break_label{node.data.break_node.merge->Definition<Id>()};
145 const Id skip_label{node.data.break_node.skip->Definition<Id>()};
146 ctx.OpBranchConditional(ctx.Def(node.data.break_node.cond), break_label, skip_label);
147 break;
148 }
149 case IR::AbstractSyntaxNode::Type::EndIf:
150 if (current_block) {
151 ctx.OpBranch(node.data.end_if.merge->Definition<Id>());
152 }
153 break;
154 case IR::AbstractSyntaxNode::Type::Repeat: {
155 Id cond{ctx.Def(node.data.repeat.cond)};
156 if (!Settings::values.disable_shader_loop_safety_checks) {
157 const Id pointer_type{ctx.TypePointer(spv::StorageClass::Private, ctx.U32[1])};
158 const Id safety_counter{ctx.AddGlobalVariable(
159 pointer_type, spv::StorageClass::Private, ctx.Const(0x2000u))};
160 if (ctx.profile.supported_spirv >= 0x00010400) {
161 ctx.interfaces.push_back(safety_counter);
162 }
163 const Id old_counter{ctx.OpLoad(ctx.U32[1], safety_counter)};
164 const Id new_counter{ctx.OpISub(ctx.U32[1], old_counter, ctx.Const(1u))};
165 ctx.OpStore(safety_counter, new_counter);
166
167 const Id safety_cond{
168 ctx.OpSGreaterThanEqual(ctx.U1, new_counter, ctx.u32_zero_value)};
169 cond = ctx.OpLogicalAnd(ctx.U1, cond, safety_cond);
170 }
171 const Id loop_header_label{node.data.repeat.loop_header->Definition<Id>()};
172 const Id merge_label{node.data.repeat.merge->Definition<Id>()};
173 ctx.OpBranchConditional(cond, loop_header_label, merge_label);
174 break;
175 }
176 case IR::AbstractSyntaxNode::Type::Return:
177 ctx.OpReturn();
178 break;
179 case IR::AbstractSyntaxNode::Type::Unreachable:
180 ctx.OpUnreachable();
181 break;
182 }
183 if (node.type != IR::AbstractSyntaxNode::Type::Block) {
184 current_block = nullptr;
185 }
186 }
187}
188
189Id DefineMain(EmitContext& ctx, IR::Program& program) {
190 const Id void_function{ctx.TypeFunction(ctx.void_id)};
191 const Id main{ctx.OpFunction(ctx.void_id, spv::FunctionControlMask::MaskNone, void_function)};
192 for (IR::Block* const block : program.blocks) {
193 block->SetDefinition(ctx.OpLabel());
194 }
195 Traverse(ctx, program);
196 ctx.OpFunctionEnd();
197 return main;
198}
199
200spv::ExecutionMode ExecutionMode(TessPrimitive primitive) {
201 switch (primitive) {
202 case TessPrimitive::Isolines:
203 return spv::ExecutionMode::Isolines;
204 case TessPrimitive::Triangles:
205 return spv::ExecutionMode::Triangles;
206 case TessPrimitive::Quads:
207 return spv::ExecutionMode::Quads;
208 }
209 throw InvalidArgument("Tessellation primitive {}", primitive);
210}
211
212spv::ExecutionMode ExecutionMode(TessSpacing spacing) {
213 switch (spacing) {
214 case TessSpacing::Equal:
215 return spv::ExecutionMode::SpacingEqual;
216 case TessSpacing::FractionalOdd:
217 return spv::ExecutionMode::SpacingFractionalOdd;
218 case TessSpacing::FractionalEven:
219 return spv::ExecutionMode::SpacingFractionalEven;
220 }
221 throw InvalidArgument("Tessellation spacing {}", spacing);
222}
223
224void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
225 const std::span interfaces(ctx.interfaces.data(), ctx.interfaces.size());
226 spv::ExecutionModel execution_model{};
227 switch (program.stage) {
228 case Stage::Compute: {
229 const std::array<u32, 3> workgroup_size{program.workgroup_size};
230 execution_model = spv::ExecutionModel::GLCompute;
231 ctx.AddExecutionMode(main, spv::ExecutionMode::LocalSize, workgroup_size[0],
232 workgroup_size[1], workgroup_size[2]);
233 break;
234 }
235 case Stage::VertexB:
236 execution_model = spv::ExecutionModel::Vertex;
237 break;
238 case Stage::TessellationControl:
239 execution_model = spv::ExecutionModel::TessellationControl;
240 ctx.AddCapability(spv::Capability::Tessellation);
241 ctx.AddExecutionMode(main, spv::ExecutionMode::OutputVertices, program.invocations);
242 break;
243 case Stage::TessellationEval:
244 execution_model = spv::ExecutionModel::TessellationEvaluation;
245 ctx.AddCapability(spv::Capability::Tessellation);
246 ctx.AddExecutionMode(main, ExecutionMode(ctx.runtime_info.tess_primitive));
247 ctx.AddExecutionMode(main, ExecutionMode(ctx.runtime_info.tess_spacing));
248 ctx.AddExecutionMode(main, ctx.runtime_info.tess_clockwise
249 ? spv::ExecutionMode::VertexOrderCw
250 : spv::ExecutionMode::VertexOrderCcw);
251 break;
252 case Stage::Geometry:
253 execution_model = spv::ExecutionModel::Geometry;
254 ctx.AddCapability(spv::Capability::Geometry);
255 ctx.AddCapability(spv::Capability::GeometryStreams);
256 switch (ctx.runtime_info.input_topology) {
257 case InputTopology::Points:
258 ctx.AddExecutionMode(main, spv::ExecutionMode::InputPoints);
259 break;
260 case InputTopology::Lines:
261 ctx.AddExecutionMode(main, spv::ExecutionMode::InputLines);
262 break;
263 case InputTopology::LinesAdjacency:
264 ctx.AddExecutionMode(main, spv::ExecutionMode::InputLinesAdjacency);
265 break;
266 case InputTopology::Triangles:
267 ctx.AddExecutionMode(main, spv::ExecutionMode::Triangles);
268 break;
269 case InputTopology::TrianglesAdjacency:
270 ctx.AddExecutionMode(main, spv::ExecutionMode::InputTrianglesAdjacency);
271 break;
272 }
273 switch (program.output_topology) {
274 case OutputTopology::PointList:
275 ctx.AddExecutionMode(main, spv::ExecutionMode::OutputPoints);
276 break;
277 case OutputTopology::LineStrip:
278 ctx.AddExecutionMode(main, spv::ExecutionMode::OutputLineStrip);
279 break;
280 case OutputTopology::TriangleStrip:
281 ctx.AddExecutionMode(main, spv::ExecutionMode::OutputTriangleStrip);
282 break;
283 }
284 if (program.info.stores[IR::Attribute::PointSize]) {
285 ctx.AddCapability(spv::Capability::GeometryPointSize);
286 }
287 ctx.AddExecutionMode(main, spv::ExecutionMode::OutputVertices, program.output_vertices);
288 ctx.AddExecutionMode(main, spv::ExecutionMode::Invocations, program.invocations);
289 if (program.is_geometry_passthrough) {
290 if (ctx.profile.support_geometry_shader_passthrough) {
291 ctx.AddExtension("SPV_NV_geometry_shader_passthrough");
292 ctx.AddCapability(spv::Capability::GeometryShaderPassthroughNV);
293 } else {
294 LOG_WARNING(Shader_SPIRV, "Geometry shader passthrough used with no support");
295 }
296 }
297 break;
298 case Stage::Fragment:
299 execution_model = spv::ExecutionModel::Fragment;
300 if (ctx.profile.lower_left_origin_mode) {
301 ctx.AddExecutionMode(main, spv::ExecutionMode::OriginLowerLeft);
302 } else {
303 ctx.AddExecutionMode(main, spv::ExecutionMode::OriginUpperLeft);
304 }
305 if (program.info.stores_frag_depth) {
306 ctx.AddExecutionMode(main, spv::ExecutionMode::DepthReplacing);
307 }
308 if (ctx.runtime_info.force_early_z) {
309 ctx.AddExecutionMode(main, spv::ExecutionMode::EarlyFragmentTests);
310 }
311 break;
312 default:
313 throw NotImplementedException("Stage {}", program.stage);
314 }
315 ctx.AddEntryPoint(execution_model, main, "main", interfaces);
316}
317
318void SetupDenormControl(const Profile& profile, const IR::Program& program, EmitContext& ctx,
319 Id main_func) {
320 const Info& info{program.info};
321 if (info.uses_fp32_denorms_flush && info.uses_fp32_denorms_preserve) {
322 LOG_DEBUG(Shader_SPIRV, "Fp32 denorm flush and preserve on the same shader");
323 } else if (info.uses_fp32_denorms_flush) {
324 if (profile.support_fp32_denorm_flush) {
325 ctx.AddCapability(spv::Capability::DenormFlushToZero);
326 ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormFlushToZero, 32U);
327 } else {
328 // Drivers will most likely flush denorms by default, no need to warn
329 }
330 } else if (info.uses_fp32_denorms_preserve) {
331 if (profile.support_fp32_denorm_preserve) {
332 ctx.AddCapability(spv::Capability::DenormPreserve);
333 ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormPreserve, 32U);
334 } else {
335 LOG_DEBUG(Shader_SPIRV, "Fp32 denorm preserve used in shader without host support");
336 }
337 }
338 if (!profile.support_separate_denorm_behavior || profile.has_broken_fp16_float_controls) {
339 // No separate denorm behavior
340 return;
341 }
342 if (info.uses_fp16_denorms_flush && info.uses_fp16_denorms_preserve) {
343 LOG_DEBUG(Shader_SPIRV, "Fp16 denorm flush and preserve on the same shader");
344 } else if (info.uses_fp16_denorms_flush) {
345 if (profile.support_fp16_denorm_flush) {
346 ctx.AddCapability(spv::Capability::DenormFlushToZero);
347 ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormFlushToZero, 16U);
348 } else {
349 // Same as fp32, no need to warn as most drivers will flush by default
350 }
351 } else if (info.uses_fp16_denorms_preserve) {
352 if (profile.support_fp16_denorm_preserve) {
353 ctx.AddCapability(spv::Capability::DenormPreserve);
354 ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormPreserve, 16U);
355 } else {
356 LOG_DEBUG(Shader_SPIRV, "Fp16 denorm preserve used in shader without host support");
357 }
358 }
359}
360
361void SetupSignedNanCapabilities(const Profile& profile, const IR::Program& program,
362 EmitContext& ctx, Id main_func) {
363 if (profile.has_broken_fp16_float_controls && program.info.uses_fp16) {
364 return;
365 }
366 if (program.info.uses_fp16 && profile.support_fp16_signed_zero_nan_preserve) {
367 ctx.AddCapability(spv::Capability::SignedZeroInfNanPreserve);
368 ctx.AddExecutionMode(main_func, spv::ExecutionMode::SignedZeroInfNanPreserve, 16U);
369 }
370 if (profile.support_fp32_signed_zero_nan_preserve) {
371 ctx.AddCapability(spv::Capability::SignedZeroInfNanPreserve);
372 ctx.AddExecutionMode(main_func, spv::ExecutionMode::SignedZeroInfNanPreserve, 32U);
373 }
374 if (program.info.uses_fp64 && profile.support_fp64_signed_zero_nan_preserve) {
375 ctx.AddCapability(spv::Capability::SignedZeroInfNanPreserve);
376 ctx.AddExecutionMode(main_func, spv::ExecutionMode::SignedZeroInfNanPreserve, 64U);
377 }
378}
379
380void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ctx) {
381 if (info.uses_sampled_1d) {
382 ctx.AddCapability(spv::Capability::Sampled1D);
383 }
384 if (info.uses_sparse_residency) {
385 ctx.AddCapability(spv::Capability::SparseResidency);
386 }
387 if (info.uses_demote_to_helper_invocation && profile.support_demote_to_helper_invocation) {
388 ctx.AddExtension("SPV_EXT_demote_to_helper_invocation");
389 ctx.AddCapability(spv::Capability::DemoteToHelperInvocationEXT);
390 }
391 if (info.stores[IR::Attribute::ViewportIndex]) {
392 ctx.AddCapability(spv::Capability::MultiViewport);
393 }
394 if (info.stores[IR::Attribute::ViewportMask] && profile.support_viewport_mask) {
395 ctx.AddExtension("SPV_NV_viewport_array2");
396 ctx.AddCapability(spv::Capability::ShaderViewportMaskNV);
397 }
398 if (info.stores[IR::Attribute::Layer] || info.stores[IR::Attribute::ViewportIndex]) {
399 if (profile.support_viewport_index_layer_non_geometry && ctx.stage != Stage::Geometry) {
400 ctx.AddExtension("SPV_EXT_shader_viewport_index_layer");
401 ctx.AddCapability(spv::Capability::ShaderViewportIndexLayerEXT);
402 }
403 }
404 if (!profile.support_vertex_instance_id &&
405 (info.loads[IR::Attribute::InstanceId] || info.loads[IR::Attribute::VertexId])) {
406 ctx.AddExtension("SPV_KHR_shader_draw_parameters");
407 ctx.AddCapability(spv::Capability::DrawParameters);
408 }
409 if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id ||
410 info.uses_subgroup_shuffles) &&
411 profile.support_vote) {
412 ctx.AddExtension("SPV_KHR_shader_ballot");
413 ctx.AddCapability(spv::Capability::SubgroupBallotKHR);
414 if (!profile.warp_size_potentially_larger_than_guest) {
415 // vote ops are only used when not taking the long path
416 ctx.AddExtension("SPV_KHR_subgroup_vote");
417 ctx.AddCapability(spv::Capability::SubgroupVoteKHR);
418 }
419 }
420 if (info.uses_int64_bit_atomics && profile.support_int64_atomics) {
421 ctx.AddCapability(spv::Capability::Int64Atomics);
422 }
423 if (info.uses_typeless_image_reads && profile.support_typeless_image_loads) {
424 ctx.AddCapability(spv::Capability::StorageImageReadWithoutFormat);
425 }
426 if (info.uses_typeless_image_writes) {
427 ctx.AddCapability(spv::Capability::StorageImageWriteWithoutFormat);
428 }
429 if (info.uses_image_buffers) {
430 ctx.AddCapability(spv::Capability::ImageBuffer);
431 }
432 if (info.uses_sample_id) {
433 ctx.AddCapability(spv::Capability::SampleRateShading);
434 }
435 if (!ctx.runtime_info.xfb_varyings.empty()) {
436 ctx.AddCapability(spv::Capability::TransformFeedback);
437 }
438 if (info.uses_derivatives) {
439 ctx.AddCapability(spv::Capability::DerivativeControl);
440 }
441 // TODO: Track this usage
442 ctx.AddCapability(spv::Capability::ImageGatherExtended);
443 ctx.AddCapability(spv::Capability::ImageQuery);
444 ctx.AddCapability(spv::Capability::SampledBuffer);
445}
446
447void PatchPhiNodes(IR::Program& program, EmitContext& ctx) {
448 auto inst{program.blocks.front()->begin()};
449 size_t block_index{0};
450 ctx.PatchDeferredPhi([&](size_t phi_arg) {
451 if (phi_arg == 0) {
452 ++inst;
453 if (inst == program.blocks[block_index]->end() ||
454 inst->GetOpcode() != IR::Opcode::Phi) {
455 do {
456 ++block_index;
457 inst = program.blocks[block_index]->begin();
458 } while (inst->GetOpcode() != IR::Opcode::Phi);
459 }
460 }
461 return ctx.Def(inst->Arg(phi_arg));
462 });
463}
464} // Anonymous namespace
465
466std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info,
467 IR::Program& program, Bindings& bindings) {
468 EmitContext ctx{profile, runtime_info, program, bindings};
469 const Id main{DefineMain(ctx, program)};
470 DefineEntryPoint(program, ctx, main);
471 if (profile.support_float_controls) {
472 ctx.AddExtension("SPV_KHR_float_controls");
473 SetupDenormControl(profile, program, ctx, main);
474 SetupSignedNanCapabilities(profile, program, ctx, main);
475 }
476 SetupCapabilities(profile, program.info, ctx);
477 PatchPhiNodes(program, ctx);
478 return ctx.Assemble();
479}
480
481Id EmitPhi(EmitContext& ctx, IR::Inst* inst) {
482 const size_t num_args{inst->NumArgs()};
483 boost::container::small_vector<Id, 32> blocks;
484 blocks.reserve(num_args);
485 for (size_t index = 0; index < num_args; ++index) {
486 blocks.push_back(inst->PhiBlock(index)->Definition<Id>());
487 }
488 // The type of a phi instruction is stored in its flags
489 const Id result_type{TypeId(ctx, inst->Flags<IR::Type>())};
490 return ctx.DeferredOpPhi(result_type, std::span(blocks.data(), blocks.size()));
491}
492
493void EmitVoid(EmitContext&) {}
494
495Id EmitIdentity(EmitContext& ctx, const IR::Value& value) {
496 const Id id{ctx.Def(value)};
497 if (!Sirit::ValidId(id)) {
498 throw NotImplementedException("Forward identity declaration");
499 }
500 return id;
501}
502
503Id EmitConditionRef(EmitContext& ctx, const IR::Value& value) {
504 const Id id{ctx.Def(value)};
505 if (!Sirit::ValidId(id)) {
506 throw NotImplementedException("Forward identity declaration");
507 }
508 return id;
509}
510
511void EmitReference(EmitContext&) {}
512
513void EmitPhiMove(EmitContext&) {
514 throw LogicError("Unreachable instruction");
515}
516
517void EmitGetZeroFromOp(EmitContext&) {
518 throw LogicError("Unreachable instruction");
519}
520
521void EmitGetSignFromOp(EmitContext&) {
522 throw LogicError("Unreachable instruction");
523}
524
525void EmitGetCarryFromOp(EmitContext&) {
526 throw LogicError("Unreachable instruction");
527}
528
529void EmitGetOverflowFromOp(EmitContext&) {
530 throw LogicError("Unreachable instruction");
531}
532
533void EmitGetSparseFromOp(EmitContext&) {
534 throw LogicError("Unreachable instruction");
535}
536
537void EmitGetInBoundsFromOp(EmitContext&) {
538 throw LogicError("Unreachable instruction");
539}
540
541} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h
new file mode 100644
index 000000000..db0c935fe
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.h
@@ -0,0 +1,27 @@
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 <vector>
8
9#include <sirit/sirit.h>
10
11#include "common/common_types.h"
12#include "shader_recompiler/backend/bindings.h"
13#include "shader_recompiler/backend/spirv/emit_context.h"
14#include "shader_recompiler/frontend/ir/program.h"
15#include "shader_recompiler/profile.h"
16
17namespace Shader::Backend::SPIRV {
18
19[[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info,
20 IR::Program& program, Bindings& bindings);
21
22[[nodiscard]] inline std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program) {
23 Bindings binding;
24 return EmitSPIRV(profile, {}, program, binding);
25}
26
27} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
new file mode 100644
index 000000000..9af8bb9e1
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
@@ -0,0 +1,448 @@
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 "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7
8namespace Shader::Backend::SPIRV {
9namespace {
10Id SharedPointer(EmitContext& ctx, Id offset, u32 index_offset = 0) {
11 const Id shift_id{ctx.Const(2U)};
12 Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
13 if (index_offset > 0) {
14 index = ctx.OpIAdd(ctx.U32[1], index, ctx.Const(index_offset));
15 }
16 return ctx.profile.support_explicit_workgroup_layout
17 ? ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, ctx.u32_zero_value, index)
18 : ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index);
19}
20
21Id StorageIndex(EmitContext& ctx, const IR::Value& offset, size_t element_size) {
22 if (offset.IsImmediate()) {
23 const u32 imm_offset{static_cast<u32>(offset.U32() / element_size)};
24 return ctx.Const(imm_offset);
25 }
26 const u32 shift{static_cast<u32>(std::countr_zero(element_size))};
27 const Id index{ctx.Def(offset)};
28 if (shift == 0) {
29 return index;
30 }
31 const Id shift_id{ctx.Const(shift)};
32 return ctx.OpShiftRightLogical(ctx.U32[1], index, shift_id);
33}
34
35Id StoragePointer(EmitContext& ctx, const StorageTypeDefinition& type_def,
36 Id StorageDefinitions::*member_ptr, const IR::Value& binding,
37 const IR::Value& offset, size_t element_size) {
38 if (!binding.IsImmediate()) {
39 throw NotImplementedException("Dynamic storage buffer indexing");
40 }
41 const Id ssbo{ctx.ssbos[binding.U32()].*member_ptr};
42 const Id index{StorageIndex(ctx, offset, element_size)};
43 return ctx.OpAccessChain(type_def.element, ssbo, ctx.u32_zero_value, index);
44}
45
46std::pair<Id, Id> AtomicArgs(EmitContext& ctx) {
47 const Id scope{ctx.Const(static_cast<u32>(spv::Scope::Device))};
48 const Id semantics{ctx.u32_zero_value};
49 return {scope, semantics};
50}
51
52Id SharedAtomicU32(EmitContext& ctx, Id offset, Id value,
53 Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
54 const Id pointer{SharedPointer(ctx, offset)};
55 const auto [scope, semantics]{AtomicArgs(ctx)};
56 return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value);
57}
58
59Id StorageAtomicU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value,
60 Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
61 const Id pointer{StoragePointer(ctx, ctx.storage_types.U32, &StorageDefinitions::U32, binding,
62 offset, sizeof(u32))};
63 const auto [scope, semantics]{AtomicArgs(ctx)};
64 return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value);
65}
66
67Id StorageAtomicU64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value,
68 Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id),
69 Id (Sirit::Module::*non_atomic_func)(Id, Id, Id)) {
70 if (ctx.profile.support_int64_atomics) {
71 const Id pointer{StoragePointer(ctx, ctx.storage_types.U64, &StorageDefinitions::U64,
72 binding, offset, sizeof(u64))};
73 const auto [scope, semantics]{AtomicArgs(ctx)};
74 return (ctx.*atomic_func)(ctx.U64, pointer, scope, semantics, value);
75 }
76 LOG_ERROR(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
77 const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2,
78 binding, offset, sizeof(u32[2]))};
79 const Id original_value{ctx.OpBitcast(ctx.U64, ctx.OpLoad(ctx.U32[2], pointer))};
80 const Id result{(ctx.*non_atomic_func)(ctx.U64, value, original_value)};
81 ctx.OpStore(pointer, ctx.OpBitcast(ctx.U32[2], result));
82 return original_value;
83}
84} // Anonymous namespace
85
86Id EmitSharedAtomicIAdd32(EmitContext& ctx, Id offset, Id value) {
87 return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicIAdd);
88}
89
90Id EmitSharedAtomicSMin32(EmitContext& ctx, Id offset, Id value) {
91 return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicSMin);
92}
93
94Id EmitSharedAtomicUMin32(EmitContext& ctx, Id offset, Id value) {
95 return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicUMin);
96}
97
98Id EmitSharedAtomicSMax32(EmitContext& ctx, Id offset, Id value) {
99 return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicSMax);
100}
101
102Id EmitSharedAtomicUMax32(EmitContext& ctx, Id offset, Id value) {
103 return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicUMax);
104}
105
106Id EmitSharedAtomicInc32(EmitContext& ctx, Id offset, Id value) {
107 const Id shift_id{ctx.Const(2U)};
108 const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
109 return ctx.OpFunctionCall(ctx.U32[1], ctx.increment_cas_shared, index, value);
110}
111
112Id EmitSharedAtomicDec32(EmitContext& ctx, Id offset, Id value) {
113 const Id shift_id{ctx.Const(2U)};
114 const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
115 return ctx.OpFunctionCall(ctx.U32[1], ctx.decrement_cas_shared, index, value);
116}
117
118Id EmitSharedAtomicAnd32(EmitContext& ctx, Id offset, Id value) {
119 return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicAnd);
120}
121
122Id EmitSharedAtomicOr32(EmitContext& ctx, Id offset, Id value) {
123 return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicOr);
124}
125
126Id EmitSharedAtomicXor32(EmitContext& ctx, Id offset, Id value) {
127 return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicXor);
128}
129
130Id EmitSharedAtomicExchange32(EmitContext& ctx, Id offset, Id value) {
131 return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicExchange);
132}
133
134Id EmitSharedAtomicExchange64(EmitContext& ctx, Id offset, Id value) {
135 if (ctx.profile.support_int64_atomics && ctx.profile.support_explicit_workgroup_layout) {
136 const Id shift_id{ctx.Const(3U)};
137 const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
138 const Id pointer{
139 ctx.OpAccessChain(ctx.shared_u64, ctx.shared_memory_u64, ctx.u32_zero_value, index)};
140 const auto [scope, semantics]{AtomicArgs(ctx)};
141 return ctx.OpAtomicExchange(ctx.U64, pointer, scope, semantics, value);
142 }
143 LOG_ERROR(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
144 const Id pointer_1{SharedPointer(ctx, offset, 0)};
145 const Id pointer_2{SharedPointer(ctx, offset, 1)};
146 const Id value_1{ctx.OpLoad(ctx.U32[1], pointer_1)};
147 const Id value_2{ctx.OpLoad(ctx.U32[1], pointer_2)};
148 const Id new_vector{ctx.OpBitcast(ctx.U32[2], value)};
149 ctx.OpStore(pointer_1, ctx.OpCompositeExtract(ctx.U32[1], new_vector, 0U));
150 ctx.OpStore(pointer_2, ctx.OpCompositeExtract(ctx.U32[1], new_vector, 1U));
151 return ctx.OpBitcast(ctx.U64, ctx.OpCompositeConstruct(ctx.U32[2], value_1, value_2));
152}
153
154Id EmitStorageAtomicIAdd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
155 Id value) {
156 return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicIAdd);
157}
158
159Id EmitStorageAtomicSMin32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
160 Id value) {
161 return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicSMin);
162}
163
164Id EmitStorageAtomicUMin32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
165 Id value) {
166 return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicUMin);
167}
168
169Id EmitStorageAtomicSMax32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
170 Id value) {
171 return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicSMax);
172}
173
174Id EmitStorageAtomicUMax32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
175 Id value) {
176 return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicUMax);
177}
178
179Id EmitStorageAtomicInc32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
180 Id value) {
181 const Id ssbo{ctx.ssbos[binding.U32()].U32};
182 const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
183 return ctx.OpFunctionCall(ctx.U32[1], ctx.increment_cas_ssbo, base_index, value, ssbo);
184}
185
186Id EmitStorageAtomicDec32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
187 Id value) {
188 const Id ssbo{ctx.ssbos[binding.U32()].U32};
189 const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
190 return ctx.OpFunctionCall(ctx.U32[1], ctx.decrement_cas_ssbo, base_index, value, ssbo);
191}
192
193Id EmitStorageAtomicAnd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
194 Id value) {
195 return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicAnd);
196}
197
198Id EmitStorageAtomicOr32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
199 Id value) {
200 return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicOr);
201}
202
203Id EmitStorageAtomicXor32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
204 Id value) {
205 return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicXor);
206}
207
208Id EmitStorageAtomicExchange32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
209 Id value) {
210 return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicExchange);
211}
212
213Id EmitStorageAtomicIAdd64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
214 Id value) {
215 return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicIAdd,
216 &Sirit::Module::OpIAdd);
217}
218
219Id EmitStorageAtomicSMin64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
220 Id value) {
221 return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicSMin,
222 &Sirit::Module::OpSMin);
223}
224
225Id EmitStorageAtomicUMin64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
226 Id value) {
227 return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicUMin,
228 &Sirit::Module::OpUMin);
229}
230
231Id EmitStorageAtomicSMax64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
232 Id value) {
233 return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicSMax,
234 &Sirit::Module::OpSMax);
235}
236
237Id EmitStorageAtomicUMax64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
238 Id value) {
239 return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicUMax,
240 &Sirit::Module::OpUMax);
241}
242
243Id EmitStorageAtomicAnd64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
244 Id value) {
245 return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicAnd,
246 &Sirit::Module::OpBitwiseAnd);
247}
248
249Id EmitStorageAtomicOr64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
250 Id value) {
251 return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicOr,
252 &Sirit::Module::OpBitwiseOr);
253}
254
255Id EmitStorageAtomicXor64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
256 Id value) {
257 return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicXor,
258 &Sirit::Module::OpBitwiseXor);
259}
260
261Id EmitStorageAtomicExchange64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
262 Id value) {
263 if (ctx.profile.support_int64_atomics) {
264 const Id pointer{StoragePointer(ctx, ctx.storage_types.U64, &StorageDefinitions::U64,
265 binding, offset, sizeof(u64))};
266 const auto [scope, semantics]{AtomicArgs(ctx)};
267 return ctx.OpAtomicExchange(ctx.U64, pointer, scope, semantics, value);
268 }
269 LOG_ERROR(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
270 const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2,
271 binding, offset, sizeof(u32[2]))};
272 const Id original{ctx.OpBitcast(ctx.U64, ctx.OpLoad(ctx.U32[2], pointer))};
273 ctx.OpStore(pointer, value);
274 return original;
275}
276
277Id EmitStorageAtomicAddF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
278 Id value) {
279 const Id ssbo{ctx.ssbos[binding.U32()].U32};
280 const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
281 return ctx.OpFunctionCall(ctx.F32[1], ctx.f32_add_cas, base_index, value, ssbo);
282}
283
284Id EmitStorageAtomicAddF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
285 Id value) {
286 const Id ssbo{ctx.ssbos[binding.U32()].U32};
287 const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
288 const Id result{ctx.OpFunctionCall(ctx.F16[2], ctx.f16x2_add_cas, base_index, value, ssbo)};
289 return ctx.OpBitcast(ctx.U32[1], result);
290}
291
292Id EmitStorageAtomicAddF32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
293 Id value) {
294 const Id ssbo{ctx.ssbos[binding.U32()].U32};
295 const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
296 const Id result{ctx.OpFunctionCall(ctx.F32[2], ctx.f32x2_add_cas, base_index, value, ssbo)};
297 return ctx.OpPackHalf2x16(ctx.U32[1], result);
298}
299
300Id EmitStorageAtomicMinF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
301 Id value) {
302 const Id ssbo{ctx.ssbos[binding.U32()].U32};
303 const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
304 const Id result{ctx.OpFunctionCall(ctx.F16[2], ctx.f16x2_min_cas, base_index, value, ssbo)};
305 return ctx.OpBitcast(ctx.U32[1], result);
306}
307
308Id EmitStorageAtomicMinF32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
309 Id value) {
310 const Id ssbo{ctx.ssbos[binding.U32()].U32};
311 const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
312 const Id result{ctx.OpFunctionCall(ctx.F32[2], ctx.f32x2_min_cas, base_index, value, ssbo)};
313 return ctx.OpPackHalf2x16(ctx.U32[1], result);
314}
315
316Id EmitStorageAtomicMaxF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
317 Id value) {
318 const Id ssbo{ctx.ssbos[binding.U32()].U32};
319 const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
320 const Id result{ctx.OpFunctionCall(ctx.F16[2], ctx.f16x2_max_cas, base_index, value, ssbo)};
321 return ctx.OpBitcast(ctx.U32[1], result);
322}
323
324Id EmitStorageAtomicMaxF32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
325 Id value) {
326 const Id ssbo{ctx.ssbos[binding.U32()].U32};
327 const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
328 const Id result{ctx.OpFunctionCall(ctx.F32[2], ctx.f32x2_max_cas, base_index, value, ssbo)};
329 return ctx.OpPackHalf2x16(ctx.U32[1], result);
330}
331
332Id EmitGlobalAtomicIAdd32(EmitContext&) {
333 throw NotImplementedException("SPIR-V Instruction");
334}
335
336Id EmitGlobalAtomicSMin32(EmitContext&) {
337 throw NotImplementedException("SPIR-V Instruction");
338}
339
340Id EmitGlobalAtomicUMin32(EmitContext&) {
341 throw NotImplementedException("SPIR-V Instruction");
342}
343
344Id EmitGlobalAtomicSMax32(EmitContext&) {
345 throw NotImplementedException("SPIR-V Instruction");
346}
347
348Id EmitGlobalAtomicUMax32(EmitContext&) {
349 throw NotImplementedException("SPIR-V Instruction");
350}
351
352Id EmitGlobalAtomicInc32(EmitContext&) {
353 throw NotImplementedException("SPIR-V Instruction");
354}
355
356Id EmitGlobalAtomicDec32(EmitContext&) {
357 throw NotImplementedException("SPIR-V Instruction");
358}
359
360Id EmitGlobalAtomicAnd32(EmitContext&) {
361 throw NotImplementedException("SPIR-V Instruction");
362}
363
364Id EmitGlobalAtomicOr32(EmitContext&) {
365 throw NotImplementedException("SPIR-V Instruction");
366}
367
368Id EmitGlobalAtomicXor32(EmitContext&) {
369 throw NotImplementedException("SPIR-V Instruction");
370}
371
372Id EmitGlobalAtomicExchange32(EmitContext&) {
373 throw NotImplementedException("SPIR-V Instruction");
374}
375
376Id EmitGlobalAtomicIAdd64(EmitContext&) {
377 throw NotImplementedException("SPIR-V Instruction");
378}
379
380Id EmitGlobalAtomicSMin64(EmitContext&) {
381 throw NotImplementedException("SPIR-V Instruction");
382}
383
384Id EmitGlobalAtomicUMin64(EmitContext&) {
385 throw NotImplementedException("SPIR-V Instruction");
386}
387
388Id EmitGlobalAtomicSMax64(EmitContext&) {
389 throw NotImplementedException("SPIR-V Instruction");
390}
391
392Id EmitGlobalAtomicUMax64(EmitContext&) {
393 throw NotImplementedException("SPIR-V Instruction");
394}
395
396Id EmitGlobalAtomicInc64(EmitContext&) {
397 throw NotImplementedException("SPIR-V Instruction");
398}
399
400Id EmitGlobalAtomicDec64(EmitContext&) {
401 throw NotImplementedException("SPIR-V Instruction");
402}
403
404Id EmitGlobalAtomicAnd64(EmitContext&) {
405 throw NotImplementedException("SPIR-V Instruction");
406}
407
408Id EmitGlobalAtomicOr64(EmitContext&) {
409 throw NotImplementedException("SPIR-V Instruction");
410}
411
412Id EmitGlobalAtomicXor64(EmitContext&) {
413 throw NotImplementedException("SPIR-V Instruction");
414}
415
416Id EmitGlobalAtomicExchange64(EmitContext&) {
417 throw NotImplementedException("SPIR-V Instruction");
418}
419
420Id EmitGlobalAtomicAddF32(EmitContext&) {
421 throw NotImplementedException("SPIR-V Instruction");
422}
423
424Id EmitGlobalAtomicAddF16x2(EmitContext&) {
425 throw NotImplementedException("SPIR-V Instruction");
426}
427
428Id EmitGlobalAtomicAddF32x2(EmitContext&) {
429 throw NotImplementedException("SPIR-V Instruction");
430}
431
432Id EmitGlobalAtomicMinF16x2(EmitContext&) {
433 throw NotImplementedException("SPIR-V Instruction");
434}
435
436Id EmitGlobalAtomicMinF32x2(EmitContext&) {
437 throw NotImplementedException("SPIR-V Instruction");
438}
439
440Id EmitGlobalAtomicMaxF16x2(EmitContext&) {
441 throw NotImplementedException("SPIR-V Instruction");
442}
443
444Id EmitGlobalAtomicMaxF32x2(EmitContext&) {
445 throw NotImplementedException("SPIR-V Instruction");
446}
447
448} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp
new file mode 100644
index 000000000..e0b52a001
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp
@@ -0,0 +1,38 @@
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 "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/frontend/ir/modifiers.h"
8
9namespace Shader::Backend::SPIRV {
10namespace {
11void MemoryBarrier(EmitContext& ctx, spv::Scope scope) {
12 const auto semantics{
13 spv::MemorySemanticsMask::AcquireRelease | spv::MemorySemanticsMask::UniformMemory |
14 spv::MemorySemanticsMask::WorkgroupMemory | spv::MemorySemanticsMask::AtomicCounterMemory |
15 spv::MemorySemanticsMask::ImageMemory};
16 ctx.OpMemoryBarrier(ctx.Const(static_cast<u32>(scope)), ctx.Const(static_cast<u32>(semantics)));
17}
18} // Anonymous namespace
19
20void EmitBarrier(EmitContext& ctx) {
21 const auto execution{spv::Scope::Workgroup};
22 const auto memory{spv::Scope::Workgroup};
23 const auto memory_semantics{spv::MemorySemanticsMask::AcquireRelease |
24 spv::MemorySemanticsMask::WorkgroupMemory};
25 ctx.OpControlBarrier(ctx.Const(static_cast<u32>(execution)),
26 ctx.Const(static_cast<u32>(memory)),
27 ctx.Const(static_cast<u32>(memory_semantics)));
28}
29
30void EmitWorkgroupMemoryBarrier(EmitContext& ctx) {
31 MemoryBarrier(ctx, spv::Scope::Workgroup);
32}
33
34void EmitDeviceMemoryBarrier(EmitContext& ctx) {
35 MemoryBarrier(ctx, spv::Scope::Device);
36}
37
38} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
new file mode 100644
index 000000000..bb11f4f4e
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
@@ -0,0 +1,66 @@
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 "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7
8namespace Shader::Backend::SPIRV {
9
10void EmitBitCastU16F16(EmitContext&) {
11 throw NotImplementedException("SPIR-V Instruction");
12}
13
14Id EmitBitCastU32F32(EmitContext& ctx, Id value) {
15 return ctx.OpBitcast(ctx.U32[1], value);
16}
17
18void EmitBitCastU64F64(EmitContext&) {
19 throw NotImplementedException("SPIR-V Instruction");
20}
21
22void EmitBitCastF16U16(EmitContext&) {
23 throw NotImplementedException("SPIR-V Instruction");
24}
25
26Id EmitBitCastF32U32(EmitContext& ctx, Id value) {
27 return ctx.OpBitcast(ctx.F32[1], value);
28}
29
30void EmitBitCastF64U64(EmitContext&) {
31 throw NotImplementedException("SPIR-V Instruction");
32}
33
34Id EmitPackUint2x32(EmitContext& ctx, Id value) {
35 return ctx.OpBitcast(ctx.U64, value);
36}
37
38Id EmitUnpackUint2x32(EmitContext& ctx, Id value) {
39 return ctx.OpBitcast(ctx.U32[2], value);
40}
41
42Id EmitPackFloat2x16(EmitContext& ctx, Id value) {
43 return ctx.OpBitcast(ctx.U32[1], value);
44}
45
46Id EmitUnpackFloat2x16(EmitContext& ctx, Id value) {
47 return ctx.OpBitcast(ctx.F16[2], value);
48}
49
50Id EmitPackHalf2x16(EmitContext& ctx, Id value) {
51 return ctx.OpPackHalf2x16(ctx.U32[1], value);
52}
53
54Id EmitUnpackHalf2x16(EmitContext& ctx, Id value) {
55 return ctx.OpUnpackHalf2x16(ctx.F32[2], value);
56}
57
58Id EmitPackDouble2x32(EmitContext& ctx, Id value) {
59 return ctx.OpBitcast(ctx.F64[1], value);
60}
61
62Id EmitUnpackDouble2x32(EmitContext& ctx, Id value) {
63 return ctx.OpBitcast(ctx.U32[2], value);
64}
65
66} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp
new file mode 100644
index 000000000..10ff4ecab
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp
@@ -0,0 +1,155 @@
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 "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/frontend/ir/modifiers.h"
8
9namespace Shader::Backend::SPIRV {
10
11Id EmitCompositeConstructU32x2(EmitContext& ctx, Id e1, Id e2) {
12 return ctx.OpCompositeConstruct(ctx.U32[2], e1, e2);
13}
14
15Id EmitCompositeConstructU32x3(EmitContext& ctx, Id e1, Id e2, Id e3) {
16 return ctx.OpCompositeConstruct(ctx.U32[3], e1, e2, e3);
17}
18
19Id EmitCompositeConstructU32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4) {
20 return ctx.OpCompositeConstruct(ctx.U32[4], e1, e2, e3, e4);
21}
22
23Id EmitCompositeExtractU32x2(EmitContext& ctx, Id composite, u32 index) {
24 return ctx.OpCompositeExtract(ctx.U32[1], composite, index);
25}
26
27Id EmitCompositeExtractU32x3(EmitContext& ctx, Id composite, u32 index) {
28 return ctx.OpCompositeExtract(ctx.U32[1], composite, index);
29}
30
31Id EmitCompositeExtractU32x4(EmitContext& ctx, Id composite, u32 index) {
32 return ctx.OpCompositeExtract(ctx.U32[1], composite, index);
33}
34
35Id EmitCompositeInsertU32x2(EmitContext& ctx, Id composite, Id object, u32 index) {
36 return ctx.OpCompositeInsert(ctx.U32[2], object, composite, index);
37}
38
39Id EmitCompositeInsertU32x3(EmitContext& ctx, Id composite, Id object, u32 index) {
40 return ctx.OpCompositeInsert(ctx.U32[3], object, composite, index);
41}
42
43Id EmitCompositeInsertU32x4(EmitContext& ctx, Id composite, Id object, u32 index) {
44 return ctx.OpCompositeInsert(ctx.U32[4], object, composite, index);
45}
46
47Id EmitCompositeConstructF16x2(EmitContext& ctx, Id e1, Id e2) {
48 return ctx.OpCompositeConstruct(ctx.F16[2], e1, e2);
49}
50
51Id EmitCompositeConstructF16x3(EmitContext& ctx, Id e1, Id e2, Id e3) {
52 return ctx.OpCompositeConstruct(ctx.F16[3], e1, e2, e3);
53}
54
55Id EmitCompositeConstructF16x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4) {
56 return ctx.OpCompositeConstruct(ctx.F16[4], e1, e2, e3, e4);
57}
58
59Id EmitCompositeExtractF16x2(EmitContext& ctx, Id composite, u32 index) {
60 return ctx.OpCompositeExtract(ctx.F16[1], composite, index);
61}
62
63Id EmitCompositeExtractF16x3(EmitContext& ctx, Id composite, u32 index) {
64 return ctx.OpCompositeExtract(ctx.F16[1], composite, index);
65}
66
67Id EmitCompositeExtractF16x4(EmitContext& ctx, Id composite, u32 index) {
68 return ctx.OpCompositeExtract(ctx.F16[1], composite, index);
69}
70
71Id EmitCompositeInsertF16x2(EmitContext& ctx, Id composite, Id object, u32 index) {
72 return ctx.OpCompositeInsert(ctx.F16[2], object, composite, index);
73}
74
75Id EmitCompositeInsertF16x3(EmitContext& ctx, Id composite, Id object, u32 index) {
76 return ctx.OpCompositeInsert(ctx.F16[3], object, composite, index);
77}
78
79Id EmitCompositeInsertF16x4(EmitContext& ctx, Id composite, Id object, u32 index) {
80 return ctx.OpCompositeInsert(ctx.F16[4], object, composite, index);
81}
82
83Id EmitCompositeConstructF32x2(EmitContext& ctx, Id e1, Id e2) {
84 return ctx.OpCompositeConstruct(ctx.F32[2], e1, e2);
85}
86
87Id EmitCompositeConstructF32x3(EmitContext& ctx, Id e1, Id e2, Id e3) {
88 return ctx.OpCompositeConstruct(ctx.F32[3], e1, e2, e3);
89}
90
91Id EmitCompositeConstructF32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4) {
92 return ctx.OpCompositeConstruct(ctx.F32[4], e1, e2, e3, e4);
93}
94
95Id EmitCompositeExtractF32x2(EmitContext& ctx, Id composite, u32 index) {
96 return ctx.OpCompositeExtract(ctx.F32[1], composite, index);
97}
98
99Id EmitCompositeExtractF32x3(EmitContext& ctx, Id composite, u32 index) {
100 return ctx.OpCompositeExtract(ctx.F32[1], composite, index);
101}
102
103Id EmitCompositeExtractF32x4(EmitContext& ctx, Id composite, u32 index) {
104 return ctx.OpCompositeExtract(ctx.F32[1], composite, index);
105}
106
107Id EmitCompositeInsertF32x2(EmitContext& ctx, Id composite, Id object, u32 index) {
108 return ctx.OpCompositeInsert(ctx.F32[2], object, composite, index);
109}
110
111Id EmitCompositeInsertF32x3(EmitContext& ctx, Id composite, Id object, u32 index) {
112 return ctx.OpCompositeInsert(ctx.F32[3], object, composite, index);
113}
114
115Id EmitCompositeInsertF32x4(EmitContext& ctx, Id composite, Id object, u32 index) {
116 return ctx.OpCompositeInsert(ctx.F32[4], object, composite, index);
117}
118
119void EmitCompositeConstructF64x2(EmitContext&) {
120 throw NotImplementedException("SPIR-V Instruction");
121}
122
123void EmitCompositeConstructF64x3(EmitContext&) {
124 throw NotImplementedException("SPIR-V Instruction");
125}
126
127void EmitCompositeConstructF64x4(EmitContext&) {
128 throw NotImplementedException("SPIR-V Instruction");
129}
130
131void EmitCompositeExtractF64x2(EmitContext&) {
132 throw NotImplementedException("SPIR-V Instruction");
133}
134
135void EmitCompositeExtractF64x3(EmitContext&) {
136 throw NotImplementedException("SPIR-V Instruction");
137}
138
139void EmitCompositeExtractF64x4(EmitContext&) {
140 throw NotImplementedException("SPIR-V Instruction");
141}
142
143Id EmitCompositeInsertF64x2(EmitContext& ctx, Id composite, Id object, u32 index) {
144 return ctx.OpCompositeInsert(ctx.F64[2], object, composite, index);
145}
146
147Id EmitCompositeInsertF64x3(EmitContext& ctx, Id composite, Id object, u32 index) {
148 return ctx.OpCompositeInsert(ctx.F64[3], object, composite, index);
149}
150
151Id EmitCompositeInsertF64x4(EmitContext& ctx, Id composite, Id object, u32 index) {
152 return ctx.OpCompositeInsert(ctx.F64[4], object, composite, index);
153}
154
155} // namespace Shader::Backend::SPIRV
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
new file mode 100644
index 000000000..fb8c02a77
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -0,0 +1,505 @@
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 <tuple>
6#include <utility>
7
8#include "shader_recompiler/backend/spirv/emit_spirv.h"
9#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
10
11namespace Shader::Backend::SPIRV {
12namespace {
13struct AttrInfo {
14 Id pointer;
15 Id id;
16 bool needs_cast;
17};
18
19std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) {
20 const AttributeType type{ctx.runtime_info.generic_input_types.at(index)};
21 switch (type) {
22 case AttributeType::Float:
23 return AttrInfo{ctx.input_f32, ctx.F32[1], false};
24 case AttributeType::UnsignedInt:
25 return AttrInfo{ctx.input_u32, ctx.U32[1], true};
26 case AttributeType::SignedInt:
27 return AttrInfo{ctx.input_s32, ctx.TypeInt(32, true), true};
28 case AttributeType::Disabled:
29 return std::nullopt;
30 }
31 throw InvalidArgument("Invalid attribute type {}", type);
32}
33
34template <typename... Args>
35Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&... args) {
36 switch (ctx.stage) {
37 case Stage::TessellationControl:
38 case Stage::TessellationEval:
39 case Stage::Geometry:
40 return ctx.OpAccessChain(pointer_type, base, vertex, std::forward<Args>(args)...);
41 default:
42 return ctx.OpAccessChain(pointer_type, base, std::forward<Args>(args)...);
43 }
44}
45
46template <typename... Args>
47Id OutputAccessChain(EmitContext& ctx, Id result_type, Id base, Args&&... args) {
48 if (ctx.stage == Stage::TessellationControl) {
49 const Id invocation_id{ctx.OpLoad(ctx.U32[1], ctx.invocation_id)};
50 return ctx.OpAccessChain(result_type, base, invocation_id, std::forward<Args>(args)...);
51 } else {
52 return ctx.OpAccessChain(result_type, base, std::forward<Args>(args)...);
53 }
54}
55
56struct OutAttr {
57 OutAttr(Id pointer_) : pointer{pointer_} {}
58 OutAttr(Id pointer_, Id type_) : pointer{pointer_}, type{type_} {}
59
60 Id pointer{};
61 Id type{};
62};
63
64std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
65 if (IR::IsGeneric(attr)) {
66 const u32 index{IR::GenericAttributeIndex(attr)};
67 const u32 element{IR::GenericAttributeElement(attr)};
68 const GenericElementInfo& info{ctx.output_generics.at(index).at(element)};
69 if (info.num_components == 1) {
70 return info.id;
71 } else {
72 const u32 index_element{element - info.first_element};
73 const Id index_id{ctx.Const(index_element)};
74 return OutputAccessChain(ctx, ctx.output_f32, info.id, index_id);
75 }
76 }
77 switch (attr) {
78 case IR::Attribute::PointSize:
79 return ctx.output_point_size;
80 case IR::Attribute::PositionX:
81 case IR::Attribute::PositionY:
82 case IR::Attribute::PositionZ:
83 case IR::Attribute::PositionW: {
84 const u32 element{static_cast<u32>(attr) % 4};
85 const Id element_id{ctx.Const(element)};
86 return OutputAccessChain(ctx, ctx.output_f32, ctx.output_position, element_id);
87 }
88 case IR::Attribute::ClipDistance0:
89 case IR::Attribute::ClipDistance1:
90 case IR::Attribute::ClipDistance2:
91 case IR::Attribute::ClipDistance3:
92 case IR::Attribute::ClipDistance4:
93 case IR::Attribute::ClipDistance5:
94 case IR::Attribute::ClipDistance6:
95 case IR::Attribute::ClipDistance7: {
96 const u32 base{static_cast<u32>(IR::Attribute::ClipDistance0)};
97 const u32 index{static_cast<u32>(attr) - base};
98 const Id clip_num{ctx.Const(index)};
99 return OutputAccessChain(ctx, ctx.output_f32, ctx.clip_distances, clip_num);
100 }
101 case IR::Attribute::Layer:
102 if (ctx.profile.support_viewport_index_layer_non_geometry ||
103 ctx.stage == Shader::Stage::Geometry) {
104 return OutAttr{ctx.layer, ctx.U32[1]};
105 }
106 return std::nullopt;
107 case IR::Attribute::ViewportIndex:
108 if (ctx.profile.support_viewport_index_layer_non_geometry ||
109 ctx.stage == Shader::Stage::Geometry) {
110 return OutAttr{ctx.viewport_index, ctx.U32[1]};
111 }
112 return std::nullopt;
113 case IR::Attribute::ViewportMask:
114 if (!ctx.profile.support_viewport_mask) {
115 return std::nullopt;
116 }
117 return OutAttr{ctx.OpAccessChain(ctx.output_u32, ctx.viewport_mask, ctx.u32_zero_value),
118 ctx.U32[1]};
119 default:
120 throw NotImplementedException("Read attribute {}", attr);
121 }
122}
123
124Id GetCbuf(EmitContext& ctx, Id result_type, Id UniformDefinitions::*member_ptr, u32 element_size,
125 const IR::Value& binding, const IR::Value& offset) {
126 if (!binding.IsImmediate()) {
127 throw NotImplementedException("Constant buffer indexing");
128 }
129 const Id cbuf{ctx.cbufs[binding.U32()].*member_ptr};
130 const Id uniform_type{ctx.uniform_types.*member_ptr};
131 if (!offset.IsImmediate()) {
132 Id index{ctx.Def(offset)};
133 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 index = ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.Def(offset), shift);
137 }
138 const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, index)};
139 return ctx.OpLoad(result_type, access_chain);
140 }
141 // Hardware been proved to read the aligned offset (e.g. LDC.U32 at 6 will read offset 4)
142 const Id imm_offset{ctx.Const(offset.U32() / element_size)};
143 const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, imm_offset)};
144 return ctx.OpLoad(result_type, access_chain);
145}
146
147Id GetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
148 return GetCbuf(ctx, ctx.U32[1], &UniformDefinitions::U32, sizeof(u32), binding, offset);
149}
150
151Id GetCbufU32x4(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
152 return GetCbuf(ctx, ctx.U32[4], &UniformDefinitions::U32x4, sizeof(u32[4]), binding, offset);
153}
154
155Id GetCbufElement(EmitContext& ctx, Id vector, const IR::Value& offset, u32 index_offset) {
156 if (offset.IsImmediate()) {
157 const u32 element{(offset.U32() / 4) % 4 + index_offset};
158 return ctx.OpCompositeExtract(ctx.U32[1], vector, element);
159 }
160 const Id shift{ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.Def(offset), ctx.Const(2u))};
161 Id element{ctx.OpBitwiseAnd(ctx.U32[1], shift, ctx.Const(3u))};
162 if (index_offset > 0) {
163 element = ctx.OpIAdd(ctx.U32[1], element, ctx.Const(index_offset));
164 }
165 return ctx.OpVectorExtractDynamic(ctx.U32[1], vector, element);
166}
167} // Anonymous namespace
168
169void EmitGetRegister(EmitContext&) {
170 throw LogicError("Unreachable instruction");
171}
172
173void EmitSetRegister(EmitContext&) {
174 throw LogicError("Unreachable instruction");
175}
176
177void EmitGetPred(EmitContext&) {
178 throw LogicError("Unreachable instruction");
179}
180
181void EmitSetPred(EmitContext&) {
182 throw LogicError("Unreachable instruction");
183}
184
185void EmitSetGotoVariable(EmitContext&) {
186 throw LogicError("Unreachable instruction");
187}
188
189void EmitGetGotoVariable(EmitContext&) {
190 throw LogicError("Unreachable instruction");
191}
192
193void EmitSetIndirectBranchVariable(EmitContext&) {
194 throw LogicError("Unreachable instruction");
195}
196
197void EmitGetIndirectBranchVariable(EmitContext&) {
198 throw LogicError("Unreachable instruction");
199}
200
201Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
202 if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) {
203 const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset)};
204 return ctx.OpUConvert(ctx.U32[1], load);
205 }
206 Id element{};
207 if (ctx.profile.support_descriptor_aliasing) {
208 element = GetCbufU32(ctx, binding, offset);
209 } else {
210 const Id vector{GetCbufU32x4(ctx, binding, offset)};
211 element = GetCbufElement(ctx, vector, offset, 0u);
212 }
213 const Id bit_offset{ctx.BitOffset8(offset)};
214 return ctx.OpBitFieldUExtract(ctx.U32[1], element, bit_offset, ctx.Const(8u));
215}
216
217Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
218 if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) {
219 const Id load{GetCbuf(ctx, ctx.S8, &UniformDefinitions::S8, sizeof(s8), binding, offset)};
220 return ctx.OpSConvert(ctx.U32[1], load);
221 }
222 Id element{};
223 if (ctx.profile.support_descriptor_aliasing) {
224 element = GetCbufU32(ctx, binding, offset);
225 } else {
226 const Id vector{GetCbufU32x4(ctx, binding, offset)};
227 element = GetCbufElement(ctx, vector, offset, 0u);
228 }
229 const Id bit_offset{ctx.BitOffset8(offset)};
230 return ctx.OpBitFieldSExtract(ctx.U32[1], element, bit_offset, ctx.Const(8u));
231}
232
233Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
234 if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) {
235 const Id load{
236 GetCbuf(ctx, ctx.U16, &UniformDefinitions::U16, sizeof(u16), binding, offset)};
237 return ctx.OpUConvert(ctx.U32[1], load);
238 }
239 Id element{};
240 if (ctx.profile.support_descriptor_aliasing) {
241 element = GetCbufU32(ctx, binding, offset);
242 } else {
243 const Id vector{GetCbufU32x4(ctx, binding, offset)};
244 element = GetCbufElement(ctx, vector, offset, 0u);
245 }
246 const Id bit_offset{ctx.BitOffset16(offset)};
247 return ctx.OpBitFieldUExtract(ctx.U32[1], element, bit_offset, ctx.Const(16u));
248}
249
250Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
251 if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) {
252 const Id load{
253 GetCbuf(ctx, ctx.S16, &UniformDefinitions::S16, sizeof(s16), binding, offset)};
254 return ctx.OpSConvert(ctx.U32[1], load);
255 }
256 Id element{};
257 if (ctx.profile.support_descriptor_aliasing) {
258 element = GetCbufU32(ctx, binding, offset);
259 } else {
260 const Id vector{GetCbufU32x4(ctx, binding, offset)};
261 element = GetCbufElement(ctx, vector, offset, 0u);
262 }
263 const Id bit_offset{ctx.BitOffset16(offset)};
264 return ctx.OpBitFieldSExtract(ctx.U32[1], element, bit_offset, ctx.Const(16u));
265}
266
267Id EmitGetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
268 if (ctx.profile.support_descriptor_aliasing) {
269 return GetCbufU32(ctx, binding, offset);
270 } else {
271 const Id vector{GetCbufU32x4(ctx, binding, offset)};
272 return GetCbufElement(ctx, vector, offset, 0u);
273 }
274}
275
276Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
277 if (ctx.profile.support_descriptor_aliasing) {
278 return GetCbuf(ctx, ctx.F32[1], &UniformDefinitions::F32, sizeof(f32), binding, offset);
279 } else {
280 const Id vector{GetCbufU32x4(ctx, binding, offset)};
281 return ctx.OpBitcast(ctx.F32[1], GetCbufElement(ctx, vector, offset, 0u));
282 }
283}
284
285Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
286 if (ctx.profile.support_descriptor_aliasing) {
287 return GetCbuf(ctx, ctx.U32[2], &UniformDefinitions::U32x2, sizeof(u32[2]), binding,
288 offset);
289 } else {
290 const Id vector{GetCbufU32x4(ctx, binding, offset)};
291 return ctx.OpCompositeConstruct(ctx.U32[2], GetCbufElement(ctx, vector, offset, 0u),
292 GetCbufElement(ctx, vector, offset, 1u));
293 }
294}
295
296Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
297 const u32 element{static_cast<u32>(attr) % 4};
298 if (IR::IsGeneric(attr)) {
299 const u32 index{IR::GenericAttributeIndex(attr)};
300 const std::optional<AttrInfo> type{AttrTypes(ctx, index)};
301 if (!type) {
302 // Attribute is disabled
303 return ctx.Const(element == 3 ? 1.0f : 0.0f);
304 }
305 if (!ctx.runtime_info.previous_stage_stores.Generic(index, element)) {
306 // Varying component is not written
307 return ctx.Const(type && element == 3 ? 1.0f : 0.0f);
308 }
309 const Id generic_id{ctx.input_generics.at(index)};
310 const Id pointer{AttrPointer(ctx, type->pointer, vertex, generic_id, ctx.Const(element))};
311 const Id value{ctx.OpLoad(type->id, pointer)};
312 return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value;
313 }
314 switch (attr) {
315 case IR::Attribute::PrimitiveId:
316 return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.primitive_id));
317 case IR::Attribute::PositionX:
318 case IR::Attribute::PositionY:
319 case IR::Attribute::PositionZ:
320 case IR::Attribute::PositionW:
321 return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position,
322 ctx.Const(element)));
323 case IR::Attribute::InstanceId:
324 if (ctx.profile.support_vertex_instance_id) {
325 return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.instance_id));
326 } else {
327 const Id index{ctx.OpLoad(ctx.U32[1], ctx.instance_index)};
328 const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_instance)};
329 return ctx.OpBitcast(ctx.F32[1], ctx.OpISub(ctx.U32[1], index, base));
330 }
331 case IR::Attribute::VertexId:
332 if (ctx.profile.support_vertex_instance_id) {
333 return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.vertex_id));
334 } else {
335 const Id index{ctx.OpLoad(ctx.U32[1], ctx.vertex_index)};
336 const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)};
337 return ctx.OpBitcast(ctx.F32[1], ctx.OpISub(ctx.U32[1], index, base));
338 }
339 case IR::Attribute::FrontFace:
340 return ctx.OpSelect(ctx.U32[1], ctx.OpLoad(ctx.U1, ctx.front_face),
341 ctx.Const(std::numeric_limits<u32>::max()), ctx.u32_zero_value);
342 case IR::Attribute::PointSpriteS:
343 return ctx.OpLoad(ctx.F32[1],
344 ctx.OpAccessChain(ctx.input_f32, ctx.point_coord, ctx.u32_zero_value));
345 case IR::Attribute::PointSpriteT:
346 return ctx.OpLoad(ctx.F32[1],
347 ctx.OpAccessChain(ctx.input_f32, ctx.point_coord, ctx.Const(1U)));
348 case IR::Attribute::TessellationEvaluationPointU:
349 return ctx.OpLoad(ctx.F32[1],
350 ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord, ctx.u32_zero_value));
351 case IR::Attribute::TessellationEvaluationPointV:
352 return ctx.OpLoad(ctx.F32[1],
353 ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord, ctx.Const(1U)));
354
355 default:
356 throw NotImplementedException("Read attribute {}", attr);
357 }
358}
359
360void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, [[maybe_unused]] Id vertex) {
361 const std::optional<OutAttr> output{OutputAttrPointer(ctx, attr)};
362 if (!output) {
363 return;
364 }
365 if (Sirit::ValidId(output->type)) {
366 value = ctx.OpBitcast(output->type, value);
367 }
368 ctx.OpStore(output->pointer, value);
369}
370
371Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex) {
372 switch (ctx.stage) {
373 case Stage::TessellationControl:
374 case Stage::TessellationEval:
375 case Stage::Geometry:
376 return ctx.OpFunctionCall(ctx.F32[1], ctx.indexed_load_func, offset, vertex);
377 default:
378 return ctx.OpFunctionCall(ctx.F32[1], ctx.indexed_load_func, offset);
379 }
380}
381
382void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value, [[maybe_unused]] Id vertex) {
383 ctx.OpFunctionCall(ctx.void_id, ctx.indexed_store_func, offset, value);
384}
385
386Id EmitGetPatch(EmitContext& ctx, IR::Patch patch) {
387 if (!IR::IsGeneric(patch)) {
388 throw NotImplementedException("Non-generic patch load");
389 }
390 const u32 index{IR::GenericPatchIndex(patch)};
391 const Id element{ctx.Const(IR::GenericPatchElement(patch))};
392 const Id type{ctx.stage == Stage::TessellationControl ? ctx.output_f32 : ctx.input_f32};
393 const Id pointer{ctx.OpAccessChain(type, ctx.patches.at(index), element)};
394 return ctx.OpLoad(ctx.F32[1], pointer);
395}
396
397void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value) {
398 const Id pointer{[&] {
399 if (IR::IsGeneric(patch)) {
400 const u32 index{IR::GenericPatchIndex(patch)};
401 const Id element{ctx.Const(IR::GenericPatchElement(patch))};
402 return ctx.OpAccessChain(ctx.output_f32, ctx.patches.at(index), element);
403 }
404 switch (patch) {
405 case IR::Patch::TessellationLodLeft:
406 case IR::Patch::TessellationLodRight:
407 case IR::Patch::TessellationLodTop:
408 case IR::Patch::TessellationLodBottom: {
409 const u32 index{static_cast<u32>(patch) - u32(IR::Patch::TessellationLodLeft)};
410 const Id index_id{ctx.Const(index)};
411 return ctx.OpAccessChain(ctx.output_f32, ctx.output_tess_level_outer, index_id);
412 }
413 case IR::Patch::TessellationLodInteriorU:
414 return ctx.OpAccessChain(ctx.output_f32, ctx.output_tess_level_inner,
415 ctx.u32_zero_value);
416 case IR::Patch::TessellationLodInteriorV:
417 return ctx.OpAccessChain(ctx.output_f32, ctx.output_tess_level_inner, ctx.Const(1u));
418 default:
419 throw NotImplementedException("Patch {}", patch);
420 }
421 }()};
422 ctx.OpStore(pointer, value);
423}
424
425void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value) {
426 const Id component_id{ctx.Const(component)};
427 const Id pointer{ctx.OpAccessChain(ctx.output_f32, ctx.frag_color.at(index), component_id)};
428 ctx.OpStore(pointer, value);
429}
430
431void EmitSetSampleMask(EmitContext& ctx, Id value) {
432 ctx.OpStore(ctx.sample_mask, value);
433}
434
435void EmitSetFragDepth(EmitContext& ctx, Id value) {
436 ctx.OpStore(ctx.frag_depth, value);
437}
438
439void EmitGetZFlag(EmitContext&) {
440 throw NotImplementedException("SPIR-V Instruction");
441}
442
443void EmitGetSFlag(EmitContext&) {
444 throw NotImplementedException("SPIR-V Instruction");
445}
446
447void EmitGetCFlag(EmitContext&) {
448 throw NotImplementedException("SPIR-V Instruction");
449}
450
451void EmitGetOFlag(EmitContext&) {
452 throw NotImplementedException("SPIR-V Instruction");
453}
454
455void EmitSetZFlag(EmitContext&) {
456 throw NotImplementedException("SPIR-V Instruction");
457}
458
459void EmitSetSFlag(EmitContext&) {
460 throw NotImplementedException("SPIR-V Instruction");
461}
462
463void EmitSetCFlag(EmitContext&) {
464 throw NotImplementedException("SPIR-V Instruction");
465}
466
467void EmitSetOFlag(EmitContext&) {
468 throw NotImplementedException("SPIR-V Instruction");
469}
470
471Id EmitWorkgroupId(EmitContext& ctx) {
472 return ctx.OpLoad(ctx.U32[3], ctx.workgroup_id);
473}
474
475Id EmitLocalInvocationId(EmitContext& ctx) {
476 return ctx.OpLoad(ctx.U32[3], ctx.local_invocation_id);
477}
478
479Id EmitInvocationId(EmitContext& ctx) {
480 return ctx.OpLoad(ctx.U32[1], ctx.invocation_id);
481}
482
483Id EmitSampleId(EmitContext& ctx) {
484 return ctx.OpLoad(ctx.U32[1], ctx.sample_id);
485}
486
487Id EmitIsHelperInvocation(EmitContext& ctx) {
488 return ctx.OpLoad(ctx.U1, ctx.is_helper_invocation);
489}
490
491Id EmitYDirection(EmitContext& ctx) {
492 return ctx.Const(ctx.runtime_info.y_negate ? -1.0f : 1.0f);
493}
494
495Id EmitLoadLocal(EmitContext& ctx, Id word_offset) {
496 const Id pointer{ctx.OpAccessChain(ctx.private_u32, ctx.local_memory, word_offset)};
497 return ctx.OpLoad(ctx.U32[1], pointer);
498}
499
500void EmitWriteLocal(EmitContext& ctx, Id word_offset, Id value) {
501 const Id pointer{ctx.OpAccessChain(ctx.private_u32, ctx.local_memory, word_offset)};
502 ctx.OpStore(pointer, value);
503}
504
505} // 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
new file mode 100644
index 000000000..d33486f28
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
@@ -0,0 +1,28 @@
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 "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7
8namespace Shader::Backend::SPIRV {
9
10void EmitJoin(EmitContext&) {
11 throw NotImplementedException("Join shouldn't be emitted");
12}
13
14void EmitDemoteToHelperInvocation(EmitContext& ctx) {
15 if (ctx.profile.support_demote_to_helper_invocation) {
16 ctx.OpDemoteToHelperInvocationEXT();
17 } else {
18 const Id kill_label{ctx.OpLabel()};
19 const Id impossible_label{ctx.OpLabel()};
20 ctx.OpSelectionMerge(impossible_label, spv::SelectionControlMask::MaskNone);
21 ctx.OpBranchConditional(ctx.true_value, kill_label, impossible_label);
22 ctx.AddLabel(kill_label);
23 ctx.OpKill();
24 ctx.AddLabel(impossible_label);
25 }
26}
27
28} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp
new file mode 100644
index 000000000..fd42b7a16
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp
@@ -0,0 +1,269 @@
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 "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7
8namespace Shader::Backend::SPIRV {
9namespace {
10Id ExtractU16(EmitContext& ctx, Id value) {
11 if (ctx.profile.support_int16) {
12 return ctx.OpUConvert(ctx.U16, value);
13 } else {
14 return ctx.OpBitFieldUExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(16u));
15 }
16}
17
18Id ExtractS16(EmitContext& ctx, Id value) {
19 if (ctx.profile.support_int16) {
20 return ctx.OpSConvert(ctx.S16, value);
21 } else {
22 return ctx.OpBitFieldSExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(16u));
23 }
24}
25
26Id ExtractU8(EmitContext& ctx, Id value) {
27 if (ctx.profile.support_int8) {
28 return ctx.OpUConvert(ctx.U8, value);
29 } else {
30 return ctx.OpBitFieldUExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(8u));
31 }
32}
33
34Id ExtractS8(EmitContext& ctx, Id value) {
35 if (ctx.profile.support_int8) {
36 return ctx.OpSConvert(ctx.S8, value);
37 } else {
38 return ctx.OpBitFieldSExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(8u));
39 }
40}
41} // Anonymous namespace
42
43Id EmitConvertS16F16(EmitContext& ctx, Id value) {
44 if (ctx.profile.support_int16) {
45 return ctx.OpSConvert(ctx.U32[1], ctx.OpConvertFToS(ctx.U16, value));
46 } else {
47 return ExtractS16(ctx, ctx.OpConvertFToS(ctx.U32[1], value));
48 }
49}
50
51Id EmitConvertS16F32(EmitContext& ctx, Id value) {
52 if (ctx.profile.support_int16) {
53 return ctx.OpSConvert(ctx.U32[1], ctx.OpConvertFToS(ctx.U16, value));
54 } else {
55 return ExtractS16(ctx, ctx.OpConvertFToS(ctx.U32[1], value));
56 }
57}
58
59Id EmitConvertS16F64(EmitContext& ctx, Id value) {
60 if (ctx.profile.support_int16) {
61 return ctx.OpSConvert(ctx.U32[1], ctx.OpConvertFToS(ctx.U16, value));
62 } else {
63 return ExtractS16(ctx, ctx.OpConvertFToS(ctx.U32[1], value));
64 }
65}
66
67Id EmitConvertS32F16(EmitContext& ctx, Id value) {
68 return ctx.OpConvertFToS(ctx.U32[1], value);
69}
70
71Id EmitConvertS32F32(EmitContext& ctx, Id value) {
72 if (ctx.profile.has_broken_signed_operations) {
73 return ctx.OpBitcast(ctx.U32[1], ctx.OpConvertFToS(ctx.S32[1], value));
74 } else {
75 return ctx.OpConvertFToS(ctx.U32[1], value);
76 }
77}
78
79Id EmitConvertS32F64(EmitContext& ctx, Id value) {
80 return ctx.OpConvertFToS(ctx.U32[1], value);
81}
82
83Id EmitConvertS64F16(EmitContext& ctx, Id value) {
84 return ctx.OpConvertFToS(ctx.U64, value);
85}
86
87Id EmitConvertS64F32(EmitContext& ctx, Id value) {
88 return ctx.OpConvertFToS(ctx.U64, value);
89}
90
91Id EmitConvertS64F64(EmitContext& ctx, Id value) {
92 return ctx.OpConvertFToS(ctx.U64, value);
93}
94
95Id EmitConvertU16F16(EmitContext& ctx, Id value) {
96 if (ctx.profile.support_int16) {
97 return ctx.OpUConvert(ctx.U32[1], ctx.OpConvertFToU(ctx.U16, value));
98 } else {
99 return ExtractU16(ctx, ctx.OpConvertFToU(ctx.U32[1], value));
100 }
101}
102
103Id EmitConvertU16F32(EmitContext& ctx, Id value) {
104 if (ctx.profile.support_int16) {
105 return ctx.OpUConvert(ctx.U32[1], ctx.OpConvertFToU(ctx.U16, value));
106 } else {
107 return ExtractU16(ctx, ctx.OpConvertFToU(ctx.U32[1], value));
108 }
109}
110
111Id EmitConvertU16F64(EmitContext& ctx, Id value) {
112 if (ctx.profile.support_int16) {
113 return ctx.OpUConvert(ctx.U32[1], ctx.OpConvertFToU(ctx.U16, value));
114 } else {
115 return ExtractU16(ctx, ctx.OpConvertFToU(ctx.U32[1], value));
116 }
117}
118
119Id EmitConvertU32F16(EmitContext& ctx, Id value) {
120 return ctx.OpConvertFToU(ctx.U32[1], value);
121}
122
123Id EmitConvertU32F32(EmitContext& ctx, Id value) {
124 return ctx.OpConvertFToU(ctx.U32[1], value);
125}
126
127Id EmitConvertU32F64(EmitContext& ctx, Id value) {
128 return ctx.OpConvertFToU(ctx.U32[1], value);
129}
130
131Id EmitConvertU64F16(EmitContext& ctx, Id value) {
132 return ctx.OpConvertFToU(ctx.U64, value);
133}
134
135Id EmitConvertU64F32(EmitContext& ctx, Id value) {
136 return ctx.OpConvertFToU(ctx.U64, value);
137}
138
139Id EmitConvertU64F64(EmitContext& ctx, Id value) {
140 return ctx.OpConvertFToU(ctx.U64, value);
141}
142
143Id EmitConvertU64U32(EmitContext& ctx, Id value) {
144 return ctx.OpUConvert(ctx.U64, value);
145}
146
147Id EmitConvertU32U64(EmitContext& ctx, Id value) {
148 return ctx.OpUConvert(ctx.U32[1], value);
149}
150
151Id EmitConvertF16F32(EmitContext& ctx, Id value) {
152 return ctx.OpFConvert(ctx.F16[1], value);
153}
154
155Id EmitConvertF32F16(EmitContext& ctx, Id value) {
156 return ctx.OpFConvert(ctx.F32[1], value);
157}
158
159Id EmitConvertF32F64(EmitContext& ctx, Id value) {
160 return ctx.OpFConvert(ctx.F32[1], value);
161}
162
163Id EmitConvertF64F32(EmitContext& ctx, Id value) {
164 return ctx.OpFConvert(ctx.F64[1], value);
165}
166
167Id EmitConvertF16S8(EmitContext& ctx, Id value) {
168 return ctx.OpConvertSToF(ctx.F16[1], ExtractS8(ctx, value));
169}
170
171Id EmitConvertF16S16(EmitContext& ctx, Id value) {
172 return ctx.OpConvertSToF(ctx.F16[1], ExtractS16(ctx, value));
173}
174
175Id EmitConvertF16S32(EmitContext& ctx, Id value) {
176 return ctx.OpConvertSToF(ctx.F16[1], value);
177}
178
179Id EmitConvertF16S64(EmitContext& ctx, Id value) {
180 return ctx.OpConvertSToF(ctx.F16[1], value);
181}
182
183Id EmitConvertF16U8(EmitContext& ctx, Id value) {
184 return ctx.OpConvertUToF(ctx.F16[1], ExtractU8(ctx, value));
185}
186
187Id EmitConvertF16U16(EmitContext& ctx, Id value) {
188 return ctx.OpConvertUToF(ctx.F16[1], ExtractU16(ctx, value));
189}
190
191Id EmitConvertF16U32(EmitContext& ctx, Id value) {
192 return ctx.OpConvertUToF(ctx.F16[1], value);
193}
194
195Id EmitConvertF16U64(EmitContext& ctx, Id value) {
196 return ctx.OpConvertUToF(ctx.F16[1], value);
197}
198
199Id EmitConvertF32S8(EmitContext& ctx, Id value) {
200 return ctx.OpConvertSToF(ctx.F32[1], ExtractS8(ctx, value));
201}
202
203Id EmitConvertF32S16(EmitContext& ctx, Id value) {
204 return ctx.OpConvertSToF(ctx.F32[1], ExtractS16(ctx, value));
205}
206
207Id EmitConvertF32S32(EmitContext& ctx, Id value) {
208 if (ctx.profile.has_broken_signed_operations) {
209 value = ctx.OpBitcast(ctx.S32[1], value);
210 }
211 return ctx.OpConvertSToF(ctx.F32[1], value);
212}
213
214Id EmitConvertF32S64(EmitContext& ctx, Id value) {
215 return ctx.OpConvertSToF(ctx.F32[1], value);
216}
217
218Id EmitConvertF32U8(EmitContext& ctx, Id value) {
219 return ctx.OpConvertUToF(ctx.F32[1], ExtractU8(ctx, value));
220}
221
222Id EmitConvertF32U16(EmitContext& ctx, Id value) {
223 return ctx.OpConvertUToF(ctx.F32[1], ExtractU16(ctx, value));
224}
225
226Id EmitConvertF32U32(EmitContext& ctx, Id value) {
227 return ctx.OpConvertUToF(ctx.F32[1], value);
228}
229
230Id EmitConvertF32U64(EmitContext& ctx, Id value) {
231 return ctx.OpConvertUToF(ctx.F32[1], value);
232}
233
234Id EmitConvertF64S8(EmitContext& ctx, Id value) {
235 return ctx.OpConvertSToF(ctx.F64[1], ExtractS8(ctx, value));
236}
237
238Id EmitConvertF64S16(EmitContext& ctx, Id value) {
239 return ctx.OpConvertSToF(ctx.F64[1], ExtractS16(ctx, value));
240}
241
242Id EmitConvertF64S32(EmitContext& ctx, Id value) {
243 if (ctx.profile.has_broken_signed_operations) {
244 value = ctx.OpBitcast(ctx.S32[1], value);
245 }
246 return ctx.OpConvertSToF(ctx.F64[1], value);
247}
248
249Id EmitConvertF64S64(EmitContext& ctx, Id value) {
250 return ctx.OpConvertSToF(ctx.F64[1], value);
251}
252
253Id EmitConvertF64U8(EmitContext& ctx, Id value) {
254 return ctx.OpConvertUToF(ctx.F64[1], ExtractU8(ctx, value));
255}
256
257Id EmitConvertF64U16(EmitContext& ctx, Id value) {
258 return ctx.OpConvertUToF(ctx.F64[1], ExtractU16(ctx, value));
259}
260
261Id EmitConvertF64U32(EmitContext& ctx, Id value) {
262 return ctx.OpConvertUToF(ctx.F64[1], value);
263}
264
265Id EmitConvertF64U64(EmitContext& ctx, Id value) {
266 return ctx.OpConvertUToF(ctx.F64[1], value);
267}
268
269} // 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
new file mode 100644
index 000000000..61cf25f9c
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp
@@ -0,0 +1,396 @@
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 "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/frontend/ir/modifiers.h"
8
9namespace Shader::Backend::SPIRV {
10namespace {
11Id Decorate(EmitContext& ctx, IR::Inst* inst, Id op) {
12 const auto flags{inst->Flags<IR::FpControl>()};
13 if (flags.no_contraction) {
14 ctx.Decorate(op, spv::Decoration::NoContraction);
15 }
16 return op;
17}
18
19Id Clamp(EmitContext& ctx, Id type, Id value, Id zero, Id one) {
20 if (ctx.profile.has_broken_spirv_clamp) {
21 return ctx.OpFMin(type, ctx.OpFMax(type, value, zero), one);
22 } else {
23 return ctx.OpFClamp(type, value, zero, one);
24 }
25}
26
27Id FPOrdNotEqual(EmitContext& ctx, Id lhs, Id rhs) {
28 if (ctx.profile.ignore_nan_fp_comparisons) {
29 const Id comp{ctx.OpFOrdEqual(ctx.U1, lhs, rhs)};
30 const Id lhs_not_nan{ctx.OpLogicalNot(ctx.U1, ctx.OpIsNan(ctx.U1, lhs))};
31 const Id rhs_not_nan{ctx.OpLogicalNot(ctx.U1, ctx.OpIsNan(ctx.U1, rhs))};
32 return ctx.OpLogicalAnd(ctx.U1, ctx.OpLogicalAnd(ctx.U1, comp, lhs_not_nan), rhs_not_nan);
33 } else {
34 return ctx.OpFOrdNotEqual(ctx.U1, lhs, rhs);
35 }
36}
37
38Id FPUnordCompare(Id (EmitContext::*comp_func)(Id, Id, Id), EmitContext& ctx, Id lhs, Id rhs) {
39 if (ctx.profile.ignore_nan_fp_comparisons) {
40 const Id lhs_nan{ctx.OpIsNan(ctx.U1, lhs)};
41 const Id rhs_nan{ctx.OpIsNan(ctx.U1, rhs)};
42 const Id comp{(ctx.*comp_func)(ctx.U1, lhs, rhs)};
43 return ctx.OpLogicalOr(ctx.U1, ctx.OpLogicalOr(ctx.U1, comp, lhs_nan), rhs_nan);
44 } else {
45 return (ctx.*comp_func)(ctx.U1, lhs, rhs);
46 }
47}
48} // Anonymous namespace
49
50Id EmitFPAbs16(EmitContext& ctx, Id value) {
51 return ctx.OpFAbs(ctx.F16[1], value);
52}
53
54Id EmitFPAbs32(EmitContext& ctx, Id value) {
55 return ctx.OpFAbs(ctx.F32[1], value);
56}
57
58Id EmitFPAbs64(EmitContext& ctx, Id value) {
59 return ctx.OpFAbs(ctx.F64[1], value);
60}
61
62Id EmitFPAdd16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
63 return Decorate(ctx, inst, ctx.OpFAdd(ctx.F16[1], a, b));
64}
65
66Id EmitFPAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
67 return Decorate(ctx, inst, ctx.OpFAdd(ctx.F32[1], a, b));
68}
69
70Id EmitFPAdd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
71 return Decorate(ctx, inst, ctx.OpFAdd(ctx.F64[1], a, b));
72}
73
74Id EmitFPFma16(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
75 return Decorate(ctx, inst, ctx.OpFma(ctx.F16[1], a, b, c));
76}
77
78Id EmitFPFma32(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
79 return Decorate(ctx, inst, ctx.OpFma(ctx.F32[1], a, b, c));
80}
81
82Id EmitFPFma64(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
83 return Decorate(ctx, inst, ctx.OpFma(ctx.F64[1], a, b, c));
84}
85
86Id EmitFPMax32(EmitContext& ctx, Id a, Id b) {
87 return ctx.OpFMax(ctx.F32[1], a, b);
88}
89
90Id EmitFPMax64(EmitContext& ctx, Id a, Id b) {
91 return ctx.OpFMax(ctx.F64[1], a, b);
92}
93
94Id EmitFPMin32(EmitContext& ctx, Id a, Id b) {
95 return ctx.OpFMin(ctx.F32[1], a, b);
96}
97
98Id EmitFPMin64(EmitContext& ctx, Id a, Id b) {
99 return ctx.OpFMin(ctx.F64[1], a, b);
100}
101
102Id EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
103 return Decorate(ctx, inst, ctx.OpFMul(ctx.F16[1], a, b));
104}
105
106Id EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
107 return Decorate(ctx, inst, ctx.OpFMul(ctx.F32[1], a, b));
108}
109
110Id EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
111 return Decorate(ctx, inst, ctx.OpFMul(ctx.F64[1], a, b));
112}
113
114Id EmitFPNeg16(EmitContext& ctx, Id value) {
115 return ctx.OpFNegate(ctx.F16[1], value);
116}
117
118Id EmitFPNeg32(EmitContext& ctx, Id value) {
119 return ctx.OpFNegate(ctx.F32[1], value);
120}
121
122Id EmitFPNeg64(EmitContext& ctx, Id value) {
123 return ctx.OpFNegate(ctx.F64[1], value);
124}
125
126Id EmitFPSin(EmitContext& ctx, Id value) {
127 return ctx.OpSin(ctx.F32[1], value);
128}
129
130Id EmitFPCos(EmitContext& ctx, Id value) {
131 return ctx.OpCos(ctx.F32[1], value);
132}
133
134Id EmitFPExp2(EmitContext& ctx, Id value) {
135 return ctx.OpExp2(ctx.F32[1], value);
136}
137
138Id EmitFPLog2(EmitContext& ctx, Id value) {
139 return ctx.OpLog2(ctx.F32[1], value);
140}
141
142Id EmitFPRecip32(EmitContext& ctx, Id value) {
143 return ctx.OpFDiv(ctx.F32[1], ctx.Const(1.0f), value);
144}
145
146Id EmitFPRecip64(EmitContext& ctx, Id value) {
147 return ctx.OpFDiv(ctx.F64[1], ctx.Constant(ctx.F64[1], 1.0f), value);
148}
149
150Id EmitFPRecipSqrt32(EmitContext& ctx, Id value) {
151 return ctx.OpInverseSqrt(ctx.F32[1], value);
152}
153
154Id EmitFPRecipSqrt64(EmitContext& ctx, Id value) {
155 return ctx.OpInverseSqrt(ctx.F64[1], value);
156}
157
158Id EmitFPSqrt(EmitContext& ctx, Id value) {
159 return ctx.OpSqrt(ctx.F32[1], value);
160}
161
162Id EmitFPSaturate16(EmitContext& ctx, Id value) {
163 const Id zero{ctx.Constant(ctx.F16[1], u16{0})};
164 const Id one{ctx.Constant(ctx.F16[1], u16{0x3c00})};
165 return Clamp(ctx, ctx.F16[1], value, zero, one);
166}
167
168Id EmitFPSaturate32(EmitContext& ctx, Id value) {
169 const Id zero{ctx.Const(f32{0.0})};
170 const Id one{ctx.Const(f32{1.0})};
171 return Clamp(ctx, ctx.F32[1], value, zero, one);
172}
173
174Id EmitFPSaturate64(EmitContext& ctx, Id value) {
175 const Id zero{ctx.Constant(ctx.F64[1], f64{0.0})};
176 const Id one{ctx.Constant(ctx.F64[1], f64{1.0})};
177 return Clamp(ctx, ctx.F64[1], value, zero, one);
178}
179
180Id EmitFPClamp16(EmitContext& ctx, Id value, Id min_value, Id max_value) {
181 return Clamp(ctx, ctx.F16[1], value, min_value, max_value);
182}
183
184Id EmitFPClamp32(EmitContext& ctx, Id value, Id min_value, Id max_value) {
185 return Clamp(ctx, ctx.F32[1], value, min_value, max_value);
186}
187
188Id EmitFPClamp64(EmitContext& ctx, Id value, Id min_value, Id max_value) {
189 return Clamp(ctx, ctx.F64[1], value, min_value, max_value);
190}
191
192Id EmitFPRoundEven16(EmitContext& ctx, Id value) {
193 return ctx.OpRoundEven(ctx.F16[1], value);
194}
195
196Id EmitFPRoundEven32(EmitContext& ctx, Id value) {
197 return ctx.OpRoundEven(ctx.F32[1], value);
198}
199
200Id EmitFPRoundEven64(EmitContext& ctx, Id value) {
201 return ctx.OpRoundEven(ctx.F64[1], value);
202}
203
204Id EmitFPFloor16(EmitContext& ctx, Id value) {
205 return ctx.OpFloor(ctx.F16[1], value);
206}
207
208Id EmitFPFloor32(EmitContext& ctx, Id value) {
209 return ctx.OpFloor(ctx.F32[1], value);
210}
211
212Id EmitFPFloor64(EmitContext& ctx, Id value) {
213 return ctx.OpFloor(ctx.F64[1], value);
214}
215
216Id EmitFPCeil16(EmitContext& ctx, Id value) {
217 return ctx.OpCeil(ctx.F16[1], value);
218}
219
220Id EmitFPCeil32(EmitContext& ctx, Id value) {
221 return ctx.OpCeil(ctx.F32[1], value);
222}
223
224Id EmitFPCeil64(EmitContext& ctx, Id value) {
225 return ctx.OpCeil(ctx.F64[1], value);
226}
227
228Id EmitFPTrunc16(EmitContext& ctx, Id value) {
229 return ctx.OpTrunc(ctx.F16[1], value);
230}
231
232Id EmitFPTrunc32(EmitContext& ctx, Id value) {
233 return ctx.OpTrunc(ctx.F32[1], value);
234}
235
236Id EmitFPTrunc64(EmitContext& ctx, Id value) {
237 return ctx.OpTrunc(ctx.F64[1], value);
238}
239
240Id EmitFPOrdEqual16(EmitContext& ctx, Id lhs, Id rhs) {
241 return ctx.OpFOrdEqual(ctx.U1, lhs, rhs);
242}
243
244Id EmitFPOrdEqual32(EmitContext& ctx, Id lhs, Id rhs) {
245 return ctx.OpFOrdEqual(ctx.U1, lhs, rhs);
246}
247
248Id EmitFPOrdEqual64(EmitContext& ctx, Id lhs, Id rhs) {
249 return ctx.OpFOrdEqual(ctx.U1, lhs, rhs);
250}
251
252Id EmitFPUnordEqual16(EmitContext& ctx, Id lhs, Id rhs) {
253 return FPUnordCompare(&EmitContext::OpFUnordEqual, ctx, lhs, rhs);
254}
255
256Id EmitFPUnordEqual32(EmitContext& ctx, Id lhs, Id rhs) {
257 return FPUnordCompare(&EmitContext::OpFUnordEqual, ctx, lhs, rhs);
258}
259
260Id EmitFPUnordEqual64(EmitContext& ctx, Id lhs, Id rhs) {
261 return FPUnordCompare(&EmitContext::OpFUnordEqual, ctx, lhs, rhs);
262}
263
264Id EmitFPOrdNotEqual16(EmitContext& ctx, Id lhs, Id rhs) {
265 return FPOrdNotEqual(ctx, lhs, rhs);
266}
267
268Id EmitFPOrdNotEqual32(EmitContext& ctx, Id lhs, Id rhs) {
269 return FPOrdNotEqual(ctx, lhs, rhs);
270}
271
272Id EmitFPOrdNotEqual64(EmitContext& ctx, Id lhs, Id rhs) {
273 return FPOrdNotEqual(ctx, lhs, rhs);
274}
275
276Id EmitFPUnordNotEqual16(EmitContext& ctx, Id lhs, Id rhs) {
277 return ctx.OpFUnordNotEqual(ctx.U1, lhs, rhs);
278}
279
280Id EmitFPUnordNotEqual32(EmitContext& ctx, Id lhs, Id rhs) {
281 return ctx.OpFUnordNotEqual(ctx.U1, lhs, rhs);
282}
283
284Id EmitFPUnordNotEqual64(EmitContext& ctx, Id lhs, Id rhs) {
285 return ctx.OpFUnordNotEqual(ctx.U1, lhs, rhs);
286}
287
288Id EmitFPOrdLessThan16(EmitContext& ctx, Id lhs, Id rhs) {
289 return ctx.OpFOrdLessThan(ctx.U1, lhs, rhs);
290}
291
292Id EmitFPOrdLessThan32(EmitContext& ctx, Id lhs, Id rhs) {
293 return ctx.OpFOrdLessThan(ctx.U1, lhs, rhs);
294}
295
296Id EmitFPOrdLessThan64(EmitContext& ctx, Id lhs, Id rhs) {
297 return ctx.OpFOrdLessThan(ctx.U1, lhs, rhs);
298}
299
300Id EmitFPUnordLessThan16(EmitContext& ctx, Id lhs, Id rhs) {
301 return FPUnordCompare(&EmitContext::OpFUnordLessThan, ctx, lhs, rhs);
302}
303
304Id EmitFPUnordLessThan32(EmitContext& ctx, Id lhs, Id rhs) {
305 return FPUnordCompare(&EmitContext::OpFUnordLessThan, ctx, lhs, rhs);
306}
307
308Id EmitFPUnordLessThan64(EmitContext& ctx, Id lhs, Id rhs) {
309 return FPUnordCompare(&EmitContext::OpFUnordLessThan, ctx, lhs, rhs);
310}
311
312Id EmitFPOrdGreaterThan16(EmitContext& ctx, Id lhs, Id rhs) {
313 return ctx.OpFOrdGreaterThan(ctx.U1, lhs, rhs);
314}
315
316Id EmitFPOrdGreaterThan32(EmitContext& ctx, Id lhs, Id rhs) {
317 return ctx.OpFOrdGreaterThan(ctx.U1, lhs, rhs);
318}
319
320Id EmitFPOrdGreaterThan64(EmitContext& ctx, Id lhs, Id rhs) {
321 return ctx.OpFOrdGreaterThan(ctx.U1, lhs, rhs);
322}
323
324Id EmitFPUnordGreaterThan16(EmitContext& ctx, Id lhs, Id rhs) {
325 return FPUnordCompare(&EmitContext::OpFUnordGreaterThan, ctx, lhs, rhs);
326}
327
328Id EmitFPUnordGreaterThan32(EmitContext& ctx, Id lhs, Id rhs) {
329 return FPUnordCompare(&EmitContext::OpFUnordGreaterThan, ctx, lhs, rhs);
330}
331
332Id EmitFPUnordGreaterThan64(EmitContext& ctx, Id lhs, Id rhs) {
333 return FPUnordCompare(&EmitContext::OpFUnordGreaterThan, ctx, lhs, rhs);
334}
335
336Id EmitFPOrdLessThanEqual16(EmitContext& ctx, Id lhs, Id rhs) {
337 return ctx.OpFOrdLessThanEqual(ctx.U1, lhs, rhs);
338}
339
340Id EmitFPOrdLessThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
341 return ctx.OpFOrdLessThanEqual(ctx.U1, lhs, rhs);
342}
343
344Id EmitFPOrdLessThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
345 return ctx.OpFOrdLessThanEqual(ctx.U1, lhs, rhs);
346}
347
348Id EmitFPUnordLessThanEqual16(EmitContext& ctx, Id lhs, Id rhs) {
349 return FPUnordCompare(&EmitContext::OpFUnordLessThanEqual, ctx, lhs, rhs);
350}
351
352Id EmitFPUnordLessThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
353 return FPUnordCompare(&EmitContext::OpFUnordLessThanEqual, ctx, lhs, rhs);
354}
355
356Id EmitFPUnordLessThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
357 return FPUnordCompare(&EmitContext::OpFUnordLessThanEqual, ctx, lhs, rhs);
358}
359
360Id EmitFPOrdGreaterThanEqual16(EmitContext& ctx, Id lhs, Id rhs) {
361 return ctx.OpFOrdGreaterThanEqual(ctx.U1, lhs, rhs);
362}
363
364Id EmitFPOrdGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
365 return ctx.OpFOrdGreaterThanEqual(ctx.U1, lhs, rhs);
366}
367
368Id EmitFPOrdGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
369 return ctx.OpFOrdGreaterThanEqual(ctx.U1, lhs, rhs);
370}
371
372Id EmitFPUnordGreaterThanEqual16(EmitContext& ctx, Id lhs, Id rhs) {
373 return FPUnordCompare(&EmitContext::OpFUnordGreaterThanEqual, ctx, lhs, rhs);
374}
375
376Id EmitFPUnordGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
377 return FPUnordCompare(&EmitContext::OpFUnordGreaterThanEqual, ctx, lhs, rhs);
378}
379
380Id EmitFPUnordGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
381 return FPUnordCompare(&EmitContext::OpFUnordGreaterThanEqual, ctx, lhs, rhs);
382}
383
384Id EmitFPIsNan16(EmitContext& ctx, Id value) {
385 return ctx.OpIsNan(ctx.U1, value);
386}
387
388Id EmitFPIsNan32(EmitContext& ctx, Id value) {
389 return ctx.OpIsNan(ctx.U1, value);
390}
391
392Id EmitFPIsNan64(EmitContext& ctx, Id value) {
393 return ctx.OpIsNan(ctx.U1, value);
394}
395
396} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
new file mode 100644
index 000000000..3588f052b
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -0,0 +1,462 @@
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 <boost/container/static_vector.hpp>
6
7#include "shader_recompiler/backend/spirv/emit_spirv.h"
8#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
9#include "shader_recompiler/frontend/ir/modifiers.h"
10
11namespace Shader::Backend::SPIRV {
12namespace {
13class ImageOperands {
14public:
15 explicit ImageOperands(EmitContext& ctx, bool has_bias, bool has_lod, bool has_lod_clamp,
16 Id lod, const IR::Value& offset) {
17 if (has_bias) {
18 const Id bias{has_lod_clamp ? ctx.OpCompositeExtract(ctx.F32[1], lod, 0) : lod};
19 Add(spv::ImageOperandsMask::Bias, bias);
20 }
21 if (has_lod) {
22 const Id lod_value{has_lod_clamp ? ctx.OpCompositeExtract(ctx.F32[1], lod, 0) : lod};
23 Add(spv::ImageOperandsMask::Lod, lod_value);
24 }
25 AddOffset(ctx, offset);
26 if (has_lod_clamp) {
27 const Id lod_clamp{has_bias ? ctx.OpCompositeExtract(ctx.F32[1], lod, 1) : lod};
28 Add(spv::ImageOperandsMask::MinLod, lod_clamp);
29 }
30 }
31
32 explicit ImageOperands(EmitContext& ctx, const IR::Value& offset, const IR::Value& offset2) {
33 if (offset2.IsEmpty()) {
34 if (offset.IsEmpty()) {
35 return;
36 }
37 Add(spv::ImageOperandsMask::Offset, ctx.Def(offset));
38 return;
39 }
40 const std::array values{offset.InstRecursive(), offset2.InstRecursive()};
41 if (!values[0]->AreAllArgsImmediates() || !values[1]->AreAllArgsImmediates()) {
42 LOG_WARNING(Shader_SPIRV, "Not all arguments in PTP are immediate, ignoring");
43 return;
44 }
45 const IR::Opcode opcode{values[0]->GetOpcode()};
46 if (opcode != values[1]->GetOpcode() || opcode != IR::Opcode::CompositeConstructU32x4) {
47 throw LogicError("Invalid PTP arguments");
48 }
49 auto read{[&](unsigned int a, unsigned int b) { return values[a]->Arg(b).U32(); }};
50
51 const Id offsets{ctx.ConstantComposite(
52 ctx.TypeArray(ctx.U32[2], ctx.Const(4U)), ctx.Const(read(0, 0), read(0, 1)),
53 ctx.Const(read(0, 2), read(0, 3)), ctx.Const(read(1, 0), read(1, 1)),
54 ctx.Const(read(1, 2), read(1, 3)))};
55 Add(spv::ImageOperandsMask::ConstOffsets, offsets);
56 }
57
58 explicit ImageOperands(Id offset, Id lod, Id ms) {
59 if (Sirit::ValidId(lod)) {
60 Add(spv::ImageOperandsMask::Lod, lod);
61 }
62 if (Sirit::ValidId(offset)) {
63 Add(spv::ImageOperandsMask::Offset, offset);
64 }
65 if (Sirit::ValidId(ms)) {
66 Add(spv::ImageOperandsMask::Sample, ms);
67 }
68 }
69
70 explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivates, u32 num_derivates,
71 Id offset, Id lod_clamp) {
72 if (!Sirit::ValidId(derivates)) {
73 throw LogicError("Derivates must be present");
74 }
75 boost::container::static_vector<Id, 3> deriv_x_accum;
76 boost::container::static_vector<Id, 3> deriv_y_accum;
77 for (u32 i = 0; i < num_derivates; ++i) {
78 deriv_x_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivates, i * 2));
79 deriv_y_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivates, i * 2 + 1));
80 }
81 const Id derivates_X{ctx.OpCompositeConstruct(
82 ctx.F32[num_derivates], std::span{deriv_x_accum.data(), deriv_x_accum.size()})};
83 const Id derivates_Y{ctx.OpCompositeConstruct(
84 ctx.F32[num_derivates], std::span{deriv_y_accum.data(), deriv_y_accum.size()})};
85 Add(spv::ImageOperandsMask::Grad, derivates_X, derivates_Y);
86 if (Sirit::ValidId(offset)) {
87 Add(spv::ImageOperandsMask::Offset, offset);
88 }
89 if (has_lod_clamp) {
90 Add(spv::ImageOperandsMask::MinLod, lod_clamp);
91 }
92 }
93
94 std::span<const Id> Span() const noexcept {
95 return std::span{operands.data(), operands.size()};
96 }
97
98 std::optional<spv::ImageOperandsMask> MaskOptional() const noexcept {
99 return mask != spv::ImageOperandsMask{} ? std::make_optional(mask) : std::nullopt;
100 }
101
102 spv::ImageOperandsMask Mask() const noexcept {
103 return mask;
104 }
105
106private:
107 void AddOffset(EmitContext& ctx, const IR::Value& offset) {
108 if (offset.IsEmpty()) {
109 return;
110 }
111 if (offset.IsImmediate()) {
112 Add(spv::ImageOperandsMask::ConstOffset, ctx.SConst(static_cast<s32>(offset.U32())));
113 return;
114 }
115 IR::Inst* const inst{offset.InstRecursive()};
116 if (inst->AreAllArgsImmediates()) {
117 switch (inst->GetOpcode()) {
118 case IR::Opcode::CompositeConstructU32x2:
119 Add(spv::ImageOperandsMask::ConstOffset,
120 ctx.SConst(static_cast<s32>(inst->Arg(0).U32()),
121 static_cast<s32>(inst->Arg(1).U32())));
122 return;
123 case IR::Opcode::CompositeConstructU32x3:
124 Add(spv::ImageOperandsMask::ConstOffset,
125 ctx.SConst(static_cast<s32>(inst->Arg(0).U32()),
126 static_cast<s32>(inst->Arg(1).U32()),
127 static_cast<s32>(inst->Arg(2).U32())));
128 return;
129 case IR::Opcode::CompositeConstructU32x4:
130 Add(spv::ImageOperandsMask::ConstOffset,
131 ctx.SConst(static_cast<s32>(inst->Arg(0).U32()),
132 static_cast<s32>(inst->Arg(1).U32()),
133 static_cast<s32>(inst->Arg(2).U32()),
134 static_cast<s32>(inst->Arg(3).U32())));
135 return;
136 default:
137 break;
138 }
139 }
140 Add(spv::ImageOperandsMask::Offset, ctx.Def(offset));
141 }
142
143 void Add(spv::ImageOperandsMask new_mask, Id value) {
144 mask = static_cast<spv::ImageOperandsMask>(static_cast<unsigned>(mask) |
145 static_cast<unsigned>(new_mask));
146 operands.push_back(value);
147 }
148
149 void Add(spv::ImageOperandsMask new_mask, Id value_1, Id value_2) {
150 mask = static_cast<spv::ImageOperandsMask>(static_cast<unsigned>(mask) |
151 static_cast<unsigned>(new_mask));
152 operands.push_back(value_1);
153 operands.push_back(value_2);
154 }
155
156 boost::container::static_vector<Id, 4> operands;
157 spv::ImageOperandsMask mask{};
158};
159
160Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR::Value& index) {
161 const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
162 if (def.count > 1) {
163 const Id pointer{ctx.OpAccessChain(def.pointer_type, def.id, ctx.Def(index))};
164 return ctx.OpLoad(def.sampled_type, pointer);
165 } else {
166 return ctx.OpLoad(def.sampled_type, def.id);
167 }
168}
169
170Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& index) {
171 if (!index.IsImmediate() || index.U32() != 0) {
172 throw NotImplementedException("Indirect image indexing");
173 }
174 if (info.type == TextureType::Buffer) {
175 const TextureBufferDefinition& def{ctx.texture_buffers.at(info.descriptor_index)};
176 if (def.count > 1) {
177 throw NotImplementedException("Indirect texture sample");
178 }
179 const Id sampler_id{def.id};
180 const Id id{ctx.OpLoad(ctx.sampled_texture_buffer_type, sampler_id)};
181 return ctx.OpImage(ctx.image_buffer_type, id);
182 } else {
183 const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
184 if (def.count > 1) {
185 throw NotImplementedException("Indirect texture sample");
186 }
187 return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, def.id));
188 }
189}
190
191Id Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
192 if (!index.IsImmediate() || index.U32() != 0) {
193 throw NotImplementedException("Indirect image indexing");
194 }
195 if (info.type == TextureType::Buffer) {
196 const ImageBufferDefinition def{ctx.image_buffers.at(info.descriptor_index)};
197 return ctx.OpLoad(def.image_type, def.id);
198 } else {
199 const ImageDefinition def{ctx.images.at(info.descriptor_index)};
200 return ctx.OpLoad(def.image_type, def.id);
201 }
202}
203
204Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) {
205 const auto info{inst->Flags<IR::TextureInstInfo>()};
206 if (info.relaxed_precision != 0) {
207 ctx.Decorate(sample, spv::Decoration::RelaxedPrecision);
208 }
209 return sample;
210}
211
212template <typename MethodPtrType, typename... Args>
213Id Emit(MethodPtrType sparse_ptr, MethodPtrType non_sparse_ptr, EmitContext& ctx, IR::Inst* inst,
214 Id result_type, Args&&... args) {
215 IR::Inst* const sparse{inst->GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
216 if (!sparse) {
217 return Decorate(ctx, inst, (ctx.*non_sparse_ptr)(result_type, std::forward<Args>(args)...));
218 }
219 const Id struct_type{ctx.TypeStruct(ctx.U32[1], result_type)};
220 const Id sample{(ctx.*sparse_ptr)(struct_type, std::forward<Args>(args)...)};
221 const Id resident_code{ctx.OpCompositeExtract(ctx.U32[1], sample, 0U)};
222 sparse->SetDefinition(ctx.OpImageSparseTexelsResident(ctx.U1, resident_code));
223 sparse->Invalidate();
224 Decorate(ctx, inst, sample);
225 return ctx.OpCompositeExtract(result_type, sample, 1U);
226}
227} // Anonymous namespace
228
229Id EmitBindlessImageSampleImplicitLod(EmitContext&) {
230 throw LogicError("Unreachable instruction");
231}
232
233Id EmitBindlessImageSampleExplicitLod(EmitContext&) {
234 throw LogicError("Unreachable instruction");
235}
236
237Id EmitBindlessImageSampleDrefImplicitLod(EmitContext&) {
238 throw LogicError("Unreachable instruction");
239}
240
241Id EmitBindlessImageSampleDrefExplicitLod(EmitContext&) {
242 throw LogicError("Unreachable instruction");
243}
244
245Id EmitBindlessImageGather(EmitContext&) {
246 throw LogicError("Unreachable instruction");
247}
248
249Id EmitBindlessImageGatherDref(EmitContext&) {
250 throw LogicError("Unreachable instruction");
251}
252
253Id EmitBindlessImageFetch(EmitContext&) {
254 throw LogicError("Unreachable instruction");
255}
256
257Id EmitBindlessImageQueryDimensions(EmitContext&) {
258 throw LogicError("Unreachable instruction");
259}
260
261Id EmitBindlessImageQueryLod(EmitContext&) {
262 throw LogicError("Unreachable instruction");
263}
264
265Id EmitBindlessImageGradient(EmitContext&) {
266 throw LogicError("Unreachable instruction");
267}
268
269Id EmitBindlessImageRead(EmitContext&) {
270 throw LogicError("Unreachable instruction");
271}
272
273Id EmitBindlessImageWrite(EmitContext&) {
274 throw LogicError("Unreachable instruction");
275}
276
277Id EmitBoundImageSampleImplicitLod(EmitContext&) {
278 throw LogicError("Unreachable instruction");
279}
280
281Id EmitBoundImageSampleExplicitLod(EmitContext&) {
282 throw LogicError("Unreachable instruction");
283}
284
285Id EmitBoundImageSampleDrefImplicitLod(EmitContext&) {
286 throw LogicError("Unreachable instruction");
287}
288
289Id EmitBoundImageSampleDrefExplicitLod(EmitContext&) {
290 throw LogicError("Unreachable instruction");
291}
292
293Id EmitBoundImageGather(EmitContext&) {
294 throw LogicError("Unreachable instruction");
295}
296
297Id EmitBoundImageGatherDref(EmitContext&) {
298 throw LogicError("Unreachable instruction");
299}
300
301Id EmitBoundImageFetch(EmitContext&) {
302 throw LogicError("Unreachable instruction");
303}
304
305Id EmitBoundImageQueryDimensions(EmitContext&) {
306 throw LogicError("Unreachable instruction");
307}
308
309Id EmitBoundImageQueryLod(EmitContext&) {
310 throw LogicError("Unreachable instruction");
311}
312
313Id EmitBoundImageGradient(EmitContext&) {
314 throw LogicError("Unreachable instruction");
315}
316
317Id EmitBoundImageRead(EmitContext&) {
318 throw LogicError("Unreachable instruction");
319}
320
321Id EmitBoundImageWrite(EmitContext&) {
322 throw LogicError("Unreachable instruction");
323}
324
325Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
326 Id bias_lc, const IR::Value& offset) {
327 const auto info{inst->Flags<IR::TextureInstInfo>()};
328 if (ctx.stage == Stage::Fragment) {
329 const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0,
330 bias_lc, offset);
331 return Emit(&EmitContext::OpImageSparseSampleImplicitLod,
332 &EmitContext::OpImageSampleImplicitLod, ctx, inst, ctx.F32[4],
333 Texture(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
334 } else {
335 // We can't use implicit lods on non-fragment stages on SPIR-V. Maxwell hardware behaves as
336 // if the lod was explicitly zero. This may change on Turing with implicit compute
337 // derivatives
338 const Id lod{ctx.Const(0.0f)};
339 const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset);
340 return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
341 &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
342 Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
343 }
344}
345
346Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
347 Id lod, const IR::Value& offset) {
348 const auto info{inst->Flags<IR::TextureInstInfo>()};
349 const ImageOperands operands(ctx, false, true, false, lod, offset);
350 return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
351 &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
352 Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
353}
354
355Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
356 Id coords, Id dref, Id bias_lc, const IR::Value& offset) {
357 const auto info{inst->Flags<IR::TextureInstInfo>()};
358 const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0, bias_lc,
359 offset);
360 return Emit(&EmitContext::OpImageSparseSampleDrefImplicitLod,
361 &EmitContext::OpImageSampleDrefImplicitLod, ctx, inst, ctx.F32[1],
362 Texture(ctx, info, index), coords, dref, operands.MaskOptional(), operands.Span());
363}
364
365Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
366 Id coords, Id dref, Id lod, const IR::Value& offset) {
367 const auto info{inst->Flags<IR::TextureInstInfo>()};
368 const ImageOperands operands(ctx, false, true, false, lod, offset);
369 return Emit(&EmitContext::OpImageSparseSampleDrefExplicitLod,
370 &EmitContext::OpImageSampleDrefExplicitLod, ctx, inst, ctx.F32[1],
371 Texture(ctx, info, index), coords, dref, operands.Mask(), operands.Span());
372}
373
374Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
375 const IR::Value& offset, const IR::Value& offset2) {
376 const auto info{inst->Flags<IR::TextureInstInfo>()};
377 const ImageOperands operands(ctx, offset, offset2);
378 return Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst,
379 ctx.F32[4], Texture(ctx, info, index), coords, ctx.Const(info.gather_component),
380 operands.MaskOptional(), operands.Span());
381}
382
383Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
384 const IR::Value& offset, const IR::Value& offset2, Id dref) {
385 const auto info{inst->Flags<IR::TextureInstInfo>()};
386 const ImageOperands operands(ctx, offset, offset2);
387 return Emit(&EmitContext::OpImageSparseDrefGather, &EmitContext::OpImageDrefGather, ctx, inst,
388 ctx.F32[4], Texture(ctx, info, index), coords, dref, operands.MaskOptional(),
389 operands.Span());
390}
391
392Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
393 Id lod, Id ms) {
394 const auto info{inst->Flags<IR::TextureInstInfo>()};
395 if (info.type == TextureType::Buffer) {
396 lod = Id{};
397 }
398 const ImageOperands operands(offset, lod, ms);
399 return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
400 TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
401}
402
403Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod) {
404 const auto info{inst->Flags<IR::TextureInstInfo>()};
405 const Id image{TextureImage(ctx, info, index)};
406 const Id zero{ctx.u32_zero_value};
407 const auto mips{[&] { return ctx.OpImageQueryLevels(ctx.U32[1], image); }};
408 switch (info.type) {
409 case TextureType::Color1D:
410 return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[1], image, lod),
411 zero, zero, mips());
412 case TextureType::ColorArray1D:
413 case TextureType::Color2D:
414 case TextureType::ColorCube:
415 return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[2], image, lod),
416 zero, mips());
417 case TextureType::ColorArray2D:
418 case TextureType::Color3D:
419 case TextureType::ColorArrayCube:
420 return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[3], image, lod),
421 mips());
422 case TextureType::Buffer:
423 return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySize(ctx.U32[1], image), zero,
424 zero, mips());
425 }
426 throw LogicError("Unspecified image type {}", info.type.Value());
427}
428
429Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {
430 const auto info{inst->Flags<IR::TextureInstInfo>()};
431 const Id zero{ctx.f32_zero_value};
432 const Id sampler{Texture(ctx, info, index)};
433 return ctx.OpCompositeConstruct(ctx.F32[4], ctx.OpImageQueryLod(ctx.F32[2], sampler, coords),
434 zero, zero);
435}
436
437Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
438 Id derivates, Id offset, Id lod_clamp) {
439 const auto info{inst->Flags<IR::TextureInstInfo>()};
440 const ImageOperands operands(ctx, info.has_lod_clamp != 0, derivates, info.num_derivates,
441 offset, lod_clamp);
442 return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
443 &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
444 Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
445}
446
447Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {
448 const auto info{inst->Flags<IR::TextureInstInfo>()};
449 if (info.image_format == ImageFormat::Typeless && !ctx.profile.support_typeless_image_loads) {
450 LOG_WARNING(Shader_SPIRV, "Typeless image read not supported by host");
451 return ctx.ConstantNull(ctx.U32[4]);
452 }
453 return Emit(&EmitContext::OpImageSparseRead, &EmitContext::OpImageRead, ctx, inst, ctx.U32[4],
454 Image(ctx, index, info), coords, std::nullopt, std::span<const Id>{});
455}
456
457void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color) {
458 const auto info{inst->Flags<IR::TextureInstInfo>()};
459 ctx.OpImageWrite(Image(ctx, index, info), coords, color);
460}
461
462} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp
new file mode 100644
index 000000000..d7f1a365a
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp
@@ -0,0 +1,183 @@
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 "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/frontend/ir/modifiers.h"
8
9namespace Shader::Backend::SPIRV {
10namespace {
11Id Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
12 if (!index.IsImmediate()) {
13 throw NotImplementedException("Indirect image indexing");
14 }
15 if (info.type == TextureType::Buffer) {
16 const ImageBufferDefinition def{ctx.image_buffers.at(index.U32())};
17 return def.id;
18 } else {
19 const ImageDefinition def{ctx.images.at(index.U32())};
20 return def.id;
21 }
22}
23
24std::pair<Id, Id> AtomicArgs(EmitContext& ctx) {
25 const Id scope{ctx.Const(static_cast<u32>(spv::Scope::Device))};
26 const Id semantics{ctx.u32_zero_value};
27 return {scope, semantics};
28}
29
30Id ImageAtomicU32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id value,
31 Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
32 const auto info{inst->Flags<IR::TextureInstInfo>()};
33 const Id image{Image(ctx, index, info)};
34 const Id pointer{ctx.OpImageTexelPointer(ctx.image_u32, image, coords, ctx.Const(0U))};
35 const auto [scope, semantics]{AtomicArgs(ctx)};
36 return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value);
37}
38} // Anonymous namespace
39
40Id EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
41 Id value) {
42 return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicIAdd);
43}
44
45Id EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
46 Id value) {
47 return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicSMin);
48}
49
50Id EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
51 Id value) {
52 return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicUMin);
53}
54
55Id EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
56 Id value) {
57 return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicSMax);
58}
59
60Id EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
61 Id value) {
62 return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicUMax);
63}
64
65Id EmitImageAtomicInc32(EmitContext&, IR::Inst*, const IR::Value&, Id, Id) {
66 // TODO: This is not yet implemented
67 throw NotImplementedException("SPIR-V Instruction");
68}
69
70Id EmitImageAtomicDec32(EmitContext&, IR::Inst*, const IR::Value&, Id, Id) {
71 // TODO: This is not yet implemented
72 throw NotImplementedException("SPIR-V Instruction");
73}
74
75Id EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
76 Id value) {
77 return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicAnd);
78}
79
80Id EmitImageAtomicOr32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
81 Id value) {
82 return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicOr);
83}
84
85Id EmitImageAtomicXor32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
86 Id value) {
87 return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicXor);
88}
89
90Id EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
91 Id value) {
92 return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicExchange);
93}
94
95Id EmitBindlessImageAtomicIAdd32(EmitContext&) {
96 throw NotImplementedException("SPIR-V Instruction");
97}
98
99Id EmitBindlessImageAtomicSMin32(EmitContext&) {
100 throw NotImplementedException("SPIR-V Instruction");
101}
102
103Id EmitBindlessImageAtomicUMin32(EmitContext&) {
104 throw NotImplementedException("SPIR-V Instruction");
105}
106
107Id EmitBindlessImageAtomicSMax32(EmitContext&) {
108 throw NotImplementedException("SPIR-V Instruction");
109}
110
111Id EmitBindlessImageAtomicUMax32(EmitContext&) {
112 throw NotImplementedException("SPIR-V Instruction");
113}
114
115Id EmitBindlessImageAtomicInc32(EmitContext&) {
116 throw NotImplementedException("SPIR-V Instruction");
117}
118
119Id EmitBindlessImageAtomicDec32(EmitContext&) {
120 throw NotImplementedException("SPIR-V Instruction");
121}
122
123Id EmitBindlessImageAtomicAnd32(EmitContext&) {
124 throw NotImplementedException("SPIR-V Instruction");
125}
126
127Id EmitBindlessImageAtomicOr32(EmitContext&) {
128 throw NotImplementedException("SPIR-V Instruction");
129}
130
131Id EmitBindlessImageAtomicXor32(EmitContext&) {
132 throw NotImplementedException("SPIR-V Instruction");
133}
134
135Id EmitBindlessImageAtomicExchange32(EmitContext&) {
136 throw NotImplementedException("SPIR-V Instruction");
137}
138
139Id EmitBoundImageAtomicIAdd32(EmitContext&) {
140 throw NotImplementedException("SPIR-V Instruction");
141}
142
143Id EmitBoundImageAtomicSMin32(EmitContext&) {
144 throw NotImplementedException("SPIR-V Instruction");
145}
146
147Id EmitBoundImageAtomicUMin32(EmitContext&) {
148 throw NotImplementedException("SPIR-V Instruction");
149}
150
151Id EmitBoundImageAtomicSMax32(EmitContext&) {
152 throw NotImplementedException("SPIR-V Instruction");
153}
154
155Id EmitBoundImageAtomicUMax32(EmitContext&) {
156 throw NotImplementedException("SPIR-V Instruction");
157}
158
159Id EmitBoundImageAtomicInc32(EmitContext&) {
160 throw NotImplementedException("SPIR-V Instruction");
161}
162
163Id EmitBoundImageAtomicDec32(EmitContext&) {
164 throw NotImplementedException("SPIR-V Instruction");
165}
166
167Id EmitBoundImageAtomicAnd32(EmitContext&) {
168 throw NotImplementedException("SPIR-V Instruction");
169}
170
171Id EmitBoundImageAtomicOr32(EmitContext&) {
172 throw NotImplementedException("SPIR-V Instruction");
173}
174
175Id EmitBoundImageAtomicXor32(EmitContext&) {
176 throw NotImplementedException("SPIR-V Instruction");
177}
178
179Id EmitBoundImageAtomicExchange32(EmitContext&) {
180 throw NotImplementedException("SPIR-V Instruction");
181}
182
183} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
new file mode 100644
index 000000000..f99c02848
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
@@ -0,0 +1,579 @@
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 <sirit/sirit.h>
6
7#include "common/common_types.h"
8
9namespace Shader::IR {
10enum class Attribute : u64;
11enum class Patch : u64;
12class Inst;
13class Value;
14} // namespace Shader::IR
15
16namespace Shader::Backend::SPIRV {
17
18using Sirit::Id;
19
20class EmitContext;
21
22// Microinstruction emitters
23Id EmitPhi(EmitContext& ctx, IR::Inst* inst);
24void EmitVoid(EmitContext& ctx);
25Id EmitIdentity(EmitContext& ctx, const IR::Value& value);
26Id EmitConditionRef(EmitContext& ctx, const IR::Value& value);
27void EmitReference(EmitContext&);
28void EmitPhiMove(EmitContext&);
29void EmitJoin(EmitContext& ctx);
30void EmitDemoteToHelperInvocation(EmitContext& ctx);
31void EmitBarrier(EmitContext& ctx);
32void EmitWorkgroupMemoryBarrier(EmitContext& ctx);
33void EmitDeviceMemoryBarrier(EmitContext& ctx);
34void EmitPrologue(EmitContext& ctx);
35void EmitEpilogue(EmitContext& ctx);
36void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream);
37void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream);
38void EmitGetRegister(EmitContext& ctx);
39void EmitSetRegister(EmitContext& ctx);
40void EmitGetPred(EmitContext& ctx);
41void EmitSetPred(EmitContext& ctx);
42void EmitSetGotoVariable(EmitContext& ctx);
43void EmitGetGotoVariable(EmitContext& ctx);
44void EmitSetIndirectBranchVariable(EmitContext& ctx);
45void EmitGetIndirectBranchVariable(EmitContext& ctx);
46Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
47Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
48Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
49Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
50Id EmitGetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
51Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
52Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
53Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex);
54void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, Id vertex);
55Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex);
56void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value, Id vertex);
57Id EmitGetPatch(EmitContext& ctx, IR::Patch patch);
58void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value);
59void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value);
60void EmitSetSampleMask(EmitContext& ctx, Id value);
61void EmitSetFragDepth(EmitContext& ctx, Id value);
62void EmitGetZFlag(EmitContext& ctx);
63void EmitGetSFlag(EmitContext& ctx);
64void EmitGetCFlag(EmitContext& ctx);
65void EmitGetOFlag(EmitContext& ctx);
66void EmitSetZFlag(EmitContext& ctx);
67void EmitSetSFlag(EmitContext& ctx);
68void EmitSetCFlag(EmitContext& ctx);
69void EmitSetOFlag(EmitContext& ctx);
70Id EmitWorkgroupId(EmitContext& ctx);
71Id EmitLocalInvocationId(EmitContext& ctx);
72Id EmitInvocationId(EmitContext& ctx);
73Id EmitSampleId(EmitContext& ctx);
74Id EmitIsHelperInvocation(EmitContext& ctx);
75Id EmitYDirection(EmitContext& ctx);
76Id EmitLoadLocal(EmitContext& ctx, Id word_offset);
77void EmitWriteLocal(EmitContext& ctx, Id word_offset, Id value);
78Id EmitUndefU1(EmitContext& ctx);
79Id EmitUndefU8(EmitContext& ctx);
80Id EmitUndefU16(EmitContext& ctx);
81Id EmitUndefU32(EmitContext& ctx);
82Id EmitUndefU64(EmitContext& ctx);
83void EmitLoadGlobalU8(EmitContext& ctx);
84void EmitLoadGlobalS8(EmitContext& ctx);
85void EmitLoadGlobalU16(EmitContext& ctx);
86void EmitLoadGlobalS16(EmitContext& ctx);
87Id EmitLoadGlobal32(EmitContext& ctx, Id address);
88Id EmitLoadGlobal64(EmitContext& ctx, Id address);
89Id EmitLoadGlobal128(EmitContext& ctx, Id address);
90void EmitWriteGlobalU8(EmitContext& ctx);
91void EmitWriteGlobalS8(EmitContext& ctx);
92void EmitWriteGlobalU16(EmitContext& ctx);
93void EmitWriteGlobalS16(EmitContext& ctx);
94void EmitWriteGlobal32(EmitContext& ctx, Id address, Id value);
95void EmitWriteGlobal64(EmitContext& ctx, Id address, Id value);
96void EmitWriteGlobal128(EmitContext& ctx, Id address, Id value);
97Id EmitLoadStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
98Id EmitLoadStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
99Id EmitLoadStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
100Id EmitLoadStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
101Id EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
102Id EmitLoadStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
103Id EmitLoadStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
104void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
105 Id value);
106void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
107 Id value);
108void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
109 Id value);
110void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
111 Id value);
112void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
113 Id value);
114void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
115 Id value);
116void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
117 Id value);
118Id EmitLoadSharedU8(EmitContext& ctx, Id offset);
119Id EmitLoadSharedS8(EmitContext& ctx, Id offset);
120Id EmitLoadSharedU16(EmitContext& ctx, Id offset);
121Id EmitLoadSharedS16(EmitContext& ctx, Id offset);
122Id EmitLoadSharedU32(EmitContext& ctx, Id offset);
123Id EmitLoadSharedU64(EmitContext& ctx, Id offset);
124Id EmitLoadSharedU128(EmitContext& ctx, Id offset);
125void EmitWriteSharedU8(EmitContext& ctx, Id offset, Id value);
126void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value);
127void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value);
128void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value);
129void EmitWriteSharedU128(EmitContext& ctx, Id offset, Id value);
130Id EmitCompositeConstructU32x2(EmitContext& ctx, Id e1, Id e2);
131Id EmitCompositeConstructU32x3(EmitContext& ctx, Id e1, Id e2, Id e3);
132Id EmitCompositeConstructU32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4);
133Id EmitCompositeExtractU32x2(EmitContext& ctx, Id composite, u32 index);
134Id EmitCompositeExtractU32x3(EmitContext& ctx, Id composite, u32 index);
135Id EmitCompositeExtractU32x4(EmitContext& ctx, Id composite, u32 index);
136Id EmitCompositeInsertU32x2(EmitContext& ctx, Id composite, Id object, u32 index);
137Id EmitCompositeInsertU32x3(EmitContext& ctx, Id composite, Id object, u32 index);
138Id EmitCompositeInsertU32x4(EmitContext& ctx, Id composite, Id object, u32 index);
139Id EmitCompositeConstructF16x2(EmitContext& ctx, Id e1, Id e2);
140Id EmitCompositeConstructF16x3(EmitContext& ctx, Id e1, Id e2, Id e3);
141Id EmitCompositeConstructF16x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4);
142Id EmitCompositeExtractF16x2(EmitContext& ctx, Id composite, u32 index);
143Id EmitCompositeExtractF16x3(EmitContext& ctx, Id composite, u32 index);
144Id EmitCompositeExtractF16x4(EmitContext& ctx, Id composite, u32 index);
145Id EmitCompositeInsertF16x2(EmitContext& ctx, Id composite, Id object, u32 index);
146Id EmitCompositeInsertF16x3(EmitContext& ctx, Id composite, Id object, u32 index);
147Id EmitCompositeInsertF16x4(EmitContext& ctx, Id composite, Id object, u32 index);
148Id EmitCompositeConstructF32x2(EmitContext& ctx, Id e1, Id e2);
149Id EmitCompositeConstructF32x3(EmitContext& ctx, Id e1, Id e2, Id e3);
150Id EmitCompositeConstructF32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4);
151Id EmitCompositeExtractF32x2(EmitContext& ctx, Id composite, u32 index);
152Id EmitCompositeExtractF32x3(EmitContext& ctx, Id composite, u32 index);
153Id EmitCompositeExtractF32x4(EmitContext& ctx, Id composite, u32 index);
154Id EmitCompositeInsertF32x2(EmitContext& ctx, Id composite, Id object, u32 index);
155Id EmitCompositeInsertF32x3(EmitContext& ctx, Id composite, Id object, u32 index);
156Id EmitCompositeInsertF32x4(EmitContext& ctx, Id composite, Id object, u32 index);
157void EmitCompositeConstructF64x2(EmitContext& ctx);
158void EmitCompositeConstructF64x3(EmitContext& ctx);
159void EmitCompositeConstructF64x4(EmitContext& ctx);
160void EmitCompositeExtractF64x2(EmitContext& ctx);
161void EmitCompositeExtractF64x3(EmitContext& ctx);
162void EmitCompositeExtractF64x4(EmitContext& ctx);
163Id EmitCompositeInsertF64x2(EmitContext& ctx, Id composite, Id object, u32 index);
164Id EmitCompositeInsertF64x3(EmitContext& ctx, Id composite, Id object, u32 index);
165Id EmitCompositeInsertF64x4(EmitContext& ctx, Id composite, Id object, u32 index);
166Id EmitSelectU1(EmitContext& ctx, Id cond, Id true_value, Id false_value);
167Id EmitSelectU8(EmitContext& ctx, Id cond, Id true_value, Id false_value);
168Id EmitSelectU16(EmitContext& ctx, Id cond, Id true_value, Id false_value);
169Id EmitSelectU32(EmitContext& ctx, Id cond, Id true_value, Id false_value);
170Id EmitSelectU64(EmitContext& ctx, Id cond, Id true_value, Id false_value);
171Id EmitSelectF16(EmitContext& ctx, Id cond, Id true_value, Id false_value);
172Id EmitSelectF32(EmitContext& ctx, Id cond, Id true_value, Id false_value);
173Id EmitSelectF64(EmitContext& ctx, Id cond, Id true_value, Id false_value);
174void EmitBitCastU16F16(EmitContext& ctx);
175Id EmitBitCastU32F32(EmitContext& ctx, Id value);
176void EmitBitCastU64F64(EmitContext& ctx);
177void EmitBitCastF16U16(EmitContext& ctx);
178Id EmitBitCastF32U32(EmitContext& ctx, Id value);
179void EmitBitCastF64U64(EmitContext& ctx);
180Id EmitPackUint2x32(EmitContext& ctx, Id value);
181Id EmitUnpackUint2x32(EmitContext& ctx, Id value);
182Id EmitPackFloat2x16(EmitContext& ctx, Id value);
183Id EmitUnpackFloat2x16(EmitContext& ctx, Id value);
184Id EmitPackHalf2x16(EmitContext& ctx, Id value);
185Id EmitUnpackHalf2x16(EmitContext& ctx, Id value);
186Id EmitPackDouble2x32(EmitContext& ctx, Id value);
187Id EmitUnpackDouble2x32(EmitContext& ctx, Id value);
188void EmitGetZeroFromOp(EmitContext& ctx);
189void EmitGetSignFromOp(EmitContext& ctx);
190void EmitGetCarryFromOp(EmitContext& ctx);
191void EmitGetOverflowFromOp(EmitContext& ctx);
192void EmitGetSparseFromOp(EmitContext& ctx);
193void EmitGetInBoundsFromOp(EmitContext& ctx);
194Id EmitFPAbs16(EmitContext& ctx, Id value);
195Id EmitFPAbs32(EmitContext& ctx, Id value);
196Id EmitFPAbs64(EmitContext& ctx, Id value);
197Id EmitFPAdd16(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
198Id EmitFPAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
199Id EmitFPAdd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
200Id EmitFPFma16(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c);
201Id EmitFPFma32(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c);
202Id EmitFPFma64(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c);
203Id EmitFPMax32(EmitContext& ctx, Id a, Id b);
204Id EmitFPMax64(EmitContext& ctx, Id a, Id b);
205Id EmitFPMin32(EmitContext& ctx, Id a, Id b);
206Id EmitFPMin64(EmitContext& ctx, Id a, Id b);
207Id EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
208Id EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
209Id EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
210Id EmitFPNeg16(EmitContext& ctx, Id value);
211Id EmitFPNeg32(EmitContext& ctx, Id value);
212Id EmitFPNeg64(EmitContext& ctx, Id value);
213Id EmitFPSin(EmitContext& ctx, Id value);
214Id EmitFPCos(EmitContext& ctx, Id value);
215Id EmitFPExp2(EmitContext& ctx, Id value);
216Id EmitFPLog2(EmitContext& ctx, Id value);
217Id EmitFPRecip32(EmitContext& ctx, Id value);
218Id EmitFPRecip64(EmitContext& ctx, Id value);
219Id EmitFPRecipSqrt32(EmitContext& ctx, Id value);
220Id EmitFPRecipSqrt64(EmitContext& ctx, Id value);
221Id EmitFPSqrt(EmitContext& ctx, Id value);
222Id EmitFPSaturate16(EmitContext& ctx, Id value);
223Id EmitFPSaturate32(EmitContext& ctx, Id value);
224Id EmitFPSaturate64(EmitContext& ctx, Id value);
225Id EmitFPClamp16(EmitContext& ctx, Id value, Id min_value, Id max_value);
226Id EmitFPClamp32(EmitContext& ctx, Id value, Id min_value, Id max_value);
227Id EmitFPClamp64(EmitContext& ctx, Id value, Id min_value, Id max_value);
228Id EmitFPRoundEven16(EmitContext& ctx, Id value);
229Id EmitFPRoundEven32(EmitContext& ctx, Id value);
230Id EmitFPRoundEven64(EmitContext& ctx, Id value);
231Id EmitFPFloor16(EmitContext& ctx, Id value);
232Id EmitFPFloor32(EmitContext& ctx, Id value);
233Id EmitFPFloor64(EmitContext& ctx, Id value);
234Id EmitFPCeil16(EmitContext& ctx, Id value);
235Id EmitFPCeil32(EmitContext& ctx, Id value);
236Id EmitFPCeil64(EmitContext& ctx, Id value);
237Id EmitFPTrunc16(EmitContext& ctx, Id value);
238Id EmitFPTrunc32(EmitContext& ctx, Id value);
239Id EmitFPTrunc64(EmitContext& ctx, Id value);
240Id EmitFPOrdEqual16(EmitContext& ctx, Id lhs, Id rhs);
241Id EmitFPOrdEqual32(EmitContext& ctx, Id lhs, Id rhs);
242Id EmitFPOrdEqual64(EmitContext& ctx, Id lhs, Id rhs);
243Id EmitFPUnordEqual16(EmitContext& ctx, Id lhs, Id rhs);
244Id EmitFPUnordEqual32(EmitContext& ctx, Id lhs, Id rhs);
245Id EmitFPUnordEqual64(EmitContext& ctx, Id lhs, Id rhs);
246Id EmitFPOrdNotEqual16(EmitContext& ctx, Id lhs, Id rhs);
247Id EmitFPOrdNotEqual32(EmitContext& ctx, Id lhs, Id rhs);
248Id EmitFPOrdNotEqual64(EmitContext& ctx, Id lhs, Id rhs);
249Id EmitFPUnordNotEqual16(EmitContext& ctx, Id lhs, Id rhs);
250Id EmitFPUnordNotEqual32(EmitContext& ctx, Id lhs, Id rhs);
251Id EmitFPUnordNotEqual64(EmitContext& ctx, Id lhs, Id rhs);
252Id EmitFPOrdLessThan16(EmitContext& ctx, Id lhs, Id rhs);
253Id EmitFPOrdLessThan32(EmitContext& ctx, Id lhs, Id rhs);
254Id EmitFPOrdLessThan64(EmitContext& ctx, Id lhs, Id rhs);
255Id EmitFPUnordLessThan16(EmitContext& ctx, Id lhs, Id rhs);
256Id EmitFPUnordLessThan32(EmitContext& ctx, Id lhs, Id rhs);
257Id EmitFPUnordLessThan64(EmitContext& ctx, Id lhs, Id rhs);
258Id EmitFPOrdGreaterThan16(EmitContext& ctx, Id lhs, Id rhs);
259Id EmitFPOrdGreaterThan32(EmitContext& ctx, Id lhs, Id rhs);
260Id EmitFPOrdGreaterThan64(EmitContext& ctx, Id lhs, Id rhs);
261Id EmitFPUnordGreaterThan16(EmitContext& ctx, Id lhs, Id rhs);
262Id EmitFPUnordGreaterThan32(EmitContext& ctx, Id lhs, Id rhs);
263Id EmitFPUnordGreaterThan64(EmitContext& ctx, Id lhs, Id rhs);
264Id EmitFPOrdLessThanEqual16(EmitContext& ctx, Id lhs, Id rhs);
265Id EmitFPOrdLessThanEqual32(EmitContext& ctx, Id lhs, Id rhs);
266Id EmitFPOrdLessThanEqual64(EmitContext& ctx, Id lhs, Id rhs);
267Id EmitFPUnordLessThanEqual16(EmitContext& ctx, Id lhs, Id rhs);
268Id EmitFPUnordLessThanEqual32(EmitContext& ctx, Id lhs, Id rhs);
269Id EmitFPUnordLessThanEqual64(EmitContext& ctx, Id lhs, Id rhs);
270Id EmitFPOrdGreaterThanEqual16(EmitContext& ctx, Id lhs, Id rhs);
271Id EmitFPOrdGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs);
272Id EmitFPOrdGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs);
273Id EmitFPUnordGreaterThanEqual16(EmitContext& ctx, Id lhs, Id rhs);
274Id EmitFPUnordGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs);
275Id EmitFPUnordGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs);
276Id EmitFPIsNan16(EmitContext& ctx, Id value);
277Id EmitFPIsNan32(EmitContext& ctx, Id value);
278Id EmitFPIsNan64(EmitContext& ctx, Id value);
279Id EmitIAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
280Id EmitIAdd64(EmitContext& ctx, Id a, Id b);
281Id EmitISub32(EmitContext& ctx, Id a, Id b);
282Id EmitISub64(EmitContext& ctx, Id a, Id b);
283Id EmitIMul32(EmitContext& ctx, Id a, Id b);
284Id EmitINeg32(EmitContext& ctx, Id value);
285Id EmitINeg64(EmitContext& ctx, Id value);
286Id EmitIAbs32(EmitContext& ctx, Id value);
287Id EmitShiftLeftLogical32(EmitContext& ctx, Id base, Id shift);
288Id EmitShiftLeftLogical64(EmitContext& ctx, Id base, Id shift);
289Id EmitShiftRightLogical32(EmitContext& ctx, Id base, Id shift);
290Id EmitShiftRightLogical64(EmitContext& ctx, Id base, Id shift);
291Id EmitShiftRightArithmetic32(EmitContext& ctx, Id base, Id shift);
292Id EmitShiftRightArithmetic64(EmitContext& ctx, Id base, Id shift);
293Id EmitBitwiseAnd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
294Id EmitBitwiseOr32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
295Id EmitBitwiseXor32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
296Id EmitBitFieldInsert(EmitContext& ctx, Id base, Id insert, Id offset, Id count);
297Id EmitBitFieldSExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count);
298Id EmitBitFieldUExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count);
299Id EmitBitReverse32(EmitContext& ctx, Id value);
300Id EmitBitCount32(EmitContext& ctx, Id value);
301Id EmitBitwiseNot32(EmitContext& ctx, Id value);
302Id EmitFindSMsb32(EmitContext& ctx, Id value);
303Id EmitFindUMsb32(EmitContext& ctx, Id value);
304Id EmitSMin32(EmitContext& ctx, Id a, Id b);
305Id EmitUMin32(EmitContext& ctx, Id a, Id b);
306Id EmitSMax32(EmitContext& ctx, Id a, Id b);
307Id EmitUMax32(EmitContext& ctx, Id a, Id b);
308Id EmitSClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max);
309Id EmitUClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max);
310Id EmitSLessThan(EmitContext& ctx, Id lhs, Id rhs);
311Id EmitULessThan(EmitContext& ctx, Id lhs, Id rhs);
312Id EmitIEqual(EmitContext& ctx, Id lhs, Id rhs);
313Id EmitSLessThanEqual(EmitContext& ctx, Id lhs, Id rhs);
314Id EmitULessThanEqual(EmitContext& ctx, Id lhs, Id rhs);
315Id EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs);
316Id EmitUGreaterThan(EmitContext& ctx, Id lhs, Id rhs);
317Id EmitINotEqual(EmitContext& ctx, Id lhs, Id rhs);
318Id EmitSGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs);
319Id EmitUGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs);
320Id EmitSharedAtomicIAdd32(EmitContext& ctx, Id pointer_offset, Id value);
321Id EmitSharedAtomicSMin32(EmitContext& ctx, Id pointer_offset, Id value);
322Id EmitSharedAtomicUMin32(EmitContext& ctx, Id pointer_offset, Id value);
323Id EmitSharedAtomicSMax32(EmitContext& ctx, Id pointer_offset, Id value);
324Id EmitSharedAtomicUMax32(EmitContext& ctx, Id pointer_offset, Id value);
325Id EmitSharedAtomicInc32(EmitContext& ctx, Id pointer_offset, Id value);
326Id EmitSharedAtomicDec32(EmitContext& ctx, Id pointer_offset, Id value);
327Id EmitSharedAtomicAnd32(EmitContext& ctx, Id pointer_offset, Id value);
328Id EmitSharedAtomicOr32(EmitContext& ctx, Id pointer_offset, Id value);
329Id EmitSharedAtomicXor32(EmitContext& ctx, Id pointer_offset, Id value);
330Id EmitSharedAtomicExchange32(EmitContext& ctx, Id pointer_offset, Id value);
331Id EmitSharedAtomicExchange64(EmitContext& ctx, Id pointer_offset, Id value);
332Id EmitStorageAtomicIAdd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
333 Id value);
334Id EmitStorageAtomicSMin32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
335 Id value);
336Id EmitStorageAtomicUMin32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
337 Id value);
338Id EmitStorageAtomicSMax32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
339 Id value);
340Id EmitStorageAtomicUMax32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
341 Id value);
342Id EmitStorageAtomicInc32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
343 Id value);
344Id EmitStorageAtomicDec32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
345 Id value);
346Id EmitStorageAtomicAnd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
347 Id value);
348Id EmitStorageAtomicOr32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
349 Id value);
350Id EmitStorageAtomicXor32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
351 Id value);
352Id EmitStorageAtomicExchange32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
353 Id value);
354Id EmitStorageAtomicIAdd64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
355 Id value);
356Id EmitStorageAtomicSMin64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
357 Id value);
358Id EmitStorageAtomicUMin64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
359 Id value);
360Id EmitStorageAtomicSMax64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
361 Id value);
362Id EmitStorageAtomicUMax64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
363 Id value);
364Id EmitStorageAtomicAnd64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
365 Id value);
366Id EmitStorageAtomicOr64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
367 Id value);
368Id EmitStorageAtomicXor64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
369 Id value);
370Id EmitStorageAtomicExchange64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
371 Id value);
372Id EmitStorageAtomicAddF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
373 Id value);
374Id EmitStorageAtomicAddF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
375 Id value);
376Id EmitStorageAtomicAddF32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
377 Id value);
378Id EmitStorageAtomicMinF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
379 Id value);
380Id EmitStorageAtomicMinF32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
381 Id value);
382Id EmitStorageAtomicMaxF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
383 Id value);
384Id EmitStorageAtomicMaxF32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
385 Id value);
386Id EmitGlobalAtomicIAdd32(EmitContext& ctx);
387Id EmitGlobalAtomicSMin32(EmitContext& ctx);
388Id EmitGlobalAtomicUMin32(EmitContext& ctx);
389Id EmitGlobalAtomicSMax32(EmitContext& ctx);
390Id EmitGlobalAtomicUMax32(EmitContext& ctx);
391Id EmitGlobalAtomicInc32(EmitContext& ctx);
392Id EmitGlobalAtomicDec32(EmitContext& ctx);
393Id EmitGlobalAtomicAnd32(EmitContext& ctx);
394Id EmitGlobalAtomicOr32(EmitContext& ctx);
395Id EmitGlobalAtomicXor32(EmitContext& ctx);
396Id EmitGlobalAtomicExchange32(EmitContext& ctx);
397Id EmitGlobalAtomicIAdd64(EmitContext& ctx);
398Id EmitGlobalAtomicSMin64(EmitContext& ctx);
399Id EmitGlobalAtomicUMin64(EmitContext& ctx);
400Id EmitGlobalAtomicSMax64(EmitContext& ctx);
401Id EmitGlobalAtomicUMax64(EmitContext& ctx);
402Id EmitGlobalAtomicInc64(EmitContext& ctx);
403Id EmitGlobalAtomicDec64(EmitContext& ctx);
404Id EmitGlobalAtomicAnd64(EmitContext& ctx);
405Id EmitGlobalAtomicOr64(EmitContext& ctx);
406Id EmitGlobalAtomicXor64(EmitContext& ctx);
407Id EmitGlobalAtomicExchange64(EmitContext& ctx);
408Id EmitGlobalAtomicAddF32(EmitContext& ctx);
409Id EmitGlobalAtomicAddF16x2(EmitContext& ctx);
410Id EmitGlobalAtomicAddF32x2(EmitContext& ctx);
411Id EmitGlobalAtomicMinF16x2(EmitContext& ctx);
412Id EmitGlobalAtomicMinF32x2(EmitContext& ctx);
413Id EmitGlobalAtomicMaxF16x2(EmitContext& ctx);
414Id EmitGlobalAtomicMaxF32x2(EmitContext& ctx);
415Id EmitLogicalOr(EmitContext& ctx, Id a, Id b);
416Id EmitLogicalAnd(EmitContext& ctx, Id a, Id b);
417Id EmitLogicalXor(EmitContext& ctx, Id a, Id b);
418Id EmitLogicalNot(EmitContext& ctx, Id value);
419Id EmitConvertS16F16(EmitContext& ctx, Id value);
420Id EmitConvertS16F32(EmitContext& ctx, Id value);
421Id EmitConvertS16F64(EmitContext& ctx, Id value);
422Id EmitConvertS32F16(EmitContext& ctx, Id value);
423Id EmitConvertS32F32(EmitContext& ctx, Id value);
424Id EmitConvertS32F64(EmitContext& ctx, Id value);
425Id EmitConvertS64F16(EmitContext& ctx, Id value);
426Id EmitConvertS64F32(EmitContext& ctx, Id value);
427Id EmitConvertS64F64(EmitContext& ctx, Id value);
428Id EmitConvertU16F16(EmitContext& ctx, Id value);
429Id EmitConvertU16F32(EmitContext& ctx, Id value);
430Id EmitConvertU16F64(EmitContext& ctx, Id value);
431Id EmitConvertU32F16(EmitContext& ctx, Id value);
432Id EmitConvertU32F32(EmitContext& ctx, Id value);
433Id EmitConvertU32F64(EmitContext& ctx, Id value);
434Id EmitConvertU64F16(EmitContext& ctx, Id value);
435Id EmitConvertU64F32(EmitContext& ctx, Id value);
436Id EmitConvertU64F64(EmitContext& ctx, Id value);
437Id EmitConvertU64U32(EmitContext& ctx, Id value);
438Id EmitConvertU32U64(EmitContext& ctx, Id value);
439Id EmitConvertF16F32(EmitContext& ctx, Id value);
440Id EmitConvertF32F16(EmitContext& ctx, Id value);
441Id EmitConvertF32F64(EmitContext& ctx, Id value);
442Id EmitConvertF64F32(EmitContext& ctx, Id value);
443Id EmitConvertF16S8(EmitContext& ctx, Id value);
444Id EmitConvertF16S16(EmitContext& ctx, Id value);
445Id EmitConvertF16S32(EmitContext& ctx, Id value);
446Id EmitConvertF16S64(EmitContext& ctx, Id value);
447Id EmitConvertF16U8(EmitContext& ctx, Id value);
448Id EmitConvertF16U16(EmitContext& ctx, Id value);
449Id EmitConvertF16U32(EmitContext& ctx, Id value);
450Id EmitConvertF16U64(EmitContext& ctx, Id value);
451Id EmitConvertF32S8(EmitContext& ctx, Id value);
452Id EmitConvertF32S16(EmitContext& ctx, Id value);
453Id EmitConvertF32S32(EmitContext& ctx, Id value);
454Id EmitConvertF32S64(EmitContext& ctx, Id value);
455Id EmitConvertF32U8(EmitContext& ctx, Id value);
456Id EmitConvertF32U16(EmitContext& ctx, Id value);
457Id EmitConvertF32U32(EmitContext& ctx, Id value);
458Id EmitConvertF32U64(EmitContext& ctx, Id value);
459Id EmitConvertF64S8(EmitContext& ctx, Id value);
460Id EmitConvertF64S16(EmitContext& ctx, Id value);
461Id EmitConvertF64S32(EmitContext& ctx, Id value);
462Id EmitConvertF64S64(EmitContext& ctx, Id value);
463Id EmitConvertF64U8(EmitContext& ctx, Id value);
464Id EmitConvertF64U16(EmitContext& ctx, Id value);
465Id EmitConvertF64U32(EmitContext& ctx, Id value);
466Id EmitConvertF64U64(EmitContext& ctx, Id value);
467Id EmitBindlessImageSampleImplicitLod(EmitContext&);
468Id EmitBindlessImageSampleExplicitLod(EmitContext&);
469Id EmitBindlessImageSampleDrefImplicitLod(EmitContext&);
470Id EmitBindlessImageSampleDrefExplicitLod(EmitContext&);
471Id EmitBindlessImageGather(EmitContext&);
472Id EmitBindlessImageGatherDref(EmitContext&);
473Id EmitBindlessImageFetch(EmitContext&);
474Id EmitBindlessImageQueryDimensions(EmitContext&);
475Id EmitBindlessImageQueryLod(EmitContext&);
476Id EmitBindlessImageGradient(EmitContext&);
477Id EmitBindlessImageRead(EmitContext&);
478Id EmitBindlessImageWrite(EmitContext&);
479Id EmitBoundImageSampleImplicitLod(EmitContext&);
480Id EmitBoundImageSampleExplicitLod(EmitContext&);
481Id EmitBoundImageSampleDrefImplicitLod(EmitContext&);
482Id EmitBoundImageSampleDrefExplicitLod(EmitContext&);
483Id EmitBoundImageGather(EmitContext&);
484Id EmitBoundImageGatherDref(EmitContext&);
485Id EmitBoundImageFetch(EmitContext&);
486Id EmitBoundImageQueryDimensions(EmitContext&);
487Id EmitBoundImageQueryLod(EmitContext&);
488Id EmitBoundImageGradient(EmitContext&);
489Id EmitBoundImageRead(EmitContext&);
490Id EmitBoundImageWrite(EmitContext&);
491Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
492 Id bias_lc, const IR::Value& offset);
493Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
494 Id lod, const IR::Value& offset);
495Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
496 Id coords, Id dref, Id bias_lc, const IR::Value& offset);
497Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
498 Id coords, Id dref, Id lod, const IR::Value& offset);
499Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
500 const IR::Value& offset, const IR::Value& offset2);
501Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
502 const IR::Value& offset, const IR::Value& offset2, Id dref);
503Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
504 Id lod, Id ms);
505Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod);
506Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
507Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
508 Id derivates, Id offset, Id lod_clamp);
509Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
510void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color);
511Id EmitBindlessImageAtomicIAdd32(EmitContext&);
512Id EmitBindlessImageAtomicSMin32(EmitContext&);
513Id EmitBindlessImageAtomicUMin32(EmitContext&);
514Id EmitBindlessImageAtomicSMax32(EmitContext&);
515Id EmitBindlessImageAtomicUMax32(EmitContext&);
516Id EmitBindlessImageAtomicInc32(EmitContext&);
517Id EmitBindlessImageAtomicDec32(EmitContext&);
518Id EmitBindlessImageAtomicAnd32(EmitContext&);
519Id EmitBindlessImageAtomicOr32(EmitContext&);
520Id EmitBindlessImageAtomicXor32(EmitContext&);
521Id EmitBindlessImageAtomicExchange32(EmitContext&);
522Id EmitBoundImageAtomicIAdd32(EmitContext&);
523Id EmitBoundImageAtomicSMin32(EmitContext&);
524Id EmitBoundImageAtomicUMin32(EmitContext&);
525Id EmitBoundImageAtomicSMax32(EmitContext&);
526Id EmitBoundImageAtomicUMax32(EmitContext&);
527Id EmitBoundImageAtomicInc32(EmitContext&);
528Id EmitBoundImageAtomicDec32(EmitContext&);
529Id EmitBoundImageAtomicAnd32(EmitContext&);
530Id EmitBoundImageAtomicOr32(EmitContext&);
531Id EmitBoundImageAtomicXor32(EmitContext&);
532Id EmitBoundImageAtomicExchange32(EmitContext&);
533Id EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
534 Id value);
535Id EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
536 Id value);
537Id EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
538 Id value);
539Id EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
540 Id value);
541Id EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
542 Id value);
543Id EmitImageAtomicInc32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
544 Id value);
545Id EmitImageAtomicDec32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
546 Id value);
547Id EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
548 Id value);
549Id EmitImageAtomicOr32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
550 Id value);
551Id EmitImageAtomicXor32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
552 Id value);
553Id EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
554 Id value);
555Id EmitLaneId(EmitContext& ctx);
556Id EmitVoteAll(EmitContext& ctx, Id pred);
557Id EmitVoteAny(EmitContext& ctx, Id pred);
558Id EmitVoteEqual(EmitContext& ctx, Id pred);
559Id EmitSubgroupBallot(EmitContext& ctx, Id pred);
560Id EmitSubgroupEqMask(EmitContext& ctx);
561Id EmitSubgroupLtMask(EmitContext& ctx);
562Id EmitSubgroupLeMask(EmitContext& ctx);
563Id EmitSubgroupGtMask(EmitContext& ctx);
564Id EmitSubgroupGeMask(EmitContext& ctx);
565Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
566 Id segmentation_mask);
567Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
568 Id segmentation_mask);
569Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
570 Id segmentation_mask);
571Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
572 Id segmentation_mask);
573Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle);
574Id EmitDPdxFine(EmitContext& ctx, Id op_a);
575Id EmitDPdyFine(EmitContext& ctx, Id op_a);
576Id EmitDPdxCoarse(EmitContext& ctx, Id op_a);
577Id EmitDPdyCoarse(EmitContext& ctx, Id op_a);
578
579} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
new file mode 100644
index 000000000..3501d7495
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
@@ -0,0 +1,270 @@
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 "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7
8namespace Shader::Backend::SPIRV {
9namespace {
10void SetZeroFlag(EmitContext& ctx, IR::Inst* inst, Id result) {
11 IR::Inst* const zero{inst->GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp)};
12 if (!zero) {
13 return;
14 }
15 zero->SetDefinition(ctx.OpIEqual(ctx.U1, result, ctx.u32_zero_value));
16 zero->Invalidate();
17}
18
19void SetSignFlag(EmitContext& ctx, IR::Inst* inst, Id result) {
20 IR::Inst* const sign{inst->GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp)};
21 if (!sign) {
22 return;
23 }
24 sign->SetDefinition(ctx.OpSLessThan(ctx.U1, result, ctx.u32_zero_value));
25 sign->Invalidate();
26}
27} // Anonymous namespace
28
29Id EmitIAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
30 Id result{};
31 if (IR::Inst* const carry{inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp)}) {
32 const Id carry_type{ctx.TypeStruct(ctx.U32[1], ctx.U32[1])};
33 const Id carry_result{ctx.OpIAddCarry(carry_type, a, b)};
34 result = ctx.OpCompositeExtract(ctx.U32[1], carry_result, 0U);
35
36 const Id carry_value{ctx.OpCompositeExtract(ctx.U32[1], carry_result, 1U)};
37 carry->SetDefinition(ctx.OpINotEqual(ctx.U1, carry_value, ctx.u32_zero_value));
38 carry->Invalidate();
39 } else {
40 result = ctx.OpIAdd(ctx.U32[1], a, b);
41 }
42 SetZeroFlag(ctx, inst, result);
43 SetSignFlag(ctx, inst, result);
44 if (IR::Inst * overflow{inst->GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp)}) {
45 // https://stackoverflow.com/questions/55468823/how-to-detect-integer-overflow-in-c
46 constexpr u32 s32_max{static_cast<u32>(std::numeric_limits<s32>::max())};
47 const Id is_positive{ctx.OpSGreaterThanEqual(ctx.U1, a, ctx.u32_zero_value)};
48 const Id sub_a{ctx.OpISub(ctx.U32[1], ctx.Const(s32_max), a)};
49
50 const Id positive_test{ctx.OpSGreaterThan(ctx.U1, b, sub_a)};
51 const Id negative_test{ctx.OpSLessThan(ctx.U1, b, sub_a)};
52 const Id carry_flag{ctx.OpSelect(ctx.U1, is_positive, positive_test, negative_test)};
53 overflow->SetDefinition(carry_flag);
54 overflow->Invalidate();
55 }
56 return result;
57}
58
59Id EmitIAdd64(EmitContext& ctx, Id a, Id b) {
60 return ctx.OpIAdd(ctx.U64, a, b);
61}
62
63Id EmitISub32(EmitContext& ctx, Id a, Id b) {
64 return ctx.OpISub(ctx.U32[1], a, b);
65}
66
67Id EmitISub64(EmitContext& ctx, Id a, Id b) {
68 return ctx.OpISub(ctx.U64, a, b);
69}
70
71Id EmitIMul32(EmitContext& ctx, Id a, Id b) {
72 return ctx.OpIMul(ctx.U32[1], a, b);
73}
74
75Id EmitINeg32(EmitContext& ctx, Id value) {
76 return ctx.OpSNegate(ctx.U32[1], value);
77}
78
79Id EmitINeg64(EmitContext& ctx, Id value) {
80 return ctx.OpSNegate(ctx.U64, value);
81}
82
83Id EmitIAbs32(EmitContext& ctx, Id value) {
84 return ctx.OpSAbs(ctx.U32[1], value);
85}
86
87Id EmitShiftLeftLogical32(EmitContext& ctx, Id base, Id shift) {
88 return ctx.OpShiftLeftLogical(ctx.U32[1], base, shift);
89}
90
91Id EmitShiftLeftLogical64(EmitContext& ctx, Id base, Id shift) {
92 return ctx.OpShiftLeftLogical(ctx.U64, base, shift);
93}
94
95Id EmitShiftRightLogical32(EmitContext& ctx, Id base, Id shift) {
96 return ctx.OpShiftRightLogical(ctx.U32[1], base, shift);
97}
98
99Id EmitShiftRightLogical64(EmitContext& ctx, Id base, Id shift) {
100 return ctx.OpShiftRightLogical(ctx.U64, base, shift);
101}
102
103Id EmitShiftRightArithmetic32(EmitContext& ctx, Id base, Id shift) {
104 return ctx.OpShiftRightArithmetic(ctx.U32[1], base, shift);
105}
106
107Id EmitShiftRightArithmetic64(EmitContext& ctx, Id base, Id shift) {
108 return ctx.OpShiftRightArithmetic(ctx.U64, base, shift);
109}
110
111Id EmitBitwiseAnd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
112 const Id result{ctx.OpBitwiseAnd(ctx.U32[1], a, b)};
113 SetZeroFlag(ctx, inst, result);
114 SetSignFlag(ctx, inst, result);
115 return result;
116}
117
118Id EmitBitwiseOr32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
119 const Id result{ctx.OpBitwiseOr(ctx.U32[1], a, b)};
120 SetZeroFlag(ctx, inst, result);
121 SetSignFlag(ctx, inst, result);
122 return result;
123}
124
125Id EmitBitwiseXor32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
126 const Id result{ctx.OpBitwiseXor(ctx.U32[1], a, b)};
127 SetZeroFlag(ctx, inst, result);
128 SetSignFlag(ctx, inst, result);
129 return result;
130}
131
132Id EmitBitFieldInsert(EmitContext& ctx, Id base, Id insert, Id offset, Id count) {
133 return ctx.OpBitFieldInsert(ctx.U32[1], base, insert, offset, count);
134}
135
136Id EmitBitFieldSExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count) {
137 const Id result{ctx.OpBitFieldSExtract(ctx.U32[1], base, offset, count)};
138 SetZeroFlag(ctx, inst, result);
139 SetSignFlag(ctx, inst, result);
140 return result;
141}
142
143Id EmitBitFieldUExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count) {
144 const Id result{ctx.OpBitFieldUExtract(ctx.U32[1], base, offset, count)};
145 SetZeroFlag(ctx, inst, result);
146 SetSignFlag(ctx, inst, result);
147 return result;
148}
149
150Id EmitBitReverse32(EmitContext& ctx, Id value) {
151 return ctx.OpBitReverse(ctx.U32[1], value);
152}
153
154Id EmitBitCount32(EmitContext& ctx, Id value) {
155 return ctx.OpBitCount(ctx.U32[1], value);
156}
157
158Id EmitBitwiseNot32(EmitContext& ctx, Id value) {
159 return ctx.OpNot(ctx.U32[1], value);
160}
161
162Id EmitFindSMsb32(EmitContext& ctx, Id value) {
163 return ctx.OpFindSMsb(ctx.U32[1], value);
164}
165
166Id EmitFindUMsb32(EmitContext& ctx, Id value) {
167 return ctx.OpFindUMsb(ctx.U32[1], value);
168}
169
170Id EmitSMin32(EmitContext& ctx, Id a, Id b) {
171 const bool is_broken{ctx.profile.has_broken_signed_operations};
172 if (is_broken) {
173 a = ctx.OpBitcast(ctx.S32[1], a);
174 b = ctx.OpBitcast(ctx.S32[1], b);
175 }
176 const Id result{ctx.OpSMin(ctx.U32[1], a, b)};
177 return is_broken ? ctx.OpBitcast(ctx.U32[1], result) : result;
178}
179
180Id EmitUMin32(EmitContext& ctx, Id a, Id b) {
181 return ctx.OpUMin(ctx.U32[1], a, b);
182}
183
184Id EmitSMax32(EmitContext& ctx, Id a, Id b) {
185 const bool is_broken{ctx.profile.has_broken_signed_operations};
186 if (is_broken) {
187 a = ctx.OpBitcast(ctx.S32[1], a);
188 b = ctx.OpBitcast(ctx.S32[1], b);
189 }
190 const Id result{ctx.OpSMax(ctx.U32[1], a, b)};
191 return is_broken ? ctx.OpBitcast(ctx.U32[1], result) : result;
192}
193
194Id EmitUMax32(EmitContext& ctx, Id a, Id b) {
195 return ctx.OpUMax(ctx.U32[1], a, b);
196}
197
198Id EmitSClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max) {
199 Id result{};
200 if (ctx.profile.has_broken_signed_operations || ctx.profile.has_broken_spirv_clamp) {
201 value = ctx.OpBitcast(ctx.S32[1], value);
202 min = ctx.OpBitcast(ctx.S32[1], min);
203 max = ctx.OpBitcast(ctx.S32[1], max);
204 if (ctx.profile.has_broken_spirv_clamp) {
205 result = ctx.OpSMax(ctx.S32[1], ctx.OpSMin(ctx.S32[1], value, max), min);
206 } else {
207 result = ctx.OpSClamp(ctx.S32[1], value, min, max);
208 }
209 result = ctx.OpBitcast(ctx.U32[1], result);
210 } else {
211 result = ctx.OpSClamp(ctx.U32[1], value, min, max);
212 }
213 SetZeroFlag(ctx, inst, result);
214 SetSignFlag(ctx, inst, result);
215 return result;
216}
217
218Id EmitUClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max) {
219 Id result{};
220 if (ctx.profile.has_broken_spirv_clamp) {
221 result = ctx.OpUMax(ctx.U32[1], ctx.OpUMin(ctx.U32[1], value, max), min);
222 } else {
223 result = ctx.OpUClamp(ctx.U32[1], value, min, max);
224 }
225 SetZeroFlag(ctx, inst, result);
226 SetSignFlag(ctx, inst, result);
227 return result;
228}
229
230Id EmitSLessThan(EmitContext& ctx, Id lhs, Id rhs) {
231 return ctx.OpSLessThan(ctx.U1, lhs, rhs);
232}
233
234Id EmitULessThan(EmitContext& ctx, Id lhs, Id rhs) {
235 return ctx.OpULessThan(ctx.U1, lhs, rhs);
236}
237
238Id EmitIEqual(EmitContext& ctx, Id lhs, Id rhs) {
239 return ctx.OpIEqual(ctx.U1, lhs, rhs);
240}
241
242Id EmitSLessThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
243 return ctx.OpSLessThanEqual(ctx.U1, lhs, rhs);
244}
245
246Id EmitULessThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
247 return ctx.OpULessThanEqual(ctx.U1, lhs, rhs);
248}
249
250Id EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs) {
251 return ctx.OpSGreaterThan(ctx.U1, lhs, rhs);
252}
253
254Id EmitUGreaterThan(EmitContext& ctx, Id lhs, Id rhs) {
255 return ctx.OpUGreaterThan(ctx.U1, lhs, rhs);
256}
257
258Id EmitINotEqual(EmitContext& ctx, Id lhs, Id rhs) {
259 return ctx.OpINotEqual(ctx.U1, lhs, rhs);
260}
261
262Id EmitSGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
263 return ctx.OpSGreaterThanEqual(ctx.U1, lhs, rhs);
264}
265
266Id EmitUGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
267 return ctx.OpUGreaterThanEqual(ctx.U1, lhs, rhs);
268}
269
270} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp
new file mode 100644
index 000000000..b9a9500fc
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp
@@ -0,0 +1,26 @@
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 "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7
8namespace Shader::Backend::SPIRV {
9
10Id EmitLogicalOr(EmitContext& ctx, Id a, Id b) {
11 return ctx.OpLogicalOr(ctx.U1, a, b);
12}
13
14Id EmitLogicalAnd(EmitContext& ctx, Id a, Id b) {
15 return ctx.OpLogicalAnd(ctx.U1, a, b);
16}
17
18Id EmitLogicalXor(EmitContext& ctx, Id a, Id b) {
19 return ctx.OpLogicalNotEqual(ctx.U1, a, b);
20}
21
22Id EmitLogicalNot(EmitContext& ctx, Id value) {
23 return ctx.OpLogicalNot(ctx.U1, value);
24}
25
26} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
new file mode 100644
index 000000000..679ee2684
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
@@ -0,0 +1,275 @@
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 <bit>
6
7#include "shader_recompiler/backend/spirv/emit_spirv.h"
8#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
9
10namespace Shader::Backend::SPIRV {
11namespace {
12Id StorageIndex(EmitContext& ctx, const IR::Value& offset, size_t element_size,
13 u32 index_offset = 0) {
14 if (offset.IsImmediate()) {
15 const u32 imm_offset{static_cast<u32>(offset.U32() / element_size) + index_offset};
16 return ctx.Const(imm_offset);
17 }
18 const u32 shift{static_cast<u32>(std::countr_zero(element_size))};
19 Id index{ctx.Def(offset)};
20 if (shift != 0) {
21 const Id shift_id{ctx.Const(shift)};
22 index = ctx.OpShiftRightLogical(ctx.U32[1], index, shift_id);
23 }
24 if (index_offset != 0) {
25 index = ctx.OpIAdd(ctx.U32[1], index, ctx.Const(index_offset));
26 }
27 return index;
28}
29
30Id StoragePointer(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
31 const StorageTypeDefinition& type_def, size_t element_size,
32 Id StorageDefinitions::*member_ptr, u32 index_offset = 0) {
33 if (!binding.IsImmediate()) {
34 throw NotImplementedException("Dynamic storage buffer indexing");
35 }
36 const Id ssbo{ctx.ssbos[binding.U32()].*member_ptr};
37 const Id index{StorageIndex(ctx, offset, element_size, index_offset)};
38 return ctx.OpAccessChain(type_def.element, ssbo, ctx.u32_zero_value, index);
39}
40
41Id LoadStorage(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id result_type,
42 const StorageTypeDefinition& type_def, size_t element_size,
43 Id StorageDefinitions::*member_ptr, u32 index_offset = 0) {
44 const Id pointer{
45 StoragePointer(ctx, binding, offset, type_def, element_size, member_ptr, index_offset)};
46 return ctx.OpLoad(result_type, pointer);
47}
48
49Id LoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
50 u32 index_offset = 0) {
51 return LoadStorage(ctx, binding, offset, ctx.U32[1], ctx.storage_types.U32, sizeof(u32),
52 &StorageDefinitions::U32, index_offset);
53}
54
55void WriteStorage(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value,
56 const StorageTypeDefinition& type_def, size_t element_size,
57 Id StorageDefinitions::*member_ptr, u32 index_offset = 0) {
58 const Id pointer{
59 StoragePointer(ctx, binding, offset, type_def, element_size, member_ptr, index_offset)};
60 ctx.OpStore(pointer, value);
61}
62
63void WriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value,
64 u32 index_offset = 0) {
65 WriteStorage(ctx, binding, offset, value, ctx.storage_types.U32, sizeof(u32),
66 &StorageDefinitions::U32, index_offset);
67}
68} // Anonymous namespace
69
70void EmitLoadGlobalU8(EmitContext&) {
71 throw NotImplementedException("SPIR-V Instruction");
72}
73
74void EmitLoadGlobalS8(EmitContext&) {
75 throw NotImplementedException("SPIR-V Instruction");
76}
77
78void EmitLoadGlobalU16(EmitContext&) {
79 throw NotImplementedException("SPIR-V Instruction");
80}
81
82void EmitLoadGlobalS16(EmitContext&) {
83 throw NotImplementedException("SPIR-V Instruction");
84}
85
86Id EmitLoadGlobal32(EmitContext& ctx, Id address) {
87 if (ctx.profile.support_int64) {
88 return ctx.OpFunctionCall(ctx.U32[1], ctx.load_global_func_u32, address);
89 }
90 LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
91 return ctx.Const(0u);
92}
93
94Id EmitLoadGlobal64(EmitContext& ctx, Id address) {
95 if (ctx.profile.support_int64) {
96 return ctx.OpFunctionCall(ctx.U32[2], ctx.load_global_func_u32x2, address);
97 }
98 LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
99 return ctx.Const(0u, 0u);
100}
101
102Id EmitLoadGlobal128(EmitContext& ctx, Id address) {
103 if (ctx.profile.support_int64) {
104 return ctx.OpFunctionCall(ctx.U32[4], ctx.load_global_func_u32x4, address);
105 }
106 LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
107 return ctx.Const(0u, 0u, 0u, 0u);
108}
109
110void EmitWriteGlobalU8(EmitContext&) {
111 throw NotImplementedException("SPIR-V Instruction");
112}
113
114void EmitWriteGlobalS8(EmitContext&) {
115 throw NotImplementedException("SPIR-V Instruction");
116}
117
118void EmitWriteGlobalU16(EmitContext&) {
119 throw NotImplementedException("SPIR-V Instruction");
120}
121
122void EmitWriteGlobalS16(EmitContext&) {
123 throw NotImplementedException("SPIR-V Instruction");
124}
125
126void EmitWriteGlobal32(EmitContext& ctx, Id address, Id value) {
127 if (ctx.profile.support_int64) {
128 ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32, address, value);
129 return;
130 }
131 LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
132}
133
134void EmitWriteGlobal64(EmitContext& ctx, Id address, Id value) {
135 if (ctx.profile.support_int64) {
136 ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32x2, address, value);
137 return;
138 }
139 LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
140}
141
142void EmitWriteGlobal128(EmitContext& ctx, Id address, Id value) {
143 if (ctx.profile.support_int64) {
144 ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32x4, address, value);
145 return;
146 }
147 LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
148}
149
150Id EmitLoadStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
151 if (ctx.profile.support_int8 && ctx.profile.support_descriptor_aliasing) {
152 return ctx.OpUConvert(ctx.U32[1],
153 LoadStorage(ctx, binding, offset, ctx.U8, ctx.storage_types.U8,
154 sizeof(u8), &StorageDefinitions::U8));
155 } else {
156 return ctx.OpBitFieldUExtract(ctx.U32[1], LoadStorage32(ctx, binding, offset),
157 ctx.BitOffset8(offset), ctx.Const(8u));
158 }
159}
160
161Id EmitLoadStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
162 if (ctx.profile.support_int8 && ctx.profile.support_descriptor_aliasing) {
163 return ctx.OpSConvert(ctx.U32[1],
164 LoadStorage(ctx, binding, offset, ctx.S8, ctx.storage_types.S8,
165 sizeof(s8), &StorageDefinitions::S8));
166 } else {
167 return ctx.OpBitFieldSExtract(ctx.U32[1], LoadStorage32(ctx, binding, offset),
168 ctx.BitOffset8(offset), ctx.Const(8u));
169 }
170}
171
172Id EmitLoadStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
173 if (ctx.profile.support_int16 && ctx.profile.support_descriptor_aliasing) {
174 return ctx.OpUConvert(ctx.U32[1],
175 LoadStorage(ctx, binding, offset, ctx.U16, ctx.storage_types.U16,
176 sizeof(u16), &StorageDefinitions::U16));
177 } else {
178 return ctx.OpBitFieldUExtract(ctx.U32[1], LoadStorage32(ctx, binding, offset),
179 ctx.BitOffset16(offset), ctx.Const(16u));
180 }
181}
182
183Id EmitLoadStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
184 if (ctx.profile.support_int16 && ctx.profile.support_descriptor_aliasing) {
185 return ctx.OpSConvert(ctx.U32[1],
186 LoadStorage(ctx, binding, offset, ctx.S16, ctx.storage_types.S16,
187 sizeof(s16), &StorageDefinitions::S16));
188 } else {
189 return ctx.OpBitFieldSExtract(ctx.U32[1], LoadStorage32(ctx, binding, offset),
190 ctx.BitOffset16(offset), ctx.Const(16u));
191 }
192}
193
194Id EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
195 return LoadStorage32(ctx, binding, offset);
196}
197
198Id EmitLoadStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
199 if (ctx.profile.support_descriptor_aliasing) {
200 return LoadStorage(ctx, binding, offset, ctx.U32[2], ctx.storage_types.U32x2,
201 sizeof(u32[2]), &StorageDefinitions::U32x2);
202 } else {
203 return ctx.OpCompositeConstruct(ctx.U32[2], LoadStorage32(ctx, binding, offset, 0),
204 LoadStorage32(ctx, binding, offset, 1));
205 }
206}
207
208Id EmitLoadStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
209 if (ctx.profile.support_descriptor_aliasing) {
210 return LoadStorage(ctx, binding, offset, ctx.U32[4], ctx.storage_types.U32x4,
211 sizeof(u32[4]), &StorageDefinitions::U32x4);
212 } else {
213 return ctx.OpCompositeConstruct(ctx.U32[4], LoadStorage32(ctx, binding, offset, 0),
214 LoadStorage32(ctx, binding, offset, 1),
215 LoadStorage32(ctx, binding, offset, 2),
216 LoadStorage32(ctx, binding, offset, 3));
217 }
218}
219
220void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
221 Id value) {
222 WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U8, value), ctx.storage_types.U8,
223 sizeof(u8), &StorageDefinitions::U8);
224}
225
226void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
227 Id value) {
228 WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S8, value), ctx.storage_types.S8,
229 sizeof(s8), &StorageDefinitions::S8);
230}
231
232void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
233 Id value) {
234 WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U16, value), ctx.storage_types.U16,
235 sizeof(u16), &StorageDefinitions::U16);
236}
237
238void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
239 Id value) {
240 WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S16, value), ctx.storage_types.S16,
241 sizeof(s16), &StorageDefinitions::S16);
242}
243
244void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
245 Id value) {
246 WriteStorage32(ctx, binding, offset, value);
247}
248
249void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
250 Id value) {
251 if (ctx.profile.support_descriptor_aliasing) {
252 WriteStorage(ctx, binding, offset, value, ctx.storage_types.U32x2, sizeof(u32[2]),
253 &StorageDefinitions::U32x2);
254 } else {
255 for (u32 index = 0; index < 2; ++index) {
256 const Id element{ctx.OpCompositeExtract(ctx.U32[1], value, index)};
257 WriteStorage32(ctx, binding, offset, element, index);
258 }
259 }
260}
261
262void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
263 Id value) {
264 if (ctx.profile.support_descriptor_aliasing) {
265 WriteStorage(ctx, binding, offset, value, ctx.storage_types.U32x4, sizeof(u32[4]),
266 &StorageDefinitions::U32x4);
267 } else {
268 for (u32 index = 0; index < 4; ++index) {
269 const Id element{ctx.OpCompositeExtract(ctx.U32[1], value, index)};
270 WriteStorage32(ctx, binding, offset, element, index);
271 }
272 }
273}
274
275} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp
new file mode 100644
index 000000000..c5b4f4720
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp
@@ -0,0 +1,42 @@
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 "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7
8namespace Shader::Backend::SPIRV {
9
10Id EmitSelectU1(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
11 return ctx.OpSelect(ctx.U1, cond, true_value, false_value);
12}
13
14Id EmitSelectU8(EmitContext&, Id, Id, Id) {
15 throw NotImplementedException("SPIR-V Instruction");
16}
17
18Id EmitSelectU16(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
19 return ctx.OpSelect(ctx.U16, cond, true_value, false_value);
20}
21
22Id EmitSelectU32(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
23 return ctx.OpSelect(ctx.U32[1], cond, true_value, false_value);
24}
25
26Id EmitSelectU64(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
27 return ctx.OpSelect(ctx.U64, cond, true_value, false_value);
28}
29
30Id EmitSelectF16(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
31 return ctx.OpSelect(ctx.F16[1], cond, true_value, false_value);
32}
33
34Id EmitSelectF32(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
35 return ctx.OpSelect(ctx.F32[1], cond, true_value, false_value);
36}
37
38Id EmitSelectF64(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
39 return ctx.OpSelect(ctx.F64[1], cond, true_value, false_value);
40}
41
42} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp
new file mode 100644
index 000000000..9a79fc7a2
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp
@@ -0,0 +1,174 @@
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 "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7
8namespace Shader::Backend::SPIRV {
9namespace {
10Id Pointer(EmitContext& ctx, Id pointer_type, Id array, Id offset, u32 shift) {
11 const Id shift_id{ctx.Const(shift)};
12 const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
13 return ctx.OpAccessChain(pointer_type, array, ctx.u32_zero_value, index);
14}
15
16Id Word(EmitContext& ctx, Id offset) {
17 const Id shift_id{ctx.Const(2U)};
18 const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
19 const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)};
20 return ctx.OpLoad(ctx.U32[1], pointer);
21}
22
23std::pair<Id, Id> ExtractArgs(EmitContext& ctx, Id offset, u32 mask, u32 count) {
24 const Id shift{ctx.OpShiftLeftLogical(ctx.U32[1], offset, ctx.Const(3U))};
25 const Id bit{ctx.OpBitwiseAnd(ctx.U32[1], shift, ctx.Const(mask))};
26 const Id count_id{ctx.Const(count)};
27 return {bit, count_id};
28}
29} // Anonymous namespace
30
31Id EmitLoadSharedU8(EmitContext& ctx, Id offset) {
32 if (ctx.profile.support_explicit_workgroup_layout) {
33 const Id pointer{
34 ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
35 return ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U8, pointer));
36 } else {
37 const auto [bit, count]{ExtractArgs(ctx, offset, 24, 8)};
38 return ctx.OpBitFieldUExtract(ctx.U32[1], Word(ctx, offset), bit, count);
39 }
40}
41
42Id EmitLoadSharedS8(EmitContext& ctx, Id offset) {
43 if (ctx.profile.support_explicit_workgroup_layout) {
44 const Id pointer{
45 ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
46 return ctx.OpSConvert(ctx.U32[1], ctx.OpLoad(ctx.U8, pointer));
47 } else {
48 const auto [bit, count]{ExtractArgs(ctx, offset, 24, 8)};
49 return ctx.OpBitFieldSExtract(ctx.U32[1], Word(ctx, offset), bit, count);
50 }
51}
52
53Id EmitLoadSharedU16(EmitContext& ctx, Id offset) {
54 if (ctx.profile.support_explicit_workgroup_layout) {
55 const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
56 return ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer));
57 } else {
58 const auto [bit, count]{ExtractArgs(ctx, offset, 16, 16)};
59 return ctx.OpBitFieldUExtract(ctx.U32[1], Word(ctx, offset), bit, count);
60 }
61}
62
63Id EmitLoadSharedS16(EmitContext& ctx, Id offset) {
64 if (ctx.profile.support_explicit_workgroup_layout) {
65 const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
66 return ctx.OpSConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer));
67 } else {
68 const auto [bit, count]{ExtractArgs(ctx, offset, 16, 16)};
69 return ctx.OpBitFieldSExtract(ctx.U32[1], Word(ctx, offset), bit, count);
70 }
71}
72
73Id EmitLoadSharedU32(EmitContext& ctx, Id offset) {
74 if (ctx.profile.support_explicit_workgroup_layout) {
75 const Id pointer{Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2)};
76 return ctx.OpLoad(ctx.U32[1], pointer);
77 } else {
78 return Word(ctx, offset);
79 }
80}
81
82Id EmitLoadSharedU64(EmitContext& ctx, Id offset) {
83 if (ctx.profile.support_explicit_workgroup_layout) {
84 const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)};
85 return ctx.OpLoad(ctx.U32[2], pointer);
86 } else {
87 const Id shift_id{ctx.Const(2U)};
88 const Id base_index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
89 const Id next_index{ctx.OpIAdd(ctx.U32[1], base_index, ctx.Const(1U))};
90 const Id lhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, base_index)};
91 const Id rhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, next_index)};
92 return ctx.OpCompositeConstruct(ctx.U32[2], ctx.OpLoad(ctx.U32[1], lhs_pointer),
93 ctx.OpLoad(ctx.U32[1], rhs_pointer));
94 }
95}
96
97Id EmitLoadSharedU128(EmitContext& ctx, Id offset) {
98 if (ctx.profile.support_explicit_workgroup_layout) {
99 const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)};
100 return ctx.OpLoad(ctx.U32[4], pointer);
101 }
102 const Id shift_id{ctx.Const(2U)};
103 const Id base_index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
104 std::array<Id, 4> values{};
105 for (u32 i = 0; i < 4; ++i) {
106 const Id index{i == 0 ? base_index : ctx.OpIAdd(ctx.U32[1], base_index, ctx.Const(i))};
107 const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)};
108 values[i] = ctx.OpLoad(ctx.U32[1], pointer);
109 }
110 return ctx.OpCompositeConstruct(ctx.U32[4], values);
111}
112
113void EmitWriteSharedU8(EmitContext& ctx, Id offset, Id value) {
114 if (ctx.profile.support_explicit_workgroup_layout) {
115 const Id pointer{
116 ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
117 ctx.OpStore(pointer, ctx.OpUConvert(ctx.U8, value));
118 } else {
119 ctx.OpFunctionCall(ctx.void_id, ctx.shared_store_u8_func, offset, value);
120 }
121}
122
123void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value) {
124 if (ctx.profile.support_explicit_workgroup_layout) {
125 const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
126 ctx.OpStore(pointer, ctx.OpUConvert(ctx.U16, value));
127 } else {
128 ctx.OpFunctionCall(ctx.void_id, ctx.shared_store_u16_func, offset, value);
129 }
130}
131
132void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) {
133 Id pointer{};
134 if (ctx.profile.support_explicit_workgroup_layout) {
135 pointer = Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2);
136 } else {
137 const Id shift{ctx.Const(2U)};
138 const Id word_offset{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift)};
139 pointer = ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, word_offset);
140 }
141 ctx.OpStore(pointer, value);
142}
143
144void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) {
145 if (ctx.profile.support_explicit_workgroup_layout) {
146 const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)};
147 ctx.OpStore(pointer, value);
148 return;
149 }
150 const Id shift{ctx.Const(2U)};
151 const Id word_offset{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift)};
152 const Id next_offset{ctx.OpIAdd(ctx.U32[1], word_offset, ctx.Const(1U))};
153 const Id lhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, word_offset)};
154 const Id rhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, next_offset)};
155 ctx.OpStore(lhs_pointer, ctx.OpCompositeExtract(ctx.U32[1], value, 0U));
156 ctx.OpStore(rhs_pointer, ctx.OpCompositeExtract(ctx.U32[1], value, 1U));
157}
158
159void EmitWriteSharedU128(EmitContext& ctx, Id offset, Id value) {
160 if (ctx.profile.support_explicit_workgroup_layout) {
161 const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)};
162 ctx.OpStore(pointer, value);
163 return;
164 }
165 const Id shift{ctx.Const(2U)};
166 const Id base_index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift)};
167 for (u32 i = 0; i < 4; ++i) {
168 const Id index{i == 0 ? base_index : ctx.OpIAdd(ctx.U32[1], base_index, ctx.Const(i))};
169 const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)};
170 ctx.OpStore(pointer, ctx.OpCompositeExtract(ctx.U32[1], value, i));
171 }
172}
173
174} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
new file mode 100644
index 000000000..9e7eb3cb1
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
@@ -0,0 +1,150 @@
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 "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7
8namespace Shader::Backend::SPIRV {
9namespace {
10void ConvertDepthMode(EmitContext& ctx) {
11 const Id type{ctx.F32[1]};
12 const Id position{ctx.OpLoad(ctx.F32[4], ctx.output_position)};
13 const Id z{ctx.OpCompositeExtract(type, position, 2u)};
14 const Id w{ctx.OpCompositeExtract(type, position, 3u)};
15 const Id screen_depth{ctx.OpFMul(type, ctx.OpFAdd(type, z, w), ctx.Constant(type, 0.5f))};
16 const Id vector{ctx.OpCompositeInsert(ctx.F32[4], screen_depth, position, 2u)};
17 ctx.OpStore(ctx.output_position, vector);
18}
19
20void SetFixedPipelinePointSize(EmitContext& ctx) {
21 if (ctx.runtime_info.fixed_state_point_size) {
22 const float point_size{*ctx.runtime_info.fixed_state_point_size};
23 ctx.OpStore(ctx.output_point_size, ctx.Const(point_size));
24 }
25}
26
27Id DefaultVarying(EmitContext& ctx, u32 num_components, u32 element, Id zero, Id one,
28 Id default_vector) {
29 switch (num_components) {
30 case 1:
31 return element == 3 ? one : zero;
32 case 2:
33 return ctx.ConstantComposite(ctx.F32[2], zero, element + 1 == 3 ? one : zero);
34 case 3:
35 return ctx.ConstantComposite(ctx.F32[3], zero, zero, element + 2 == 3 ? one : zero);
36 case 4:
37 return default_vector;
38 }
39 throw InvalidArgument("Bad element");
40}
41
42Id ComparisonFunction(EmitContext& ctx, CompareFunction comparison, Id operand_1, Id operand_2) {
43 switch (comparison) {
44 case CompareFunction::Never:
45 return ctx.false_value;
46 case CompareFunction::Less:
47 return ctx.OpFOrdLessThan(ctx.U1, operand_1, operand_2);
48 case CompareFunction::Equal:
49 return ctx.OpFOrdEqual(ctx.U1, operand_1, operand_2);
50 case CompareFunction::LessThanEqual:
51 return ctx.OpFOrdLessThanEqual(ctx.U1, operand_1, operand_2);
52 case CompareFunction::Greater:
53 return ctx.OpFOrdGreaterThan(ctx.U1, operand_1, operand_2);
54 case CompareFunction::NotEqual:
55 return ctx.OpFOrdNotEqual(ctx.U1, operand_1, operand_2);
56 case CompareFunction::GreaterThanEqual:
57 return ctx.OpFOrdGreaterThanEqual(ctx.U1, operand_1, operand_2);
58 case CompareFunction::Always:
59 return ctx.true_value;
60 }
61 throw InvalidArgument("Comparison function {}", comparison);
62}
63
64void AlphaTest(EmitContext& ctx) {
65 if (!ctx.runtime_info.alpha_test_func) {
66 return;
67 }
68 const auto comparison{*ctx.runtime_info.alpha_test_func};
69 if (comparison == CompareFunction::Always) {
70 return;
71 }
72 if (!Sirit::ValidId(ctx.frag_color[0])) {
73 return;
74 }
75
76 const Id type{ctx.F32[1]};
77 const Id rt0_color{ctx.OpLoad(ctx.F32[4], ctx.frag_color[0])};
78 const Id alpha{ctx.OpCompositeExtract(type, rt0_color, 3u)};
79
80 const Id true_label{ctx.OpLabel()};
81 const Id discard_label{ctx.OpLabel()};
82 const Id alpha_reference{ctx.Const(ctx.runtime_info.alpha_test_reference)};
83 const Id condition{ComparisonFunction(ctx, comparison, alpha, alpha_reference)};
84
85 ctx.OpSelectionMerge(true_label, spv::SelectionControlMask::MaskNone);
86 ctx.OpBranchConditional(condition, true_label, discard_label);
87 ctx.AddLabel(discard_label);
88 ctx.OpKill();
89 ctx.AddLabel(true_label);
90}
91} // Anonymous namespace
92
93void EmitPrologue(EmitContext& ctx) {
94 if (ctx.stage == Stage::VertexB) {
95 const Id zero{ctx.Const(0.0f)};
96 const Id one{ctx.Const(1.0f)};
97 const Id default_vector{ctx.ConstantComposite(ctx.F32[4], zero, zero, zero, one)};
98 ctx.OpStore(ctx.output_position, default_vector);
99 for (const auto& info : ctx.output_generics) {
100 if (info[0].num_components == 0) {
101 continue;
102 }
103 u32 element{0};
104 while (element < 4) {
105 const auto& element_info{info[element]};
106 const u32 num{element_info.num_components};
107 const Id value{DefaultVarying(ctx, num, element, zero, one, default_vector)};
108 ctx.OpStore(element_info.id, value);
109 element += num;
110 }
111 }
112 }
113 if (ctx.stage == Stage::VertexB || ctx.stage == Stage::Geometry) {
114 SetFixedPipelinePointSize(ctx);
115 }
116}
117
118void EmitEpilogue(EmitContext& ctx) {
119 if (ctx.stage == Stage::VertexB && ctx.runtime_info.convert_depth_mode) {
120 ConvertDepthMode(ctx);
121 }
122 if (ctx.stage == Stage::Fragment) {
123 AlphaTest(ctx);
124 }
125}
126
127void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
128 if (ctx.runtime_info.convert_depth_mode) {
129 ConvertDepthMode(ctx);
130 }
131 if (stream.IsImmediate()) {
132 ctx.OpEmitStreamVertex(ctx.Def(stream));
133 } else {
134 LOG_WARNING(Shader_SPIRV, "Stream is not immediate");
135 ctx.OpEmitStreamVertex(ctx.u32_zero_value);
136 }
137 // Restore fixed pipeline point size after emitting the vertex
138 SetFixedPipelinePointSize(ctx);
139}
140
141void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
142 if (stream.IsImmediate()) {
143 ctx.OpEndStreamPrimitive(ctx.Def(stream));
144 } else {
145 LOG_WARNING(Shader_SPIRV, "Stream is not immediate");
146 ctx.OpEndStreamPrimitive(ctx.u32_zero_value);
147 }
148}
149
150} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp
new file mode 100644
index 000000000..c9f469e90
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp
@@ -0,0 +1,30 @@
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 "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7
8namespace Shader::Backend::SPIRV {
9
10Id EmitUndefU1(EmitContext& ctx) {
11 return ctx.OpUndef(ctx.U1);
12}
13
14Id EmitUndefU8(EmitContext&) {
15 throw NotImplementedException("SPIR-V Instruction");
16}
17
18Id EmitUndefU16(EmitContext&) {
19 throw NotImplementedException("SPIR-V Instruction");
20}
21
22Id EmitUndefU32(EmitContext& ctx) {
23 return ctx.OpUndef(ctx.U32[1]);
24}
25
26Id EmitUndefU64(EmitContext&) {
27 throw NotImplementedException("SPIR-V Instruction");
28}
29
30} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
new file mode 100644
index 000000000..78b1e1ba7
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
@@ -0,0 +1,203 @@
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 "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7
8namespace Shader::Backend::SPIRV {
9namespace {
10Id WarpExtract(EmitContext& ctx, Id value) {
11 const Id local_index{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
12 return ctx.OpVectorExtractDynamic(ctx.U32[1], value, local_index);
13}
14
15Id LoadMask(EmitContext& ctx, Id mask) {
16 const Id value{ctx.OpLoad(ctx.U32[4], mask)};
17 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
18 return ctx.OpCompositeExtract(ctx.U32[1], value, 0U);
19 }
20 return WarpExtract(ctx, value);
21}
22
23void SetInBoundsFlag(IR::Inst* inst, Id result) {
24 IR::Inst* const in_bounds{inst->GetAssociatedPseudoOperation(IR::Opcode::GetInBoundsFromOp)};
25 if (!in_bounds) {
26 return;
27 }
28 in_bounds->SetDefinition(result);
29 in_bounds->Invalidate();
30}
31
32Id ComputeMinThreadId(EmitContext& ctx, Id thread_id, Id segmentation_mask) {
33 return ctx.OpBitwiseAnd(ctx.U32[1], thread_id, segmentation_mask);
34}
35
36Id ComputeMaxThreadId(EmitContext& ctx, Id min_thread_id, Id clamp, Id not_seg_mask) {
37 return ctx.OpBitwiseOr(ctx.U32[1], min_thread_id,
38 ctx.OpBitwiseAnd(ctx.U32[1], clamp, not_seg_mask));
39}
40
41Id GetMaxThreadId(EmitContext& ctx, Id thread_id, Id clamp, Id segmentation_mask) {
42 const Id not_seg_mask{ctx.OpNot(ctx.U32[1], segmentation_mask)};
43 const Id min_thread_id{ComputeMinThreadId(ctx, thread_id, segmentation_mask)};
44 return ComputeMaxThreadId(ctx, min_thread_id, clamp, not_seg_mask);
45}
46
47Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) {
48 return ctx.OpSelect(ctx.U32[1], in_range,
49 ctx.OpSubgroupReadInvocationKHR(ctx.U32[1], value, src_thread_id), value);
50}
51} // Anonymous namespace
52
53Id EmitLaneId(EmitContext& ctx) {
54 const Id id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
55 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
56 return id;
57 }
58 return ctx.OpBitwiseAnd(ctx.U32[1], id, ctx.Const(31U));
59}
60
61Id EmitVoteAll(EmitContext& ctx, Id pred) {
62 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
63 return ctx.OpSubgroupAllKHR(ctx.U1, pred);
64 }
65 const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)};
66 const Id active_mask{WarpExtract(ctx, mask_ballot)};
67 const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))};
68 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)};
69 return ctx.OpIEqual(ctx.U1, lhs, active_mask);
70}
71
72Id EmitVoteAny(EmitContext& ctx, Id pred) {
73 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
74 return ctx.OpSubgroupAnyKHR(ctx.U1, pred);
75 }
76 const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)};
77 const Id active_mask{WarpExtract(ctx, mask_ballot)};
78 const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))};
79 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)};
80 return ctx.OpINotEqual(ctx.U1, lhs, ctx.u32_zero_value);
81}
82
83Id EmitVoteEqual(EmitContext& ctx, Id pred) {
84 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
85 return ctx.OpSubgroupAllEqualKHR(ctx.U1, pred);
86 }
87 const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)};
88 const Id active_mask{WarpExtract(ctx, mask_ballot)};
89 const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))};
90 const Id lhs{ctx.OpBitwiseXor(ctx.U32[1], ballot, active_mask)};
91 return ctx.OpLogicalOr(ctx.U1, ctx.OpIEqual(ctx.U1, lhs, ctx.u32_zero_value),
92 ctx.OpIEqual(ctx.U1, lhs, active_mask));
93}
94
95Id EmitSubgroupBallot(EmitContext& ctx, Id pred) {
96 const Id ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], pred)};
97 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
98 return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U);
99 }
100 return WarpExtract(ctx, ballot);
101}
102
103Id EmitSubgroupEqMask(EmitContext& ctx) {
104 return LoadMask(ctx, ctx.subgroup_mask_eq);
105}
106
107Id EmitSubgroupLtMask(EmitContext& ctx) {
108 return LoadMask(ctx, ctx.subgroup_mask_lt);
109}
110
111Id EmitSubgroupLeMask(EmitContext& ctx) {
112 return LoadMask(ctx, ctx.subgroup_mask_le);
113}
114
115Id EmitSubgroupGtMask(EmitContext& ctx) {
116 return LoadMask(ctx, ctx.subgroup_mask_gt);
117}
118
119Id EmitSubgroupGeMask(EmitContext& ctx) {
120 return LoadMask(ctx, ctx.subgroup_mask_ge);
121}
122
123Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
124 Id segmentation_mask) {
125 const Id not_seg_mask{ctx.OpNot(ctx.U32[1], segmentation_mask)};
126 const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
127 const Id min_thread_id{ComputeMinThreadId(ctx, thread_id, segmentation_mask)};
128 const Id max_thread_id{ComputeMaxThreadId(ctx, min_thread_id, clamp, not_seg_mask)};
129
130 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], index, not_seg_mask)};
131 const Id src_thread_id{ctx.OpBitwiseOr(ctx.U32[1], lhs, min_thread_id)};
132 const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
133
134 SetInBoundsFlag(inst, in_range);
135 return SelectValue(ctx, in_range, value, src_thread_id);
136}
137
138Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
139 Id segmentation_mask) {
140 const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
141 const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
142 const Id src_thread_id{ctx.OpISub(ctx.U32[1], thread_id, index)};
143 const Id in_range{ctx.OpSGreaterThanEqual(ctx.U1, src_thread_id, max_thread_id)};
144
145 SetInBoundsFlag(inst, in_range);
146 return SelectValue(ctx, in_range, value, src_thread_id);
147}
148
149Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
150 Id segmentation_mask) {
151 const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
152 const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
153 const Id src_thread_id{ctx.OpIAdd(ctx.U32[1], thread_id, index)};
154 const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
155
156 SetInBoundsFlag(inst, in_range);
157 return SelectValue(ctx, in_range, value, src_thread_id);
158}
159
160Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
161 Id segmentation_mask) {
162 const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
163 const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
164 const Id src_thread_id{ctx.OpBitwiseXor(ctx.U32[1], thread_id, index)};
165 const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
166
167 SetInBoundsFlag(inst, in_range);
168 return SelectValue(ctx, in_range, value, src_thread_id);
169}
170
171Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle) {
172 const Id three{ctx.Const(3U)};
173 Id mask{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
174 mask = ctx.OpBitwiseAnd(ctx.U32[1], mask, three);
175 mask = ctx.OpShiftLeftLogical(ctx.U32[1], mask, ctx.Const(1U));
176 mask = ctx.OpShiftRightLogical(ctx.U32[1], swizzle, mask);
177 mask = ctx.OpBitwiseAnd(ctx.U32[1], mask, three);
178
179 const Id modifier_a{ctx.OpVectorExtractDynamic(ctx.F32[1], ctx.fswzadd_lut_a, mask)};
180 const Id modifier_b{ctx.OpVectorExtractDynamic(ctx.F32[1], ctx.fswzadd_lut_b, mask)};
181
182 const Id result_a{ctx.OpFMul(ctx.F32[1], op_a, modifier_a)};
183 const Id result_b{ctx.OpFMul(ctx.F32[1], op_b, modifier_b)};
184 return ctx.OpFAdd(ctx.F32[1], result_a, result_b);
185}
186
187Id EmitDPdxFine(EmitContext& ctx, Id op_a) {
188 return ctx.OpDPdxFine(ctx.F32[1], op_a);
189}
190
191Id EmitDPdyFine(EmitContext& ctx, Id op_a) {
192 return ctx.OpDPdyFine(ctx.F32[1], op_a);
193}
194
195Id EmitDPdxCoarse(EmitContext& ctx, Id op_a) {
196 return ctx.OpDPdxCoarse(ctx.F32[1], op_a);
197}
198
199Id EmitDPdyCoarse(EmitContext& ctx, Id op_a) {
200 return ctx.OpDPdyCoarse(ctx.F32[1], op_a);
201}
202
203} // namespace Shader::Backend::SPIRV