[core] Use higher level LoadOp,StoreOp (#6785)

This commit is contained in:
Samson
2024-12-23 03:48:11 +01:00
committed by GitHub
parent f97b28b5f8
commit ee3ae0e549
6 changed files with 255 additions and 284 deletions

View File

@@ -72,20 +72,38 @@ pub struct GpuRenderPassColorAttachment {
view: ResourceId,
resolve_target: Option<ResourceId>,
clear_value: Option<wgpu_types::Color>,
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<V>(self, clear: V) -> wgpu_types::LoadOp<V> {
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<f32>,
depth_load_op: Option<wgpu_core::command::LoadOp>,
depth_load_op: Option<LoadOp>,
depth_store_op: Option<wgpu_core::command::StoreOp>,
depth_read_only: bool,
stencil_clear_value: u32,
stencil_load_op: Option<wgpu_core::command::LoadOp>,
stencil_load_op: Option<LoadOp>,
stencil_store_op: Option<wgpu_core::command::StoreOp>,
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,
},
});

View File

@@ -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,

View File

@@ -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<V>(load: LoadOp<V>) -> 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<V, L = Option<LoadOp>, S = Option<StoreOp>> {
pub struct PassChannel<V> {
/// 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<LoadOp<V>>,
/// 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<StoreOp>,
/// 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<V> PassChannel<V, LoadOp, StoreOp> {
fn hal_ops(&self) -> hal::AttachmentOps {
self.load_op.hal_ops() | self.store_op.hal_ops()
}
}
impl<V: Default> Default for PassChannel<V, LoadOp, StoreOp> {
fn default() -> Self {
PassChannel {
load_op: LoadOp::Load,
store_op: StoreOp::Store,
clear_value: V::default(),
read_only: false,
impl<V: Copy + Default> PassChannel<Option<V>> {
fn resolve(
&self,
handle_clear: impl Fn(Option<V>) -> Result<V, AttachmentError>,
) -> Result<ResolvedPassChannel<V>, 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<V: Copy + Default> PassChannel<Option<V>, Option<LoadOp>, Option<StoreOp>> {
fn resolve(&self) -> Result<PassChannel<V, LoadOp, StoreOp>, 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<V> {
ReadOnly,
Operational(wgt::Operations<V>),
}
impl<V: Copy + Default> ResolvedPassChannel<V> {
fn load_op(&self) -> LoadOp<V> {
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<Color>,
/// 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<Color>,
/// 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<f32>, Option<LoadOp>, Option<StoreOp>>,
pub depth: PassChannel<Option<f32>>,
/// What operations will be performed on the stencil part of the attachment.
pub stencil: PassChannel<Option<u32>, Option<LoadOp>, Option<StoreOp>>,
pub stencil: PassChannel<Option<u32>>,
}
/// 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<TextureView>,
/// What operations will be performed on the depth part of the attachment.
pub depth: PassChannel<f32, LoadOp, StoreOp>,
pub depth: ResolvedPassChannel<f32>,
/// What operations will be performed on the stencil part of the attachment.
pub stencil: PassChannel<u32, LoadOp, StoreOp>,
}
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<u32>,
}
/// 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<V>(
load_op: LoadOp<V>,
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 {

View File

@@ -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<V> {
/// 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 isnt a significant difference.
///
/// As a result, it is recommended to use "clear" rather than "load" in cases
/// where the initial value doesnt 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<V> LoadOp<V> {
/// Returns true if variants are same (ignoring clear value)
pub fn eq_variant<T>(&self, other: LoadOp<T>) -> bool {
matches!(
(self, other),
(LoadOp::Clear(_), LoadOp::Clear(_)) | (LoadOp::Load, LoadOp::Load)
)
}
}
impl<V: Default> Default for LoadOp<V> {
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<V> {
/// How data should be read through this attachment.
pub load: LoadOp<V>,
/// 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<V: Default> Default for Operations<V> {
#[inline]
fn default() -> Self {
Self {
load: LoadOp::<V>::default(),
store: StoreOp::default(),
}
}
}
/// Describes the depth/stencil state in a render pipeline.
///
/// Corresponds to [WebGPU `GPUDepthStencilState`](

View File

@@ -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<V> {
/// 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 isnt a significant difference.
///
/// As a result, it is recommended to use "clear" rather than "load" in cases
/// where the initial value doesnt 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<V: Default> Default for LoadOp<V> {
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<V> {
/// How data should be read through this attachment.
pub load: LoadOp<V>,
/// 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<V: Default> Default for Operations<V> {
#[inline]
fn default() -> Self {
Self {
load: LoadOp::<V>::default(),
store: StoreOp::default(),
}
}
}
/// Describes the timestamp writes of a render pass.
///
/// For use with [`RenderPassDescriptor`].

View File

@@ -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<V>(op: LoadOp<V>) -> (wgc::command::LoadOp, Option<V>) {
match op {
LoadOp::Clear(v) => (wgc::command::LoadOp::Clear, Some(v)),
LoadOp::Load => (wgc::command::LoadOp::Load, None),
fn map_load_op<V: Copy>(load: &LoadOp<V>) -> LoadOp<Option<V>> {
match load {
LoadOp::Clear(clear_value) => LoadOp::Clear(Some(*clear_value)),
LoadOp::Load => LoadOp::Load,
}
}
fn map_pass_channel<V: Copy>(ops: Option<&Operations<V>>) -> wgc::command::PassChannel<Option<V>> {
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::<Vec<_>>();