From 88333dd8bd107c3c26a1e24d4c84ad1ecb5ff86f Mon Sep 17 00:00:00 2001 From: mars Date: Sun, 24 Apr 2022 20:44:51 -0600 Subject: [PATCH] Viewport trait + depth prepass --- src/lib.rs | 35 ++++----- src/main.rs | 81 ++++----------------- src/pass/debug.rs | 25 +++++-- src/pass/mesh.rs | 62 +++++++++++----- src/viewport.rs | 182 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 276 insertions(+), 109 deletions(-) create mode 100644 src/viewport.rs diff --git a/src/lib.rs b/src/lib.rs index cf0aeac..47ca92d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,10 +10,12 @@ pub mod mesh; pub mod pass; pub mod phase; pub mod staging; +pub mod viewport; use camera::Camera; use pass::*; use phase::*; +use viewport::ViewportImage; #[repr(C)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] @@ -119,8 +121,8 @@ impl FrameData { pub struct Renderer { device: Arc, + queue: Arc, layouts: Arc, - queue: wgpu::Queue, frames_in_flight: usize, frame_datas: Vec, frame_index: usize, @@ -128,7 +130,7 @@ pub struct Renderer { } impl Renderer { - pub fn new(device: Arc, queue: wgpu::Queue) -> Self { + pub fn new(device: Arc, queue: Arc) -> Self { let layouts = RenderLayouts::new_arc(device.clone()); let frames_in_flight = 1; @@ -171,12 +173,7 @@ impl Renderer { self.render_passes.push(pass); } - pub fn render( - &mut self, - camera: &impl Camera, - surface: &wgpu::Surface, - format: wgpu::TextureFormat, - ) -> Result<(), wgpu::SurfaceError> { + pub fn render<'a>(&mut self, target: &impl ViewportImage<'a>, camera: &impl Camera) { self.frame_index += 1; if self.frame_index >= self.frame_datas.len() { self.frame_index = 0; @@ -212,10 +209,6 @@ impl Renderer { let phase_passes = phase_passes.into_inner().unwrap(); let viewport = ViewportData; - let output = surface.get_current_texture()?; - let view = output - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); let mut encoder = self .device .create_command_encoder(&wgpu::CommandEncoderDescriptor { @@ -247,14 +240,17 @@ impl Renderer { }; // TODO: parallelize each phase's record_render + let depth_cmds = record_render(Phase::Depth); let opaque_cmds = record_render(Phase::Opaque); let overlay_cmds = record_render(Phase::Overlay); { + let target_views = target.get_views(); + let mut rp = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("Render Pass"), color_attachments: &[wgpu::RenderPassColorAttachment { - view: &view, + view: target_views.output, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color { @@ -266,16 +262,21 @@ impl Renderer { store: true, }, }], - depth_stencil_attachment: None, + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: target_views.depth, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Clear(1.0), + store: true, + }), + stencil_ops: None, + }), }); + rp.execute_bundles(depth_cmds.iter()); rp.execute_bundles(opaque_cmds.iter()); rp.execute_bundles(overlay_cmds.iter()); } self.queue.submit(std::iter::once(encoder.finish())); - output.present(); - - Ok(()) } } diff --git a/src/main.rs b/src/main.rs index eeb1010..f17192b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use cyborg::{pass, Renderer}; +use cyborg::{pass, viewport::*, Renderer}; use std::sync::Arc; use winit::{ event::*, @@ -6,71 +6,11 @@ use winit::{ window::WindowBuilder, }; -struct SurfaceViewport { - device: Arc, - size: winit::dpi::PhysicalSize, - surface: wgpu::Surface, - config: wgpu::SurfaceConfiguration, -} - -impl SurfaceViewport { - fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { - if new_size.width > 0 && new_size.height > 0 { - self.size = new_size; - self.config.width = new_size.width; - self.config.height = new_size.height; - self.surface.configure(&self.device, &self.config); - } - } -} - -async fn make_window_renderer(window: &winit::window::Window) -> (Renderer, SurfaceViewport) { - let size = window.inner_size(); - let instance = wgpu::Instance::new(wgpu::Backends::all()); - let surface = unsafe { instance.create_surface(window) }; - let adapter = instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::LowPower, - compatible_surface: Some(&surface), - force_fallback_adapter: false, - }) - .await - .unwrap(); - let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - features: wgpu::Features::empty(), - limits: wgpu::Limits::default(), - label: None, - }, - None, - ) - .await - .unwrap(); - let config = wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: surface.get_preferred_format(&adapter).unwrap(), - width: size.width, - height: size.height, - present_mode: wgpu::PresentMode::Fifo, - }; - surface.configure(&device, &config); - - let device = Arc::new(device); - let renderer = Renderer::new(device.clone(), queue); - let viewport = SurfaceViewport { - device, - size, - surface, - config, - }; - (renderer, viewport) -} - fn main() { let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); - let (mut renderer, mut viewport) = pollster::block_on(make_window_renderer(&window)); + 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 is_grabbed = false; @@ -78,8 +18,10 @@ fn main() { let device = renderer.get_device(); let layouts = renderer.get_layouts(); - let mesh_pass = pass::mesh::MeshPass::new(device.to_owned(), layouts.to_owned(), viewport.config.format); - let debug_pass = pass::debug::DebugPass::new(device.to_owned(), layouts.to_owned(), viewport.config.format); + let mesh_pass = + pass::mesh::MeshPass::new(device.to_owned(), layouts.to_owned(), viewport.get_info()); + 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); @@ -87,12 +29,15 @@ fn main() { event_loop.run(move |event, _, control_flow| match event { Event::RedrawRequested(_) => { println!("camera: {:#?}", camera); - match renderer.render(&camera, &viewport.surface, viewport.config.format) { - Ok(_) => {} + 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(); + } + } } Event::MainEventsCleared => { camera.update(); diff --git a/src/pass/debug.rs b/src/pass/debug.rs index fc5b037..8605319 100644 --- a/src/pass/debug.rs +++ b/src/pass/debug.rs @@ -1,4 +1,5 @@ use super::*; +use crate::viewport::ViewportInfo; use crate::RenderLayouts; #[repr(C)] @@ -34,14 +35,14 @@ pub struct FrameData { pub struct DebugPass { device: Arc, pipeline: wgpu::RenderPipeline, - target_format: wgpu::TextureFormat, + target_info: ViewportInfo, } impl DebugPass { pub fn new( device: Arc, layouts: Arc, - target_format: wgpu::TextureFormat, + target_info: ViewportInfo, ) -> Self { let shader = device.create_shader_module(&wgpu::include_wgsl!("mesh_shader.wgsl")); @@ -63,7 +64,7 @@ impl DebugPass { module: &shader, entry_point: "fs_main", targets: &[wgpu::ColorTargetState { - format: target_format, + format: target_info.output_format, blend: Some(wgpu::BlendState::REPLACE), write_mask: wgpu::ColorWrites::ALL, }], @@ -77,7 +78,13 @@ impl DebugPass { unclipped_depth: false, conservative: false, }, - depth_stencil: None, + depth_stencil: Some(wgpu::DepthStencilState { + format: target_info.depth_format, + depth_write_enabled: false, + depth_compare: wgpu::CompareFunction::Less, + stencil: Default::default(), + bias: Default::default(), + }), multisample: wgpu::MultisampleState { count: 1, mask: !0, @@ -89,7 +96,7 @@ impl DebugPass { Self { device, pipeline, - target_format, + target_info, } } } @@ -153,8 +160,12 @@ impl RenderPass for DebugPass { self.device .create_render_bundle_encoder(&wgpu::RenderBundleEncoderDescriptor { label: Some("DebugPass Render Bundle"), - color_formats: &[self.target_format], - depth_stencil: None, + color_formats: &[self.target_info.output_format], + depth_stencil: Some(wgpu::RenderBundleDepthStencil { + format: self.target_info.depth_format, + depth_read_only: false, // TODO optimize? + stencil_read_only: true, + }), sample_count: 1, multiview: None, }); diff --git a/src/pass/mesh.rs b/src/pass/mesh.rs index a0ddb0c..c3b53af 100644 --- a/src/pass/mesh.rs +++ b/src/pass/mesh.rs @@ -1,5 +1,6 @@ use super::*; use crate::mesh::*; +use crate::viewport::ViewportInfo; use crate::RenderLayouts; #[repr(C)] @@ -41,15 +42,16 @@ pub struct MeshPass { index_attr_id: AttrId, mesh_layout_id: MeshLayoutId, example_mesh: MeshHandle, + depth_pipeline: wgpu::RenderPipeline, opaque_pipeline: wgpu::RenderPipeline, - target_format: wgpu::TextureFormat, + target_info: ViewportInfo, } impl MeshPass { pub fn new( device: Arc, layouts: Arc, - target_format: wgpu::TextureFormat, + target_info: ViewportInfo, ) -> Self { let attr_store = AttrStore::new(); let mesh_pool = MeshPool::new(device.clone(), attr_store.to_owned()); @@ -110,7 +112,13 @@ impl MeshPass { let shader = device.create_shader_module(&wgpu::include_wgsl!("mesh_shader.wgsl")); - let opaque_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + let targets = &[wgpu::ColorTargetState { + format: target_info.output_format, + blend: Some(wgpu::BlendState::REPLACE), + write_mask: wgpu::ColorWrites::ALL, + }]; + + let mut pipeline_desc = wgpu::RenderPipelineDescriptor { label: Some("Opaque MeshPass Pipeline"), layout: Some(&render_pipeline_layout), vertex: wgpu::VertexState { @@ -121,11 +129,7 @@ impl MeshPass { fragment: Some(wgpu::FragmentState { module: &shader, entry_point: "fs_main", - targets: &[wgpu::ColorTargetState { - format: target_format, - blend: Some(wgpu::BlendState::REPLACE), - write_mask: wgpu::ColorWrites::ALL, - }], + targets, }), primitive: wgpu::PrimitiveState { topology: wgpu::PrimitiveTopology::TriangleList, @@ -136,15 +140,33 @@ impl MeshPass { unclipped_depth: false, conservative: false, }, - depth_stencil: None, + depth_stencil: Some(wgpu::DepthStencilState { + format: target_info.depth_format, + depth_write_enabled: true, + depth_compare: wgpu::CompareFunction::Less, + stencil: Default::default(), + bias: Default::default(), + }), multisample: wgpu::MultisampleState { count: 1, mask: !0, alpha_to_coverage_enabled: false, }, multiview: None, + }; + + let depth_pipeline = device.create_render_pipeline(&pipeline_desc); + + pipeline_desc.depth_stencil = Some(wgpu::DepthStencilState { + format: target_info.depth_format, + depth_write_enabled: false, + depth_compare: wgpu::CompareFunction::Equal, + stencil: Default::default(), + bias: Default::default(), }); + let opaque_pipeline = device.create_render_pipeline(&pipeline_desc); + Self { device, layouts, @@ -154,8 +176,9 @@ impl MeshPass { vertex_attr_id, mesh_layout_id, example_mesh, + depth_pipeline, opaque_pipeline, - target_format, + target_info, } } } @@ -186,21 +209,26 @@ 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, + _ => return None, + }; + let mut cmds = self.device .create_render_bundle_encoder(&wgpu::RenderBundleEncoderDescriptor { label: Some("Opaque Pass Render Bundle"), - color_formats: &[self.target_format], - depth_stencil: None, + color_formats: &[self.target_info.output_format], + depth_stencil: Some(wgpu::RenderBundleDepthStencil { + format: self.target_info.depth_format, + depth_read_only: false, // TODO optimize? + stencil_read_only: true, + }), sample_count: 1, multiview: None, }); - let pipeline = match data.phase { - Phase::Opaque => &self.opaque_pipeline, - _ => return None, - }; - let meshes = &[&self.example_mesh, &self.example_mesh]; // yikes diff --git a/src/viewport.rs b/src/viewport.rs new file mode 100644 index 0000000..73e0e11 --- /dev/null +++ b/src/viewport.rs @@ -0,0 +1,182 @@ +use std::sync::Arc; + +#[derive(Clone)] +pub struct ViewportInfo { + pub output_format: wgpu::TextureFormat, + pub depth_format: wgpu::TextureFormat, +} + +pub struct ViewportViews<'a> { + pub output: &'a wgpu::TextureView, + pub depth: &'a wgpu::TextureView, +} + +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; +} + +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, +} + +impl WinitViewport { + pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; + + pub async fn from_window(window: &winit::window::Window) -> Self { + let size = window.inner_size(); + let instance = wgpu::Instance::new(wgpu::Backends::all()); + let surface = unsafe { instance.create_surface(window) }; + let adapter = instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::LowPower, + compatible_surface: Some(&surface), + force_fallback_adapter: false, + }) + .await + .unwrap(); + let (device, queue) = adapter + .request_device( + &wgpu::DeviceDescriptor { + features: wgpu::Features::empty(), + limits: wgpu::Limits::default(), + label: None, + }, + None, + ) + .await + .unwrap(); + let config = wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + format: surface.get_preferred_format(&adapter).unwrap(), + width: size.width, + height: size.height, + present_mode: wgpu::PresentMode::Fifo, + }; + surface.configure(&device, &config); + + let device = Arc::new(device); + let queue = Arc::new(queue); + + let info = ViewportInfo { + output_format: config.format, + depth_format: Self::DEPTH_FORMAT, + }; + + let (depth_texture, depth_texture_view) = Self::make_depth_texture(&device, &config); + + Self { + device, + queue, + size, + surface, + config, + info, + depth_texture, + depth_texture_view, + } + } + + pub fn acquire(&mut self) -> Result { + let surface_texture = self.surface.get_current_texture()?; + let output_view = 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, + }) + } + + pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { + if new_size.width > 0 && new_size.height > 0 { + self.size = new_size; + self.config.width = new_size.width; + self.config.height = new_size.height; + self.surface.configure(&self.device, &self.config); + let (depth_texture, depth_texture_view) = + Self::make_depth_texture(&self.device, &self.config); + self.depth_texture = depth_texture; + self.depth_texture_view = depth_texture_view; + } + } + + fn make_depth_texture( + device: &wgpu::Device, + config: &wgpu::SurfaceConfiguration, + ) -> (wgpu::Texture, wgpu::TextureView) { + let size = wgpu::Extent3d { + width: config.width, + height: config.height, + depth_or_array_layers: 1, + }; + let desc = wgpu::TextureDescriptor { + label: Some("Depth Texture"), + size, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: Self::DEPTH_FORMAT, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, + }; + let texture = device.create_texture(&desc); + let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); + (texture, view) + } +} + +impl Viewport for WinitViewport { + fn get_info(&self) -> ViewportInfo { + self.info.clone() + } + + 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, + } + } +}