WebGPU Voxel Game

Tutorial 4: Buffers

+178 -65
+15
Cargo.lock
··· 152 152 name = "bl0ck" 153 153 version = "0.1.0" 154 154 dependencies = [ 155 + "bytemuck", 155 156 "cfg-if", 156 157 "console_error_panic_hook", 157 158 "console_log", ··· 201 202 version = "1.18.0" 202 203 source = "registry+https://github.com/rust-lang/crates.io-index" 203 204 checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" 205 + dependencies = [ 206 + "bytemuck_derive", 207 + ] 208 + 209 + [[package]] 210 + name = "bytemuck_derive" 211 + version = "1.7.1" 212 + source = "registry+https://github.com/rust-lang/crates.io-index" 213 + checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26" 214 + dependencies = [ 215 + "proc-macro2", 216 + "quote", 217 + "syn 2.0.77", 218 + ] 204 219 205 220 [[package]] 206 221 name = "bytes"
+1
Cargo.toml
··· 12 12 wasm-bindgen = "0.2.93" 13 13 web-sys = "0.3.70" 14 14 pollster = "0.3.0" 15 + bytemuck = { version = "1.18.0", features = ["derive"] } 15 16 16 17 17 18 [lib]
+151 -59
src/lib.rs
··· 1 + use wgpu::util::DeviceExt; 1 2 use winit::{ 2 3 event::*, 3 4 event_loop::EventLoop, ··· 8 9 #[cfg(target_arch = "wasm32")] 9 10 use wasm_bindgen::prelude::*; 10 11 12 + #[repr(C)] 13 + #[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)] 14 + struct Vertex { 15 + position: [f32; 3], 16 + color: [f32; 3], 17 + } 18 + 19 + impl Vertex { 20 + fn desc() -> wgpu::VertexBufferLayout<'static> { 21 + wgpu::VertexBufferLayout { 22 + array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress, 23 + step_mode: wgpu::VertexStepMode::Vertex, 24 + attributes: &[ 25 + wgpu::VertexAttribute { 26 + offset: 0, 27 + shader_location: 0, 28 + format: wgpu::VertexFormat::Float32x3, 29 + }, 30 + wgpu::VertexAttribute { 31 + offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, 32 + shader_location: 1, 33 + format: wgpu::VertexFormat::Float32x3, 34 + }, 35 + ], 36 + } 37 + } 38 + } 39 + 40 + const TRI_VERTICES: &[Vertex] = &[ 41 + // CCW order 42 + Vertex { 43 + position: [0.0, 0.5, 0.0], 44 + color: [1.0, 0.0, 0.0], 45 + }, 46 + Vertex { 47 + position: [-0.5, -0.5, 0.0], 48 + color: [0.0, 1.0, 0.0], 49 + }, 50 + Vertex { 51 + position: [0.5, -0.5, 0.0], 52 + color: [0.0, 0.0, 1.0], 53 + }, 54 + ]; 55 + 56 + const VERTICES: &[Vertex] = &[ 57 + Vertex { position: [-0.0868241, 0.49240386, 0.0], color: [0.5, 0.0, 0.5] }, // A 58 + Vertex { position: [-0.49513406, 0.06958647, 0.0], color: [0.5, 0.0, 0.5] }, // B 59 + Vertex { position: [-0.21918549, -0.44939706, 0.0], color: [0.5, 0.0, 0.5] }, // C 60 + Vertex { position: [0.35966998, -0.3473291, 0.0], color: [0.5, 0.0, 0.5] }, // D 61 + Vertex { position: [0.44147372, 0.2347359, 0.0], color: [0.5, 0.0, 0.5] }, // E 62 + ]; 63 + 64 + const INDICES: &[u16] = &[ 65 + 0, 1, 4, 66 + 1, 2, 4, 67 + 2, 3, 4, 68 + ]; 69 + 11 70 struct State<'srfc> { 12 71 surface: wgpu::Surface<'srfc>, 13 72 device: wgpu::Device, ··· 15 74 config: wgpu::SurfaceConfiguration, 16 75 size: winit::dpi::PhysicalSize<u32>, 17 76 render_pipeline: wgpu::RenderPipeline, 77 + vertex_buffer: wgpu::Buffer, 78 + index_buffer: wgpu::Buffer, 79 + num_indices: u32, 18 80 19 81 // Window must be declared after surface 20 82 // so it gets dropped after it ··· 23 85 // src: learn wgpu 24 86 window: &'srfc Window, 25 87 26 - clear_color: wgpu::Color 88 + clear_color: wgpu::Color, 27 89 } 28 90 29 91 impl<'a> State<'a> { ··· 100 162 101 163 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { 102 164 label: Some("Shader"), 103 - source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()) 165 + source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()), 104 166 }); 105 167 106 168 let render_pipeline_layout = 107 169 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { 108 170 label: Some("Render Pipeline Layout"), 109 171 bind_group_layouts: &[], 110 - push_constant_ranges: &[] 172 + push_constant_ranges: &[], 111 173 }); 112 - 174 + 113 175 let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { 114 176 label: Some("Render Pipeline"), 115 177 layout: Some(&render_pipeline_layout), 116 178 vertex: wgpu::VertexState { 117 179 module: &shader, 118 180 entry_point: "vs_main", 119 - buffers: &[], 181 + buffers: &[Vertex::desc()], 120 182 compilation_options: wgpu::PipelineCompilationOptions::default(), 121 183 }, 122 184 fragment: Some(wgpu::FragmentState { ··· 142 204 multisample: wgpu::MultisampleState { 143 205 count: 1, 144 206 mask: !0, 145 - alpha_to_coverage_enabled: false 207 + alpha_to_coverage_enabled: false, 146 208 }, 147 209 multiview: None, 148 210 cache: None, 149 211 }); 150 212 213 + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { 214 + label: Some("Vertex Buffer"), 215 + contents: bytemuck::cast_slice(VERTICES), 216 + usage: wgpu::BufferUsages::VERTEX, 217 + }); 218 + 219 + let index_buffer = device.create_buffer_init( 220 + &wgpu::util::BufferInitDescriptor { 221 + label: Some("Index Buffer"), 222 + contents: bytemuck::cast_slice(INDICES), 223 + usage: wgpu::BufferUsages::INDEX, 224 + } 225 + ); 226 + 227 + let num_indices = INDICES.len() as u32; 228 + 151 229 Self { 152 230 window, 153 231 surface, ··· 156 234 config, 157 235 size, 158 236 render_pipeline, 237 + vertex_buffer, 238 + index_buffer, 239 + num_indices, 159 240 clear_color: wgpu::Color { 160 241 r: 0.1, 161 242 g: 0.2, 162 243 b: 0.3, 163 - a: 1.0 164 - } 244 + a: 1.0, 245 + }, 165 246 } 166 247 } 167 248 ··· 182 263 183 264 fn input(&mut self, event: &WindowEvent) -> bool { 184 265 match event { 185 - WindowEvent::CursorMoved { device_id, position } => { 266 + WindowEvent::CursorMoved { 267 + device_id, 268 + position, 269 + } => { 186 270 dbg!(position); 187 271 self.clear_color = wgpu::Color { 188 - r: position.x / (self.window.inner_size().width as f64), 189 - g: 0.2, 190 - b: position.y / (self.window.inner_size().height as f64), 191 - a: 0.1 272 + r: position.x / (self.window.inner_size().width as f64), 273 + g: 0.2, 274 + b: position.y / (self.window.inner_size().height as f64), 275 + a: 0.1, 192 276 }; 193 277 return true; 194 - }, 278 + } 195 279 _ => {} 196 280 } 197 281 198 282 false 199 283 } 200 284 201 - fn update(&mut self) { 202 - } 285 + fn update(&mut self) {} 203 286 204 287 fn render(&mut self) -> Result<(), wgpu::SurfaceError> { 205 288 let output = self.surface.get_current_texture()?; 206 289 207 - let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default()); 290 + let view = output 291 + .texture 292 + .create_view(&wgpu::TextureViewDescriptor::default()); 208 293 209 - let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { 210 - label: Some("Render Encoder") 211 - }); 294 + let mut encoder = self 295 + .device 296 + .create_command_encoder(&wgpu::CommandEncoderDescriptor { 297 + label: Some("Render Encoder"), 298 + }); 212 299 213 300 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { 214 301 label: Some("Render Pass"), ··· 218 305 ops: wgpu::Operations { 219 306 load: wgpu::LoadOp::Clear(self.clear_color), 220 307 store: wgpu::StoreOp::Store, 221 - } 308 + }, 222 309 })], 223 310 depth_stencil_attachment: None, 224 311 occlusion_query_set: None, 225 312 timestamp_writes: None, 226 313 }); 227 - 314 + 228 315 render_pass.set_pipeline(&self.render_pipeline); 229 - render_pass.draw(0..3, 0..1); 316 + render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); 317 + render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16); 318 + render_pass.draw_indexed(0..self.num_indices, 0, 0..1); 230 319 231 320 // drop render pass before we submit to drop the mut borrow on encoder 232 321 drop(render_pass); ··· 277 366 let mut state = State::new(&window).await; 278 367 let mut surface_configured = false; 279 368 280 - event_loop.run(move |event, control_flow| { 281 - match event { 369 + event_loop 370 + .run(move |event, control_flow| match event { 282 371 Event::WindowEvent { 283 372 ref event, 284 373 window_id, 285 - } if window_id == state.window().id() => if !state.input(event) { 286 - match event { 287 - WindowEvent::CloseRequested | WindowEvent::KeyboardInput { 288 - event: 289 - KeyEvent { 290 - state: ElementState::Pressed, 291 - physical_key: PhysicalKey::Code(KeyCode::Escape), 292 - .. 293 - }, 294 - .. 295 - } => control_flow.exit(), 296 - WindowEvent::Resized(physical_size) => { 297 - surface_configured = true; 298 - state.resize(*physical_size); 299 - } 300 - WindowEvent::RedrawRequested => { 301 - state.window().request_redraw(); 302 - 303 - if !surface_configured { 304 - return; 374 + } if window_id == state.window().id() => { 375 + if !state.input(event) { 376 + match event { 377 + WindowEvent::CloseRequested 378 + | WindowEvent::KeyboardInput { 379 + event: 380 + KeyEvent { 381 + state: ElementState::Pressed, 382 + physical_key: PhysicalKey::Code(KeyCode::Escape), 383 + .. 384 + }, 385 + .. 386 + } => control_flow.exit(), 387 + WindowEvent::Resized(physical_size) => { 388 + surface_configured = true; 389 + state.resize(*physical_size); 305 390 } 391 + WindowEvent::RedrawRequested => { 392 + state.window().request_redraw(); 306 393 307 - state.update(); 308 - match state.render() { 309 - Ok(_) => {} 310 - Err( 311 - wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated 312 - ) => state.resize(state.size), 313 - Err(wgpu::SurfaceError::OutOfMemory) => { 314 - log::error!("Out of memory!"); 315 - control_flow.exit(); 394 + if !surface_configured { 395 + return; 316 396 } 317 - Err(wgpu::SurfaceError::Timeout) => { 318 - log::warn!("Surface timeout!"); 397 + 398 + state.update(); 399 + match state.render() { 400 + Ok(_) => {} 401 + Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => { 402 + state.resize(state.size) 403 + } 404 + Err(wgpu::SurfaceError::OutOfMemory) => { 405 + log::error!("Out of memory!"); 406 + control_flow.exit(); 407 + } 408 + Err(wgpu::SurfaceError::Timeout) => { 409 + log::warn!("Surface timeout!"); 410 + } 319 411 } 320 412 } 413 + _ => {} 321 414 } 322 - _ => {} 323 415 } 324 - }, 416 + } 325 417 Event::AboutToWait => { 326 418 state.window().request_redraw(); 327 419 } 328 420 _ => {} 329 - }}) 421 + }) 330 422 .unwrap(); 331 423 }
+11 -6
src/shader.wgsl
··· 1 1 // Vertex shader 2 2 3 + struct VertexInput { 4 + @location(0) position: vec3<f32>, 5 + @location(1) color: vec3<f32>, 6 + } 7 + 3 8 struct VertexOutput { 4 9 @builtin(position) clip_position: vec4<f32>, 10 + @location(0) color: vec3<f32>, 5 11 }; 6 12 7 13 @vertex 8 14 fn vs_main( 9 - @builtin(vertex_index) in_vertex_index: u32, 15 + model: VertexInput, 10 16 ) -> VertexOutput { 11 17 var out: VertexOutput; 12 - let x = f32(1 - i32(in_vertex_index)) * 0.5; 13 - let y = f32(i32(in_vertex_index & 1u) * 2 - 1) * 0.5; 14 - out.clip_position = vec4<f32>(x, y, 0.0, 1.0); 18 + out.color = model.color; 19 + out.clip_position = vec4<f32>(model.position, 1.0); 15 20 return out; 16 21 } 17 22 ··· 20 25 21 26 @fragment 22 27 fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> { 23 - return vec4<f32>(0.3, 0.2, 0.1, 1.0); 24 - } 28 + return vec4<f32>(in.color, 1.0); 29 + }