Skip to content

Commit 0c77e1b

Browse files
committed
address Farzon
1 parent ebd6bf4 commit 0c77e1b

2 files changed

Lines changed: 87 additions & 83 deletions

File tree

test/Feature/HLSLLib/InterlockedCompareExchangeFloatBitwise.32.test

Lines changed: 66 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,38 @@
11
#--- source.hlsl
22

3-
// Tests InterlockedCompareExchangeFloatBitwise on groupshared destinations
4-
// with 256 concurrent threads. Comparison is on the bit pattern, not the
5-
// numeric value, so all checks use asuint(). Four checks:
3+
// Tests InterlockedCompareExchangeFloatBitwise on groupshared destinations.
4+
// 256 threads run at the same time. Each test value is exact in float, thus
5+
// the shader compares floats directly. There are four checks:
66
//
7-
// 1. Private slots: a mismatching compare must not store, a following
8-
// matching compare must, and both must report the original value.
9-
// 2. Contended locations: every thread proposes the same new value, so
10-
// exactly one thread may observe the original compare value. A
11-
// never-matching compare must leave the destination alone.
12-
// 3. Bitwise semantics: +0.0 compared against -0.0 must not store even
13-
// though the two are numerically equal, and a NaN compared against an
14-
// identical NaN bit pattern must store even though NaN != NaN.
15-
// 4. CounterZero: thread `i` only advances the counter from `i` to `i+1`,
16-
// so over 256 barrier-separated attempts each thread succeeds exactly
17-
// once and the counter ends at 256. It must never decrease.
7+
// 1. Private slots. A compare that does not match must not store. A later
8+
// compare that matches must store. Both report the initial value.
9+
// 2. Contended locations. All threads propose the same value. Each thread
10+
// reports the value that the winner replaced, or the value that the
11+
// winner stored. Only one thread can report the replaced value. That
12+
// thread claims a second location to prove that it is the only winner.
13+
// A compare that never matches must not change the destination.
14+
// 3. Bit patterns. +0.0 against -0.0 must not store. A NaN against the
15+
// same NaN bits must store. A buffer supplies the NaN, because HLSL
16+
// has no NaN literal.
17+
// 4. CounterZero. Thread `i` moves the counter from `i` to `i+1` only.
18+
// The shader makes 256 attempts, with a barrier between them. Each
19+
// thread wins one time, the counter stops at 256, and the counter must
20+
// never decrease.
1821

1922
RWStructuredBuffer<uint> OutMono : register(u0);
2023
RWStructuredBuffer<uint> OutSlots : register(u1);
2124
RWStructuredBuffer<uint> OutOrig : register(u2);
2225
RWStructuredBuffer<uint> OutWins : register(u3);
23-
RWStructuredBuffer<uint> OutFinal : register(u4);
24-
25-
static const uint NanBits = 0x7FC00000u; // quiet NaN
26-
static const uint NegZeroBits = 0x80000000u;
26+
RWStructuredBuffer<float> OutFinal : register(u4);
27+
RWStructuredBuffer<float> NanIn : register(u5);
2728

2829
groupshared float SlotsMinusTwoPointFive[256]; // per-thread slots, each -2.5
2930
groupshared float MatchMinusFive; // contended, compare matches
3031
groupshared float NoMatchSeventySeven; // contended, never matches
3132
groupshared float ZeroPositive; // +0.0, compared against -0.0
3233
groupshared float NanQuiet; // NaN, compared against NaN
3334
groupshared float CounterZero; // stepped from 0.0 to 256.0
34-
groupshared uint MatchWinCountZero; // threads that saw -5.0
35-
groupshared uint NanWinCountZero; // threads that saw the NaN
35+
groupshared float ClaimZero; // 0.0, claimed by the winner
3636

