Early work in progress on pixels crate

This commit is contained in:
JanNeuendorf 2024-12-19 02:12:00 +01:00
parent 8d3db1a1c3
commit 07c6223a87
4 changed files with 1829 additions and 104 deletions

1750
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[package] [package]
name = "svc16" name = "svc16"
version = "0.3.0" version = "0.4.0"
edition = "2021" edition = "2021"
authors = ["Jan Neuendorf"] authors = ["Jan Neuendorf"]
description = "An emulator for a simple virtual computer" description = "An emulator for a simple virtual computer"
@ -10,4 +10,7 @@ license="MIT"
anyhow = "1.0.93" anyhow = "1.0.93"
clap = { version = "4.5.21", features = ["derive"] } clap = { version = "4.5.21", features = ["derive"] }
minifb = "0.27.0" minifb = "0.27.0"
pixels = { git = "https://github.com/parasyte/pixels.git", rev = "d4df286"}
thiserror = "2.0.3" thiserror = "2.0.3"
winit = "0.29.5"
winit_input_helper = "0.16.0"

View File

@ -6,7 +6,7 @@ pub struct Cli {
pub program: String, pub program: String,
#[arg(short, long, default_value = "1", help = "Set the window scaling")] #[arg(short, long, default_value = "1", help = "Set the window scaling")]
pub scaling: usize, pub scaling: u32,
#[arg( #[arg(
short, short,

View File

@ -1,59 +1,90 @@
mod cli; mod cli;
mod engine; mod engine;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Context, Result};
use clap::Parser; use clap::Parser;
use cli::Cli; use cli::Cli;
use engine::Engine; use engine::Engine;
use minifb::{Key, Scale, Window, WindowOptions}; use minifb::{Scale, Window, WindowOptions};
use pixels::{Error, Pixels, SurfaceTexture};
use std::time::Instant; use std::time::Instant;
use winit::dpi::LogicalSize;
use winit::error::EventLoopError;
use winit::event::{Event, WindowEvent};
use winit::event_loop::EventLoop;
use winit::keyboard::Key;
use winit::keyboard::KeyCode;
use winit::window::WindowBuilder;
use winit_input_helper::WinitInputHelper;
const RES: usize = 256; const RES: usize = 256;
fn main() -> Result<()> { fn main() -> Result<()> {
let cli = Cli::parse(); let cli = Cli::parse();
let initial_state = read_u16s_from_file(&cli.program)?; let initial_state = read_u16s_from_file(&cli.program)?;
let mut engine = Engine::new(initial_state); let mut engine = Engine::new(initial_state);
let mut options = WindowOptions::default();
options.scale = match cli.scaling { let event_loop = EventLoop::new()?;
1 => Scale::X1, let mut input = WinitInputHelper::new();
2 => Scale::X2, let window = {
4 => Scale::X4, let size = LogicalSize::new(
8 => Scale::X8, (RES as u32 * cli.scaling) as f64,
16 => Scale::X16, (RES as u32 * cli.scaling) as f64,
_ => return Err(anyhow!("Scaling must be 1,2,4,8 or 16")), );
let min_size = LogicalSize::new((RES) as f64, (RES) as f64);
WindowBuilder::new()
.with_title("SVC16")
.with_inner_size(size)
.with_min_inner_size(min_size)
.build(&event_loop)?
}; };
let mut pixels = {
let window_size = window.inner_size();
let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
Pixels::new(RES as u32, RES as u32, surface_texture)?
};
let res = event_loop.run(|event, elwt| {
// Draw the current frame
if let Event::WindowEvent {
event: WindowEvent::RedrawRequested,
..
} = event
{
// world.draw(pixels.frame_mut());
if let Err(_) = pixels.render() {
elwt.exit();
return;
}
}
let mut image_buffer = vec![0_u32; RES * RES]; // Handle input events
let mut window = Window::new("SVC16", RES, RES, options)?; if input.update(&event) {
window.set_target_fps(30); // Close events
window.set_cursor_visibility(cli.cursor); if input.key_pressed(KeyCode::Escape) || input.close_requested() {
while window.is_open() && !window.is_key_down(Key::Escape) { elwt.exit();
let mut ipf = 0_usize; return;
let start = Instant::now();
while !engine.wants_to_sync() {
if let Some(debug_vals) = &cli.debug {
print_debug_info(debug_vals, &engine);
} }
engine.step()?;
ipf += 1; // Resize the window
if ipf >= cli.max_ipf { if let Some(size) = input.window_resized() {
break; if let Err(_) = pixels.resize_surface(size.width, size.height) {
elwt.exit();
return;
}
} }
// Update internal state and request a redraw
// world.update();
while !engine.wants_to_sync() {
engine.step().unwrap();
}
let (c1, c2) = get_input_code(&input).unwrap();
let nb = engine.perform_sync(c1, c2);
update_image_buffer(pixels.frame_mut(), &nb);
window.request_redraw();
} }
let frametime = start.elapsed(); });
let input_code = get_input_code(&window);
let screenview = engine.perform_sync(input_code.0, input_code.1);
update_image_buffer(&mut image_buffer, &screenview);
window.update_with_buffer(&image_buffer, RES, RES)?;
if cli.verbose {
println!(
"frame needed {} instructions ({}ms)",
ipf,
frametime.as_millis()
);
}
}
Ok(()) Ok(())
} }
@ -70,52 +101,63 @@ fn read_u16s_from_file(file_path: &str) -> Result<Vec<u16>> {
Ok(u16s) Ok(u16s)
} }
fn rgb565_to_argb(rgb565: u16) -> u32 { fn rgb565_to_argb(rgb565: u16) -> (u8, u8, u8) {
let r = ((rgb565 >> 11) & 0x1F) as u8; let r = ((rgb565 >> 11) & 0x1F) as u8;
let g = ((rgb565 >> 5) & 0x3F) as u8; let g = ((rgb565 >> 5) & 0x3F) as u8;
let b = (rgb565 & 0x1F) as u8; let b = (rgb565 & 0x1F) as u8;
let r = (r << 3) | (r >> 2); let r = (r << 3) | (r >> 2);
let g = (g << 2) | (g >> 4); let g = (g << 2) | (g >> 4);
let b = (b << 3) | (b >> 2); let b = (b << 3) | (b >> 2);
(0xFF << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32) (r, g, b)
// (0xFF << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32)
} }
fn update_image_buffer(imbuff: &mut Vec<u32>, screen: &[u16; RES * RES]) { fn update_image_buffer(imbuff: &mut [u8], screen: &[u16; RES * RES]) {
for i in 0..RES * RES { for i in 0..RES * RES {
*imbuff.get_mut(i).expect("Error with image buffer") = rgb565_to_argb(screen[i]); let col = rgb565_to_argb(screen[i]);
*imbuff.get_mut(4 * i).expect("Error with image buffer") = col.0;
*imbuff.get_mut(4 * i + 1).expect("Error with image buffer") = col.1;
*imbuff.get_mut(4 * i + 2).expect("Error with image buffer") = col.2;
*imbuff.get_mut(4 * i + 3).expect("Error with image buffer") = 255;
} }
} }
fn get_input_code(window: &Window) -> (u16, u16) { fn get_input_code(input: &WinitInputHelper) -> Result<(u16, u16)> {
let mp = window.get_mouse_pos(minifb::MouseMode::Clamp).unwrap(); let mp = input.cursor().unwrap_or((0., 0.));
let pos_code = mp.1 as u16 * 256 + mp.0 as u16; // let mp = (100., 100.);
dbg!(mp);
let pos_code = (mp.1 as u16 * 256) + mp.0 as u16;
let mut key_code = 0_u16; let mut key_code = 0_u16;
if window.get_mouse_down(minifb::MouseButton::Left) || window.is_key_down(Key::Space) { if input.key_pressed(KeyCode::Space) || input.mouse_pressed(winit::event::MouseButton::Left) {
key_code += 1; key_code += 1;
} }
if window.get_mouse_down(minifb::MouseButton::Right) || window.is_key_down(Key::B) { // if window.get_mouse_down(minifb::MouseButton::Left) || window.is_key_down(Key::Space) {
key_code += 2; // key_code += 1;
} // }
if window.is_key_down(Key::Up) || window.is_key_down(Key::W) { // if window.get_mouse_down(minifb::MouseButton::Right) || window.is_key_down(Key::B) {
key_code += 4; // key_code += 2;
} // }
if window.is_key_down(Key::Down) || window.is_key_down(Key::S) { // if window.is_key_down(Key::Up) || window.is_key_down(Key::W) {
key_code += 8; // key_code += 4;
} // }
if window.is_key_down(Key::Left) || window.is_key_down(Key::A) { // if window.is_key_down(Key::Down) || window.is_key_down(Key::S) {
key_code += 16; // key_code += 8;
} // }
if window.is_key_down(Key::Right) || window.is_key_down(Key::D) { // if window.is_key_down(Key::Left) || window.is_key_down(Key::A) {
key_code += 32; // key_code += 16;
} // }
if window.is_key_down(Key::N) { // if window.is_key_down(Key::Right) || window.is_key_down(Key::D) {
key_code += 64; // key_code += 32;
} // }
if window.is_key_down(Key::M) { // if window.is_key_down(Key::N) {
key_code += 128; // key_code += 64;
} // }
// if window.is_key_down(Key::M) {
// key_code += 128;
// }
(pos_code, key_code) // todo!();
Ok((pos_code, key_code))
} }
fn print_debug_info(debug_vals: &Vec<u16>, engine: &Engine) { fn print_debug_info(debug_vals: &Vec<u16>, engine: &Engine) {