Skip to content

Commit e39b3ae

Browse files
kvarkjimblandy
authored andcommitted
Plumbing and a test for ACCELERATION_STRUCTURE_BINDING_ARRAY
1 parent 43f48b4 commit e39b3ae

18 files changed

Lines changed: 393 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ depth_stencil: Some(wgpu::DepthStencilState::stencil(
122122

123123
#### General
124124

125+
- Added TLAS binding array support via `ACCELERATION_STRUCTURE_BINDING_ARRAY`. By @kvark in #8923.
125126
- Added support for cooperative load/store operations in shaders. Currently only WGSL on the input and SPIR-V, METAL, and WGSL on the output are supported. By @kvark in [#8251](https://github.com/gfx-rs/wgpu/issues/8251).
126127
- Added support for per-vertex attributes in fragment shaders. Currently only WGSL input is supported, and only SPIR-V or WGSL output is supported. By @atlv24 in [#8821](https://github.com/gfx-rs/wgpu/issues/8821).
127128
- Added support for no-perspective barycentric coordinates. By @atlv24 in [#8852](https://github.com/gfx-rs/wgpu/issues/8852).

naga/src/valid/interface.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -962,7 +962,14 @@ impl super::Validator {
962962
}
963963
}
964964
crate::TypeInner::AccelerationStructure { .. } => {
965-
return Err(GlobalVariableError::InvalidBindingArray(base));
965+
if !self
966+
.capabilities
967+
.contains(Capabilities::ACCELERATION_STRUCTURE_BINDING_ARRAY)
968+
{
969+
return Err(GlobalVariableError::UnsupportedCapability(
970+
Capabilities::ACCELERATION_STRUCTURE_BINDING_ARRAY,
971+
));
972+
}
966973
}
967974
crate::TypeInner::RayQuery { .. } => {
968975
// This should have been rejected in `validate_type`.

naga/src/valid/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ bitflags::bitflags! {
208208
const RAY_TRACING_PIPELINE = 1 << 38;
209209
/// Support for draw index builtin
210210
const DRAW_INDEX = 1 << 39;
211+
/// Support for binding arrays of acceleration structures.
212+
const ACCELERATION_STRUCTURE_BINDING_ARRAY = 1 << 40;
211213
}
212214
}
213215

naga/tests/naga/wgsl_errors.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4685,18 +4685,20 @@ fn binding_array_requires_capability() {
46854685
Capabilities::TEXTURE_EXTERNAL
46864686
}
46874687

4688-
// Acceleration structures are not allowed in binding arrays
4688+
// Binding arrays of acceleration structures require a capability.
46894689
check_validation! {
46904690
r#"
46914691
enable wgpu_ray_query;
46924692
@group(0) @binding(0)
46934693
var acc_struct_array: binding_array<acceleration_structure, 10>;
46944694
"#:
46954695
Err(naga::valid::ValidationError::GlobalVariable {
4696-
source: naga::valid::GlobalVariableError::InvalidBindingArray(_),
4696+
source: naga::valid::GlobalVariableError::UnsupportedCapability(
4697+
Capabilities::ACCELERATION_STRUCTURE_BINDING_ARRAY
4698+
),
46974699
..
46984700
}),
4699-
Capabilities::all()
4701+
Capabilities::RAY_QUERY
47004702
}
47014703
}
47024704

player/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,16 @@ impl Player {
781781
let tlas = self.resolve_tlas_id(tlas_id);
782782
wgc::binding_model::ResolvedBindingResource::AccelerationStructure(tlas)
783783
}
784+
BindingResource::AccelerationStructureArray(tlas_ids) => {
785+
let resolved_tlas: Vec<_> = tlas_ids
786+
.to_vec()
787+
.into_iter()
788+
.map(|id| self.resolve_tlas_id(id))
789+
.collect();
790+
wgc::binding_model::ResolvedBindingResource::AccelerationStructureArray(
791+
Cow::Owned(resolved_tlas),
792+
)
793+
}
784794
BindingResource::ExternalTexture(external_texture_id) => {
785795
let external_texture =
786796
self.resolve_external_texture_id(external_texture_id);

tests/tests/wgpu-gpu/binding_array/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ mod buffers;
22
mod sampled_textures;
33
mod samplers;
44
mod storage_textures;
5+
mod tlas;
56

67
pub fn all_tests(tests: &mut Vec<wgpu_test::GpuTestInitializer>) {
78
buffers::all_tests(tests);
89
sampled_textures::all_tests(tests);
910
samplers::all_tests(tests);
1011
storage_textures::all_tests(tests);
12+
tlas::all_tests(tests);
1113
}
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
use std::{borrow::Cow, num::NonZeroU32};
2+
3+
use wgpu::util::DeviceExt;
4+
use wgpu::*;
5+
use wgpu_test::{
6+
gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,
7+
};
8+
9+
pub fn all_tests(tests: &mut Vec<GpuTestInitializer>) {
10+
tests.push(BINDING_ARRAY_TLAS);
11+
}
12+
13+
#[gpu_test]
14+
static BINDING_ARRAY_TLAS: GpuTestConfiguration = GpuTestConfiguration::new()
15+
.parameters(
16+
TestParameters::default()
17+
.instance_flags(wgpu::InstanceFlags::GPU_BASED_VALIDATION)
18+
// Ray queries + acceleration structure bindings are gated behind this experimental feature.
19+
.features(
20+
Features::EXPERIMENTAL_RAY_QUERY | Features::ACCELERATION_STRUCTURE_BINDING_ARRAY,
21+
)
22+
.limits({
23+
let mut limits =
24+
Limits::default().using_minimum_supported_acceleration_structure_values();
25+
// Keep this small; we only need a couple of array elements.
26+
limits.max_binding_array_elements_per_shader_stage = 8;
27+
limits.max_acceleration_structures_per_shader_stage = 8;
28+
limits
29+
})
30+
// As of writing, Metal's HAL does not implement binding acceleration structures.
31+
.skip(FailureCase::backend(Backends::METAL)),
32+
)
33+
.run_async(|ctx| async move { binding_array_tlas(ctx).await });
34+
35+
async fn binding_array_tlas(ctx: TestingContext) {
36+
// Minimal shader that consumes a TLAS binding array.
37+
//
38+
// We don't need to actually "trace" anything for this test. We only need:
39+
// - Pipeline compilation to accept `binding_array<acceleration_structure>`
40+
// - Bind group creation to accept `BindingResource::AccelerationStructureArray`
41+
// - Encoder to successfully set the bind group and submit.
42+
//
43+
// Creating a `ray_query` and initializing it against element 0 forces the binding to be used.
44+
let shader = r#"
45+
enable wgpu_ray_query;
46+
47+
@group(0) @binding(0)
48+
var tlas_array: binding_array<acceleration_structure>;
49+
50+
@compute
51+
@workgroup_size(1, 1, 1)
52+
fn main() {
53+
var rq: ray_query;
54+
rayQueryInitialize(
55+
&rq,
56+
tlas_array[0],
57+
RayDesc(
58+
0u,
59+
0xffu,
60+
0.001,
61+
1000.0,
62+
vec3f(0.0, 0.0, 0.0),
63+
vec3f(0.0, 0.0, 1.0)
64+
)
65+
);
66+
}
67+
"#;
68+
69+
let module = ctx.device.create_shader_module(ShaderModuleDescriptor {
70+
label: Some("Binding Array TLAS"),
71+
source: ShaderSource::Wgsl(Cow::Borrowed(shader)),
72+
});
73+
74+
// Build a minimal BLAS + two TLAS so we can bind an array of TLAS.
75+
//
76+
// This follows the shapes used in the ray tracing examples.
77+
let vertex_data: [[f32; 3]; 3] = [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
78+
let index_data: [u16; 3] = [0, 1, 2];
79+
80+
let vertex_buf = ctx
81+
.device
82+
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
83+
label: Some("RT Vertex Buffer"),
84+
contents: bytemuck::cast_slice(&vertex_data),
85+
usage: BufferUsages::VERTEX | BufferUsages::BLAS_INPUT,
86+
});
87+
88+
let index_buf = ctx
89+
.device
90+
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
91+
label: Some("RT Index Buffer"),
92+
contents: bytemuck::cast_slice(&index_data),
93+
usage: BufferUsages::INDEX | BufferUsages::BLAS_INPUT,
94+
});
95+
96+
let blas_geo_size_desc = wgpu::BlasTriangleGeometrySizeDescriptor {
97+
vertex_format: wgpu::VertexFormat::Float32x3,
98+
vertex_count: vertex_data.len() as u32,
99+
index_format: Some(wgpu::IndexFormat::Uint16),
100+
index_count: Some(index_data.len() as u32),
101+
flags: wgpu::AccelerationStructureGeometryFlags::OPAQUE,
102+
};
103+
104+
let blas = ctx.device.create_blas(
105+
&wgpu::CreateBlasDescriptor {
106+
label: Some("BLAS"),
107+
flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,
108+
update_mode: wgpu::AccelerationStructureUpdateMode::Build,
109+
},
110+
wgpu::BlasGeometrySizeDescriptors::Triangles {
111+
descriptors: vec![blas_geo_size_desc.clone()],
112+
},
113+
);
114+
115+
let mut tlas_a = ctx.device.create_tlas(&wgpu::CreateTlasDescriptor {
116+
label: Some("TLAS A"),
117+
flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,
118+
update_mode: wgpu::AccelerationStructureUpdateMode::Build,
119+
max_instances: 1,
120+
});
121+
122+
let mut tlas_b = ctx.device.create_tlas(&wgpu::CreateTlasDescriptor {
123+
label: Some("TLAS B"),
124+
flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,
125+
update_mode: wgpu::AccelerationStructureUpdateMode::Build,
126+
max_instances: 1,
127+
});
128+
129+
// Put a single instance into each TLAS. Both reference the same BLAS.
130+
//
131+
// NOTE: This indexing API is how TLAS instances are populated in the examples.
132+
tlas_a[0] = Some(wgpu::TlasInstance::new(
133+
&blas,
134+
[1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0],
135+
0,
136+
0xff,
137+
));
138+
tlas_b[0] = Some(wgpu::TlasInstance::new(
139+
&blas,
140+
[1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0],
141+
0,
142+
0xff,
143+
));
144+
145+
// Build BLAS and TLASes.
146+
let mut encoder = ctx
147+
.device
148+
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
149+
label: Some("RT Build"),
150+
});
151+
152+
encoder.build_acceleration_structures(
153+
std::iter::once(&wgpu::BlasBuildEntry {
154+
blas: &blas,
155+
geometry: wgpu::BlasGeometries::TriangleGeometries(vec![wgpu::BlasTriangleGeometry {
156+
size: &blas_geo_size_desc,
157+
vertex_buffer: &vertex_buf,
158+
first_vertex: 0,
159+
vertex_stride: std::mem::size_of::<[f32; 3]>() as u64,
160+
index_buffer: Some(&index_buf),
161+
first_index: Some(0),
162+
transform_buffer: None,
163+
transform_buffer_offset: None,
164+
}]),
165+
}),
166+
std::iter::empty::<&wgpu::Tlas>()
167+
.chain(std::iter::once(&tlas_a))
168+
.chain(std::iter::once(&tlas_b)),
169+
);
170+
171+
ctx.queue.submit(Some(encoder.finish()));
172+
173+
// Bind group layout with a TLAS array binding.
174+
let bgl = ctx
175+
.device
176+
.create_bind_group_layout(&BindGroupLayoutDescriptor {
177+
label: Some("TLAS array BGL"),
178+
entries: &[BindGroupLayoutEntry {
179+
binding: 0,
180+
visibility: ShaderStages::COMPUTE,
181+
ty: BindingType::AccelerationStructure {
182+
vertex_return: false,
183+
},
184+
count: Some(NonZeroU32::new(2).unwrap()),
185+
}],
186+
});
187+
188+
let tlas_refs: [&Tlas; 2] = [&tlas_a, &tlas_b];
189+
190+
let bg = ctx.device.create_bind_group(&BindGroupDescriptor {
191+
label: Some("TLAS array BG"),
192+
layout: &bgl,
193+
entries: &[BindGroupEntry {
194+
binding: 0,
195+
resource: BindingResource::AccelerationStructureArray(&tlas_refs),
196+
}],
197+
});
198+
199+
let pipeline_layout = ctx
200+
.device
201+
.create_pipeline_layout(&PipelineLayoutDescriptor {
202+
label: Some("TLAS array pipeline layout"),
203+
bind_group_layouts: &[Some(&bgl)],
204+
immediate_size: 0,
205+
});
206+
207+
let pipeline = ctx
208+
.device
209+
.create_compute_pipeline(&ComputePipelineDescriptor {
210+
label: Some("TLAS array pipeline"),
211+
layout: Some(&pipeline_layout),
212+
module: &module,
213+
entry_point: Some("main"),
214+
compilation_options: Default::default(),
215+
cache: None,
216+
});
217+
218+
let mut encoder = ctx
219+
.device
220+
.create_command_encoder(&CommandEncoderDescriptor {
221+
label: Some("Dispatch"),
222+
});
223+
224+
{
225+
let mut pass = encoder.begin_compute_pass(&ComputePassDescriptor {
226+
label: Some("Compute pass"),
227+
timestamp_writes: None,
228+
});
229+
pass.set_pipeline(&pipeline);
230+
pass.set_bind_group(0, &bg, &[]);
231+
pass.dispatch_workgroups(1, 1, 1);
232+
}
233+
234+
ctx.queue.submit(Some(encoder.finish()));
235+
}

