diff --git a/crates/console/prometeu-drivers/src/gfx.rs b/crates/console/prometeu-drivers/src/gfx.rs index 9d9e233b..26d09ebf 100644 --- a/crates/console/prometeu-drivers/src/gfx.rs +++ b/crates/console/prometeu-drivers/src/gfx.rs @@ -210,6 +210,9 @@ impl GfxBridge for Gfx { fn render_all(&mut self) { self.render_all() } + fn render_scene_from_cache(&mut self, cache: &SceneViewportCache, update: &ResolverUpdate) { + self.render_scene_from_cache(cache, update) + } fn draw_text(&mut self, x: i32, y: i32, text: &str, color: Color) { self.draw_text(x, y, text, color) } @@ -545,40 +548,11 @@ impl Gfx { &*self.glyph_banks, ); - // 2. Main layers and prioritized sprites. - // Order: Layer 0 -> Sprites 1 -> Layer 1 -> Sprites 2 ... - // for i in 0..self.layers.len() { - // let bank_id = self.layers[i].bank_id as usize; - // if let Some(bank) = self.glyph_banks.glyph_bank_slot(bank_id) { - // Self::draw_tile_map( - // &mut self.back, - // self.w, - // self.h, - // &self.layers[i].map, - // &bank, - // self.layers[i].scroll_x, - // self.layers[i].scroll_y, - // ); - // } - // - // // Draw sprites that belong to this depth level - // Self::draw_bucket_on_buffer( - // &mut self.back, - // self.w, - // self.h, - // &self.priority_buckets[i + 1], - // &self.sprites, - // &*self.glyph_banks, - // ); - // } - - // 4. Scene Fade: Applies a color blend to the entire world (excluding HUD). + // 2. Scene-only fallback path: sprites and fades still work even before a + // cache-backed world composition request is issued for the frame. Self::apply_fade_to_buffer(&mut self.back, self.scene_fade_level, self.scene_fade_color); - // 5. HUD: The fixed interface layer, always drawn on top of the world. - // Self::render_hud_with_pool(&mut self.back, self.w, self.h, &self.hud, &*self.glyph_banks); - - // 6. HUD Fade: Independent fade effect for the UI. + // 3. HUD Fade: independent from scene fade; HUD composition itself remains external. Self::apply_fade_to_buffer(&mut self.back, self.hud_fade_level, self.hud_fade_color); } @@ -624,137 +598,6 @@ impl Gfx { Self::apply_fade_to_buffer(&mut self.back, self.hud_fade_level, self.hud_fade_color); } - // /// Renders a specific game layer. - // pub fn render_layer(&mut self, layer_idx: usize) { - // if layer_idx >= self.layers.len() { - // return; - // } - // - // let bank_id = self.layers[layer_idx].bank_id as usize; - // let scroll_x = self.layers[layer_idx].scroll_x; - // let scroll_y = self.layers[layer_idx].scroll_y; - // - // let bank = match self.glyph_banks.glyph_bank_slot(bank_id) { - // Some(b) => b, - // _ => return, - // }; - // - // Self::draw_tile_map( - // &mut self.back, - // self.w, - // self.h, - // &self.layers[layer_idx].map, - // &bank, - // scroll_x, - // scroll_y, - // ); - // } - - // /// Rasterizes a TileMap into the provided pixel buffer using scrolling. - // fn draw_tile_map( - // back: &mut [u16], - // screen_w: usize, - // screen_h: usize, - // map: &TileMap, - // bank: &GlyphBank, - // scroll_x: i32, - // scroll_y: i32, - // ) { - // let tile_size = bank.tile_size as usize; - // - // // 1. Determine the range of visible tiles based on the scroll position. - // // We add a margin of 1 tile to ensure smooth pixel-perfect scrolling at the borders. - // let visible_tiles_x = (screen_w / tile_size) + 1; - // let visible_tiles_y = (screen_h / tile_size) + 1; - // - // // 2. Calculate offsets within the tilemap. - // let start_tile_x = scroll_x / tile_size as i32; - // let start_tile_y = scroll_y / tile_size as i32; - // - // // 3. Fine scroll: how many pixels the tiles are shifted within the first visible cell. - // let fine_scroll_x = scroll_x % tile_size as i32; - // let fine_scroll_y = scroll_y % tile_size as i32; - // - // // 4. Iterate only through the tiles that are actually visible on screen. - // for ty in 0..visible_tiles_y { - // for tx in 0..visible_tiles_x { - // let map_x = (start_tile_x + tx as i32) as usize; - // let map_y = (start_tile_y + ty as i32) as usize; - // - // // Bounds check: don't draw if the camera is outside the map. - // if map_x >= map.width || map_y >= map.height { - // continue; - // } - // - // let tile = map.tiles[map_y * map.width + map_x]; - // - // // Optimized skip for empty (ID 0) tiles. - // if !tile.active { - // continue; - // } - // - // // 5. Project the tile pixels to screen space. - // let screen_tile_x = (tx as i32 * tile_size as i32) - fine_scroll_x; - // let screen_tile_y = (ty as i32 * tile_size as i32) - fine_scroll_y; - // - // Self::draw_tile_pixels( - // back, - // screen_w, - // screen_h, - // screen_tile_x, - // screen_tile_y, - // tile, - // bank, - // ); - // } - // } - // } - - // /// Internal helper to copy a single tile's pixels to the framebuffer. - // /// Handles flipping and palette resolution. - // fn draw_tile_pixels( - // back: &mut [u16], - // screen_w: usize, - // screen_h: usize, - // x: i32, - // y: i32, - // tile: Tile, - // bank: &GlyphBank, - // ) { - // let size = bank.tile_size as usize; - // - // for local_y in 0..size { - // let world_y = y + local_y as i32; - // if world_y < 0 || world_y >= screen_h as i32 { - // continue; - // } - // - // for local_x in 0..size { - // let world_x = x + local_x as i32; - // if world_x < 0 || world_x >= screen_w as i32 { - // continue; - // } - // - // // Handle flip flags by reversing the fetch coordinates. - // let fetch_x = if tile.flip_x { size - 1 - local_x } else { local_x }; - // let fetch_y = if tile.flip_y { size - 1 - local_y } else { local_y }; - // - // // 1. Get the pixel color index (0-15) from the bank. - // let px_index = bank.get_pixel_index(tile.glyph.glyph_id, fetch_x, fetch_y); - // - // // 2. Hardware rule: Color index 0 is always fully transparent. - // if px_index == 0 { - // continue; - // } - // - // // 3. Resolve the virtual index to a real RGB565 color using the tile's assigned palette. - // let color = bank.resolve_color(tile.glyph.palette_id, px_index); - // - // back[world_y as usize * screen_w + world_x as usize] = color.raw(); - // } - // } - // } - fn populate_priority_buckets(&mut self) { for bucket in self.priority_buckets.iter_mut() { bucket.clear(); diff --git a/crates/console/prometeu-drivers/src/hardware.rs b/crates/console/prometeu-drivers/src/hardware.rs index 5f93bace..3d7a2194 100644 --- a/crates/console/prometeu-drivers/src/hardware.rs +++ b/crates/console/prometeu-drivers/src/hardware.rs @@ -87,7 +87,11 @@ impl Hardware { /// Creates a fresh hardware instance with default settings. pub fn new() -> Self { - let memory_banks = Arc::new(MemoryBanks::new()); + Self::new_with_memory_banks(Arc::new(MemoryBanks::new())) + } + + /// Creates hardware with explicit shared bank ownership. + pub fn new_with_memory_banks(memory_banks: Arc) -> Self { Self { gfx: Gfx::new( Self::W, @@ -107,3 +111,75 @@ impl Hardware { } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::memory_banks::{ + GlyphBankPoolInstaller, SceneBankPoolAccess, SceneBankPoolInstaller, + }; + use prometeu_hal::color::Color; + use prometeu_hal::glyph::Glyph; + use prometeu_hal::glyph_bank::{GlyphBank, TileSize}; + use prometeu_hal::scene_bank::SceneBank; + use prometeu_hal::scene_layer::{MotionFactor, SceneLayer}; + use prometeu_hal::scene_viewport_cache::SceneViewportCache; + use prometeu_hal::scene_viewport_resolver::SceneViewportResolver; + use prometeu_hal::tile::Tile; + use prometeu_hal::tilemap::TileMap; + + fn make_glyph_bank() -> GlyphBank { + let mut bank = GlyphBank::new(TileSize::Size8, 8, 8); + bank.palettes[0][1] = Color::RED; + for pixel in &mut bank.pixel_indices { + *pixel = 1; + } + bank + } + + fn make_scene() -> SceneBank { + let layer = SceneLayer { + active: true, + glyph_bank_id: 0, + tile_size: TileSize::Size8, + motion_factor: MotionFactor { x: 1.0, y: 1.0 }, + tilemap: TileMap { + width: 4, + height: 4, + tiles: vec![ + Tile { + active: true, + glyph: Glyph { glyph_id: 0, palette_id: 0 }, + flip_x: false, + flip_y: false, + }; + 16 + ], + }, + }; + + SceneBank { layers: std::array::from_fn(|_| layer.clone()) } + } + + #[test] + fn hardware_can_render_scene_from_shared_scene_bank_pipeline() { + 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())); + + let mut hardware = Hardware::new_with_memory_banks(Arc::clone(&banks)); + let scene = banks.scene_bank_slot(0).expect("scene bank slot 0 should be resident"); + let mut cache = SceneViewportCache::new(&scene, 4, 4); + cache.materialize_all_layers(&scene); + + let mut resolver = SceneViewportResolver::new(16, 16, 4, 4, 12, 20); + let update = resolver.update(&scene, 0, 0); + + hardware.gfx.scene_fade_level = 31; + hardware.gfx.hud_fade_level = 31; + hardware.gfx.render_scene_from_cache(&cache, &update); + hardware.gfx.present(); + + assert_eq!(hardware.gfx.front_buffer()[0], Color::RED.raw()); + } +} diff --git a/crates/console/prometeu-drivers/src/lib.rs b/crates/console/prometeu-drivers/src/lib.rs index eea0e5fc..f9d82bc5 100644 --- a/crates/console/prometeu-drivers/src/lib.rs +++ b/crates/console/prometeu-drivers/src/lib.rs @@ -9,5 +9,8 @@ mod touch; pub use crate::asset::AssetManager; pub use crate::audio::{Audio, AudioCommand, Channel, MAX_CHANNELS, OUTPUT_SAMPLE_RATE}; pub use crate::gfx::Gfx; -pub use crate::memory_banks::MemoryBanks; +pub use crate::memory_banks::{ + GlyphBankPoolAccess, GlyphBankPoolInstaller, MemoryBanks, SceneBankPoolAccess, + SceneBankPoolInstaller, SoundBankPoolAccess, SoundBankPoolInstaller, +}; pub use crate::pad::Pad; diff --git a/crates/console/prometeu-hal/src/gfx_bridge.rs b/crates/console/prometeu-hal/src/gfx_bridge.rs index 5d866e2c..1c677ab4 100644 --- a/crates/console/prometeu-hal/src/gfx_bridge.rs +++ b/crates/console/prometeu-hal/src/gfx_bridge.rs @@ -1,4 +1,6 @@ use crate::color::Color; +use crate::scene_viewport_cache::SceneViewportCache; +use crate::scene_viewport_resolver::ResolverUpdate; use crate::sprite::Sprite; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -47,6 +49,7 @@ pub trait GfxBridge { fn draw_vertical_line(&mut self, x: i32, y0: i32, y1: i32, color: Color); fn present(&mut self); fn render_all(&mut self); + fn render_scene_from_cache(&mut self, cache: &SceneViewportCache, update: &ResolverUpdate); fn draw_text(&mut self, x: i32, y: i32, text: &str, color: Color); fn draw_char(&mut self, x: i32, y: i32, c: char, color: Color);