|
|
|
@ -10,15 +10,45 @@ use crate::defs::ScreenOp; |
|
|
|
|
use minifb::{Window, WindowOptions, ScaleMode}; |
|
|
|
|
use parking_lot::Mutex; |
|
|
|
|
use std::sync::Arc; |
|
|
|
|
use std::time::{Instant, Duration}; |
|
|
|
|
use std::ops::Sub; |
|
|
|
|
use crsn::utils::UncheckedOptionExt; |
|
|
|
|
|
|
|
|
|
#[derive(Debug)] |
|
|
|
|
struct Opts { |
|
|
|
|
auto_blit: bool, |
|
|
|
|
frame_rate: Duration, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[derive(Debug, Default)] |
|
|
|
|
#[derive(Debug)] |
|
|
|
|
struct Backend { |
|
|
|
|
width: usize, |
|
|
|
|
height: usize, |
|
|
|
|
buffer: Vec<u32>, |
|
|
|
|
window: Option<Window>, |
|
|
|
|
last_render: Instant, |
|
|
|
|
opts: Opts |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Default for Backend { |
|
|
|
|
fn default() -> Self { |
|
|
|
|
Self { |
|
|
|
|
width: 0, |
|
|
|
|
height: 0, |
|
|
|
|
buffer: vec![], |
|
|
|
|
window: None, |
|
|
|
|
last_render: Instant::now().sub(Duration::from_secs(1)), |
|
|
|
|
opts: Opts { |
|
|
|
|
auto_blit: true, |
|
|
|
|
frame_rate: Duration::from_micros(16600), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const OPT_AUTO_BLIT : u64 = 1; |
|
|
|
|
const OPT_FRAME_RATE : u64 = 2; |
|
|
|
|
|
|
|
|
|
// Hack for Window
|
|
|
|
|
unsafe impl std::marker::Send for Backend { } |
|
|
|
|
|
|
|
|
@ -31,12 +61,48 @@ impl OpTrait for ScreenOp { |
|
|
|
|
let h = state.read(*height)?; |
|
|
|
|
init(state, w, h)?; |
|
|
|
|
} |
|
|
|
|
ScreenOp::SetOpt { opt, val } => { |
|
|
|
|
let opt = state.read(*opt)?; |
|
|
|
|
let val = state.read(*val)?; |
|
|
|
|
let backend : &mut Backend = state.ext_mut(); |
|
|
|
|
|
|
|
|
|
match opt { |
|
|
|
|
OPT_AUTO_BLIT => { |
|
|
|
|
backend.opts.auto_blit = (val != 0); |
|
|
|
|
debug!("Set auto blit to {:?}", backend.opts.auto_blit); |
|
|
|
|
} |
|
|
|
|
OPT_FRAME_RATE => { |
|
|
|
|
backend.opts.frame_rate = Duration::from_micros(1_000_000 as u64 / val); |
|
|
|
|
debug!("Set frame rate to {:?}", backend.opts.frame_rate); |
|
|
|
|
} |
|
|
|
|
other => { |
|
|
|
|
warn!("Bad screen opt: {}", other); |
|
|
|
|
state.set_flag(Cond::Invalid, true); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
ScreenOp::Blit { force } => { |
|
|
|
|
let force = state.read(*force)?; |
|
|
|
|
let backend : &mut Backend = state.ext_mut(); |
|
|
|
|
|
|
|
|
|
if force != 0 { |
|
|
|
|
blit(backend) |
|
|
|
|
} else { |
|
|
|
|
blit_maybe(backend) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
ScreenOp::SetPixel { x, y, color } => { |
|
|
|
|
let x = state.read(*x)?; |
|
|
|
|
let y = state.read(*y)?; |
|
|
|
|
let color = state.read(*color)?; |
|
|
|
|
|
|
|
|
|
let backend : &mut Backend = state.ext_mut(); |
|
|
|
|
|
|
|
|
|
if x >= backend.width as u64 || y >= backend.height as u64 { |
|
|
|
|
state.set_flag(Cond::Overflow, true); |
|
|
|
|
return Ok(eres); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
match &mut backend.window { |
|
|
|
|
Some(w) => { |
|
|
|
|
let index = y * backend.width as u64 + x; |
|
|
|
@ -46,9 +112,9 @@ impl OpTrait for ScreenOp { |
|
|
|
|
} else { |
|
|
|
|
backend.buffer[index as usize] = color as u32; |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
.update_with_buffer(&backend.buffer, backend.width, backend.height) |
|
|
|
|
.expect("Update screen"); // TODO fault
|
|
|
|
|
if backend.opts.auto_blit { |
|
|
|
|
blit_maybe(backend); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
None => { |
|
|
|
@ -70,7 +136,7 @@ fn init(state: &mut RunState, width: Value, height: Value) -> Result<(), Fault> |
|
|
|
|
height as usize, |
|
|
|
|
WindowOptions { |
|
|
|
|
resize: true, |
|
|
|
|
scale_mode: ScaleMode::AspectRatioStretch, |
|
|
|
|
scale_mode: ScaleMode::UpperLeft, |
|
|
|
|
..WindowOptions::default() |
|
|
|
|
}, |
|
|
|
|
).expect("Unable to create window"); // TODO fault
|
|
|
|
@ -87,13 +153,25 @@ fn init(state: &mut RunState, width: Value, height: Value) -> Result<(), Fault> |
|
|
|
|
backend.buffer = vec![0; (width * height) as usize]; |
|
|
|
|
|
|
|
|
|
// window.limit_update_rate(Some(std::time::Duration::from_micros(16600)));
|
|
|
|
|
window.limit_update_rate(None); |
|
|
|
|
|
|
|
|
|
window |
|
|
|
|
.update_with_buffer(&backend.buffer, backend.width, backend.height) |
|
|
|
|
.expect("Update screen"); // TODO fault
|
|
|
|
|
// window.limit_update_rate(None);
|
|
|
|
|
|
|
|
|
|
backend.window = Some(window); |
|
|
|
|
|
|
|
|
|
blit_maybe(backend); |
|
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn blit_maybe(backend: &mut Backend) { |
|
|
|
|
if backend.last_render.elapsed() >= backend.opts.frame_rate { |
|
|
|
|
blit(backend); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn blit(backend: &mut Backend) { |
|
|
|
|
backend.window.as_mut().unwrap() |
|
|
|
|
.update_with_buffer(&backend.buffer, backend.width, backend.height) |
|
|
|
|
.expect("Update screen"); // TODO fault
|
|
|
|
|
|
|
|
|
|
backend.last_render = Instant::now(); |
|
|
|
|
} |
|
|
|
|