diff --git a/wgpu-core/src/command/clear.rs b/wgpu-core/src/command/clear.rs index 3e573b04e7..34fa510413 100644 --- a/wgpu-core/src/command/clear.rs +++ b/wgpu-core/src/command/clear.rs @@ -498,7 +498,7 @@ fn clear_texture_via_render_passes( }, depth_slice: None, resolve_target: None, - ops: hal::AttachmentOps::STORE, + ops: hal::AttachmentOps::STORE | hal::AttachmentOps::LOAD_CLEAR, clear_value: wgt::Color::TRANSPARENT, })]; (&color_attachments_tmp[..], None) @@ -515,8 +515,8 @@ fn clear_texture_via_render_passes( ), usage: wgt::TextureUses::DEPTH_STENCIL_WRITE, }, - depth_ops: hal::AttachmentOps::STORE, - stencil_ops: hal::AttachmentOps::STORE, + depth_ops: hal::AttachmentOps::STORE | hal::AttachmentOps::LOAD_CLEAR, + stencil_ops: hal::AttachmentOps::STORE | hal::AttachmentOps::LOAD_CLEAR, clear_value: (0.0, 0), }), ) diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index b31ea59a4d..209b35f69c 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -65,14 +65,15 @@ pub use wgt::{LoadOp, StoreOp}; fn load_hal_ops(load: LoadOp) -> hal::AttachmentOps { match load { LoadOp::Load => hal::AttachmentOps::LOAD, - LoadOp::Clear(_) => hal::AttachmentOps::empty(), + LoadOp::Clear(_) => hal::AttachmentOps::LOAD_CLEAR, + LoadOp::DontCare(_) => hal::AttachmentOps::LOAD_DONT_CARE, } } fn store_hal_ops(store: StoreOp) -> hal::AttachmentOps { match store { StoreOp::Store => hal::AttachmentOps::STORE, - StoreOp::Discard => hal::AttachmentOps::empty(), + StoreOp::Discard => hal::AttachmentOps::STORE_DISCARD, } } @@ -115,6 +116,7 @@ impl PassChannel> { Ok(ResolvedPassChannel::Operational(wgt::Operations { load: match self.load_op.ok_or(AttachmentError::NoLoad)? { LoadOp::Clear(clear_value) => LoadOp::Clear(handle_clear(clear_value)?), + LoadOp::DontCare(token) => LoadOp::DontCare(token), LoadOp::Load => LoadOp::Load, }, store: self.store_op.ok_or(AttachmentError::NoStore)?, @@ -204,7 +206,7 @@ impl ArcRenderPassColorAttachment { fn clear_value(&self) -> Color { match self.load_op { LoadOp::Clear(clear_value) => clear_value, - LoadOp::Load => Color::default(), + LoadOp::DontCare(_) | LoadOp::Load => Color::default(), } } } @@ -1555,13 +1557,13 @@ impl RenderPassInfo { if let Some((aspect, view)) = self.divergent_discarded_depth_stencil_aspect { let (depth_ops, stencil_ops) = if aspect == wgt::TextureAspect::DepthOnly { ( - hal::AttachmentOps::STORE, // clear depth - hal::AttachmentOps::LOAD | hal::AttachmentOps::STORE, // unchanged stencil + hal::AttachmentOps::LOAD_CLEAR | hal::AttachmentOps::STORE, // clear depth + hal::AttachmentOps::LOAD | hal::AttachmentOps::STORE, // unchanged stencil ) } else { ( hal::AttachmentOps::LOAD | hal::AttachmentOps::STORE, // unchanged stencil - hal::AttachmentOps::STORE, // clear depth + hal::AttachmentOps::LOAD_CLEAR | hal::AttachmentOps::STORE, // clear depth ) }; let desc = hal::RenderPassDescriptor::<'_, _, dyn hal::DynTextureView> { diff --git a/wgpu-hal/examples/halmark/main.rs b/wgpu-hal/examples/halmark/main.rs index f5391012ca..c6f4816031 100644 --- a/wgpu-hal/examples/halmark/main.rs +++ b/wgpu-hal/examples/halmark/main.rs @@ -718,7 +718,7 @@ impl Example { }, depth_slice: None, resolve_target: None, - ops: hal::AttachmentOps::STORE, + ops: hal::AttachmentOps::STORE | hal::AttachmentOps::LOAD_CLEAR, clear_value: wgpu_types::Color { r: 0.1, g: 0.2, diff --git a/wgpu-hal/examples/raw-gles.rs b/wgpu-hal/examples/raw-gles.rs index bda0d34587..b99da55a8d 100644 --- a/wgpu-hal/examples/raw-gles.rs +++ b/wgpu-hal/examples/raw-gles.rs @@ -325,7 +325,7 @@ fn fill_screen(exposed: &hal::ExposedAdapter, width: u32, height }, depth_slice: None, resolve_target: None, - ops: hal::AttachmentOps::STORE, + ops: hal::AttachmentOps::STORE | hal::AttachmentOps::LOAD_CLEAR, clear_value: wgpu_types::Color::BLUE, })], depth_stencil_attachment: None, diff --git a/wgpu-hal/src/dx12/command.rs b/wgpu-hal/src/dx12/command.rs index 9598d9b1cc..e9320f3b29 100644 --- a/wgpu-hal/src/dx12/command.rs +++ b/wgpu-hal/src/dx12/command.rs @@ -964,7 +964,7 @@ impl crate::CommandEncoder for super::CommandEncoder { self.pass.resolves.clear(); for (rtv, cat) in color_views.iter().zip(desc.color_attachments.iter()) { if let Some(cat) = cat.as_ref() { - if !cat.ops.contains(crate::AttachmentOps::LOAD) { + if cat.ops.contains(crate::AttachmentOps::LOAD_CLEAR) { let value = [ cat.clear_value.r as f32, cat.clear_value.g as f32, @@ -989,12 +989,12 @@ impl crate::CommandEncoder for super::CommandEncoder { if let Some(ref ds) = desc.depth_stencil_attachment { let mut flags = Direct3D12::D3D12_CLEAR_FLAGS::default(); let aspects = ds.target.view.aspects; - if !ds.depth_ops.contains(crate::AttachmentOps::LOAD) + if ds.depth_ops.contains(crate::AttachmentOps::LOAD_CLEAR) && aspects.contains(crate::FormatAspects::DEPTH) { flags |= Direct3D12::D3D12_CLEAR_FLAG_DEPTH; } - if !ds.stencil_ops.contains(crate::AttachmentOps::LOAD) + if ds.stencil_ops.contains(crate::AttachmentOps::LOAD_CLEAR) && aspects.contains(crate::FormatAspects::STENCIL) { flags |= Direct3D12::D3D12_CLEAR_FLAG_STENCIL; diff --git a/wgpu-hal/src/gles/command.rs b/wgpu-hal/src/gles/command.rs index 559de39cb8..798ca2b790 100644 --- a/wgpu-hal/src/gles/command.rs +++ b/wgpu-hal/src/gles/command.rs @@ -567,7 +567,7 @@ impl crate::CommandEncoder for super::CommandEncoder { .resolve_attachments .push((attachment, rat.view.clone())); } - if !cat.ops.contains(crate::AttachmentOps::STORE) { + if cat.ops.contains(crate::AttachmentOps::STORE_DISCARD) { self.state.invalidate_attachments.push(attachment); } } @@ -585,14 +585,16 @@ impl crate::CommandEncoder for super::CommandEncoder { depth_slice: None, }); if aspects.contains(crate::FormatAspects::DEPTH) - && !dsat.depth_ops.contains(crate::AttachmentOps::STORE) + && dsat.depth_ops.contains(crate::AttachmentOps::STORE_DISCARD) { self.state .invalidate_attachments .push(glow::DEPTH_ATTACHMENT); } if aspects.contains(crate::FormatAspects::STENCIL) - && !dsat.stencil_ops.contains(crate::AttachmentOps::STORE) + && dsat + .stencil_ops + .contains(crate::AttachmentOps::STORE_DISCARD) { self.state .invalidate_attachments @@ -628,7 +630,7 @@ impl crate::CommandEncoder for super::CommandEncoder { .filter_map(|at| at.as_ref()) .enumerate() { - if !cat.ops.contains(crate::AttachmentOps::LOAD) { + if cat.ops.contains(crate::AttachmentOps::LOAD_CLEAR) { let c = &cat.clear_value; self.cmd_buffer.commands.push( match cat.target.view.format.sample_type(None, None).unwrap() { @@ -652,8 +654,8 @@ impl crate::CommandEncoder for super::CommandEncoder { } if let Some(ref dsat) = desc.depth_stencil_attachment { - let clear_depth = !dsat.depth_ops.contains(crate::AttachmentOps::LOAD); - let clear_stencil = !dsat.stencil_ops.contains(crate::AttachmentOps::LOAD); + let clear_depth = dsat.depth_ops.contains(crate::AttachmentOps::LOAD_CLEAR); + let clear_stencil = dsat.stencil_ops.contains(crate::AttachmentOps::LOAD_CLEAR); if clear_depth && clear_stencil { self.cmd_buffer.commands.push(C::ClearDepthAndStencil( diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs index 8620d05a84..64cb23c4e2 100644 --- a/wgpu-hal/src/lib.rs +++ b/wgpu-hal/src/lib.rs @@ -1757,7 +1757,10 @@ bitflags!( #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct AttachmentOps: u8 { const LOAD = 1 << 0; - const STORE = 1 << 1; + const LOAD_CLEAR = 1 << 1; + const LOAD_DONT_CARE = 1 << 2; + const STORE = 1 << 3; + const STORE_DISCARD = 1 << 4; } ); diff --git a/wgpu-hal/src/metal/command.rs b/wgpu-hal/src/metal/command.rs index 86be90427d..31983309c7 100644 --- a/wgpu-hal/src/metal/command.rs +++ b/wgpu-hal/src/metal/command.rs @@ -670,9 +670,13 @@ impl crate::CommandEncoder for super::CommandEncoder { } let load_action = if at.ops.contains(crate::AttachmentOps::LOAD) { MTLLoadAction::Load - } else { + } else if at.ops.contains(crate::AttachmentOps::LOAD_DONT_CARE) { + MTLLoadAction::DontCare + } else if at.ops.contains(crate::AttachmentOps::LOAD_CLEAR) { at_descriptor.set_clear_color(conv::map_clear_color(&at.clear_value)); MTLLoadAction::Clear + } else { + unreachable!() }; let store_action = conv::map_store_action( at.ops.contains(crate::AttachmentOps::STORE), @@ -690,9 +694,13 @@ impl crate::CommandEncoder for super::CommandEncoder { let load_action = if at.depth_ops.contains(crate::AttachmentOps::LOAD) { MTLLoadAction::Load - } else { + } else if at.depth_ops.contains(crate::AttachmentOps::LOAD_DONT_CARE) { + MTLLoadAction::DontCare + } else if at.depth_ops.contains(crate::AttachmentOps::LOAD_CLEAR) { at_descriptor.set_clear_depth(at.clear_value.0 as f64); MTLLoadAction::Clear + } else { + unreachable!(); }; let store_action = if at.depth_ops.contains(crate::AttachmentOps::STORE) { MTLStoreAction::Store @@ -713,9 +721,16 @@ impl crate::CommandEncoder for super::CommandEncoder { let load_action = if at.stencil_ops.contains(crate::AttachmentOps::LOAD) { MTLLoadAction::Load - } else { + } else if at + .stencil_ops + .contains(crate::AttachmentOps::LOAD_DONT_CARE) + { + MTLLoadAction::DontCare + } else if at.stencil_ops.contains(crate::AttachmentOps::LOAD_CLEAR) { at_descriptor.set_clear_stencil(at.clear_value.1); MTLLoadAction::Clear + } else { + unreachable!() }; let store_action = if at.stencil_ops.contains(crate::AttachmentOps::STORE) { MTLStoreAction::Store diff --git a/wgpu-hal/src/vulkan/command.rs b/wgpu-hal/src/vulkan/command.rs index 16ba1e320d..095b6fa319 100644 --- a/wgpu-hal/src/vulkan/command.rs +++ b/wgpu-hal/src/vulkan/command.rs @@ -813,10 +813,11 @@ impl crate::CommandEncoder for super::CommandEncoder { }); let color = super::ColorAttachmentKey { base: cat.target.make_attachment_key(cat.ops), - resolve: cat - .resolve_target - .as_ref() - .map(|target| target.make_attachment_key(crate::AttachmentOps::STORE)), + resolve: cat.resolve_target.as_ref().map(|target| { + target.make_attachment_key( + crate::AttachmentOps::LOAD_CLEAR | crate::AttachmentOps::STORE, + ) + }), }; rp_key.colors.push(Some(color)); diff --git a/wgpu-hal/src/vulkan/conv.rs b/wgpu-hal/src/vulkan/conv.rs index fbad9b577a..24c3a5ba40 100644 --- a/wgpu-hal/src/vulkan/conv.rs +++ b/wgpu-hal/src/vulkan/conv.rs @@ -452,13 +452,19 @@ pub fn map_attachment_ops( ) -> (vk::AttachmentLoadOp, vk::AttachmentStoreOp) { let load_op = if op.contains(crate::AttachmentOps::LOAD) { vk::AttachmentLoadOp::LOAD - } else { + } else if op.contains(crate::AttachmentOps::LOAD_DONT_CARE) { + vk::AttachmentLoadOp::DONT_CARE + } else if op.contains(crate::AttachmentOps::LOAD_CLEAR) { vk::AttachmentLoadOp::CLEAR + } else { + unreachable!() }; let store_op = if op.contains(crate::AttachmentOps::STORE) { vk::AttachmentStoreOp::STORE - } else { + } else if op.contains(crate::AttachmentOps::STORE_DISCARD) { vk::AttachmentStoreOp::DONT_CARE + } else { + unreachable!() }; (load_op, store_op) } diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 6780bf3701..5c87542b0b 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -4790,6 +4790,21 @@ pub enum LoadOp { Clear(V) = 0, /// Loads the existing value for this attachment into the render pass. Load = 1, + /// The render target has undefined contents at the start of the render pass. + /// This may lead to undefined behavior if you read from the any of the + /// render target pixels without first writing to them. + /// + /// Blending also becomes undefined behavior if the source + /// pixels are undefined. + /// + /// This is the fastest option of all gpus if you will always overwrite all pixels + /// in the render target. + /// + /// # Safety + /// + /// - All pixels in the render target must be written to before + /// any read or a [`StoreOp::Store`] occurs. + DontCare(#[cfg_attr(feature = "serde", serde(skip))] LoadOpDontCare) = 2, } impl LoadOp { @@ -4797,7 +4812,9 @@ impl LoadOp { pub fn eq_variant(&self, other: LoadOp) -> bool { matches!( (self, other), - (LoadOp::Clear(_), LoadOp::Clear(_)) | (LoadOp::Load, LoadOp::Load) + (LoadOp::Clear(_), LoadOp::Clear(_)) + | (LoadOp::Load, LoadOp::Load) + | (LoadOp::DontCare(_), LoadOp::DontCare(_)) ) } } diff --git a/wgpu-types/src/tokens.rs b/wgpu-types/src/tokens.rs index ecf100ddb0..30f3e2f6bf 100644 --- a/wgpu-types/src/tokens.rs +++ b/wgpu-types/src/tokens.rs @@ -41,3 +41,32 @@ impl ExperimentalFeatures { self.enabled } } + +/// Token of the user agreeing to use [`LoadOp::DontCare`](crate::LoadOp::DontCare). +#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq)] +pub struct LoadOpDontCare { + // Private to prevent construction outside of the unsafe + // enabled() function. + _private: (), +} + +impl LoadOpDontCare { + /// Using [`LoadOp::DontCare`](crate::LoadOp::DontCare) will result + /// in the render target having undefined contents at the start of the render pass. + /// This may lead to undefined behavior if you read from the any of the + /// render target pixels without first writing to them. + /// + /// Blending also becomes undefined behavior if the source + /// pixels are undefined. + /// + /// All pixels in the render target must be written to before + /// any read or a [`StoreOp::Store`](crate::StoreOp::Store) occurs. + /// + /// # Safety + /// + /// - You acknowledge that using `LoadOp::DontCare` may lead to undefined behavior + /// if the above conditions are not met. + pub const unsafe fn enabled() -> Self { + Self { _private: () } + } +} diff --git a/wgpu/src/backend/webgpu.rs b/wgpu/src/backend/webgpu.rs index 66fefea36b..8fe632b4cd 100644 --- a/wgpu/src/backend/webgpu.rs +++ b/wgpu/src/backend/webgpu.rs @@ -3050,6 +3050,13 @@ impl dispatch::CommandEncoderInterface for WebCommandEncoder { clear_value = Some(wasm_bindgen::JsValue::from(map_color(color))); webgpu_sys::GpuLoadOp::Clear } + crate::LoadOp::DontCare(_token) => { + // WebGPU can't safely have a ClearOp::DontCare, so we clear to black + // which is ideal for most GPUs. + clear_value = + Some(wasm_bindgen::JsValue::from(map_color(crate::Color::BLACK))); + webgpu_sys::GpuLoadOp::Clear + } crate::LoadOp::Load => webgpu_sys::GpuLoadOp::Load, }; @@ -3091,6 +3098,11 @@ impl dispatch::CommandEncoderInterface for WebCommandEncoder { mapped_depth_stencil_attachment.set_depth_clear_value(v); webgpu_sys::GpuLoadOp::Clear } + crate::LoadOp::DontCare(_token) => { + // WebGPU can't safely have a ClearOp::DontCare, so we clear to 1.0 + mapped_depth_stencil_attachment.set_depth_clear_value(1.0); + webgpu_sys::GpuLoadOp::Clear + } crate::LoadOp::Load => webgpu_sys::GpuLoadOp::Load, }; mapped_depth_stencil_attachment.set_depth_load_op(load_op); @@ -3103,6 +3115,11 @@ impl dispatch::CommandEncoderInterface for WebCommandEncoder { mapped_depth_stencil_attachment.set_stencil_clear_value(v); webgpu_sys::GpuLoadOp::Clear } + crate::LoadOp::DontCare(_token) => { + // WebGPU can't safely have a ClearOp::DontCare, so we clear to 0 + mapped_depth_stencil_attachment.set_stencil_clear_value(0); + webgpu_sys::GpuLoadOp::Clear + } crate::LoadOp::Load => webgpu_sys::GpuLoadOp::Load, }; mapped_depth_stencil_attachment.set_stencil_load_op(load_op); diff --git a/wgpu/src/backend/wgpu_core.rs b/wgpu/src/backend/wgpu_core.rs index 2c5b3fea78..40964e1587 100644 --- a/wgpu/src/backend/wgpu_core.rs +++ b/wgpu/src/backend/wgpu_core.rs @@ -421,8 +421,9 @@ fn map_texture_tagged_copy_view( } fn map_load_op(load: &LoadOp) -> LoadOp> { - match load { - LoadOp::Clear(clear_value) => LoadOp::Clear(Some(*clear_value)), + match *load { + LoadOp::Clear(clear_value) => LoadOp::Clear(Some(clear_value)), + LoadOp::DontCare(token) => LoadOp::DontCare(token), LoadOp::Load => LoadOp::Load, } }