implemented tile cache and raised performance
This commit is contained in:
parent
54ed1d0eaf
commit
e6f4d0c255
@ -8,6 +8,7 @@ pub struct Machine {
|
|||||||
pub pad: Pad,
|
pub pad: Pad,
|
||||||
pub touch: Touch,
|
pub touch: Touch,
|
||||||
pub frame_index: u64,
|
pub frame_index: u64,
|
||||||
|
pub last_frame_cpu_time_us: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Machine {
|
impl Machine {
|
||||||
@ -20,16 +21,19 @@ impl Machine {
|
|||||||
pad: Pad::default(),
|
pad: Pad::default(),
|
||||||
touch: Touch::default(),
|
touch: Touch::default(),
|
||||||
frame_index: 0,
|
frame_index: 0,
|
||||||
|
last_frame_cpu_time_us: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// "Contrato de frame" do PROMETEU (Template Method sem loop).
|
/// "Contrato de frame" do PROMETEU (Template Method sem loop).
|
||||||
/// O Host controla tempo/event loop; o Core define a sequência do frame.
|
/// O Host controla tempo/event loop; o Core define a sequência do frame.
|
||||||
pub fn step_frame(&mut self, signals: &InputSignals) {
|
pub fn step_frame(&mut self, signals: &InputSignals) {
|
||||||
|
let start = std::time::Instant::now();
|
||||||
self.begin_frame(signals);
|
self.begin_frame(signals);
|
||||||
self.tick(); // futuro: executa cartucho/VM
|
self.tick(); // futuro: executa cartucho/VM
|
||||||
self.gfx.render_all(); // A máquina executa o pipeline gráfico (Ação física)
|
self.gfx.render_all(); // A máquina executa o pipeline gráfico (Ação física)
|
||||||
self.end_frame(); // present/housekeeping
|
self.end_frame(); // present/housekeeping
|
||||||
|
self.last_frame_cpu_time_us = start.elapsed().as_micros() as u64; // Calcula quanto tempo a "CPU simulada" trabalhou de verdade
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Início do frame: zera flags transitórias.
|
/// Início do frame: zera flags transitórias.
|
||||||
|
|||||||
@ -124,8 +124,24 @@ impl Gfx {
|
|||||||
/// Pipeline principal de renderização do frame.
|
/// Pipeline principal de renderização do frame.
|
||||||
/// Segue a ordem de prioridade do manual (Capítulo 4.11).
|
/// Segue a ordem de prioridade do manual (Capítulo 4.11).
|
||||||
pub fn render_all(&mut self) {
|
pub fn render_all(&mut self) {
|
||||||
|
// 0. Fase de Preparação: Organiza quem deve ser desenhado em cada camada
|
||||||
|
// Criamos listas de índices para evitar percorrer os 512 sprites 5 vezes
|
||||||
|
let mut priority_buckets: [Vec<usize>; 5] = [
|
||||||
|
Vec::with_capacity(128),
|
||||||
|
Vec::with_capacity(128),
|
||||||
|
Vec::with_capacity(128),
|
||||||
|
Vec::with_capacity(128),
|
||||||
|
Vec::with_capacity(128),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (idx, sprite) in self.sprites.iter().enumerate() {
|
||||||
|
if sprite.active && sprite.priority < 5 {
|
||||||
|
priority_buckets[sprite.priority as usize].push(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Sprites de prioridade 0 (Atrás da Layer 0 - o fundo do fundo)
|
// 1. Sprites de prioridade 0 (Atrás da Layer 0 - o fundo do fundo)
|
||||||
self.render_sprites_by_priority(0);
|
self.draw_bucket(&priority_buckets[0]);
|
||||||
|
|
||||||
for i in 0..self.layers.len() {
|
for i in 0..self.layers.len() {
|
||||||
// 2. Layers de jogo (0 a 3)
|
// 2. Layers de jogo (0 a 3)
|
||||||
@ -135,7 +151,7 @@ impl Gfx {
|
|||||||
// i=1 desenha sprites priority 2 (sobre layer 1)
|
// i=1 desenha sprites priority 2 (sobre layer 1)
|
||||||
// i=2 desenha sprites priority 3 (sobre layer 2)
|
// i=2 desenha sprites priority 3 (sobre layer 2)
|
||||||
// i=3 desenha sprites priority 4 (sobre layer 3 - à frente de tudo)
|
// i=3 desenha sprites priority 4 (sobre layer 3 - à frente de tudo)
|
||||||
self.render_sprites_by_priority((i + 1) as u8);
|
self.draw_bucket(&priority_buckets[i + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Aplica Scene Fade (Afeta tudo desenhado até agora)
|
// 4. Aplica Scene Fade (Afeta tudo desenhado até agora)
|
||||||
@ -183,51 +199,72 @@ impl Gfx {
|
|||||||
scroll_x: i32,
|
scroll_x: i32,
|
||||||
scroll_y: i32
|
scroll_y: i32
|
||||||
) {
|
) {
|
||||||
let size = bank.tile_size as usize;
|
let tile_size = bank.tile_size as usize;
|
||||||
|
|
||||||
for screen_y in 0..screen_h {
|
// 1. Calcular quantos tiles cabem na tela (com margem de 1 para o scroll)
|
||||||
for screen_x in 0..screen_w {
|
let visible_tiles_x = (screen_w / tile_size) + 1;
|
||||||
let world_x = screen_x as i32 + scroll_x;
|
let visible_tiles_y = (screen_h / tile_size) + 1;
|
||||||
let world_y = screen_y as i32 + scroll_y;
|
|
||||||
|
|
||||||
let tile_x = (world_x / size as i32) as usize;
|
// 2. Calcular o offset inicial (onde o primeiro tile começa a ser desenhado)
|
||||||
let tile_y = (world_y / size as i32) as usize;
|
let start_tile_x = scroll_x / tile_size as i32;
|
||||||
|
let start_tile_y = scroll_y / tile_size as i32;
|
||||||
|
|
||||||
if tile_x >= map.width || tile_y >= map.height { continue; }
|
let fine_scroll_x = scroll_x % tile_size as i32;
|
||||||
|
let fine_scroll_y = scroll_y % tile_size as i32;
|
||||||
|
|
||||||
let tile = map.tiles[tile_y * map.width + tile_x];
|
// 3. Loop por Tile (Muito mais eficiente)
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Skip se estiver fora dos limites do mapa
|
||||||
|
if map_x >= map.width || map_y >= map.height { continue; }
|
||||||
|
|
||||||
|
let tile = map.tiles[map_y * map.width + map_x];
|
||||||
if tile.id == 0 { continue; }
|
if tile.id == 0 { continue; }
|
||||||
|
|
||||||
let local_x = (world_x % size as i32) as usize;
|
// 4. Desenha o bloco do tile na tela
|
||||||
let local_y = (world_y % size as i32) as usize;
|
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;
|
||||||
|
|
||||||
let color = bank.get_pixel(tile.id, local_x, local_y);
|
Self::draw_tile_pixels(back, screen_w, screen_h, screen_tile_x, screen_tile_y, tile, bank);
|
||||||
if color == Color::COLOR_KEY { continue; }
|
|
||||||
|
|
||||||
let idx = (screen_y * screen_w) + screen_x;
|
|
||||||
back[idx] = color.0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_sprites_by_priority(&mut self, priority: u8) {
|
// Função auxiliar para desenhar um bloco de 8x8, 16x16 ou 32x32 pixels
|
||||||
for sprite in self.sprites.iter() {
|
fn draw_tile_pixels(back: &mut [u16], screen_w: usize, screen_h: usize, x: i32, y: i32, tile: crate::model::Tile, bank: &TileBank) {
|
||||||
if !sprite.active || sprite.priority != priority {
|
let size = bank.tile_size as usize;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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; }
|
||||||
|
|
||||||
|
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 };
|
||||||
|
|
||||||
|
let color = bank.get_pixel(tile.id, fetch_x, fetch_y);
|
||||||
|
if color == Color::COLOR_KEY { continue; }
|
||||||
|
|
||||||
|
back[world_y as usize * screen_w + world_x as usize] = color.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_bucket(&mut self, bucket: &[usize]) {
|
||||||
|
for &idx in bucket {
|
||||||
|
let sprite = &self.sprites[idx];
|
||||||
let bank = match self.banks.get(sprite.bank_id as usize) {
|
let bank = match self.banks.get(sprite.bank_id as usize) {
|
||||||
Some(Some(b)) => b,
|
Some(Some(b)) => b,
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
Self::draw_sprite_pixel_by_pixel(
|
Self::draw_sprite_pixel_by_pixel(&mut self.back, self.w, self.h, sprite, bank);
|
||||||
&mut self.back,
|
|
||||||
self.w,
|
|
||||||
self.h,
|
|
||||||
sprite,
|
|
||||||
bank
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -213,9 +213,12 @@ impl ApplicationHandler for PrometeuApp {
|
|||||||
let fps = self.frames_since_last_update as f64 / stats_elapsed.as_secs_f64();
|
let fps = self.frames_since_last_update as f64 / stats_elapsed.as_secs_f64();
|
||||||
let kb = self.machine.gfx.memory_usage_bytes() as f64 / 1024.0;
|
let kb = self.machine.gfx.memory_usage_bytes() as f64 / 1024.0;
|
||||||
|
|
||||||
|
let frame_budget_us = 16666.0;
|
||||||
|
let cpu_load = (self.machine.last_frame_cpu_time_us as f64 / frame_budget_us) * 100.0;
|
||||||
|
|
||||||
let title = format!(
|
let title = format!(
|
||||||
"PROMETEU | GFX: {:.1} KB | FPS: {:.1} | Frame: {}",
|
"PROMETEU | GFX: {:.1} KB | FPS: {:.1} | Load: {:.1}% | Frame: {}",
|
||||||
kb, fps, self.machine.frame_index
|
kb, fps, cpu_load, self.machine.frame_index
|
||||||
);
|
);
|
||||||
window.set_title(&title);
|
window.set_title(&title);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user