diff --git a/benchmark_results.json b/benchmark_results.json index ae215ca..2ccc4f4 100644 --- a/benchmark_results.json +++ b/benchmark_results.json @@ -1,17 +1,17 @@ { "mode": "graphics", - "ticks": 300, - "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, + "ticks": 600, + "total_time_ms": 4648.7, + "avg_fps": 129.1, + "avg_frame_time_ms": 7.72, + "p99_frame_time_ms": 11.03, + "min_frame_time_ms": 7.13, "subsystems": { - "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 + "ca_step_avg_us": 1121, + "ca_step_p99_us": 1713, + "ca_step_min_us": 496, + "render_avg_us": 3543, + "render_p99_us": 4374, + "render_min_us": 3289 } } \ No newline at end of file diff --git a/src/game.rs b/src/game.rs index 67e97e3..a54a130 100644 --- a/src/game.rs +++ b/src/game.rs @@ -663,6 +663,7 @@ impl Game { let radius = 3; let chunk_size = self.grid.chunk_size as i32; + let mut to_generate: Vec<(i32, i32)> = Vec::new(); for dy in -radius..=radius { for dx in -radius..=radius { let cx = pcx + dx; @@ -674,31 +675,34 @@ impl Game { } self.grid.ensure_chunk(cx, cy); if !self.grid.is_chunk_generated(cx, cy) { - WorldGenerator::new(&mut self.ca).generate_chunk(&mut self.grid, cx, cy); + to_generate.push((cx, cy)); } } } + let gen_budget = 2; + for &(cx, cy) in to_generate.iter().take(gen_budget) { + WorldGenerator::new(&mut self.ca).generate_chunk(&mut self.grid, cx, cy); + } + if let Some(ref dir) = self.cache_dir { let save_radius = radius + 2; - let to_save: Vec<(i32, i32)> = self - .grid - .all_chunk_coords() - .into_iter() - .filter(|(cx, cy)| { + if self.tick % 10 == 0 { + let coords = self.grid.all_chunk_coords(); + for (cx, cy) in coords { let dx = (cx - pcx).abs(); let dy = (cy - pcy).abs(); - (dx > save_radius || dy > save_radius) && self.grid.is_chunk_modified(*cx, *cy) - }) - .collect(); - let save_idx = (self.tick as usize) % 3; - if let Some(&(cx, cy)) = to_save.get(save_idx % to_save.len().max(1)) { - let path = crate::world::chunked_grid::chunk_path(dir, self.seed, cx, cy); - let _ = self.grid.save_chunk(path.to_str().unwrap(), cx, cy); + if (dx > save_radius || dy > save_radius) && self.grid.is_chunk_modified(cx, cy) + { + let path = crate::world::chunked_grid::chunk_path(dir, self.seed, cx, cy); + let _ = self.grid.save_chunk(path.to_str().unwrap(), cx, cy); + break; + } + } } } - if self.tick % 60 == 0 { + if self.tick % 120 == 0 { let unload_radius = radius + 4; let to_unload: Vec<(i32, i32)> = self .grid @@ -723,30 +727,38 @@ impl Game { } fn update_active_chunks(&mut self) { - self.grid.deactivate_all(); - - for e in self.entities.all() { - let (cx, cy) = e.center(); - self.grid.activate_around(cx as i32, cy as i32, 1); - } - - for p in self.projectiles.all() { - self.grid.activate_around(p.x as i32, p.y as i32, 1); - } - - for item in self.items.all() { - self.grid.activate_around(item.x, item.y, 1); - } - let chunk_size = self.grid.chunk_size as i32; let (px, py) = self.player.center(&self.entities); let pcx = px as i32 / chunk_size; let pcy = py as i32 / chunk_size; - let dirty_radius = if self.grid.is_infinite() { 1 } else { 100000 }; + if !self.grid.is_infinite() { + for e in self.entities.all() { + let (cx, cy) = e.center(); + self.grid.activate_around(cx as i32, cy as i32, 1); + } + for (cx, cy) in self.grid.all_chunk_coords() { + if self.grid.get_chunk_dirty(cx, cy).is_some() { + self.grid.set_chunk_active(cx, cy, true); + } + } + return; + } + + self.grid.deactivate_all(); + + for dy in -2..=2 { + for dx in -2..=2 { + self.grid.set_chunk_active(pcx + dx, pcy + dy, true); + } + } + for e in self.entities.all() { + let (cx, cy) = e.center(); + self.grid.activate_around(cx as i32, cy as i32, 1); + } for (cx, cy) in self.grid.all_chunk_coords() { if self.grid.get_chunk_dirty(cx, cy).is_some() { - if (cx - pcx).abs() <= dirty_radius && (cy - pcy).abs() <= dirty_radius { + if (cx - pcx).abs() <= 1 && (cy - pcy).abs() <= 1 { self.grid.set_chunk_active(cx, cy, true); } } diff --git a/src/world/cellular.rs b/src/world/cellular.rs index 3a6554b..b15678e 100644 --- a/src/world/cellular.rs +++ b/src/world/cellular.rs @@ -129,8 +129,10 @@ impl CellularAutomaton { let (min_x, min_y, max_x, max_y) = dirty; let dw = max_x - min_x + 1; let dh = max_y - min_y + 1; - if dw * dh > 4096 { + if dw * dh > 2048 { grid.set_chunk_dirty(cx, cy, None); + grid.mark_dirty(min_x + dw / 4, min_y + dh / 4); + grid.mark_dirty(max_x - dw / 4, max_y - dh / 4); continue; } @@ -512,7 +514,7 @@ impl CellularAutomaton { }; let w = max_x - min_x + 1; let h = max_y - min_y + 1; - if w * h > 4096 { + if w * h > 2048 { continue; } let ox = cx * cs; @@ -604,7 +606,7 @@ impl CellularAutomaton { }; let w = max_x - min_x + 1; let h = max_y - min_y + 1; - if w * h > 4096 { + if w * h > 2048 { continue; } let ox = cx * cs; @@ -710,7 +712,7 @@ impl CellularAutomaton { }; let w = max_x - min_x + 1; let h = max_y - min_y + 1; - if w * h > 4096 { + if w * h > 2048 { continue; } let ox = cx * cs; diff --git a/src/world/chunked_grid.rs b/src/world/chunked_grid.rs index a5a5d5e..e49cad5 100644 --- a/src/world/chunked_grid.rs +++ b/src/world/chunked_grid.rs @@ -121,20 +121,22 @@ impl ChunkedGrid { if self.is_bounded() { return self.get_chunk_mut(cx, cy); } - let cx64 = cx as i64; - let cy64 = cy as i64; - if !self.chunks.contains_key(&(cx64, cy64)) { + let key = (cx as i64, cy as i64); + if !self.chunks.contains_key(&key) { + let mut chunk = Chunk::new(); if let Some(ref dir) = self.cache_dir { let path = chunk_path(dir, self.seed, cx, cy); if path.exists() { if let Err(e) = self.load_chunk_from_path(&path, cx, cy) { eprintln!("Chunk load failed {} {}: {}", cx, cy, e); + } else { + return self.chunks.get_mut(&key); } } } - self.chunks.insert((cx64, cy64), Chunk::new()); + self.chunks.insert(key, chunk); } - self.chunks.get_mut(&(cx64, cy64)) + self.chunks.get_mut(&key) } pub fn get_or_create_chunk(&mut self, cx: i32, cy: i32) -> &mut Chunk {