From cb2b27ba03b6c5ff6aa0104e8e5475ef4f72755c Mon Sep 17 00:00:00 2001 From: Nathan Adams Date: Wed, 21 Dec 2022 01:42:20 +0100 Subject: [PATCH] wgpu: Don't create depth buffers, or use any depth testing, if we aren't expecting masks --- render/wgpu/src/commands.rs | 118 +++++++++++++++++++++++++---------- render/wgpu/src/pipelines.rs | 111 +++++++++++++++++++------------- 2 files changed, 154 insertions(+), 75 deletions(-) diff --git a/render/wgpu/src/commands.rs b/render/wgpu/src/commands.rs index b0e1b1a83..e73abe4f3 100644 --- a/render/wgpu/src/commands.rs +++ b/render/wgpu/src/commands.rs @@ -17,7 +17,7 @@ pub struct CommandTarget<'pass> { frame_buffer: FrameBuffer, blend_buffer: OnceCell, resolve_buffer: Option, - depth: DepthBuffer, + depth: OnceCell, globals: &'pass Globals, size: wgpu::Extent3d, format: wgpu::TextureFormat, @@ -97,18 +97,11 @@ impl<'pass> CommandTarget<'pass> { None }; - let depth = DepthBuffer::new( - &descriptors.device, - create_debug_label!("Depth buffer"), - sample_count, - size, - ); - Self { frame_buffer, blend_buffer: OnceCell::new(), resolve_buffer, - depth, + depth: OnceCell::new(), globals, size, format, @@ -160,9 +153,21 @@ impl<'pass> CommandTarget<'pass> { }) } - pub fn depth_attachment(&self, clear: bool) -> Option { + pub fn depth_attachment( + &self, + descriptors: &Descriptors, + clear: bool, + ) -> Option { + let depth = self.depth.get_or_init(|| { + DepthBuffer::new( + &descriptors.device, + create_debug_label!("Depth buffer"), + self.sample_count, + self.size, + ) + }); Some(wgpu::RenderPassDepthStencilAttachment { - view: self.depth.view(), + view: depth.view(), depth_ops: Some(wgpu::Operations { load: if clear { wgpu::LoadOp::Clear(0.0) @@ -231,6 +236,7 @@ pub struct CommandRenderer<'pass, 'frame: 'pass, 'global: 'frame> { render_pass: wgpu::RenderPass<'pass>, uniform_buffers: &'frame mut UniformBuffer<'global, Transforms>, uniform_encoder: &'frame mut wgpu::CommandEncoder, + needs_depth: bool, } impl<'pass, 'frame: 'pass, 'global: 'frame> CommandRenderer<'pass, 'frame, 'global> { @@ -244,6 +250,7 @@ impl<'pass, 'frame: 'pass, 'global: 'frame> CommandRenderer<'pass, 'frame, 'glob render_pass: wgpu::RenderPass<'pass>, num_masks: u32, mask_state: MaskState, + needs_depth: bool, ) -> Self { Self { pipelines, @@ -254,6 +261,7 @@ impl<'pass, 'frame: 'pass, 'global: 'frame> CommandRenderer<'pass, 'frame, 'glob descriptors, uniform_buffers, uniform_encoder, + needs_depth, } } @@ -284,12 +292,16 @@ impl<'pass, 'frame: 'pass, 'global: 'frame> CommandRenderer<'pass, 'frame, 'glob for chunk in chunk_blends(commands.0) { match chunk { - Chunk::Draw(chunk) => { + Chunk::Draw(chunk, needs_depth) => { let mut render_pass = draw_encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: None, color_attachments: &[target.color_attachments(clear_color.take())], - depth_stencil_attachment: target.depth_attachment(first && clear_depth), + depth_stencil_attachment: if needs_depth { + target.depth_attachment(&descriptors, first && clear_depth) + } else { + None + }, }); render_pass.set_bind_group(0, target.globals.bind_group(), &[]); let mut renderer = CommandRenderer::new( @@ -301,6 +313,7 @@ impl<'pass, 'frame: 'pass, 'global: 'frame> CommandRenderer<'pass, 'frame, 'glob render_pass, num_masks, mask_state, + needs_depth, ); for command in &chunk { @@ -348,7 +361,7 @@ impl<'pass, 'frame: 'pass, 'global: 'frame> CommandRenderer<'pass, 'frame, 'glob num_masks = renderer.num_masks; mask_state = renderer.mask_state; } - Chunk::Blend(commands, blend_mode) => { + Chunk::Blend(commands, blend_mode, needs_depth) => { let parent = match blend_mode { BlendMode::Alpha | BlendMode::Erase => nearest_layer, _ => target, @@ -434,7 +447,11 @@ impl<'pass, 'frame: 'pass, 'global: 'frame> CommandRenderer<'pass, 'frame, 'glob color_attachments: &[ target.color_attachments(clear_color.take()) ], - depth_stencil_attachment: target.depth_attachment(false), + depth_stencil_attachment: if needs_depth { + target.depth_attachment(descriptors, false) + } else { + None + }, }); render_pass.set_bind_group(0, target.globals.bind_group(), &[]); @@ -451,8 +468,13 @@ impl<'pass, 'frame: 'pass, 'global: 'frame> CommandRenderer<'pass, 'frame, 'glob } } - render_pass - .set_pipeline(pipelines.bitmap[blend].pipeline_for(mask_state)); + if needs_depth { + render_pass + .set_pipeline(pipelines.bitmap[blend].pipeline_for(mask_state)); + } else { + render_pass + .set_pipeline(pipelines.bitmap[blend].depthless_pipeline()); + } render_pass.set_bind_group(1, target.whole_frame_bind_group(), &[0]); render_pass.set_bind_group(2, &bitmap_bind_group, &[]); @@ -512,7 +534,11 @@ impl<'pass, 'frame: 'pass, 'global: 'frame> CommandRenderer<'pass, 'frame, 'glob color_attachments: &[ target.color_attachments(clear_color.take()) ], - depth_stencil_attachment: target.depth_attachment(false), + depth_stencil_attachment: if needs_depth { + target.depth_attachment(descriptors, false) + } else { + None + }, }); render_pass.set_bind_group(0, target.globals.bind_group(), &[]); @@ -529,8 +555,13 @@ impl<'pass, 'frame: 'pass, 'global: 'frame> CommandRenderer<'pass, 'frame, 'glob } } - render_pass - .set_pipeline(pipelines.complex_blend.pipeline_for(mask_state)); + if needs_depth { + render_pass + .set_pipeline(pipelines.complex_blend.pipeline_for(mask_state)); + } else { + render_pass + .set_pipeline(pipelines.complex_blend.depthless_pipeline()); + } render_pass.set_bind_group(1, target.whole_frame_bind_group(), &[0]); render_pass.set_bind_group(2, &blend_bind_group, &[]); @@ -554,21 +585,36 @@ impl<'pass, 'frame: 'pass, 'global: 'frame> CommandRenderer<'pass, 'frame, 'glob } pub fn prep_color(&mut self) { - self.render_pass - .set_pipeline(self.pipelines.color.pipeline_for(self.mask_state)); + if self.needs_depth { + self.render_pass + .set_pipeline(self.pipelines.color.pipeline_for(self.mask_state)); + } else { + self.render_pass + .set_pipeline(self.pipelines.color.depthless_pipeline()); + } } pub fn prep_gradient(&mut self, bind_group: &'pass wgpu::BindGroup) { - self.render_pass - .set_pipeline(self.pipelines.gradient.pipeline_for(self.mask_state)); + if self.needs_depth { + self.render_pass + .set_pipeline(self.pipelines.gradient.pipeline_for(self.mask_state)); + } else { + self.render_pass + .set_pipeline(self.pipelines.gradient.depthless_pipeline()); + } self.render_pass.set_bind_group(2, bind_group, &[]); } pub fn prep_bitmap(&mut self, bind_group: &'pass wgpu::BindGroup) { - self.render_pass.set_pipeline( - self.pipelines.bitmap[TrivialBlend::Normal].pipeline_for(self.mask_state), - ); + if self.needs_depth { + self.render_pass.set_pipeline( + self.pipelines.bitmap[TrivialBlend::Normal].pipeline_for(self.mask_state), + ); + } else { + self.render_pass + .set_pipeline(self.pipelines.bitmap[TrivialBlend::Normal].depthless_pipeline()); + } self.render_pass.set_bind_group(2, bind_group, &[]); } @@ -749,22 +795,30 @@ impl<'pass, 'frame: 'pass, 'global: 'frame> CommandRenderer<'pass, 'frame, 'glob #[derive(Debug)] pub enum Chunk { - Draw(Vec), - Blend(CommandList, BlendMode), + Draw(Vec, bool), + Blend(CommandList, BlendMode, bool), } /// Chunk the commands such that every Blend is separated out fn chunk_blends(commands: Vec) -> Vec { let mut result = vec![]; let mut current = vec![]; + let mut needs_depth = false; for command in commands { match command { Command::Blend(commands, blend_mode) => { if !current.is_empty() { - result.push(Chunk::Draw(std::mem::take(&mut current))); + result.push(Chunk::Draw(std::mem::take(&mut current), needs_depth)); } - result.push(Chunk::Blend(commands, blend_mode)); + result.push(Chunk::Blend(commands, blend_mode, needs_depth)); + } + Command::PushMask + | Command::ActivateMask + | Command::PopMask + | Command::DeactivateMask => { + needs_depth = true; + current.push(command); } _ => { current.push(command); @@ -773,7 +827,7 @@ fn chunk_blends(commands: Vec) -> Vec { } if !current.is_empty() { - result.push(Chunk::Draw(current)); + result.push(Chunk::Draw(current, needs_depth)); } result diff --git a/render/wgpu/src/pipelines.rs b/render/wgpu/src/pipelines.rs index 153def1a3..b8534afdb 100644 --- a/render/wgpu/src/pipelines.rs +++ b/render/wgpu/src/pipelines.rs @@ -17,6 +17,7 @@ pub const VERTEX_BUFFERS_DESCRIPTION: [wgpu::VertexBufferLayout; 1] = [wgpu::Ver #[derive(Debug)] pub struct ShapePipeline { pub pipelines: EnumMap, + depthless: wgpu::RenderPipeline, } #[derive(Enum, Debug)] @@ -99,10 +100,17 @@ impl ShapePipeline { &self.pipelines[mask_state] } + pub fn depthless_pipeline(&self) -> &wgpu::RenderPipeline { + &self.depthless + } + /// Builds of a nested `EnumMap` that maps a `MaskState` to /// a `RenderPipeline`. The provided callback is used to construct the `RenderPipeline` /// for each possible `MaskState`. - fn build(mut f: impl FnMut(MaskState) -> wgpu::RenderPipeline) -> Self { + fn build( + depthless: wgpu::RenderPipeline, + mut f: impl FnMut(MaskState) -> wgpu::RenderPipeline, + ) -> Self { let mask_array: [wgpu::RenderPipeline; MaskState::LENGTH] = (0..MaskState::LENGTH) .map(|mask_enum| { let mask_state = MaskState::from_usize(mask_enum); @@ -113,6 +121,7 @@ impl ShapePipeline { .unwrap(); ShapePipeline { pipelines: EnumMap::from_array(mask_array), + depthless, } } } @@ -289,46 +298,62 @@ fn create_shape_pipeline( )) }; - ShapePipeline::build(|mask_state| match mask_state { - MaskState::NoMask => mask_render_state( - "no mask", - wgpu::StencilFaceState { - compare: wgpu::CompareFunction::Always, - fail_op: wgpu::StencilOperation::Keep, - depth_fail_op: wgpu::StencilOperation::Keep, - pass_op: wgpu::StencilOperation::Keep, - }, - wgpu::ColorWrites::ALL, - ), - MaskState::DrawMaskStencil => mask_render_state( - "draw mask stencil", - wgpu::StencilFaceState { - compare: wgpu::CompareFunction::Equal, - fail_op: wgpu::StencilOperation::Keep, - depth_fail_op: wgpu::StencilOperation::Keep, - pass_op: wgpu::StencilOperation::IncrementClamp, - }, - wgpu::ColorWrites::empty(), - ), - MaskState::DrawMaskedContent => mask_render_state( - "draw masked content", - wgpu::StencilFaceState { - compare: wgpu::CompareFunction::Equal, - fail_op: wgpu::StencilOperation::Keep, - depth_fail_op: wgpu::StencilOperation::Keep, - pass_op: wgpu::StencilOperation::Keep, - }, - wgpu::ColorWrites::ALL, - ), - MaskState::ClearMaskStencil => mask_render_state( - "clear mask stencil", - wgpu::StencilFaceState { - compare: wgpu::CompareFunction::Equal, - fail_op: wgpu::StencilOperation::Keep, - depth_fail_op: wgpu::StencilOperation::Keep, - pass_op: wgpu::StencilOperation::DecrementClamp, - }, - wgpu::ColorWrites::empty(), - ), - }) + ShapePipeline::build( + device.create_render_pipeline(&create_pipeline_descriptor( + create_debug_label!("{} depthless pipeline", name).as_deref(), + shader, + shader, + &pipeline_layout, + None, + &[Some(wgpu::ColorTargetState { + format, + blend: Some(blend), + write_mask: wgpu::ColorWrites::ALL, + })], + vertex_buffers_layout, + msaa_sample_count, + )), + |mask_state| match mask_state { + MaskState::NoMask => mask_render_state( + "no mask", + wgpu::StencilFaceState { + compare: wgpu::CompareFunction::Always, + fail_op: wgpu::StencilOperation::Keep, + depth_fail_op: wgpu::StencilOperation::Keep, + pass_op: wgpu::StencilOperation::Keep, + }, + wgpu::ColorWrites::ALL, + ), + MaskState::DrawMaskStencil => mask_render_state( + "draw mask stencil", + wgpu::StencilFaceState { + compare: wgpu::CompareFunction::Equal, + fail_op: wgpu::StencilOperation::Keep, + depth_fail_op: wgpu::StencilOperation::Keep, + pass_op: wgpu::StencilOperation::IncrementClamp, + }, + wgpu::ColorWrites::empty(), + ), + MaskState::DrawMaskedContent => mask_render_state( + "draw masked content", + wgpu::StencilFaceState { + compare: wgpu::CompareFunction::Equal, + fail_op: wgpu::StencilOperation::Keep, + depth_fail_op: wgpu::StencilOperation::Keep, + pass_op: wgpu::StencilOperation::Keep, + }, + wgpu::ColorWrites::ALL, + ), + MaskState::ClearMaskStencil => mask_render_state( + "clear mask stencil", + wgpu::StencilFaceState { + compare: wgpu::CompareFunction::Equal, + fail_op: wgpu::StencilOperation::Keep, + depth_fail_op: wgpu::StencilOperation::Keep, + pass_op: wgpu::StencilOperation::DecrementClamp, + }, + wgpu::ColorWrites::empty(), + ), + }, + ) }