pub struct GroupLayout<'a> {
pub group: u32,
pub layout: &'a wgpu::BindGroupLayout,
}
pub struct OwnedGroupLayout {
pub group: u32,
pub layout: wgpu::BindGroupLayout,
}
pub fn assemble_bind_group_layouts<'a>(
label: Option<&str>,
slots: Vec<GroupLayout<'a>>,
) -> Vec<Option<&'a wgpu::BindGroupLayout>> {
if slots.is_empty() {
return Vec::new();
}
let max_group = slots.iter().map(|s| s.group).max().unwrap();
let mut assembled: Vec<Option<&wgpu::BindGroupLayout>> = vec![None; (max_group + 1) as usize];
for GroupLayout { group, layout } in slots {
let slot = &mut assembled[group as usize];
if slot.is_some() {
panic!(
"bind group {group} assigned more than once building pipeline layout{}",
label.map(|l| format!(" '{l}'")).unwrap_or_default()
);
}
*slot = Some(layout);
}
for (i, slot) in assembled.iter().enumerate() {
if slot.is_none() {
panic!(
"bind group {i} has no layout assigned building pipeline layout{} (groups must be contiguous from 0)",
label.map(|l| format!(" '{l}'")).unwrap_or_default()
);
}
}
assembled
}
#[cfg(test)]
mod tests {
use super::*;
use crate::wgpu::test_util::with_device;
fn empty_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout {
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &[],
})
}
#[test]
fn no_slots_at_all_means_zero_bind_groups_not_a_missing_group_zero() {
let assembled = assemble_bind_group_layouts(None, vec![]);
assert!(assembled.is_empty());
}
#[test]
fn slots_out_of_order_still_assemble_into_group_order() {
with_device!(device, _queue, {
let a = empty_layout(&device);
let b = empty_layout(&device);
let c = empty_layout(&device);
let assembled = assemble_bind_group_layouts(
None,
vec![
GroupLayout { group: 2, layout: &c },
GroupLayout { group: 0, layout: &a },
GroupLayout { group: 1, layout: &b },
],
);
assert_eq!(assembled.len(), 3);
assert!(std::ptr::eq(assembled[0].unwrap(), &a));
assert!(std::ptr::eq(assembled[1].unwrap(), &b));
assert!(std::ptr::eq(assembled[2].unwrap(), &c));
});
}
#[test]
fn two_slots_claiming_the_same_group_panics() {
with_device!(device, _queue, {
let a = empty_layout(&device);
let b = empty_layout(&device);
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
assemble_bind_group_layouts(
None,
vec![GroupLayout { group: 0, layout: &a }, GroupLayout { group: 0, layout: &b }],
);
}));
assert!(result.is_err(), "expected a panic for a duplicate group index");
});
}
#[test]
fn a_gap_in_group_indices_panics() {
with_device!(device, _queue, {
let a = empty_layout(&device);
let c = empty_layout(&device);
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
assemble_bind_group_layouts(
None,
vec![GroupLayout { group: 0, layout: &a }, GroupLayout { group: 2, layout: &c }],
);
}));
assert!(result.is_err(), "expected a panic for a gap in group indices");
});
}
}