wgpu-core/src/binding_model.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,9 +600,11 @@ pub struct BindGroupEntry<
600600
[BufferBinding<B>]: ToOwned,
601601
[S]: ToOwned,
602602
[TV]: ToOwned,
603+
[TLAS]: ToOwned,
603604
<[BufferBinding<B>] as ToOwned>::Owned: fmt::Debug,
604605
<[S] as ToOwned>::Owned: fmt::Debug,
605606
<[TV] as ToOwned>::Owned: fmt::Debug,
607+
<[TLAS] as ToOwned>::Owned: fmt::Debug,
606608
{
607609
/// Slot for which binding provides resource. Corresponds to an entry of the same
608610
/// binding index in the [`BindGroupLayoutDescriptor`].
@@ -640,9 +642,11 @@ pub struct BindGroupDescriptor<
640642
[BufferBinding<B>]: ToOwned,
641643
[S]: ToOwned,
642644
[TV]: ToOwned,
645+
[TLAS]: ToOwned,
643646
<[BufferBinding<B>] as ToOwned>::Owned: fmt::Debug,
644647
<[S] as ToOwned>::Owned: fmt::Debug,
645648
<[TV] as ToOwned>::Owned: fmt::Debug,
649+
<[TLAS] as ToOwned>::Owned: fmt::Debug,
646650
[BindGroupEntry<'a, B, S, TV, TLAS, ET>]: ToOwned,
647651
<[BindGroupEntry<'a, B, S, TV, TLAS, ET>] as ToOwned>::Owned: fmt::Debug,
648652
{
@@ -1007,9 +1011,11 @@ pub enum BindingResource<
10071011
[BufferBinding<B>]: ToOwned,
10081012
[S]: ToOwned,
10091013
[TV]: ToOwned,
1014+
[TLAS]: ToOwned,
10101015
<[BufferBinding<B>] as ToOwned>::Owned: fmt::Debug,
10111016
<[S] as ToOwned>::Owned: fmt::Debug,
10121017
<[TV] as ToOwned>::Owned: fmt::Debug,
1018+
<[TLAS] as ToOwned>::Owned: fmt::Debug,
10131019
{
10141020
Buffer(BufferBinding<B>),
10151021
#[cfg_attr(
@@ -1030,6 +1036,11 @@ pub enum BindingResource<
10301036
)]
10311037
TextureViewArray(Cow<'a, [TV]>),
10321038
AccelerationStructure(TLAS),
1039+
#[cfg_attr(
1040+
feature = "serde",
1041+
serde(bound(deserialize = "<[TLAS] as ToOwned>::Owned: Deserialize<'de>"))
1042+
)]
1043+
AccelerationStructureArray(Cow<'a, [TLAS]>),
10331044
ExternalTexture(ET),
10341045
}
10351046

wgpu-core/src/device/global.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,13 @@ impl Global {
885885
BindingResource::AccelerationStructure(ref tlas) => {
886886
ResolvedBindingResource::AccelerationStructure(resolve_tlas(tlas)?)
887887
}
888+
BindingResource::AccelerationStructureArray(ref tlas_array) => {
889+
let tlas_array = tlas_array
890+
.iter()
891+
.map(resolve_tlas)
892+
.collect::<Result<Vec<_>, _>>()?;
893+
ResolvedBindingResource::AccelerationStructureArray(Cow::Owned(tlas_array))
894+
}
888895
BindingResource::ExternalTexture(ref et) => {
889896
ResolvedBindingResource::ExternalTexture(resolve_external_texture(et)?)
890897
}

wgpu-core/src/device/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,10 @@ pub fn features_to_naga_capabilities(
443443
features
444444
.contains(wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING),
445445
);
446+
caps.set(
447+
Caps::ACCELERATION_STRUCTURE_BINDING_ARRAY,
448+
features.contains(wgt::Features::ACCELERATION_STRUCTURE_BINDING_ARRAY),
449+
);
446450
caps.set(
447451
Caps::STORAGE_TEXTURE_16BIT_NORM_FORMATS,
448452
features.contains(wgt::Features::TEXTURE_FORMAT_16BIT_NORM),

0 commit comments

Comments
 (0)