From d0a2682366050be3f33f7549c5691247cffd9ed2 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Mon, 15 Jun 2026 05:56:15 +0100 Subject: [PATCH] implements PLN-0104 --- .../console/prometeu-drivers/src/hardware.rs | 8 +- .../prometeu-drivers/src/test_platform.rs | 10 +- .../console/prometeu-hal/src/host_context.rs | 16 ++- crates/console/prometeu-hal/src/lib.rs | 6 +- crates/console/prometeu-hal/src/platform.rs | 135 +++++++++++++++++- .../src/services/vm_runtime/dispatch.rs | 33 +++-- .../src/services/vm_runtime/tick.rs | 34 +++-- 7 files changed, 193 insertions(+), 49 deletions(-) diff --git a/crates/console/prometeu-drivers/src/hardware.rs b/crates/console/prometeu-drivers/src/hardware.rs index 30349a28..244518e1 100644 --- a/crates/console/prometeu-drivers/src/hardware.rs +++ b/crates/console/prometeu-drivers/src/hardware.rs @@ -232,11 +232,11 @@ impl RuntimePlatform for Hardware { self } - fn audio(&self) -> &dyn prometeu_hal::AudioPlatform { + fn audio(&self) -> &dyn AudioBridge { &self.audio } - fn audio_mut(&mut self) -> &mut dyn prometeu_hal::AudioPlatform { + fn audio_mut(&mut self) -> &mut dyn AudioBridge { &mut self.audio } @@ -248,11 +248,11 @@ impl RuntimePlatform for Hardware { self } - fn assets(&self) -> &dyn prometeu_hal::AssetStoragePlatform { + fn assets(&self) -> &dyn AssetBridge { &self.assets } - fn assets_mut(&mut self) -> &mut dyn prometeu_hal::AssetStoragePlatform { + fn assets_mut(&mut self) -> &mut dyn AssetBridge { &mut self.assets } diff --git a/crates/console/prometeu-drivers/src/test_platform.rs b/crates/console/prometeu-drivers/src/test_platform.rs index 3769d964..1b34910b 100644 --- a/crates/console/prometeu-drivers/src/test_platform.rs +++ b/crates/console/prometeu-drivers/src/test_platform.rs @@ -1,7 +1,7 @@ use crate::hardware::Hardware; use crate::memory_banks::MemoryBanks; use prometeu_hal::{ - AssetStoragePlatform, AudioPlatform, Game2DFrameComposer, InputPlatform, RenderBackend, + AssetBridge, AudioBridge, Game2DFrameComposer, InputPlatform, RenderBackend, RenderSubmissionSink, RuntimePlatform, TelemetryPlatform, }; use std::sync::Arc; @@ -47,11 +47,11 @@ impl RuntimePlatform for TestPlatform { self.hardware.game2d_frame_composer() } - fn audio(&self) -> &dyn AudioPlatform { + fn audio(&self) -> &dyn AudioBridge { self.hardware.audio() } - fn audio_mut(&mut self) -> &mut dyn AudioPlatform { + fn audio_mut(&mut self) -> &mut dyn AudioBridge { self.hardware.audio_mut() } @@ -63,11 +63,11 @@ impl RuntimePlatform for TestPlatform { self.hardware.input_mut() } - fn assets(&self) -> &dyn AssetStoragePlatform { + fn assets(&self) -> &dyn AssetBridge { self.hardware.assets() } - fn assets_mut(&mut self) -> &mut dyn AssetStoragePlatform { + fn assets_mut(&mut self) -> &mut dyn AssetBridge { self.hardware.assets_mut() } diff --git a/crates/console/prometeu-hal/src/host_context.rs b/crates/console/prometeu-hal/src/host_context.rs index 714d44df..b4ec4862 100644 --- a/crates/console/prometeu-hal/src/host_context.rs +++ b/crates/console/prometeu-hal/src/host_context.rs @@ -1,13 +1,19 @@ use crate::hardware_bridge::HardwareBridge; +use crate::platform::RuntimePlatform; use crate::vm_fault::VmFault; pub struct HostContext<'a> { pub hw: Option<&'a mut dyn HardwareBridge>, + pub platform: Option<&'a mut dyn RuntimePlatform>, } impl<'a> HostContext<'a> { pub fn new(hw: Option<&'a mut dyn HardwareBridge>) -> Self { - Self { hw } + Self { hw, platform: None } + } + + pub fn new_platform(platform: Option<&'a mut dyn RuntimePlatform>) -> Self { + Self { hw: None, platform } } #[inline] @@ -17,6 +23,14 @@ impl<'a> HostContext<'a> { None => Err(VmFault::Unavailable), } } + + #[inline] + pub fn require_platform(&mut self) -> Result<&mut dyn RuntimePlatform, VmFault> { + match &mut self.platform { + Some(platform) => Ok(*platform), + None => Err(VmFault::Unavailable), + } + } } pub trait HostContextProvider { diff --git a/crates/console/prometeu-hal/src/lib.rs b/crates/console/prometeu-hal/src/lib.rs index 868b1401..1805e122 100644 --- a/crates/console/prometeu-hal/src/lib.rs +++ b/crates/console/prometeu-hal/src/lib.rs @@ -49,9 +49,9 @@ pub use native_interface::{NativeInterface, SyscallId}; pub use pad_bridge::PadBridge; pub use platform::{ AssetStoragePlatform, AudioPlatform, ClockPacingPlatform, Game2DFrameComposer, InputPlatform, - LegacyHardwareGame2DFrameComposer, LegacyHardwareRenderSubmissionSink, NoopTelemetryPlatform, - RenderBackend, RenderSubmissionSink, RenderSubmitError, RuntimePlatform, TelemetryPlatform, - TestPlatform, + LegacyHardwareGame2DFrameComposer, LegacyHardwareRenderSubmissionSink, + LegacyHardwareRuntimePlatform, NoopTelemetryPlatform, RenderBackend, RenderSubmissionSink, + RenderSubmitError, RuntimePlatform, TelemetryPlatform, TestPlatform, }; pub use render_submission::{ BoundScenePacket, Camera2D, ComposerFramePacket, FrameId, Game2DFramePacket, GameSpritePacket, diff --git a/crates/console/prometeu-hal/src/platform.rs b/crates/console/prometeu-hal/src/platform.rs index e50dc0ed..68cc42b0 100644 --- a/crates/console/prometeu-hal/src/platform.rs +++ b/crates/console/prometeu-hal/src/platform.rs @@ -103,6 +103,89 @@ impl Game2DFrameComposer for LegacyHardwareGame2DFrameComposer<'_> { } } +pub struct LegacyHardwareRuntimePlatform<'a> { + hw: &'a mut dyn HardwareBridge, +} + +impl<'a> LegacyHardwareRuntimePlatform<'a> { + pub fn new(hw: &'a mut dyn HardwareBridge) -> Self { + Self { hw } + } +} + +impl RenderSubmissionSink for LegacyHardwareRuntimePlatform<'_> { + fn submit_render_submission( + &mut self, + submission: RenderSubmission, + ) -> Result<(), RenderSubmitError> { + self.hw.publish_render_submission(&submission); + Ok(()) + } +} + +impl RenderBackend for LegacyHardwareRuntimePlatform<'_> { + fn render_frame(&mut self) { + self.hw.render_frame(); + } +} + +impl Game2DFrameComposer for LegacyHardwareRuntimePlatform<'_> { + fn begin_frame(&mut self) { + self.hw.begin_frame(); + } + + fn bind_scene(&mut self, scene_bank_id: usize) -> ComposerOpStatus { + if self.hw.bind_scene(scene_bank_id) { + ComposerOpStatus::Ok + } else { + ComposerOpStatus::SceneUnavailable + } + } + + fn unbind_scene(&mut self) { + self.hw.unbind_scene(); + } + + fn set_camera(&mut self, x: i32, y: i32) -> ComposerOpStatus { + self.hw.set_camera(x, y); + ComposerOpStatus::Ok + } + + fn emit_sprite(&mut self, sprite: Sprite) -> ComposerOpStatus { + if !self.hw.has_glyph_bank(sprite.bank_id as usize) { + return ComposerOpStatus::BankInvalid; + } + + if self.hw.emit_sprite(sprite) { + ComposerOpStatus::Ok + } else { + ComposerOpStatus::SpriteOverflow + } + } + + fn close_game2d_packet(&self) -> Game2DFramePacket { + self.hw.close_game2d_packet() + } +} + +impl InputPlatform for LegacyHardwareRuntimePlatform<'_> { + fn pad(&self) -> &dyn PadBridge { + self.hw.pad() + } + + fn pad_mut(&mut self) -> &mut dyn PadBridge { + self.hw.pad_mut() + } + + fn touch(&self) -> &dyn TouchBridge { + self.hw.touch() + } + + fn touch_mut(&mut self) -> &mut dyn TouchBridge { + self.hw.touch_mut() + } +} + pub trait AudioPlatform: AudioBridge {} impl AudioPlatform for T {} @@ -129,19 +212,63 @@ pub struct NoopTelemetryPlatform; impl TelemetryPlatform for NoopTelemetryPlatform {} +static LEGACY_NOOP_TELEMETRY: NoopTelemetryPlatform = NoopTelemetryPlatform; + pub trait RuntimePlatform { fn render_submission_sink(&mut self) -> &mut dyn RenderSubmissionSink; fn render_backend(&mut self) -> &mut dyn RenderBackend; fn game2d_frame_composer(&mut self) -> &mut dyn Game2DFrameComposer; - fn audio(&self) -> &dyn AudioPlatform; - fn audio_mut(&mut self) -> &mut dyn AudioPlatform; + fn audio(&self) -> &dyn AudioBridge; + fn audio_mut(&mut self) -> &mut dyn AudioBridge; fn input(&self) -> &dyn InputPlatform; fn input_mut(&mut self) -> &mut dyn InputPlatform; - fn assets(&self) -> &dyn AssetStoragePlatform; - fn assets_mut(&mut self) -> &mut dyn AssetStoragePlatform; + fn assets(&self) -> &dyn AssetBridge; + fn assets_mut(&mut self) -> &mut dyn AssetBridge; fn telemetry(&self) -> &dyn TelemetryPlatform; } +impl RuntimePlatform for LegacyHardwareRuntimePlatform<'_> { + fn render_submission_sink(&mut self) -> &mut dyn RenderSubmissionSink { + self + } + + fn render_backend(&mut self) -> &mut dyn RenderBackend { + self + } + + fn game2d_frame_composer(&mut self) -> &mut dyn Game2DFrameComposer { + self + } + + fn audio(&self) -> &dyn AudioBridge { + self.hw.audio() + } + + fn audio_mut(&mut self) -> &mut dyn AudioBridge { + self.hw.audio_mut() + } + + fn input(&self) -> &dyn InputPlatform { + self + } + + fn input_mut(&mut self) -> &mut dyn InputPlatform { + self + } + + fn assets(&self) -> &dyn AssetBridge { + self.hw.assets() + } + + fn assets_mut(&mut self) -> &mut dyn AssetBridge { + self.hw.assets_mut() + } + + fn telemetry(&self) -> &dyn TelemetryPlatform { + &LEGACY_NOOP_TELEMETRY + } +} + pub trait TestPlatform: RuntimePlatform {} impl TestPlatform for T {} diff --git a/crates/console/prometeu-system/src/services/vm_runtime/dispatch.rs b/crates/console/prometeu-system/src/services/vm_runtime/dispatch.rs index 0106a653..932b8589 100644 --- a/crates/console/prometeu-system/src/services/vm_runtime/dispatch.rs +++ b/crates/console/prometeu-system/src/services/vm_runtime/dispatch.rs @@ -12,9 +12,8 @@ use prometeu_hal::sprite::Sprite; use prometeu_hal::syscalls::Syscall; use prometeu_hal::vm_fault::VmFault; use prometeu_hal::{ - AudioOpStatus, ComposerOpStatus, Game2DFrameComposer, Gfx2dCommand, GfxUiCommand, HostContext, - HostReturn, LegacyHardwareGame2DFrameComposer, NativeInterface, SyscallId, expect_bool, - expect_int, + AudioOpStatus, ComposerOpStatus, Gfx2dCommand, GfxUiCommand, HostContext, HostReturn, + NativeInterface, SyscallId, expect_bool, expect_int, }; use std::collections::HashMap; use std::ops::{Deref, DerefMut}; @@ -180,7 +179,7 @@ impl NativeInterface for VmRuntimeHost<'_> { self.ensure_game_profile_syscall(syscall)?; - let hw = ctx.require_hw()?; + let platform = ctx.require_platform()?; match syscall { Syscall::SystemHasCart => unreachable!(), @@ -387,8 +386,7 @@ impl NativeInterface for VmRuntimeHost<'_> { }; let status = catch_unwind(AssertUnwindSafe(|| { - let mut composer = LegacyHardwareGame2DFrameComposer::new(hw); - composer.bind_scene(scene_bank_id) + platform.game2d_frame_composer().bind_scene(scene_bank_id) })) .map_err(|payload| VmFault::Panic(Self::panic_payload_to_string(payload)))?; @@ -396,14 +394,14 @@ impl NativeInterface for VmRuntimeHost<'_> { Ok(()) } Syscall::ComposerUnbindScene => { - LegacyHardwareGame2DFrameComposer::new(hw).unbind_scene(); + platform.game2d_frame_composer().unbind_scene(); ret.push_int(ComposerOpStatus::Ok as i64); Ok(()) } Syscall::ComposerSetCamera => { let x = Self::int_arg_to_i32_trap(expect_int(args, 0)?, "camera x")?; let y = Self::int_arg_to_i32_trap(expect_int(args, 1)?, "camera y")?; - LegacyHardwareGame2DFrameComposer::new(hw).set_camera(x, y); + platform.game2d_frame_composer().set_camera(x, y); Ok(()) } Syscall::ComposerEmitSprite => { @@ -451,7 +449,7 @@ impl NativeInterface for VmRuntimeHost<'_> { } }; - let status = LegacyHardwareGame2DFrameComposer::new(hw).emit_sprite(Sprite { + let status = platform.game2d_frame_composer().emit_sprite(Sprite { glyph: Glyph { glyph_id, palette_id }, x, y, @@ -488,7 +486,7 @@ impl NativeInterface for VmRuntimeHost<'_> { return Ok(()); } - let status = hw.audio_mut().play( + let status = platform.audio_mut().play( 0, sample_id_raw as u16, voice_id_raw as usize, @@ -518,7 +516,8 @@ impl NativeInterface for VmRuntimeHost<'_> { return Ok(()); } - if hw.assets().slot_info(SlotRef::audio(bank_id as usize)).asset_id.is_none() { + if platform.assets().slot_info(SlotRef::audio(bank_id as usize)).asset_id.is_none() + { ret.push_int(AudioOpStatus::BankInvalid as i64); return Ok(()); } @@ -534,7 +533,7 @@ impl NativeInterface for VmRuntimeHost<'_> { return Ok(()); } - let status = hw.audio_mut().play( + let status = platform.audio_mut().play( bank_id, sample_id_raw as u16, voice_id_raw as usize, @@ -731,7 +730,7 @@ impl NativeInterface for VmRuntimeHost<'_> { })?; let slot_index = expect_int(args, 1)? as usize; - match hw.assets().load(asset_id, slot_index) { + match platform.assets().load(asset_id, slot_index) { Ok(handle) => { ret.push_int(AssetOpStatus::Ok as i64); ret.push_int(handle as i64); @@ -745,16 +744,16 @@ impl NativeInterface for VmRuntimeHost<'_> { } } Syscall::AssetStatus => { - ret.push_int(hw.assets().status(expect_int(args, 0)? as u32) as i64); + ret.push_int(platform.assets().status(expect_int(args, 0)? as u32) as i64); Ok(()) } Syscall::AssetCommit => { - let status = hw.assets().commit(expect_int(args, 0)? as u32); + let status = platform.assets().commit(expect_int(args, 0)? as u32); ret.push_int(status as i64); Ok(()) } Syscall::AssetCancel => { - let status = hw.assets().cancel(expect_int(args, 0)? as u32); + let status = platform.assets().cancel(expect_int(args, 0)? as u32); ret.push_int(status as i64); Ok(()) } @@ -764,7 +763,7 @@ impl NativeInterface for VmRuntimeHost<'_> { 1 => BankType::SOUNDS, _ => return Err(VmFault::Trap(TRAP_TYPE, "Invalid asset type".to_string())), }; - let telemetry = hw + let telemetry = platform .assets() .bank_telemetry() .into_iter() diff --git a/crates/console/prometeu-system/src/services/vm_runtime/tick.rs b/crates/console/prometeu-system/src/services/vm_runtime/tick.rs index 1dd25816..0aac4cb7 100644 --- a/crates/console/prometeu-system/src/services/vm_runtime/tick.rs +++ b/crates/console/prometeu-system/src/services/vm_runtime/tick.rs @@ -9,8 +9,9 @@ use crate::services::memcard::MemcardService; use prometeu_hal::asset::{BankTelemetry, BankType}; use prometeu_hal::log::{LogLevel, LogService, LogSource}; use prometeu_hal::{ - FrameId, HardwareBridge, HostContext, InputSignals, LegacyHardwareRenderSubmissionSink, - RenderSubmission, RenderSubmissionPacket, RenderSubmissionSink, ShellUiFramePacket, + FrameId, HardwareBridge, HostContext, InputSignals, LegacyHardwareRuntimePlatform, + RenderSubmission, RenderSubmissionPacket, RenderSubmissionSink, RuntimePlatform, + ShellUiFramePacket, }; use prometeu_vm::LogicalFrameEndingReason; use std::collections::HashMap; @@ -53,9 +54,9 @@ impl VirtualMachineRuntime { } fn bank_telemetry_summary( - hw: &dyn HardwareBridge, + platform: &dyn RuntimePlatform, ) -> (BankTelemetry, BankTelemetry, BankTelemetry) { - let telemetry = hw.assets().bank_telemetry(); + let telemetry = platform.assets().bank_telemetry(); let glyph = telemetry.iter().find(|entry| entry.bank_type == BankType::GLYPH).cloned().unwrap_or( BankTelemetry { bank_type: BankType::GLYPH, used_slots: 0, total_slots: 0 }, @@ -79,7 +80,8 @@ impl VirtualMachineRuntime { hw: &mut dyn HardwareBridge, ) -> Option { let step_result = { - let mut ctx = HostContext::new(Some(hw)); + let mut platform = LegacyHardwareRuntimePlatform::new(hw); + let mut ctx = HostContext::new_platform(Some(&mut platform)); let mut fs = VirtualFS::new(); let mut fs_state = FsState::Unmounted; let mut memcard = MemcardService::new(); @@ -143,6 +145,7 @@ impl VirtualMachineRuntime { } self.update_fs(log_service, fs, fs_state); + let mut platform = LegacyHardwareRuntimePlatform::new(hw); if !self.logical_frame_active { if RenderManager::policy_for_app_mode(self.current_cartridge_app_mode) @@ -154,7 +157,7 @@ impl VirtualMachineRuntime { } self.logical_frame_active = true; self.logical_frame_remaining_cycles = Self::CYCLES_PER_LOGICAL_FRAME; - self.begin_logical_frame(signals, hw); + self.begin_logical_frame(signals, &mut platform); if self.needs_prepare_entry_call || vm.call_stack_is_empty() { vm.prepare_boot_call(); @@ -182,7 +185,7 @@ impl VirtualMachineRuntime { if budget > 0 { let run_result = { - let mut ctx = HostContext::new(Some(hw)); + let mut ctx = HostContext::new_platform(Some(&mut platform)); let mut host = VmRuntimeHost { runtime: self, log_service, @@ -250,7 +253,7 @@ impl VirtualMachineRuntime { self.current_app_id, ); if self.current_cartridge_app_mode == AppMode::Game { - let mut packet = hw.close_game2d_packet(); + let mut packet = platform.game2d_frame_composer().close_game2d_packet(); packet.gfx2d = std::mem::take(&mut self.gfx2d_commands); self.render_manager .close_frame_with_packet(RenderSubmissionPacket::Game2D(packet)) @@ -264,8 +267,8 @@ impl VirtualMachineRuntime { } let render_outcome = catch_unwind(AssertUnwindSafe(|| { - let mut sink = LegacyHardwareRenderSubmissionSink::new(hw); - let mut surface = HardwareRenderSurface { sink: &mut sink }; + let mut surface = + HardwareRenderSurface { sink: platform.render_submission_sink() }; match self .render_manager .active_render_policy() @@ -307,7 +310,8 @@ impl VirtualMachineRuntime { } // 1. Snapshot full telemetry at logical frame end - let (glyph_bank, sound_bank, scene_bank) = Self::bank_telemetry_summary(hw); + let (glyph_bank, sound_bank, scene_bank) = + Self::bank_telemetry_summary(&platform); self.atomic_telemetry .glyph_slots_used .store(glyph_bank.used_slots as u32, Ordering::Relaxed); @@ -398,7 +402,7 @@ impl VirtualMachineRuntime { // 2. High-frequency telemetry update (only if inspection is active) if self.inspection_active { - let (glyph_bank, sound_bank, scene_bank) = Self::bank_telemetry_summary(hw); + let (glyph_bank, sound_bank, scene_bank) = Self::bank_telemetry_summary(&platform); self.atomic_telemetry .glyph_slots_used .store(glyph_bank.used_slots as u32, Ordering::Relaxed); @@ -435,10 +439,10 @@ impl VirtualMachineRuntime { pub(crate) fn begin_logical_frame( &mut self, _signals: &InputSignals, - hw: &mut dyn HardwareBridge, + platform: &mut dyn RuntimePlatform, ) { - hw.begin_frame(); - hw.audio_mut().clear_commands(); + platform.game2d_frame_composer().begin_frame(); + platform.audio_mut().clear_commands(); self.logs_written_this_frame.clear(); } }