mister_fpga/
framebuffer.rs

1use bitfield::bitfield;
2use image::{DynamicImage, RgbImage};
3use simple_endian::BigEndian;
4use tracing::debug;
5
6use cyclone_v::memory::{DevMemMemoryMapper, MemoryMapper};
7
8pub const FB_BASE_ADDRESS: usize = 0x2000_0000;
9pub const BUFFER_SIZE: usize = 2048 * 1024 * 3 * 4;
10
11pub const SCALER_FB_TYPE: u8 = 0x01;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14#[repr(u8)]
15pub enum ScalerPixelFormat {
16    /// 16-bit RGB565.
17    RGB16 = 0,
18
19    /// 24-bit RGB888.
20    RGB24 = 1,
21
22    /// 32-bit RGBA8888.
23    /// The alpha channel is ignored.
24    RGBA32 = 2,
25
26    INVALID = 0xFF,
27}
28
29impl From<u8> for ScalerPixelFormat {
30    fn from(value: u8) -> Self {
31        match value {
32            0 => ScalerPixelFormat::RGB16,
33            1 => ScalerPixelFormat::RGB24,
34            2 => ScalerPixelFormat::RGBA32,
35            _ => ScalerPixelFormat::INVALID,
36        }
37    }
38}
39
40bitfield! {
41    #[derive(Clone, Copy, PartialEq, Eq)]
42    pub struct ScalerAttributes(u16);
43    impl Debug;
44    u8;
45
46    pub interlaced, _: 0;
47    pub field_number, _: 1;
48    pub horizontal_downscaled, _: 2;
49    pub vertical_downscaled, _: 3;
50
51    /// True if triple buffered.
52    pub triple_buffered, _: 4;
53
54    /// A Frame counter in the scaler image header. Although this is
55    /// named "counter", it is more like a checksum and its value will
56    /// not necessarily increment with each frame.
57    pub frame_counter, _: 5, 3;
58}
59
60impl From<u16> for ScalerAttributes {
61    fn from(value: u16) -> Self {
62        ScalerAttributes(value)
63    }
64}
65
66/// An internal type to represent the framebuffer header.
67#[derive(Debug, Copy, Clone)]
68#[repr(C)]
69pub(crate) struct FbHeader {
70    ty: u8,
71    scaler_pixel_format: u8,
72    header_len: BigEndian<u16>,
73    attributes: BigEndian<u16>,
74    width: BigEndian<u16>,
75    height: BigEndian<u16>,
76    line: BigEndian<u16>,
77    output_width: BigEndian<u16>,
78    output_height: BigEndian<u16>,
79}
80
81impl FbHeader {
82    #[inline]
83    pub unsafe fn from_memory(memory: *const u8) -> Option<Self> {
84        let header = (memory as *const FbHeader).read_volatile();
85        if header.ty == SCALER_FB_TYPE {
86            Some(header)
87        } else {
88            None
89        }
90    }
91
92    #[allow(unused)]
93    pub fn frame_checksum(&self) -> u8 {
94        self.attributes().frame_counter()
95    }
96
97    #[inline]
98    pub fn scaler_pixel_format(&self) -> ScalerPixelFormat {
99        self.scaler_pixel_format.into()
100    }
101
102    #[inline]
103    pub fn header_len(&self) -> u16 {
104        self.header_len.into()
105    }
106
107    #[inline]
108    pub fn attributes(&self) -> ScalerAttributes {
109        let bytes: u16 = self.attributes.into();
110        ScalerAttributes::from(bytes)
111    }
112
113    #[inline]
114    pub fn width(&self) -> u16 {
115        self.width.into()
116    }
117
118    #[inline]
119    pub fn height(&self) -> u16 {
120        self.height.into()
121    }
122
123    #[inline]
124    pub fn line(&self) -> u16 {
125        self.line.into()
126    }
127
128    #[allow(unused)]
129    pub fn output_width(&self) -> u16 {
130        self.output_width.into()
131    }
132
133    #[allow(unused)]
134    pub fn output_height(&self) -> u16 {
135        self.output_height.into()
136    }
137}
138
139#[derive(Debug, Clone, Copy)]
140pub enum FramebufferType {
141    Single,
142    TripleSmall,
143    TripleLarge,
144}
145
146impl FramebufferType {
147    #[inline]
148    pub(crate) fn offset_of(&self, index: u8) -> Option<usize> {
149        match (self, index) {
150            (_, 0) => Some(0),
151            (FramebufferType::TripleSmall, 1) => Some(0x0020_0000),
152            (FramebufferType::TripleSmall, 2) => Some(0x0040_0000),
153            (FramebufferType::TripleLarge, 1) => Some(0x0080_0000),
154            (FramebufferType::TripleLarge, 2) => Some(0x0100_0000),
155            _ => None,
156        }
157    }
158}
159
160/// An iterator that waits a frame.
161pub struct FrameIter {
162    frame_counters: [*const u8; 3],
163}
164
165impl FrameIter {
166    pub fn new<M: MemoryMapper>(framebuffer: &FpgaFramebuffer<M>) -> Self {
167        let header0 = framebuffer.offset_of(0).unwrap();
168        let header1 = framebuffer.offset_of(1).unwrap_or(header0);
169        let header2 = framebuffer.offset_of(2).unwrap_or(header0);
170
171        unsafe {
172            let ptr0 = framebuffer.memory.as_ptr::<u8>().add(header0).add(5);
173            let ptr1 = framebuffer.memory.as_ptr::<u8>().add(header1).add(5);
174            let ptr2 = framebuffer.memory.as_ptr::<u8>().add(header2).add(5);
175
176            let frame_counters = [ptr0, ptr1, ptr2];
177
178            Self { frame_counters }
179        }
180    }
181}
182
183impl Iterator for FrameIter {
184    type Item = ();
185
186    fn next(&mut self) -> Option<Self::Item> {
187        unsafe {
188            let last: u8 = self.frame_counters.iter().map(|f| f.read_volatile()).sum();
189
190            loop {
191                let current: u8 = self.frame_counters.iter().map(|f| f.read_volatile()).sum();
192                if current != last {
193                    break;
194                }
195            }
196
197            Some(())
198        }
199    }
200}
201
202#[derive(Debug, Clone, Copy)]
203pub struct FpgaFramebuffer<M: MemoryMapper> {
204    memory: M,
205
206    ty_: Option<FramebufferType>,
207}
208
209impl Default for FpgaFramebuffer<DevMemMemoryMapper> {
210    fn default() -> Self {
211        // In MiSTer there is an alignment of the address to the page size.
212        // We know the page size in advance, so we don't need to calculate
213        // it.
214        let address = FB_BASE_ADDRESS;
215        let size = BUFFER_SIZE;
216        let mapper =
217            DevMemMemoryMapper::create(address, size).expect("Could not mmap framebuffer.");
218
219        Self::new(mapper).unwrap()
220    }
221}
222
223impl<M: MemoryMapper> FpgaFramebuffer<M> {
224    fn new(memory: M) -> Result<Self, &'static str> {
225        Ok(Self { memory, ty_: None })
226    }
227
228    pub(crate) fn update_type_from_core(&mut self) {
229        let first = unsafe { self.header_offset(0) };
230        self.ty_ = if !first
231            .map(|h| h.attributes().triple_buffered())
232            .unwrap_or_default()
233        {
234            Some(FramebufferType::Single)
235        } else {
236            let (small, large) = unsafe {
237                (
238                    self.header_offset(FramebufferType::TripleSmall.offset_of(1).unwrap()),
239                    self.header_offset(FramebufferType::TripleLarge.offset_of(1).unwrap()),
240                )
241            };
242
243            match (small, large) {
244                (_, Some(_)) => Some(FramebufferType::TripleLarge),
245                (Some(_), None) => Some(FramebufferType::TripleSmall),
246                _ => None,
247            }
248        }
249    }
250
251    /// Unsafely acquire the framebuffer header from an offset in memory.
252    unsafe fn header_offset(&self, offset: usize) -> Option<FbHeader> {
253        FbHeader::from_memory(self.memory.as_ptr::<u8>().add(offset))
254    }
255
256    pub(crate) fn offset_of(&self, index: u8) -> Option<usize> {
257        self.ty_.and_then(|ty| ty.offset_of(index))
258    }
259
260    #[allow(unused)]
261    pub(crate) fn header(&self, index: u8) -> Option<FbHeader> {
262        self.offset_of(index)
263            .and_then(|offset| unsafe { self.header_offset(offset) })
264    }
265
266    fn first_header(&self) -> FbHeader {
267        unsafe { self.header_offset(0).unwrap() }
268    }
269
270    pub fn write(&mut self, data: &[u8]) -> Result<(), String> {
271        let header_len = self.first_header().header_len() as usize;
272        self.memory
273            .as_mut_range(header_len..(header_len + data.len()))
274            .copy_from_slice(data);
275
276        Ok(())
277    }
278
279    pub fn take_screenshot(&self) -> Result<DynamicImage, String> {
280        // Bytes are in big endian, but ARM is in little endian.
281        let header = self.first_header();
282
283        debug!("Header data: {:?}", header);
284
285        let height = header.height() as usize;
286        let width = header.width() as usize;
287        let line = header.line() as usize;
288        let start = self.memory.as_ptr::<u8>();
289        let fb = unsafe {
290            std::slice::from_raw_parts(start.add(header.header_len() as usize), line * height * 3)
291        };
292
293        // TODO: add support for RGBA and RGB565.
294        let mut img = match header.scaler_pixel_format() {
295            ScalerPixelFormat::RGB16 => {
296                return Err("RGB565 not supported.".to_string());
297            }
298            ScalerPixelFormat::RGB24 => RgbImage::new(width as u32, height as u32),
299            ScalerPixelFormat::RGBA32 => {
300                return Err("RGBA32 not supported.".to_string());
301            }
302            ScalerPixelFormat::INVALID => {
303                return Err("Invalid Scaler PixelFormat.".to_string());
304            }
305        };
306
307        for y in 0..height {
308            let line = &fb[y * line..y * line + width * 3];
309            img.get_mut(y * width * 3..y * width * 3 + width * 3)
310                .unwrap()
311                .copy_from_slice(line);
312        }
313
314        Ok(DynamicImage::ImageRgb8(img))
315    }
316}