-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathHashGridCommon.h
304 lines (250 loc) · 11.6 KB
/
HashGridCommon.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
/*
* Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved.
*
* NVIDIA CORPORATION and its licensors retain all intellectual property
* and proprietary rights in and to this software, related documentation
* and any modifications thereto. Any use, reproduction, disclosure or
* distribution of this software and related documentation without an express
* license agreement from NVIDIA CORPORATION is strictly prohibited.
*/
// Constants
#define HASH_GRID_POSITION_BIT_NUM 17
#define HASH_GRID_POSITION_BIT_MASK ((1u << HASH_GRID_POSITION_BIT_NUM) - 1)
#define HASH_GRID_LEVEL_BIT_NUM 10
#define HASH_GRID_LEVEL_BIT_MASK ((1u << HASH_GRID_LEVEL_BIT_NUM) - 1)
#define HASH_GRID_NORMAL_BIT_NUM 3
#define HASH_GRID_NORMAL_BIT_MASK ((1u << HASH_GRID_NORMAL_BIT_NUM) - 1)
#define HASH_GRID_HASH_MAP_BUCKET_SIZE 32
#define HASH_GRID_INVALID_HASH_KEY 0
#define HASH_GRID_INVALID_CACHE_INDEX 0xFFFFFFFF
// Tweakable parameters
#ifndef HASH_GRID_USE_NORMALS
#define HASH_GRID_USE_NORMALS 1 // account for the normal data in the hash key
#endif
#ifndef HASH_GRID_ALLOW_COMPACTION
#define HASH_GRID_ALLOW_COMPACTION (HASH_GRID_HASH_MAP_BUCKET_SIZE == 32)
#endif
#ifndef HASH_GRID_POSITION_OFFSET
#define HASH_GRID_POSITION_OFFSET float3(0.0f, 0.0f, 0.0f)
#endif
#ifndef HASH_GRID_POSITION_BIAS
#define HASH_GRID_POSITION_BIAS 1e-4f // may require adjustment for extreme scene scales
#endif
#ifndef HASH_GRID_NORMAL_BIAS
#define HASH_GRID_NORMAL_BIAS 1e-3f
#endif
#define HashGridIndex uint
#define HashGridKey uint64_t
struct HashGridParameters
{
float3 cameraPosition;
float logarithmBase;
float sceneScale;
float levelBias;
};
float HashGridLogBase(float x, float base)
{
return log(x) / log(base);
}
uint HashGridGetBaseSlot(uint slot, uint capacity)
{
#if HASH_GRID_ALLOW_COMPACTION
return (slot / HASH_GRID_HASH_MAP_BUCKET_SIZE) * HASH_GRID_HASH_MAP_BUCKET_SIZE;
#else // !HASH_GRID_ALLOW_COMPACTION
return min(slot, capacity - HASH_GRID_HASH_MAP_BUCKET_SIZE);
#endif // !HASH_GRID_ALLOW_COMPACTION
}
// http://burtleburtle.net/bob/hash/integer.html
uint HashGridHashJenkins32(uint a)
{
a = (a + 0x7ed55d16) + (a << 12);
a = (a ^ 0xc761c23c) ^ (a >> 19);
a = (a + 0x165667b1) + (a << 5);
a = (a + 0xd3a2646c) ^ (a << 9);
a = (a + 0xfd7046c5) + (a << 3);
a = (a ^ 0xb55a4f09) ^ (a >> 16);
return a;
}
uint HashGridHash32(HashGridKey hashKey)
{
return HashGridHashJenkins32(uint((hashKey >> 0) & 0xFFFFFFFF)) ^ HashGridHashJenkins32(uint((hashKey >> 32) & 0xFFFFFFFF));
}
uint HashGridGetLevel(float3 samplePosition, HashGridParameters gridParameters)
{
const float distance2 = dot(gridParameters.cameraPosition - samplePosition, gridParameters.cameraPosition - samplePosition);
return uint(clamp(0.5f * HashGridLogBase(distance2, gridParameters.logarithmBase) + gridParameters.levelBias, 1.0f, float(HASH_GRID_LEVEL_BIT_MASK)));
}
float HashGridGetVoxelSize(uint gridLevel, HashGridParameters gridParameters)
{
return pow(gridParameters.logarithmBase, gridLevel) / (gridParameters.sceneScale * pow(gridParameters.logarithmBase, gridParameters.levelBias));
}
// Based on logarithmic caching by Johannes Jendersie
int4 HashGridCalculatePositionLog(float3 samplePosition, HashGridParameters gridParameters)
{
samplePosition += float3(HASH_GRID_POSITION_BIAS, HASH_GRID_POSITION_BIAS, HASH_GRID_POSITION_BIAS);
uint gridLevel = HashGridGetLevel(samplePosition, gridParameters);
float voxelSize = HashGridGetVoxelSize(gridLevel, gridParameters);
int3 gridPosition = int3(floor(samplePosition / voxelSize));
return int4(gridPosition.xyz, gridLevel);
}
HashGridKey HashGridComputeSpatialHash(float3 samplePosition, float3 sampleNormal, HashGridParameters gridParameters)
{
uint4 gridPosition = uint4(HashGridCalculatePositionLog(samplePosition, gridParameters));
HashGridKey hashKey = ((uint64_t(gridPosition.x) & HASH_GRID_POSITION_BIT_MASK) << (HASH_GRID_POSITION_BIT_NUM * 0)) |
((uint64_t(gridPosition.y) & HASH_GRID_POSITION_BIT_MASK) << (HASH_GRID_POSITION_BIT_NUM * 1)) |
((uint64_t(gridPosition.z) & HASH_GRID_POSITION_BIT_MASK) << (HASH_GRID_POSITION_BIT_NUM * 2)) |
((uint64_t(gridPosition.w) & HASH_GRID_LEVEL_BIT_MASK) << (HASH_GRID_POSITION_BIT_NUM * 3));
#if HASH_GRID_USE_NORMALS
uint normalBits =
(sampleNormal.x + HASH_GRID_NORMAL_BIAS >= 0 ? 0 : 1) +
(sampleNormal.y + HASH_GRID_NORMAL_BIAS >= 0 ? 0 : 2) +
(sampleNormal.z + HASH_GRID_NORMAL_BIAS >= 0 ? 0 : 4);
hashKey |= (uint64_t(normalBits) << (HASH_GRID_POSITION_BIT_NUM * 3 + HASH_GRID_LEVEL_BIT_NUM));
#endif // HASH_GRID_USE_NORMALS
return hashKey;
}
float3 HashGridGetPositionFromKey(const HashGridKey hashKey, HashGridParameters gridParameters)
{
const int signBit = 1 << (HASH_GRID_POSITION_BIT_NUM - 1);
const int signMask = ~((1 << HASH_GRID_POSITION_BIT_NUM) - 1);
int3 gridPosition;
gridPosition.x = int((hashKey >> (HASH_GRID_POSITION_BIT_NUM * 0)) & HASH_GRID_POSITION_BIT_MASK);
gridPosition.y = int((hashKey >> (HASH_GRID_POSITION_BIT_NUM * 1)) & HASH_GRID_POSITION_BIT_MASK);
gridPosition.z = int((hashKey >> (HASH_GRID_POSITION_BIT_NUM * 2)) & HASH_GRID_POSITION_BIT_MASK);
// Fix negative coordinates
gridPosition.x = (gridPosition.x & signBit) != 0 ? gridPosition.x | signMask : gridPosition.x;
gridPosition.y = (gridPosition.y & signBit) != 0 ? gridPosition.y | signMask : gridPosition.y;
gridPosition.z = (gridPosition.z & signBit) != 0 ? gridPosition.z | signMask : gridPosition.z;
uint gridLevel = uint((hashKey >> HASH_GRID_POSITION_BIT_NUM * 3) & HASH_GRID_LEVEL_BIT_MASK);
float voxelSize = HashGridGetVoxelSize(gridLevel, gridParameters);
float3 samplePosition = (gridPosition + 0.5f) * voxelSize;
return samplePosition;
}
struct HashMapData
{
uint capacity;
RW_STRUCTURED_BUFFER(hashEntriesBuffer, uint64_t);
#if !HASH_GRID_ENABLE_64_BIT_ATOMICS
RW_STRUCTURED_BUFFER(lockBuffer, uint);
#endif // !HASH_GRID_ENABLE_64_BIT_ATOMICS
};
void HashMapAtomicCompareExchange(in HashMapData hashMapData, in uint dstOffset, in uint64_t compareValue, in uint64_t value, out uint64_t originalValue)
{
#if HASH_GRID_ENABLE_64_BIT_ATOMICS
#if SHARC_ENABLE_GLSL
originalValue = InterlockedCompareExchange(BUFFER_AT_OFFSET(hashMapData.hashEntriesBuffer, dstOffset), compareValue, value);
#else // !SHARC_ENABLE_GLSL
InterlockedCompareExchange(BUFFER_AT_OFFSET(hashMapData.hashEntriesBuffer, dstOffset), compareValue, value, originalValue);
#endif // !SHARC_ENABLE_GLSL
#else // !HASH_GRID_ENABLE_64_BIT_ATOMICS
// ANY rearangments to the code below lead to device hang if fuse is unlimited
const uint cLock = 0xAAAAAAAA;
uint fuse = 0;
const uint fuseLength = 8;
bool busy = true;
while (busy && fuse < fuseLength)
{
uint state;
InterlockedExchange(hashMapData.lockBuffer[dstOffset], cLock, state);
busy = state != 0;
if (state != cLock)
{
originalValue = BUFFER_AT_OFFSET(hashMapData.hashEntriesBuffer, dstOffset);
if (originalValue == compareValue)
BUFFER_AT_OFFSET(hashMapData.hashEntriesBuffer, dstOffset) = value;
InterlockedExchange(hashMapData.lockBuffer[dstOffset], state, fuse);
fuse = fuseLength;
}
++fuse;
}
#endif // !HASH_GRID_ENABLE_64_BIT_ATOMICS
}
bool HashMapInsert(in HashMapData hashMapData, const HashGridKey hashKey, out HashGridIndex cacheIndex)
{
uint hash = HashGridHash32(hashKey);
uint slot = hash % hashMapData.capacity;
uint initSlot = slot;
HashGridKey prevHashGridKey = HASH_GRID_INVALID_HASH_KEY;
const uint baseSlot = HashGridGetBaseSlot(slot, hashMapData.capacity);
for (uint bucketOffset = 0; bucketOffset < HASH_GRID_HASH_MAP_BUCKET_SIZE; ++bucketOffset)
{
HashMapAtomicCompareExchange(hashMapData, baseSlot + bucketOffset, HASH_GRID_INVALID_HASH_KEY, hashKey, prevHashGridKey);
if (prevHashGridKey == HASH_GRID_INVALID_HASH_KEY || prevHashGridKey == hashKey)
{
cacheIndex = baseSlot + bucketOffset;
return true;
}
}
cacheIndex = 0;
return false;
}
bool HashMapFind(in HashMapData hashMapData, const HashGridKey hashKey, inout HashGridIndex cacheIndex)
{
uint hash = HashGridHash32(hashKey);
uint slot = hash % hashMapData.capacity;
const uint baseSlot = HashGridGetBaseSlot(slot, hashMapData.capacity);
for (uint bucketOffset = 0; bucketOffset < HASH_GRID_HASH_MAP_BUCKET_SIZE; ++bucketOffset)
{
HashGridKey storedHashKey = BUFFER_AT_OFFSET(hashMapData.hashEntriesBuffer, baseSlot + bucketOffset);
if (storedHashKey == hashKey)
{
cacheIndex = baseSlot + bucketOffset;
return true;
}
#if HASH_GRID_ALLOW_COMPACTION
else if (storedHashKey == HASH_GRID_INVALID_HASH_KEY)
{
return false;
}
#endif // HASH_GRID_ALLOW_COMPACTION
}
return false;
}
HashGridIndex HashMapInsertEntry(in HashMapData hashMapData, float3 samplePosition, float3 sampleNormal, HashGridParameters gridParameters)
{
HashGridIndex cacheIndex = HASH_GRID_INVALID_CACHE_INDEX;
const HashGridKey hashKey = HashGridComputeSpatialHash(samplePosition, sampleNormal, gridParameters);
bool successful = HashMapInsert(hashMapData, hashKey, cacheIndex);
return cacheIndex;
}
HashGridIndex HashMapFindEntry(in HashMapData hashMapData, float3 samplePosition, float3 sampleNormal, HashGridParameters gridParameters)
{
HashGridIndex cacheIndex = HASH_GRID_INVALID_CACHE_INDEX;
const HashGridKey hashKey = HashGridComputeSpatialHash(samplePosition, sampleNormal, gridParameters);
bool successful = HashMapFind(hashMapData, hashKey, cacheIndex);
return cacheIndex;
}
// Debug functions
float3 HashGridGetColorFromHash32(uint hash)
{
float3 color;
color.x = ((hash >> 0) & 0x3ff) / 1023.0f;
color.y = ((hash >> 11) & 0x7ff) / 2047.0f;
color.z = ((hash >> 22) & 0x7ff) / 2047.0f;
return color;
}
// Debug visualization
float3 HashGridDebugColoredHash(float3 samplePosition, HashGridParameters gridParameters)
{
HashGridKey hashKey = HashGridComputeSpatialHash(samplePosition, float3(0, 0, 0), gridParameters);
uint gridLevel = HashGridGetLevel(samplePosition, gridParameters);
float3 color = HashGridGetColorFromHash32(HashGridHash32(hashKey)) * HashGridGetColorFromHash32(HashGridHashJenkins32(gridLevel)).xyz;
return color;
}
float3 HashGridDebugOccupancy(uint2 pixelPosition, uint2 screenSize, HashMapData hashMapData)
{
const uint elementSize = 7;
const uint borderSize = 1;
const uint blockSize = elementSize + borderSize;
uint rowNum = screenSize.y / blockSize;
uint rowIndex = pixelPosition.y / blockSize;
uint columnIndex = pixelPosition.x / blockSize;
uint elementIndex = (columnIndex / HASH_GRID_HASH_MAP_BUCKET_SIZE) * (rowNum * HASH_GRID_HASH_MAP_BUCKET_SIZE) + rowIndex * HASH_GRID_HASH_MAP_BUCKET_SIZE + (columnIndex % HASH_GRID_HASH_MAP_BUCKET_SIZE);
if (elementIndex < hashMapData.capacity && ((pixelPosition.x % blockSize) < elementSize && (pixelPosition.y % blockSize) < elementSize))
{
HashGridKey storedHashGridKey = BUFFER_AT_OFFSET(hashMapData.hashEntriesBuffer, elementIndex);
if (storedHashGridKey != HASH_GRID_INVALID_HASH_KEY)
return float3(0.0f, 1.0f, 0.0f);
}
return float3(0.0f, 0.0f, 0.0f);
}