Skip to content

Commit 6058dd4

Browse files
committed
[wgpu-core] allow 2D-Array texture views with a single layer to be used as render attachments
1 parent a439fe4 commit 6058dd4

File tree

3 files changed

+236
-2
lines changed

3 files changed

+236
-2
lines changed

tests/tests/wgpu-gpu/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ mod query_set;
4545
mod queue_transfer;
4646
mod ray_tracing;
4747
mod render_pass_ownership;
48+
mod render_target;
4849
mod resource_descriptor_accessor;
4950
mod resource_error;
5051
mod samplers;

tests/tests/wgpu-gpu/render_target.rs

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
use wgpu::{
2+
util::{BufferInitDescriptor, DeviceExt},
3+
vertex_attr_array,
4+
};
5+
use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext};
6+
7+
#[gpu_test]
8+
static DRAW_TO_2D_VIEW: GpuTestConfiguration = GpuTestConfiguration::new()
9+
.parameters(TestParameters::default())
10+
.run_async(|ctx| run_test(ctx, wgpu::TextureViewDimension::D2, false));
11+
12+
#[gpu_test]
13+
static DRAW_TO_2D_ARRAY_VIEW: GpuTestConfiguration = GpuTestConfiguration::new()
14+
.parameters(TestParameters::default())
15+
.run_async(|ctx| run_test(ctx, wgpu::TextureViewDimension::D2Array, false));
16+
17+
#[gpu_test]
18+
static RESOLVE_TO_2D_VIEW: GpuTestConfiguration = GpuTestConfiguration::new()
19+
.parameters(TestParameters::default())
20+
.run_async(|ctx| run_test(ctx, wgpu::TextureViewDimension::D2, true));
21+
22+
#[gpu_test]
23+
static RESOLVE_TO_2D_ARRAY_VIEW: GpuTestConfiguration = GpuTestConfiguration::new()
24+
.parameters(TestParameters::default())
25+
.run_async(|ctx| run_test(ctx, wgpu::TextureViewDimension::D2Array, true));
26+
27+
async fn run_test(
28+
ctx: TestingContext,
29+
view_dimension: wgpu::TextureViewDimension,
30+
multisample: bool,
31+
) {
32+
let vertex_buffer_content: &[f32; 12] = &[
33+
// Triangle 1
34+
-1.0, -1.0, // Bottom left
35+
1.0, 1.0, // Top right
36+
-1.0, 1.0, // Top left
37+
// Triangle 2
38+
-1.0, -1.0, // Bottom left
39+
1.0, -1.0, // Bottom right
40+
1.0, 1.0, // Top right
41+
];
42+
let vertex_buffer = ctx.device.create_buffer_init(&BufferInitDescriptor {
43+
label: None,
44+
contents: bytemuck::cast_slice(vertex_buffer_content),
45+
usage: wgpu::BufferUsages::VERTEX,
46+
});
47+
48+
let shader_src = "
49+
@vertex
50+
fn vs_main(@location(0) position: vec2f) -> @builtin(position) vec4f {
51+
return vec4f(position, 0.0, 1.0);
52+
}
53+
54+
@fragment
55+
fn fs_main() -> @location(0) vec4f {
56+
return vec4f(1.0);
57+
}
58+
";
59+
60+
let shader = ctx
61+
.device
62+
.create_shader_module(wgpu::ShaderModuleDescriptor {
63+
label: None,
64+
source: wgpu::ShaderSource::Wgsl(shader_src.into()),
65+
});
66+
67+
let pipeline_desc = wgpu::RenderPipelineDescriptor {
68+
label: None,
69+
layout: None,
70+
vertex: wgpu::VertexState {
71+
buffers: &[wgpu::VertexBufferLayout {
72+
array_stride: 8,
73+
step_mode: wgpu::VertexStepMode::Vertex,
74+
attributes: &vertex_attr_array![0 => Float32x2],
75+
}],
76+
module: &shader,
77+
entry_point: Some("vs_main"),
78+
compilation_options: Default::default(),
79+
},
80+
primitive: wgpu::PrimitiveState::default(),
81+
depth_stencil: None,
82+
multisample: wgpu::MultisampleState {
83+
count: if multisample { 4 } else { 1 },
84+
mask: !0,
85+
alpha_to_coverage_enabled: false,
86+
},
87+
fragment: Some(wgpu::FragmentState {
88+
module: &shader,
89+
entry_point: Some("fs_main"),
90+
compilation_options: Default::default(),
91+
targets: &[Some(wgpu::ColorTargetState {
92+
format: wgpu::TextureFormat::R8Unorm,
93+
blend: None,
94+
write_mask: wgpu::ColorWrites::ALL,
95+
})],
96+
}),
97+
multiview: None,
98+
cache: None,
99+
};
100+
let pipeline = ctx.device.create_render_pipeline(&pipeline_desc);
101+
102+
const SIZE: u32 = 512;
103+
const LAYERS: u32 = 2;
104+
const MIPS: u32 = 2;
105+
const fn size_for_mips(mips: u32) -> u64 {
106+
let mut out: u64 = 0;
107+
let mut mip = 0;
108+
while mip < mips {
109+
let size = SIZE as u64 >> mip;
110+
out += size * size;
111+
112+
mip += 1;
113+
}
114+
out * LAYERS as u64
115+
}
116+
117+
let out_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
118+
label: None,
119+
size: wgpu::Extent3d {
120+
width: SIZE,
121+
height: SIZE,
122+
depth_or_array_layers: LAYERS,
123+
},
124+
mip_level_count: MIPS,
125+
sample_count: 1,
126+
dimension: wgpu::TextureDimension::D2,
127+
format: wgpu::TextureFormat::R8Unorm,
128+
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
129+
view_formats: &[],
130+
});
131+
132+
let readback_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
133+
label: None,
134+
size: size_for_mips(MIPS),
135+
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
136+
mapped_at_creation: false,
137+
});
138+
139+
let mut encoder = ctx
140+
.device
141+
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
142+
143+
for mip in 0..MIPS {
144+
let ms_texture_view = if multisample {
145+
let ms_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
146+
label: None,
147+
size: wgpu::Extent3d {
148+
width: SIZE >> mip,
149+
height: SIZE >> mip,
150+
depth_or_array_layers: 1,
151+
},
152+
mip_level_count: 1,
153+
sample_count: 4,
154+
dimension: wgpu::TextureDimension::D2,
155+
format: wgpu::TextureFormat::R8Unorm,
156+
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
157+
view_formats: &[],
158+
});
159+
let ms_texture_view = ms_texture.create_view(&wgpu::TextureViewDescriptor::default());
160+
Some(ms_texture_view)
161+
} else {
162+
None
163+
};
164+
for layer in 0..LAYERS {
165+
let out_texture_view = out_texture.create_view(&wgpu::TextureViewDescriptor {
166+
label: None,
167+
format: Some(wgpu::TextureFormat::R8Unorm),
168+
dimension: Some(view_dimension),
169+
usage: Some(wgpu::TextureUsages::RENDER_ATTACHMENT),
170+
aspect: wgpu::TextureAspect::All,
171+
base_mip_level: mip,
172+
mip_level_count: Some(1),
173+
base_array_layer: layer,
174+
array_layer_count: Some(1),
175+
});
176+
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
177+
label: None,
178+
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
179+
view: ms_texture_view.as_ref().unwrap_or(&out_texture_view),
180+
resolve_target: multisample.then_some(&out_texture_view),
181+
ops: wgpu::Operations {
182+
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
183+
store: if multisample {
184+
wgpu::StoreOp::Discard
185+
} else {
186+
wgpu::StoreOp::Store
187+
},
188+
},
189+
})],
190+
depth_stencil_attachment: None,
191+
timestamp_writes: None,
192+
occlusion_query_set: None,
193+
});
194+
rpass.set_pipeline(&pipeline);
195+
rpass.set_vertex_buffer(0, vertex_buffer.slice(..));
196+
rpass.draw(0..6, 0..1);
197+
}
198+
}
199+
200+
for mip in 0..MIPS {
201+
encoder.copy_texture_to_buffer(
202+
wgpu::TexelCopyTextureInfo {
203+
texture: &out_texture,
204+
mip_level: mip,
205+
origin: wgpu::Origin3d::ZERO,
206+
aspect: wgpu::TextureAspect::All,
207+
},
208+
wgpu::TexelCopyBufferInfo {
209+
buffer: &readback_buffer,
210+
layout: wgpu::TexelCopyBufferLayout {
211+
offset: size_for_mips(mip),
212+
bytes_per_row: Some(SIZE >> mip),
213+
rows_per_image: Some(SIZE >> mip),
214+
},
215+
},
216+
wgpu::Extent3d {
217+
width: SIZE >> mip,
218+
height: SIZE >> mip,
219+
depth_or_array_layers: LAYERS,
220+
},
221+
);
222+
}
223+
224+
ctx.queue.submit([encoder.finish()]);
225+
226+
let slice = readback_buffer.slice(..);
227+
slice.map_async(wgpu::MapMode::Read, |_| ());
228+
229+
ctx.async_poll(wgpu::PollType::wait()).await.unwrap();
230+
231+
let data = slice.get_mapped_range();
232+
let succeeded = data.iter().all(|b| *b == u8::MAX);
233+
assert!(succeeded);
234+
}

wgpu-core/src/device/resource.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1421,8 +1421,7 @@ impl Device {
14211421
}
14221422

14231423
if !(resolved_dimension == TextureViewDimension::D2
1424-
|| (self.features.contains(wgt::Features::MULTIVIEW)
1425-
&& resolved_dimension == TextureViewDimension::D2Array))
1424+
|| resolved_dimension == TextureViewDimension::D2Array)
14261425
{
14271426
break 'error Err(TextureViewNotRenderableReason::Dimension(
14281427
resolved_dimension,

0 commit comments

Comments
 (0)