Skip to main content

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}