From c11621f56a0533542b3547403647b77163d0cd8e Mon Sep 17 00:00:00 2001 From: numzero Date: Sat, 11 Jan 2025 01:30:58 +0300 Subject: [PATCH] Basic setup --- src/fill.wgsl | 27 ++++++++ src/main.rs | 168 +++++++++++++++++++++++++++++++++++++++++++++-- src/present.wgsl | 18 ++--- src/step.wgsl | 20 ++++++ 4 files changed, 215 insertions(+), 18 deletions(-) create mode 100644 src/fill.wgsl create mode 100644 src/step.wgsl diff --git a/src/fill.wgsl b/src/fill.wgsl new file mode 100644 index 0000000..0b31d8c --- /dev/null +++ b/src/fill.wgsl @@ -0,0 +1,27 @@ +const SEED = 0x4ae95672u; + +struct Varying { + @builtin(position) screen: vec4f, +} + +@vertex +fn on_vertex(@builtin(vertex_index) vi: u32) -> Varying { + let uv = vec2f(vec2(vi, vi >> 1u) & vec2(1u)); + let screen = vec4(uv * 2. - 1., 0., 1.); + return Varying(screen); +} + +@fragment +fn on_fragment(in: Varying) -> @location(0) u32 { + let x = bitcast(in.screen.x); + let y = bitcast(in.screen.y); + let rand = hash(hash(hash(SEED) ^ hash(x)) ^ hash(y)); + return rand >> 31; +} + +fn hash(key : u32) -> u32 { + var v = key; + v *= 0xb384af1bu; + v ^= v >> 15u; + return v; +} diff --git a/src/main.rs b/src/main.rs index 64f15dc..967ee41 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,18 @@ use std::error::Error; -use glam::uvec2; +use glam::{uvec2, UVec2}; use winit::{ event::{Event, WindowEvent}, event_loop::EventLoop, window::{Window, WindowAttributes}, }; +struct GameState { + size: UVec2, + last: wgpu::TextureView, + next: wgpu::TextureView, +} + fn main() { let event_loop = EventLoop::new().unwrap(); @@ -17,7 +23,72 @@ fn main() { let (device, queue, surface) = pollster::block_on(init_gpu(window)).unwrap(); let format = wgpu::TextureFormat::Bgra8UnormSrgb; - let mut surface_configured = false; + + let fill_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("fill"), + source: wgpu::ShaderSource::Wgsl(include_str!("fill.wgsl").into()), + }); + let fill_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: None, + layout: None, + vertex: wgpu::VertexState { + module: &fill_shader, + entry_point: None, + compilation_options: wgpu::PipelineCompilationOptions::default(), + buffers: &[], + }, + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleStrip, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + fragment: Some(wgpu::FragmentState { + module: &fill_shader, + entry_point: None, + compilation_options: wgpu::PipelineCompilationOptions::default(), + targets: &[Some(wgpu::ColorTargetState { + format: wgpu::TextureFormat::R8Uint, + blend: None, + write_mask: wgpu::ColorWrites::ALL, + })], + }), + multiview: None, + cache: None, + }); + + let step_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("step"), + source: wgpu::ShaderSource::Wgsl(include_str!("step.wgsl").into()), + }); + let step_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: None, + layout: None, + vertex: wgpu::VertexState { + module: &step_shader, + entry_point: None, + compilation_options: wgpu::PipelineCompilationOptions::default(), + buffers: &[], + }, + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleStrip, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + fragment: Some(wgpu::FragmentState { + module: &step_shader, + entry_point: None, + compilation_options: wgpu::PipelineCompilationOptions::default(), + targets: &[Some(wgpu::ColorTargetState { + format: wgpu::TextureFormat::R8Uint, + blend: None, + write_mask: wgpu::ColorWrites::ALL, + })], + }), + multiview: None, + cache: None, + }); let present_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: Some("present"), @@ -52,6 +123,7 @@ fn main() { cache: None, }); + let mut state = None; #[allow(deprecated)] event_loop .run(move |event, control_flow| match event { @@ -72,15 +144,55 @@ fn main() { desired_maximum_frame_latency: 2, }, ); - surface_configured = true; + let [last, next] = [(), ()].map(|_| { + device + .create_texture(&wgpu::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width: size.x, + height: size.y, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::R8Uint, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }) + .create_view(&Default::default()) + }); + + let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &last, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::BLUE), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + occlusion_query_set: None, + timestamp_writes: None, + }); + render_pass.set_pipeline(&fill_pipeline); + render_pass.draw(0..4, 0..1); + drop(render_pass); + queue.submit(std::iter::once(encoder.finish())); + + state = Some(GameState { size, last, next }); } WindowEvent::RedrawRequested => { - if !surface_configured { + let Some(state) = &mut state else { return; - } + }; let output = surface.get_current_texture().unwrap(); let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default()); let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: None, color_attachments: &[Some(wgpu::RenderPassColorAttachment { @@ -96,10 +208,56 @@ fn main() { timestamp_writes: None, }); render_pass.set_pipeline(&present_pipeline); + render_pass.set_bind_group( + 0, + &device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &present_pipeline.get_bind_group_layout(0), + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&state.last), + }], + }), + &[], + ); render_pass.draw(0..4, 0..1); drop(render_pass); + + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &state.next, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::BLUE), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + occlusion_query_set: None, + timestamp_writes: None, + }); + render_pass.set_pipeline(&step_pipeline); + render_pass.set_bind_group( + 0, + &device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &step_pipeline.get_bind_group_layout(0), + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&state.last), + }], + }), + &[], + ); + render_pass.draw(0..4, 0..1); + drop(render_pass); + queue.submit(std::iter::once(encoder.finish())); output.present(); + + std::mem::swap(&mut state.last, &mut state.next); + window.request_redraw(); } _ => {} }, diff --git a/src/present.wgsl b/src/present.wgsl index 0663c5c..656d55f 100644 --- a/src/present.wgsl +++ b/src/present.wgsl @@ -1,5 +1,3 @@ -const SEED = 0x4ae95672u; - struct Varying { @location(0) uv: vec2f, @builtin(position) screen: vec4f, @@ -12,17 +10,11 @@ fn on_vertex(@builtin(vertex_index) vi: u32) -> Varying { return Varying(vec2(uv.x, 1. - uv.y), screen); } +@group(0) @binding(0) var field: texture_2d; + @fragment fn on_fragment(in: Varying) -> @location(0) vec4f { - let x = bitcast(in.uv.x); - let y = bitcast(in.uv.y); - let rand = hash(hash(hash(SEED) ^ hash(x)) ^ hash(y)); - return vec4f(f32(rand) / 0x1p32); -} - -fn hash(key : u32) -> u32 { - var v = key; - v *= 0xb384af1bu; - v ^= v >> 15u; - return v; + let pos = vec2u(in.screen.xy); + let state = textureLoad(field, pos, 0).x; + return vec4f(f32(state)); } diff --git a/src/step.wgsl b/src/step.wgsl new file mode 100644 index 0000000..b411e3c --- /dev/null +++ b/src/step.wgsl @@ -0,0 +1,20 @@ +struct Varying { + @location(0) uv: vec2f, + @builtin(position) screen: vec4f, +} + +@vertex +fn on_vertex(@builtin(vertex_index) vi: u32) -> Varying { + let uv = vec2f(vec2(vi, vi >> 1u) & vec2(1u)); + let screen = vec4(uv * 2. - 1., 0., 1.); + return Varying(vec2(uv.x, 1. - uv.y), screen); +} + +@group(0) @binding(0) var field: texture_2d; + +@fragment +fn on_fragment(in: Varying) -> @location(0) u32 { + let pos = vec2u(in.screen.xy); + let state = textureLoad(field, pos, 0).x; + return 1 - state; +}