1use super::binding::BindGroupLayout;
2
3pub struct GroupLayout<'a> {
5 pub group: u32,
6 pub layout: &'a BindGroupLayout,
7}
8
9pub struct OwnedGroupLayout {
12 pub group: u32,
13 pub layout: BindGroupLayout,
14}
15
16pub(crate) fn assemble_bind_group_layouts<'a>(
32 label: Option<&str>,
33 slots: Vec<GroupLayout<'a>>,
34) -> Vec<Option<&'a wgpu::BindGroupLayout>> {
35 if slots.is_empty() {
36 return Vec::new();
37 }
38
39 let max_group = slots.iter().map(|s| s.group).max().unwrap();
40 let mut assembled: Vec<Option<&wgpu::BindGroupLayout>> = vec![None; (max_group + 1) as usize];
41
42 for GroupLayout { group, layout } in slots {
43 let slot = &mut assembled[group as usize];
44 if slot.is_some() {
45 panic!(
46 "bind group {group} assigned more than once building pipeline layout{}",
47 label.map(|l| format!(" '{l}'")).unwrap_or_default()
48 );
49 }
50 *slot = Some(layout.raw());
51 }
52
53 for (i, slot) in assembled.iter().enumerate() {
54 if slot.is_none() {
55 panic!(
56 "bind group {i} has no layout assigned building pipeline layout{} (groups must be contiguous from 0)",
57 label.map(|l| format!(" '{l}'")).unwrap_or_default()
58 );
59 }
60 }
61
62 assembled
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use crate::wgpu::binding::BindGroupLayoutBuilder;
69 use crate::wgpu::test_util::with_device;
70
71 fn empty_layout(device: &wgpu::Device) -> BindGroupLayout {
72 BindGroupLayoutBuilder::new().build(device)
73 }
74
75 #[test]
76 fn no_slots_at_all_means_zero_bind_groups_not_a_missing_group_zero() {
77 let assembled = assemble_bind_group_layouts(None, vec![]);
83 assert!(assembled.is_empty());
84 }
85
86 #[test]
87 fn slots_out_of_order_still_assemble_into_group_order() {
88 with_device!(device, _queue, {
89 let a = empty_layout(&device);
90 let b = empty_layout(&device);
91 let c = empty_layout(&device);
92
93 let assembled = assemble_bind_group_layouts(
95 None,
96 vec![
97 GroupLayout { group: 2, layout: &c },
98 GroupLayout { group: 0, layout: &a },
99 GroupLayout { group: 1, layout: &b },
100 ],
101 );
102
103 assert_eq!(assembled.len(), 3);
104 assert!(std::ptr::eq(assembled[0].unwrap(), a.raw()));
105 assert!(std::ptr::eq(assembled[1].unwrap(), b.raw()));
106 assert!(std::ptr::eq(assembled[2].unwrap(), c.raw()));
107 });
108 }
109
110 #[test]
116 fn two_slots_claiming_the_same_group_panics() {
117 with_device!(device, _queue, {
118 let a = empty_layout(&device);
119 let b = empty_layout(&device);
120 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
121 assemble_bind_group_layouts(
122 None,
123 vec![GroupLayout { group: 0, layout: &a }, GroupLayout { group: 0, layout: &b }],
124 );
125 }));
126 assert!(result.is_err(), "expected a panic for a duplicate group index");
127 });
128 }
129
130 #[test]
131 fn a_gap_in_group_indices_panics() {
132 with_device!(device, _queue, {
133 let a = empty_layout(&device);
134 let c = empty_layout(&device);
135 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
137 assemble_bind_group_layouts(
138 None,
139 vec![GroupLayout { group: 0, layout: &a }, GroupLayout { group: 2, layout: &c }],
140 );
141 }));
142 assert!(result.is_err(), "expected a panic for a gap in group indices");
143 });
144 }
145}