implements PLN-0015

This commit is contained in:
bQUARKz 2026-04-13 20:35:10 +01:00
parent f7c31592bb
commit 4a5763523b
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
4 changed files with 90 additions and 165 deletions

View File

@ -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();

View File

@ -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<MemoryBanks>) -> 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());
}
}

View File

@ -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;

View File

@ -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);