From ee3ae0e549fe01c4b699cf68f9b67ae8ea807564 Mon Sep 17 00:00:00 2001 From: Samson <16504129+sagudev@users.noreply.github.com> Date: Mon, 23 Dec 2024 03:48:11 +0100 Subject: [PATCH] [core] Use higher level LoadOp,StoreOp (#6785) --- deno_webgpu/command_encoder.rs | 37 ++++- player/tests/data/quad.ron | 7 +- wgpu-core/src/command/render.rs | 285 +++++++++++++------------------- wgpu-types/src/lib.rs | 89 ++++++++++ wgpu/src/api/render_pass.rs | 76 +-------- wgpu/src/backend/wgpu_core.rs | 45 ++--- 6 files changed, 255 insertions(+), 284 deletions(-) diff --git a/deno_webgpu/command_encoder.rs b/deno_webgpu/command_encoder.rs index 104afa7a3c..3eceec8ce0 100644 --- a/deno_webgpu/command_encoder.rs +++ b/deno_webgpu/command_encoder.rs @@ -72,20 +72,38 @@ pub struct GpuRenderPassColorAttachment { view: ResourceId, resolve_target: Option, clear_value: Option, - load_op: wgpu_core::command::LoadOp, + load_op: LoadOp, store_op: wgpu_core::command::StoreOp, } +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum LoadOp { + /// Clear the output attachment with the clear color. Clearing is faster than loading. + Clear = 0, + /// Do not clear output attachment. + Load = 1, +} + +impl LoadOp { + fn into_wgt(self, clear: V) -> wgpu_types::LoadOp { + match self { + LoadOp::Clear => wgpu_types::LoadOp::Clear(clear), + LoadOp::Load => wgpu_types::LoadOp::Load, + } + } +} + #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct GpuRenderPassDepthStencilAttachment { view: ResourceId, depth_clear_value: Option, - depth_load_op: Option, + depth_load_op: Option, depth_store_op: Option, depth_read_only: bool, stencil_clear_value: u32, - stencil_load_op: Option, + stencil_load_op: Option, stencil_store_op: Option, stencil_read_only: bool, } @@ -134,9 +152,8 @@ pub fn op_webgpu_command_encoder_begin_render_pass( Some(wgpu_core::command::RenderPassColorAttachment { view: texture_view_resource.1, resolve_target, - load_op: at.load_op, + load_op: at.load_op.into_wgt(at.clear_value.unwrap_or_default()), store_op: at.store_op, - clear_value: at.clear_value.unwrap_or_default(), }) } else { None @@ -156,15 +173,17 @@ pub fn op_webgpu_command_encoder_begin_render_pass( Some(wgpu_core::command::RenderPassDepthStencilAttachment { view: texture_view_resource.1, depth: wgpu_core::command::PassChannel { - load_op: attachment.depth_load_op, + load_op: attachment + .depth_load_op + .map(|load_op| load_op.into_wgt(attachment.depth_clear_value)), store_op: attachment.depth_store_op, - clear_value: Some(attachment.depth_clear_value.unwrap_or(0.0)), read_only: attachment.depth_read_only, }, stencil: wgpu_core::command::PassChannel { - load_op: attachment.stencil_load_op, + load_op: attachment + .stencil_load_op + .map(|load_op| load_op.into_wgt(Some(attachment.stencil_clear_value))), store_op: attachment.stencil_store_op, - clear_value: Some(attachment.stencil_clear_value), read_only: attachment.stencil_read_only, }, }); diff --git a/player/tests/data/quad.ron b/player/tests/data/quad.ron index 1adfa7cfa7..7bb56a6be0 100644 --- a/player/tests/data/quad.ron +++ b/player/tests/data/quad.ron @@ -100,14 +100,13 @@ Some(( view: Id(0, 1), resolve_target: None, - load_op: clear, - store_op: store, - clear_value: ( + load_op: clear(Color( r: 0, g: 0, b: 0, a: 1, - ), + )), + store_op: store, )), ], target_depth_stencil: None, diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index 2442ef3660..31b4dd8218 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -54,47 +54,19 @@ use super::{ }; use super::{DrawKind, Rect}; -/// Operation to perform to the output attachment at the start of a renderpass. -#[repr(C)] -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] -pub enum LoadOp { - /// Clear the output attachment with the clear color. Clearing is faster than loading. - Clear = 0, - /// Do not clear output attachment. - Load = 1, -} +pub use wgt::{LoadOp, StoreOp}; -impl LoadOp { - fn hal_ops(&self) -> hal::AttachmentOps { - match self { - LoadOp::Load => hal::AttachmentOps::LOAD, - LoadOp::Clear => hal::AttachmentOps::empty(), - } +fn load_hal_ops(load: LoadOp) -> hal::AttachmentOps { + match load { + LoadOp::Load => hal::AttachmentOps::LOAD, + LoadOp::Clear(_) => hal::AttachmentOps::empty(), } } -/// Operation to perform to the output attachment at the end of a renderpass. -#[repr(C)] -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] -pub enum StoreOp { - /// Discards the content of the render target. - /// - /// If you don't care about the contents of the target, this can be faster. - Discard = 0, - /// Store the result of the renderpass. - Store = 1, -} - -impl StoreOp { - fn hal_ops(&self) -> hal::AttachmentOps { - match self { - StoreOp::Store => hal::AttachmentOps::STORE, - StoreOp::Discard => hal::AttachmentOps::empty(), - } +fn store_hal_ops(store: StoreOp) -> hal::AttachmentOps { + match store { + StoreOp::Store => hal::AttachmentOps::STORE, + StoreOp::Discard => hal::AttachmentOps::empty(), } } @@ -102,67 +74,83 @@ impl StoreOp { #[repr(C)] #[derive(Clone, Debug, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct PassChannel, S = Option> { +pub struct PassChannel { /// Operation to perform to the output attachment at the start of a /// renderpass. /// /// This must be clear if it is the first renderpass rendering to a swap /// chain image. - pub load_op: L, + pub load_op: Option>, /// Operation to perform to the output attachment at the end of a renderpass. - pub store_op: S, - /// If load_op is [`LoadOp::Clear`], the attachment will be cleared to this - /// color. - pub clear_value: V, + pub store_op: Option, /// If true, the relevant channel is not changed by a renderpass, and the /// corresponding attachment can be used inside the pass by other read-only /// usages. pub read_only: bool, } -impl PassChannel { - fn hal_ops(&self) -> hal::AttachmentOps { - self.load_op.hal_ops() | self.store_op.hal_ops() - } -} - -impl Default for PassChannel { - fn default() -> Self { - PassChannel { - load_op: LoadOp::Load, - store_op: StoreOp::Store, - clear_value: V::default(), - read_only: false, +impl PassChannel> { + fn resolve( + &self, + handle_clear: impl Fn(Option) -> Result, + ) -> Result, AttachmentError> { + if self.read_only { + if self.load_op.is_some() { + return Err(AttachmentError::ReadOnlyWithLoad); + } + if self.store_op.is_some() { + return Err(AttachmentError::ReadOnlyWithStore); + } + Ok(ResolvedPassChannel::ReadOnly) + } else { + 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::Load => LoadOp::Load, + }, + store: self.store_op.ok_or(AttachmentError::NoStore)?, + })) } } } -impl PassChannel, Option, Option> { - fn resolve(&self) -> Result, AttachmentError> { - let load_op = if self.read_only { - if self.load_op.is_some() { - return Err(AttachmentError::ReadOnlyWithLoad); - } else { - LoadOp::Load - } - } else { - self.load_op.ok_or(AttachmentError::NoLoad)? - }; - let store_op = if self.read_only { - if self.store_op.is_some() { - return Err(AttachmentError::ReadOnlyWithStore); - } else { - StoreOp::Store - } - } else { - self.store_op.ok_or(AttachmentError::NoStore)? - }; - Ok(PassChannel { - load_op, - store_op, - clear_value: self.clear_value.unwrap_or_default(), - read_only: self.read_only, - }) +#[derive(Debug)] +pub enum ResolvedPassChannel { + ReadOnly, + Operational(wgt::Operations), +} + +impl ResolvedPassChannel { + fn load_op(&self) -> LoadOp { + match self { + ResolvedPassChannel::ReadOnly => LoadOp::Load, + ResolvedPassChannel::Operational(wgt::Operations { load, .. }) => *load, + } + } + + fn store_op(&self) -> StoreOp { + match self { + ResolvedPassChannel::ReadOnly => StoreOp::Store, + ResolvedPassChannel::Operational(wgt::Operations { store, .. }) => *store, + } + } + + fn clear_value(&self) -> V { + match self { + Self::Operational(wgt::Operations { + load: LoadOp::Clear(clear_value), + .. + }) => *clear_value, + _ => Default::default(), + } + } + + fn is_readonly(&self) -> bool { + matches!(self, Self::ReadOnly) + } + + fn hal_ops(&self) -> hal::AttachmentOps { + load_hal_ops(self.load_op()) | store_hal_ops(self.store_op()) } } @@ -180,12 +168,9 @@ pub struct RenderPassColorAttachment { /// /// This must be clear if it is the first renderpass rendering to a swap /// chain image. - pub load_op: LoadOp, + pub load_op: LoadOp, /// Operation to perform to the output attachment at the end of a renderpass. pub store_op: StoreOp, - /// If load_op is [`LoadOp::Clear`], the attachment will be cleared to this - /// color. - pub clear_value: Color, } /// Describes a color attachment to a render pass. @@ -200,12 +185,21 @@ struct ArcRenderPassColorAttachment { /// /// This must be clear if it is the first renderpass rendering to a swap /// chain image. - pub load_op: LoadOp, + pub load_op: LoadOp, /// Operation to perform to the output attachment at the end of a renderpass. pub store_op: StoreOp, - /// If load_op is [`LoadOp::Clear`], the attachment will be cleared to this - /// color. - pub clear_value: Color, +} +impl ArcRenderPassColorAttachment { + fn hal_ops(&self) -> hal::AttachmentOps { + load_hal_ops(self.load_op) | store_hal_ops(self.store_op) + } + + fn clear_value(&self) -> Color { + match self.load_op { + LoadOp::Clear(clear_value) => clear_value, + LoadOp::Load => Color::default(), + } + } } /// Describes a depth/stencil attachment to a render pass. @@ -216,9 +210,9 @@ pub struct RenderPassDepthStencilAttachment { /// The view to use as an attachment. pub view: id::TextureViewId, /// What operations will be performed on the depth part of the attachment. - pub depth: PassChannel, Option, Option>, + pub depth: PassChannel>, /// What operations will be performed on the stencil part of the attachment. - pub stencil: PassChannel, Option, Option>, + pub stencil: PassChannel>, } /// Describes a depth/stencil attachment to a render pass. @@ -227,47 +221,9 @@ pub struct ArcRenderPassDepthStencilAttachment { /// The view to use as an attachment. pub view: Arc, /// What operations will be performed on the depth part of the attachment. - pub depth: PassChannel, + pub depth: ResolvedPassChannel, /// What operations will be performed on the stencil part of the attachment. - pub stencil: PassChannel, -} - -impl ArcRenderPassDepthStencilAttachment { - /// Validate the given aspects' read-only flags against their load - /// and store ops. - /// - /// When an aspect is read-only, its load and store ops must be - /// `LoadOp::Load` and `StoreOp::Store`. - /// - /// On success, return a pair `(depth, stencil)` indicating - /// whether the depth and stencil passes are read-only. - fn depth_stencil_read_only( - &self, - aspects: hal::FormatAspects, - ) -> Result<(bool, bool), RenderPassErrorInner> { - let mut depth_read_only = true; - let mut stencil_read_only = true; - - if aspects.contains(hal::FormatAspects::DEPTH) { - if self.depth.read_only - && (self.depth.load_op, self.depth.store_op) != (LoadOp::Load, StoreOp::Store) - { - return Err(RenderPassErrorInner::InvalidDepthOps); - } - depth_read_only = self.depth.read_only; - } - - if aspects.contains(hal::FormatAspects::STENCIL) { - if self.stencil.read_only - && (self.stencil.load_op, self.stencil.store_op) != (LoadOp::Load, StoreOp::Store) - { - return Err(RenderPassErrorInner::InvalidStencilOps); - } - stencil_read_only = self.stencil.read_only; - } - - Ok((depth_read_only, stencil_read_only)) - } + pub stencil: ResolvedPassChannel, } /// Describes the attachments of a render pass. @@ -855,14 +811,14 @@ struct RenderPassInfo<'d> { } impl<'d> RenderPassInfo<'d> { - fn add_pass_texture_init_actions( - load_op: LoadOp, + fn add_pass_texture_init_actions( + load_op: LoadOp, store_op: StoreOp, texture_memory_actions: &mut CommandBufferTextureMemoryActions, view: &TextureView, pending_discard_init_fixups: &mut SurfacesInDiscardState, ) { - if load_op == LoadOp::Load { + if matches!(load_op, LoadOp::Load) { pending_discard_init_fixups.extend(texture_memory_actions.register_init_action( &TextureInitTrackerAction { texture: view.parent.clone(), @@ -999,20 +955,20 @@ impl<'d> RenderPassInfo<'d> { let ds_aspects = view.desc.aspects(); if !ds_aspects.contains(hal::FormatAspects::STENCIL) - || (at.stencil.load_op == at.depth.load_op - && at.stencil.store_op == at.depth.store_op) + || (at.stencil.load_op().eq_variant(at.depth.load_op()) + && at.stencil.store_op() == at.depth.store_op()) { Self::add_pass_texture_init_actions( - at.depth.load_op, - at.depth.store_op, + at.depth.load_op(), + at.depth.store_op(), texture_memory_actions, view, &mut pending_discard_init_fixups, ); } else if !ds_aspects.contains(hal::FormatAspects::DEPTH) { Self::add_pass_texture_init_actions( - at.stencil.load_op, - at.stencil.store_op, + at.stencil.load_op(), + at.stencil.store_op(), texture_memory_actions, view, &mut pending_discard_init_fixups, @@ -1040,7 +996,7 @@ impl<'d> RenderPassInfo<'d> { // NeedsInitializedMemory should know that it doesn't need to // clear the aspect that was set to C) let need_init_beforehand = - at.depth.load_op == LoadOp::Load || at.stencil.load_op == LoadOp::Load; + at.depth.load_op() == LoadOp::Load || at.stencil.load_op() == LoadOp::Load; if need_init_beforehand { pending_discard_init_fixups.extend( texture_memory_actions.register_init_action(&TextureInitTrackerAction { @@ -1059,7 +1015,7 @@ impl<'d> RenderPassInfo<'d> { // it isn't already set to NeedsInitializedMemory). // // (possible optimization: Delay and potentially drop this zeroing) - if at.depth.store_op != at.stencil.store_op { + if at.depth.store_op() != at.stencil.store_op() { if !need_init_beforehand { texture_memory_actions.register_implicit_init( &view.parent, @@ -1067,14 +1023,14 @@ impl<'d> RenderPassInfo<'d> { ); } divergent_discarded_depth_stencil_aspect = Some(( - if at.depth.store_op == StoreOp::Discard { + if at.depth.store_op() == StoreOp::Discard { wgt::TextureAspect::DepthOnly } else { wgt::TextureAspect::StencilOnly }, view.clone(), )); - } else if at.depth.store_op == StoreOp::Discard { + } else if at.depth.store_op() == StoreOp::Discard { // Both are discarded using the regular path. discarded_surfaces.push(TextureSurfaceDiscard { texture: view.parent.clone(), @@ -1084,7 +1040,8 @@ impl<'d> RenderPassInfo<'d> { } } - (is_depth_read_only, is_stencil_read_only) = at.depth_stencil_read_only(ds_aspects)?; + is_depth_read_only = at.depth.is_readonly(); + is_stencil_read_only = at.stencil.is_readonly(); let usage = if is_depth_read_only && is_stencil_read_only @@ -1106,7 +1063,7 @@ impl<'d> RenderPassInfo<'d> { }, depth_ops: at.depth.hal_ops(), stencil_ops: at.stencil.hal_ops(), - clear_value: (at.depth.clear_value, at.stencil.clear_value), + clear_value: (at.depth.clear_value(), at.stencil.clear_value()), }); } @@ -1218,8 +1175,8 @@ impl<'d> RenderPassInfo<'d> { usage: hal::TextureUses::COLOR_TARGET, }, resolve_target: hal_resolve_target, - ops: at.load_op.hal_ops() | at.store_op.hal_ops(), - clear_value: at.clear_value, + ops: at.hal_ops(), + clear_value: at.clear_value(), })); } @@ -1436,7 +1393,6 @@ impl Global { resolve_target, load_op, store_op, - clear_value, }) = color_attachment { let view = texture_views.get(*view_id).get()?; @@ -1458,7 +1414,6 @@ impl Global { resolve_target, load_op: *load_op, store_op: *store_op, - clear_value: *clear_value, })); } else { arc_desc.color_attachments.push(None); @@ -1478,28 +1433,26 @@ impl Global { ))); } - // If this.depthLoadOp is "clear", this.depthClearValue must be provided and must be between 0.0 and 1.0, inclusive. - if depth_stencil_attachment.depth.load_op == Some(LoadOp::Clear) { - if let Some(clear_value) = depth_stencil_attachment.depth.clear_value { - if !(0.0..=1.0).contains(&clear_value) { - return Err(CommandEncoderError::InvalidAttachment(AttachmentError::ClearValueOutOfRange(clear_value))); - } - } else { - return Err(CommandEncoderError::InvalidAttachment(AttachmentError::NoClearValue)); - } - } - Some(ArcRenderPassDepthStencilAttachment { view, depth: if format.has_depth_aspect() { - depth_stencil_attachment.depth.resolve()? + depth_stencil_attachment.depth.resolve(|clear| if let Some(clear) = clear { + // If this.depthLoadOp is "clear", this.depthClearValue must be provided and must be between 0.0 and 1.0, inclusive. + if !(0.0..=1.0).contains(&clear) { + Err(AttachmentError::ClearValueOutOfRange(clear)) + } else { + Ok(clear) + } + } else { + Err(AttachmentError::NoClearValue) + })? } else { - Default::default() + ResolvedPassChannel::ReadOnly }, stencil: if format.has_stencil_aspect() { - depth_stencil_attachment.stencil.resolve()? + depth_stencil_attachment.stencil.resolve(|clear| Ok(clear.unwrap_or_default()))? } else { - Default::default() + ResolvedPassChannel::ReadOnly }, }) } else { diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 94e6f76650..de377144dd 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -4872,6 +4872,95 @@ impl PartialEq for DepthBiasState { impl Eq for DepthBiasState {} +/// Operation to perform to the output attachment at the start of a render pass. +/// +/// Corresponds to [WebGPU `GPULoadOp`](https://gpuweb.github.io/gpuweb/#enumdef-gpuloadop), +/// plus the corresponding clearValue. +#[repr(u8)] +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] +pub enum LoadOp { + /// Loads the specified value for this attachment into the render pass. + /// + /// On some GPU hardware (primarily mobile), "clear" is significantly cheaper + /// because it avoids loading data from main memory into tile-local memory. + /// + /// On other GPU hardware, there isn’t a significant difference. + /// + /// As a result, it is recommended to use "clear" rather than "load" in cases + /// where the initial value doesn’t matter + /// (e.g. the render target will be cleared using a skybox). + Clear(V) = 0, + /// Loads the existing value for this attachment into the render pass. + Load = 1, +} + +impl LoadOp { + /// Returns true if variants are same (ignoring clear value) + pub fn eq_variant(&self, other: LoadOp) -> bool { + matches!( + (self, other), + (LoadOp::Clear(_), LoadOp::Clear(_)) | (LoadOp::Load, LoadOp::Load) + ) + } +} + +impl Default for LoadOp { + fn default() -> Self { + Self::Clear(Default::default()) + } +} + +/// Operation to perform to the output attachment at the end of a render pass. +/// +/// Corresponds to [WebGPU `GPUStoreOp`](https://gpuweb.github.io/gpuweb/#enumdef-gpustoreop). +#[repr(C)] +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] +pub enum StoreOp { + /// Stores the resulting value of the render pass for this attachment. + #[default] + Store = 0, + /// Discards the resulting value of the render pass for this attachment. + /// + /// The attachment will be treated as uninitialized afterwards. + /// (If only either Depth or Stencil texture-aspects is set to `Discard`, + /// the respective other texture-aspect will be preserved.) + /// + /// This can be significantly faster on tile-based render hardware. + /// + /// Prefer this if the attachment is not read by subsequent passes. + Discard = 1, +} + +/// Pair of load and store operations for an attachment aspect. +/// +/// This type is unique to the Rust API of `wgpu`. In the WebGPU specification, +/// separate `loadOp` and `storeOp` fields are used instead. +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Operations { + /// How data should be read through this attachment. + pub load: LoadOp, + /// Whether data will be written to through this attachment. + /// + /// Note that resolve textures (if specified) are always written to, + /// regardless of this setting. + pub store: StoreOp, +} + +impl Default for Operations { + #[inline] + fn default() -> Self { + Self { + load: LoadOp::::default(), + store: StoreOp::default(), + } + } +} + /// Describes the depth/stencil state in a render pipeline. /// /// Corresponds to [WebGPU `GPUDepthStencilState`]( diff --git a/wgpu/src/api/render_pass.rs b/wgpu/src/api/render_pass.rs index 5e245dc3ef..6802025635 100644 --- a/wgpu/src/api/render_pass.rs +++ b/wgpu/src/api/render_pass.rs @@ -1,6 +1,7 @@ use std::ops::Range; use crate::*; +pub use wgt::{LoadOp, Operations, StoreOp}; /// In-progress recording of a render pass: a list of render commands in a [`CommandEncoder`]. /// @@ -496,81 +497,6 @@ impl RenderPass<'_> { } } -/// Operation to perform to the output attachment at the start of a render pass. -/// -/// Corresponds to [WebGPU `GPULoadOp`](https://gpuweb.github.io/gpuweb/#enumdef-gpuloadop), -/// plus the corresponding clearValue. -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum LoadOp { - /// Loads the specified value for this attachment into the render pass. - /// - /// On some GPU hardware (primarily mobile), "clear" is significantly cheaper - /// because it avoids loading data from main memory into tile-local memory. - /// - /// On other GPU hardware, there isn’t a significant difference. - /// - /// As a result, it is recommended to use "clear" rather than "load" in cases - /// where the initial value doesn’t matter - /// (e.g. the render target will be cleared using a skybox). - Clear(V), - /// Loads the existing value for this attachment into the render pass. - Load, -} - -impl Default for LoadOp { - fn default() -> Self { - Self::Clear(Default::default()) - } -} - -/// Operation to perform to the output attachment at the end of a render pass. -/// -/// Corresponds to [WebGPU `GPUStoreOp`](https://gpuweb.github.io/gpuweb/#enumdef-gpustoreop). -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Default)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum StoreOp { - /// Stores the resulting value of the render pass for this attachment. - #[default] - Store, - /// Discards the resulting value of the render pass for this attachment. - /// - /// The attachment will be treated as uninitialized afterwards. - /// (If only either Depth or Stencil texture-aspects is set to `Discard`, - /// the respective other texture-aspect will be preserved.) - /// - /// This can be significantly faster on tile-based render hardware. - /// - /// Prefer this if the attachment is not read by subsequent passes. - Discard, -} - -/// Pair of load and store operations for an attachment aspect. -/// -/// This type is unique to the Rust API of `wgpu`. In the WebGPU specification, -/// separate `loadOp` and `storeOp` fields are used instead. -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Operations { - /// How data should be read through this attachment. - pub load: LoadOp, - /// Whether data will be written to through this attachment. - /// - /// Note that resolve textures (if specified) are always written to, - /// regardless of this setting. - pub store: StoreOp, -} - -impl Default for Operations { - #[inline] - fn default() -> Self { - Self { - load: LoadOp::::default(), - store: StoreOp::default(), - } - } -} - /// Describes the timestamp writes of a render pass. /// /// For use with [`RenderPassDescriptor`]. diff --git a/wgpu/src/backend/wgpu_core.rs b/wgpu/src/backend/wgpu_core.rs index eacd1fdc64..4becb1e8dd 100644 --- a/wgpu/src/backend/wgpu_core.rs +++ b/wgpu/src/backend/wgpu_core.rs @@ -3,7 +3,7 @@ use crate::{ dispatch::{self, BufferMappedRangeInterface, InterfaceTypes}, BindingResource, BufferBinding, BufferDescriptor, CompilationInfo, CompilationMessage, CompilationMessageType, ErrorSource, Features, Label, LoadOp, MapMode, Operations, - ShaderSource, StoreOp, SurfaceTargetUnsafe, TextureDescriptor, + ShaderSource, SurfaceTargetUnsafe, TextureDescriptor, }; use arrayvec::ArrayVec; @@ -397,35 +397,23 @@ fn map_texture_tagged_copy_view( } } -fn map_store_op(op: StoreOp) -> wgc::command::StoreOp { - match op { - StoreOp::Store => wgc::command::StoreOp::Store, - StoreOp::Discard => wgc::command::StoreOp::Discard, - } -} - -fn map_load_op(op: LoadOp) -> (wgc::command::LoadOp, Option) { - match op { - LoadOp::Clear(v) => (wgc::command::LoadOp::Clear, Some(v)), - LoadOp::Load => (wgc::command::LoadOp::Load, None), +fn map_load_op(load: &LoadOp) -> LoadOp> { + match load { + LoadOp::Clear(clear_value) => LoadOp::Clear(Some(*clear_value)), + LoadOp::Load => LoadOp::Load, } } fn map_pass_channel(ops: Option<&Operations>) -> wgc::command::PassChannel> { match ops { - Some(&Operations { load, store }) => { - let (load_op, clear_value) = map_load_op(load); - wgc::command::PassChannel { - load_op: Some(load_op), - store_op: Some(map_store_op(store)), - clear_value, - read_only: false, - } - } + Some(&Operations { load, store }) => wgc::command::PassChannel { + load_op: Some(map_load_op(&load)), + store_op: Some(store), + read_only: false, + }, None => wgc::command::PassChannel { load_op: None, store_op: None, - clear_value: None, read_only: true, }, } @@ -2246,16 +2234,13 @@ impl dispatch::CommandEncoderInterface for CoreCommandEncoder { .color_attachments .iter() .map(|ca| { - ca.as_ref().map(|at| { - let (load_op, clear_value) = map_load_op(at.ops.load); - wgc::command::RenderPassColorAttachment { + ca.as_ref() + .map(|at| wgc::command::RenderPassColorAttachment { view: at.view.inner.as_core().id, resolve_target: at.resolve_target.map(|view| view.inner.as_core().id), - load_op, - store_op: map_store_op(at.ops.store), - clear_value: clear_value.unwrap_or_default(), - } - }) + load_op: at.ops.load, + store_op: at.ops.store, + }) }) .collect::>();