diff --git a/crates/console/prometeu-drivers/src/hardware.rs b/crates/console/prometeu-drivers/src/hardware.rs index 244518e1..6646e461 100644 --- a/crates/console/prometeu-drivers/src/hardware.rs +++ b/crates/console/prometeu-drivers/src/hardware.rs @@ -11,8 +11,8 @@ use crate::touch::Touch; use prometeu_hal::cartridge::AssetsPayloadSource; use prometeu_hal::sprite::Sprite; use prometeu_hal::{ - AssetBridge, AudioBridge, GfxBridge, HardwareBridge, InputPlatform, NoopTelemetryPlatform, - PadBridge, RenderBackend, RuntimePlatform, TelemetryPlatform, TouchBridge, + AssetBridge, AudioBridge, InputPlatform, NoopTelemetryPlatform, PadBridge, RenderBackend, + RuntimePlatform, TelemetryPlatform, TouchBridge, }; use prometeu_hal::{ Game2DFrameComposer, Game2DFramePacket, RenderSubmission, RenderSubmissionPacket, @@ -53,32 +53,8 @@ impl Default for Hardware { } } -impl HardwareBridge for Hardware { - fn begin_frame(&mut self) { - self.frame_composer.begin_frame(); - } - - fn bind_scene(&mut self, scene_bank_id: usize) -> bool { - self.frame_composer.bind_scene(scene_bank_id) - } - - fn unbind_scene(&mut self) { - self.frame_composer.unbind_scene(); - } - - fn set_camera(&mut self, x: i32, y: i32) { - self.frame_composer.set_camera(x, y); - } - - fn emit_sprite(&mut self, sprite: Sprite) -> bool { - self.frame_composer.emit_sprite(sprite) - } - - fn close_game2d_packet(&self) -> Game2DFramePacket { - self.frame_composer.close_game2d_packet() - } - - fn publish_render_submission(&mut self, submission: &RenderSubmission) { +impl Hardware { + fn consume_render_submission(&mut self, submission: &RenderSubmission) { match &submission.packet { RenderSubmissionPacket::Game2D(packet) => { if packet.composer.bound_scene.is_none() { @@ -94,54 +70,6 @@ impl HardwareBridge for Hardware { } self.gfx.present(); } - - fn render_frame(&mut self) { - if self.frame_composer.active_scene_id().is_none() { - let packet = self.frame_composer.close_game2d_packet(); - self.gfx.render_game2d_frame_packet(&packet); - } else { - self.frame_composer.render_frame(&mut self.gfx); - } - } - - fn has_glyph_bank(&self, bank_id: usize) -> bool { - self.gfx.glyph_banks.glyph_bank_slot(bank_id).is_some() - } - - fn gfx(&self) -> &dyn GfxBridge { - &self.gfx - } - fn gfx_mut(&mut self) -> &mut dyn GfxBridge { - &mut self.gfx - } - - fn audio(&self) -> &dyn AudioBridge { - &self.audio - } - fn audio_mut(&mut self) -> &mut dyn AudioBridge { - &mut self.audio - } - - fn pad(&self) -> &dyn PadBridge { - &self.pad - } - fn pad_mut(&mut self) -> &mut dyn PadBridge { - &mut self.pad - } - - fn touch(&self) -> &dyn TouchBridge { - &self.touch - } - fn touch_mut(&mut self) -> &mut dyn TouchBridge { - &mut self.touch - } - - fn assets(&self) -> &dyn AssetBridge { - &self.assets - } - fn assets_mut(&mut self) -> &mut dyn AssetBridge { - &mut self.assets - } } impl RenderSubmissionSink for Hardware { @@ -149,14 +77,19 @@ impl RenderSubmissionSink for Hardware { &mut self, submission: RenderSubmission, ) -> Result<(), RenderSubmitError> { - self.publish_render_submission(&submission); + self.consume_render_submission(&submission); Ok(()) } } impl RenderBackend for Hardware { fn render_frame(&mut self) { - HardwareBridge::render_frame(self); + if self.frame_composer.active_scene_id().is_none() { + let packet = self.frame_composer.close_game2d_packet(); + self.gfx.render_game2d_frame_packet(&packet); + } else { + self.frame_composer.render_frame(&mut self.gfx); + } } } @@ -183,7 +116,7 @@ impl Game2DFrameComposer for Hardware { } fn emit_sprite(&mut self, sprite: Sprite) -> prometeu_hal::ComposerOpStatus { - if !self.has_glyph_bank(sprite.bank_id as usize) { + if self.gfx.glyph_banks.glyph_bank_slot(sprite.bank_id as usize).is_none() { return prometeu_hal::ComposerOpStatus::BankInvalid; } @@ -406,9 +339,12 @@ mod tests { let mut glyph_slots = [None; 16]; glyph_slots[0] = Some(0); hardware.assets.glyph_asset_slot_index().rebuild_from_slots(&glyph_slots); - assert!(HardwareBridge::bind_scene(&mut hardware, 0)); + assert_eq!( + Game2DFrameComposer::bind_scene(&mut hardware, 0), + prometeu_hal::ComposerOpStatus::Ok + ); - let mut packet = HardwareBridge::close_game2d_packet(&hardware); + let mut packet = Game2DFrameComposer::close_game2d_packet(&hardware); packet.gfx2d = vec![Gfx2dCommand::FillRect { rect: Rect { x: 0, y: 0, w: 1, h: 1 }, color: Color::BLUE, @@ -433,7 +369,10 @@ mod tests { let mut glyph_slots = [None; 16]; glyph_slots[0] = Some(0); hardware.assets.glyph_asset_slot_index().rebuild_from_slots(&glyph_slots); - assert!(HardwareBridge::bind_scene(&mut hardware, 0)); + assert_eq!( + Game2DFrameComposer::bind_scene(&mut hardware, 0), + prometeu_hal::ComposerOpStatus::Ok + ); let packet = Game2DFramePacket::new( ComposerFramePacket { @@ -455,7 +394,7 @@ mod tests { Vec::new(), ); - HardwareBridge::begin_frame(&mut hardware); + Game2DFrameComposer::begin_frame(&mut hardware); RenderSubmissionSink::submit_render_submission( &mut hardware, RenderSubmission::game2d(FrameId::ZERO, packet), diff --git a/crates/console/prometeu-hal/src/hardware_bridge.rs b/crates/console/prometeu-hal/src/hardware_bridge.rs deleted file mode 100644 index 6b39c911..00000000 --- a/crates/console/prometeu-hal/src/hardware_bridge.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crate::asset_bridge::AssetBridge; -use crate::audio_bridge::AudioBridge; -use crate::gfx_bridge::GfxBridge; -use crate::pad_bridge::PadBridge; -use crate::render_submission::{Game2DFramePacket, RenderSubmission}; -use crate::sprite::Sprite; -use crate::touch_bridge::TouchBridge; - -/// Legacy migration scaffold for code that has not moved to `platform` facades yet. -/// -/// `HardwareBridge` is intentionally not the final runtime-facing contract. New -/// platform boundaries belong in `crate::platform`, and this trait must be -/// removed after runtime, firmware, host, and tests finish migrating. -pub trait HardwareBridge { - fn begin_frame(&mut self); - fn bind_scene(&mut self, scene_bank_id: usize) -> bool; - fn unbind_scene(&mut self); - fn set_camera(&mut self, x: i32, y: i32); - fn emit_sprite(&mut self, sprite: Sprite) -> bool; - fn close_game2d_packet(&self) -> Game2DFramePacket; - fn publish_render_submission(&mut self, submission: &RenderSubmission); - fn render_frame(&mut self); - fn has_glyph_bank(&self, bank_id: usize) -> bool; - - fn gfx(&self) -> &dyn GfxBridge; - fn gfx_mut(&mut self) -> &mut dyn GfxBridge; - - fn audio(&self) -> &dyn AudioBridge; - fn audio_mut(&mut self) -> &mut dyn AudioBridge; - - fn pad(&self) -> &dyn PadBridge; - fn pad_mut(&mut self) -> &mut dyn PadBridge; - - fn touch(&self) -> &dyn TouchBridge; - fn touch_mut(&mut self) -> &mut dyn TouchBridge; - - fn assets(&self) -> &dyn AssetBridge; - fn assets_mut(&mut self) -> &mut dyn AssetBridge; -} diff --git a/crates/console/prometeu-hal/src/host_context.rs b/crates/console/prometeu-hal/src/host_context.rs index b4ec4862..463351ed 100644 --- a/crates/console/prometeu-hal/src/host_context.rs +++ b/crates/console/prometeu-hal/src/host_context.rs @@ -1,27 +1,17 @@ -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, platform: None } + pub fn new(platform: Option<&'a mut dyn RuntimePlatform>) -> Self { + Self { platform } } pub fn new_platform(platform: Option<&'a mut dyn RuntimePlatform>) -> Self { - Self { hw: None, platform } - } - - #[inline] - pub fn require_hw(&mut self) -> Result<&mut dyn HardwareBridge, VmFault> { - match &mut self.hw { - Some(hw) => Ok(*hw), - None => Err(VmFault::Unavailable), - } + Self::new(platform) } #[inline] diff --git a/crates/console/prometeu-hal/src/lib.rs b/crates/console/prometeu-hal/src/lib.rs index 1805e122..0c408783 100644 --- a/crates/console/prometeu-hal/src/lib.rs +++ b/crates/console/prometeu-hal/src/lib.rs @@ -11,7 +11,6 @@ pub mod debugger_protocol; pub mod gfx_bridge; pub mod glyph; pub mod glyph_bank; -pub mod hardware_bridge; pub mod host_context; pub mod host_return; pub mod input_signals; @@ -40,7 +39,6 @@ pub use asset_bridge::AssetBridge; pub use audio_bridge::{AudioBridge, AudioOpStatus, LoopMode}; pub use composer_status::ComposerOpStatus; pub use gfx_bridge::{BlendMode, GfxBridge, GfxOpStatus}; -pub use hardware_bridge::HardwareBridge; pub use host_context::{HostContext, HostContextProvider}; pub use host_return::HostReturn; pub use input_signals::InputSignals; @@ -49,9 +47,8 @@ pub use native_interface::{NativeInterface, SyscallId}; pub use pad_bridge::PadBridge; pub use platform::{ AssetStoragePlatform, AudioPlatform, ClockPacingPlatform, Game2DFrameComposer, InputPlatform, - LegacyHardwareGame2DFrameComposer, LegacyHardwareRenderSubmissionSink, - LegacyHardwareRuntimePlatform, NoopTelemetryPlatform, RenderBackend, RenderSubmissionSink, - RenderSubmitError, RuntimePlatform, TelemetryPlatform, TestPlatform, + 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 94b97292..35855c98 100644 --- a/crates/console/prometeu-hal/src/platform.rs +++ b/crates/console/prometeu-hal/src/platform.rs @@ -1,7 +1,6 @@ use crate::asset_bridge::AssetBridge; use crate::audio_bridge::AudioBridge; use crate::composer_status::ComposerOpStatus; -use crate::hardware_bridge::HardwareBridge; use crate::pad_bridge::PadBridge; use crate::render_submission::{Game2DFramePacket, RenderSubmission}; use crate::sprite::Sprite; @@ -21,26 +20,6 @@ pub trait RenderSubmissionSink { ) -> Result<(), RenderSubmitError>; } -pub struct LegacyHardwareRenderSubmissionSink<'a> { - hw: &'a mut dyn HardwareBridge, -} - -impl<'a> LegacyHardwareRenderSubmissionSink<'a> { - pub fn new(hw: &'a mut dyn HardwareBridge) -> Self { - Self { hw } - } -} - -impl RenderSubmissionSink for LegacyHardwareRenderSubmissionSink<'_> { - fn submit_render_submission( - &mut self, - submission: RenderSubmission, - ) -> Result<(), RenderSubmitError> { - self.hw.publish_render_submission(&submission); - Ok(()) - } -} - pub trait RenderBackend { fn render_frame(&mut self); } @@ -54,138 +33,6 @@ pub trait Game2DFrameComposer { fn close_game2d_packet(&self) -> Game2DFramePacket; } -pub struct LegacyHardwareGame2DFrameComposer<'a> { - hw: &'a mut dyn HardwareBridge, -} - -impl<'a> LegacyHardwareGame2DFrameComposer<'a> { - pub fn new(hw: &'a mut dyn HardwareBridge) -> Self { - Self { hw } - } -} - -impl Game2DFrameComposer for LegacyHardwareGame2DFrameComposer<'_> { - 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() - } -} - -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 {} @@ -212,8 +59,6 @@ pub struct NoopTelemetryPlatform; impl TelemetryPlatform for NoopTelemetryPlatform {} -static LEGACY_NOOP_TELEMETRY: NoopTelemetryPlatform = NoopTelemetryPlatform; - pub trait RuntimePlatform { fn display_size(&self) -> (usize, usize) { (480, 270) @@ -231,48 +76,6 @@ pub trait RuntimePlatform { 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/os/facades/vm.rs b/crates/console/prometeu-system/src/os/facades/vm.rs index 4786f5ce..f2d3f065 100644 --- a/crates/console/prometeu-system/src/os/facades/vm.rs +++ b/crates/console/prometeu-system/src/os/facades/vm.rs @@ -3,7 +3,7 @@ use crate::os::SystemOS; use prometeu_hal::app_mode::AppMode; use prometeu_hal::cartridge::Cartridge; use prometeu_hal::telemetry::{CertificationConfig, TelemetryFrame}; -use prometeu_hal::{HardwareBridge, InputSignals, RuntimePlatform}; +use prometeu_hal::{InputSignals, RuntimePlatform}; use prometeu_vm::VirtualMachine; use std::sync::atomic::Ordering; @@ -47,9 +47,9 @@ impl<'a> VmFacade<'a> { pub fn debug_step_instruction( &mut self, vm: &mut VirtualMachine, - hw: &mut dyn HardwareBridge, + platform: &mut dyn RuntimePlatform, ) -> Option { - self.os.vm_runtime.debug_step_instruction(&mut self.os.log_service, vm, hw) + self.os.vm_runtime.debug_step_instruction(&mut self.os.log_service, vm, platform) } pub fn paused(&self) -> bool { 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 267852da..ec12b75a 100644 --- a/crates/console/prometeu-system/src/services/vm_runtime/tick.rs +++ b/crates/console/prometeu-system/src/services/vm_runtime/tick.rs @@ -9,9 +9,8 @@ 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, LegacyHardwareRuntimePlatform, - RenderSubmission, RenderSubmissionPacket, RenderSubmissionSink, RuntimePlatform, - ShellUiFramePacket, + FrameId, HostContext, InputSignals, RenderSubmission, RenderSubmissionPacket, + RenderSubmissionSink, RuntimePlatform, ShellUiFramePacket, }; use prometeu_vm::LogicalFrameEndingReason; use std::collections::HashMap; @@ -77,11 +76,10 @@ impl VirtualMachineRuntime { &mut self, log_service: &mut LogService, vm: &mut VirtualMachine, - hw: &mut dyn HardwareBridge, + platform: &mut dyn RuntimePlatform, ) -> Option { let step_result = { - let mut platform = LegacyHardwareRuntimePlatform::new(hw); - let mut ctx = HostContext::new_platform(Some(&mut platform)); + let mut ctx = HostContext::new_platform(Some(platform)); let mut fs = VirtualFS::new(); let mut fs_state = FsState::Unmounted; let mut memcard = MemcardService::new(); diff --git a/crates/host/prometeu-host-desktop-winit/src/debugger.rs b/crates/host/prometeu-host-desktop-winit/src/debugger.rs index 4ebf6291..29ecf6cd 100644 --- a/crates/host/prometeu-host-desktop-winit/src/debugger.rs +++ b/crates/host/prometeu-host-desktop-winit/src/debugger.rs @@ -1,5 +1,5 @@ -use prometeu_drivers::hardware::Hardware; use prometeu_firmware::{BootTarget, Firmware}; +use prometeu_hal::RuntimePlatform; use prometeu_hal::cartridge_loader::CartridgeLoader; use prometeu_hal::debugger_protocol::*; use prometeu_hal::telemetry::{CertificationConfig, TelemetryFrame}; @@ -99,7 +99,7 @@ impl HostDebugger { /// Main maintenance method called by the HostRunner every iteration. /// It handles new connections and processes incoming commands. - pub fn check_commands(&mut self, firmware: &mut Firmware, hardware: &mut Hardware) { + pub fn check_commands(&mut self, firmware: &mut Firmware, platform: &mut dyn RuntimePlatform) { // 1. Accept new client connections. if let Some(listener) = &self.listener && let Ok((stream, _addr)) = listener.accept() @@ -154,7 +154,7 @@ impl HostDebugger { continue; } if let Ok(cmd) = serde_json::from_str::(trimmed) { - self.handle_command(cmd, firmware, hardware); + self.handle_command(cmd, firmware, platform); } } } @@ -182,7 +182,7 @@ impl HostDebugger { &mut self, cmd: DebugCommand, firmware: &mut Firmware, - hardware: &mut Hardware, + platform: &mut dyn RuntimePlatform, ) { match cmd { DebugCommand::Ok | DebugCommand::Start => { @@ -201,7 +201,7 @@ impl HostDebugger { DebugCommand::Step => { // Execute exactly one instruction and keep paused. firmware.os.vm().set_paused(true); - let _ = firmware.os.vm().debug_step_instruction(&mut firmware.vm, hardware); + let _ = firmware.os.vm().debug_step_instruction(&mut firmware.vm, platform); } DebugCommand::StepFrame => { // Execute until the end of the current logical frame. diff --git a/docs/specs/runtime/04-gfx-peripheral.md b/docs/specs/runtime/04-gfx-peripheral.md index 6f545436..1ee1e924 100644 --- a/docs/specs/runtime/04-gfx-peripheral.md +++ b/docs/specs/runtime/04-gfx-peripheral.md @@ -31,6 +31,13 @@ MUST contain a frame id, the active app mode, and one typed packet: immutable. The runtime backpressure policy is latest-complete-submission-wins; it MUST NOT accumulate an unbounded frame queue. +The platform layer is the runtime-facing implementation boundary for render +handoff. Runtime and firmware code publish completed frames through typed +platform services such as `RenderSubmissionSink`, and they access Game 2D +composition through `Game2DFrameComposer`. They MUST NOT depend on a monolithic +hardware bridge, mutable `Hardware`, mutable `Gfx`, or live `FrameComposer` +reference as the render handoff contract. + The current 2D graphics model is based on: - framebuffer @@ -127,6 +134,10 @@ to the logical/runtime side before handoff, or to an owning service. They MUST NOT require the render consumer to hold mutable VM, `Hardware`, `Gfx`, or `FrameComposer` references. +Local host implementations may keep a concrete hardware object internally as a +platform implementation detail. That object is not part of the runtime-facing +render boundary; the boundary is the typed platform service set. + The runtime does not guarantee visual integrity if a developer or framework replaces resources behind an in-flight submission in a way that violates asset discipline. diff --git a/docs/specs/runtime/11-portability-and-cross-platform-execution.md b/docs/specs/runtime/11-portability-and-cross-platform-execution.md index bb6a9d33..845e74d8 100644 --- a/docs/specs/runtime/11-portability-and-cross-platform-execution.md +++ b/docs/specs/runtime/11-portability-and-cross-platform-execution.md @@ -123,9 +123,13 @@ The graphics system: The platform layer: +- exposes typed service facades for render submission, render backend + execution, Game 2D composition, input, audio, assets, and telemetry - consumes closed render submissions through a render-surface implementation - transports published RGBA8888 output into a host presentation surface without injecting host-owned debug overlay pixels +- is the runtime-facing portability boundary; runtime and firmware code must not + depend on a monolithic hardware bridge or on a concrete hardware aggregate The host presentation layer MUST treat RGBA8888 as the canonical logical framebuffer format. RGB565 conversion is not part of the normal host diff --git a/docs/specs/runtime/README.md b/docs/specs/runtime/README.md index 9508d066..a2d3ae67 100644 --- a/docs/specs/runtime/README.md +++ b/docs/specs/runtime/README.md @@ -50,6 +50,12 @@ Does not belong in `docs/runtime/specs/` as the primary canonical source: Here, hardware is not a generic bucket for everything. In the current package, "hardware" means the machine's virtual peripherals: GFX, AUDIO, INPUT, TOUCH, and MEMCARD/save memory. VM, firmware, cartridge, and ABI sit in sibling categories rather than inside hardware. +The implementation boundary for host portability is the platform layer. Runtime, +firmware, and host integration code use typed platform services for rendering, +input, audio, assets, and telemetry. A concrete local hardware aggregate may +exist inside a host or test platform, but it is not the normative runtime-facing +contract. + ## Document Functions - `normative`: defines the technical contract, expected behavior, or implementation-facing surface.