use crate::{Engine, OuterVm, TimeLimitReached};
use corevm_host::{AudioMode, VideoMode};
use jam_types::slot_period_sec;
use log::debug;
impl<O: OuterVm> Engine<O> {
pub(crate) fn advance_time(&mut self) -> Result<(), TimeLimitReached> {
advance_time(
&mut self.timestamp,
self.work_output.vm_state.video.as_ref(),
self.num_video_frames,
self.work_output.vm_state.audio.as_ref(),
self.num_audio_bytes,
)
}
}
fn max_video_frames_per_slot(video: Option<&VideoMode>) -> u64 {
video
.map(|video| (video.refresh_rate.get() as u64).saturating_mul(slot_period_sec()))
.unwrap_or(0)
}
fn max_audio_bytes_per_slot(audio: Option<&AudioMode>) -> u64 {
audio
.map(|audio| audio.bytes_per_second().get().saturating_mul(slot_period_sec()))
.unwrap_or(0)
}
pub(crate) trait VideoModeExt {
fn frames_per_slot(self) -> u64;
}
impl VideoModeExt for VideoMode {
fn frames_per_slot(self) -> u64 {
(u64::from(self.refresh_rate.get())).saturating_mul(slot_period_sec())
}
}
pub(crate) trait AudioModeExt {
fn bytes_per_slot(self) -> u64;
}
impl AudioModeExt for AudioMode {
fn bytes_per_slot(self) -> u64 {
self.bytes_per_second().get().saturating_mul(slot_period_sec())
}
}
fn advance_time(
timestamp: &mut u64,
video: Option<&VideoMode>,
video_frames: u64,
audio: Option<&AudioMode>,
audio_bytes: u64,
) -> Result<(), TimeLimitReached> {
let max_video_frames = max_video_frames_per_slot(video);
let max_audio_bytes = max_audio_bytes_per_slot(audio);
let video_enabled = max_video_frames != 0;
let video_limit_reached = video_enabled && video_frames >= max_video_frames;
let audio_enabled = max_audio_bytes != 0;
let audio_limit_reached = audio_enabled && audio_bytes >= max_audio_bytes;
let slot_in_ms = slot_period_sec() * 1_000_u64;
for (enabled, current, max) in [
(video_enabled, video_frames, max_video_frames),
(audio_enabled, audio_bytes, max_audio_bytes),
] {
if !enabled {
continue;
}
if current >= max {
*timestamp = slot_in_ms;
break;
}
let next_timestamp = current * slot_in_ms / max;
if next_timestamp > *timestamp {
*timestamp = next_timestamp;
}
}
let result = match (video_limit_reached, audio_limit_reached) {
(true, true) => Err(TimeLimitReached),
(true, false) if !audio_enabled => Err(TimeLimitReached),
(false, true) if !video_enabled => Err(TimeLimitReached),
_ => Ok(()),
};
if result.is_err() {
debug!("Video frame/audio samples limit reached: {video_frames} video frames, {audio_bytes} B of audio samples");
}
result
}