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.
23enum 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
37namespace {
38
39BL_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
54BL_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
69BL_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.
77BL_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
93BL_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
103BL_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