perf: UiLayer HashMap → flat Vec array — 62 FPS → 95 FPS
Replaced HashMap<(i32,i32), UiCell> with Vec<Option<UiCell>> indexed by y*width+x. dirty_keys Vec tracks non-None cells for iteration. - set/set_alpha: O(1) array write (was O(1) amortized HashMap insert with hashing overhead) - get: O(1) array index (was HashMap lookup) - keys(): iterate dirty_keys Vec (was HashMap keys iterator) - clear(): memset None over flat array (was HashMap::clear) - resize() called in build_ui to size array to viewport - All 185 tests + 14 scenarios pass - Benchmark: 95.1 FPS (was 61.9 — 53% improvement)
This commit is contained in:
+11
-11
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"mode": "graphics",
|
||||
"ticks": 300,
|
||||
"total_time_ms": 4845.5,
|
||||
"avg_fps": 61.9,
|
||||
"avg_frame_time_ms": 16.12,
|
||||
"p99_frame_time_ms": 145.95,
|
||||
"min_frame_time_ms": 8.19,
|
||||
"total_time_ms": 3153.2,
|
||||
"avg_fps": 95.1,
|
||||
"avg_frame_time_ms": 10.48,
|
||||
"p99_frame_time_ms": 65.20,
|
||||
"min_frame_time_ms": 6.55,
|
||||
"subsystems": {
|
||||
"ca_step_avg_us": 8153,
|
||||
"ca_step_p99_us": 137703,
|
||||
"ca_step_min_us": 1174,
|
||||
"render_avg_us": 4433,
|
||||
"render_p99_us": 6450,
|
||||
"render_min_us": 3956
|
||||
"ca_step_avg_us": 3386,
|
||||
"ca_step_p99_us": 58075,
|
||||
"ca_step_min_us": 231,
|
||||
"render_avg_us": 3770,
|
||||
"render_p99_us": 5334,
|
||||
"render_min_us": 3320
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 640 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 115 KiB |
@@ -357,6 +357,7 @@ impl Game {
|
||||
let ui_h = (vh as i32) * crate::ui::UI_SCALE;
|
||||
let fs = ((ui_h / 200).max(2)).min(6) as i32;
|
||||
self.ui.set_font_scale(fs);
|
||||
self.ui.resize(ui_w as usize, ui_h as usize);
|
||||
|
||||
let player = self.player.entity(&self.entities).cloned();
|
||||
let brush = self
|
||||
|
||||
+45
-13
@@ -1,5 +1,3 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::entity::entity::{Entity, EntityKind};
|
||||
use crate::world::cell::MaterialId;
|
||||
use crate::world::chunked_grid::ChunkedGrid;
|
||||
@@ -17,7 +15,10 @@ pub struct UiCell {
|
||||
}
|
||||
|
||||
pub struct UiLayer {
|
||||
cells: HashMap<(i32, i32), UiCell>,
|
||||
cells: Vec<Option<UiCell>>,
|
||||
width: usize,
|
||||
height: usize,
|
||||
dirty_keys: Vec<(i32, i32)>,
|
||||
messages: Vec<(String, u32)>,
|
||||
damage_numbers: Vec<DamageNumber>,
|
||||
font_scale: i32,
|
||||
@@ -35,7 +36,10 @@ pub struct DamageNumber {
|
||||
impl UiLayer {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
cells: HashMap::new(),
|
||||
cells: Vec::new(),
|
||||
width: 0,
|
||||
height: 0,
|
||||
dirty_keys: Vec::new(),
|
||||
messages: Vec::new(),
|
||||
damage_numbers: Vec::new(),
|
||||
font_scale: 2,
|
||||
@@ -61,7 +65,10 @@ impl UiLayer {
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.cells.clear();
|
||||
for c in self.cells.iter_mut() {
|
||||
*c = None;
|
||||
}
|
||||
self.dirty_keys.clear();
|
||||
self.damage_numbers.retain(|d| d.life > 0);
|
||||
for d in &mut self.damage_numbers {
|
||||
d.y -= 0.1;
|
||||
@@ -69,28 +76,53 @@ impl UiLayer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, w: usize, h: usize) {
|
||||
self.width = w;
|
||||
self.height = h;
|
||||
self.cells.clear();
|
||||
self.cells.resize(w * h, None);
|
||||
self.dirty_keys.clear();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn idx(&self, x: i32, y: i32) -> Option<usize> {
|
||||
if x < 0 || y < 0 || x as usize >= self.width || y as usize >= self.height {
|
||||
return None;
|
||||
}
|
||||
Some(y as usize * self.width + x as usize)
|
||||
}
|
||||
|
||||
pub fn set(&mut self, x: i32, y: i32, ch: char, fg: [u8; 3], bg: [u8; 3]) {
|
||||
self.cells.insert(
|
||||
(x, y),
|
||||
UiCell {
|
||||
if let Some(i) = self.idx(x, y) {
|
||||
let was_none = self.cells[i].is_none();
|
||||
self.cells[i] = Some(UiCell {
|
||||
ch,
|
||||
fg,
|
||||
bg,
|
||||
alpha: 255,
|
||||
},
|
||||
);
|
||||
});
|
||||
if was_none {
|
||||
self.dirty_keys.push((x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_alpha(&mut self, x: i32, y: i32, ch: char, fg: [u8; 3], bg: [u8; 3], alpha: u8) {
|
||||
self.cells.insert((x, y), UiCell { ch, fg, bg, alpha });
|
||||
if let Some(i) = self.idx(x, y) {
|
||||
let was_none = self.cells[i].is_none();
|
||||
self.cells[i] = Some(UiCell { ch, fg, bg, alpha });
|
||||
if was_none {
|
||||
self.dirty_keys.push((x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, x: i32, y: i32) -> Option<&UiCell> {
|
||||
self.cells.get(&(x, y))
|
||||
self.idx(x, y).and_then(|i| self.cells[i].as_ref())
|
||||
}
|
||||
|
||||
pub fn keys(&self) -> impl Iterator<Item = &(i32, i32)> {
|
||||
self.cells.keys()
|
||||
self.dirty_keys.iter()
|
||||
}
|
||||
|
||||
pub fn add_message(&mut self, text: &str) {
|
||||
|
||||
@@ -53,6 +53,7 @@ fn depth_shown_in_hud() {
|
||||
let mut ui = verbatim::ui::UiLayer::new();
|
||||
let screen_w = 320;
|
||||
let screen_h = 100;
|
||||
ui.resize(screen_w, screen_h);
|
||||
ui.draw_hud(
|
||||
screen_w,
|
||||
screen_h,
|
||||
|
||||
@@ -172,6 +172,7 @@ fn ui_hud_shows_player_hp() {
|
||||
let mut s = setup();
|
||||
let mut ui = verbatim::ui::UiLayer::new();
|
||||
let player = s.game.entities.all()[0].clone();
|
||||
ui.resize(80, 25);
|
||||
ui.draw_hud(
|
||||
80,
|
||||
25,
|
||||
|
||||
Reference in New Issue
Block a user