Fixed minifb frame updater to allow slower framerates

If the sim is running slower than 60Hz, it was using the frame limiter
to not apply because no frame was drawn, and so it would end up running
at full speed.  Minifb can save the last frame without cloning it, and
redraw the same frame if no new frame is ready, which allows the limiter
to still delay the next frame, so slower speeds work.  This was also
preventing things that didn't update the screen from allowing inputs
including escape to have any effect
This commit is contained in:
transistor 2022-10-02 13:07:15 -07:00
parent a9d51fb919
commit 4cb423d85a
4 changed files with 42 additions and 14 deletions

View File

@ -5,7 +5,7 @@ use std::collections::VecDeque;
use crate::Clock;
use crate::Error;
use crate::host::traits::{WindowUpdater, BlitableSurface};
use crate::host::traits::{WindowUpdater, BlitableSurface, ClockedQueue};
pub const MASK_COLOUR: u32 = 0xFFFFFFFF;
@ -111,23 +111,23 @@ impl WindowUpdater for FrameSwapper {
#[derive(Clone)]
pub struct FrameQueue {
max_size: (u32, u32),
queue: Arc<Mutex<VecDeque<(Clock, Frame)>>>,
queue: ClockedQueue<Frame>,
}
impl FrameQueue {
pub fn new(width: u32, height: u32) -> Self {
Self {
max_size: (width, height),
queue: Arc::new(Mutex::new(VecDeque::new())),
queue: ClockedQueue::new(),
}
}
pub fn add(&self, clock: Clock, frame: Frame) {
self.queue.lock().unwrap().push_back((clock, frame));
self.queue.push(clock, frame);
}
pub fn latest(&self) -> Option<(Clock, Frame)> {
self.queue.lock().unwrap().drain(..).last()
self.queue.pop_latest()
}
}

View File

@ -1,7 +1,8 @@
use std::collections::VecDeque;
use std::sync::{Arc, Mutex, MutexGuard};
use crate::error::Error;
use crate::{Clock, Error};
use crate::host::keys::Key;
use crate::host::gfx::Frame;
use crate::host::controllers::{ControllerDevice, ControllerEvent};
@ -96,6 +97,24 @@ impl<T: Copy> HostData<T> {
}
}
#[derive(Clone)]
pub struct ClockedQueue<T>(Arc<Mutex<VecDeque<(Clock, T)>>>);
impl<T: Clone> ClockedQueue<T> {
pub fn new() -> Self {
Self(Arc::new(Mutex::new(VecDeque::new())))
}
pub fn push(&self, clock: Clock, data: T) {
self.0.lock().unwrap().push_back((clock, data));
}
pub fn pop_latest(&self) -> Option<(Clock, T)> {
self.0.lock().unwrap().drain(..).last()
}
}
pub struct DummyAudio();
impl Audio for DummyAudio {

View File

@ -4,8 +4,8 @@ use std::sync::mpsc;
use moa_minifb;
use moa_peripherals_yamaha::{Ym2612, Sn76489};
use moa_core::host::gfx::Frame;
use moa_core::host::{Host, KeyboardUpdater, Key};
use moa_core::host::gfx::{Frame, FrameQueue};
use moa_core::host::{Host, WindowUpdater, KeyboardUpdater, Key};
use moa_core::{System, Error, ClockElapsed, Address, Addressable, Steppable, Transmutable, TransmutableBox, wrap_transmutable};
@ -18,12 +18,14 @@ impl KeyboardUpdater for SynthControlsUpdater {
}
struct SynthControl {
queue: FrameQueue,
receiver: mpsc::Receiver<(Key, bool)>,
}
impl SynthControl {
pub fn new(receiver: mpsc::Receiver<(Key, bool)>) -> Self {
pub fn new(queue: FrameQueue, receiver: mpsc::Receiver<(Key, bool)>) -> Self {
Self {
queue,
receiver,
}
}
@ -49,7 +51,11 @@ impl Steppable for SynthControl {
}
}
Ok(1_000_000)
let size = self.queue.max_size();
let frame = Frame::new(size.0, size.1);
self.queue.add(system.clock, frame);
Ok(33_000_000)
}
}
@ -88,8 +94,9 @@ fn main() {
moa_minifb::run(matches, |host| {
let mut system = System::new();
let queue = FrameQueue::new(384, 128);
let (sender, receiver) = mpsc::channel();
let control = wrap_transmutable(SynthControl::new(receiver));
let control = wrap_transmutable(SynthControl::new(queue.clone(), receiver));
system.add_device("control", control)?;
let ym_sound = wrap_transmutable(Ym2612::create(host)?);
@ -99,8 +106,7 @@ fn main() {
let sn_sound = wrap_transmutable(Sn76489::create(host)?);
system.add_addressable_device(0x10, sn_sound)?;
let frame = Frame::new_shared(384, 128);
host.add_window(Frame::new_updater(frame.clone()))?;
host.add_window(Box::new(queue.clone()))?;
host.register_keyboard(Box::new(SynthControlsUpdater(sender)))?;
Ok(system)

View File

@ -9,6 +9,7 @@ use clap::{App, Arg, ArgMatches};
use moa_core::{System, Error, Clock};
use moa_core::host::{Host, HostData, ControllerUpdater, KeyboardUpdater, WindowUpdater, Audio, ControllerDevice};
use moa_core::host::gfx::Frame;
use moa_common::audio::{AudioOutput, AudioMixer, AudioSource};
@ -226,6 +227,7 @@ impl MiniFrontend {
let mut average_time = 0;
let mut update_timer = Instant::now();
let mut last_frame = Frame::new(size.0, size.1);
while window.is_open() && !window.is_key_down(Key::Escape) {
let frame_time = update_timer.elapsed().as_micros();
update_timer = Instant::now();
@ -259,8 +261,9 @@ impl MiniFrontend {
if let Some(updater) = self.window.as_mut() {
if let Ok(frame) = updater.take_frame() {
window.update_with_buffer(&frame.bitmap, frame.width as usize, frame.height as usize).unwrap();
last_frame = frame
}
window.update_with_buffer(&last_frame.bitmap, last_frame.width as usize, last_frame.height as usize).unwrap();
}
}
}