Skip to content

Commit f0209e3

Browse files
opsticcwfitzgerald
andauthored
Add support for transient textures on Vulkan and Metal (#8247)
Co-authored-by: Connor Fitzgerald <[email protected]>
1 parent 7fdc5f1 commit f0209e3

File tree

25 files changed

+380
-11
lines changed

25 files changed

+380
-11
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ By @wumpf in [#8282](https://github.com/gfx-rs/wgpu/pull/8282), [#8285](https://
275275

276276
- Expose `naga::front::wgsl::UnimplementedEnableExtension`. By @ErichDonGubler in [#8237](https://github.com/gfx-rs/wgpu/pull/8237).
277277

278+
- Added support for transient textures on Vulkan and Metal. By @opstic in [#8247](https://github.com/gfx-rs/wgpu/pull/8247)
279+
278280
### Changes
279281

280282
#### General

examples/features/src/msaa_line/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ impl Example {
112112
sample_count,
113113
dimension: wgpu::TextureDimension::D2,
114114
format: config.view_formats[0],
115-
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
115+
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TRANSIENT,
116116
label: None,
117117
view_formats: &[],
118118
};

examples/features/src/shadow/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ impl Example {
192192
sample_count: 1,
193193
dimension: wgpu::TextureDimension::D2,
194194
format: Self::DEPTH_FORMAT,
195-
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
195+
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TRANSIENT,
196196
label: None,
197197
view_formats: &[],
198198
});

examples/features/src/skybox/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ impl Example {
8080
sample_count: 1,
8181
dimension: wgpu::TextureDimension::D2,
8282
format: Self::DEPTH_FORMAT,
83-
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
83+
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TRANSIENT,
8484
label: None,
8585
view_formats: &[],
8686
});

tests/tests/wgpu-gpu/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ mod texture_view_creation;
6464
mod timestamp_normalization;
6565
mod timestamp_query;
6666
mod transfer;
67+
mod transient;
6768
mod transition_resources;
6869
mod vertex_formats;
6970
mod vertex_indices;
@@ -135,6 +136,7 @@ fn all_tests() -> Vec<wgpu_test::GpuTestInitializer> {
135136
timestamp_normalization::all_tests(&mut tests);
136137
timestamp_query::all_tests(&mut tests);
137138
transfer::all_tests(&mut tests);
139+
transient::all_tests(&mut tests);
138140
transition_resources::all_tests(&mut tests);
139141
vertex_formats::all_tests(&mut tests);
140142
vertex_indices::all_tests(&mut tests);

tests/tests/wgpu-gpu/transient.rs

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};
2+
3+
pub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {
4+
vec.push(RESOLVE_WITH_TRANSIENT);
5+
}
6+
7+
#[gpu_test]
8+
static RESOLVE_WITH_TRANSIENT: GpuTestConfiguration = GpuTestConfiguration::new()
9+
.parameters(TestParameters::default())
10+
.run_async(|ctx| async move {
11+
const SIZE: wgpu::Extent3d = wgpu::Extent3d {
12+
width: 256,
13+
height: 256,
14+
depth_or_array_layers: 1,
15+
};
16+
17+
let shader_src = "
18+
@vertex
19+
fn vs_main(@builtin(vertex_index) index: u32) -> @builtin(position) vec4f {
20+
let positions: array<vec2f, 3> = array<vec2f, 3>(
21+
vec2f(-1.0, -1.0),
22+
vec2f(-1.0, 3.0),
23+
vec2f(3.0, -1.0)
24+
);
25+
return vec4f(positions[index], 0.0, 1.0);
26+
}
27+
28+
@fragment
29+
fn fs_main() -> @location(0) vec4f {
30+
return vec4f(1.0);
31+
}
32+
";
33+
34+
let shader = ctx
35+
.device
36+
.create_shader_module(wgpu::ShaderModuleDescriptor {
37+
label: None,
38+
source: wgpu::ShaderSource::Wgsl(shader_src.into()),
39+
});
40+
41+
let pipeline_desc = wgpu::RenderPipelineDescriptor {
42+
label: None,
43+
layout: None,
44+
vertex: wgpu::VertexState {
45+
buffers: &[],
46+
module: &shader,
47+
entry_point: Some("vs_main"),
48+
compilation_options: Default::default(),
49+
},
50+
primitive: wgpu::PrimitiveState::default(),
51+
depth_stencil: None,
52+
multisample: wgpu::MultisampleState {
53+
count: 4,
54+
mask: !0,
55+
alpha_to_coverage_enabled: false,
56+
},
57+
fragment: Some(wgpu::FragmentState {
58+
module: &shader,
59+
entry_point: Some("fs_main"),
60+
compilation_options: Default::default(),
61+
targets: &[Some(wgpu::ColorTargetState {
62+
format: wgpu::TextureFormat::Rgba8Unorm,
63+
blend: None,
64+
write_mask: wgpu::ColorWrites::ALL,
65+
})],
66+
}),
67+
multiview: None,
68+
cache: None,
69+
};
70+
let pipeline = ctx.device.create_render_pipeline(&pipeline_desc);
71+
72+
let transient_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
73+
label: None,
74+
size: SIZE,
75+
mip_level_count: 1,
76+
sample_count: 4,
77+
dimension: wgpu::TextureDimension::D2,
78+
format: wgpu::TextureFormat::Rgba8Unorm,
79+
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TRANSIENT,
80+
view_formats: &[],
81+
});
82+
83+
let target_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
84+
label: None,
85+
size: SIZE,
86+
mip_level_count: 1,
87+
sample_count: 1,
88+
dimension: wgpu::TextureDimension::D2,
89+
format: wgpu::TextureFormat::Rgba8Unorm,
90+
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
91+
view_formats: &[],
92+
});
93+
94+
let readback_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
95+
label: None,
96+
size: 256 * 256 * 4,
97+
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
98+
mapped_at_creation: false,
99+
});
100+
101+
let mut encoder = ctx
102+
.device
103+
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
104+
105+
{
106+
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
107+
label: None,
108+
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
109+
view: &transient_texture.create_view(&wgpu::TextureViewDescriptor::default()),
110+
depth_slice: None,
111+
resolve_target: Some(
112+
&target_texture.create_view(&wgpu::TextureViewDescriptor::default()),
113+
),
114+
ops: wgpu::Operations {
115+
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
116+
store: wgpu::StoreOp::Discard,
117+
},
118+
})],
119+
depth_stencil_attachment: None,
120+
timestamp_writes: None,
121+
occlusion_query_set: None,
122+
});
123+
124+
rpass.set_pipeline(&pipeline);
125+
rpass.draw(0..3, 0..1);
126+
}
127+
128+
encoder.copy_texture_to_buffer(
129+
wgpu::TexelCopyTextureInfo {
130+
texture: &target_texture,
131+
mip_level: 0,
132+
origin: wgpu::Origin3d { x: 0, y: 0, z: 0 },
133+
aspect: wgpu::TextureAspect::All,
134+
},
135+
wgpu::TexelCopyBufferInfo {
136+
buffer: &readback_buffer,
137+
layout: wgpu::TexelCopyBufferLayout {
138+
offset: 0,
139+
bytes_per_row: Some(256 * 4),
140+
rows_per_image: Some(256),
141+
},
142+
},
143+
SIZE,
144+
);
145+
146+
ctx.queue.submit([encoder.finish()]);
147+
148+
let slice = readback_buffer.slice(..);
149+
slice.map_async(wgpu::MapMode::Read, |_| ());
150+
151+
ctx.async_poll(wgpu::PollType::wait_indefinitely())
152+
.await
153+
.unwrap();
154+
155+
let data = slice.get_mapped_range();
156+
let succeeded = data.iter().all(|b| *b == u8::MAX);
157+
assert!(succeeded);
158+
});

