diff --git a/enkou-shaders/examples/gasket.rs b/enkou-shaders/examples/gasket.rs index a0f0450..55d0001 100644 --- a/enkou-shaders/examples/gasket.rs +++ b/enkou-shaders/examples/gasket.rs @@ -43,7 +43,7 @@ pub fn main() -> Result<()> { main_chaos_game( ITERATIONS_DISCARD, - &[4u8], + &[4u8; 32], &transforms, &weights, &variations, diff --git a/enkou-shaders/src/chaos_game.rs b/enkou-shaders/src/chaos_game.rs index 93f65c2..8ac3858 100644 --- a/enkou-shaders/src/chaos_game.rs +++ b/enkou-shaders/src/chaos_game.rs @@ -110,7 +110,7 @@ impl<'a, R: Rng> Iterator for ChaosGame<'a, R> { /// Shader entry point for running the chaos game to produce new IFS coordinates pub mod entry { use crate::chaos_game::ChaosGame; - use crate::rng::xoshiro_from_state; + use crate::rng::xoshiro256starstar_from_seed; use crate::transform::Transform; use crate::variation::Variation; use glam::Vec2; @@ -121,15 +121,16 @@ pub mod entry { #[spirv(compute(entry_point_name = "main_chaos_game", threads(1)))] pub fn main_chaos_game( #[spirv(spec_constant(id = 1, default = 20))] iteration_discard: u32, - #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] _rng_seed: &[u8], + #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] rng_seed: &[u8], #[spirv(storage_buffer, descriptor_set = 0, binding = 1)] transforms: &[Transform], #[spirv(storage_buffer, descriptor_set = 0, binding = 2)] weights: &[f32], #[spirv(storage_buffer, descriptor_set = 0, binding = 3)] variations: &[Variation], #[spirv(storage_buffer, descriptor_set = 1, binding = 0)] output: &mut [Vec2], ) { - let rng_seed_actual = [0u8; 32]; + let mut rng_seed_actual = [0u8; 32]; + (0..32).for_each(|i| rng_seed_actual[i] = rng_seed[i]); - let mut rng = xoshiro_from_state(rng_seed_actual); + let mut rng = xoshiro256starstar_from_seed(rng_seed_actual); let mut chaos_game = ChaosGame::new(&mut rng, transforms, weights, variations); for _ in 0..iteration_discard { diff --git a/enkou-shaders/src/rng.rs b/enkou-shaders/src/rng.rs index eb8473b..7dbeae8 100644 --- a/enkou-shaders/src/rng.rs +++ b/enkou-shaders/src/rng.rs @@ -14,9 +14,42 @@ use rand_xoshiro::Xoshiro256StarStar; /// This function assumes a properly-initialized state array; /// output may silently degenerate if the initial state is all zeros, /// so this module is private to the crate. -pub(crate) fn xoshiro_from_state( - _rng_state: ::Seed, +pub(crate) fn xoshiro256starstar_from_seed( + rng_state: ::Seed, ) -> Xoshiro256StarStar { - let rng_state_actual = [1u64, 2u64, 3u64, 4u64]; + let mut rng_state_actual = [0u64; 4]; + + // NOTE: Bit shifting is bad, but we don't have great alternatives: + // - `chunks_exact` has issues with pointer casting + // - `u64::from_le_bytes` has issues with `OpBitcast` in SPIR-V validation + for i in 0..rng_state_actual.len() { + for j in 0..size_of::() { + rng_state_actual[i] |= (rng_state[i * size_of::() + j] as u64) << j * 8; + } + } + unsafe { core::mem::transmute(rng_state_actual) } } + +#[cfg(test)] +mod test { + use crate::rng::xoshiro256starstar_from_seed; + use core::iter::zip; + use rand::{RngExt, SeedableRng}; + use rand_xoshiro::Xoshiro256StarStar; + + #[test] + fn match_seeded() { + let mut seed: ::Seed = [0u8; 32]; + for i in 0..seed.len() { + seed[i] = i as u8; + } + + let rng1 = Xoshiro256StarStar::from_seed(seed).random_iter::(); + let rng2 = xoshiro256starstar_from_seed(seed).random_iter::(); + + zip(rng1, rng2) + .take(100) + .for_each(|(rng1_value, rng2_value)| assert_eq!(rng1_value, rng2_value)); + } +}