extern crate alloc;
use alloc::vec::Vec;
use crate::blit::{PixelFmt, Rect};
use crate::hwcore::surface::{BackBuffer, BorrowedForDma, InFlight, pixel_size};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MockOp {
Fill {
dst_addr: u32,
dst_stride: u32,
format: PixelFmt,
area: Rect,
color: u32,
},
Blit {
src: usize,
src_stride: u32,
dst_addr: u32,
dst_stride: u32,
dst_pos: (i32, i32),
width: u32,
height: u32,
format: PixelFmt,
},
}
#[derive(Debug, Default)]
pub struct MockBlitter {
history: Vec<MockOp>,
}
impl MockBlitter {
pub fn new() -> Self {
Self {
history: Vec::new(),
}
}
pub fn start_fill_typed<'b, 'fb>(
&'b mut self,
dst: BorrowedForDma<'b, BackBuffer<'fb>>,
area: Rect,
color: u32,
) -> InFlight<'b, BackBuffer<'fb>> {
let stride = dst.stride_bytes();
let bpp = pixel_size(dst.format()) as u32;
let base = dst.dma_addr().raw();
let dst_addr = base + (area.y as u32) * stride + (area.x as u32) * bpp;
self.history.push(MockOp::Fill {
dst_addr,
dst_stride: stride,
format: dst.format(),
area,
color,
});
InFlight::new(dst)
}
pub fn start_blit_typed<'b, 'fb>(
&'b mut self,
src: *const u8,
src_stride: u32,
dst: BorrowedForDma<'b, BackBuffer<'fb>>,
dst_pos: (i32, i32),
width: u32,
height: u32,
) -> InFlight<'b, BackBuffer<'fb>> {
self.history.push(MockOp::Blit {
src: src as usize,
src_stride,
dst_addr: dst.dma_addr().raw(),
dst_stride: dst.stride_bytes(),
dst_pos,
width,
height,
format: dst.format(),
});
InFlight::new(dst)
}
pub fn history(&self) -> &[MockOp] {
&self.history
}
pub fn last(&self) -> Option<&MockOp> {
self.history.last()
}
pub fn clear(&mut self) {
self.history.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hwcore::addr::{PhysAddr, SDRAM_BANK2_BASE};
use crate::hwcore::surface::{BackBuffer, FrameBuffer};
fn fb() -> FrameBuffer {
unsafe {
FrameBuffer::from_phys(
PhysAddr::new(SDRAM_BANK2_BASE),
480,
272,
480 * 4,
PixelFmt::Argb8888,
)
}
}
#[test]
fn fill_records_destination_address_and_geometry() {
let mut fb = fb();
let mut back = BackBuffer::wrap(&mut fb);
let mut blitter = MockBlitter::new();
let dst = back.dma_dst();
let inflight = blitter.start_fill_typed(
dst,
Rect {
x: 10,
y: 20,
w: 100,
h: 100,
},
0xFF11_2233,
);
let _ = inflight.into_borrow();
let op = blitter.last().unwrap().clone();
match op {
MockOp::Fill {
dst_addr,
dst_stride,
area,
color,
..
} => {
assert_eq!(dst_addr, SDRAM_BANK2_BASE + 20 * 1920 + 10 * 4);
assert_eq!(dst_stride, 480 * 4);
assert_eq!(
area,
Rect {
x: 10,
y: 20,
w: 100,
h: 100,
}
);
assert_eq!(color, 0xFF11_2233);
}
other => panic!("expected Fill, got {other:?}"),
}
}
#[test]
fn second_fill_appends_to_history() {
let mut fb = fb();
let mut blitter = MockBlitter::new();
for color in [0xFF11_0000, 0xFF00_FF00, 0xFF00_00FF] {
let mut back = BackBuffer::wrap(&mut fb);
let dst = back.dma_dst();
let _ = blitter
.start_fill_typed(
dst,
Rect {
x: 0,
y: 0,
w: 1,
h: 1,
},
color,
)
.into_borrow();
}
assert_eq!(blitter.history().len(), 3);
}
#[test]
fn clear_drops_history() {
let mut fb = fb();
let mut blitter = MockBlitter::new();
let mut back = BackBuffer::wrap(&mut fb);
let dst = back.dma_dst();
let _ = blitter
.start_fill_typed(
dst,
Rect {
x: 0,
y: 0,
w: 1,
h: 1,
},
0,
)
.into_borrow();
blitter.clear();
assert!(blitter.history().is_empty());
}
}