fixes, graphic acceleration commands, upscaling pixels

This commit is contained in:
2020-10-20 20:59:35 +02:00
parent 9faeceba61
commit 562c6baddc
14 changed files with 271 additions and 56 deletions
+12
View File
@@ -12,6 +12,11 @@ pub enum ScreenOp {
y: Rd,
color: Rd,
},
GetPixel {
color: Wr,
x: Rd,
y: Rd,
},
GetMouse {
x: Wr,
y: Wr,
@@ -28,6 +33,13 @@ pub enum ScreenOp {
Erase {
color: Rd,
},
FillRect {
x: Rd,
y: Rd,
w: Rd,
h: Rd,
color: Rd,
},
Blit {
force: Rd,
},
+134 -21
View File
@@ -3,7 +3,9 @@ use std::time::{Duration, Instant};
use minifb::{Key, MouseButton, MouseMode, ScaleMode, Window, WindowOptions};
use crsn::asm::data::literal::Value;
use crsn::asm::data::literal::{Value, is_negative};
use crsn::asm::error::CrsnError;
use crsn::asm::instr::cond::Flag;
use crsn::module::{EvalRes, OpTrait};
use crsn::runtime::fault::Fault;
use crsn::runtime::run_thread::{state::RunState, ThreadInfo};
@@ -12,18 +14,21 @@ use crsn::sexp::Sexp;
use crsn::utils::A;
use crate::defs::ScreenOp;
use crsn::asm::instr::cond::Flag;
use std::mem;
#[derive(Debug)]
struct Opts {
auto_blit: bool,
frame_rate: Duration,
upscale: Value,
}
#[derive(Debug)]
struct Backend {
width: usize,
height: usize,
width: Value,
height: Value,
log_width: Value,
log_height: Value,
buffer: Vec<u32>,
window: Option<Window>,
last_render: Instant,
@@ -35,19 +40,33 @@ impl Default for Backend {
Self {
width: 0,
height: 0,
log_width: 0,
log_height: 0,
buffer: vec![],
window: None,
last_render: Instant::now().sub(Duration::from_secs(1)),
opts: Opts {
auto_blit: true,
upscale: 1,
frame_rate: Duration::from_micros(16600),
},
}
}
}
impl Backend {
pub fn draw_rect_log(&mut self, mut x: Value, mut y: Value, w: Value, h: Value, color: u32) {
for yy in (y * self.opts.upscale)..((y + h) * self.opts.upscale).min(self.height) {
for xx in (x * self.opts.upscale)..((x + w) * self.opts.upscale).min(self.width) {
self.buffer[(yy * self.width + xx) as usize] = color as u32;
}
}
}
}
pub const OPT_AUTO_BLIT: u64 = 1;
pub const OPT_FRAME_RATE: u64 = 2;
pub const OPT_UPSCALE: u64 = 3;
// Hack for Window
unsafe impl std::marker::Send for Backend {}
@@ -81,8 +100,22 @@ impl OpTrait for ScreenOp {
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);
if val == 0 {
state.set_flag(Flag::Invalid, true);
} else {
backend.opts.frame_rate = Duration::from_micros(1_000_000 as u64 / val);
debug!("Set frame rate to {:?}", backend.opts.frame_rate);
}
}
OPT_UPSCALE => {
if val == 0 {
state.set_flag(Flag::Invalid, true);
} else {
backend.opts.upscale = val;
backend.log_width = backend.width / val;
backend.log_height = backend.height / val;
debug!("Set upscale to {:?}", val);
}
}
_other => {
unreachable!()
@@ -106,8 +139,9 @@ impl OpTrait for ScreenOp {
match &mut backend.window {
Some(w) => {
if !w.is_open() {
// TODO...
std::process::exit(0);
debug!("Window is closed");
let _ = backend.window.take();
return Err(Fault::Halt);
}
w.update();
@@ -126,23 +160,46 @@ impl OpTrait for ScreenOp {
let backend: &mut Backend = state.ext_mut();
if x >= backend.width as u64 || y >= backend.height as u64 {
if x >= backend.log_width as u64 || y >= backend.log_height as u64 {
state.set_flag(Flag::Overflow, true);
return Ok(eres);
}
match &mut backend.window {
Some(_w) => {
let index = y * backend.width as u64 + x;
backend.draw_rect_log(x, y, 1, 1, color as u32);
if backend.opts.auto_blit {
blit_maybe(backend)?;
}
}
None => {
state.set_flag(Flag::Invalid, true);
}
}
}
ScreenOp::GetPixel { color, x, y } => {
state.clear_status();
let x = state.read(x)?;
let y = state.read(y)?;
let backend: &mut Backend = state.ext_mut();
if x >= backend.log_width as u64 || y >= backend.log_height as u64 {
state.set_flag(Flag::Overflow, true);
return Ok(eres);
}
match &mut backend.window {
Some(_w) => {
let index = y * backend.opts.upscale * backend.width as u64 + x * backend.opts.upscale;
if index as usize > backend.buffer.len() {
warn!("Screen set pixel out of bounds");
state.set_flag(Flag::Invalid, true);
} else {
backend.buffer[index as usize] = color as u32;
if backend.opts.auto_blit {
blit_maybe(backend)?;
}
let c = backend.buffer[index as usize];
trace!("index {}, c {}", index, c);
state.write(color, c as Value)?;
}
}
None => {
@@ -163,8 +220,11 @@ impl OpTrait for ScreenOp {
state.set_flag(Flag::Overflow, true);
}
Some((xf, yf)) => {
let xval = xf.round() as u64;
let yval = yf.round() as u64;
let mut xval = xf.round() as u64;
let mut yval = yf.round() as u64;
xval /= backend.opts.upscale;
yval /= backend.opts.upscale;
state.write(x, xval)?;
state.write(y, yval)?;
@@ -227,6 +287,51 @@ impl OpTrait for ScreenOp {
}
}
}
ScreenOp::FillRect {
x, y,
w, h,
color
} => {
let mut x = state.read(x)?;
let mut y = state.read(y)?;
let mut w = state.read(w)?;
let mut h = state.read(h)?;
let c = state.read(color)?;
let backend: &mut Backend = state.ext_mut();
if is_negative(w) || is_negative(h) {
state.set_flag(Flag::Invalid, true);
return Ok(eres);
}
if w > backend.log_width {
w = backend.log_width;
}
if h > backend.log_height {
h = backend.log_height;
}
if is_negative(x) {
let xi : i64 = unsafe { mem::transmute(x) };
let minus = xi.abs() as u64;
w -= minus;
x = 0;
}
if is_negative(y) {
let yi : i64 = unsafe { mem::transmute(y) };
let minus = yi.abs() as u64;
h -= minus;
y = 0;
}
match &mut backend.window {
Some(_w) => {
backend.draw_rect_log(x, y, w, h, c as u32);
}
None => {
state.set_flag(Flag::Invalid, true);
}
}
}
}
Ok(eres)
@@ -236,13 +341,17 @@ impl OpTrait for ScreenOp {
match self {
ScreenOp::SetOpt { opt, val } => sexp::list(&[A("sc-opt"), A(opt), A(val)]),
ScreenOp::ScreenInit { width, height } => sexp::list(&[A("sc-init"), A(width), A(height)]),
ScreenOp::SetPixel { x, y, color } => sexp::list(&[A("sc-px"), A(x), A(y), A(color)]),
ScreenOp::SetPixel { x, y, color } => sexp::list(&[A("sc-wr"), A(x), A(y), A(color)]),
ScreenOp::GetPixel { color, x, y } => sexp::list(&[A("sc-rd"), A(color), A(x), A(y)]),
ScreenOp::Blit { force } => sexp::list(&[A("sc-blit"), A(force)]),
ScreenOp::Update => sexp::list(&[A("sc-poll")]),
ScreenOp::GetMouse { x, y } => sexp::list(&[A("sc-mouse"), A(x), A(y)]),
ScreenOp::TestKey { pressed, code } => sexp::list(&[A("sc-key"), A(pressed), A(code)]),
ScreenOp::TestMouse { pressed, button } => sexp::list(&[A("sc-mbtn"), A(pressed), A(button)]),
ScreenOp::Erase { color } => sexp::list(&[A("sc-erase"), A(color)]),
ScreenOp::FillRect { x, y, w, h, color } => {
sexp::list(&[A("sc-rect"), A(x), A(y), A(w), A(h), A(color)])
}
}
}
}
@@ -266,8 +375,10 @@ fn init(state: &mut RunState, width: Value, height: Value) -> Result<(), Fault>
return Err(Fault::NotAllowed("Screen already initialized".into()));
}
backend.width = width as usize;
backend.height = height as usize;
backend.width = width;
backend.height = height;
backend.log_width = width;
backend.log_height = height;
backend.buffer = vec![0; (width * height) as usize];
backend.window = Some(window);
@@ -289,10 +400,12 @@ fn blit(backend: &mut Backend) -> Result<(), Fault> {
let w = backend.window.as_mut().unwrap();
if !w.is_open() {
debug!("Window is closed");
let _ = backend.window.take();
return Err(Fault::Halt);
}
w.update_with_buffer(&backend.buffer, backend.width, backend.height)
w.update_with_buffer(&backend.buffer, backend.width as usize, backend.height as usize)
.expect("Update screen"); // TODO fault
backend.last_render = Instant::now();
+2 -1
View File
@@ -7,7 +7,7 @@ use crsn::asm::parse::arg_parser::TokenParser;
use crsn::module::{CrsnExtension, ParseRes};
use crsn::sexp::SourcePosition;
use crsn::asm::data::literal::Value;
use crate::exec::{OPT_AUTO_BLIT, OPT_FRAME_RATE};
use crate::exec::{OPT_AUTO_BLIT, OPT_FRAME_RATE, OPT_UPSCALE};
mod defs;
mod parse;
@@ -38,6 +38,7 @@ impl CrsnExtension for ScreenOps {
match name {
"SCREEN_AUTO_BLIT" => Some(OPT_AUTO_BLIT),
"SCREEN_FPS" => Some(OPT_FRAME_RATE),
"SCREEN_UPSCALE" => Some(OPT_UPSCALE),
_ => None
}
}
+27 -3
View File
@@ -9,9 +9,10 @@ use crate::defs::ScreenOp;
use super::exec::OPT_AUTO_BLIT;
use super::exec::OPT_FRAME_RATE;
use super::exec::OPT_UPSCALE;
pub(crate) fn parse<'a>(_pos: &SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> {
Ok(ParseRes::ext(match keyword {
let rv = Ok(ParseRes::ext(match keyword {
"sc-init" => {
ScreenOp::ScreenInit {
width: args.next_rd()?,
@@ -29,7 +30,7 @@ pub(crate) fn parse<'a>(_pos: &SourcePosition, keyword: &str, mut args: TokenPar
}
}
"sc-px" => {
"sc-wr" | "sc-px" => {
ScreenOp::SetPixel {
x: args.next_rd()?,
y: args.next_rd()?,
@@ -37,6 +38,14 @@ pub(crate) fn parse<'a>(_pos: &SourcePosition, keyword: &str, mut args: TokenPar
}
}
"sc-rd" => {
ScreenOp::GetPixel {
color: args.next_wr()?,
x: args.next_rd()?,
y: args.next_rd()?,
}
}
"sc-opt" => {
let (val, valopt) = args.next_value()?;
ScreenOp::SetOpt {
@@ -47,6 +56,9 @@ pub(crate) fn parse<'a>(_pos: &SourcePosition, keyword: &str, mut args: TokenPar
OPT_FRAME_RATE => {
OPT_FRAME_RATE
}
OPT_UPSCALE => {
OPT_UPSCALE
}
_ => {
return Err(CrsnError::Parse("Bad screen option".into(), valopt));
}
@@ -90,8 +102,20 @@ pub(crate) fn parse<'a>(_pos: &SourcePosition, keyword: &str, mut args: TokenPar
}
}
"sc-rect" => {
ScreenOp::FillRect {
x: args.next_rd()?,
y: args.next_rd()?,
w: args.next_rd()?,
h: args.next_rd()?,
color: args.next_rd()?
}
}
_other => {
return Ok(ParseRes::Unknown(args));
}
}))
}));
args.ensure_empty_custom("Too many arguments for this instruction!")?;
return rv;
}