Skip to main content

embedded_3dgfx/pipeline/
mod.rs

1//! The graphics pipeline.
2//!
3//! Rendering is modelled as five ordered stages, each owning one module. A
4//! stage consumes the previous stage's output and nothing else:
5//!
6//! | # | Stage | Module | Input -> output |
7//! |---|-------|--------|-----------------|
8//! | 1 | [`Stage::Vertex`]    | [`vertex`]    | model-space mesh -> clip-space vertices |
9//! | 2 | [`Stage::Assemble`]  | [`assemble`]  | clip-space vertices -> screen-space [`DrawPrimitive`](assemble::primitive::DrawPrimitive) |
10//! | 3 | [`Stage::Rasterize`] | [`rasterize`] | screen primitives -> covered pixels |
11//! | 4 | [`Stage::Shade`]     | [`shade`]     | fragments -> `Rgb565` colours |
12//! | 5 | [`Stage::Output`]    | [`output`]    | framebuffer -> presented frame |
13//!
14//! [`command_buffer`] is the transport between the record half (stages 1-2) and
15//! the execute half (stages 3-5); [`renderer`] drives that execute half.
16//! Configuration shared by several stages lives in [`effects`].
17//!
18//! # Dependency direction
19//!
20//! A stage may depend on lower-numbered stages, [`effects`], and the crate-root
21//! `core` types — never on a higher-numbered stage. The one intentional
22//! exception is that [`rasterize`] invokes the fragment programs defined in
23//! [`shade`]: the shader interface is a *parameter* of the rasterizer, so
24//! `shade` sits below `rasterize` in the dependency order even though it runs
25//! afterwards in the data flow.
26
27pub mod assemble;
28pub mod command_buffer;
29pub mod effects;
30pub mod output;
31pub mod rasterize;
32pub mod renderer;
33pub mod shade;
34pub mod vertex;
35
36use embedded_graphics_core::pixelcolor::Rgb565;
37
38/// One ordered stage of the graphics pipeline.
39#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
40pub enum Stage {
41    /// Model-space geometry and camera transforms.
42    Vertex,
43    /// Screen-space primitive assembly.
44    Assemble,
45    /// Triangle / line / point rasterization.
46    Rasterize,
47    /// Fragment shading.
48    Shade,
49    /// Framebuffer writes and presentation.
50    Output,
51}
52
53impl Stage {
54    /// Every stage, in execution order.
55    pub const ORDER: [Stage; 5] = [
56        Stage::Vertex,
57        Stage::Assemble,
58        Stage::Rasterize,
59        Stage::Shade,
60        Stage::Output,
61    ];
62
63    /// Zero-based position of this stage in [`Stage::ORDER`].
64    #[must_use]
65    pub const fn index(self) -> usize {
66        self as usize
67    }
68
69    /// The stage that consumes this stage's output.
70    #[must_use]
71    pub const fn next(self) -> Option<Stage> {
72        match self {
73            Stage::Vertex => Some(Stage::Assemble),
74            Stage::Assemble => Some(Stage::Rasterize),
75            Stage::Rasterize => Some(Stage::Shade),
76            Stage::Shade => Some(Stage::Output),
77            Stage::Output => None,
78        }
79    }
80}
81
82/// Implemented by the principal type of each [`Stage`].
83pub trait StageKind {
84    /// Which pipeline stage this type belongs to.
85    const STAGE: Stage;
86}
87
88/// A homogeneous clip-space vertex, passed from [`vertex`] to [`assemble`].
89pub type ClipVertex = nalgebra::Vector4<f32>;
90
91/// The screen-space primitive handed from [`assemble`] to [`rasterize`].
92pub type ScreenPrimitive = assemble::primitive::DrawPrimitive;
93
94/// A shaded fragment colour.
95pub type FragmentColor = Rgb565;
96
97impl StageKind for vertex::camera::Camera {
98    const STAGE: Stage = Stage::Vertex;
99}
100impl StageKind for assemble::primitive::DrawPrimitive {
101    const STAGE: Stage = Stage::Assemble;
102}
103impl<'a> StageKind for rasterize::draw::state::RasterState<'a> {
104    const STAGE: Stage = Stage::Rasterize;
105}
106impl StageKind for shade::shader::FlatColorShader {
107    const STAGE: Stage = Stage::Shade;
108}
109impl StageKind for crate::error::DisplayError {
110    const STAGE: Stage = Stage::Output;
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn stage_order_is_sequential() {
119        assert_eq!(Stage::ORDER.len(), 5);
120        for pair in Stage::ORDER.windows(2) {
121            assert_eq!(pair[0].next(), Some(pair[1]));
122            assert!(pair[0] < pair[1]);
123        }
124        assert_eq!(Stage::Output.next(), None);
125        assert_eq!(Stage::Vertex.index(), 0);
126        assert_eq!(Stage::Output.index(), 4);
127    }
128
129    #[test]
130    fn principal_types_declare_their_stage() {
131        assert_eq!(vertex::camera::Camera::STAGE, Stage::Vertex);
132        assert_eq!(
133            <assemble::primitive::DrawPrimitive as StageKind>::STAGE,
134            Stage::Assemble
135        );
136        assert_eq!(
137            <shade::shader::FlatColorShader as StageKind>::STAGE,
138            Stage::Shade
139        );
140        assert_eq!(
141            <crate::error::DisplayError as StageKind>::STAGE,
142            Stage::Output
143        );
144    }
145}