Photon Engine 2.0.0-beta
A physically based renderer.
Loading...
Searching...
No Matches
Pcg64DXSM.h
Go to the documentation of this file.
1#pragma once
2
4#include "Math/hash.h"
5#include "Math/math.h"
6
7#include <Common/primitive_type.h>
8
9// This is a good reference on built-in 128-bit integer:
10// https://stackoverflow.com/questions/16088282/is-there-a-128-bit-integer-in-gcc
11// In the future, consider using `_BitInt`
12
14#define PH_MATH_PCG64_FORCE_EMULATED_UINT128 0
15
16#if !defined(__SIZEOF_INT128__) || PH_MATH_PCG64_FORCE_EMULATED_UINT128
17#define PH_MATH_PCG64_EMULATED_UINT128 1
18#else
19#define PH_MATH_PCG64_EMULATED_UINT128 0
20#endif
21
22namespace ph::math
23{
24
25namespace detail
26{
27
30{
31public:
32 constexpr Pcg64UInt128();
33 constexpr Pcg64UInt128(uint64 high64, uint64 low64);
34
35 uint64 getHigh64() const;
36 uint64 getLow64() const;
37
38 Pcg64UInt128 operator + (const Pcg64UInt128& rhs) const;
39 Pcg64UInt128 operator * (const Pcg64UInt128& rhs) const;
40
41private:
42#if PH_MATH_PCG64_EMULATED_UINT128
43 uint64 m_high64;
44 uint64 m_low64;
45#else
46 __uint128_t m_128;
47#endif
48};
49
51 : Pcg64UInt128(0, 0)
52{}
53
54inline constexpr Pcg64UInt128::Pcg64UInt128(const uint64 high64, const uint64 low64)
55#if PH_MATH_PCG64_EMULATED_UINT128
56 : m_high64(high64)
57 , m_low64(low64)
58#else
59 : m_128((__uint128_t(high64) << 64) + low64)
60#endif
61{}
62
63inline uint64 Pcg64UInt128::getHigh64() const
64{
65#if PH_MATH_PCG64_EMULATED_UINT128
66 return m_high64;
67#else
68 return static_cast<uint64>(m_128 >> 64);
69#endif
70}
71
72inline uint64 Pcg64UInt128::getLow64() const
73{
74#if PH_MATH_PCG64_EMULATED_UINT128
75 return m_low64;
76#else
77 return static_cast<uint64>(m_128);
78#endif
79}
80
82{
83 Pcg64UInt128 result;
84#if PH_MATH_PCG64_EMULATED_UINT128
85 result.m_low64 = m_low64 + rhs.m_low64;
86 result.m_high64 = m_high64 + rhs.m_high64 + (result.m_low64 < m_low64);
87#else
88 result.m_128 = m_128 + rhs.m_128;
89#endif
90 return result;
91}
92
94{
95 Pcg64UInt128 result;
96#if PH_MATH_PCG64_EMULATED_UINT128
97 // Multiply the lower parts first
98 math::uint64_mul(m_low64, rhs.m_low64, result.m_high64, result.m_low64);
99
100 // The rest work like `uint64_mul()` (see the doc for its impl.), except that the high bit part
101 // is discarded (it will overflow)
102 result.m_high64 += (m_high64 * rhs.m_low64) + (m_low64 * rhs.m_high64);
103#else
104 result.m_128 = m_128 * rhs.m_128;
105#endif
106 return result;
107}
108
109}// end namespace detail
110
111/*
112 * The following PCG implementation is a version adapted for use in Photon. Most algorithmic parts
113 * are left as-is the original reference implementation by Melissa O'Neill and other sources (listed
114 * in the documentation of `Pcg64DXSM` class).
115 *
116 * The PCG Random Number Generator was developed by Melissa O'Neill <oneill@pcg-random.org>.
117 *
118 * Licensed under the Apache License, Version 2.0 (the "License");
119 * you may not use this file except in compliance with the License.
120 * You may obtain a copy of the License at
121 *
122 * http://www.apache.org/licenses/LICENSE-2.0
123 *
124 * Unless required by applicable law or agreed to in writing, software
125 * distributed under the License is distributed on an "AS IS" BASIS,
126 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
127 * See the License for the specific language governing permissions and
128 * limitations under the License.
129 *
130 * For additional information about the PCG random number generation scheme,
131 * including its license and other licensing options, visit
132 *
133 * http://www.pcg-random.org
134 */
135
142class Pcg64DXSM final : public TUniformRandomBitGenerator<Pcg64DXSM, uint64>
143{
144public:
146
147 Pcg64DXSM(uint64 initialSequenceHigh64, uint64 initialSequenceLow64);
148
157 Pcg64DXSM(
158 uint64 initialSequenceHigh64, uint64 initialSequenceLow64,
159 uint64 initialStateHigh64, uint64 initialStateLow64);
160
161 uint64 impl_generate();
162 void impl_jumpAhead(uint64 distance);
163
164private:
166
167 uint64 generateUInt64();
168
169 inline static constexpr auto DEFAULT_STATE = UInt128(0x979C9A98D8462005ull, 0x7D3E9CB6CFE0549Bull);
170 inline static constexpr auto DEFAULT_STREAM_ID = UInt128(0x5851F42D4C957F2Dull, 0x14057B7EF767814Full);
171
172 // Cheap (half-width) multiplier
173 inline static constexpr auto CHEAP_MULTIPLIER_64 = 0xDA942042E4DD58B5ull;
174
175 UInt128 m_state = DEFAULT_STATE;
176
178 UInt128 m_increment = DEFAULT_STREAM_ID;
179};
180
181inline Pcg64DXSM::Pcg64DXSM(const uint64 initialSequenceHigh64, const uint64 initialSequenceLow64)
182 : Pcg64DXSM(
183 initialSequenceHigh64, initialSequenceLow64,
184 moremur_bit_mix_64(initialSequenceHigh64), moremur_bit_mix_64(initialSequenceLow64))
185{}
186
188 const uint64 initialSequenceHigh64, const uint64 initialSequenceLow64,
189 const uint64 initialStateHigh64, const uint64 initialStateLow64)
190 : Pcg64DXSM()
191{
192 m_state = UInt128(0ull, 0ull);
193
194 // Ensure `m_increment` is odd (basically doing `(initialSequence << 1u) | 1u`)
195 uint64 incrementHigh64 = initialSequenceHigh64 << 1u;
196 incrementHigh64 |= initialSequenceLow64 >> 63u;
197 uint64 incrementLow64 = (initialSequenceLow64 << 1u) | 1u;
198 m_increment = UInt128(incrementHigh64, incrementLow64);
199
200 generateUInt64();
201 m_state = m_state + UInt128(initialStateHigh64, initialStateLow64);
202 generateUInt64();
203}
204
206{
207 return generateUInt64();
208}
209
210inline void Pcg64DXSM::impl_jumpAhead(const uint64 distance)
211{
212 /* Multi-step advance functions (jump-ahead, jump-back)
213
214 The method used here is based on Brown, "Random Number Generation with Arbitrary Stride,",
215 Transactions of the American Nuclear Society (Nov. 1994). The algorithm is very similar to fast
216 exponentiation.
217
218 Even though delta is an unsigned integer, we can pass a signed integer to go backwards,
219 it just goes "the long way round".
220 */
221
222 constexpr auto ZERO = UInt128(0, 0);
223 constexpr auto ONE = UInt128(0, 1);
224
225 auto curMult = UInt128(0, CHEAP_MULTIPLIER_64);
226 auto curPlus = m_increment;
227 auto accMult = ONE;
228 auto accPlus = ZERO;
229 uint64 delta = distance;
230 while(delta > 0)
231 {
232 if(delta & 1)
233 {
234 accMult = accMult * curMult;
235 accPlus = accPlus * curMult + curPlus;
236 }
237 curPlus = (curMult + ONE) * curPlus;
238 curMult = curMult * curMult;
239 delta /= 2;
240 }
241 m_state = accMult * m_state + accPlus;
242}
243
244inline uint64 Pcg64DXSM::generateUInt64()
245{
246 // Linear congruential generator
247 const UInt128 oldState = m_state;
248 m_state = oldState * UInt128(0, CHEAP_MULTIPLIER_64) + m_increment;
249
250 // DXSM (double xor shift multiply) permuted output
251 uint64 hi = oldState.getHigh64();
252 uint64 lo = oldState.getLow64();
253 lo |= 1;
254 hi ^= hi >> 32;
255 hi *= CHEAP_MULTIPLIER_64;
256 hi ^= hi >> 48;
257 hi *= lo;
258
259 return hi;
260}
261
262}// end namespace ph::math
PCG-64 DXSM generator. This is the pcg_engines::cm_setseq_dxsm_128_64 generator in O'Neill's original...
Definition Pcg64DXSM.h:143
uint64 impl_generate()
Definition Pcg64DXSM.h:205
void impl_jumpAhead(uint64 distance)
Definition Pcg64DXSM.h:210
PH_DEFINE_INLINE_RULE_OF_5_MEMBERS(Pcg64DXSM)
Pcg64DXSM(uint64 initialSequenceHigh64, uint64 initialSequenceLow64)
Definition Pcg64DXSM.h:181
Definition TUniformRandomBitGenerator.h:30
Definition Pcg64DXSM.h:30
Pcg64UInt128 operator*(const Pcg64UInt128 &rhs) const
Definition Pcg64DXSM.h:93
constexpr Pcg64UInt128()
Definition Pcg64DXSM.h:50
Pcg64UInt128 operator+(const Pcg64UInt128 &rhs) const
Definition Pcg64DXSM.h:81
uint64 getLow64() const
Definition Pcg64DXSM.h:72
uint64 getHigh64() const
Definition Pcg64DXSM.h:63
Miscellaneous math utilities.
Math functions and utilities.
Definition TransformInfo.h:10
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
void uint64_mul(const uint64 lhs, const uint64 rhs, uint64 &out_high64, uint64 &out_low64)
Multiply two unsigined 64-bit numbers and get a 128-bit result.
Definition math.h:695