summaryrefslogtreecommitdiff
path: root/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
diff options
context:
space:
mode:
authorGravatar bunnei2021-07-25 11:39:04 -0700
committerGravatar GitHub2021-07-25 11:39:04 -0700
commit98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f (patch)
tree816faa96c2c4d291825063433331a8ea4b3d08f1 /src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
parentMerge pull request #6699 from lat9nq/common-threads (diff)
parentshader: Support out of bound local memory reads and immediate writes (diff)
downloadyuzu-98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f.tar.gz
yuzu-98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f.tar.xz
yuzu-98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f.zip
Merge pull request #6585 from ameerj/hades
Shader Decompiler Rewrite
Diffstat (limited to 'src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp')
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp448
1 files changed, 448 insertions, 0 deletions
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