2026-07-05 00:26:16 +01:00

321 lines
10 KiB
Rust

use crate::CrashReport;
use crate::os::{GameLifecycleEvent, SystemOS};
use crate::task::TaskId;
use crate::vm_session::VmSession;
use crate::{RenderWorkerConfig, RenderWorkerController};
use prometeu_bytecode::Value;
use prometeu_hal::app_mode::AppMode;
use prometeu_hal::telemetry::{CertificationConfig, TelemetryFrame};
use prometeu_hal::{
FrameId, InputSignals, RenderOwnership, RenderWorkerBackend, RenderWorkerError,
RenderWorkerFrameSink, RuntimePlatform,
};
use std::sync::atomic::Ordering;
pub struct VmFacade<'a> {
pub(in crate::os) os: &'a mut SystemOS,
}
impl<'a> VmFacade<'a> {
pub fn tick_session(
&mut self,
task_id: TaskId,
signals: &InputSignals,
platform: &mut dyn RuntimePlatform,
) -> Option<CrashReport> {
self.deliver_pending_game_lifecycle_events_to_session(task_id);
let Some(session) = self.os.vm_sessions.for_task_mut(task_id) else {
return Some(CrashReport::VmPanic {
message: format!("task {:?} has no VM session", task_id),
pc: None,
});
};
session.runtime.tick(
&mut self.os.log_service,
&mut self.os.fs,
&mut session.fs_state,
&mut self.os.memcard,
&mut session.open_files,
&mut session.next_handle,
&mut session.vm,
signals,
platform,
)
}
fn deliver_pending_game_lifecycle_events_to_session(&mut self, task_id: TaskId) {
let Some(session) = self.os.vm_sessions.for_task(task_id) else {
return;
};
if session.runtime.current_cartridge_app_mode != AppMode::Game {
return;
}
let mut retained = Vec::new();
let mut delivered = Vec::new();
for event in self.os.game_lifecycle_events.drain(..) {
if event.task_id == task_id {
delivered.push(event);
} else {
retained.push(event);
}
}
self.os.game_lifecycle_events = retained;
if let Some(session) = self.os.vm_sessions.for_task_mut(task_id) {
session.runtime.deliver_game_lifecycle_events(delivered);
}
}
pub fn reset_global_runtime_state(&mut self) {
self.os.clear_cartridge_service_state();
self.os.vm_runtime.clear_cartridge_state();
}
pub fn debug_step_active_session(
&mut self,
platform: &mut dyn RuntimePlatform,
) -> Option<CrashReport> {
let Some(task_id) = self.active_session_task_id() else {
return Some(CrashReport::VmPanic {
message: "debug step requested without an active VM session".to_string(),
pc: None,
});
};
let Some(session) = self.os.vm_sessions.for_task_mut(task_id) else {
return Some(CrashReport::VmPanic {
message: format!("active debug task {:?} has no VM session", task_id),
pc: None,
});
};
session.runtime.debug_step_instruction(&mut self.os.log_service, &mut session.vm, platform)
}
pub fn active_pc(&self) -> Option<usize> {
self.active_session().map(|session| session.vm.pc())
}
pub fn active_operand_stack_top(&self, n: usize) -> Vec<Value> {
self.active_session().map(|session| session.vm.operand_stack_top(n)).unwrap_or_default()
}
pub fn insert_active_breakpoint(&mut self, pc: usize) -> bool {
if let Some(session) = self.active_session_mut() {
session.vm.insert_breakpoint(pc);
return true;
}
false
}
pub fn remove_active_breakpoint(&mut self, pc: usize) -> bool {
if let Some(session) = self.active_session_mut() {
session.vm.remove_breakpoint(pc);
return true;
}
false
}
pub fn active_breakpoints_list(&self) -> Vec<usize> {
self.active_session().map(|session| session.vm.breakpoints_list()).unwrap_or_default()
}
pub fn paused(&self) -> bool {
self.active_session()
.map(|session| session.runtime.paused)
.unwrap_or(self.os.vm_runtime.paused)
}
pub fn set_paused(&mut self, paused: bool) {
if let Some(session) = self.active_session_mut() {
session.runtime.paused = paused;
return;
}
self.os.vm_runtime.paused = paused;
}
pub fn set_inspection_active(&mut self, active: bool) {
if let Some(session) = self.active_session_mut() {
session.runtime.inspection_active = active;
return;
}
self.os.vm_runtime.inspection_active = active;
}
pub fn request_debug_step(&mut self) {
if let Some(session) = self.active_session_mut() {
session.runtime.debug_step_request = true;
return;
}
self.os.vm_runtime.debug_step_request = true;
}
pub fn debug_step_requested(&self) -> bool {
self.active_session()
.map(|session| session.runtime.debug_step_request)
.unwrap_or(self.os.vm_runtime.debug_step_request)
}
pub fn logical_frame_active(&self) -> bool {
self.active_session()
.map(|session| session.runtime.logical_frame_active)
.unwrap_or(self.os.vm_runtime.logical_frame_active)
}
pub fn delivered_game_lifecycle_events(&self) -> &[GameLifecycleEvent] {
self.active_session()
.map(|session| session.runtime.delivered_game_lifecycle_events())
.unwrap_or_else(|| self.os.vm_runtime.delivered_game_lifecycle_events())
}
pub fn take_delivered_game_lifecycle_events(&mut self) -> Vec<GameLifecycleEvent> {
if let Some(session) = self.active_session_mut() {
return session.runtime.take_delivered_game_lifecycle_events();
}
self.os.vm_runtime.take_delivered_game_lifecycle_events()
}
pub fn tick_index(&self) -> u64 {
self.active_session()
.map(|session| session.runtime.tick_index)
.unwrap_or(self.os.vm_runtime.tick_index)
}
pub fn logical_frame_index(&self) -> u64 {
self.active_session()
.map(|session| session.runtime.logical_frame_index)
.unwrap_or(self.os.vm_runtime.logical_frame_index)
}
pub fn last_frame_cpu_time_us(&self) -> u64 {
self.active_session()
.map(|session| session.runtime.last_frame_cpu_time_us)
.unwrap_or(self.os.vm_runtime.last_frame_cpu_time_us)
}
pub fn completed_logical_frames(&self) -> u64 {
self.active_session()
.map(|session| {
session
.runtime
.atomic_telemetry
.completed_logical_frames
.load(Ordering::Relaxed)
.into()
})
.unwrap_or_else(|| {
self.os
.vm_runtime
.atomic_telemetry
.completed_logical_frames
.load(Ordering::Relaxed)
.into()
})
}
pub fn telemetry_snapshot(&self) -> TelemetryFrame {
self.active_session()
.map(|session| session.runtime.atomic_telemetry.snapshot())
.unwrap_or_else(|| self.os.vm_runtime.atomic_telemetry.snapshot())
}
pub fn start_render_worker<B, S>(&mut self, config: RenderWorkerConfig, backend: B, sink: S)
where
B: RenderWorkerBackend,
S: RenderWorkerFrameSink,
{
self.os.vm_runtime.start_render_worker(config, backend, sink);
}
pub fn stop_render_worker(&mut self) -> Result<(), RenderWorkerError> {
self.os.vm_runtime.stop_render_worker()
}
pub fn render_worker_controller(&self) -> Option<&RenderWorkerController> {
self.os.vm_runtime.render_worker_controller.as_ref()
}
pub fn record_repeated_render_worker_frame(&self, frame_id: FrameId) {
self.os.vm_runtime.record_repeated_render_worker_frame(frame_id);
}
pub fn active_render_ownership(&self) -> RenderOwnership {
self.os.vm_runtime.render_manager.active_ownership()
}
pub fn transition_render_owner(&mut self, app_mode: AppMode, app_id: u32) {
self.os.vm_runtime.render_manager.transition_render_owner(app_mode, app_id);
self.os.vm_runtime.sync_render_worker_ownership();
self.os.vm_runtime.render_manager.sync_telemetry(&self.os.vm_runtime.atomic_telemetry);
}
pub fn cert_config(&self) -> &CertificationConfig {
&self.os.vm_runtime.certifier.config
}
pub fn last_crash_report(&self) -> Option<&CrashReport> {
self.active_session()
.and_then(|session| session.runtime.last_crash_report.as_ref())
.or(self.os.vm_runtime.last_crash_report.as_ref())
}
pub fn current_app_id(&self) -> u32 {
self.active_session()
.map(|session| session.runtime.current_app_id)
.unwrap_or(self.os.vm_runtime.current_app_id)
}
pub fn current_cartridge_title(&self) -> String {
self.active_session()
.map(|session| session.runtime.current_cartridge_title.clone())
.unwrap_or_else(|| self.os.vm_runtime.current_cartridge_title.clone())
}
pub fn current_cartridge_app_version(&self) -> String {
self.active_session()
.map(|session| session.runtime.current_cartridge_app_version.clone())
.unwrap_or_else(|| self.os.vm_runtime.current_cartridge_app_version.clone())
}
pub fn current_cartridge_app_mode(&self) -> AppMode {
self.active_session()
.map(|session| session.runtime.current_cartridge_app_mode)
.unwrap_or(self.os.vm_runtime.current_cartridge_app_mode)
}
fn active_session(&self) -> Option<&VmSession> {
self.os
.task_manager
.foreground_task()
.and_then(|task_id| self.os.vm_sessions.for_task(task_id))
.or_else(|| {
self.os
.foreground_stack
.resident_game_task()
.and_then(|task_id| self.os.vm_sessions.for_task(task_id))
})
}
fn active_session_mut(&mut self) -> Option<&mut VmSession> {
let task_id = self.active_session_task_id()?;
self.os.vm_sessions.for_task_mut(task_id)
}
fn active_session_task_id(&self) -> Option<TaskId> {
self.os
.task_manager
.foreground_task()
.or_else(|| self.os.foreground_stack.resident_game_task())
}
}