Skip to main content

pebble/graphics/pipeline/
layout.rs

1use crate::graphics::pipeline::binding::{BindGroupLayout, BindingEntry, BindingKind};
2
3pub enum GroupEntry {
4    Own(Vec<BindingEntry>),
5    Layout(BindGroupLayout),
6    Global(&'static str),
7}
8
9#[derive(Default)]
10pub struct OwnEntriesBuilder {
11    entries: Vec<BindingEntry>,
12    next_binding: u32,
13}
14
15impl OwnEntriesBuilder {
16    pub fn new() -> Self {
17        Self::default()
18    }
19
20    pub fn with_entry(self, name: &'static str, kind: BindingKind) -> Self {
21        let binding = self.next_binding;
22        self.with_entry_at(name, binding, kind)
23    }
24
25    pub fn with_entry_at(mut self, name: &'static str, binding: u32, kind: BindingKind) -> Self {
26        self.entries.push(BindingEntry { name, binding, kind });
27        self.next_binding = self.next_binding.max(binding + 1);
28        self
29    }
30
31    pub fn build(self) -> GroupEntry {
32        GroupEntry::Own(self.entries)
33    }
34}
35
36#[derive(Default)]
37pub struct GlobalLayoutPool {
38    entries: std::collections::HashMap<&'static str, BindGroupLayout>,
39}
40
41impl GlobalLayoutPool {
42    pub fn new() -> Self {
43        Self::default()
44    }
45
46    pub fn register(&mut self, name: &'static str, layout: BindGroupLayout) {
47        if self.entries.insert(name, layout).is_some() {
48            panic!("global layout pool: '{name}' is already registered");
49        }
50    }
51
52    pub fn get(&self, name: &str) -> Option<BindGroupLayout> {
53        self.entries.get(name).cloned()
54    }
55
56    pub(crate) fn get_ref(&self, name: &str) -> Option<&BindGroupLayout> {
57        self.entries.get(name)
58    }
59}
60
61#[derive(Clone, Copy)]
62pub(crate) enum PipelineKind {
63    Material,
64    Compute,
65}
66
67impl std::fmt::Display for PipelineKind {
68    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69        f.write_str(match self {
70            PipelineKind::Material => "material",
71            PipelineKind::Compute => "compute pass",
72        })
73    }
74}
75
76pub(crate) fn find_own_entries<'a>(
77    label: Option<&str>,
78    kind: PipelineKind,
79    groups: &'a [GroupEntry],
80) -> &'a [BindingEntry] {
81    let mut found: Option<&[BindingEntry]> = None;
82    for g in groups {
83        if let GroupEntry::Own(entries) = g {
84            if found.is_some() {
85                panic!(
86                    "{kind}{}: more than one GroupEntry::Own(...) in .entries(...) — a {kind} \
87                     can only have one group of its own bind group entries",
88                    label.map(|l| format!(" '{l}'")).unwrap_or_default()
89                );
90            }
91            found = Some(entries);
92        }
93    }
94    found.unwrap_or(&[])
95}
96
97pub(crate) fn assemble_group_layouts<'a>(
98    label: Option<&str>,
99    groups: &'a [GroupEntry],
100    own_layout: &'a BindGroupLayout,
101    pool: &'a GlobalLayoutPool,
102    max_bind_groups: u32,
103) -> Option<Vec<Option<&'a wgpu::BindGroupLayout>>> {
104    if groups.len() as u32 > max_bind_groups {
105        panic!(
106            "pipeline layout{} needs {} bind groups, but this device only supports \
107             {max_bind_groups} — trim .entries(...) to only the groups actually used",
108            label.map(|l| format!(" '{l}'")).unwrap_or_default(),
109            groups.len(),
110        );
111    }
112
113    groups
114        .iter()
115        .map(|g| {
116            let layout = match g {
117                GroupEntry::Own(_) => own_layout,
118                GroupEntry::Layout(l) => l,
119                GroupEntry::Global(name) => pool.get_ref(name)?,
120            };
121            Some(Some(layout.raw()))
122        })
123        .collect()
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    #[test]
131    fn find_own_entries_returns_empty_slice_when_there_is_no_own_group() {
132        let entries = find_own_entries(None, PipelineKind::Material, &[]);
133        assert!(entries.is_empty());
134    }
135}