Skip to main content

dynamis_gpu/
bindings.rs

1use crate::{BindingKind, StreamElement};
2
3#[derive(Clone, Debug, PartialEq, Eq)]
4pub struct ShaderBinding {
5    pub group: u32,
6    pub binding: u32,
7    pub kind: BindingKind,
8    pub name: String,
9    pub element: String,
10}
11
12pub fn assert_binding_element(
13    label: &str,
14    name: &str,
15    declaration: &ShaderBinding,
16    element: StreamElement,
17) {
18    let declared = declaration.element.as_str();
19    assert!(
20        declared == element.wgsl() || (word_view(declared) && element.bytes() >= 4),
21        "stage {label:?} binds {name:?} to the {} stream while its shader declares {declared:?}",
22        element.wgsl(),
23    );
24}
25
26fn word_view(declared: &str) -> bool {
27    declared == "u32" || declared == "f32"
28}
29
30pub fn parse_bindings(source: &str) -> Vec<ShaderBinding> {
31    let mut bindings = Vec::new();
32    let mut cursor = 0;
33    while let Some(offset) = source[cursor..].find("@group(") {
34        cursor += offset + "@group(".len();
35        let group = parse_index(source, &mut cursor, "group");
36        expect(source, &mut cursor, ")");
37        skip_space(source, &mut cursor);
38        expect(source, &mut cursor, "@binding(");
39        let binding = parse_index(source, &mut cursor, "binding");
40        expect(source, &mut cursor, ")");
41        skip_space(source, &mut cursor);
42        expect(source, &mut cursor, "var<");
43        let access = read_until(source, &mut cursor, '>');
44        skip_space(source, &mut cursor);
45        let declaration = read_until(source, &mut cursor, ':');
46        let name = declaration.trim().to_owned();
47        assert!(!name.is_empty(), "every shader binding needs a name");
48        skip_space(source, &mut cursor);
49        let element = element_of(&read_until(source, &mut cursor, ';'));
50        assert!(
51            !bindings
52                .iter()
53                .any(|entry: &ShaderBinding| entry.group == group && entry.binding == binding),
54            "the shader declares binding {binding} of group {group} twice"
55        );
56        assert!(
57            !bindings
58                .iter()
59                .any(|entry: &ShaderBinding| entry.group == group && entry.name == name),
60            "the shader declares the binding {name:?} of group {group} twice"
61        );
62        bindings.push(ShaderBinding {
63            group,
64            binding,
65            kind: kind_of(&access),
66            name,
67            element,
68        });
69    }
70    bindings
71}
72
73fn element_of(declaration: &str) -> String {
74    let declared = declaration.trim();
75    let element = match declared.strip_prefix("array<") {
76        Some(inner) => {
77            let inner = inner.strip_suffix('>').unwrap_or_else(|| {
78                panic!("the shader declares the malformed array type {declared:?}")
79            });
80            match inner.rsplit_once(',') {
81                Some((element, _)) => element,
82                None => inner,
83            }
84        }
85        None => declared,
86    };
87    let element = match element.strip_prefix("atomic<") {
88        Some(inner) => inner.strip_suffix('>').unwrap_or_else(|| {
89            panic!("the shader declares the malformed atomic type {declared:?}")
90        }),
91        None => element,
92    };
93    assert!(
94        !element.is_empty() && !element.contains(' '),
95        "the shader declares the malformed binding type {declared:?}"
96    );
97    element.to_owned()
98}
99
100fn kind_of(access: &str) -> BindingKind {
101    let fields = access
102        .split(',')
103        .map(str::trim)
104        .filter(|field| !field.is_empty())
105        .collect::<Vec<_>>();
106    match fields[..] {
107        ["uniform"] => BindingKind::Uniform,
108        ["storage", "read"] => BindingKind::ReadOnlyStorage,
109        ["storage", "read_write"] => BindingKind::ReadWriteStorage,
110        _ => panic!("the shader declares an unknown binding access {access:?}"),
111    }
112}
113
114fn parse_index(source: &str, cursor: &mut usize, what: &str) -> u32 {
115    let start = *cursor;
116    while source
117        .as_bytes()
118        .get(*cursor)
119        .is_some_and(u8::is_ascii_digit)
120    {
121        *cursor += 1;
122    }
123    source[start..*cursor]
124        .parse()
125        .unwrap_or_else(|_| panic!("every shader binding needs a numeric {what} index"))
126}
127
128fn read_until(source: &str, cursor: &mut usize, terminator: char) -> String {
129    let start = *cursor;
130    let end = source[start..]
131        .find(terminator)
132        .unwrap_or_else(|| panic!("the shader binding misses a {terminator:?}"));
133    *cursor = start + end + terminator.len_utf8();
134    source[start..start + end].to_owned()
135}
136
137fn expect(source: &str, cursor: &mut usize, expected: &str) {
138    assert!(
139        source[*cursor..].starts_with(expected),
140        "the shader binding must declare {expected:?}"
141    );
142    *cursor += expected.len();
143}
144
145fn skip_space(source: &str, cursor: &mut usize) {
146    while source[*cursor..].starts_with(char::is_whitespace) {
147        *cursor += 1;
148    }
149}