refactor(runtime): inline FrameExecutor, add joypad2 and video mode setter
Remove FrameExecutor indirection by inlining its logic into RuntimeHostLoop. Add set_video_mode() for NTSC/PAL switching, set_joypad2_buttons() for player 2 input, and fix mapper scanline IRQ test timing.
This commit is contained in:
@@ -1,23 +1,35 @@
|
||||
use crate::runtime::{NesRuntime, RuntimeError};
|
||||
use crate::runtime::{
|
||||
AudioMixer, FRAME_HEIGHT, FRAME_RGBA_BYTES, FRAME_WIDTH, NesRuntime, RuntimeError,
|
||||
};
|
||||
|
||||
use super::clock::{FrameClock, NoopClock, PacingClock};
|
||||
use super::config::HostConfig;
|
||||
use super::executor::{FrameExecution, FrameExecutor};
|
||||
use super::io::{AudioOutput, InputProvider, VideoOutput};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub struct FrameExecution {
|
||||
pub frame_number: u64,
|
||||
pub audio_samples: usize,
|
||||
}
|
||||
|
||||
pub struct RuntimeHostLoop<C = PacingClock> {
|
||||
runtime: NesRuntime,
|
||||
executor: FrameExecutor,
|
||||
mixer: AudioMixer,
|
||||
frame_buffer: Vec<u8>,
|
||||
audio_buffer: Vec<f32>,
|
||||
clock: C,
|
||||
}
|
||||
|
||||
impl RuntimeHostLoop<PacingClock> {
|
||||
pub fn new(runtime: NesRuntime, sample_rate: u32) -> Self {
|
||||
let executor = FrameExecutor::from_runtime(&runtime, sample_rate);
|
||||
let mixer = runtime.default_audio_mixer(sample_rate);
|
||||
let clock = PacingClock::from_runtime(&runtime);
|
||||
Self {
|
||||
runtime,
|
||||
executor,
|
||||
mixer,
|
||||
frame_buffer: vec![0; FRAME_RGBA_BYTES],
|
||||
audio_buffer: Vec::new(),
|
||||
clock,
|
||||
}
|
||||
}
|
||||
@@ -26,7 +38,7 @@ impl RuntimeHostLoop<PacingClock> {
|
||||
runtime: NesRuntime,
|
||||
config: HostConfig,
|
||||
) -> RuntimeHostLoop<Box<dyn FrameClock>> {
|
||||
let executor = FrameExecutor::from_runtime(&runtime, config.sample_rate);
|
||||
let mixer = runtime.default_audio_mixer(config.sample_rate);
|
||||
let clock: Box<dyn FrameClock> = if config.pacing {
|
||||
Box::new(PacingClock::from_runtime(&runtime))
|
||||
} else {
|
||||
@@ -34,7 +46,9 @@ impl RuntimeHostLoop<PacingClock> {
|
||||
};
|
||||
RuntimeHostLoop {
|
||||
runtime,
|
||||
executor,
|
||||
mixer,
|
||||
frame_buffer: vec![0; FRAME_RGBA_BYTES],
|
||||
audio_buffer: Vec::new(),
|
||||
clock,
|
||||
}
|
||||
}
|
||||
@@ -45,10 +59,12 @@ where
|
||||
C: FrameClock,
|
||||
{
|
||||
pub fn with_clock(runtime: NesRuntime, sample_rate: u32, clock: C) -> Self {
|
||||
let executor = FrameExecutor::from_runtime(&runtime, sample_rate);
|
||||
let mixer = runtime.default_audio_mixer(sample_rate);
|
||||
Self {
|
||||
runtime,
|
||||
executor,
|
||||
mixer,
|
||||
frame_buffer: vec![0; FRAME_RGBA_BYTES],
|
||||
audio_buffer: Vec::new(),
|
||||
clock,
|
||||
}
|
||||
}
|
||||
@@ -65,12 +81,12 @@ where
|
||||
self.runtime
|
||||
}
|
||||
|
||||
pub fn executor(&self) -> &FrameExecutor {
|
||||
&self.executor
|
||||
pub fn mixer(&self) -> &AudioMixer {
|
||||
&self.mixer
|
||||
}
|
||||
|
||||
pub fn executor_mut(&mut self) -> &mut FrameExecutor {
|
||||
&mut self.executor
|
||||
pub fn mixer_mut(&mut self) -> &mut AudioMixer {
|
||||
&mut self.mixer
|
||||
}
|
||||
|
||||
pub fn clock(&self) -> &C {
|
||||
@@ -108,8 +124,20 @@ where
|
||||
V: VideoOutput,
|
||||
A: AudioOutput,
|
||||
{
|
||||
self.executor
|
||||
.execute_frame(&mut self.runtime, input, video, audio)
|
||||
self.runtime.set_buttons(input.poll_buttons());
|
||||
self.audio_buffer.clear();
|
||||
|
||||
self.runtime
|
||||
.run_until_frame_complete_with_audio(&mut self.mixer, &mut self.audio_buffer)?;
|
||||
self.runtime.render_frame_rgba(&mut self.frame_buffer)?;
|
||||
|
||||
audio.push_samples(&self.audio_buffer);
|
||||
video.present_rgba(&self.frame_buffer, FRAME_WIDTH, FRAME_HEIGHT);
|
||||
|
||||
Ok(FrameExecution {
|
||||
frame_number: self.runtime.frame_number(),
|
||||
audio_samples: self.audio_buffer.len(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn run_frames<I, V, A>(
|
||||
|
||||
Reference in New Issue
Block a user