random/philox.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /// @file | ||
| 2 | /// @brief The Philox4x32-10 counter-based generator for the CPU and GPU backends. | ||
| 3 | #pragma once | ||
| 4 | |||
| 5 | #include "terraingen/util/portable.h" | ||
| 6 | |||
| 7 | namespace terraingen { | ||
| 8 | |||
| 9 | /// Four 32-bit words: a Philox counter or output block. | ||
| 10 | struct U32x4 { | ||
| 11 | 14 | uint32_t x = 0; ///< First word. | |
| 12 | 7 | uint32_t y = 0; ///< Second word. | |
| 13 | 6 | uint32_t z = 0; ///< Third word. | |
| 14 | 5 | uint32_t w = 0; ///< Fourth word. | |
| 15 | |||
| 16 | #ifndef __SLANG__ | ||
| 17 | 14 | bool operator==(const U32x4&) const = default; | |
| 18 | #endif | ||
| 19 | }; | ||
| 20 | |||
| 21 | /// Two 32-bit words: a Philox key. | ||
| 22 | struct U32x2 { | ||
| 23 | 3 | uint32_t x = 0; ///< First word. | |
| 24 | 2 | uint32_t y = 0; ///< Second word. | |
| 25 | |||
| 26 | #ifndef __SLANG__ | ||
| 27 | 3 | bool operator==(const U32x2&) const = default; | |
| 28 | #endif | ||
| 29 | }; | ||
| 30 | |||
| 31 | namespace detail { | ||
| 32 | |||
| 33 | /// The high (`x`) and low (`y`) words of the 64-bit product `a * b`, built from 16-bit halves on | ||
| 34 | /// GPUs without 64-bit integers, with the same result. | ||
| 35 | 2052712980 | constexpr U32x2 MulHiLo32(uint32_t a, uint32_t b) { | |
| 36 | 2052712980 | U32x2 result = {}; | |
| 37 | #ifdef __SLANG__ | ||
| 38 | const uint32_t aLow = a & 0xFFFFu; | ||
| 39 | const uint32_t aHigh = a >> 16u; | ||
| 40 | const uint32_t bLow = b & 0xFFFFu; | ||
| 41 | const uint32_t bHigh = b >> 16u; | ||
| 42 | const uint32_t lowHigh = aLow * bHigh; | ||
| 43 | const uint32_t highLow = aHigh * bLow; | ||
| 44 | const uint32_t middle = ((aLow * bLow) >> 16u) + (lowHigh & 0xFFFFu) + (highLow & 0xFFFFu); | ||
| 45 | result.x = aHigh * bHigh + (lowHigh >> 16u) + (highLow >> 16u) + (middle >> 16u); | ||
| 46 | result.y = a * b; | ||
| 47 | #else | ||
| 48 | 2052712980 | const std::uint64_t product = std::uint64_t(a) * b; | |
| 49 | 2052712980 | result.x = uint32_t(product >> 32u); | |
| 50 | 2052712980 | result.y = uint32_t(product); | |
| 51 | #endif | ||
| 52 | 2052712980 | return result; | |
| 53 | } | ||
| 54 | |||
| 55 | /// One Philox4x32 round of a block under a round key. | ||
| 56 | 1026356490 | constexpr U32x4 PhiloxRound(U32x4 counter, U32x2 key) { | |
| 57 | 1026356490 | const U32x2 first = MulHiLo32(0xD2511F53u, counter.x); | |
| 58 | 1026356490 | const U32x2 second = MulHiLo32(0xCD9E8D57u, counter.z); | |
| 59 | 1026356490 | const U32x4 result = {second.x ^ counter.y ^ key.x, second.y, first.x ^ counter.w ^ key.y, | |
| 60 | 1026356490 | first.y}; | |
| 61 | 1026356490 | return result; | |
| 62 | } | ||
| 63 | |||
| 64 | } // namespace detail | ||
| 65 | |||
| 66 | /// Philox4x32-10: 128 pseudo-random bits, a bijection of the counter under the key, so any | ||
| 67 | /// element of any stream can be computed directly. Bit-identical on every platform and GPU. | ||
| 68 | 757106067 | constexpr U32x4 Philox4x32(U32x4 counter, U32x2 key) { | |
| 69 | 1128992139 | for (int round = 0; round < 10; ++round) { | |
| 70 | 1026356490 | if (round > 0) { | |
| 71 | 923720841 | key.x += 0x9E3779B9u; | |
| 72 | 923720841 | key.y += 0xBB67AE85u; | |
| 73 | } | ||
| 74 | 1026356490 | counter = detail::PhiloxRound(counter, key); | |
| 75 | } | ||
| 76 | 757106067 | return counter; | |
| 77 | } | ||
| 78 | |||
| 79 | } // namespace terraingen | ||
| 80 |