embedded_3dgfx/
command_buffer.rs1use heapless::Vec;
2
3use crate::{
4 DrawPrimitive,
5 error::{BudgetKind, RenderError},
6};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub struct PrimitiveHeader {
10 pub min_x: i32,
11 pub min_y: i32,
12 pub max_x: i32,
13 pub max_y: i32,
14}
15
16impl PrimitiveHeader {
17 pub fn new(min_x: i32, min_y: i32, max_x: i32, max_y: i32) -> Self {
18 Self {
19 min_x,
20 min_y,
21 max_x,
22 max_y,
23 }
24 }
25
26 pub fn from_primitive(primitive: &DrawPrimitive) -> Self {
27 let (min_x, min_y, max_x, max_y) = primitive.bounds();
28 Self {
29 min_x,
30 min_y,
31 max_x,
32 max_y,
33 }
34 }
35
36 pub fn bounds(&self) -> (i32, i32, i32, i32) {
37 (self.min_x, self.min_y, self.max_x, self.max_y)
38 }
39}
40
41#[derive(Debug, Clone)]
42pub enum RenderCommand {
43 ClearColor(embedded_graphics_core::pixelcolor::Rgb565),
44 ClearDepth(crate::ZDepth),
45 Draw(DrawPrimitive),
46}
47
48impl RenderCommand {
49 pub fn bounds(&self) -> Option<(i32, i32, i32, i32)> {
50 match self {
51 RenderCommand::Draw(primitive) => Some(primitive.bounds()),
52 _ => None,
53 }
54 }
55}
56
57pub struct CommandBuffer<const MAX: usize> {
58 commands: Vec<RenderCommand, MAX>,
59}
60
61impl<const MAX: usize> CommandBuffer<MAX> {
62 pub const fn new() -> Self {
63 Self {
64 commands: Vec::new(),
65 }
66 }
67
68 pub fn clear(&mut self) {
69 self.commands.clear();
70 }
71
72 pub fn len(&self) -> usize {
73 self.commands.len()
74 }
75
76 pub fn is_empty(&self) -> bool {
77 self.commands.is_empty()
78 }
79
80 pub fn push(&mut self, cmd: RenderCommand) -> Result<(), RenderError> {
81 self.commands.push(cmd).map_err(|_| {
82 RenderError::OutOfBudget(BudgetKind::DrawPrimitives {
83 attempted: self.commands.len() + 1,
84 max: MAX,
85 })
86 })
87 }
88
89 pub fn iter(&self) -> core::slice::Iter<'_, RenderCommand> {
90 self.commands.iter()
91 }
92
93 pub fn get(&self, index: usize) -> Option<&RenderCommand> {
94 self.commands.get(index)
95 }
96}
97
98impl<const MAX: usize> Default for CommandBuffer<MAX> {
99 fn default() -> Self {
100 Self::new()
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 use embedded_graphics_core::pixelcolor::Rgb565;
108 use nalgebra::Point2;
109
110 #[test]
111 fn new_buffer_starts_empty() {
112 let buf: CommandBuffer<4> = CommandBuffer::new();
113 assert_eq!(buf.len(), 0);
114 assert!(buf.is_empty());
115 }
116
117 #[test]
118 fn push_and_get_roundtrip() {
119 let mut buf: CommandBuffer<4> = CommandBuffer::new();
120 buf.push(RenderCommand::Draw(DrawPrimitive::ColoredPoint(
121 Point2::new(3, 7),
122 Rgb565::new(31, 0, 0),
123 )))
124 .unwrap();
125 assert_eq!(buf.len(), 1);
126 assert!(matches!(
127 buf.get(0),
128 Some(RenderCommand::Draw(DrawPrimitive::ColoredPoint(_, _)))
129 ));
130 }
131
132 #[test]
133 fn push_over_capacity_returns_budget_error() {
134 let mut buf: CommandBuffer<1> = CommandBuffer::new();
135 buf.push(RenderCommand::ClearDepth(0)).unwrap();
136 let err = buf
137 .push(RenderCommand::ClearDepth(1))
138 .expect_err("overflow must fail");
139 assert_eq!(
140 err,
141 RenderError::OutOfBudget(BudgetKind::DrawPrimitives {
142 attempted: 2,
143 max: 1
144 })
145 );
146 }
147}