diff --git a/shaders/line.wgsl b/shaders/line.wgsl new file mode 100644 index 0000000..df87f46 --- /dev/null +++ b/shaders/line.wgsl @@ -0,0 +1,27 @@ +struct LookParams { + m: mat4x4f, +} + +struct Vertex { + @location(0) pos: vec3f, + @location(1) color: vec3f, +} + +struct Varying { + @builtin(position) screen: vec4f, + @location(0) color: vec4f, +} + +@group(0) @binding(0) var look: LookParams; + +@vertex +fn on_vertex(in: Vertex) -> Varying { + let pos = look.m * vec4f(in.pos, 1.0); + let color = vec4f(in.color, 1.0); + return Varying(pos, color); +} + +@fragment +fn on_fragment(in: Varying) -> @location(0) vec4f { + return in.color; +} diff --git a/src/main.rs b/src/main.rs index a886807..10af420 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,8 @@ -use std::{error::Error, sync::Arc}; +use std::{error::Error, mem::offset_of, sync::Arc}; +use bytemuck::{Pod, Zeroable, bytes_of}; +use glam::{Mat4, Vec3, vec3}; +use wgpu::util::DeviceExt as _; use winit::{ application::ApplicationHandler, event::WindowEvent, @@ -16,6 +19,135 @@ struct MainWindow { queue: wgpu::Queue, surface: wgpu::Surface<'static>, surface_configured: bool, + + tripod: Pipeline, +} + +#[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); + } } impl MainWindow { @@ -26,7 +158,8 @@ impl MainWindow { let handle = Arc::new(handle); let (device, queue, surface) = pollster::block_on(init_gpu(Arc::clone(&handle))).unwrap(); - queue.submit([]); + let tripod = Pipeline::new(&device, OUTPUT_FORMAT); + queue.submit([]); // flush buffer updates Self { handle, @@ -34,15 +167,20 @@ impl MainWindow { queue, surface, surface_configured: false, + tripod, } } fn render(&self, output: &wgpu::Texture) { + self.tripod + .set_look(&self.queue, LookParams { m: Mat4::IDENTITY }); + self.queue.submit([]); // flush buffer updates + let view = output.create_view(&wgpu::TextureViewDescriptor::default()); let mut encoder = self .device .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); - encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: &view, depth_slice: None, @@ -60,6 +198,8 @@ impl MainWindow { depth_stencil_attachment: None, ..Default::default() }); + self.tripod.render(&mut pass); + drop(pass); self.queue.submit(std::iter::once(encoder.finish())); }