wgpu: Don't create depth buffers, or use any depth testing, if we aren't expecting masks

This commit is contained in:
Nathan Adams 2022-12-21 01:42:20 +01:00
parent 05f49dd487
commit cb2b27ba03
2 changed files with 154 additions and 75 deletions

View File

@ -17,7 +17,7 @@ pub struct CommandTarget<'pass> {
frame_buffer: FrameBuffer, frame_buffer: FrameBuffer,
blend_buffer: OnceCell<BlendBuffer>, blend_buffer: OnceCell<BlendBuffer>,
resolve_buffer: Option<ResolveBuffer>, resolve_buffer: Option<ResolveBuffer>,
depth: DepthBuffer, depth: OnceCell<DepthBuffer>,
globals: &'pass Globals, globals: &'pass Globals,
size: wgpu::Extent3d, size: wgpu::Extent3d,
format: wgpu::TextureFormat, format: wgpu::TextureFormat,
@ -97,18 +97,11 @@ impl<'pass> CommandTarget<'pass> {
None None
}; };
let depth = DepthBuffer::new(
&descriptors.device,
create_debug_label!("Depth buffer"),
sample_count,
size,
);
Self { Self {
frame_buffer, frame_buffer,
blend_buffer: OnceCell::new(), blend_buffer: OnceCell::new(),
resolve_buffer, resolve_buffer,
depth, depth: OnceCell::new(),
globals, globals,
size, size,
format, format,
@ -160,9 +153,21 @@ impl<'pass> CommandTarget<'pass> {
}) })
} }
pub fn depth_attachment(&self, clear: bool) -> Option<wgpu::RenderPassDepthStencilAttachment> { pub fn depth_attachment(
&self,
descriptors: &Descriptors,
clear: bool,
) -> Option<wgpu::RenderPassDepthStencilAttachment> {
let depth = self.depth.get_or_init(|| {
DepthBuffer::new(
&descriptors.device,
create_debug_label!("Depth buffer"),
self.sample_count,
self.size,
)
});
Some(wgpu::RenderPassDepthStencilAttachment { Some(wgpu::RenderPassDepthStencilAttachment {
view: self.depth.view(), view: depth.view(),
depth_ops: Some(wgpu::Operations { depth_ops: Some(wgpu::Operations {
load: if clear { load: if clear {
wgpu::LoadOp::Clear(0.0) wgpu::LoadOp::Clear(0.0)
@ -231,6 +236,7 @@ pub struct CommandRenderer<'pass, 'frame: 'pass, 'global: 'frame> {
render_pass: wgpu::RenderPass<'pass>, render_pass: wgpu::RenderPass<'pass>,
uniform_buffers: &'frame mut UniformBuffer<'global, Transforms>, uniform_buffers: &'frame mut UniformBuffer<'global, Transforms>,
uniform_encoder: &'frame mut wgpu::CommandEncoder, uniform_encoder: &'frame mut wgpu::CommandEncoder,
needs_depth: bool,
} }
impl<'pass, 'frame: 'pass, 'global: 'frame> CommandRenderer<'pass, 'frame, 'global> { 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>, render_pass: wgpu::RenderPass<'pass>,
num_masks: u32, num_masks: u32,
mask_state: MaskState, mask_state: MaskState,
needs_depth: bool,
) -> Self { ) -> Self {
Self { Self {
pipelines, pipelines,
@ -254,6 +261,7 @@ impl<'pass, 'frame: 'pass, 'global: 'frame> CommandRenderer<'pass, 'frame, 'glob
descriptors, descriptors,
uniform_buffers, uniform_buffers,
uniform_encoder, 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) { for chunk in chunk_blends(commands.0) {
match chunk { match chunk {
Chunk::Draw(chunk) => { Chunk::Draw(chunk, needs_depth) => {
let mut render_pass = let mut render_pass =
draw_encoder.begin_render_pass(&wgpu::RenderPassDescriptor { draw_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None, label: None,
color_attachments: &[target.color_attachments(clear_color.take())], 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(), &[]); render_pass.set_bind_group(0, target.globals.bind_group(), &[]);
let mut renderer = CommandRenderer::new( let mut renderer = CommandRenderer::new(
@ -301,6 +313,7 @@ impl<'pass, 'frame: 'pass, 'global: 'frame> CommandRenderer<'pass, 'frame, 'glob
render_pass, render_pass,
num_masks, num_masks,
mask_state, mask_state,
needs_depth,
); );
for command in &chunk { for command in &chunk {
@ -348,7 +361,7 @@ impl<'pass, 'frame: 'pass, 'global: 'frame> CommandRenderer<'pass, 'frame, 'glob
num_masks = renderer.num_masks; num_masks = renderer.num_masks;
mask_state = renderer.mask_state; mask_state = renderer.mask_state;
} }
Chunk::Blend(commands, blend_mode) => { Chunk::Blend(commands, blend_mode, needs_depth) => {
let parent = match blend_mode { let parent = match blend_mode {
BlendMode::Alpha | BlendMode::Erase => nearest_layer, BlendMode::Alpha | BlendMode::Erase => nearest_layer,
_ => target, _ => target,
@ -434,7 +447,11 @@ impl<'pass, 'frame: 'pass, 'global: 'frame> CommandRenderer<'pass, 'frame, 'glob
color_attachments: &[ color_attachments: &[
target.color_attachments(clear_color.take()) 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(), &[]); 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 if needs_depth {
.set_pipeline(pipelines.bitmap[blend].pipeline_for(mask_state)); 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(1, target.whole_frame_bind_group(), &[0]);
render_pass.set_bind_group(2, &bitmap_bind_group, &[]); 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: &[ color_attachments: &[
target.color_attachments(clear_color.take()) 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(), &[]); 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 if needs_depth {
.set_pipeline(pipelines.complex_blend.pipeline_for(mask_state)); 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(1, target.whole_frame_bind_group(), &[0]);
render_pass.set_bind_group(2, &blend_bind_group, &[]); 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) { pub fn prep_color(&mut self) {
self.render_pass if self.needs_depth {
.set_pipeline(self.pipelines.color.pipeline_for(self.mask_state)); 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) { pub fn prep_gradient(&mut self, bind_group: &'pass wgpu::BindGroup) {
self.render_pass if self.needs_depth {
.set_pipeline(self.pipelines.gradient.pipeline_for(self.mask_state)); 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, &[]); self.render_pass.set_bind_group(2, bind_group, &[]);
} }
pub fn prep_bitmap(&mut self, bind_group: &'pass wgpu::BindGroup) { pub fn prep_bitmap(&mut self, bind_group: &'pass wgpu::BindGroup) {
self.render_pass.set_pipeline( if self.needs_depth {
self.pipelines.bitmap[TrivialBlend::Normal].pipeline_for(self.mask_state), 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, &[]); 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)] #[derive(Debug)]
pub enum Chunk { pub enum Chunk {
Draw(Vec<Command>), Draw(Vec<Command>, bool),
Blend(CommandList, BlendMode), Blend(CommandList, BlendMode, bool),
} }
/// Chunk the commands such that every Blend is separated out /// Chunk the commands such that every Blend is separated out
fn chunk_blends(commands: Vec<Command>) -> Vec<Chunk> { fn chunk_blends(commands: Vec<Command>) -> Vec<Chunk> {
let mut result = vec![]; let mut result = vec![];
let mut current = vec![]; let mut current = vec![];
let mut needs_depth = false;
for command in commands { for command in commands {
match command { match command {
Command::Blend(commands, blend_mode) => { Command::Blend(commands, blend_mode) => {
if !current.is_empty() { 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); current.push(command);
@ -773,7 +827,7 @@ fn chunk_blends(commands: Vec<Command>) -> Vec<Chunk> {
} }
if !current.is_empty() { if !current.is_empty() {
result.push(Chunk::Draw(current)); result.push(Chunk::Draw(current, needs_depth));
} }
result result

View File

@ -17,6 +17,7 @@ pub const VERTEX_BUFFERS_DESCRIPTION: [wgpu::VertexBufferLayout; 1] = [wgpu::Ver
#[derive(Debug)] #[derive(Debug)]
pub struct ShapePipeline { pub struct ShapePipeline {
pub pipelines: EnumMap<MaskState, wgpu::RenderPipeline>, pub pipelines: EnumMap<MaskState, wgpu::RenderPipeline>,
depthless: wgpu::RenderPipeline,
} }
#[derive(Enum, Debug)] #[derive(Enum, Debug)]
@ -99,10 +100,17 @@ impl ShapePipeline {
&self.pipelines[mask_state] &self.pipelines[mask_state]
} }
pub fn depthless_pipeline(&self) -> &wgpu::RenderPipeline {
&self.depthless
}
/// Builds of a nested `EnumMap` that maps a `MaskState` to /// Builds of a nested `EnumMap` that maps a `MaskState` to
/// a `RenderPipeline`. The provided callback is used to construct the `RenderPipeline` /// a `RenderPipeline`. The provided callback is used to construct the `RenderPipeline`
/// for each possible `MaskState`. /// 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) let mask_array: [wgpu::RenderPipeline; MaskState::LENGTH] = (0..MaskState::LENGTH)
.map(|mask_enum| { .map(|mask_enum| {
let mask_state = MaskState::from_usize(mask_enum); let mask_state = MaskState::from_usize(mask_enum);
@ -113,6 +121,7 @@ impl ShapePipeline {
.unwrap(); .unwrap();
ShapePipeline { ShapePipeline {
pipelines: EnumMap::from_array(mask_array), pipelines: EnumMap::from_array(mask_array),
depthless,
} }
} }
} }
@ -289,46 +298,62 @@ fn create_shape_pipeline(
)) ))
}; };
ShapePipeline::build(|mask_state| match mask_state { ShapePipeline::build(
MaskState::NoMask => mask_render_state( device.create_render_pipeline(&create_pipeline_descriptor(
"no mask", create_debug_label!("{} depthless pipeline", name).as_deref(),
wgpu::StencilFaceState { shader,
compare: wgpu::CompareFunction::Always, shader,
fail_op: wgpu::StencilOperation::Keep, &pipeline_layout,
depth_fail_op: wgpu::StencilOperation::Keep, None,
pass_op: wgpu::StencilOperation::Keep, &[Some(wgpu::ColorTargetState {
}, format,
wgpu::ColorWrites::ALL, blend: Some(blend),
), write_mask: wgpu::ColorWrites::ALL,
MaskState::DrawMaskStencil => mask_render_state( })],
"draw mask stencil", vertex_buffers_layout,
wgpu::StencilFaceState { msaa_sample_count,
compare: wgpu::CompareFunction::Equal, )),
fail_op: wgpu::StencilOperation::Keep, |mask_state| match mask_state {
depth_fail_op: wgpu::StencilOperation::Keep, MaskState::NoMask => mask_render_state(
pass_op: wgpu::StencilOperation::IncrementClamp, "no mask",
}, wgpu::StencilFaceState {
wgpu::ColorWrites::empty(), compare: wgpu::CompareFunction::Always,
), fail_op: wgpu::StencilOperation::Keep,
MaskState::DrawMaskedContent => mask_render_state( depth_fail_op: wgpu::StencilOperation::Keep,
"draw masked content", pass_op: wgpu::StencilOperation::Keep,
wgpu::StencilFaceState { },
compare: wgpu::CompareFunction::Equal, wgpu::ColorWrites::ALL,
fail_op: wgpu::StencilOperation::Keep, ),
depth_fail_op: wgpu::StencilOperation::Keep, MaskState::DrawMaskStencil => mask_render_state(
pass_op: wgpu::StencilOperation::Keep, "draw mask stencil",
}, wgpu::StencilFaceState {
wgpu::ColorWrites::ALL, compare: wgpu::CompareFunction::Equal,
), fail_op: wgpu::StencilOperation::Keep,
MaskState::ClearMaskStencil => mask_render_state( depth_fail_op: wgpu::StencilOperation::Keep,
"clear mask stencil", pass_op: wgpu::StencilOperation::IncrementClamp,
wgpu::StencilFaceState { },
compare: wgpu::CompareFunction::Equal, wgpu::ColorWrites::empty(),
fail_op: wgpu::StencilOperation::Keep, ),
depth_fail_op: wgpu::StencilOperation::Keep, MaskState::DrawMaskedContent => mask_render_state(
pass_op: wgpu::StencilOperation::DecrementClamp, "draw masked content",
}, wgpu::StencilFaceState {
wgpu::ColorWrites::empty(), 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(),
),
},
)
} }