forked from nannou-org/nannou
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
cbe0da6
commit 91f7d19
Showing
5 changed files
with
546 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// 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. | ||
|
||
struct GameOfLife { | ||
index: u32, | ||
} | ||
|
||
@group(0) @binding(0) var input: texture_storage_2d<r32float, read>; | ||
@group(0) @binding(1) var output: texture_storage_2d<r32float, write>; | ||
|
||
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; | ||
} | ||
|
||
fn randomFloat(value: u32) -> f32 { | ||
return f32(hash(value)) / 4294967295.0; | ||
} | ||
|
||
@compute @workgroup_size(8, 8, 1) | ||
fn init(@builtin(global_invocation_id) invocation_id: vec3<u32>, @builtin(num_workgroups) num_workgroups: vec3<u32>) { | ||
let location = vec2<i32>(i32(invocation_id.x), i32(invocation_id.y)); | ||
|
||
let randomNumber = randomFloat(invocation_id.y << 16u | invocation_id.x); | ||
let alive = randomNumber > 0.9; | ||
let color = vec4<f32>(f32(alive)); | ||
|
||
textureStore(output, location, color); | ||
} | ||
|
||
fn is_alive(location: vec2<i32>, offset_x: i32, offset_y: i32) -> i32 { | ||
let value: vec4<f32> = textureLoad(input, location + vec2<i32>(offset_x, offset_y)); | ||
return i32(value.x); | ||
} | ||
|
||
fn count_alive(location: vec2<i32>) -> i32 { | ||
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); | ||
} | ||
|
||
@compute @workgroup_size(8, 8, 1) | ||
fn update(@builtin(global_invocation_id) invocation_id: vec3<u32>) { | ||
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) { | ||
let currently_alive = is_alive(location, 0, 0); | ||
alive = bool(currently_alive); | ||
} else { | ||
alive = false; | ||
} | ||
let color = vec4<f32>(f32(alive)); | ||
|
||
textureStore(output, location, color); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
use nannou::prelude::*; | ||
|
||
const DISPLAY_FACTOR: u32 = 4; | ||
const SIZE: (u32, u32) = (1280 / DISPLAY_FACTOR, 720 / DISPLAY_FACTOR); | ||
const WORKGROUP_SIZE: u32 = 8; | ||
|
||
fn main() { | ||
nannou::app(model) | ||
.update(update) | ||
.compute(compute) | ||
.view(view) | ||
.run() | ||
} | ||
|
||
struct Model { | ||
texture_a: Handle<Image>, | ||
texture_b: Handle<Image>, | ||
displayed: Handle<Image>, | ||
} | ||
|
||
#[derive(Clone, Default)] | ||
enum State { | ||
#[default] | ||
Init, | ||
Update(usize), | ||
} | ||
|
||
|
||
#[derive(AsBindGroup, Clone)] | ||
struct ComputeModel { | ||
#[storage_texture(0, image_format = R32Float, access = ReadOnly)] | ||
texture_read: Handle<Image>, | ||
#[storage_texture(1, image_format = R32Float, access = WriteOnly)] | ||
texture_write: Handle<Image>, | ||
} | ||
|
||
impl ComputeShader for ComputeModel { | ||
type State = State; | ||
|
||
fn compute_shader() -> ShaderRef { | ||
ShaderRef::Path("shaders/game_of_life.wgsl".into()) | ||
} | ||
|
||
fn shader_entry(state: &Self::State) -> String { | ||
match state { | ||
State::Init => "init".into(), | ||
State::Update(_) => "update".into(), | ||
} | ||
} | ||
|
||
fn workgroup_size(state: &Self::State) -> (u32, u32, u32) { | ||
(SIZE.0 / WORKGROUP_SIZE, SIZE.1 / WORKGROUP_SIZE, 1) | ||
} | ||
} | ||
|
||
fn model(app: &App) -> Model { | ||
let _window = app.new_window::<Model>().size(SIZE.0 * DISPLAY_FACTOR, SIZE.1 * DISPLAY_FACTOR).build(); | ||
|
||
let mut image = Image::new_fill( | ||
Extent3d { | ||
width: SIZE.0, | ||
height: SIZE.1, | ||
depth_or_array_layers: 1, | ||
}, | ||
TextureDimension::D2, | ||
&[0, 0, 0, 255], | ||
TextureFormat::R32Float, | ||
RenderAssetUsages::RENDER_WORLD, | ||
); | ||
image.texture_descriptor.usage = | ||
TextureUsages::COPY_DST | TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING; | ||
|
||
info!("Adding image to assets"); | ||
let image0 = app.assets_mut().add(image.clone()); | ||
let image1 = app.assets_mut().add(image); | ||
info!("Added image to assets"); | ||
Model { | ||
texture_a: image0.clone(), | ||
texture_b: image1, | ||
displayed: image0.clone(), | ||
} | ||
} | ||
|
||
fn update(app: &App, model: &mut Model) { | ||
if model.displayed == model.texture_a { | ||
model.displayed = model.texture_b.clone(); | ||
} else { | ||
model.displayed = model.texture_a.clone(); | ||
} | ||
} | ||
|
||
fn compute(app: &App, model: &Model, state: &mut State, view: Entity) -> ComputeModel { | ||
if let State::Init = state { | ||
*state = State::Update(0); | ||
return ComputeModel { | ||
texture_read: model.texture_a.clone(), | ||
texture_write: model.texture_b.clone(), | ||
} | ||
} | ||
|
||
if model.displayed == model.texture_a { | ||
*state = State::Update(0); | ||
ComputeModel { | ||
texture_read: model.texture_a.clone(), | ||
texture_write: model.texture_b.clone(), | ||
} | ||
} else { | ||
*state = State::Update(1); | ||
ComputeModel { | ||
texture_read: model.texture_b.clone(), | ||
texture_write: model.texture_a.clone(), | ||
} | ||
} | ||
} | ||
|
||
fn view( | ||
app: &App, | ||
model: &Model, | ||
view: Entity, | ||
) { | ||
let draw = app.draw(); | ||
let window_rect = app.window_rect(); | ||
draw.rect() | ||
.w_h(window_rect.w(), window_rect.h()) | ||
.texture(&model.displayed); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.