trueLMAO/frontend/src/app.rs

284 lines
8.7 KiB
Rust
Raw Normal View History

2022-06-06 11:48:35 -04:00
use emu::Megadrive;
2022-06-09 20:01:39 -04:00
use crate::widgets;
2022-06-11 09:33:13 -04:00
use instant::Instant;
2022-06-12 09:08:31 -04:00
use std::collections::VecDeque;
2022-06-06 19:37:33 -04:00
pub struct Frontend {
2022-06-11 09:33:13 -04:00
emu: Megadrive,
2022-06-06 19:37:33 -04:00
fullscreen: bool,
2022-06-11 09:33:13 -04:00
game_state: GameState,
2022-06-14 16:31:49 -04:00
test_vec: VecDeque<u64>,
2022-06-05 16:10:44 -04:00
}
2022-06-14 14:57:45 -04:00
impl Default for Frontend {
fn default() -> Self {
let buf: Vec<u8> = include_bytes!("/home/cake/sonic/roms/s1p.bin").to_vec();
Self {
emu: Megadrive::new(buf),
fullscreen: true,
game_state: Default::default(),
test_vec: VecDeque::with_capacity(60),
}
}
}
2022-06-11 09:33:13 -04:00
// TODO: move to core
pub struct GameState {
pub running: bool,
pub vsync: bool,
frames: u64,
epoch: Instant,
frames_to_render: u64,
}
impl Default for GameState {
fn default() -> Self {
Self {
running: true,
2022-06-14 14:57:45 -04:00
vsync: false,
2022-06-11 09:33:13 -04:00
frames: 0,
frames_to_render: 0,
epoch: Instant::now(),
}
}
}
impl GameState {
pub fn tick(&mut self) {
let diff = Instant::now().duration_since(self.epoch);
let frames = (diff.as_millis() as f64 * 0.05992274) as u64; // TODO: PAL
// self.emu.gfx.framerate()
self.frames_to_render = frames - self.frames;
self.frames = frames;
}
pub fn frames_to_render(&self) -> u64 {
if self.vsync {
1
} else {
self.frames_to_render
}
}
}
2022-06-06 19:37:33 -04:00
impl Frontend {
2022-06-05 16:10:44 -04:00
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
2022-06-07 12:29:52 -04:00
cc.egui_ctx.set_visuals(egui::Visuals {
dark_mode: true,
..egui::Visuals::default()
});
// let mut fonts = egui::FontDefinitions::default();
// fonts
// .families
// .entry(egui::FontFamily::Monospace)
// .or_default()
// .push("Hack".to_string());
// cc.egui_ctx.set_fonts(fonts);
2022-06-05 16:10:44 -04:00
// Load previous app state (if any).
// Note that you must enable the `persistence` feature for this to work.
2022-06-06 11:48:35 -04:00
// if let Some(storage) = cc.storage {
// return eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default();
// }
2022-06-05 16:10:44 -04:00
2022-06-07 12:29:52 -04:00
2022-06-05 16:10:44 -04:00
Default::default()
}
}
2022-06-06 19:37:33 -04:00
impl eframe::App for Frontend {
2022-06-05 16:10:44 -04:00
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
2022-06-06 19:37:33 -04:00
2022-06-11 09:33:13 -04:00
if self.game_state.running {
ctx.request_repaint();
2022-06-12 18:18:45 -04:00
crate::input::dummy_input(ctx, &mut self.emu);
2022-06-11 09:33:13 -04:00
self.game_state.tick();
2022-06-09 20:22:03 -04:00
2022-06-11 09:33:13 -04:00
let frames_to_render = self.game_state.frames_to_render();
2022-06-09 20:01:39 -04:00
2022-06-12 17:06:24 -04:00
if frames_to_render > 3 {
2022-06-11 09:33:13 -04:00
self.emu.frame(true);
} else if frames_to_render > 0 {
for _ in 0..frames_to_render - 1 {
self.emu.frame(false);
}
self.emu.frame(true);
}
}
// main layout
2022-06-09 20:01:39 -04:00
2022-06-06 19:37:33 -04:00
if self.fullscreen {
2022-06-09 20:22:03 -04:00
egui::CentralPanel::default()
.frame(egui::containers::Frame::none())
.show(ctx, |ui| {
2022-06-11 09:33:13 -04:00
let response = ui.add(widgets::viewport_centred(&self.emu));
2022-06-09 20:22:03 -04:00
if response.double_clicked() {
self.fullscreen = false;
}
});
2022-06-07 12:29:52 -04:00
return
2022-06-06 19:37:33 -04:00
}
2022-06-07 12:29:52 -04:00
2022-06-12 09:08:31 -04:00
// TODO menu module
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
egui::menu::bar(ui, |ui| {
ui.menu_button("File", |ui| {
if ui.button("Quit").clicked() {
frame.quit();
}
});
2022-06-12 17:06:24 -04:00
ui.menu_button("Window", |ui| {
2022-06-12 09:08:31 -04:00
if ui.button("Auto-arrange").clicked() {
ui.ctx().memory().reset_areas();
ui.close_menu();
}
2022-06-14 14:57:45 -04:00
if ui.button("Fullscreen").clicked() {
self.fullscreen = true;
ui.close_menu();
}
2022-06-12 09:08:31 -04:00
});
// *ui.ctx().memory() = Default::default();
});
});
2022-06-11 09:33:13 -04:00
egui::Window::new("screen")
.min_height(100.)
.show(ctx, |ui| {
let response = ui.add(widgets::viewport(&self.emu));
if response.double_clicked() {
self.fullscreen = true;
}
});
2022-06-06 19:37:33 -04:00
2022-06-13 17:29:54 -04:00
egui::Window::new("palette")
.show(ctx, |ui| {
let pixels = self.emu.core.mem.vdp.cram_rgb().iter()
.map(|&(r, g, b)| egui::Color32::from_rgb(r, g, b))
.collect();
let texture: &egui::TextureHandle = &ui.ctx().load_texture(
"palette",
egui::ColorImage {
size: [16, 4],
pixels,
},
egui::TextureFilter::Nearest
);
let img = egui::Image::new(texture, texture.size_vec2() * 20.);
ui.add(img);
});
2022-06-14 14:57:45 -04:00
egui::Window::new("vram")
.show(ctx, |ui| {
2022-06-15 12:53:25 -04:00
let mut pixels = vec![];
for duxel in &self.emu.core.mem.vdp.VRAM[0..64] {
let pixel = (*duxel & 0xF0) >> 4;
let color = self.emu.core.mem.vdp.color(0, pixel as _);
pixels.push(egui::Color32::from_rgb(color.0, color.1, color.2));
let pixel = *duxel & 0xF;
let color = self.emu.core.mem.vdp.color(0, pixel as _);
pixels.push(egui::Color32::from_rgb(color.0, color.1, color.2));
}
let texture: &egui::TextureHandle = &ui.ctx().load_texture(
"palette",
egui::ColorImage {
size: [8, 8* 2],
pixels,
},
egui::TextureFilter::Nearest
);
let img = egui::Image::new(texture, texture.size_vec2() * 20.);
ui.add(img);
2022-06-14 14:57:45 -04:00
});
2022-06-11 09:33:13 -04:00
egui::CentralPanel::default().show(ctx, |ui| {
2022-06-13 17:29:54 -04:00
2022-06-11 09:33:13 -04:00
egui::warn_if_debug_build(ui);
// ctx.inspection_ui(ui);
2022-06-12 11:44:56 -04:00
2022-06-12 17:06:24 -04:00
ui.label(&format!("MD frames this frame: {}", self.game_state.frames_to_render));
ui.label(&format!("avg frames {:.1}", self.test_vec.iter().sum::<u64>() as f32 / self.test_vec.len() as f32));
2022-06-12 11:44:56 -04:00
if ui.button(if self.game_state.running { "pause" } else { "play" }).clicked() {
self.game_state.running = !self.game_state.running;
}
2022-06-12 09:08:31 -04:00
ui.radio_value(&mut self.game_state.vsync, true, "vsync");
ui.radio_value(&mut self.game_state.vsync, false, "not vsync");
self.test_vec.push_back(self.game_state.frames_to_render().min(4));
if self.test_vec.len() > 60 {
self.test_vec.pop_front();
}
use egui::plot::{
Bar, BarChart, Legend, Plot,
};
2022-06-12 17:06:24 -04:00
let chart = BarChart::new(
2022-06-12 09:08:31 -04:00
self.test_vec
.iter()
.enumerate()
.map(|(i, x)| Bar::new((i + 1) as _, *x as f64))
.collect()
)
.color(egui::Color32::LIGHT_BLUE)
.name("Normal Distribution");
// if !self.vertical {
// chart = chart.horizontal();
// }
Plot::new("Normal Distribution Demo")
2022-06-12 11:44:56 -04:00
.width(200.)
.height(100.)
2022-06-12 09:08:31 -04:00
.legend(Legend::default())
.data_aspect(1.0)
.show(ui, |plot_ui| plot_ui.bar_chart(chart))
.response
2022-06-06 19:37:33 -04:00
});
2022-06-11 09:33:13 -04:00
// TODO debug module
2022-06-05 16:10:44 -04:00
2022-06-11 09:33:13 -04:00
egui::Window::new("cpu")
.min_width(800.)
.show(ctx, |ui| {
let mut debug = String::new();
debug.push_str(&format!("PC: {:X}\n\n", self.emu.core.pc));
2022-06-05 16:10:44 -04:00
2022-06-06 19:37:33 -04:00
2022-06-11 09:33:13 -04:00
// let v = self.emu.core.mem.vdp.VSRAM.iter().map(|x|format!("{:X}", x)).collect::<Vec<String>>().join(" ");
// debug.push_str(&format!("VSRAM: {}\n\n", v));
2022-06-09 20:22:03 -04:00
2022-06-11 09:33:13 -04:00
debug.push_str(&format!("D "));
for i in 0..=7 {
debug.push_str(&format!("{:X} ", self.emu.core.dar[i]));
}
debug.push_str(&format!("\n"));
2022-06-06 19:37:33 -04:00
2022-06-11 09:33:13 -04:00
debug.push_str(&format!("A "));
for i in 0..=7 {
debug.push_str(&format!("{:X} ", self.emu.core.dar[i + 8]));
}
debug.push_str(&format!("\n"));
debug.push_str(&format!("\n"));
2022-06-06 19:37:33 -04:00
2022-06-11 09:33:13 -04:00
for (pc, opcode) in self.emu.disasm() {
debug.push_str(&format!("0x{:X}\t{}\n", pc, opcode));
}
ui.label(&debug);
});
2022-06-05 16:10:44 -04:00
}
}