From 7e855f5a468a8a3fb6a7ae15870f18e232735f99 Mon Sep 17 00:00:00 2001 From: mars Date: Wed, 11 May 2022 09:30:01 -0600 Subject: [PATCH] scene module + better trait/generic relationships --- src/camera.rs | 19 +++++++---- src/lib.rs | 44 +++++++++++------------- src/main.rs | 75 +++++++++++++++++++++++++++++++++++------ src/pass.rs | 12 +++++-- src/pass/debug.rs | 23 +------------ src/pass/mesh.rs | 86 ++++++++++++++--------------------------------- src/scene.rs | 54 +++++++++++++++++++++++++++++ src/viewport.rs | 70 +++++++++++++------------------------- 8 files changed, 210 insertions(+), 173 deletions(-) create mode 100644 src/scene.rs diff --git a/src/camera.rs b/src/camera.rs index beb7003..2d9679f 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -3,9 +3,9 @@ use std::f32::consts::LN_2; use std::time::Instant; use winit::event::{ElementState, VirtualKeyCode}; -pub trait Camera { - fn get_eye(&self) -> [f32; 4]; - fn get_vp(&self) -> [[f32; 4]; 4]; +pub struct Camera { + pub eye: [f32; 4], + pub vp: [[f32; 4]; 4], } #[derive(Debug)] @@ -209,14 +209,19 @@ impl Flycam { fn get_orientation(&self) -> glam::Quat { Quat::from_euler(glam::EulerRot::YXZ, self.euler_y, self.euler_x, 0.0) } -} -impl Camera for Flycam { - fn get_eye(&self) -> [f32; 4] { + pub fn get_camera(&self) -> Camera { + Camera { + eye: self.get_eye(), + vp: self.get_vp() + } + } + + pub fn get_eye(&self) -> [f32; 4] { self.position.extend(0.0).to_array() } - fn get_vp(&self) -> [[f32; 4]; 4] { + pub fn get_vp(&self) -> [[f32; 4]; 4] { // view matrix is inverted camera pose (world space to camera space) let rotation = Mat4::from_quat(self.get_orientation().inverse()); let translation = Mat4::from_translation(-self.position); diff --git a/src/lib.rs b/src/lib.rs index 8a1e593..9fb9bca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,14 @@ //! Cyborg is a high-performance, modern, experimental rendering engine written //! in Rust. +use parking_lot::Mutex; use rayon::prelude::*; use std::sync::Arc; -use parking_lot::Mutex; pub mod camera; pub mod pass; pub mod phase; +pub mod scene; pub mod shader; pub mod staging; pub mod storage; @@ -16,7 +17,7 @@ pub mod viewport; use camera::Camera; use pass::*; use phase::*; -use viewport::ViewportImage; +use viewport::Viewport; #[repr(C)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] @@ -26,10 +27,10 @@ pub struct ViewportUniform { } impl ViewportUniform { - pub fn from_camera(camera: &impl Camera) -> Self { + pub fn from_camera(camera: &Camera) -> Self { Self { - eye: camera.get_eye(), - vp: camera.get_vp(), + eye: camera.eye, + vp: camera.vp, } } } @@ -160,21 +161,16 @@ impl Renderer { &self.layouts } - pub fn add_pass(&mut self, pass: T) { - let pass = Arc::new(pass); - self.add_pass_arc(pass); + pub fn get_frames_in_flight(&self) -> usize { + self.frames_in_flight } - pub fn add_pass_arc(&mut self, pass: Arc) { - let pass = RenderPassBox::new(pass, self.frames_in_flight); - self.add_pass_box(pass); - } - - pub fn add_pass_box(&mut self, pass: Box) { - self.render_passes.push(pass); - } - - pub fn render<'a>(&mut self, target: &impl ViewportImage<'a>, camera: &impl Camera) { + pub fn render<'a>( + &mut self, + passes: &mut [&mut dyn RenderPassBoxTrait], + target: &dyn Viewport, + camera: &Camera, + ) { self.frame_index += 1; if self.frame_index >= self.frame_datas.len() { self.frame_index = 0; @@ -193,7 +189,7 @@ impl Renderer { let phase_passes = multimap::MultiMap::::new(); let phase_passes = std::sync::Mutex::new(phase_passes); - self.render_passes + passes .par_iter_mut() .enumerate() .for_each(|(pass_index, rp)| { @@ -219,7 +215,7 @@ impl Renderer { if let Some(upload) = phase_passes.get_vec(&Phase::Upload) { upload.iter().for_each(|pass_index| { let phase_data = frame_data.make_phase_data(Phase::Upload, &viewport); - let pass = &self.render_passes[*pass_index]; + let pass = &passes[*pass_index]; pass.record_commands(phase_data, &mut encoder); }); } @@ -231,17 +227,17 @@ impl Renderer { skinning.iter().for_each(|pass_index| { let phase_data = frame_data.make_phase_data(Phase::Skinning, &viewport); - let pass = &self.render_passes[*pass_index]; + let pass = &passes[*pass_index]; pass.record_compute(phase_data, &mut cmds); }) } let record_render = |phase| { let cmds = Mutex::new(Vec::new()); - if let Some(passes) = phase_passes.get_vec(&phase) { - passes.par_iter().for_each(|pass_index| { + if let Some(phase_passes) = phase_passes.get_vec(&phase) { + phase_passes.par_iter().for_each(|pass_index| { let phase_data = frame_data.make_phase_data(phase, &viewport); - let pass = &self.render_passes[*pass_index]; + let pass = &passes[*pass_index]; if let Some(cmd) = pass.record_render(phase_data) { cmds.lock().push(cmd); diff --git a/src/main.rs b/src/main.rs index 2e00356..787bd1b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use cyborg::shader; +use cyborg::storage::mesh::*; use cyborg::{pass, viewport::*, Renderer}; use std::sync::Arc; use winit::{ @@ -13,7 +14,7 @@ fn main() { let mut viewport = pollster::block_on(WinitViewport::from_window(&window)); let mut renderer = Renderer::new(viewport.device.clone(), viewport.queue.clone()); - let mut camera = cyborg::camera::Flycam::new(0.002, 10.0, 0.25); + let mut flycam = cyborg::camera::Flycam::new(0.002, 10.0, 0.25); let mut is_grabbed = false; let device = renderer.get_device(); @@ -43,31 +44,85 @@ fn main() { let debug_pass = pass::debug::DebugPass::new(device.to_owned(), layouts.to_owned(), viewport.get_info()); - renderer.add_pass(mesh_pass); - renderer.add_pass(debug_pass); + let example_vertices = vec![ + pass::mesh::Vertex { + position: [-0.5, 0.5, 0.0], + tan_frame: 0, + }, + pass::mesh::Vertex { + position: [0.5, 0.5, 0.0], + tan_frame: 0, + }, + pass::mesh::Vertex { + position: [0.0, -0.5, 0.0], + tan_frame: 0, + }, + ]; + + let example_vertices = AttrBuffer { + id: mesh_pass.get_vertex_attr_id(), + count: example_vertices.len(), + data: bytemuck::cast_slice(&example_vertices).to_vec(), + }; + + let example_indices = vec![0u32, 1u32, 2u32]; + let example_indices = AttrBuffer { + id: mesh_pass.get_index_attr_id(), + count: example_indices.len(), + data: bytemuck::cast_slice(&example_indices).to_vec(), + }; + + let mut example_mesh = MeshBuffer::default(); + example_mesh.attributes.push(example_vertices); + example_mesh.attributes.push(example_indices); + let example_mesh = mesh_pass.get_mesh_pool().load(example_mesh).unwrap(); + + let meshes = mesh_pass.get_meshes().to_owned(); + + let r = 8; + let mut meshes_lock = meshes.write(); + for x in -r..r { + for y in -r..r { + for z in -r..r { + let translation = glam::Vec3::new(x as f32, y as f32, z as f32); + let transform = glam::Mat4::from_translation(translation); + meshes_lock.transformed.push(cyborg::scene::TransformedMesh { + transform, + mesh: example_mesh.clone(), + }); + } + } + } + drop(meshes_lock); + + let frames_in_flight = renderer.get_frames_in_flight(); + let mut mesh_pass = pass::RenderPassBox::new(Arc::new(mesh_pass), frames_in_flight); + let mut debug_pass = pass::RenderPassBox::new(Arc::new(debug_pass), frames_in_flight); event_loop.run(move |event, _, control_flow| match event { Event::RedrawRequested(_) => { - println!("camera: {:#?}", camera); match viewport.acquire() { Err(wgpu::SurfaceError::Lost) => viewport.resize(viewport.size), Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit, Err(e) => eprintln!("error: {:?}", e), - Ok(target) => { - renderer.render(&target, &camera); - target.present(); + Ok(_) => { + let mut passes: Vec<&mut dyn pass::RenderPassBoxTrait> = Vec::new(); + passes.push(&mut mesh_pass); + passes.push(&mut debug_pass); + renderer.render(passes.as_mut_slice(), &viewport, &flycam.get_camera()); + viewport.present(); } } } Event::MainEventsCleared => { shader_watcher.watch(); - camera.update(); + flycam.update(); window.request_redraw(); } Event::DeviceEvent { ref event, .. } => match event { DeviceEvent::MouseMotion { delta } => { if is_grabbed { - camera.process_mouse(delta.0, delta.1); + flycam.process_mouse(delta.0, delta.1); } } _ => {} @@ -92,7 +147,7 @@ fn main() { is_grabbed = false; } } else { - camera.process_keyboard(*key, *state); + flycam.process_keyboard(*key, *state); } } WindowEvent::MouseInput { diff --git a/src/pass.rs b/src/pass.rs index 9bd357e..c6dff16 100644 --- a/src/pass.rs +++ b/src/pass.rs @@ -97,17 +97,23 @@ impl RenderPassBox { /// /// Calls to [RenderPassBoxTrait] functions with frame indices greater /// than or equal to `frame_num` are out-of-bounds and will panic. - pub fn new(render_pass: Arc, frame_num: usize) -> Box { + pub fn new(render_pass: Arc, frame_num: usize) -> Self { let frame_data = { (0..frame_num) .map(|_| render_pass.create_frame_data()) .collect() }; - Box::new(Self { + Self { render_pass, frame_data, - }) + } + } + + /// Same as [Self::new], but creates a boxed [RenderPassBoxTrait] in a + /// generic-friendly interface. + pub fn new_boxed(render_pass: Arc, frame_num: usize) -> Box { + Box::new(Self::new(render_pass, frame_num)) } } diff --git a/src/pass/debug.rs b/src/pass/debug.rs index 8e2ad07..114b6dd 100644 --- a/src/pass/debug.rs +++ b/src/pass/debug.rs @@ -2,28 +2,7 @@ use super::*; use crate::storage::GpuVec; use crate::viewport::ViewportInfo; use crate::RenderLayouts; - -#[repr(C)] -#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] -pub struct Vertex { - pub position: [f32; 3], - pub color: [f32; 3], -} - -const VERTEX_ATTRS: &[wgpu::VertexAttribute] = - &wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x3]; - -impl Vertex { - pub fn desc() -> wgpu::VertexBufferLayout<'static> { - wgpu::VertexBufferLayout { - array_stride: std::mem::size_of::() as wgpu::BufferAddress, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: VERTEX_ATTRS, - } - } -} - -pub type Index = u32; +use crate::scene::{DebugVertex as Vertex, DebugIndex as Index}; pub struct FrameData { vertices: GpuVec, diff --git a/src/pass/mesh.rs b/src/pass/mesh.rs index ccbb55f..72793b6 100644 --- a/src/pass/mesh.rs +++ b/src/pass/mesh.rs @@ -1,10 +1,12 @@ use super::*; +use crate::scene; use crate::shader::{ShaderHandle, ShaderStore}; use crate::storage::mesh::*; use crate::storage::GpuVec; use crate::viewport::ViewportInfo; use crate::RenderLayouts; +#[derive(Clone)] pub struct ShaderInfo { pub store: Arc, pub forward: ShaderHandle, @@ -66,11 +68,6 @@ struct MeshGroupCommands { meshes: Vec, } -pub struct MeshInstance { - pub transform: glam::Mat4, - pub mesh: MeshHandle, -} - pub struct FrameData { skinned_vertices: GpuVec, skinning_uniforms: GpuVec, @@ -86,13 +83,12 @@ pub struct MeshPass { vertex_attr_id: AttrId, index_attr_id: AttrId, mesh_layout_id: MeshLayoutId, - example_mesh: MeshHandle, skinning_bind_group_layout: wgpu::BindGroupLayout, skinning_pipeline: wgpu::ComputePipeline, depth_pipeline: wgpu::RenderPipeline, opaque_pipeline: wgpu::RenderPipeline, target_info: ViewportInfo, - instances: Vec, + meshes: scene::MeshesHandle, } impl MeshPass { @@ -119,54 +115,6 @@ impl MeshPass { mesh_layout.insert(index_attr_id, ()); let mesh_layout_id = mesh_pool.add_layout(mesh_layout).unwrap(); - let example_vertices = vec![ - Vertex { - position: [-0.5, 0.5, 0.0], - tan_frame: 0, - }, - Vertex { - position: [0.5, 0.5, 0.0], - tan_frame: 0, - }, - Vertex { - position: [0.0, -0.5, 0.0], - tan_frame: 0, - }, - ]; - - let example_vertices = AttrBuffer { - id: vertex_attr_id, - count: example_vertices.len(), - data: bytemuck::cast_slice(&example_vertices).to_vec(), - }; - - let example_indices = vec![0u32, 1u32, 2u32]; - let example_indices = AttrBuffer { - id: index_attr_id, - count: example_indices.len(), - data: bytemuck::cast_slice(&example_indices).to_vec(), - }; - - let mut example_mesh = MeshBuffer::default(); - example_mesh.attributes.push(example_vertices); - example_mesh.attributes.push(example_indices); - let example_mesh = mesh_pool.load(example_mesh).unwrap(); - - let mut instances = Vec::new(); - let r = 4; - for x in -r..r { - for y in -r..r { - for z in -r..r { - let translation = glam::Vec3::new(x as f32, y as f32, z as f32); - let transform = glam::Mat4::from_translation(translation); - instances.push(MeshInstance { - transform, - mesh: example_mesh.clone(), - }); - } - } - } - let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("MeshPass Pipeline Layout"), @@ -297,15 +245,30 @@ impl MeshPass { index_attr_id, vertex_attr_id, mesh_layout_id, - example_mesh, skinning_bind_group_layout, skinning_pipeline, depth_pipeline, opaque_pipeline, target_info, - instances, + meshes: Default::default(), } } + + pub fn get_mesh_pool(&self) -> &MeshPool { + &self.mesh_pool + } + + pub fn get_vertex_attr_id(&self) -> AttrId { + self.vertex_attr_id + } + + pub fn get_index_attr_id(&self) -> AttrId { + self.index_attr_id + } + + pub fn get_meshes(&self) -> &scene::MeshesHandle { + &self.meshes + } } impl RenderPass for MeshPass { @@ -343,9 +306,12 @@ impl RenderPass for MeshPass { data.groups.clear(); data.skinning_uniforms.clear(); + let meshes_read = self.meshes.read(); let mesh_bindings = self .mesh_pool - .iter_meshes(self.mesh_layout_id, self.instances.iter(), |i| &i.mesh) + .iter_meshes(self.mesh_layout_id, meshes_read.transformed.iter(), |i| { + &i.mesh + }) .unwrap(); let mut skinned_cursor = 0; @@ -468,8 +434,6 @@ impl RenderPass for MeshPass { } fn record_render(&self, data: PhaseData<&FrameData>) -> Option { - println!("MeshPass::record_render(phase: {:?})", data.phase); - let pipeline = match data.phase { Phase::Depth => &self.depth_pipeline, Phase::Opaque => &self.opaque_pipeline, @@ -491,7 +455,7 @@ impl RenderPass for MeshPass { }); // yikes - let mesh_bindings: Vec<(MeshLayoutBindings, &Vec)> = data + let mesh_bindings: Vec<_> = data .frame_data .groups .iter() diff --git a/src/scene.rs b/src/scene.rs new file mode 100644 index 0000000..d543c4c --- /dev/null +++ b/src/scene.rs @@ -0,0 +1,54 @@ +//! Traits for describing Cyborg's scene representation. +//! +//! TODO this will all need to be replaced in favor of a way to represent +//! querying component and resource data framework-agnostically + +use crate::storage::mesh::MeshHandle; +use parking_lot::RwLock; +use std::sync::Arc; + +pub struct TransformedMesh { + pub transform: glam::Mat4, + pub mesh: MeshHandle, +} + +#[derive(Default)] +pub struct Meshes { + pub transformed: Vec, +} + +pub type MeshesHandle = Arc>; + +#[repr(C)] +#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +pub struct DebugVertex { + pub position: [f32; 3], + pub color: [f32; 3], +} + +pub const DEBUG_VERTEX_ATTRS: &[wgpu::VertexAttribute] = + &wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x3]; + +impl DebugVertex { + pub fn desc() -> wgpu::VertexBufferLayout<'static> { + wgpu::VertexBufferLayout { + array_stride: std::mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: DEBUG_VERTEX_ATTRS, + } + } +} + +pub type DebugIndex = u32; + +pub struct DebugDraw { + pub vertices: Vec, + pub indices: Vec, +} + +#[derive(Default)] +pub struct DebugDraws { + pub draws: Vec, +} + +pub type DebugDrawsHandle = Arc>; diff --git a/src/viewport.rs b/src/viewport.rs index 879b167..e5941d8 100644 --- a/src/viewport.rs +++ b/src/viewport.rs @@ -14,12 +14,6 @@ pub struct ViewportViews<'a> { pub trait Viewport { fn get_info(&self) -> ViewportInfo; fn get_queue(&self) -> &wgpu::Queue; -} - -pub trait ViewportImage<'a> { - type Viewport: Viewport; - - fn get_viewport(&self) -> &Self::Viewport; fn get_views(&self) -> ViewportViews; } @@ -27,11 +21,13 @@ pub struct WinitViewport { pub device: Arc, pub queue: Arc, pub size: winit::dpi::PhysicalSize, - pub surface: wgpu::Surface, - pub config: wgpu::SurfaceConfiguration, - pub info: ViewportInfo, - pub depth_texture: wgpu::Texture, - pub depth_texture_view: wgpu::TextureView, + surface: wgpu::Surface, + config: wgpu::SurfaceConfiguration, + info: ViewportInfo, + depth_texture: wgpu::Texture, + depth_texture_view: wgpu::TextureView, + surface_texture: Option, + output_view: Option, } impl WinitViewport { @@ -88,22 +84,18 @@ impl WinitViewport { info, depth_texture, depth_texture_view, + surface_texture: None, + output_view: None, } } - pub fn acquire(&mut self) -> Result { + pub fn acquire(&mut self) -> Result<(), wgpu::SurfaceError> { let surface_texture = self.surface.get_current_texture()?; - let output_view = surface_texture + self.output_view = Some(surface_texture .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - let depth_view = &self.depth_texture_view; - - Ok(WinitImage { - viewport: self, - surface_texture, - output_view, - depth_view, - }) + .create_view(&wgpu::TextureViewDescriptor::default())); + self.surface_texture = Some(surface_texture); + Ok(()) } pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { @@ -119,6 +111,12 @@ impl WinitViewport { } } + pub fn present(&mut self) { + if let Some(surface_texture) = self.surface_texture.take() { + surface_texture.present(); + } + } + fn make_depth_texture( device: &wgpu::Device, config: &wgpu::SurfaceConfiguration, @@ -151,32 +149,12 @@ impl Viewport for WinitViewport { fn get_queue(&self) -> &wgpu::Queue { &self.queue } -} - -pub struct WinitImage<'a> { - viewport: &'a WinitViewport, - surface_texture: wgpu::SurfaceTexture, - output_view: wgpu::TextureView, - depth_view: &'a wgpu::TextureView, -} - -impl<'a> WinitImage<'a> { - pub fn present(self) { - self.surface_texture.present(); - } -} - -impl<'a> ViewportImage<'a> for WinitImage<'a> { - type Viewport = WinitViewport; - - fn get_viewport(&self) -> &WinitViewport { - self.viewport - } fn get_views(&self) -> ViewportViews { - ViewportViews { - output: &self.output_view, - depth: self.depth_view, + let output = self.output_view.as_ref().unwrap(); + ViewportViews { + output, + depth: &self.depth_texture_view } } }