Modified how frames are updated

It turns out to not be too much of a performance issue to allocate
a new frame each time one is produced, so to reduce lock contention
I added a queue where frames are added to and taken from without
locking the frame for the whole update.  I'm hoping this will give
more flexibility to frontend implementations, which can simply
skip or repeat frames if needed.
This commit is contained in:
transistor 2022-10-01 12:12:25 -07:00
parent 5278c301ef
commit 2d6044084d
6 changed files with 84 additions and 89 deletions

View File

@ -1,7 +1,9 @@
use std::mem;
use std::sync::{Arc, Mutex};
use std::collections::VecDeque;
use crate::Clock;
use crate::Error;
use crate::host::traits::{WindowUpdater, BlitableSurface};
@ -23,10 +25,6 @@ impl Frame {
pub fn new_shared(width: u32, height: u32) -> Arc<Mutex<Frame>> {
Arc::new(Mutex::new(Frame::new(width, height)))
}
pub fn new_updater(frame: Arc<Mutex<Frame>>) -> Box<dyn WindowUpdater> {
Box::new(FrameUpdateWrapper(frame))
}
}
@ -67,35 +65,6 @@ impl BlitableSurface for Frame {
}
}
pub struct FrameUpdateWrapper(Arc<Mutex<Frame>>);
impl WindowUpdater for FrameUpdateWrapper {
fn get_size(&mut self) -> (u32, u32) {
match self.0.lock() {
Ok(frame) => (frame.width, frame.height),
_ => (0, 0),
}
}
fn get_frame(&mut self) -> Result<Frame, Error> {
let mut current = self.0.lock().map_err(|_| Error::new("Lock error"))?;
let mut frame = Frame::new(current.width, current.height);
mem::swap(&mut *current, &mut frame);
Ok(frame)
}
fn update_frame(&mut self, width: u32, _height: u32, bitmap: &mut [u32]) {
if let Ok(frame) = self.0.lock() {
for y in 0..frame.height {
for x in 0..frame.width {
bitmap[(x + (y * width)) as usize] = frame.bitmap[(x + (y * frame.width)) as usize];
}
}
}
}
}
#[derive(Clone)]
pub struct FrameSwapper {
pub current: Arc<Mutex<Frame>>,
@ -125,29 +94,50 @@ impl FrameSwapper {
}
impl WindowUpdater for FrameSwapper {
fn get_size(&mut self) -> (u32, u32) {
if let Ok(frame) = self.current.lock() {
(frame.width, frame.height)
} else {
(0, 0)
}
fn max_size(&mut self) -> (u32, u32) {
let frame = self.current.lock().unwrap();
(frame.width, frame.height)
}
fn get_frame(&mut self) -> Result<Frame, Error> {
fn take_frame(&mut self) -> Result<Frame, Error> {
let mut previous = self.previous.lock().map_err(|_| Error::new("Lock error"))?;
let mut frame = Frame::new(previous.width, previous.height);
mem::swap(&mut *previous, &mut frame);
Ok(frame)
}
}
fn update_frame(&mut self, width: u32, _height: u32, bitmap: &mut [u32]) {
if let Ok(frame) = self.previous.lock() {
for y in 0..frame.height {
for x in 0..frame.width {
bitmap[(x + (y * width)) as usize] = frame.bitmap[(x + (y * frame.width)) as usize];
}
}
#[derive(Clone)]
pub struct FrameQueue {
max_size: (u32, u32),
queue: Arc<Mutex<VecDeque<(Clock, Frame)>>>,
}
impl FrameQueue {
pub fn new(width: u32, height: u32) -> Self {
Self {
max_size: (width, height),
queue: Arc::new(Mutex::new(VecDeque::new())),
}
}
pub fn add(&self, clock: Clock, frame: Frame) {
self.queue.lock().unwrap().push_back((clock, frame));
}
pub fn latest(&self) -> Option<(Clock, Frame)> {
self.queue.lock().unwrap().drain(..).last()
}
}
impl WindowUpdater for FrameQueue {
fn max_size(&mut self) -> (u32, u32) {
self.max_size
}
fn take_frame(&mut self) -> Result<Frame, Error> {
self.latest().map(|(_, f)| f).ok_or_else(|| Error::new("No frame available"))
}
}

View File

@ -36,9 +36,18 @@ pub trait Tty {
}
pub trait WindowUpdater: Send {
fn get_size(&mut self) -> (u32, u32);
fn get_frame(&mut self) -> Result<Frame, Error>;
fn update_frame(&mut self, width: u32, height: u32, bitmap: &mut [u32]);
fn max_size(&mut self) -> (u32, u32);
fn take_frame(&mut self) -> Result<Frame, Error>;
fn update_frame(&mut self, width: u32, _height: u32, bitmap: &mut [u32]) {
if let Ok(frame) = self.take_frame() {
for y in 0..frame.height {
for x in 0..frame.width {
bitmap[(x + (y * width)) as usize] = frame.bitmap[(x + (y * frame.width)) as usize];
}
}
}
}
}
pub trait ControllerUpdater: Send {

View File

@ -1,8 +1,8 @@
use std::thread;
use std::str::FromStr;
use std::time::Duration;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use minifb::{self, Key};
use clap::{App, Arg, ArgMatches};
@ -162,7 +162,6 @@ impl Host for MiniFrontendBuilder {
pub struct MiniFrontend {
pub buffer: Vec<u32>,
pub modifiers: u16,
pub window: Option<Box<dyn WindowUpdater>>,
pub controller: Option<Box<dyn ControllerUpdater>>,
@ -174,7 +173,6 @@ pub struct MiniFrontend {
impl MiniFrontend {
pub fn new(window: Option<Box<dyn WindowUpdater>>, controller: Option<Box<dyn ControllerUpdater>>, keyboard: Option<Box<dyn KeyboardUpdater>>, mixer: HostData<AudioMixer>) -> Self {
Self {
buffer: vec![0; (WIDTH * HEIGHT) as usize],
modifiers: 0,
window,
controller,
@ -210,8 +208,7 @@ impl MiniFrontend {
let mut size = (WIDTH, HEIGHT);
if let Some(updater) = self.window.as_mut() {
size = updater.get_size();
self.buffer = vec![0; (size.0 * size.1) as usize];
size = updater.max_size();
}
let mut window = minifb::Window::new(
@ -251,8 +248,9 @@ impl MiniFrontend {
}
if let Some(updater) = self.window.as_mut() {
updater.update_frame(size.0, size.1, &mut self.buffer);
window.update_with_buffer(&self.buffer, size.0 as usize, size.1 as usize).unwrap();
if let Ok(frame) = updater.take_frame() {
window.update_with_buffer(&frame.bitmap, frame.width as usize, frame.height as usize).unwrap();
}
}
}
}

View File

@ -2,7 +2,7 @@
use moa_core::{debug, warning, error};
use moa_core::{System, Error, EdgeSignal, Clock, ClockElapsed, Address, Addressable, Steppable, Inspectable, Transmutable, TransmutableBox, read_beu16, dump_slice};
use moa_core::host::{Host, BlitableSurface, HostData};
use moa_core::host::gfx::{Frame, FrameSwapper};
use moa_core::host::gfx::{Frame, FrameQueue};
const REG_MODE_SET_1: usize = 0x00;
@ -647,10 +647,10 @@ impl Steppable for Ym7101 {
system.get_interrupt_controller().set(true, 6, 30)?;
}
self.swapper.swap();
if (self.state.mode_1 & MODE1_BF_DISABLE_DISPLAY) == 0 {
let mut frame = self.swapper.current.lock().unwrap();
let mut frame = Frame::new(self.state.screen_size.0 as u32 * 8, self.state.screen_size.1 as u32 * 8);
self.state.draw_frame(&mut frame);
self.queue.add(system.clock, frame);
}
self.frame_complete.signal();
@ -669,9 +669,8 @@ impl Steppable for Ym7101 {
}
pub struct Ym7101 {
swapper: FrameSwapper,
queue: FrameQueue,
state: Ym7101State,
sn_sound: TransmutableBox,
@ -681,12 +680,11 @@ pub struct Ym7101 {
impl Ym7101 {
pub fn new<H: Host>(host: &mut H, external_interrupt: HostData<bool>, sn_sound: TransmutableBox) -> Ym7101 {
let swapper = FrameSwapper::new(320, 224);
host.add_window(FrameSwapper::to_boxed(swapper.clone())).unwrap();
let queue = FrameQueue::new(320, 224);
host.add_window(Box::new(queue.clone())).unwrap();
Ym7101 {
swapper,
queue,
state: Ym7101State::new(),
sn_sound,
external_interrupt,
@ -707,7 +705,6 @@ impl Ym7101 {
REG_MODE_SET_2 => {
self.state.mode_2 = data;
self.state.update_screen_size();
self.swapper.set_size(self.state.screen_size.0 as u32 * 8, self.state.screen_size.1 as u32 * 8);
},
REG_SCROLL_A_ADDR => { self.state.scroll_a_addr = (data as usize) << 10; },
REG_WINDOW_ADDR => { self.state.window_addr = (data as usize) << 10; },
@ -719,7 +716,6 @@ impl Ym7101 {
REG_MODE_SET_4 => {
self.state.mode_4 = data;
self.state.update_screen_size();
self.swapper.set_size(self.state.screen_size.0 as u32 * 8, self.state.screen_size.1 as u32 * 8);
},
REG_HSCROLL_ADDR => { self.state.hscroll_addr = (data as usize) << 10; },
REG_AUTO_INCREMENT => { self.state.memory.transfer_auto_inc = data as u32; },

View File

@ -1,25 +1,24 @@
use std::sync::{Arc, Mutex};
use moa_core::{System, Error, ClockElapsed, Address, Addressable, Steppable, Transmutable};
use moa_core::host::gfx::Frame;
use moa_core::host::gfx::{Frame, FrameQueue};
use moa_core::host::{Host, BlitableSurface};
const SCRN_BASE: u32 = 0x07A700;
const SCRN_BASE: u32 = 0x07A700;
const SCRN_SIZE: (u32, u32) = (512, 342);
pub struct MacVideo {
frame: Arc<Mutex<Frame>>,
frame_queue: FrameQueue,
}
impl MacVideo {
pub fn create<H: Host>(host: &mut H) -> Result<Self, Error> {
let frame = Frame::new_shared(512, 342);
let frame_queue = FrameQueue::new(SCRN_SIZE.0, SCRN_SIZE.1);
host.add_window(Frame::new_updater(frame.clone()))?;
host.add_window(Box::new(frame_queue.clone()))?;
Ok(Self {
frame,
frame_queue,
})
}
}
@ -60,13 +59,15 @@ impl Iterator for BitIter {
impl Steppable for MacVideo {
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
let mut memory = system.get_bus();
let mut frame = self.frame.lock().unwrap();
for y in 0..342 {
for x in 0..(512 / 16) {
let word = memory.read_beu16((SCRN_BASE + (x * 2) + (y * (512 / 8))) as Address)?;
let mut frame = Frame::new(SCRN_SIZE.0, SCRN_SIZE.1);
for y in 0..SCRN_SIZE.1 {
for x in 0..(SCRN_SIZE.0 / 16) {
let word = memory.read_beu16((SCRN_BASE + (x * 2) + (y * (SCRN_SIZE.0 / 8))) as Address)?;
frame.blit(x * 16, y, BitIter::new(word), 16, 1);
}
}
self.frame_queue.add(system.clock, frame);
Ok(16_600_000)
}
}

View File

@ -2,31 +2,32 @@
use std::sync::{Arc, Mutex};
use moa_core::{System, Error, ClockElapsed, Address, Addressable, Steppable, Transmutable, debug, warning};
use moa_core::host::gfx::Frame;
use moa_core::host::gfx::{Frame, FrameQueue};
use moa_core::host::{Host, BlitableSurface, KeyboardUpdater, Key};
use super::keymap;
use super::charset::CharacterGenerator;
const DEV_NAME: &'static str = "model1";
const DEV_NAME: &'static str = "model1";
const SCREEN_SIZE: (u32, u32) = (384, 128);
pub struct Model1Peripherals {
frame: Arc<Mutex<Frame>>,
frame_queue: FrameQueue,
keyboard_mem: Arc<Mutex<[u8; 8]>>,
video_mem: [u8; 1024],
}
impl Model1Peripherals {
pub fn create<H: Host>(host: &mut H) -> Result<Self, Error> {
let frame = Frame::new_shared(384, 128);
let frame_queue = FrameQueue::new(SCREEN_SIZE.0, SCREEN_SIZE.1);
let keyboard_mem = Arc::new(Mutex::new([0; 8]));
host.add_window(Frame::new_updater(frame.clone()))?;
host.add_window(Box::new(frame_queue.clone()))?;
host.register_keyboard(Box::new(Model1KeyboardUpdater(keyboard_mem.clone())))?;
Ok(Self {
frame,
frame_queue,
keyboard_mem,
video_mem: [0; 1024],
})
@ -43,9 +44,8 @@ impl KeyboardUpdater for Model1KeyboardUpdater {
}
impl Steppable for Model1Peripherals {
fn step(&mut self, _system: &System) -> Result<ClockElapsed, Error> {
let mut frame = self.frame.lock().unwrap();
frame.clear(0);
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
let mut frame = Frame::new(SCREEN_SIZE.0, SCREEN_SIZE.1);
for y in 0..16 {
for x in 0..64 {
let ch = self.video_mem[x + (y * 64)];
@ -53,6 +53,7 @@ impl Steppable for Model1Peripherals {
frame.blit((x * 6) as u32, (y * 8) as u32, iter, 6, 8);
}
}
self.frame_queue.add(system.clock, frame);
Ok(16_630_000)
}