This lets us move the seeding overhead outside the main loop, which in turn lets us avoid taking it every round.
99 lines
4.0 KiB
Rust
99 lines
4.0 KiB
Rust
/// Performs a base-2 Owen scramble on an integer.
|
|
pub fn owen2(n: u32, seed: u32) -> u32 {
|
|
// Multiply by a large random prime and xor by a random number.
|
|
// This is to ensure that the seed doesn't behave poorly with
|
|
// e.g. incrementing parameters, and also that zero doesn't
|
|
// map to zero in the hash function.
|
|
let seed = seed.wrapping_mul(0xe8559dcb) ^ 0x372fcdb9;
|
|
|
|
let mut result = n;
|
|
|
|
for i in 0..32 {
|
|
let mask = (!0 << 1) << i; // Two shifts to avoid undefined overflow.
|
|
result ^= hash((n & mask) ^ seed) & (1 << i);
|
|
}
|
|
|
|
result
|
|
}
|
|
|
|
/// Performs a base-4 Owen scramble on an integer.
|
|
///
|
|
/// This is motivated by z-scrambling from the paper "Screen-Space
|
|
/// Blue-Noise Diffusion of Monte Carlo Sampling Error via Hierarchical
|
|
/// Ordering of Pixels" by Ahmed et al. Their scrambling function is
|
|
/// actually just Owen scrambling in base 4, but their attempt at
|
|
/// implementing that function isn't great, both in terms of memory
|
|
/// usage and scramble quality. (The paper itself is otherwise
|
|
/// fantastic, though.)
|
|
///
|
|
/// The implementation below is a full proper base-4 Owen scramble,
|
|
/// requiring only a 24-byte lookup table.
|
|
pub fn owen4(n: u32, seed: u32) -> u32 {
|
|
// Bit-packed permutation table.
|
|
const PERMUTATION_TABLE: [u8; 24] = [
|
|
0 | (1 << 2) | (2 << 4) | (3 << 6), // [0, 1, 2, 3],
|
|
0 | (1 << 2) | (3 << 4) | (2 << 6), // [0, 1, 3, 2],
|
|
0 | (2 << 2) | (1 << 4) | (3 << 6), // [0, 2, 1, 3],
|
|
0 | (2 << 2) | (3 << 4) | (1 << 6), // [0, 2, 3, 1],
|
|
0 | (3 << 2) | (1 << 4) | (2 << 6), // [0, 3, 1, 2],
|
|
0 | (3 << 2) | (2 << 4) | (1 << 6), // [0, 3, 2, 1],
|
|
1 | (0 << 2) | (2 << 4) | (3 << 6), // [1, 0, 2, 3],
|
|
1 | (0 << 2) | (3 << 4) | (2 << 6), // [1, 0, 3, 2],
|
|
1 | (2 << 2) | (0 << 4) | (3 << 6), // [1, 2, 0, 3],
|
|
1 | (2 << 2) | (3 << 4) | (0 << 6), // [1, 2, 3, 0],
|
|
1 | (3 << 2) | (0 << 4) | (2 << 6), // [1, 3, 0, 2],
|
|
1 | (3 << 2) | (2 << 4) | (0 << 6), // [1, 3, 2, 0],
|
|
2 | (0 << 2) | (1 << 4) | (3 << 6), // [2, 0, 1, 3],
|
|
2 | (0 << 2) | (3 << 4) | (1 << 6), // [2, 0, 3, 1],
|
|
2 | (1 << 2) | (0 << 4) | (3 << 6), // [2, 1, 0, 3],
|
|
2 | (1 << 2) | (3 << 4) | (0 << 6), // [2, 1, 3, 0],
|
|
2 | (3 << 2) | (0 << 4) | (1 << 6), // [2, 3, 0, 1],
|
|
2 | (3 << 2) | (1 << 4) | (0 << 6), // [2, 3, 1, 0],
|
|
3 | (0 << 2) | (1 << 4) | (2 << 6), // [3, 0, 1, 2],
|
|
3 | (0 << 2) | (2 << 4) | (1 << 6), // [3, 0, 2, 1],
|
|
3 | (1 << 2) | (0 << 4) | (2 << 6), // [3, 1, 0, 2],
|
|
3 | (1 << 2) | (2 << 4) | (0 << 6), // [3, 1, 2, 0],
|
|
3 | (2 << 2) | (0 << 4) | (1 << 6), // [3, 2, 0, 1],
|
|
3 | (2 << 2) | (1 << 4) | (0 << 6), // [3, 2, 1, 0],
|
|
];
|
|
|
|
// Multiply by a large random prime and xor by a random number.
|
|
// This is to ensure that the seed doesn't behave poorly with
|
|
// e.g. incrementing parameters, and also that zero doesn't
|
|
// map to zero in the hash function.
|
|
let seed = seed.wrapping_mul(0xe8559dcb) ^ 0x372fcdb9;
|
|
|
|
let mut result = 0;
|
|
|
|
for i in 0..16 {
|
|
let mask = (!0 << 2) << (i * 2); // Two shifts to avoid undefined overflow.
|
|
let perm_entry = PERMUTATION_TABLE[
|
|
// The xor with `i` is to ensure runs of zeros in `n` still
|
|
// result in different shuffles on each iteration. `i` is
|
|
// shifted to avoid interacting poorly with an incrementing
|
|
// `n`.
|
|
(hash((n & mask) ^ seed ^ (i << 16)) % 24) as usize
|
|
];
|
|
let perm_cell_idx = ((n >> (i * 2)) & 0b11) as usize;
|
|
|
|
result |= (((perm_entry >> (perm_cell_idx * 2)) & 0b11) as u32) << (i * 2);
|
|
}
|
|
|
|
result
|
|
}
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
/// Fast bit-mixing hash for use in the functions above.
|
|
#[inline(always)]
|
|
pub fn hash(mut n: u32) -> u32 {
|
|
// From https://github.com/skeeto/hash-prospector
|
|
n ^= n >> 16;
|
|
n = n.wrapping_mul(0x21f0aaad);
|
|
n ^= n >> 15;
|
|
n = n.wrapping_mul(0xd35a2d97);
|
|
n ^= n >> 15;
|
|
|
|
n
|
|
}
|