1use super::geometry::Vertex;
13use super::stage::{Bindings, Constants};
14use super::{Executable, error};
15use crate::{Error, Sampler};
16use iridia::{IoType, MeshFacts, MeshTopology, Program, StageFacts, mesh_vertex_io};
17
18#[derive(Clone, Debug, PartialEq, Eq)]
20pub struct Meshlet {
21 pub group: [u32; 3],
23 pub vertices: Vec<Vertex>,
25 pub primitives: Vec<Vec<u32>>,
27}
28
29#[derive(Debug)]
31pub struct MeshShader {
32 program: Program,
33 executable: Executable,
34 facts: MeshFacts,
35 outputs: Vec<(String, usize)>,
37 bindings: Bindings,
38}
39
40impl MeshShader {
41 pub fn new(program: Program) -> Result<Self, Error> {
43 let StageFacts::Mesh(facts) = program.interface.facts else {
44 return Err(error(format!(
45 "a {:?} program is not a mesh program",
46 program.stage
47 )));
48 };
49 let outputs = mesh_vertex_io(&program.interface)
50 .map_err(error)?
51 .into_iter()
52 .map(|item| match item.ty {
53 IoType::Value(v) => Ok((item.name, usize::from(v.width))),
54 _ => Err(error(format!(
55 "{}: a vertex output that is not a value",
56 item.name
57 ))),
58 })
59 .collect::<Result<Vec<_>, _>>()?;
60 if outputs.len() != program.interface.outputs.len() {
61 return Err(error(
62 "a mesh program whose clip or cull distances are carried together",
63 ));
64 }
65 let executable = Executable::new(&program)?;
66 let (_, words) = executable
67 .mesh_output
68 .ok_or_else(|| error("a mesh program without its output variable"))?;
69 let shader = Self {
70 bindings: Bindings::new(&program).map_err(error)?,
71 program,
72 executable,
73 facts,
74 outputs,
75 };
76 let layout = shader.layout();
77 if words as usize != layout.total {
78 return Err(error(format!(
79 "the output variable has {words} words, its vertices and primitives make {}",
80 layout.total
81 )));
82 }
83 Ok(shader)
84 }
85
86 pub fn program(&self) -> &Program {
88 &self.program
89 }
90
91 pub fn facts(&self) -> MeshFacts {
93 self.facts
94 }
95
96 pub fn group_size(&self) -> [u32; 3] {
98 self.executable.workgroup.unwrap_or([1, 1, 1])
99 }
100
101 pub fn outputs(&self) -> impl Iterator<Item = &str> {
103 self.outputs.iter().map(|(name, _)| name.as_str())
104 }
105
106 fn layout(&self) -> Layout {
108 let vertex: usize = self.outputs.iter().map(|(_, width)| width).sum();
109 let index = match self.facts.topology {
110 MeshTopology::Triangle => 3,
111 MeshTopology::Line => 2,
112 };
113 let (max_vertices, max_primitives) = (
114 self.facts.max_vertices as usize,
115 self.facts.max_primitives as usize,
116 );
117 Layout {
118 vertex,
119 index,
120 vertices_at: 2,
121 primitives_at: 2usize.saturating_add(max_vertices.saturating_mul(vertex)),
122 total: 2usize
123 .saturating_add(max_vertices.saturating_mul(vertex))
124 .saturating_add(max_primitives.saturating_mul(index)),
125 }
126 }
127
128 pub fn draw(
132 &self,
133 constants: &Constants,
134 sampler: &mut dyn Sampler,
135 groups: [u32; 3],
136 wave: u32,
137 ) -> Result<Vec<Meshlet>, Error> {
138 let bound = self.bindings.bind(&self.executable, constants).map_err(error)?;
139 let mut backend = self.bindings.backend(sampler);
140 let layout = self.layout();
141 let mut meshlets = Vec::new();
142 self.executable
143 .dispatch_mesh(&bound, &mut backend, groups, wave, &mut |group, words| {
144 meshlets.push(self.read(&layout, group, words)?);
145 Ok(())
146 })
147 .map_err(error)?;
148 Ok(meshlets)
149 }
150
151 fn read(&self, layout: &Layout, group: [u32; 3], words: &[u32]) -> Result<Meshlet, String> {
153 if words.len() != layout.total {
154 return Err(format!(
155 "{} words of output, expected {}",
156 words.len(),
157 layout.total
158 ));
159 }
160 let (Some(&vertex_count), Some(&primitive_count)) = (words.first(), words.get(1)) else {
161 return Err("an output variable without its counts".into());
162 };
163 let (vertex_count, primitive_count) = (vertex_count as usize, primitive_count as usize);
164 if vertex_count > self.facts.max_vertices as usize {
165 return Err(format!(
166 "the group set {vertex_count} vertices, past the {} it declares",
167 self.facts.max_vertices
168 ));
169 }
170 if primitive_count > self.facts.max_primitives as usize {
171 return Err(format!(
172 "the group set {primitive_count} primitives, past the {} it declares",
173 self.facts.max_primitives
174 ));
175 }
176 let mut vertices = Vec::with_capacity(vertex_count);
177 for k in 0..vertex_count {
178 let mut at = layout.vertices_at.saturating_add(k.saturating_mul(layout.vertex));
179 let mut registers = Vec::with_capacity(self.outputs.len());
180 for &(_, width) in &self.outputs {
181 let mut register = [0u32; 4];
182 let (Some(to), Some(from)) =
183 (register.get_mut(..width), words.get(at..at.saturating_add(width)))
184 else {
185 return Err(format!(
186 "vertex {k}: {width} words at word {at} of {}",
187 words.len()
188 ));
189 };
190 to.copy_from_slice(from);
191 at = at.saturating_add(width);
192 registers.push(register);
193 }
194 vertices.push(registers);
195 }
196 let mut primitives = Vec::with_capacity(primitive_count);
197 for k in 0..primitive_count {
198 let at = layout
199 .primitives_at
200 .saturating_add(k.saturating_mul(layout.index));
201 let Some(indices) = words
202 .get(at..at.saturating_add(layout.index))
203 .map(<[u32]>::to_vec)
204 else {
205 return Err(format!(
206 "primitive {k}: {} words at word {at} of {}",
207 layout.index,
208 words.len()
209 ));
210 };
211 if let Some(bad) = indices.iter().find(|&&i| i as usize >= vertex_count) {
212 return Err(format!(
213 "primitive {k} names vertex {bad}, past the {vertex_count} the group set"
214 ));
215 }
216 primitives.push(indices);
217 }
218 Ok(Meshlet {
219 group,
220 vertices,
221 primitives,
222 })
223 }
224}
225
226struct Layout {
228 vertex: usize,
230 index: usize,
232 vertices_at: usize,
233 primitives_at: usize,
234 total: usize,
235}