diff --git a/crates/console/prometeu-drivers/src/frame_composer.rs b/crates/console/prometeu-drivers/src/frame_composer.rs index bdbdebc9..dd95bfda 100644 --- a/crates/console/prometeu-drivers/src/frame_composer.rs +++ b/crates/console/prometeu-drivers/src/frame_composer.rs @@ -327,6 +327,55 @@ impl FrameComposer { gfx.render_no_scene_frame(); } + pub fn render_game2d_frame_packet( + &mut self, + packet: &Game2DFramePacket, + gfx: &mut dyn GfxBridge, + ) { + let sprites: Vec = + packet.composer.sprites.iter().map(Self::sprite_from_packet).collect(); + gfx.load_frame_sprites(&sprites); + + let Some(bound_scene) = packet.composer.bound_scene else { + gfx.render_no_scene_frame(); + return; + }; + + let scene_bank_id = bound_scene.bank_id as usize; + if self.active_scene_id != Some(scene_bank_id) && !self.bind_scene(scene_bank_id) { + gfx.render_no_scene_frame(); + return; + } + + self.camera_x_px = packet.composer.camera.x; + self.camera_y_px = packet.composer.camera.y; + + let scene = + self.active_scene.as_deref().expect("active scene should exist after packet bind"); + let resolved_glyph_slots = self.resolve_glyph_slots(scene, scene_bank_id, "render_packet"); + let (Some(cache), Some(resolver)) = (self.cache.as_mut(), self.resolver.as_mut()) else { + panic!("SCENE runtime invariant broken: packet scene without cache/resolver"); + }; + let update = resolver.update(scene, self.camera_x_px, self.camera_y_px); + Self::sync_cache_windows(cache, &update); + Self::apply_refresh_requests(cache, scene, &update.refresh_requests); + gfx.render_scene_from_cache(cache, &update, &resolved_glyph_slots); + } + + fn sprite_from_packet(sprite: &GameSpritePacket) -> Sprite { + Sprite { + glyph: Glyph { glyph_id: sprite.glyph_id as u16, palette_id: sprite.palette_id as u8 }, + x: sprite.x, + y: sprite.y, + layer: sprite.layer as u8, + bank_id: sprite.bank_id as u8, + active: true, + flip_x: sprite.flip_x, + flip_y: sprite.flip_y, + priority: sprite.priority as u8, + } + } + fn build_scene_runtime( viewport_width_px: usize, viewport_height_px: usize, diff --git a/crates/console/prometeu-drivers/src/hardware.rs b/crates/console/prometeu-drivers/src/hardware.rs index 77ff7dd8..c04868cc 100644 --- a/crates/console/prometeu-drivers/src/hardware.rs +++ b/crates/console/prometeu-drivers/src/hardware.rs @@ -75,10 +75,10 @@ impl HardwareBridge for Hardware { fn publish_render_submission(&mut self, submission: &RenderSubmission) { match &submission.packet { RenderSubmissionPacket::Game2D(packet) => { - if self.frame_composer.active_scene_id().is_none() { + if packet.composer.bound_scene.is_none() { self.gfx.render_game2d_frame_packet(packet); } else { - self.frame_composer.render_frame(&mut self.gfx); + self.frame_composer.render_game2d_frame_packet(packet, &mut self.gfx); self.gfx.apply_gfx2d_commands(&packet.gfx2d); } } @@ -195,7 +195,9 @@ mod tests { use prometeu_hal::scene_viewport_resolver::SceneViewportResolver; use prometeu_hal::tile::Tile; use prometeu_hal::tilemap::TileMap; - use prometeu_hal::{FrameId, Gfx2dCommand}; + use prometeu_hal::{ + BoundScenePacket, Camera2D, ComposerFramePacket, FrameId, GameSpritePacket, Gfx2dCommand, + }; fn make_glyph_bank() -> GlyphBank { let mut bank = GlyphBank::new(TileSize::Size8, 8, 8); @@ -292,4 +294,42 @@ mod tests { assert_eq!(hardware.gfx.front_buffer()[0], Color::BLUE.raw()); } + + #[test] + fn publish_game2d_submission_uses_packet_scene_and_sprites_not_live_frame_state() { + let banks = Arc::new(MemoryBanks::new()); + banks.install_glyph_bank(0, Arc::new(make_glyph_bank())); + banks.install_scene_bank(0, Arc::new(make_scene_with_glyph_asset_id(0))); + + let mut hardware = Hardware::new_with_memory_banks(banks); + let mut glyph_slots = [None; 16]; + glyph_slots[0] = Some(0); + hardware.assets.glyph_asset_slot_index().rebuild_from_slots(&glyph_slots); + assert!(hardware.bind_scene(0)); + + let packet = Game2DFramePacket::new( + ComposerFramePacket { + bound_scene: Some(BoundScenePacket { bank_id: 0 }), + camera: Camera2D::default(), + sprites: vec![GameSpritePacket { + glyph_id: 0, + palette_id: 0, + x: 0, + y: 0, + layer: 0, + bank_id: 0, + flip_x: false, + flip_y: false, + priority: 0, + }], + hud: Default::default(), + }, + Vec::new(), + ); + + hardware.begin_frame(); + hardware.publish_render_submission(&RenderSubmission::game2d(FrameId::ZERO, packet)); + + assert_eq!(hardware.gfx.front_buffer()[0], Color::RED.raw()); + } } diff --git a/crates/tools/pbxgen-stress/src/lib.rs b/crates/tools/pbxgen-stress/src/lib.rs index cb00152f..11b8bee1 100644 --- a/crates/tools/pbxgen-stress/src/lib.rs +++ b/crates/tools/pbxgen-stress/src/lib.rs @@ -41,13 +41,6 @@ fn asm(s: &str) -> Vec { pub fn generate() -> Result<()> { let mut rom: Vec = Vec::new(); let syscalls = vec![ - SyscallDecl { - module: "gfx2d".into(), - name: "clear".into(), - version: 1, - arg_slots: 1, - ret_slots: 0, - }, SyscallDecl { module: "gfx2d".into(), name: "draw_text".into(), @@ -143,17 +136,15 @@ fn heavy_load(rom: &mut Vec) { let jif_bind_done_offset = rom.len() + 2; rom.extend(asm("JMP_IF_FALSE 0")); - rom.extend(asm("PUSH_I32 0\nHOSTCALL 3\nPOP_N 1\nPUSH_I32 1\nSET_GLOBAL 1")); + rom.extend(asm("PUSH_I32 0\nHOSTCALL 2\nPOP_N 1\nPUSH_I32 1\nSET_GLOBAL 1")); let bind_done_target = rom.len() as u32; - rom.extend(asm("PUSH_I32 0\nHOSTCALL 0")); - let scene_width_px = (STRESS_SCENE_TILE_W * 8) as i32; let scene_height_px = (STRESS_SCENE_TILE_H * 8) as i32; let camera_x_span = scene_width_px - STRESS_VIEWPORT_W; let camera_y_span = scene_height_px - STRESS_VIEWPORT_H; rom.extend(asm(&format!( - "GET_GLOBAL 0\nPUSH_I32 2\nMUL\nPUSH_I32 {camera_x_span}\nMOD\nGET_GLOBAL 0\nPUSH_I32 {camera_y_span}\nMOD\nHOSTCALL 4" + "GET_GLOBAL 0\nPUSH_I32 2\nMUL\nPUSH_I32 {camera_x_span}\nMOD\nGET_GLOBAL 0\nPUSH_I32 {camera_y_span}\nMOD\nHOSTCALL 3" ))); rom.extend(asm("PUSH_I32 0\nSET_LOCAL 0")); @@ -178,7 +169,7 @@ fn heavy_load(rom: &mut Vec) { GET_LOCAL 1\nGET_GLOBAL 0\nADD\nPUSH_I32 1\nBIT_AND\nPUSH_I32 0\nNEQ\n\ GET_LOCAL 0\nGET_GLOBAL 0\nADD\nPUSH_I32 1\nBIT_AND\nPUSH_I32 0\nNEQ\n\ GET_LOCAL 0\nGET_LOCAL 1\nADD\nPUSH_I32 4\nMOD\n\ - HOSTCALL 5\nPOP_N 1" + HOSTCALL 4\nPOP_N 1" ))); rom.extend(asm("GET_LOCAL 1\nPUSH_I32 1\nADD\nSET_LOCAL 1")); @@ -195,27 +186,27 @@ fn heavy_load(rom: &mut Vec) { PUSH_I32 8\n\ PUSH_CONST 0\n\ GET_GLOBAL 0\nPUSH_I32 2047\nMUL\nPUSH_I32 65535\nBIT_AND\n\ - HOSTCALL 1")); + HOSTCALL 0")); rom.extend(asm("PUSH_I32 16\n\ GET_GLOBAL 0\nPUSH_I32 2\nMUL\nPUSH_I32 186\nMOD\nPUSH_I32 32\nADD\n\ PUSH_CONST 1\n\ GET_GLOBAL 0\nPUSH_I32 4093\nMUL\nPUSH_I32 65535\nBIT_AND\n\ - HOSTCALL 1")); + HOSTCALL 0")); rom.extend(asm("PUSH_I32 336\n\ GET_GLOBAL 0\nPUSH_I32 5\nMUL\nPUSH_I32 194\nMOD\n\ PUSH_CONST 2\n\ GET_GLOBAL 0\nPUSH_I32 1237\nMUL\nPUSH_I32 65535\nBIT_AND\n\ - HOSTCALL 1")); + HOSTCALL 0")); rom.extend(asm("GET_GLOBAL 0\nPUSH_I32 4\nMUL\nPUSH_I32 240\nMOD\nPUSH_I32 112\nADD\n\ GET_GLOBAL 0\nPUSH_I32 3\nMUL\nPUSH_I32 120\nMOD\nPUSH_I32 88\nADD\n\ PUSH_CONST 3\n\ GET_GLOBAL 0\nPUSH_I32 3001\nMUL\nPUSH_I32 65535\nBIT_AND\n\ - HOSTCALL 1")); + HOSTCALL 0")); rom.extend(asm("GET_GLOBAL 0\nPUSH_I32 60\nMOD\nPUSH_I32 0\nEQ")); let jif_log_offset = rom.len() + 2; rom.extend(asm("JMP_IF_FALSE 0")); - rom.extend(asm("PUSH_I32 2\nPUSH_CONST 0\nHOSTCALL 2")); + rom.extend(asm("PUSH_I32 2\nPUSH_CONST 0\nHOSTCALL 1")); let after_log = rom.len() as u32; rom.extend(asm("FRAME_SYNC\nRET")); diff --git a/test-cartridges/stress-console/program.pbx b/test-cartridges/stress-console/program.pbx index a06e0838..7191e3a6 100644 Binary files a/test-cartridges/stress-console/program.pbx and b/test-cartridges/stress-console/program.pbx differ