diff --git a/crates/console/prometeu-drivers/src/frame_composer.rs b/crates/console/prometeu-drivers/src/frame_composer.rs index 0520c16a..7f6c476b 100644 --- a/crates/console/prometeu-drivers/src/frame_composer.rs +++ b/crates/console/prometeu-drivers/src/frame_composer.rs @@ -340,6 +340,7 @@ impl FrameComposer { mod tests { use super::*; use crate::gfx::Gfx; + use crate::hardware::Hardware; use crate::memory_banks::{ GlyphBankPoolAccess, GlyphBankPoolInstaller, MemoryBanks, SceneBankPoolInstaller, }; @@ -405,10 +406,14 @@ mod tests { #[test] fn frame_composer_starts_unbound_with_empty_owned_state() { - let frame_composer = - FrameComposer::new(320, 180, Arc::new(MemoryBanks::new()), make_glyph_slot_index(&[])); + let frame_composer = FrameComposer::new( + Hardware::W, + Hardware::H, + Arc::new(MemoryBanks::new()), + make_glyph_slot_index(&[]), + ); - assert_eq!(frame_composer.viewport_size(), (320, 180)); + assert_eq!(frame_composer.viewport_size(), (Hardware::W, Hardware::H)); assert_eq!(frame_composer.active_scene_id(), None); assert!(frame_composer.active_scene().is_none()); assert_eq!(frame_composer.scene_status(), SceneStatus::Unbound); @@ -425,7 +430,8 @@ mod tests { let banks = Arc::new(MemoryBanks::new()); banks.install_scene_bank(3, Arc::new(make_scene())); - let frame_composer = FrameComposer::new(320, 180, banks, make_glyph_slot_index(&[(1, 0)])); + let frame_composer = + FrameComposer::new(Hardware::W, Hardware::H, banks, make_glyph_slot_index(&[(1, 0)])); let scene = frame_composer.scene_bank_slot(3).expect("scene bank slot 3 should be resident"); @@ -442,7 +448,7 @@ mod tests { let expected_scene = banks.scene_bank_slot(3).expect("scene bank slot 3 should be resident"); let mut frame_composer = - FrameComposer::new(320, 180, banks, make_glyph_slot_index(&[(1, 0)])); + FrameComposer::new(Hardware::W, Hardware::H, banks, make_glyph_slot_index(&[(1, 0)])); assert!(frame_composer.bind_scene(3)); @@ -462,7 +468,7 @@ mod tests { banks.install_scene_bank(1, Arc::new(make_scene())); let mut frame_composer = - FrameComposer::new(320, 180, banks, make_glyph_slot_index(&[(1, 0)])); + FrameComposer::new(Hardware::W, Hardware::H, banks, make_glyph_slot_index(&[(1, 0)])); assert!(frame_composer.bind_scene(1)); frame_composer.unbind_scene(); @@ -476,8 +482,12 @@ mod tests { #[test] fn set_camera_stores_top_left_pixel_coordinates() { - let mut frame_composer = - FrameComposer::new(320, 180, Arc::new(MemoryBanks::new()), make_glyph_slot_index(&[])); + let mut frame_composer = FrameComposer::new( + Hardware::W, + Hardware::H, + Arc::new(MemoryBanks::new()), + make_glyph_slot_index(&[]), + ); frame_composer.set_camera(-12, 48); @@ -490,17 +500,21 @@ mod tests { banks.install_scene_bank(0, Arc::new(make_scene())); let mut frame_composer = - FrameComposer::new(320, 180, banks, make_glyph_slot_index(&[(1, 0)])); + FrameComposer::new(Hardware::W, Hardware::H, banks, make_glyph_slot_index(&[(1, 0)])); assert!(frame_composer.bind_scene(0)); let cache = frame_composer.cache().expect("cache should exist for bound scene"); - assert_eq!((cache.width(), cache.height()), (45, 27)); + assert_eq!((cache.width(), cache.height()), (65, 38)); } #[test] fn missing_scene_binding_falls_back_to_no_scene_state() { - let mut frame_composer = - FrameComposer::new(320, 180, Arc::new(MemoryBanks::new()), make_glyph_slot_index(&[])); + let mut frame_composer = FrameComposer::new( + Hardware::W, + Hardware::H, + Arc::new(MemoryBanks::new()), + make_glyph_slot_index(&[]), + ); assert!(!frame_composer.bind_scene(7)); @@ -514,7 +528,8 @@ mod tests { let banks = Arc::new(MemoryBanks::new()); banks.install_scene_bank(0, Arc::new(make_scene())); - let mut frame_composer = FrameComposer::new(320, 180, banks, make_glyph_slot_index(&[])); + let mut frame_composer = + FrameComposer::new(Hardware::W, Hardware::H, banks, make_glyph_slot_index(&[])); let panic = catch_unwind(AssertUnwindSafe(|| frame_composer.bind_scene(0))) .expect_err("bind_scene should panic when dependency is missing"); @@ -649,8 +664,12 @@ mod tests { #[test] fn frame_composer_emits_ordered_sprites_for_rendering() { - let mut frame_composer = - FrameComposer::new(320, 180, Arc::new(MemoryBanks::new()), make_glyph_slot_index(&[])); + let mut frame_composer = FrameComposer::new( + Hardware::W, + Hardware::H, + Arc::new(MemoryBanks::new()), + make_glyph_slot_index(&[]), + ); frame_composer.begin_frame(); assert!(frame_composer.emit_sprite(Sprite { diff --git a/crates/console/prometeu-drivers/src/hardware.rs b/crates/console/prometeu-drivers/src/hardware.rs index 4a6138f4..6cf23447 100644 --- a/crates/console/prometeu-drivers/src/hardware.rs +++ b/crates/console/prometeu-drivers/src/hardware.rs @@ -21,7 +21,7 @@ use std::sync::Arc; /// for easier testing and different host implementations (Desktop, Web, etc.). /// /// ### Console Specifications: -/// - **Resolution**: 320x180 (16:9 Aspect Ratio). +/// - **Resolution**: 480x270 (16:9 Aspect Ratio). /// - **Color Depth**: RGB565 (16-bit). /// - **Audio**: Stereo, Command-based mixing. /// - **Input**: 12-button Digital Gamepad + Absolute Touch/Mouse. @@ -115,9 +115,9 @@ impl HardwareBridge for Hardware { impl Hardware { /// Internal hardware width in pixels. - pub const W: usize = 320; + pub const W: usize = 480; /// Internal hardware height in pixels. - pub const H: usize = 180; + pub const H: usize = 270; /// Creates a fresh hardware instance with default settings. pub fn new() -> Self { diff --git a/crates/console/prometeu-hal/src/scene_viewport_resolver.rs b/crates/console/prometeu-hal/src/scene_viewport_resolver.rs index b2a65a50..04d33b74 100644 --- a/crates/console/prometeu-hal/src/scene_viewport_resolver.rs +++ b/crates/console/prometeu-hal/src/scene_viewport_resolver.rs @@ -433,20 +433,20 @@ mod tests { #[test] fn first_update_initializes_master_and_layer_anchors() { let scene = make_scene(); - let mut resolver = SceneViewportResolver::new(320, 180, 25, 16, 12, 20); + let mut resolver = SceneViewportResolver::new(480, 270, 35, 21, 12, 20); let update = resolver.update(&scene, 0, 0); - assert_eq!(update.master_anchor, TileAnchor { x: 12, y: 8 }); - assert_eq!(update.layer_anchors[0], TileAnchor { x: 12, y: 8 }); - assert_eq!(update.layer_anchors[1], TileAnchor { x: 12, y: 8 }); + assert_eq!(update.master_anchor, TileAnchor { x: 17, y: 10 }); + assert_eq!(update.layer_anchors[0], TileAnchor { x: 17, y: 10 }); + assert_eq!(update.layer_anchors[1], TileAnchor { x: 17, y: 10 }); assert_eq!(update.refresh_requests.len(), 4); } #[test] fn per_layer_copy_requests_follow_parallax_factor() { let scene = make_scene(); - let mut resolver = SceneViewportResolver::new(320, 180, 25, 16, 12, 20); + let mut resolver = SceneViewportResolver::new(480, 270, 35, 21, 12, 20); let update = resolver.update(&scene, 32, 48); @@ -461,24 +461,24 @@ mod tests { #[test] fn hysteresis_prevents_small_back_and_forth_refresh_churn() { let scene = make_scene(); - let mut resolver = SceneViewportResolver::new(320, 180, 25, 16, 12, 20); + let mut resolver = SceneViewportResolver::new(480, 270, 35, 21, 12, 20); let _ = resolver.update(&scene, 0, 0); let update = resolver.update(&scene, 8, 0); assert!(update.refresh_requests.is_empty()); - assert_eq!(update.master_anchor, TileAnchor { x: 12, y: 8 }); + assert_eq!(update.master_anchor, TileAnchor { x: 17, y: 10 }); } #[test] fn repeated_high_speed_movement_advances_in_tile_steps() { let scene = make_scene(); - let mut resolver = SceneViewportResolver::new(320, 180, 25, 16, 12, 20); + let mut resolver = SceneViewportResolver::new(480, 270, 35, 21, 12, 20); let _ = resolver.update(&scene, 0, 0); let update = resolver.update(&scene, 64, 0); - assert!(update.master_anchor.x > 12); + assert!(update.master_anchor.x > 17); assert!(update.refresh_requests.iter().any(|request| matches!( request, CacheRefreshRequest::RefreshColumn { layer_index: 0, .. } @@ -489,18 +489,18 @@ mod tests { #[test] fn anchors_clamp_near_scene_edges() { let scene = make_scene(); - let mut resolver = SceneViewportResolver::new(320, 180, 25, 16, 12, 20); + let mut resolver = SceneViewportResolver::new(480, 270, 35, 21, 12, 20); let update = resolver.update(&scene, 10_000, 10_000); - assert_eq!(update.master_anchor, TileAnchor { x: 51, y: 56 }); - assert_eq!(update.layer_anchors[0], TileAnchor { x: 51, y: 56 }); + assert_eq!(update.master_anchor, TileAnchor { x: 46, y: 53 }); + assert_eq!(update.layer_anchors[0], TileAnchor { x: 46, y: 53 }); } #[test] fn corner_trigger_converts_to_non_overlapping_region_requests() { let scene = make_scene(); - let mut resolver = SceneViewportResolver::new(320, 180, 25, 16, 12, 20); + let mut resolver = SceneViewportResolver::new(480, 270, 35, 21, 12, 20); let _ = resolver.update(&scene, 0, 0); let update = resolver.update(&scene, 64, 80); @@ -515,15 +515,15 @@ mod tests { .collect(); assert_eq!(regions.len(), 2); - assert_eq!(regions[0].x + regions[0].width, 25); - assert_eq!(regions[0].height, 16); - assert_eq!(regions[1].width, 24); - assert_eq!(regions[1].height, 1); + assert_eq!(regions[0].x + regions[0].width, 35); + assert_eq!(regions[0].height, 21); + assert_eq!(regions[1].width, 34); + assert_eq!(regions[1].height, 2); } #[test] fn reset_scene_requests_full_invalidation_for_all_layers() { - let mut resolver = SceneViewportResolver::new(320, 180, 25, 16, 12, 20); + let mut resolver = SceneViewportResolver::new(480, 270, 35, 21, 12, 20); let requests = resolver.reset_scene(); diff --git a/discussion/index.ndjson b/discussion/index.ndjson index 49df7439..412c0fbe 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -28,4 +28,4 @@ {"type":"discussion","id":"DSC-0017","status":"done","ticket":"asset-entry-metadata-normalization-contract","title":"Asset Entry Metadata Normalization Contract","created_at":"2026-03-27","updated_at":"2026-04-09","tags":[],"agendas":[{"id":"AGD-0016","file":"workflow/agendas/AGD-0016-asset-entry-metadata-normalization-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-04-09"}],"decisions":[{"id":"DEC-0004","file":"workflow/decisions/DEC-0004-asset-entry-metadata-normalization-contract.md","status":"accepted","created_at":"2026-04-09","updated_at":"2026-04-09"}],"plans":[],"lessons":[{"id":"LSN-0023","file":"lessons/DSC-0017-asset-metadata-normalization/LSN-0023-typed-asset-metadata-helpers.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]} {"type":"discussion","id":"DSC-0018","status":"done","ticket":"asset-load-asset-id-int-contract","title":"Asset Load Asset ID Int Contract","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["asset","runtime","abi"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0019","file":"lessons/DSC-0018-asset-load-asset-id-int-contract/LSN-0019-asset-load-id-abi-convergence.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]} {"type":"discussion","id":"DSC-0019","status":"done","ticket":"jenkinsfile-correction","title":"Jenkinsfile Correction and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins"],"agendas":[{"id":"AGD-0017","file":"workflow/agendas/AGD-0017-jenkinsfile-correction.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0002","file":"workflow/decisions/DEC-0002-jenkinsfile-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0002","file":"workflow/plans/PLN-0002-jenkinsfile-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0020","file":"lessons/DSC-0019-jenkins-ci-standardization/LSN-0020-jenkins-standard-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]} -{"type":"discussion","id":"DSC-0030","status":"open","ticket":"internal-viewport-270p","title":"Agenda - Internal Viewport 270p (480x270)","created_at":"2026-04-27","updated_at":"2026-04-27","tags":["gfx","runtime","viewport","resolution","frame-composer","host"],"agendas":[{"id":"AGD-0030","file":"workflow/agendas/AGD-0030-internal-viewport-270p.md","status":"done","created_at":"2026-04-27","updated_at":"2026-04-27"}],"decisions":[{"id":"DEC-0022","file":"workflow/decisions/DEC-0022-internal-viewport-270p.md","status":"accepted","created_at":"2026-04-27","updated_at":"2026-04-27","ref_agenda":"AGD-0030"}],"plans":[{"id":"PLN-0043","file":"workflow/plans/PLN-0043-runtime-270p-core-migration.md","status":"review","created_at":"2026-04-27","updated_at":"2026-04-27","ref_decisions":["DEC-0022"]},{"id":"PLN-0044","file":"workflow/plans/PLN-0044-host-and-tests-270p-propagation.md","status":"review","created_at":"2026-04-27","updated_at":"2026-04-27","ref_decisions":["DEC-0022"]},{"id":"PLN-0045","file":"workflow/plans/PLN-0045-specs-and-docs-270p-publication.md","status":"review","created_at":"2026-04-27","updated_at":"2026-04-27","ref_decisions":["DEC-0022"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0030","status":"open","ticket":"internal-viewport-270p","title":"Agenda - Internal Viewport 270p (480x270)","created_at":"2026-04-27","updated_at":"2026-04-27","tags":["gfx","runtime","viewport","resolution","frame-composer","host"],"agendas":[{"id":"AGD-0030","file":"workflow/agendas/AGD-0030-internal-viewport-270p.md","status":"done","created_at":"2026-04-27","updated_at":"2026-04-27"}],"decisions":[{"id":"DEC-0022","file":"workflow/decisions/DEC-0022-internal-viewport-270p.md","status":"accepted","created_at":"2026-04-27","updated_at":"2026-04-27","ref_agenda":"AGD-0030"}],"plans":[{"id":"PLN-0043","file":"workflow/plans/PLN-0043-runtime-270p-core-migration.md","status":"done","created_at":"2026-04-27","updated_at":"2026-04-27","ref_decisions":["DEC-0022"]},{"id":"PLN-0044","file":"workflow/plans/PLN-0044-host-and-tests-270p-propagation.md","status":"review","created_at":"2026-04-27","updated_at":"2026-04-27","ref_decisions":["DEC-0022"]},{"id":"PLN-0045","file":"workflow/plans/PLN-0045-specs-and-docs-270p-publication.md","status":"review","created_at":"2026-04-27","updated_at":"2026-04-27","ref_decisions":["DEC-0022"]}],"lessons":[]} diff --git a/discussion/workflow/plans/PLN-0043-runtime-270p-core-migration.md b/discussion/workflow/plans/PLN-0043-runtime-270p-core-migration.md index 006ac08f..e8b97e57 100644 --- a/discussion/workflow/plans/PLN-0043-runtime-270p-core-migration.md +++ b/discussion/workflow/plans/PLN-0043-runtime-270p-core-migration.md @@ -2,9 +2,9 @@ id: PLN-0043 ticket: internal-viewport-270p title: Runtime 270p Core Migration -status: review +status: done created: 2026-04-27 -completed: +completed: 2026-04-27 tags: [gfx, runtime, viewport, resolution, frame-composer] ---