iridia_exec/geometry.rs
1//! A geometry program on the CPU: what the input assembler does before it and what a draw does around it, since WGSL
2//! has no geometry stage. [`Topology`] assembles a draw's vertices into the primitives the program reads (with the
3//! adjacent vertices of the `*Adjacency` topologies), [`GeometryShader::draw`] runs the program for every primitive and
4//! every one of its instances and returns the strips it emitted, and [`Strip::primitives`] assembles a strip's vertices
5//! into the points, lines and triangles they make.
6//!
7//! A vertex is one register per input varying of the program, in the order of its interface (the names are
8//! [`GeometryShader::inputs`]); an emitted vertex is one register per output varying ([`GeometryShader::outputs`]).
9//! The program is the function-form [`Program`] of [`iridia::stage_functions`], run one invocation at a time with
10//! [`Executable::function`].
11//!
12//! The assembly rules are the ones D3D11's input assembler and stream output follow, measured with the D3D11 reference
13//! of WARP (a probe program streamed the draw's vertices' numbers out through a geometry shader
14//! that writes each input vertex out): a triangle strip's odd primitives swap their last two vertices, so that every
15//! triangle keeps its winding; a triangle strip with adjacency is numbered as [`Topology::TriangleStripAdjacency`]
16//! says; `SV_PrimitiveID` counts the primitives of the draw from 0; the instances of a primitive run in order before the
17//! next primitive's. A draw with a restart index (an index list holding the value that cuts a strip) is refused by
18//! [`GeometryShader::draw_indexed`]: what the assembler makes of the vertices around a cut is not measured.
19
20use super::pixel::SlotBackend;
21use super::stage::{Bindings, Constants};
22use super::{Bound, Executable, Runner, Scratch};
23use crate::Sampler;
24use iridia::{FunctionSig, GeometryFacts, GsInput, GsOutput, Program, StageFacts, stage_functions};
25
26/// The values of one vertex: a register (four 32-bit lanes) for each varying.
27pub type Vertex = Vec<[u32; 4]>;
28
29/// How a draw's vertices make primitives (D3D's primitive topologies a geometry shader can follow).
30#[derive(Clone, Copy, Debug, PartialEq, Eq)]
31pub enum Topology {
32 /// Each vertex is a point.
33 PointList,
34 /// Every two vertices make a line.
35 LineList,
36 /// Each vertex after the first makes a line with the one before it.
37 LineStrip,
38 /// Every three vertices make a triangle.
39 TriangleList,
40 /// Each vertex after the second makes a triangle with the two before it (odd triangles swap their last two vertices).
41 TriangleStrip,
42 /// Every four vertices make a line with its two neighbours.
43 LineListAdjacency,
44 /// A strip of lines with an adjacent vertex at each end.
45 LineStripAdjacency,
46 /// Every six vertices make a triangle with its three neighbours.
47 TriangleListAdjacency,
48 /// A strip of triangles with adjacency, numbered as D3D11 numbers them.
49 TriangleStripAdjacency,
50}
51
52impl Topology {
53 /// The primitive a geometry program reads to be drawn with this topology.
54 pub fn input(self) -> GsInput {
55 match self {
56 Self::PointList => GsInput::Point,
57 Self::LineList | Self::LineStrip => GsInput::Line,
58 Self::TriangleList | Self::TriangleStrip => GsInput::Triangle,
59 Self::LineListAdjacency | Self::LineStripAdjacency => GsInput::LineAdjacency,
60 Self::TriangleListAdjacency | Self::TriangleStripAdjacency => GsInput::TriangleAdjacency,
61 }
62 }
63
64 /// The primitives of a draw of `vertices` vertices, each as the indices of its vertices in the order the geometry
65 /// program reads them (`GsInput::vertices` of them). Vertices that complete no primitive are ignored.
66 ///
67 /// - A strip's primitive `i` is `[i, i + 1, ...]`, a triangle strip's odd primitive `[i, i + 2, i + 1]` (its last two swapped).
68 /// - A line with adjacency is `[before, start, end, after]`.
69 /// - A triangle with adjacency is `[a, adjacent to a-b, b, adjacent to b-c, c, adjacent to c-a]`.
70 /// - A triangle strip with adjacency is the vertices `0, 2, 4, ...` of a triangle strip, and between them the
71 /// vertices that are adjacent to its edges (an odd vertex `2i + 1` the first's `0-2` edge): for `n = (vertices -
72 /// 4) / 2` primitives, primitive `i` (even) is `[2i, 2i - 2 (1 for i = 0), 2i + 2, 2i + 6, 2i + 4, 2i + 3]` and
73 /// primitive `i` (odd) `[2i, 2i + 3, 2i + 4, 2i + 6, 2i + 2, 2i - 2]`, the vertex `2i + 6` that the last
74 /// primitive does not have replaced by `2i + 5`.
75 #[allow(clippy::arithmetic_side_effects)] // indices stay below `vertices`, the length of a slice (under isize::MAX), and the formulas subtract only past the first primitive
76 pub fn primitives(self, vertices: usize) -> Vec<Vec<usize>> {
77 let list = |size: usize| -> Vec<Vec<usize>> {
78 (0..vertices / size)
79 .map(|p| (p * size..(p + 1) * size).collect())
80 .collect()
81 };
82 let strip = |size: usize| -> Vec<Vec<usize>> {
83 (0..(vertices + 1).saturating_sub(size))
84 .map(|p| (p..p + size).collect())
85 .collect()
86 };
87 match self {
88 Self::PointList => list(1),
89 Self::LineList => list(2),
90 Self::LineStrip => strip(2),
91 Self::TriangleList => list(3),
92 Self::TriangleStrip => strip(3)
93 .into_iter()
94 .enumerate()
95 .map(|(p, mut t)| {
96 if p % 2 == 1 {
97 t.swap(1, 2);
98 }
99 t
100 })
101 .collect(),
102 Self::LineListAdjacency => list(4),
103 Self::LineStripAdjacency => strip(4),
104 Self::TriangleListAdjacency => list(6),
105 Self::TriangleStripAdjacency => {
106 let count = vertices.saturating_sub(4) / 2;
107 (0..count)
108 .map(|i| {
109 let last = i + 1 == count;
110 let after = if last { 2 * i + 5 } else { 2 * i + 6 };
111 if i % 2 == 0 {
112 vec![
113 2 * i,
114 if i == 0 { 1 } else { 2 * i - 2 },
115 2 * i + 2,
116 after,
117 2 * i + 4,
118 2 * i + 3,
119 ]
120 } else {
121 vec![2 * i, 2 * i + 3, 2 * i + 4, after, 2 * i + 2, 2 * i - 2]
122 }
123 })
124 .collect()
125 }
126 }
127 }
128}
129
130impl From<GsOutput> for Topology {
131 /// The topology of the strips a program's output makes.
132 fn from(output: GsOutput) -> Self {
133 match output {
134 GsOutput::PointList => Self::PointList,
135 GsOutput::LineStrip => Self::LineStrip,
136 GsOutput::TriangleStrip => Self::TriangleStrip,
137 }
138 }
139}
140
141/// The vertices an invocation emitted between two cuts, with the primitive and the instance that emitted them.
142#[derive(Clone, Debug, PartialEq, Eq)]
143pub struct Strip {
144 /// The primitive of the draw that emitted the vertices, counted from 0 (`SV_PrimitiveID`).
145 pub primitive: u32,
146 /// The instance of that primitive.
147 pub instance: u32,
148 /// The emitted vertices, one register per output varying.
149 pub vertices: Vec<Vertex>,
150}
151
152impl Strip {
153 /// The primitives the strip makes under the program's output kind, as indices into [`Strip::vertices`]: every
154 /// point, every pair of consecutive vertices, every triple (an odd triangle's last two swapped to keep the
155 /// winding). A strip with too few vertices makes none.
156 pub fn primitives(&self, output: GsOutput) -> Vec<Vec<usize>> {
157 Topology::from(output).primitives(self.vertices.len())
158 }
159}
160
161/// A geometry program ready to run.
162#[derive(Debug)]
163pub struct GeometryShader {
164 program: Program,
165 executable: Executable,
166 signature: FunctionSig,
167 facts: GeometryFacts,
168 inputs: usize,
169 outputs: usize,
170 bindings: Bindings,
171}
172
173impl GeometryShader {
174 /// Prepare `program`, a geometry program, to run.
175 pub fn new(program: Program) -> Result<Self, String> {
176 let StageFacts::Geometry(facts) = program.interface.facts else {
177 return Err(format!("a {:?} program is not a geometry program", program.stage));
178 };
179 let signature = stage_functions(&program.interface)?
180 .and_then(|mut f| f.pop())
181 .ok_or("a geometry program without its function")?;
182 let executable = Executable::function(&program, "main").map_err(|e| e.to_string())?;
183 let (inputs, outputs) = (program.interface.inputs.len(), program.interface.outputs.len());
184 let bindings = Bindings::new(&program)?;
185 Ok(Self {
186 program,
187 executable,
188 signature,
189 facts,
190 inputs,
191 outputs,
192 bindings,
193 })
194 }
195
196 /// The geometry program the shader runs.
197 pub fn program(&self) -> &Program {
198 &self.program
199 }
200
201 /// What the program states: the primitive it reads, what it emits, the most vertices an invocation emits and its
202 /// number of instances.
203 pub fn facts(&self) -> GeometryFacts {
204 self.facts
205 }
206
207 /// The names of the input varyings: the registers of a [`Vertex`] given to the program, in order.
208 pub fn inputs(&self) -> impl Iterator<Item = &str> {
209 self.signature
210 .arguments
211 .iter()
212 .take(self.inputs)
213 .map(|a| a.name.as_str())
214 }
215
216 /// The names of the output varyings: the registers of the vertices of a [`Strip`], in order.
217 pub fn outputs(&self) -> impl Iterator<Item = &str> {
218 self.signature
219 .result
220 .iter()
221 .take(self.outputs)
222 .map(|a| a.name.as_str())
223 }
224
225 /// Run the program for the primitive of the draw numbered `primitive` whose vertices are `vertices` (as many as
226 /// the program's input primitive has, in the order [`Topology::primitives`] gives), as instance `instance`, and
227 /// return the strips it emitted. `constants` holds the rows of the constant buffers by (register, space); image
228 /// and buffer access go to `sampler` by bind slot.
229 pub fn run(
230 &self,
231 constants: &Constants,
232 sampler: &mut dyn Sampler,
233 vertices: &[&Vertex],
234 primitive: u32,
235 instance: u32,
236 ) -> Result<Vec<Strip>, String> {
237 let bound = self.bind(constants)?;
238 let mut backend = self.backend(sampler);
239 self.invoke(
240 &bound,
241 &mut backend,
242 Scratch::new(&self.executable),
243 vertices,
244 primitive,
245 instance,
246 )
247 .map(|(strips, _)| strips)
248 }
249
250 /// Draw `vertices` (the output of a vertex program) as `topology`: assemble the primitives, and run the program for
251 /// each of them and each of its instances. The strips are in the order of the primitives, the instances of a
252 /// primitive in order, then the strips an instance cut. The topology must be the kind of primitive the program
253 /// reads.
254 pub fn draw(
255 &self,
256 constants: &Constants,
257 sampler: &mut dyn Sampler,
258 vertices: &[Vertex],
259 topology: Topology,
260 ) -> Result<Vec<Strip>, String> {
261 if topology.input() != self.facts.input {
262 return Err(format!(
263 "a draw of {topology:?} feeds the geometry program primitives of {:?}, which reads {:?}",
264 topology.input(),
265 self.facts.input
266 ));
267 }
268 let bound = self.bind(constants)?;
269 let mut backend = self.backend(sampler);
270 let mut scratch = Scratch::new(&self.executable);
271 let mut strips = Vec::new();
272 for (primitive, indices) in topology.primitives(vertices.len()).iter().enumerate() {
273 let primitive_vertices: Vec<&Vertex> = indices
274 .iter()
275 .map(|&i| {
276 vertices.get(i).ok_or_else(|| {
277 format!(
278 "primitive {primitive} names vertex {i}, past the {} given",
279 vertices.len()
280 )
281 })
282 })
283 .collect::<Result<_, _>>()?;
284 for instance in 0..self.facts.instances.max(1) {
285 let (emitted, kept) = self.invoke(
286 &bound,
287 &mut backend,
288 scratch,
289 &primitive_vertices,
290 u32::try_from(primitive).map_err(|e| e.to_string())?,
291 instance,
292 )?;
293 scratch = kept;
294 strips.extend(emitted);
295 }
296 }
297 Ok(strips)
298 }
299
300 /// [`GeometryShader::draw`] of an indexed draw: the vertices at `indices`, in order. When the draw has a restart
301 /// index (`restart`, the value that cuts a strip: 0xFFFF or 0xFFFFFFFF in D3D), an index list that holds it is
302 /// refused: the primitives around a cut are not assembled.
303 pub fn draw_indexed(
304 &self,
305 constants: &Constants,
306 sampler: &mut dyn Sampler,
307 vertices: &[Vertex],
308 indices: &[u32],
309 restart: Option<u32>,
310 topology: Topology,
311 ) -> Result<Vec<Strip>, String> {
312 if let Some(cut) = restart
313 && let Some(at) = indices.iter().position(|&i| i == cut)
314 {
315 return Err(format!(
316 "index {at} is the restart index {cut:#x}: a draw with a strip restart is not assembled (the \
317 primitives around a cut are not measured on the D3D reference)"
318 ));
319 }
320 let drawn = indices
321 .iter()
322 .map(|&i| {
323 vertices
324 .get(i as usize)
325 .cloned()
326 .ok_or_else(|| format!("index {i} names a vertex past the {} given", vertices.len()))
327 })
328 .collect::<Result<Vec<_>, _>>()?;
329 self.draw(constants, sampler, &drawn, topology)
330 }
331
332 fn bind<'a>(&self, constants: &'a Constants) -> Result<Bound<'a>, String> {
333 self.bindings.bind(&self.executable, constants)
334 }
335
336 fn backend<'a>(&'a self, sampler: &'a mut dyn Sampler) -> SlotBackend<'a> {
337 self.bindings.backend(sampler)
338 }
339
340 /// One invocation: the registers of the arguments (each input varying for each vertex of the primitive, then the
341 /// primitive and the instance), and the emitted vertices split into strips at the flags the program sets.
342 fn invoke(
343 &self,
344 bound: &Bound<'_>,
345 backend: &mut SlotBackend<'_>,
346 scratch: Scratch<1>,
347 vertices: &[&Vertex],
348 primitive: u32,
349 instance: u32,
350 ) -> Result<(Vec<Strip>, Scratch<1>), String> {
351 let size = self.facts.input.vertices() as usize;
352 if vertices.len() != size {
353 return Err(format!(
354 "{} vertices given for a primitive of {:?}, which has {size}",
355 vertices.len(),
356 self.facts.input
357 ));
358 }
359 let mut arguments = Vec::with_capacity(self.inputs.saturating_mul(size).saturating_add(2));
360 for varying in 0..self.inputs {
361 for (k, vertex) in vertices.iter().enumerate() {
362 arguments.push(*vertex.get(varying).ok_or_else(|| {
363 format!(
364 "vertex {k} has {} registers, the program reads {} ({})",
365 vertex.len(),
366 self.inputs,
367 self.inputs().collect::<Vec<_>>().join(", ")
368 )
369 })?);
370 }
371 }
372 arguments.push([primitive, 0, 0, 0]);
373 arguments.push([instance, 0, 0, 0]);
374 let mut runner = Runner::<1>::with_scratch(&self.executable, bound, backend, None, scratch);
375 let [out] = runner.run([&arguments])?;
376 let scratch = runner.into_scratch();
377 let max = self.facts.max_vertices as usize;
378 let count_at = self.outputs.saturating_mul(max);
379 let want = count_at.saturating_add(1).saturating_add(max);
380 if out.values.len() != want {
381 return Err(format!(
382 "the program returned {} values, its signature has {want}",
383 out.values.len()
384 ));
385 }
386 let count = out.values.get(count_at).map_or(0, |r| r[0]) as usize;
387 if count > max {
388 return Err(format!(
389 "the invocation emitted {count} vertices, past the {max} it declares"
390 ));
391 }
392 let mut strips: Vec<Strip> = Vec::new();
393 for k in 0..count {
394 let cut = out
395 .values
396 .get(count_at.saturating_add(1).saturating_add(k))
397 .ok_or_else(|| format!("the program returned no strip flag for vertex {k}"))?;
398 if k == 0 || cut[0] != 0 {
399 strips.push(Strip {
400 primitive,
401 instance,
402 vertices: Vec::new(),
403 });
404 }
405 let vertex = (0..self.outputs)
406 .map(|o| {
407 out.values
408 .get(o.saturating_mul(max).saturating_add(k))
409 .copied()
410 .ok_or_else(|| format!("the program returned no output {o} for vertex {k}"))
411 })
412 .collect::<Result<_, _>>()?;
413 if let Some(strip) = strips.last_mut() {
414 strip.vertices.push(vertex);
415 }
416 }
417 Ok((strips, scratch))
418 }
419}