3737
[numthreads(256, 1, 1)]
3838
void main(uint3 GTID : SV_GroupThreadID) {
@@ -41,50 +41,48 @@ void main(uint3 GTID : SV_GroupThreadID) {
4141
MatchMinusFive = -5.0f;
4242
NoMatchSeventySeven = 77.0f;
4343
ZeroPositive = 0.0f;
44-
NanQuiet = asfloat(NanBits);
44+
NanQuiet = NanIn[0];
4545
CounterZero = 0.0f;
46-
MatchWinCountZero = 0u;
47-
NanWinCountZero = 0u;
46+
ClaimZero = 0.0f;
4847
}
4948
GroupMemoryBarrierWithGroupSync();
5049

51-
// Check 1: both calls report the initial value, only the second stores.
50+
// Check 1. Both calls report the initial value. Only the second stores.
5251
float OrigNoStore, OrigStore;
5352
InterlockedCompareExchangeFloatBitwise(SlotsMinusTwoPointFive[GTID.x], 1.0f,
5453
99.0f, OrigNoStore);
5554
InterlockedCompareExchangeFloatBitwise(SlotsMinusTwoPointFive[GTID.x], -2.5f,
5655
(float)GTID.x + 1000.0f, OrigStore);
5756

58-
OutSlots[GTID.x] = (asuint(OrigNoStore) == asuint(-2.5f) &&
59-
asuint(OrigStore) == asuint(-2.5f) &&
60-
asuint(SlotsMinusTwoPointFive[GTID.x]) ==
61-
asuint((float)GTID.x + 1000.0f)) ? 1u : 0u;
57+
OutSlots[GTID.x] = (OrigNoStore == -2.5f && OrigStore == -2.5f &&
58+
SlotsMinusTwoPointFive[GTID.x] ==
59+
(float)GTID.x + 1000.0f) ? 1u : 0u;
6260

63-
// Check 2: a matching compare reports either the value the winner replaced
64-
// or the value it stored; a never-matching compare reports it untouched.
61+
// Check 2. A compare that matches reports the value that the winner
62+
// replaced, or the value that the winner stored. A compare that never
63+
// matches reports the unchanged destination.
6564
float OrigMatch, OrigNoMatch;
6665
InterlockedCompareExchangeFloatBitwise(MatchMinusFive, -5.0f, 42.0f,
6766
OrigMatch);
6867
InterlockedCompareExchangeFloatBitwise(NoMatchSeventySeven, 78.0f, -1.0f,
6968
OrigNoMatch);
70-
if (asuint(OrigMatch) == asuint(-5.0f))
71-
InterlockedAdd(MatchWinCountZero, 1u);
7269

73-
// Check 3: -0.0 must not match +0.0, and NaN must match its own bits.
70+
// Only the thread that received the original tries to claim. Thus a
71+
// second claim shows that the exchange reported the original to more
72+
// than one thread.
73+
float Claim = 0.0f;
74+
if (OrigMatch == -5.0f)
75+
InterlockedCompareExchangeFloatBitwise(ClaimZero, 0.0f, 1.0f, Claim);
76+
77+
// Check 3. The final value shows whether each compare matched. A compare
78+
// of NaN against NaN gives false, thus the reported value cannot show it.
7479
float OrigZero, OrigNan;
75-
InterlockedCompareExchangeFloatBitwise(ZeroPositive, asfloat(NegZeroBits),
76-
99.0f, OrigZero);
77-
InterlockedCompareExchangeFloatBitwise(NanQuiet, asfloat(NanBits), 7.0f,
78-
OrigNan);
79-
if (asuint(OrigNan) == NanBits)
80-
InterlockedAdd(NanWinCountZero, 1u);
81-
82-
OutOrig[GTID.x] = ((asuint(OrigMatch) == asuint(-5.0f) ||
83-
asuint(OrigMatch) == asuint(42.0f)) &&
84-
asuint(OrigNoMatch) == asuint(77.0f) &&
85-
asuint(OrigZero) == 0u &&
86-
(asuint(OrigNan) == NanBits ||
87-
asuint(OrigNan) == asuint(7.0f))) ? 1u : 0u;
80+
InterlockedCompareExchangeFloatBitwise(ZeroPositive, -0.0f, 99.0f, OrigZero);
81+
InterlockedCompareExchangeFloatBitwise(NanQuiet, NanIn[0], 7.0f, OrigNan);
82+
83+
OutOrig[GTID.x] = ((OrigMatch == -5.0f || OrigMatch == 42.0f) &&
84+
OrigNoMatch == 77.0f && OrigZero == 0.0f &&
85+
Claim == 0.0f) ? 1u : 0u;
8886

8987
// Check 4.
9088
float CompareValue = (float)GTID.x;
@@ -96,7 +94,7 @@ void main(uint3 GTID : SV_GroupThreadID) {
9694
float Orig;
9795
InterlockedCompareExchangeFloatBitwise(CounterZero, CompareValue, NewValue,
9896
Orig);
99-
if (asuint(Orig) == asuint(CompareValue))
97+
if (Orig == CompareValue)
10098
++Wins;
10199
GroupMemoryBarrierWithGroupSync();
102100
float Cur = CounterZero;
@@ -110,13 +108,12 @@ void main(uint3 GTID : SV_GroupThreadID) {
110108
GroupMemoryBarrierWithGroupSync();
111109

112110
if (GTID.x == 0) {
113-
OutFinal[0] = asuint(CounterZero); // 256.0
114-
OutFinal[1] = asuint(MatchMinusFive); // 42.0
115-
OutFinal[2] = asuint(NoMatchSeventySeven); // 77.0
116-
OutFinal[3] = asuint(ZeroPositive); // +0.0, never stored
117-
OutFinal[4] = asuint(NanQuiet); // 7.0
118-
OutFinal[5] = MatchWinCountZero; // 1
119-
OutFinal[6] = NanWinCountZero; // 1
111+
OutFinal[0] = CounterZero; // 256.0
112+
OutFinal[1] = MatchMinusFive; // 42.0
113+
OutFinal[2] = NoMatchSeventySeven; // 77.0
114+
OutFinal[3] = ZeroPositive; // +0.0, -0.0 must not have matched
115+
OutFinal[4] = NanQuiet; // 7.0, the NaN must have matched
116+
OutFinal[5] = ClaimZero; // 1.0, exactly one thread claimed
120117
}
121118
}
122119

@@ -163,13 +160,17 @@ Buffers:
163160
Stride: 4
164161
FillSize: 1024
165162
- Name: OutFinal
166-
Format: UInt32
163+
Format: Float32
167164
Stride: 4
168-
FillSize: 28
165+
FillSize: 24
169166
- Name: ExpectedFinal
170-
Format: Hex32
167+
Format: Float32
168+
Stride: 4
169+
Data: [ 256.0, 42.0, 77.0, 0.0, 7.0, 1.0 ]
170+
- Name: NanIn
171+
Format: Float32
171172
Stride: 4
172-
Data: [ 0x43800000, 0x42280000, 0x429A0000, 0x0, 0x40E00000, 0x1, 0x1 ]
173+
Data: [ nan ]
173174
Results:
174175
- Result: TestMono
175176
Rule: BufferExact
@@ -228,6 +229,13 @@ DescriptorSets:
228229
Space: 0
229230
VulkanBinding:
230231
Binding: 4
232+
- Name: NanIn
233+
Kind: RWStructuredBuffer
234+
DirectXBinding:
235+
Register: 5
236+
Space: 0
237+
VulkanBinding:
238+
Binding: 5
231239
...
232240
#--- end
233241

test/Feature/HLSLLib/InterlockedCompareExchangeFloatBitwise.resources.32.test

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
#--- source.hlsl
22

3-
// Tests InterlockedCompareExchangeFloatBitwise on the HLSL-legal
4-
// non-groupshared (resource) destination types:
3+
// Tests InterlockedCompareExchangeFloatBitwise on the resource destination
4+
// types that HLSL permits:
55
//
66
// * RWStructuredBuffer<float>[i] (free function)
77
// * RWBuffer<float>[i] (typed UAV)
88
// * RWTexture2D<float>[coord] (typed UAV)
99
// * RWByteAddressBuffer::InterlockedCompareExchangeFloatBitwise (method)
1010
//
11-
// Every destination covers a matching and a never-matching compare and
12-
// checks the reported original. RWStructuredBuffer and RWByteAddressBuffer
13-
// additionally run the stepped counter, per-thread success count and
14-
// monotonicity check described in
15-
// InterlockedCompareExchangeFloatBitwise.32.test, which is also where the
16-
// bit-pattern semantics (-0.0 vs +0.0, NaN) are covered.
17-
11+
// For each type, the shader makes one compare that matches and one compare
12+
// that never matches. It then checks the reported original value.
13+
// RWStructuredBuffer and RWByteAddressBuffer also run the stepped counter,
14+
// the count of wins for each thread, and the monotonicity check. The file
15+
// InterlockedCompareExchangeFloatBitwise.32.test describes these checks. It
16+
// also covers the bit patterns (-0.0 against +0.0, and NaN).
1817
RWStructuredBuffer<float> SBufF : register(u0);
1918
RWBuffer<float> TBufF : register(u1);
2019
RWTexture2D<float> Tex2D : register(u2);
@@ -34,9 +33,9 @@ void main(uint3 GTID : SV_GroupThreadID) {
3433
TBufF[1] = 9.0f; // compare never matches
3534
Tex2D[uint2(0, 0)] = -5.0f; // compare matches
3635
Tex2D[uint2(1, 0)] = 9.0f; // compare never matches
37-
BABuf.Store(0, asuint(-5.0f)); // compare matches
38-
BABuf.Store(4, asuint(44.0f)); // compare never matches
39-
BABuf.Store(8, asuint(0.0f)); // stepped counter
36+
BABuf.Store<float>(0, -5.0f); // compare matches
37+
BABuf.Store<float>(4, 44.0f); // compare never matches
38+
BABuf.Store<float>(8, 0.0f); // stepped counter
4039
}
4140
DeviceMemoryBarrierWithGroupSync();
4241

@@ -65,16 +64,13 @@ void main(uint3 GTID : SV_GroupThreadID) {
6564
BABuf.InterlockedCompareExchangeFloatBitwise(4, 45.0f, -1.0f,
6665
OrigBABufNoMatch);
6766

68-
OutOrig[GTID.x] = (asuint(OrigSBufF) == asuint(9.0f) &&
69-
(asuint(OrigTBufFMatch) == asuint(-5.0f) ||
70-
asuint(OrigTBufFMatch) == asuint(77.0f)) &&
71-
asuint(OrigTBufFNoMatch) == asuint(9.0f) &&
72-
(asuint(OrigTexMatch) == asuint(-5.0f) ||
73-
asuint(OrigTexMatch) == asuint(88.0f)) &&
74-
asuint(OrigTexNoMatch) == asuint(9.0f) &&
75-
(asuint(OrigBABufMatch) == asuint(-5.0f) ||
76-
asuint(OrigBABufMatch) == asuint(123.0f)) &&
77-
asuint(OrigBABufNoMatch) == asuint(44.0f)) ? 1u : 0u;
67+
OutOrig[GTID.x] = (OrigSBufF == 9.0f &&
68+
(OrigTBufFMatch == -5.0f || OrigTBufFMatch == 77.0f) &&
69+
OrigTBufFNoMatch == 9.0f &&
70+
(OrigTexMatch == -5.0f || OrigTexMatch == 88.0f) &&
71+
OrigTexNoMatch == 9.0f &&
72+
(OrigBABufMatch == -5.0f || OrigBABufMatch == 123.0f) &&
73+
OrigBABufNoMatch == 44.0f) ? 1u : 0u;
7874

7975
// Stepped counter on both a structured and a byte-address destination.
8076
float CompareValue = (float)GTID.x;
@@ -89,18 +85,18 @@ void main(uint3 GTID : SV_GroupThreadID) {
8985
float OrigS, OrigB;
9086
InterlockedCompareExchangeFloatBitwise(SBufF[0], CompareValue, NewValue,
9187
OrigS);
92-
if (asuint(OrigS) == asuint(CompareValue))
88+
if (OrigS == CompareValue)
9389
++WinsSBufF;
9490
BABuf.InterlockedCompareExchangeFloatBitwise(8, CompareValue, NewValue,
9591
OrigB);
96-
if (asuint(OrigB) == asuint(CompareValue))
92+
if (OrigB == CompareValue)
9793
++WinsBABuf;
9894
DeviceMemoryBarrierWithGroupSync();
9995
float CurS = SBufF[0];
10096
if (CurS < PrevSBufF)
10197
MonoSBufF = 0u;
10298
PrevSBufF = CurS;
103-
float CurB = asfloat(BABuf.Load(8));
99+
float CurB = BABuf.Load<float>(8);
104100
if (CurB < PrevBABuf)
105101
MonoBABuf = 0u;
106102
PrevBABuf = CurB;

0 commit comments

Comments
 (0)