mirror of
https://github.com/gfx-rs/wgpu.git
synced 2026-04-22 03:02:01 -04:00
[core] Use higher level LoadOp,StoreOp (#6785)
This commit is contained in:
@@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 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<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`](
|
||||
|
||||
@@ -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 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<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`].
|
||||
|
||||
@@ -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<_>>();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user