tests/tests/wgpu-validation/api/texture.rs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,3 +496,106 @@ fn copy_buffer_to_texture_forbidden_format_aspect() {
496496
);
497497
}
498498
}
499+
500+
/// Ensures that attempting to create a texture with [`wgpu::TextureUsages::TRANSIENT`]
501+
/// and its unsupported usages fails validation.
502+
#[test]
503+
fn transient_invalid_usage() {
504+
let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());
505+
506+
let size = wgpu::Extent3d {
507+
width: 256,
508+
height: 256,
509+
depth_or_array_layers: 1,
510+
};
511+
512+
let invalid_usages = wgpu::TextureUsages::all()
513+
- wgpu::TextureUsages::RENDER_ATTACHMENT
514+
- wgpu::TextureUsages::TRANSIENT;
515+
516+
for usage in invalid_usages {
517+
let invalid_texture_descriptor = wgpu::TextureDescriptor {
518+
label: None,
519+
size,
520+
mip_level_count: 1,
521+
sample_count: 1,
522+
dimension: wgpu::TextureDimension::D2,
523+
format: wgpu::TextureFormat::Rgba8Unorm,
524+
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TRANSIENT | usage,
525+
view_formats: &[],
526+
};
527+
fail(
528+
&device,
529+
|| device.create_texture(&invalid_texture_descriptor),
530+
Some(&format!("Texture usage TextureUsages(TRANSIENT) is not compatible with texture usage {usage:?}")),
531+
);
532+
}
533+
534+
let invalid_texture_descriptor = wgpu::TextureDescriptor {
535+
label: None,
536+
size,
537+
mip_level_count: 1,
538+
sample_count: 1,
539+
dimension: wgpu::TextureDimension::D2,
540+
format: wgpu::TextureFormat::Rgba8Unorm,
541+
usage: wgpu::TextureUsages::TRANSIENT,
542+
view_formats: &[],
543+
};
544+
fail(
545+
&device,
546+
|| device.create_texture(&invalid_texture_descriptor),
547+
Some("Invalid usage flags TextureUsages(TRANSIENT)"),
548+
);
549+
}
550+
551+
/// Ensures that attempting to use a texture of [`wgpu::TextureUsages::TRANSIENT`]
552+
/// with [`wgpu::StoreOp::Store`] fails validation.
553+
#[test]
554+
fn transient_invalid_storeop() {
555+
let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());
556+
557+
let size = wgpu::Extent3d {
558+
width: 256,
559+
height: 256,
560+
depth_or_array_layers: 1,
561+
};
562+
563+
let transient_texture = device.create_texture(&wgpu::TextureDescriptor {
564+
label: None,
565+
size,
566+
mip_level_count: 1,
567+
sample_count: 1,
568+
dimension: wgpu::TextureDimension::D2,
569+
format: wgpu::TextureFormat::Rgba8Unorm,
570+
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TRANSIENT,
571+
view_formats: &[],
572+
});
573+
574+
fail(
575+
&device,
576+
|| {
577+
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
578+
579+
let invalid_render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
580+
label: None,
581+
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
582+
view: &transient_texture.create_view(&wgpu::TextureViewDescriptor::default()),
583+
depth_slice: None,
584+
resolve_target: None,
585+
ops: wgpu::Operations {
586+
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
587+
store: wgpu::StoreOp::Store,
588+
},
589+
})],
590+
depth_stencil_attachment: None,
591+
timestamp_writes: None,
592+
occlusion_query_set: None,
593+
});
594+
595+
drop(invalid_render_pass);
596+
597+
encoder.finish()
598+
},
599+
Some("Color attachment's usage contains TextureUsages(TRANSIENT). This can only be used with StoreOp::Discard, but StoreOp::Store was provided")
600+
);
601+
}

