Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,23 @@ By @R-Cramer4 in [#8230](https://github.com/gfx-rs/wgpu/pull/8230)

[kek]: https://web.archive.org/web/20250923122958/https://knowyourmeme.com/memes/kek

#### New `LoadOp::DontCare`

In the case where a renderpass unconditionally writes to all pixels in the rendertarget,
`Load` can cause unnecessary memory traffic, and `Clear` can spend time unnecessarily
clearing the rendertargets. `DontCare` is a new `LoadOp` which will leave the contents
of the rendertarget undefined. Because this could lead to undefined behavior, this API
requires that the user gives an unsafe token to use the api.

While you can use this unconditionally, on platforms where `DontCare` is not available,
it will internally use a different load op.

```rust
load: LoadOp::DontCare(unsafe { wgpu::LoadOpDontCare::enabled() })
```

By @cwfitzgerald in [#8549](https://github.com/gfx-rs/wgpu/pull/8549)

#### `MipmapFilterMode` is split from `FilterMode`

This is a breaking change that aligns wgpu with spec.
Expand Down
2 changes: 2 additions & 0 deletions tests/tests/wgpu-gpu/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ mod multiview;
mod occlusion_query;
mod oob_indexing;
mod oom;
mod pass_ops;
mod pipeline;
mod pipeline_cache;
mod planar_texture;
Expand Down Expand Up @@ -104,6 +105,7 @@ fn all_tests() -> Vec<wgpu_test::GpuTestInitializer> {
occlusion_query::all_tests(&mut tests);
oob_indexing::all_tests(&mut tests);
oom::all_tests(&mut tests);
pass_ops::all_tests(&mut tests);
pipeline_cache::all_tests(&mut tests);
pipeline::all_tests(&mut tests);
planar_texture::all_tests(&mut tests);
Expand Down
114 changes: 114 additions & 0 deletions tests/tests/wgpu-gpu/pass_ops/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use wgpu_test::{
gpu_test, image::ReadbackBuffers, GpuTestConfiguration, GpuTestInitializer, TestParameters,
TestingContext,
};

pub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {
vec.push(DONT_CARE);
}

#[gpu_test]
static DONT_CARE: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default())
.run_async(run_test);

async fn run_test(ctx: TestingContext) {
let shader_src = "
const triangles = array<vec2f, 3>(vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0));

@vertex
fn vs_main(@builtin(vertex_index) vertex_index: u32) -> @builtin(position) vec4f {
return vec4f(triangles[vertex_index], 0.0, 1.0);
}

@fragment
fn fs_main() -> @location(0) vec4f {
return vec4f(127.0 / 255.0);
}
";

let shader = ctx
.device
.create_shader_module(wgpu::ShaderModuleDescriptor {
label: None,
source: wgpu::ShaderSource::Wgsl(shader_src.into()),
});

let pipeline_desc = wgpu::RenderPipelineDescriptor {
label: None,
layout: None,
vertex: wgpu::VertexState {
buffers: &[],
module: &shader,
entry_point: Some("vs_main"),
compilation_options: Default::default(),
},
primitive: wgpu::PrimitiveState::default(),
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: Some("fs_main"),
compilation_options: Default::default(),
targets: &[Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba8Unorm,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview_mask: None,
cache: None,
};
let pipeline = ctx.device.create_render_pipeline(&pipeline_desc);

let out_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8Unorm,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
view_formats: &[],
});

let readbacks = ReadbackBuffers::new(&ctx.device, &out_texture);

let mut encoder = ctx
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());

let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &out_texture.create_view(&wgpu::TextureViewDescriptor::default()),
depth_slice: None,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::DontCare(unsafe { wgpu::LoadOpDontCare::enabled() }),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&pipeline);
rpass.draw(0..3, 0..1);

drop(rpass);

readbacks.copy_from(&ctx.device, &mut encoder, &out_texture);

ctx.queue.submit([encoder.finish()]);

// Assert that DONT_CARE load op was fully overridden by the draw.
readbacks
.assert_buffer_contents(&ctx, &[127, 127, 127, 127])
.await;
}
6 changes: 3 additions & 3 deletions wgpu-core/src/command/clear.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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),
}),
)
Expand Down
14 changes: 8 additions & 6 deletions wgpu-core/src/command/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,15 @@ pub use wgt::{LoadOp, StoreOp};
fn load_hal_ops<V>(load: LoadOp<V>) -> 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,
}
}

Expand Down Expand Up @@ -115,6 +116,7 @@ impl<V: Copy + Default> PassChannel<Option<V>> {
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)?,
Expand Down Expand Up @@ -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(),
}
}
}
Expand Down Expand Up @@ -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> {
Expand Down
2 changes: 1 addition & 1 deletion wgpu-hal/examples/halmark/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,7 @@ impl<A: hal::Api> Example<A> {
},
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,
Expand Down
2 changes: 1 addition & 1 deletion wgpu-hal/examples/raw-gles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ fn fill_screen(exposed: &hal::ExposedAdapter<hal::api::Gles>, 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,
Expand Down
6 changes: 3 additions & 3 deletions wgpu-hal/src/dx12/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
Expand Down
14 changes: 8 additions & 6 deletions wgpu-hal/src/gles/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand All @@ -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
Expand Down Expand Up @@ -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() {
Expand All @@ -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(
Expand Down
5 changes: 4 additions & 1 deletion wgpu-hal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
);

Expand Down
21 changes: 18 additions & 3 deletions wgpu-hal/src/metal/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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
Expand All @@ -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
Expand Down
9 changes: 5 additions & 4 deletions wgpu-hal/src/vulkan/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
Loading