Photon Engine 2.0.0-beta
A physically based renderer.
Loading...
Searching...
No Matches
hash.ipp
Go to the documentation of this file.
1#pragma once
2
3#include "Math/hash.h"
4#include "Math/TVector3.h"
5
6#include <Common/assertion.h>
7
8#include <type_traits>
9#include <cmath>
10#include <bit>
11#include <algorithm>
12#include <climits>
13#include <utility>
14
15namespace ph::math
16{
17
18template<typename Integer>
19inline std::size_t discrete_spatial_hash(
20 const Integer x,
21 const Integer y,
22 const Integer z,
23 const std::size_t hashTableSize)
24{
25 static_assert(std::is_integral_v<Integer>);
26
27 PH_ASSERT_GT(hashTableSize, 0);
28
29 return ((static_cast<std::size_t>(x) * 73856093) ^
30 (static_cast<std::size_t>(y) * 19349663) ^
31 (static_cast<std::size_t>(z) * 83492791)) % hashTableSize;
32}
33
34template<typename Integer>
35inline std::size_t discrete_spatial_hash(
36 const Integer x,
37 const Integer y,
38 const std::size_t hashTableSize)
39{
40 static_assert(std::is_integral_v<Integer>);
41
42 PH_ASSERT_GT(hashTableSize, 0);
43
44 return ((static_cast<std::size_t>(x) * 73856093) ^
45 (static_cast<std::size_t>(y) * 83492791)) % hashTableSize;
46}
47
48template<std::integral T>
49inline std::size_t discrete_spatial_hash(const TVector3<T>& point, const std::size_t hashTableSize)
50{
51 return discrete_spatial_hash(point.x, point.y, point.z, hashTableSize);
52}
53
54template<std::floating_point T>
55inline std::size_t discrete_spatial_hash(
56 const TVector3<T>& point,
57 const TVector3<T>& cellSize,
58 const std::size_t hashTableSize)
59{
60 PH_ASSERT_GT(cellSize.x, 0);
61 PH_ASSERT_GT(cellSize.y, 0);
62 PH_ASSERT_GT(cellSize.z, 0);
63
65 static_cast<std::size_t>(std::floor(point.x / cellSize.x)),
66 static_cast<std::size_t>(std::floor(point.y / cellSize.y)),
67 static_cast<std::size_t>(std::floor(point.z / cellSize.z)),
68 hashTableSize);
69}
70
71inline uint32 murmur3_bit_mix_32(uint32 v)
72{
73 v ^= v >> 16;
74 v *= 0x85EBCA6BUL;
75 v ^= v >> 13;
76 v *= 0xC2B2AE35UL;
77 v ^= v >> 16;
78
79 return v;
80}
81
82inline uint64 murmur3_bit_mix_64(uint64 v)
83{
84 v ^= (v >> 33);
85 v *= 0xFF51AFD7ED558CCDULL;
86 v ^= (v >> 33);
87 v *= 0xC4CEB9FE1A85EC53ULL;
88 v ^= (v >> 33);
89
90 return v;
91}
92
93inline uint64 murmur3_v13_bit_mix_64(uint64 v)
94{
95 v ^= (v >> 30);
96 v *= 0xBF58476D1CE4E5B9ULL;
97 v ^= (v >> 27);
98 v *= 0x94D049BB133111EBULL;
99 v ^= (v >> 31);
100
101 return v;
102}
103
104inline uint64 moremur_bit_mix_64(uint64 v)
105{
106 // The constants were derived by Pelle Evensen:
107 // https://mostlymangling.blogspot.com/2019/12/stronger-better-morer-moremur-better.html
108
109 v ^= v >> 27;
110 v *= 0x3C79AC492BA7B653ULL;
111 v ^= v >> 33;
112 v *= 0x1C69B3F74AC4AE35ULL;
113 v ^= v >> 27;
114
115 return v;
116}
117
118template<typename T, typename BitMixerType>
119inline uint32 murmur3_32(const T& data, const uint32 seed)
120{
121 return murmur3_32(&data, 1, BitMixerType{}, seed);
122}
123
124template<typename T, typename BitMixerType>
125inline uint32 murmur3_32(
126 const T* const data,
127 const std::size_t dataSize,
128 BitMixerType&& bitMixer,
129 const uint32 seed)
130{
131 /*
132 References:
133 [1] Wiki: https://en.wikipedia.org/wiki/MurmurHash (`murmur3_32()`)
134 [2] aappleby's smhasher: https://github.com/aappleby/smhasher/ (`MurmurHash3_x86_32()`)
135 */
136
137 static_assert(CHAR_BIT == 8);
138 static_assert(std::is_trivially_copyable_v<T>,
139 "`T` should be trivially copyable to be able to interpret it as bytes.");
140
141 constexpr uint32 c1 = 0xCC9E2D51UL;
142 constexpr uint32 c2 = 0x1B873593UL;
143 constexpr int r1 = 15;
144 constexpr int r2 = 13;
145 constexpr uint32 m = 5;
146 constexpr uint32 n = 0xE6546B64UL;
147
148 auto const bytes = reinterpret_cast<const uint8*>(data);
149 const std::size_t numBytes = dataSize * sizeof(T);
150 const std::size_t numBlocks = numBytes / 4;
151
152 uint32 h1 = seed;
153
154 // Body
155
156 // Read in blocks of 4 bytes
157 for(uint32 bi = 0; bi < numBlocks; ++bi)
158 {
159 const auto byteIndex = bi * 4;
160 PH_ASSERT_LT(byteIndex, numBytes);
161
162 uint32 block32;
163 std::copy_n(bytes + byteIndex, 4, reinterpret_cast<uint8*>(&block32));
164
165 // To remove a source of differing results across endiannesses, perform byte swap on
166 // big-endian CPUs (a swap here has no effects on hash properties though)
167 if constexpr(std::endian::native == std::endian::big)
168 {
169 block32 = std::byteswap(block32);
170 }
171
172 uint32 k1 = block32;
173
174 k1 *= c1;
175 k1 = std::rotl(k1, r1);
176 k1 *= c2;
177
178 h1 ^= k1;
179 h1 = std::rotl(h1, r2);
180 h1 = h1 * m + n;
181 }
182
183 // Tail
184
185 auto const tailBytes = bytes + numBlocks * 4;
186
187 uint32 k1 = 0;
188
189 switch(numBytes & 3)
190 {
191 case 3: k1 ^= tailBytes[2] << 16UL;
192 [[fallthrough]];
193 case 2: k1 ^= tailBytes[1] << 8UL;
194 [[fallthrough]];
195 case 1: k1 ^= tailBytes[0];
196 k1 *= c1; k1 = std::rotl(k1, r1); k1 *= c2; h1 ^= k1;
197 };
198
199 // Finalization
200
201 h1 ^= static_cast<uint32>(dataSize);
202 h1 = std::forward<BitMixerType>(bitMixer)(h1);
203 return h1;
204}
205
206inline uint32 permuted_index(uint32 i, uint32 l, uint32 p)
207{
208 // This is the implementation from Kensler's paper: "Correlated Multi-Jittered Sampling".
209 // See https://afnan.io/posts/2019-04-05-explaining-the-hashed-permutation/ for
210 // a nice introduction.
211 // Note that PBRT-v4 also uses the same implementation, see their `PermutationElement()`.
212
213 unsigned w = l - 1;
214 w |= w >> 1;
215 w |= w >> 2;
216 w |= w >> 4;
217 w |= w >> 8;
218 w |= w >> 16;
219
220 do
221 {
222 i ^= p;
223 i *= 0xe170893d;
224 i ^= p >> 16;
225 i ^= (i & w) >> 4;
226 i ^= p >> 8;
227 i *= 0x0929eb3f;
228 i ^= p >> 23;
229 i ^= (i & w) >> 1;
230 i *= 1 | p >> 27;
231 i *= 0x6935fa69;
232 i ^= (i & w) >> 11;
233 i *= 0x74dcb303;
234 i ^= (i & w) >> 2;
235 i *= 0x9e501cc3;
236 i ^= (i & w) >> 2;
237 i *= 0xc860a3df;
238 i &= w;
239 i ^= i >> 5;
240 } while (i >= l);
241
242 return (i + p) % l;
243}
244
245}// end namespace ph::math
Represents a 3-D vector.
Definition TVector3.h:17
T & y()
Definition TVector3.ipp:189
T & z()
Definition TVector3.ipp:195
T & x()
Definition TVector3.ipp:183
Math functions and utilities.
Definition TransformInfo.h:10
uint64 murmur3_bit_mix_64(uint64 v)
MurmurHash3's bit mixer. 64-bit version.
Definition hash.ipp:82
uint32 permuted_index(uint32 i, uint32 l, uint32 p)
Get the permuted index or value in O(1) space and O(1) time.
Definition hash.ipp:206
uint64 moremur_bit_mix_64(uint64 v)
A MurmurHash3-style bit mixer that outperforms the original by quite some margin. 64-bit version.
Definition hash.ipp:104
uint32 murmur3_bit_mix_32(uint32 v)
MurmurHash3's bit mixer. 32-bit version.
Definition hash.ipp:71
uint64 murmur3_v13_bit_mix_64(uint64 v)
MurmurHash3's bit mixer. 64-bit version.
Definition hash.ipp:93
uint32 murmur3_32(const T &data, uint32 seed)
Generate 32-bit hash values using MurmurHash3. Note that there are no collisions when T has <= 32 bit...
Definition hash.ipp:119
std::size_t discrete_spatial_hash(Integer x, Integer y, Integer z, std::size_t hashTableSize)
Definition hash.ipp:19