321 lines
10 KiB
Rust
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())
|
|
}
|
|
}
|