wgpu-core/src/command/render.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,8 @@ pub enum ColorAttachmentError {
629629
mip_level: u32,
630630
depth_or_array_layer: u32,
631631
},
632+
#[error("Color attachment's usage contains {0:?}. This can only be used with StoreOp::{1:?}, but StoreOp::{2:?} was provided")]
633+
InvalidUsageForStoreOp(TextureUsages, StoreOp, StoreOp),
632634
}
633635

634636
impl WebGpuError for ColorAttachmentError {
@@ -1585,6 +1587,18 @@ impl Global {
15851587
let view = texture_views.get(*view_id).get()?;
15861588
view.same_device(device)?;
15871589

1590+
if view.desc.usage.contains(TextureUsages::TRANSIENT)
1591+
&& *store_op != StoreOp::Discard
1592+
{
1593+
return Err(RenderPassErrorInner::ColorAttachment(
1594+
ColorAttachmentError::InvalidUsageForStoreOp(
1595+
TextureUsages::TRANSIENT,
1596+
StoreOp::Discard,
1597+
*store_op,
1598+
),
1599+
));
1600+
}
1601+
15881602
let resolve_target = if let Some(resolve_target_id) = resolve_target {
15891603
let rt_arc = texture_views.get(*resolve_target_id).get()?;
15901604
rt_arc.same_device(device)?;

wgpu-core/src/conv.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ pub fn map_texture_usage(
124124
wgt::TextureUses::STORAGE_ATOMIC,
125125
usage.contains(wgt::TextureUsages::STORAGE_ATOMIC),
126126
);
127+
u.set(
128+
wgt::TextureUses::TRANSIENT,
129+
usage.contains(wgt::TextureUsages::TRANSIENT),
130+
);
127131
u
128132
}
129133

@@ -183,6 +187,10 @@ pub fn map_texture_usage_from_hal(uses: wgt::TextureUses) -> wgt::TextureUsages
183187
wgt::TextureUsages::STORAGE_ATOMIC,
184188
uses.contains(wgt::TextureUses::STORAGE_ATOMIC),
185189
);
190+
u.set(
191+
wgt::TextureUsages::TRANSIENT,
192+
uses.contains(wgt::TextureUses::TRANSIENT),
193+
);
186194
u
187195
}
188196

wgpu-core/src/device/resource.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1274,6 +1274,22 @@ impl Device {
12741274
}
12751275
}
12761276

1277+
if desc.usage.contains(wgt::TextureUsages::TRANSIENT) {
1278+
if !desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
1279+
return Err(CreateTextureError::InvalidUsage(
1280+
wgt::TextureUsages::TRANSIENT,
1281+
));
1282+
}
1283+
let extra_usage =
1284+
desc.usage - wgt::TextureUsages::TRANSIENT - wgt::TextureUsages::RENDER_ATTACHMENT;
1285+
if !extra_usage.is_empty() {
1286+
return Err(CreateTextureError::IncompatibleUsage(
1287+
wgt::TextureUsages::TRANSIENT,
1288+
extra_usage,
1289+
));
1290+
}
1291+
}
1292+
12771293
let format_features = self
12781294
.describe_format_features(desc.format)
12791295
.map_err(|error| CreateTextureError::MissingFeatures(desc.format, error))?;

0 commit comments

Comments
 (0)