summaryrefslogtreecommitdiff
path: root/src/shader_recompiler/backend/spirv
diff options
context:
space:
mode:
Diffstat (limited to 'src/shader_recompiler/backend/spirv')
-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
23 files changed, 6542 insertions, 0 deletions
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp
new file mode 100644
index 000000000..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