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}