Skip to main content

mraphics_core/render/
conveyor.rs

1use std::collections::HashMap;
2
3#[derive(Debug)]
4struct Gadget {
5    buffer: wgpu::Buffer,
6    ty: wgpu::BufferBindingType,
7}
8
9#[derive(Debug, Clone, Copy)]
10pub struct GadgetIndex {
11    pub group_index: usize,
12    pub binding_index: u32,
13}
14
15pub struct GadgetDescriptor<'a> {
16    pub label: &'a str,
17    pub index: GadgetIndex,
18    pub size: u64,
19    pub usage: wgpu::BufferUsages,
20    pub ty: wgpu::BufferBindingType,
21}
22
23#[derive(Clone, Debug)]
24pub struct GadgetData {
25    pub label: String,
26    pub index: GadgetIndex,
27    pub data: Vec<u8>,
28    pub needs_update_value: bool,
29    pub needs_update_buffer: bool,
30}
31
32#[derive(Debug)]
33pub struct Bundle {
34    bind_group: wgpu::BindGroup,
35    bind_group_layout: wgpu::BindGroupLayout,
36}
37
38#[derive(Debug)]
39pub enum ConveyorError {
40    UnknownGadgetLabel,
41}
42
43pub struct Conveyor {
44    pub needs_update: bool,
45    pub bundles: Vec<Option<Bundle>>,
46
47    gadgets: HashMap<String, Gadget>,
48    indices: Vec<Option<HashMap<u32, String>>>,
49}
50
51impl Conveyor {
52    pub fn new() -> Self {
53        Self {
54            gadgets: HashMap::new(),
55            bundles: Vec::new(),
56            indices: Vec::new(),
57            needs_update: false,
58        }
59    }
60
61    /// Updates or inserts a gadget and marks self as requiring an update
62    pub fn upsert_gadget(&mut self, device: &wgpu::Device, desc: &GadgetDescriptor) {
63        let buffer = device.create_buffer(&wgpu::BufferDescriptor {
64            label: Some(desc.label),
65            size: desc.size,
66            usage: desc.usage,
67            mapped_at_creation: false,
68        });
69
70        let gadget = Gadget {
71            buffer,
72            ty: desc.ty,
73        };
74
75        self.gadgets.insert(String::from(desc.label), gadget);
76
77        let group_index = desc.index.group_index;
78
79        while self.indices.len() <= group_index {
80            self.indices.push(None);
81        }
82
83        if self.indices[group_index].is_none() {
84            self.indices[group_index] = Some(HashMap::new());
85        }
86
87        // SATFTY: Checked upon
88        let group_desc = self.indices[group_index].as_mut().unwrap();
89        group_desc.insert(desc.index.binding_index, String::from(desc.label));
90
91        self.needs_update = true;
92    }
93
94    pub fn update_gadget(
95        &mut self,
96        queue: &wgpu::Queue,
97        gadget_label: &str,
98        data: &[u8],
99    ) -> Result<(), ConveyorError> {
100        let gadget = self
101            .gadgets
102            .get(gadget_label)
103            .ok_or(ConveyorError::UnknownGadgetLabel)?;
104
105        queue.write_buffer(&gadget.buffer, 0, data);
106
107        Ok(())
108    }
109
110    pub fn update_bundles(&mut self, device: &wgpu::Device) {
111        self.bundles = Vec::new();
112
113        for (group_index, group_desc) in self.indices.iter().enumerate() {
114            if group_desc.is_none() {
115                self.bundles.push(None);
116                continue;
117            }
118
119            let group_desc = group_desc.as_ref().unwrap();
120
121            let mut bind_group_layout_entries: Vec<wgpu::BindGroupLayoutEntry> = Vec::new();
122            let mut bind_group_entries: Vec<wgpu::BindGroupEntry> = Vec::new();
123
124            for (binding_index, gadget_label) in group_desc {
125                let gadget = self.gadgets.get(gadget_label).unwrap();
126
127                bind_group_layout_entries.push(wgpu::BindGroupLayoutEntry {
128                    binding: *binding_index,
129                    visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, // Hard coded currently
130                    ty: wgpu::BindingType::Buffer {
131                        ty: gadget.ty,
132                        has_dynamic_offset: false,
133                        min_binding_size: None,
134                    },
135                    count: None,
136                });
137
138                bind_group_entries.push(wgpu::BindGroupEntry {
139                    binding: *binding_index,
140                    resource: gadget.buffer.as_entire_binding(),
141                })
142            }
143
144            let bind_group_layout =
145                device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
146                    label: Some(&format!(
147                        "Mraphics bind group layout with index {}",
148                        group_index
149                    )),
150                    entries: &bind_group_layout_entries,
151                });
152
153            let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
154                label: Some(&format!("Mraphics bind group with index {}", group_index)),
155                layout: &bind_group_layout,
156                entries: &bind_group_entries,
157            });
158
159            let bundle = Bundle {
160                bind_group: bind_group,
161                bind_group_layout: bind_group_layout,
162            };
163
164            self.bundles.push(Some(bundle));
165
166            self.needs_update = false;
167        }
168    }
169
170    pub fn attach_bundles(&self, render_pass: &mut wgpu::RenderPass) {
171        for (index, maybe_bundle) in self.bundles.iter().enumerate() {
172            if let Some(bundle) = maybe_bundle {
173                render_pass.set_bind_group(index as u32, &bundle.bind_group, &[]);
174            }
175        }
176    }
177
178    /// Collects bind group layouts from a collection of bundles.
179    ///
180    /// # Behavior
181    /// - If no bundle defines a bind group at index `n`, but a later index `m > n` is defined,
182    ///   the result at position `n` will be `None`.
183    /// - If multiple bundles define a bind group at the same index, only the first
184    ///   encountered (in iteration order) will be used.
185    /// - The output length equals the maximum bundle length across the collection.
186    pub fn collect_bind_group_layouts(
187        bundles_collection: Vec<&Vec<Option<Bundle>>>,
188    ) -> Vec<Option<&wgpu::BindGroupLayout>> {
189        let mut max_len = 0;
190        let mut bind_group_layouts = Vec::new();
191
192        for bundles in bundles_collection.iter() {
193            if bundles.len() > max_len {
194                max_len = bundles.len()
195            }
196        }
197
198        'outer: for i in 0..max_len {
199            for bundles in bundles_collection.iter() {
200                if !bundles.get(i).is_none() && !bundles[i].is_none() {
201                    bind_group_layouts.push(Some(&bundles[i].as_ref().unwrap().bind_group_layout));
202                    continue 'outer;
203                }
204            }
205
206            bind_group_layouts.push(None);
207        }
208
209        bind_group_layouts
210    }
211}