2024-04-08 17:19:07 +00:00
|
|
|
// The shader reads the previous frame's state from the `input` texture, and writes the new state of
|
|
|
|
// each pixel to the `output` texture. The textures are flipped each step to progress the
|
|
|
|
// simulation.
|
|
|
|
// Two textures are needed for the game of life as each pixel of step N depends on the state of its
|
|
|
|
// neighbors at step N-1.
|
|
|
|
|
|
|
|
@group(0) @binding(0) var input: texture_storage_2d<r32float, read>;
|
|
|
|
|
|
|
|
@group(0) @binding(1) var output: texture_storage_2d<r32float, write>;
|
2022-01-05 19:43:11 +00:00
|
|
|
|
|
|
|
fn hash(value: u32) -> u32 {
|
|
|
|
var state = value;
|
|
|
|
state = state ^ 2747636419u;
|
|
|
|
state = state * 2654435769u;
|
|
|
|
state = state ^ state >> 16u;
|
|
|
|
state = state * 2654435769u;
|
|
|
|
state = state ^ state >> 16u;
|
|
|
|
state = state * 2654435769u;
|
|
|
|
return state;
|
|
|
|
}
|
2022-07-17 15:24:24 +00:00
|
|
|
|
2022-01-05 19:43:11 +00:00
|
|
|
fn randomFloat(value: u32) -> f32 {
|
|
|
|
return f32(hash(value)) / 4294967295.0;
|
|
|
|
}
|
|
|
|
|
2022-07-14 21:17:16 +00:00
|
|
|
@compute @workgroup_size(8, 8, 1)
|
|
|
|
fn init(@builtin(global_invocation_id) invocation_id: vec3<u32>, @builtin(num_workgroups) num_workgroups: vec3<u32>) {
|
2022-01-05 19:43:11 +00:00
|
|
|
let location = vec2<i32>(i32(invocation_id.x), i32(invocation_id.y));
|
|
|
|
|
2024-04-08 17:19:07 +00:00
|
|
|
let randomNumber = randomFloat(invocation_id.y << 16u | invocation_id.x);
|
2022-01-05 19:43:11 +00:00
|
|
|
let alive = randomNumber > 0.9;
|
|
|
|
let color = vec4<f32>(f32(alive));
|
|
|
|
|
2024-04-08 17:19:07 +00:00
|
|
|
textureStore(output, location, color);
|
2022-01-05 19:43:11 +00:00
|
|
|
}
|
|
|
|
|
2022-07-14 21:17:16 +00:00
|
|
|
fn is_alive(location: vec2<i32>, offset_x: i32, offset_y: i32) -> i32 {
|
2024-04-08 17:19:07 +00:00
|
|
|
let value: vec4<f32> = textureLoad(input, location + vec2<i32>(offset_x, offset_y));
|
2022-01-05 19:43:11 +00:00
|
|
|
return i32(value.x);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn count_alive(location: vec2<i32>) -> i32 {
|
2022-07-14 21:17:16 +00:00
|
|
|
return is_alive(location, -1, -1) +
|
|
|
|
is_alive(location, -1, 0) +
|
|
|
|
is_alive(location, -1, 1) +
|
|
|
|
is_alive(location, 0, -1) +
|
|
|
|
is_alive(location, 0, 1) +
|
|
|
|
is_alive(location, 1, -1) +
|
|
|
|
is_alive(location, 1, 0) +
|
|
|
|
is_alive(location, 1, 1);
|
2022-01-05 19:43:11 +00:00
|
|
|
}
|
|
|
|
|
2022-07-14 21:17:16 +00:00
|
|
|
@compute @workgroup_size(8, 8, 1)
|
|
|
|
fn update(@builtin(global_invocation_id) invocation_id: vec3<u32>) {
|
2022-01-05 19:43:11 +00:00
|
|
|
let location = vec2<i32>(i32(invocation_id.x), i32(invocation_id.y));
|
|
|
|
|
|
|
|
let n_alive = count_alive(location);
|
|
|
|
|
|
|
|
var alive: bool;
|
|
|
|
if (n_alive == 3) {
|
|
|
|
alive = true;
|
|
|
|
} else if (n_alive == 2) {
|
2022-07-14 21:17:16 +00:00
|
|
|
let currently_alive = is_alive(location, 0, 0);
|
2022-01-05 19:43:11 +00:00
|
|
|
alive = bool(currently_alive);
|
|
|
|
} else {
|
|
|
|
alive = false;
|
|
|
|
}
|
2022-07-17 15:24:24 +00:00
|
|
|
let color = vec4<f32>(f32(alive));
|
2022-01-05 19:43:11 +00:00
|
|
|
|
2024-04-08 17:19:07 +00:00
|
|
|
textureStore(output, location, color);
|
|
|
|
}
|