orinium_browser/platform/renderer/
draw_sink.rs1use std::fmt;
15
16use crate::engine::renderer_model::DrawCommand;
17
18#[derive(Debug)]
20pub enum DrawError {
21 Surface(String),
23 Rejected(String),
25}
26
27impl fmt::Display for DrawError {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 match self {
30 Self::Surface(s) => write!(f, "Could not acquire a drawable: {s}"),
31 Self::Rejected(s) => write!(f, "Draw commands rejected: {s}"),
32 }
33 }
34}
35
36impl std::error::Error for DrawError {}
37
38pub trait DrawSink {
48 fn upload(&mut self, commands: &[DrawCommand]);
50
51 fn present(&mut self) -> Result<(), DrawError>;
53
54 fn resize(&mut self, size: (u32, u32));
56
57 fn set_scale_factor(&mut self, scale_factor: f64);
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[derive(Default)]
69 struct RecordingSink {
70 frames: Vec<Vec<DrawCommand>>,
71 pending: Vec<DrawCommand>,
72 size: (u32, u32),
73 scale_factor: f64,
74 present_err: Option<DrawError>,
75 }
76
77 impl DrawSink for RecordingSink {
78 fn upload(&mut self, commands: &[DrawCommand]) {
79 self.pending.extend_from_slice(commands);
80 }
81
82 fn present(&mut self) -> Result<(), DrawError> {
83 match self.present_err.take() {
84 Some(err) => Err(err),
85 None => {
86 self.frames.push(std::mem::take(&mut self.pending));
87 Ok(())
88 }
89 }
90 }
91
92 fn resize(&mut self, size: (u32, u32)) {
93 self.size = size;
94 }
95
96 fn set_scale_factor(&mut self, scale_factor: f64) {
97 self.scale_factor = scale_factor;
98 }
99 }
100
101 fn command(x: f32) -> DrawCommand {
102 DrawCommand::PushTransform {
103 transform: crate::engine::renderer_model::AffineTransform::translate(x, 0.0),
104 }
105 }
106
107 #[test]
108 fn upload_records_and_present_flushes() {
109 let mut sink = RecordingSink::default();
110
111 sink.upload(&[command(1.0)]);
112 assert!(sink.frames.is_empty(), "upload must not present");
113
114 sink.present().unwrap();
115 assert_eq!(sink.frames.len(), 1);
116 assert_eq!(sink.frames[0].len(), 1);
117 }
118
119 #[test]
120 fn several_uploads_accumulate_into_one_frame() {
121 let mut sink = RecordingSink::default();
122
123 sink.upload(&[command(1.0), command(2.0)]);
124 sink.upload(&[command(3.0)]);
125 sink.present().unwrap();
126
127 assert_eq!(sink.frames.len(), 1);
128 assert_eq!(sink.frames[0].len(), 3);
129 }
130
131 #[test]
132 fn present_starts_a_fresh_frame() {
133 let mut sink = RecordingSink::default();
134
135 sink.upload(&[command(1.0)]);
136 sink.present().unwrap();
137 sink.upload(&[command(2.0)]);
138 sink.present().unwrap();
139
140 assert_eq!(sink.frames.len(), 2);
141 assert_eq!(sink.frames[1].len(), 1);
142 }
143
144 #[test]
145 fn present_errors_surface_to_the_caller() {
146 let mut sink = RecordingSink {
147 present_err: Some(DrawError::Surface("lost".to_string())),
148 ..Default::default()
149 };
150
151 let err = sink.present().unwrap_err();
152 assert!(matches!(err, DrawError::Surface(_)));
153 assert!(err.to_string().contains("lost"));
154 }
155
156 #[test]
157 fn surface_changes_are_forwarded() {
158 let mut sink = RecordingSink::default();
159 sink.resize((1024, 768));
160 sink.set_scale_factor(2.0);
161
162 assert_eq!(sink.size, (1024, 768));
163 assert_eq!(sink.scale_factor, 2.0);
164 }
165}