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",
|
"mode": "graphics",
|
||||||
"ticks": 300,
|
"ticks": 300,
|
||||||
"total_time_ms": 4845.5,
|
"total_time_ms": 3153.2,
|
||||||
"avg_fps": 61.9,
|
"avg_fps": 95.1,
|
||||||
"avg_frame_time_ms": 16.12,
|
"avg_frame_time_ms": 10.48,
|
||||||
"p99_frame_time_ms": 145.95,
|
"p99_frame_time_ms": 65.20,
|
||||||
"min_frame_time_ms": 8.19,
|
"min_frame_time_ms": 6.55,
|
||||||
"subsystems": {
|
"subsystems": {
|
||||||
"ca_step_avg_us": 8153,
|
"ca_step_avg_us": 3386,
|
||||||
"ca_step_p99_us": 137703,
|
"ca_step_p99_us": 58075,
|
||||||
"ca_step_min_us": 1174,
|
"ca_step_min_us": 231,
|
||||||
"render_avg_us": 4433,
|
"render_avg_us": 3770,
|
||||||
"render_p99_us": 6450,
|
"render_p99_us": 5334,
|
||||||
"render_min_us": 3956
|
"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 ui_h = (vh as i32) * crate::ui::UI_SCALE;
|
||||||
let fs = ((ui_h / 200).max(2)).min(6) as i32;
|
let fs = ((ui_h / 200).max(2)).min(6) as i32;
|
||||||
self.ui.set_font_scale(fs);
|
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 player = self.player.entity(&self.entities).cloned();
|
||||||
let brush = self
|
let brush = self
|
||||||
|
|||||||
+45
-13
@@ -1,5 +1,3 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use crate::entity::entity::{Entity, EntityKind};
|
use crate::entity::entity::{Entity, EntityKind};
|
||||||
use crate::world::cell::MaterialId;
|
use crate::world::cell::MaterialId;
|
||||||
use crate::world::chunked_grid::ChunkedGrid;
|
use crate::world::chunked_grid::ChunkedGrid;
|
||||||
@@ -17,7 +15,10 @@ pub struct UiCell {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct UiLayer {
|
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)>,
|
messages: Vec<(String, u32)>,
|
||||||
damage_numbers: Vec<DamageNumber>,
|
damage_numbers: Vec<DamageNumber>,
|
||||||
font_scale: i32,
|
font_scale: i32,
|
||||||
@@ -35,7 +36,10 @@ pub struct DamageNumber {
|
|||||||
impl UiLayer {
|
impl UiLayer {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
cells: HashMap::new(),
|
cells: Vec::new(),
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
dirty_keys: Vec::new(),
|
||||||
messages: Vec::new(),
|
messages: Vec::new(),
|
||||||
damage_numbers: Vec::new(),
|
damage_numbers: Vec::new(),
|
||||||
font_scale: 2,
|
font_scale: 2,
|
||||||
@@ -61,7 +65,10 @@ impl UiLayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
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);
|
self.damage_numbers.retain(|d| d.life > 0);
|
||||||
for d in &mut self.damage_numbers {
|
for d in &mut self.damage_numbers {
|
||||||
d.y -= 0.1;
|
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]) {
|
pub fn set(&mut self, x: i32, y: i32, ch: char, fg: [u8; 3], bg: [u8; 3]) {
|
||||||
self.cells.insert(
|
if let Some(i) = self.idx(x, y) {
|
||||||
(x, y),
|
let was_none = self.cells[i].is_none();
|
||||||
UiCell {
|
self.cells[i] = Some(UiCell {
|
||||||
ch,
|
ch,
|
||||||
fg,
|
fg,
|
||||||
bg,
|
bg,
|
||||||
alpha: 255,
|
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) {
|
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> {
|
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)> {
|
pub fn keys(&self) -> impl Iterator<Item = &(i32, i32)> {
|
||||||
self.cells.keys()
|
self.dirty_keys.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_message(&mut self, text: &str) {
|
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 mut ui = verbatim::ui::UiLayer::new();
|
||||||
let screen_w = 320;
|
let screen_w = 320;
|
||||||
let screen_h = 100;
|
let screen_h = 100;
|
||||||
|
ui.resize(screen_w, screen_h);
|
||||||
ui.draw_hud(
|
ui.draw_hud(
|
||||||
screen_w,
|
screen_w,
|
||||||
screen_h,
|
screen_h,
|
||||||
|
|||||||
@@ -172,6 +172,7 @@ fn ui_hud_shows_player_hp() {
|
|||||||
let mut s = setup();
|
let mut s = setup();
|
||||||
let mut ui = verbatim::ui::UiLayer::new();
|
let mut ui = verbatim::ui::UiLayer::new();
|
||||||
let player = s.game.entities.all()[0].clone();
|
let player = s.game.entities.all()[0].clone();
|
||||||
|
ui.resize(80, 25);
|
||||||
ui.draw_hud(
|
ui.draw_hud(
|
||||||
80,
|
80,
|
||||||
25,
|
25,
|
||||||
|
|||||||
Reference in New Issue
Block a user