From aa94681ab964f16b9cd967e942541ceb2e56dee0 Mon Sep 17 00:00:00 2001 From: numzero Date: Wed, 25 Sep 2024 23:53:29 +0300 Subject: [PATCH] Extract line rendering --- src/bin/wireframe/lines.rs | 170 +++++++++++++++++++++++++++++++++++++ src/bin/wireframe/main.rs | 161 ++++++----------------------------- 2 files changed, 196 insertions(+), 135 deletions(-) create mode 100644 src/bin/wireframe/lines.rs diff --git a/src/bin/wireframe/lines.rs b/src/bin/wireframe/lines.rs new file mode 100644 index 0000000..f1f47e8 --- /dev/null +++ b/src/bin/wireframe/lines.rs @@ -0,0 +1,170 @@ +use std::mem; + +use bytemuck::{bytes_of, cast_slice, Pod, Zeroable}; +use glam::Vec3; +use refraction::types::Ray; +use wgpu::util::DeviceExt as _; + +#[repr(C)] +#[derive(Copy, Clone, Debug, Pod, Zeroable)] +struct Vertex { + pub position: [f32; 3], + pub tangent: [f32; 3], +} + +#[repr(C)] +#[derive(Copy, Clone, Pod, Zeroable)] +struct PushConsts { + pub color: [f32; 3], + pub _pad: f32, +} + +#[derive(Copy, Clone)] +pub struct Attrs { + pub color: Vec3, +} + +impl Attrs { + fn consts(&self) -> PushConsts { + PushConsts { + color: self.color.to_array(), + _pad: 0., + } + } +} + +pub struct Line { + consts: PushConsts, + npoints: u32, + buf: wgpu::Buffer, +} + +impl Line { + pub fn new_strip(device: &wgpu::Device, attrs: Attrs, points: Vec) -> Line { + let data: Vec = points + .into_iter() + .map(|r| Vertex { + position: r.pos.to_array(), + tangent: r.dir.to_array(), + }) + .collect(); + let buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Vertex Buffer"), + contents: cast_slice(&data), + usage: wgpu::BufferUsages::VERTEX, + }); + Line { + consts: attrs.consts(), + npoints: data.len() as u32, + buf, + } + } +} + +pub struct LineRenderer { + pipeline: wgpu::RenderPipeline, +} + +static SHADER: &'static str = include_str!("ray.wgsl"); + +impl LineRenderer { + pub fn new( + device: &wgpu::Device, + cam_layout: &wgpu::BindGroupLayout, + target_format: wgpu::TextureFormat, + depth_stencil: Option, + multisample: wgpu::MultisampleState, + ) -> LineRenderer { + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("Line Shader"), + source: wgpu::ShaderSource::Wgsl(SHADER.into()), + }); + + let consts_range = wgpu::PushConstantRange { + stages: wgpu::ShaderStages::VERTEX, + range: 0..mem::size_of::() as u32, + }; + + let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Line RenderPipelineLayout"), + bind_group_layouts: &[cam_layout], + push_constant_ranges: &[consts_range], + }); + + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Line RenderPipeline"), + layout: Some(&layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[wgpu::VertexBufferLayout { + array_stride: mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Instance, + attributes: &[ + wgpu::VertexAttribute { + offset: mem::offset_of!(Vertex, position) as u64, + shader_location: 0, + format: wgpu::VertexFormat::Float32x3, + }, + wgpu::VertexAttribute { + offset: mem::offset_of!(Vertex, tangent) as u64, + shader_location: 1, + format: wgpu::VertexFormat::Float32x3, + }, + wgpu::VertexAttribute { + offset: (mem::size_of::() + mem::offset_of!(Vertex, position)) + as u64, + shader_location: 2, + format: wgpu::VertexFormat::Float32x3, + }, + wgpu::VertexAttribute { + offset: (mem::size_of::() + mem::offset_of!(Vertex, tangent)) + as u64, + shader_location: 3, + format: wgpu::VertexFormat::Float32x3, + }, + ], + }], + compilation_options: Default::default(), + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[Some(wgpu::ColorTargetState { + format: target_format, + blend: Some(wgpu::BlendState { + color: wgpu::BlendComponent::OVER, + alpha: wgpu::BlendComponent::OVER, + }), + write_mask: wgpu::ColorWrites::ALL, + })], + compilation_options: Default::default(), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleStrip, + ..Default::default() + }, + depth_stencil, + multisample, + multiview: None, + cache: None, + }); + + LineRenderer { pipeline } + } + + pub fn render<'a>( + &self, + pass: &mut wgpu::RenderPass, + cam_bind: &wgpu::BindGroup, + lines: impl Iterator, + ) { + pass.set_pipeline(&self.pipeline); + pass.set_bind_group(0, cam_bind, &[]); + for line in lines { + pass.set_push_constants(wgpu::ShaderStages::VERTEX, 0, bytes_of(&line.consts)); + pass.set_vertex_buffer(0, line.buf.slice(..)); + pass.draw(0..4, 0..line.npoints - 1); + } + } +} diff --git a/src/bin/wireframe/main.rs b/src/bin/wireframe/main.rs index 01b7e3f..22d494d 100644 --- a/src/bin/wireframe/main.rs +++ b/src/bin/wireframe/main.rs @@ -1,7 +1,6 @@ -use std::{iter, mem, time::Instant}; +use std::{iter, time::Instant}; use glam::{vec2, vec3, Vec3}; -use wgpu::{util::DeviceExt, ShaderStages}; use winit::{ event::*, event_loop::EventLoop, @@ -10,6 +9,7 @@ use winit::{ }; mod camera; +mod lines; mod scene; // The coordinate system: @@ -17,40 +17,10 @@ mod scene; // * Y: left // * Z: up -#[repr(C)] -#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] -struct Vertex { - position: [f32; 3], - tangent: [f32; 3], -} - -struct Wireframe { - color: Vec3, - data: wgpu::Buffer, - size: u32, -} - -fn prepare_scene(device: &wgpu::Device) -> Vec { +fn prepare_scene(device: &wgpu::Device) -> Vec { scene::build() .into_iter() - .map(|line| { - let color = line.color; - let data: Vec = line - .pts - .into_iter() - .map(|r| Vertex { - position: r.pos.to_array(), - tangent: r.dir.to_array(), - }) - .collect(); - let size = data.len() as u32; - let data = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Vertex Buffer"), - contents: bytemuck::cast_slice(&data), - usage: wgpu::BufferUsages::VERTEX, - }); - Wireframe { color, data, size } - }) + .map(|line| lines::Line::new_strip(device, lines::Attrs { color: line.color }, line.pts)) .collect() } @@ -141,28 +111,21 @@ static KEYS_ROTATE: &'static [(PhysicalKey, Vec3)] = &[ (PhysicalKey::Code(KeyCode::Numpad6), vec3(0., 0., -1.)), ]; -#[repr(C)] -#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] -struct LineUniform { - color: [f32; 3], - _pad: f32, -} - struct State<'a> { surface: wgpu::Surface<'a>, device: wgpu::Device, queue: wgpu::Queue, config: wgpu::SurfaceConfiguration, size: winit::dpi::PhysicalSize, - render_pipeline: wgpu::RenderPipeline, kbd: keyctl::Keyboard, t1: Instant, cam_loc: camctl::CameraLocation, cam_obj: camera::Camera, + line_rend: lines::LineRenderer, - scene: Vec, + scene: Vec, window: &'a Window, } @@ -190,7 +153,7 @@ impl<'a> State<'a> { label: None, required_features: wgpu::Features::PUSH_CONSTANTS, required_limits: wgpu::Limits { - max_push_constant_size: mem::size_of::() as u32, + max_push_constant_size: 16, ..wgpu::Limits::default() }, memory_hints: Default::default(), @@ -222,87 +185,21 @@ impl<'a> State<'a> { let cam_loc = camctl::CameraLocation::new(); let t1 = Instant::now(); - let cam_obj = camera::Camera::new(&device); - - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("Shader"), - source: wgpu::ShaderSource::Wgsl(include_str!("ray.wgsl").into()), - }); - - let line_push_constant_range = wgpu::PushConstantRange { - stages: ShaderStages::VERTEX, - range: 0..mem::size_of::() as u32, + let depth = None; + let msaa = wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, }; - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Render Pipeline Layout"), - bind_group_layouts: &[cam_obj.bind_group_layout()], - push_constant_ranges: &[line_push_constant_range], - }); - - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Render Pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", - buffers: &[wgpu::VertexBufferLayout { - array_stride: mem::size_of::() as wgpu::BufferAddress, - step_mode: wgpu::VertexStepMode::Instance, - attributes: &[ - wgpu::VertexAttribute { - offset: mem::offset_of!(Vertex, position) as u64, - shader_location: 0, - format: wgpu::VertexFormat::Float32x3, - }, - wgpu::VertexAttribute { - offset: mem::offset_of!(Vertex, tangent) as u64, - shader_location: 1, - format: wgpu::VertexFormat::Float32x3, - }, - wgpu::VertexAttribute { - offset: (mem::size_of::() + mem::offset_of!(Vertex, position)) - as u64, - shader_location: 2, - format: wgpu::VertexFormat::Float32x3, - }, - wgpu::VertexAttribute { - offset: (mem::size_of::() + mem::offset_of!(Vertex, tangent)) - as u64, - shader_location: 3, - format: wgpu::VertexFormat::Float32x3, - }, - ], - }], - compilation_options: Default::default(), - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_main", - targets: &[Some(wgpu::ColorTargetState { - format: config.format, - blend: Some(wgpu::BlendState { - color: wgpu::BlendComponent::OVER, - alpha: wgpu::BlendComponent::OVER, - }), - write_mask: wgpu::ColorWrites::ALL, - })], - compilation_options: Default::default(), - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleStrip, - ..Default::default() - }, - depth_stencil: None, - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - multiview: None, - cache: None, - }); + let cam_obj = camera::Camera::new(&device); + let line_rend = lines::LineRenderer::new( + &device, + cam_obj.bind_group_layout(), + config.format, + depth, + msaa, + ); let scene = prepare_scene(&device); @@ -312,7 +209,7 @@ impl<'a> State<'a> { queue, config, size, - render_pipeline, + line_rend, kbd, cam_loc, cam_obj, @@ -384,17 +281,11 @@ impl<'a> State<'a> { timestamp_writes: None, }); - render_pass.set_pipeline(&self.render_pipeline); - render_pass.set_bind_group(0, self.cam_obj.bind_group(), &[]); - for wireframe in &self.scene { - let line = LineUniform { - color: wireframe.color.to_array(), - _pad: 0., - }; - render_pass.set_push_constants(ShaderStages::VERTEX, 0, bytemuck::bytes_of(&line)); - render_pass.set_vertex_buffer(0, wireframe.data.slice(..)); - render_pass.draw(0..4, 0..wireframe.size - 1); - } + self.line_rend.render( + &mut render_pass, + self.cam_obj.bind_group(), + self.scene.iter(), + ); } self.queue.submit(iter::once(encoder.finish()));