dev/render-all-scene-cache-and-camera-integration #16

Merged
bquarkz merged 21 commits from dev/render-all-scene-cache-and-camera-integration into master 2026-04-18 16:20:50 +00:00
4 changed files with 105 additions and 1 deletions
Showing only changes of commit 2865cb3803 - Show all commits

View File

@ -1,3 +1,4 @@
use crate::gfx_overlay::{DeferredGfxOverlay, OverlayCommand};
use crate::memory_banks::GlyphBankPoolAccess; use crate::memory_banks::GlyphBankPoolAccess;
use prometeu_hal::GfxBridge; use prometeu_hal::GfxBridge;
use prometeu_hal::color::Color; use prometeu_hal::color::Color;
@ -49,6 +50,8 @@ pub struct Gfx {
/// Shared access to graphical memory banks (tiles and palettes). /// Shared access to graphical memory banks (tiles and palettes).
pub glyph_banks: Arc<dyn GlyphBankPoolAccess>, pub glyph_banks: Arc<dyn GlyphBankPoolAccess>,
/// Deferred overlay/debug capture kept separate from canonical game composition.
overlay: DeferredGfxOverlay,
/// Hardware sprite list (512 slots). Equivalent to OAM (Object Attribute Memory). /// Hardware sprite list (512 slots). Equivalent to OAM (Object Attribute Memory).
pub sprites: [Sprite; 512], pub sprites: [Sprite; 512],
@ -288,6 +291,7 @@ impl Gfx {
front: vec![0; len], front: vec![0; len],
back: vec![0; len], back: vec![0; len],
glyph_banks, glyph_banks,
overlay: DeferredGfxOverlay::default(),
sprites: [EMPTY_SPRITE; 512], sprites: [EMPTY_SPRITE; 512],
sprite_count: 0, sprite_count: 0,
scene_fade_level: 31, scene_fade_level: 31,
@ -307,6 +311,14 @@ impl Gfx {
(self.w, self.h) (self.w, self.h)
} }
pub fn begin_overlay_frame(&mut self) {
self.overlay.begin_frame();
}
pub fn overlay(&self) -> &DeferredGfxOverlay {
&self.overlay
}
/// The buffer that the host should display (RGB565). /// The buffer that the host should display (RGB565).
pub fn front_buffer(&self) -> &[u16] { pub fn front_buffer(&self) -> &[u16] {
&self.front &self.front
@ -326,6 +338,7 @@ impl Gfx {
color: Color, color: Color,
mode: BlendMode, mode: BlendMode,
) { ) {
self.overlay.push(OverlayCommand::FillRectBlend { x, y, w, h, color, mode });
if color == Color::COLOR_KEY { if color == Color::COLOR_KEY {
return; return;
} }
@ -367,6 +380,7 @@ impl Gfx {
/// Draws a line between two points using Bresenham's algorithm. /// Draws a line between two points using Bresenham's algorithm.
pub fn draw_line(&mut self, x0: i32, y0: i32, x1: i32, y1: i32, color: Color) { pub fn draw_line(&mut self, x0: i32, y0: i32, x1: i32, y1: i32, color: Color) {
self.overlay.push(OverlayCommand::DrawLine { x0, y0, x1, y1, color });
if color == Color::COLOR_KEY { if color == Color::COLOR_KEY {
return; return;
} }
@ -399,6 +413,7 @@ impl Gfx {
/// Draws a circle outline using Midpoint Circle Algorithm. /// Draws a circle outline using Midpoint Circle Algorithm.
pub fn draw_circle(&mut self, xc: i32, yc: i32, r: i32, color: Color) { pub fn draw_circle(&mut self, xc: i32, yc: i32, r: i32, color: Color) {
self.overlay.push(OverlayCommand::DrawCircle { x: xc, y: yc, r, color });
if color == Color::COLOR_KEY { if color == Color::COLOR_KEY {
return; return;
} }
@ -467,6 +482,7 @@ impl Gfx {
/// Draws a disc (filled circle with border). /// Draws a disc (filled circle with border).
pub fn draw_disc(&mut self, x: i32, y: i32, r: i32, border_color: Color, fill_color: Color) { pub fn draw_disc(&mut self, x: i32, y: i32, r: i32, border_color: Color, fill_color: Color) {
self.overlay.push(OverlayCommand::DrawDisc { x, y, r, border_color, fill_color });
self.fill_circle(x, y, r, fill_color); self.fill_circle(x, y, r, fill_color);
self.draw_circle(x, y, r, border_color); self.draw_circle(x, y, r, border_color);
} }
@ -496,6 +512,7 @@ impl Gfx {
border_color: Color, border_color: Color,
fill_color: Color, fill_color: Color,
) { ) {
self.overlay.push(OverlayCommand::DrawSquare { x, y, w, h, border_color, fill_color });
self.fill_rect(x, y, w, h, fill_color); self.fill_rect(x, y, w, h, fill_color);
self.draw_rect(x, y, w, h, border_color); self.draw_rect(x, y, w, h, border_color);
} }
@ -783,6 +800,7 @@ impl Gfx {
} }
pub fn draw_text(&mut self, x: i32, y: i32, text: &str, color: Color) { pub fn draw_text(&mut self, x: i32, y: i32, text: &str, color: Color) {
self.overlay.push(OverlayCommand::DrawText { x, y, text: text.to_string(), color });
let mut cx = x; let mut cx = x;
for c in text.chars() { for c in text.chars() {
self.draw_char(cx, y, c, color); self.draw_char(cx, y, c, color);
@ -827,7 +845,8 @@ impl Gfx {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::memory_banks::{GlyphBankPoolInstaller, MemoryBanks}; use crate::FrameComposer;
use crate::memory_banks::{GlyphBankPoolInstaller, MemoryBanks, SceneBankPoolAccess};
use prometeu_hal::glyph_bank::TileSize; use prometeu_hal::glyph_bank::TileSize;
use prometeu_hal::scene_bank::SceneBank; use prometeu_hal::scene_bank::SceneBank;
use prometeu_hal::scene_layer::{ParallaxFactor, SceneLayer}; use prometeu_hal::scene_layer::{ParallaxFactor, SceneLayer};
@ -926,9 +945,14 @@ mod tests {
fn test_draw_line() { fn test_draw_line() {
let banks = Arc::new(MemoryBanks::new()); let banks = Arc::new(MemoryBanks::new());
let mut gfx = Gfx::new(10, 10, banks); let mut gfx = Gfx::new(10, 10, banks);
gfx.begin_overlay_frame();
gfx.draw_line(0, 0, 9, 9, Color::WHITE); gfx.draw_line(0, 0, 9, 9, Color::WHITE);
assert_eq!(gfx.back[0], Color::WHITE.0); assert_eq!(gfx.back[0], Color::WHITE.0);
assert_eq!(gfx.back[9 * 10 + 9], Color::WHITE.0); assert_eq!(gfx.back[9 * 10 + 9], Color::WHITE.0);
assert_eq!(
gfx.overlay().commands(),
&[OverlayCommand::DrawLine { x0: 0, y0: 0, x1: 9, y1: 9, color: Color::WHITE }]
);
} }
#[test] #[test]
@ -954,11 +978,53 @@ mod tests {
fn test_draw_square() { fn test_draw_square() {
let banks = Arc::new(MemoryBanks::new()); let banks = Arc::new(MemoryBanks::new());
let mut gfx = Gfx::new(10, 10, banks); let mut gfx = Gfx::new(10, 10, banks);
gfx.begin_overlay_frame();
gfx.draw_square(2, 2, 6, 6, Color::WHITE, Color::BLACK); gfx.draw_square(2, 2, 6, 6, Color::WHITE, Color::BLACK);
// Border // Border
assert_eq!(gfx.back[2 * 10 + 2], Color::WHITE.0); assert_eq!(gfx.back[2 * 10 + 2], Color::WHITE.0);
// Fill // Fill
assert_eq!(gfx.back[3 * 10 + 3], Color::BLACK.0); assert_eq!(gfx.back[3 * 10 + 3], Color::BLACK.0);
assert_eq!(gfx.overlay().command_count(), 2);
}
#[test]
fn draw_text_captures_overlay_command() {
let banks = Arc::new(MemoryBanks::new());
let mut gfx = Gfx::new(32, 18, banks);
gfx.begin_overlay_frame();
gfx.draw_text(4, 5, "HUD", Color::WHITE);
assert_eq!(
gfx.overlay().commands(),
&[OverlayCommand::DrawText { x: 4, y: 5, text: "HUD".into(), color: Color::WHITE }]
);
}
#[test]
fn overlay_state_is_separate_from_frame_composer_sprite_state() {
let banks = Arc::new(MemoryBanks::new());
let mut gfx = Gfx::new(32, 18, Arc::clone(&banks) as Arc<dyn GlyphBankPoolAccess>);
let mut frame_composer =
FrameComposer::new(32, 18, Arc::clone(&banks) as Arc<dyn SceneBankPoolAccess>);
gfx.begin_overlay_frame();
frame_composer.begin_frame();
frame_composer.emit_sprite(Sprite {
glyph: Glyph { glyph_id: 0, palette_id: 0 },
x: 1,
y: 2,
layer: 0,
bank_id: 0,
active: true,
flip_x: false,
flip_y: false,
priority: 0,
});
gfx.draw_text(1, 1, "X", Color::WHITE);
assert_eq!(frame_composer.sprite_controller().sprite_count(), 1);
assert_eq!(gfx.overlay().command_count(), 1);
} }
#[test] #[test]

View File

@ -0,0 +1,35 @@
use crate::gfx::BlendMode;
use prometeu_hal::color::Color;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OverlayCommand {
FillRectBlend { x: i32, y: i32, w: i32, h: i32, color: Color, mode: BlendMode },
DrawLine { x0: i32, y0: i32, x1: i32, y1: i32, color: Color },
DrawCircle { x: i32, y: i32, r: i32, color: Color },
DrawDisc { x: i32, y: i32, r: i32, border_color: Color, fill_color: Color },
DrawSquare { x: i32, y: i32, w: i32, h: i32, border_color: Color, fill_color: Color },
DrawText { x: i32, y: i32, text: String, color: Color },
}
#[derive(Debug, Clone, Default)]
pub struct DeferredGfxOverlay {
commands: Vec<OverlayCommand>,
}
impl DeferredGfxOverlay {
pub fn begin_frame(&mut self) {
self.commands.clear();
}
pub fn push(&mut self, command: OverlayCommand) {
self.commands.push(command);
}
pub fn commands(&self) -> &[OverlayCommand] {
&self.commands
}
pub fn command_count(&self) -> usize {
self.commands.len()
}
}

View File

@ -48,6 +48,7 @@ impl Default for Hardware {
impl HardwareBridge for Hardware { impl HardwareBridge for Hardware {
fn begin_frame(&mut self) { fn begin_frame(&mut self) {
self.gfx.begin_overlay_frame();
self.frame_composer.begin_frame(); self.frame_composer.begin_frame();
} }

View File

@ -2,6 +2,7 @@ mod asset;
mod audio; mod audio;
mod frame_composer; mod frame_composer;
mod gfx; mod gfx;
mod gfx_overlay;
pub mod hardware; pub mod hardware;
mod memory_banks; mod memory_banks;
mod pad; mod pad;
@ -11,6 +12,7 @@ pub use crate::asset::AssetManager;
pub use crate::audio::{Audio, AudioCommand, Channel, MAX_CHANNELS, OUTPUT_SAMPLE_RATE}; pub use crate::audio::{Audio, AudioCommand, Channel, MAX_CHANNELS, OUTPUT_SAMPLE_RATE};
pub use crate::frame_composer::{FrameComposer, SceneStatus, SpriteController}; pub use crate::frame_composer::{FrameComposer, SceneStatus, SpriteController};
pub use crate::gfx::Gfx; pub use crate::gfx::Gfx;
pub use crate::gfx_overlay::{DeferredGfxOverlay, OverlayCommand};
pub use crate::memory_banks::{ pub use crate::memory_banks::{
GlyphBankPoolAccess, GlyphBankPoolInstaller, MemoryBanks, SceneBankPoolAccess, GlyphBankPoolAccess, GlyphBankPoolInstaller, MemoryBanks, SceneBankPoolAccess,
SceneBankPoolInstaller, SoundBankPoolAccess, SoundBankPoolInstaller, SceneBankPoolInstaller, SoundBankPoolAccess, SoundBankPoolInstaller,