-- v2: vkd3d-shader/spirv: Emit a compiler warning if an atomic op is flagged volatile. vkd3d-shader/dxil: Implement the DXIL CMPXCHG instruction. tests/hlsl: Add an InterlockedCompareExchange() TGSM test.
From: Conor McCarthy cmccarthy@codeweavers.com
--- tests/hlsl/tgsm.shader_test | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+)
diff --git a/tests/hlsl/tgsm.shader_test b/tests/hlsl/tgsm.shader_test index 4e0c7a49e..5654254f9 100644 --- a/tests/hlsl/tgsm.shader_test +++ b/tests/hlsl/tgsm.shader_test @@ -133,3 +133,31 @@ probe uav 2 (4) ri (9) probe uav 2 (5) ri (6) probe uav 2 (6) ri (3) probe uav 2 (7) ri (4) + + +[uav 1] +format r32 uint +size (buffer, 1) + +0 + +[compute shader todo] +RWByteAddressBuffer u : register(u1); +groupshared uint m; + + [numthreads(32, 1, 1)] +void main(uint local_idx : SV_GroupIndex) +{ + uint orig; + if (!local_idx) + m = 7; + GroupMemoryBarrierWithGroupSync(); + InterlockedCompareExchange(m, local_idx, local_idx + 32, orig); + GroupMemoryBarrierWithGroupSync(); + if (!local_idx) + u.Store(0, m); +} + +[test] +todo dispatch 1 1 1 +probe uav 1 (0) rui (39)
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/dxil.c | 115 ++++++++++++++++++++++++++++++++++++ tests/hlsl/tgsm.shader_test | 2 +- 2 files changed, 116 insertions(+), 1 deletion(-)
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index 03c5458b6..89a19a908 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -1932,6 +1932,25 @@ static const struct sm6_type *sm6_type_get_pointer_to_type(const struct sm6_type return NULL; }
+static const struct sm6_type *sm6_type_get_cmpxchg_result_struct(struct sm6_parser *sm6) +{ + const struct sm6_type *type; + unsigned int i; + + for (i = 0; i < sm6->type_count; ++i) + { + type = &sm6->types[i]; + if (sm6_type_is_struct(type) && type->u.struc->elem_count == 2 + && sm6_type_is_i32(type->u.struc->elem_types[0]) + && sm6_type_is_bool(type->u.struc->elem_types[1])) + { + return type; + } + } + + return NULL; +} + /* Call for aggregate types only. */ static const struct sm6_type *sm6_type_get_element_type_at_index(const struct sm6_type *type, uint64_t elem_idx) { @@ -2642,6 +2661,18 @@ static bool sm6_value_validate_is_pointer_to_i32(const struct sm6_value *value, return true; }
+static bool sm6_value_validate_is_i32(const struct sm6_value *value, struct sm6_parser *sm6) +{ + if (!sm6_type_is_i32(value->type)) + { + WARN("Operand result type %u is not i32.\n", value->type->class); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND, + "An int32 operand passed to a DXIL instruction is not an int32."); + return false; + } + return true; +} + static const struct sm6_value *sm6_parser_get_value_safe(struct sm6_parser *sm6, unsigned int idx) { if (idx < sm6->value_count) @@ -6094,6 +6125,87 @@ static void sm6_parser_emit_cmp2(struct sm6_parser *sm6, const struct dxil_recor instruction_dst_param_init_ssa_scalar(ins, sm6); }
+static void sm6_parser_emit_cmpxchg(struct sm6_parser *sm6, const struct dxil_record *record, + struct vkd3d_shader_instruction *ins, struct sm6_value *dst) +{ + uint64_t success_ordering, failure_ordering; + struct vkd3d_shader_dst_param *dst_params; + struct vkd3d_shader_src_param *src_params; + const struct sm6_value *ptr, *cmp, *new; + const struct sm6_type *type; + unsigned int i = 0; + bool is_volatile; + uint64_t code; + + if (!(ptr = sm6_parser_get_value_by_ref(sm6, record, NULL, &i)) + || !sm6_value_validate_is_pointer_to_i32(ptr, sm6)) + return; + + if (ptr->u.reg.type != VKD3DSPR_GROUPSHAREDMEM) + { + WARN("Register is not groupshared.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND, + "The destination register for a cmpxchg instruction is not groupshared memory."); + return; + } + + if (!(dst->type = sm6_type_get_cmpxchg_result_struct(sm6))) + { + WARN("Failed to find result struct.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_MODULE, + "Module does not define a result struct type for a cmpxchg instruction."); + return; + } + + type = ptr->type->u.pointer.type; + cmp = sm6_parser_get_value_by_ref(sm6, record, type, &i); + new = sm6_parser_get_value_by_ref(sm6, record, type, &i); + if (!cmp || !new) + return; + + if (!sm6_value_validate_is_i32(cmp, sm6) + || !sm6_value_validate_is_i32(new, sm6) + || !dxil_record_validate_operand_count(record, i + 3, i + 5, sm6)) + { + return; + } + + is_volatile = record->operands[i++]; + success_ordering = record->operands[i++]; + + if ((code = record->operands[i++]) != 1) + FIXME("Ignoring synchronisation scope %"PRIu64".\n", code); + + failure_ordering = (record->operand_count > i) ? record->operands[i++] : success_ordering; + + /* It's currently not possible to specify an atomic ordering in HLSL, and it defaults to seq_cst. */ + if (success_ordering != ORDERING_SEQCST) + FIXME("Unhandled success ordering %"PRIu64".\n", success_ordering); + if (success_ordering != failure_ordering) + FIXME("Unhandled failure ordering %"PRIu64".\n", failure_ordering); + + if (record->operand_count > i && record->operands[i]) + FIXME("Ignoring weak cmpxchg.\n"); + + vsir_instruction_init(ins, &sm6->p.location, VKD3DSIH_IMM_ATOMIC_CMP_EXCH); + ins->flags = is_volatile ? VKD3DARF_SEQ_CST | VKD3DARF_VOLATILE : VKD3DARF_SEQ_CST; + + if (!(src_params = instruction_src_params_alloc(ins, 3, sm6))) + return; + src_param_make_constant_uint(&src_params[0], 0); + src_param_init_from_value(&src_params[1], cmp); + src_param_init_from_value(&src_params[2], new); + + if (!(dst_params = instruction_dst_params_alloc(ins, 2, sm6))) + return; + register_init_ssa_scalar(&dst_params[0].reg, dst->type, dst, sm6); + dst_param_init(&dst_params[0]); + dst_params[1].reg = ptr->u.reg; + dst_param_init(&dst_params[1]); + + dst->u.reg = dst_params[0].reg; +} + static void sm6_parser_emit_extractval(struct sm6_parser *sm6, const struct dxil_record *record, struct vkd3d_shader_instruction *ins, struct sm6_value *dst) { @@ -7079,6 +7191,9 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const case FUNC_CODE_INST_CMP2: sm6_parser_emit_cmp2(sm6, record, ins, dst); break; + case FUNC_CODE_INST_CMPXCHG: + sm6_parser_emit_cmpxchg(sm6, record, ins, dst); + break; case FUNC_CODE_INST_EXTRACTVAL: sm6_parser_emit_extractval(sm6, record, ins, dst); break; diff --git a/tests/hlsl/tgsm.shader_test b/tests/hlsl/tgsm.shader_test index 5654254f9..c18e7c1dd 100644 --- a/tests/hlsl/tgsm.shader_test +++ b/tests/hlsl/tgsm.shader_test @@ -159,5 +159,5 @@ void main(uint local_idx : SV_GroupIndex) }
[test] -todo dispatch 1 1 1 +todo(sm<6) dispatch 1 1 1 probe uav 1 (0) rui (39)
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/spirv.c | 4 ++++ libs/vkd3d-shader/vkd3d_shader_private.h | 1 + 2 files changed, 5 insertions(+)
diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index 61390b09c..264a8ea5c 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -9263,7 +9263,11 @@ static void spirv_compiler_emit_atomic_instruction(struct spirv_compiler *compil val_id = spirv_compiler_emit_load_src_with_type(compiler, &src[1], VKD3DSP_WRITEMASK_0, component_type);
if (instruction->flags & VKD3DARF_VOLATILE) + { WARN("Ignoring 'volatile' attribute.\n"); + spirv_compiler_warning(compiler, VKD3D_SHADER_WARNING_SPV_IGNORING_FLAG, + "Ignoring the 'volatile' attribute flag for atomic instruction %#x.", instruction->handler_idx); + }
memory_semantic = (instruction->flags & VKD3DARF_SEQ_CST) ? SpvMemorySemanticsSequentiallyConsistentMask diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index b07a7bff7..27c025926 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -100,6 +100,7 @@ enum vkd3d_shader_error
VKD3D_SHADER_WARNING_SPV_INVALID_SWIZZLE = 2300, VKD3D_SHADER_WARNING_SPV_INVALID_UAV_FLAGS = 2301, + VKD3D_SHADER_WARNING_SPV_IGNORING_FLAG = 2302,
VKD3D_SHADER_ERROR_RS_OUT_OF_MEMORY = 3000, VKD3D_SHADER_ERROR_RS_INVALID_VERSION = 3001,
No changes, just a rebase. Random test failure in 32-bit `test_queue_wait()` disappeared on retest.
No changes, just a rebase.
Don't do that after I've already approved a MR.
Random test failure in 32-bit `test_queue_wait()` disappeared on retest.
Do we know why that happens?
On Wed Apr 10 14:14:16 2024 +0000, Henri Verbeet wrote:
No changes, just a rebase.
Don't do that after I've already approved a MR.
Random test failure in 32-bit `test_queue_wait()` disappeared on retest.
Do we know why that happens?
One reference remained on the device. I see nothing in this MR which could cause it, so it's probably very rare and random, but still a concern it can happen at all. I haven't looked into it in more detail.