1 | // [Blend2D] |
2 | // 2D Vector Graphics Powered by a JIT Compiler. |
3 | // |
4 | // [License] |
5 | // Zlib - See LICENSE.md file in the package. |
6 | |
7 | #ifndef BLEND2D_BLRANDOM_P_H |
8 | #define BLEND2D_BLRANDOM_P_H |
9 | |
10 | #include "./blrandom.h" |
11 | #include "./blsimd_p.h" |
12 | #include "./blsupport_p.h" |
13 | |
14 | //! \cond INTERNAL |
15 | //! \addtogroup blend2d_internal |
16 | //! \{ |
17 | |
18 | // ============================================================================ |
19 | // [Constants] |
20 | // ============================================================================ |
21 | |
22 | //! Shift constants used by `BLRandom` implementation. |
23 | enum BLInternalRandomShifts : uint32_t { |
24 | // Constants suggested as `23/18/5`. |
25 | BL_RANDOM_STEP1_SHL = 23, |
26 | BL_RANDOM_STEP2_SHR = 18, |
27 | BL_RANDOM_STEP3_SHR = 5, |
28 | |
29 | // Number of bits needed to shift right to extract mantissa. |
30 | BL_RANDOM_MANTISSA_SHIFT = 64 - 52 |
31 | }; |
32 | |
33 | // ============================================================================ |
34 | // [BLRandom - Inline] |
35 | // ============================================================================ |
36 | |
37 | namespace { |
38 | |
39 | BL_INLINE void blRandomResetInline(BLRandom* self, uint64_t seed) noexcept { |
40 | // The number is arbitrary, it means nothing. |
41 | const uint64_t kZeroSeed = 0x1F0A2BE71D163FA0u; |
42 | |
43 | // Generate the state data by using splitmix64. |
44 | for (uint32_t i = 0; i < 2; i++) { |
45 | seed += 0x9E3779B97F4A7C15u; |
46 | uint64_t x = seed; |
47 | x = (x ^ (x >> 30)) * 0xBF58476D1CE4E5B9u; |
48 | x = (x ^ (x >> 27)) * 0x94D049BB133111EBu; |
49 | x = (x ^ (x >> 31)); |
50 | self->data[i] = x != 0 ? x : kZeroSeed; |
51 | } |
52 | } |
53 | |
54 | BL_INLINE uint64_t blRandomNextUInt64Inline(BLRandom* self) noexcept { |
55 | uint64_t x = self->data[0]; |
56 | uint64_t y = self->data[1]; |
57 | |
58 | x ^= x << BL_RANDOM_STEP1_SHL; |
59 | y ^= y >> BL_RANDOM_STEP3_SHR; |
60 | x ^= x >> BL_RANDOM_STEP2_SHR; |
61 | x ^= y; |
62 | |
63 | self->data[0] = y; |
64 | self->data[1] = x; |
65 | |
66 | return x + y; |
67 | } |
68 | |
69 | BL_INLINE uint32_t blRandomNextUInt32Inline(BLRandom* self) noexcept { |
70 | return uint32_t(blRandomNextUInt64Inline(self) >> 32); |
71 | } |
72 | |
73 | #ifdef BL_TARGET_OPT_SSE2 |
74 | //! High-performance SIMD implementation. Better utilizes CPU in 32-bit mode |
75 | //! and it's a better candidate for `blRandomNextDouble()` in general on X86 as |
76 | //! it returns a SIMD register, which is easier to convert to `double` than GP. |
77 | BL_INLINE __m128i blRandomNextUInt64AsI128Inline(BLRandom* self) noexcept { |
78 | using namespace SIMD; |
79 | |
80 | I128 x = vloadi128_64(&self->data[0]); |
81 | I128 y = vloadi128_64(&self->data[1]); |
82 | |
83 | x = vxor(x, vslli64<BL_RANDOM_STEP1_SHL>(x)); |
84 | y = vxor(y, vsrli64<BL_RANDOM_STEP3_SHR>(y)); |
85 | x = vxor(x, vsrli64<BL_RANDOM_STEP2_SHR>(x)); |
86 | x = vxor(x, y); |
87 | vstorei64(&self->data[0], y); |
88 | vstorei64(&self->data[1], x); |
89 | |
90 | return vaddi64(x, y); |
91 | } |
92 | |
93 | BL_INLINE double blRandomNextDoubleInline(BLRandom* self) noexcept { |
94 | using namespace SIMD; |
95 | |
96 | I128 kExpMsk128 = _mm_set_epi32(0x3FF00000, 0, 0x3FF00000, 0); |
97 | I128 x = blRandomNextUInt64AsI128Inline(self); |
98 | I128 y = vsrli64<BL_RANDOM_MANTISSA_SHIFT>(x); |
99 | I128 z = vor(y, kExpMsk128); |
100 | return vcvtd128d64(vcast<D128>(z)) - 1.0; |
101 | } |
102 | #else |
103 | BL_INLINE double blRandomNextDoubleInline(BLRandom* self) noexcept { |
104 | uint64_t kExpMsk = 0x3FF0000000000000u; |
105 | uint64_t u = (blRandomNextUInt64Inline(self) >> BL_RANDOM_MANTISSA_SHIFT) | kExpMsk; |
106 | return blBitCast<double>(u) - 1.0; |
107 | } |
108 | #endif |
109 | |
110 | } // {anonymous} |
111 | |
112 | //! \} |
113 | //! \endcond |
114 | |
115 | #endif // BLEND2D_BLRANDOM_P_H |
116 | |