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
1922RWStructuredBuffer<uint> OutMono : register(u0);
2023RWStructuredBuffer<uint> OutSlots : register(u1);
2124RWStructuredBuffer<uint> OutOrig : register(u2);
2225RWStructuredBuffer<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
2829groupshared float SlotsMinusTwoPointFive[256]; // per-thread slots, each -2.5
2930groupshared float MatchMinusFive; // contended, compare matches
3031groupshared float NoMatchSeventySeven; // contended, never matches
3132groupshared float ZeroPositive; // +0.0, compared against -0.0
3233groupshared float NanQuiet; // NaN, compared against NaN
3334groupshared 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)]
3838void 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 ]
173174Results:
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
0 commit comments