new ui, control panel kinda works, display works, framebuffer works, serial write works. widget for registers works. next is serial input and the editor
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
use egui::Vec2;
|
||||
|
||||
use crate::{MemoryBank, Page, backend::memory::PhysAddr};
|
||||
|
||||
use super::Component;
|
||||
|
||||
pub struct FrameBuffer {
|
||||
width: usize,
|
||||
height: usize,
|
||||
addr: PhysAddr,
|
||||
mem: MemoryBank,
|
||||
pub buffer: Vec<u32>, // RGBA pixels
|
||||
visible: bool,
|
||||
|
||||
texture: Option<egui::TextureHandle>,
|
||||
}
|
||||
|
||||
impl FrameBuffer {
|
||||
pub fn new(width: u32, height: u32, addr: PhysAddr, mem: MemoryBank) -> Self {
|
||||
Self {
|
||||
width: width as usize,
|
||||
height: height as usize,
|
||||
addr,
|
||||
mem,
|
||||
buffer: vec![0u32; width as usize * height as usize],
|
||||
visible: true,
|
||||
|
||||
texture: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn width(&self) -> usize {
|
||||
self.width
|
||||
}
|
||||
pub fn height(&self) -> usize {
|
||||
self.height
|
||||
}
|
||||
pub fn visible(&mut self) -> &mut bool {
|
||||
&mut self.visible
|
||||
}
|
||||
|
||||
fn internal_read(&self) -> Vec<u32> {
|
||||
let byte_size = self.width * self.height * 4;
|
||||
let page_count = byte_size.div_ceil(Page::SIZE);
|
||||
|
||||
(0..page_count)
|
||||
.map(|i| {
|
||||
let page = self.mem.read_page(self.addr + i as u32 * 4096);
|
||||
page.as_slice().to_owned()
|
||||
})
|
||||
.flatten()
|
||||
.take(byte_size)
|
||||
.collect::<Vec<u8>>()
|
||||
.chunks_exact(4)
|
||||
.map(|chunk| u32::from_le_bytes(chunk.try_into().unwrap()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn changed(&mut self) -> bool {
|
||||
let temp = self.internal_read();
|
||||
if temp != self.buffer {
|
||||
self.buffer = temp;
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn read(&self) -> &[u32] {
|
||||
&self.buffer
|
||||
}
|
||||
|
||||
/// Convert buffer to RGBA bytes for use with egui/wgpu textures
|
||||
pub fn as_rgba_bytes(&self) -> Vec<u8> {
|
||||
self.buffer
|
||||
.iter()
|
||||
.flat_map(|&pixel| pixel.to_le_bytes())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for FrameBuffer {
|
||||
fn title(&self) -> &str {
|
||||
"Framebuffer"
|
||||
}
|
||||
|
||||
fn visible(&mut self) -> &mut bool {
|
||||
&mut self.visible
|
||||
}
|
||||
|
||||
fn ui(&mut self, ui: &mut egui::Ui, ctx: &egui::Context) {
|
||||
let changed = self.changed();
|
||||
|
||||
// get or create the texture
|
||||
let texture = self.texture.get_or_insert_with(|| {
|
||||
ctx.load_texture(
|
||||
"framebuffer",
|
||||
egui::ColorImage {
|
||||
size: [self.width, self.height],
|
||||
source_size: Vec2::from([self.width as f32, self.height as f32]),
|
||||
pixels: vec![egui::Color32::BLACK; self.width * self.height],
|
||||
},
|
||||
egui::TextureOptions::NEAREST,
|
||||
)
|
||||
});
|
||||
|
||||
if changed {
|
||||
let pixels: Vec<egui::Color32> = self
|
||||
.buffer
|
||||
.iter()
|
||||
.map(|&p| {
|
||||
let bytes = p.to_le_bytes();
|
||||
egui::Color32::from_rgba_premultiplied(bytes[0], bytes[1], bytes[2], bytes[3])
|
||||
})
|
||||
.collect();
|
||||
|
||||
texture.set(
|
||||
egui::ColorImage {
|
||||
size: [self.width, self.height],
|
||||
source_size: Vec2::from([self.width as f32, self.height as f32]),
|
||||
pixels,
|
||||
},
|
||||
egui::TextureOptions::NEAREST,
|
||||
);
|
||||
}
|
||||
|
||||
// scale to fit available space while preserving aspect ratio
|
||||
let available = ui.available_size();
|
||||
let aspect = self.width as f32 / self.height as f32;
|
||||
let size = if available.x / aspect <= available.y {
|
||||
egui::vec2(available.x, available.x / aspect)
|
||||
} else {
|
||||
egui::vec2(available.y * aspect, available.y)
|
||||
};
|
||||
|
||||
ui.image(egui::load::SizedTexture::new(texture.id(), size));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user