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}