diff --git a/src/main.rs b/src/main.rs index 10af420..e46c85d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,6 @@ -use std::{error::Error, mem::offset_of, sync::Arc}; +use std::{error::Error, sync::Arc}; -use bytemuck::{Pod, Zeroable, bytes_of}; -use glam::{Mat4, Vec3, vec3}; -use wgpu::util::DeviceExt as _; +use glam::{Mat4, vec3}; use winit::{ application::ApplicationHandler, event::WindowEvent, @@ -10,6 +8,10 @@ use winit::{ window::Window, }; +use crate::render::lines::{LookParams, Mesh, Pipeline, Vertex}; + +mod render; + const TITLE: &str = "WGPU example"; const OUTPUT_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8UnormSrgb; @@ -20,134 +22,22 @@ struct MainWindow { surface: wgpu::Surface<'static>, surface_configured: bool, - tripod: Pipeline, + pipeline: Pipeline, + tripod: Mesh, } -#[derive(Debug, Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct LookParams { - pub m: Mat4, -} - -#[derive(Debug, Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct Vertex { - pub pos: Vec3, - pub color: Vec3, -} - -impl Vertex { - pub fn new(pos: Vec3, color: Vec3) -> Self { - Self { pos, color } - } -} - -pub struct Pipeline { - tripod_buf: wgpu::Buffer, - look_buf: wgpu::Buffer, - bindings: wgpu::BindGroup, - pipeline: wgpu::RenderPipeline, -} - -impl Pipeline { - pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self { - let tripod_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: None, - usage: wgpu::BufferUsages::VERTEX, - contents: bytes_of(&[ - Vertex::new(vec3(0., 0., 0.), vec3(1., 0., 0.)), - Vertex::new(vec3(1., 0., 0.), vec3(1., 0., 0.)), - Vertex::new(vec3(0., 0., 0.), vec3(0., 1., 0.)), - Vertex::new(vec3(0., 1., 0.), vec3(0., 1., 0.)), - Vertex::new(vec3(0., 0., 0.), vec3(0., 0., 1.)), - Vertex::new(vec3(0., 0., 1.), vec3(0., 0., 1.)), - ]), - }); - let look_buf = device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: size_of::() as u64, - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - mapped_at_creation: false, - }); - - let shader = std::fs::read_to_string("shaders/line.wgsl").unwrap(); - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(shader.into()), - }); - let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: None, - layout: None, - vertex: wgpu::VertexState { - module: &shader, - entry_point: None, - compilation_options: wgpu::PipelineCompilationOptions::default(), - buffers: &[wgpu::VertexBufferLayout { - array_stride: size_of::() as u64, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &[ - wgpu::VertexAttribute { - shader_location: 0, - offset: offset_of!(Vertex, pos) as u64, - format: wgpu::VertexFormat::Float32x3, - }, - wgpu::VertexAttribute { - shader_location: 1, - offset: offset_of!(Vertex, color) as u64, - format: wgpu::VertexFormat::Float32x3, - }, - ], - }], - }, - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::LineList, - ..Default::default() - }, - depth_stencil: None, - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: None, - compilation_options: wgpu::PipelineCompilationOptions::default(), - targets: &[Some(wgpu::ColorTargetState { - format, - blend: Some(wgpu::BlendState::REPLACE), - write_mask: wgpu::ColorWrites::ALL, - })], - }), - multiview: None, - cache: None, - }); - let bindings = device.create_bind_group(&wgpu::BindGroupDescriptor { - label: None, - layout: &pipeline.get_bind_group_layout(0), - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: look_buf.as_entire_binding(), - }], - }); - Self { - tripod_buf, - look_buf, - bindings, - pipeline, - } - } - - pub fn set_look(&self, queue: &wgpu::Queue, look: LookParams) { - queue.write_buffer(&self.look_buf, 0, bytes_of(&look)); - } - - pub fn render(&self, pass: &mut wgpu::RenderPass) { - pass.set_pipeline(&self.pipeline); - pass.set_vertex_buffer(0, self.tripod_buf.slice(..)); - pass.set_bind_group(0, &self.bindings, &[]); - pass.draw(0..6, 0..1); - } +pub fn new_tripod(device: &wgpu::Device) -> Mesh { + Mesh::new( + device, + &[ + Vertex::new(vec3(0., 0., 0.), vec3(1., 0., 0.)), + Vertex::new(vec3(1., 0., 0.), vec3(1., 0., 0.)), + Vertex::new(vec3(0., 0., 0.), vec3(0., 1., 0.)), + Vertex::new(vec3(0., 1., 0.), vec3(0., 1., 0.)), + Vertex::new(vec3(0., 0., 0.), vec3(0., 0., 1.)), + Vertex::new(vec3(0., 0., 1.), vec3(0., 0., 1.)), + ], + ) } impl MainWindow { @@ -158,7 +48,8 @@ impl MainWindow { let handle = Arc::new(handle); let (device, queue, surface) = pollster::block_on(init_gpu(Arc::clone(&handle))).unwrap(); - let tripod = Pipeline::new(&device, OUTPUT_FORMAT); + let pipeline = Pipeline::new(&device, OUTPUT_FORMAT); + let tripod = new_tripod(&device); queue.submit([]); // flush buffer updates Self { @@ -167,12 +58,13 @@ impl MainWindow { queue, surface, surface_configured: false, + pipeline, tripod, } } fn render(&self, output: &wgpu::Texture) { - self.tripod + self.pipeline .set_look(&self.queue, LookParams { m: Mat4::IDENTITY }); self.queue.submit([]); // flush buffer updates @@ -198,7 +90,7 @@ impl MainWindow { depth_stencil_attachment: None, ..Default::default() }); - self.tripod.render(&mut pass); + self.pipeline.render(&mut pass, [&self.tripod]); drop(pass); self.queue.submit(std::iter::once(encoder.finish())); } diff --git a/src/render/lines.rs b/src/render/lines.rs new file mode 100644 index 0000000..62cade4 --- /dev/null +++ b/src/render/lines.rs @@ -0,0 +1,143 @@ +use std::mem::offset_of; + +use bytemuck::{Pod, Zeroable, bytes_of, cast_slice}; +use glam::{Mat4, Vec3}; +use wgpu::util::DeviceExt as _; + +#[derive(Debug, Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct LookParams { + pub m: Mat4, +} + +#[derive(Debug, Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct Vertex { + pub pos: Vec3, + pub color: Vec3, +} + +impl Vertex { + pub fn new(pos: Vec3, color: Vec3) -> Self { + Self { pos, color } + } +} + +pub struct Mesh { + vertex_buffer: wgpu::Buffer, + vertex_count: u32, +} + +impl Mesh { + pub fn new(device: &wgpu::Device, vertices: &[Vertex]) -> Self { + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + usage: wgpu::BufferUsages::VERTEX, + contents: cast_slice(&vertices), + }); + Self { + vertex_buffer, + vertex_count: vertices.len().try_into().expect("too many vertices"), + } + } +} + +pub struct Pipeline { + look_buf: wgpu::Buffer, + bindings: wgpu::BindGroup, + pipeline: wgpu::RenderPipeline, +} + +impl Pipeline { + pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self { + let look_buf = device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: size_of::() as u64, + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + + let shader = std::fs::read_to_string("shaders/line.wgsl").unwrap(); + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: None, + source: wgpu::ShaderSource::Wgsl(shader.into()), + }); + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: None, + layout: None, + vertex: wgpu::VertexState { + module: &shader, + entry_point: None, + compilation_options: wgpu::PipelineCompilationOptions::default(), + buffers: &[wgpu::VertexBufferLayout { + array_stride: size_of::() as u64, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &[ + wgpu::VertexAttribute { + shader_location: 0, + offset: offset_of!(Vertex, pos) as u64, + format: wgpu::VertexFormat::Float32x3, + }, + wgpu::VertexAttribute { + shader_location: 1, + offset: offset_of!(Vertex, color) as u64, + format: wgpu::VertexFormat::Float32x3, + }, + ], + }], + }, + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::LineList, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: None, + compilation_options: wgpu::PipelineCompilationOptions::default(), + targets: &[Some(wgpu::ColorTargetState { + format, + blend: Some(wgpu::BlendState::REPLACE), + write_mask: wgpu::ColorWrites::ALL, + })], + }), + multiview: None, + cache: None, + }); + let bindings = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &pipeline.get_bind_group_layout(0), + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: look_buf.as_entire_binding(), + }], + }); + Self { + look_buf, + bindings, + pipeline, + } + } + + pub fn set_look(&self, queue: &wgpu::Queue, look: LookParams) { + queue.write_buffer(&self.look_buf, 0, bytes_of(&look)); + } + + pub fn render<'a>( + &self, + pass: &mut wgpu::RenderPass, + meshes: impl IntoIterator, + ) { + pass.set_pipeline(&self.pipeline); + pass.set_bind_group(0, &self.bindings, &[]); + for mesh in meshes { + pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..)); + pass.draw(0..mesh.vertex_count, 0..1); + } + } +} diff --git a/src/render/mod.rs b/src/render/mod.rs new file mode 100644 index 0000000..ef14730 --- /dev/null +++ b/src/render/mod.rs @@ -0,0 +1 @@ +pub mod lines;