summaryrefslogtreecommitdiff
path: root/src/sim/sim.rs
diff options
context:
space:
mode:
authorKai Stevenson <kai@kaistevenson.com>2026-08-13 21:31:53 -0700
committerKai Stevenson <kai@kaistevenson.com>2026-08-13 21:31:53 -0700
commit69f0407637a071d79d73115e4ae9c0ab526066a7 (patch)
treec22f8fa6e761985a70d86ad023b54fc374f6f6a3 /src/sim/sim.rs
parentfeefeecec6c6050635b2c016452dfa1529575987 (diff)
parallelization
Diffstat (limited to 'src/sim/sim.rs')
-rw-r--r--src/sim/sim.rs78
1 files changed, 54 insertions, 24 deletions
diff --git a/src/sim/sim.rs b/src/sim/sim.rs
index 5cd065e..6e0029e 100644
--- a/src/sim/sim.rs
+++ b/src/sim/sim.rs
@@ -1,4 +1,6 @@
-use std::marker::PhantomData;
+use std::{collections::HashMap, marker::PhantomData};
+
+use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use crate::{
config::CHUNK_SIZE,
@@ -25,6 +27,8 @@ impl<'a> ChunkAccess<'a> {
}
}
+unsafe impl Sync for ChunkAccess<'_> {}
+
pub struct UpdateCtx<'a, 'b, 'c> {
pub chunks: &'a mut [Option<&'b mut Chunk>; 9],
pub seqno: u64,
@@ -87,6 +91,12 @@ impl UpdateCtx<'_, '_, '_> {
let x = self.x + dx;
let y = self.y + dy;
set_cell(self.chunks, x, y, cell);
+ // wake all the chunks
+ self.chunks.iter_mut().for_each(|c| {
+ if let Some(chunk) = c {
+ chunk.sleeping = false;
+ }
+ });
}
pub fn candidates_swap(&mut self, candidates: &[(i32, i32)]) -> bool {
@@ -155,37 +165,57 @@ const NEIGHBORHOOD_OFFSETS: [(i32, i32); 9] = [
(1, 1),
];
-pub fn sim_tick(world: &mut World, seqno: u64) {
+pub fn sim_tick(world: &mut World, seqno: u64, use_threading: bool) {
puffin::profile_function!();
- let mut update_groups: [Vec<(i32, i32)>; 9] = Default::default();
- // assign a color to each chunk s.t. every chunk is surrounded by <= 8 chunks of different colors
+ let mut columns: HashMap<i32, Vec<i32>> = HashMap::new();
+ for &(cx, cy) in world.chunk_position_to_chunk_idx.keys() {
+ columns.entry(cx).or_default().push(cy);
+ }
+
+ // color columns s.t. columns of same color are separated by two columns
+ // and sort the column bottom-to-top
// --------------------
// | 0, 1, 2, 0, 1, 2 |
- // | 3, 4, 5, 3, 4, 5 |
- // | 6, 7, 8, 6, 7, 8 |
// | 0, 1, 2, 0, 1, 2 |
- // | 3, 4, 5, 3, 4, 5 |
- // | 6, 7, 8, 6, 7, 8 |
+ // | 0, 1, 2, 0, 1, 2 |
// --------------------
-
- for (&(cx, cy), _) in &world.chunk_position_to_chunk_idx {
- let color = (cx.rem_euclid(3) * 3 + cy.rem_euclid(3)) as usize;
- update_groups[color].push((cx, cy));
+ let mut columns_by_color: [Vec<(i32, Vec<i32>)>; 3] = Default::default();
+ for (cx, mut cys) in columns {
+ cys.sort_unstable_by(|a, b| b.cmp(a));
+ columns_by_color[cx.rem_euclid(3) as usize].push((cx, cys));
}
- for group in &update_groups {
- let access = ChunkAccess::new(&mut world.chunks);
- // TODO this can be parallelized since they will never share neighbours
- for &(cx, cy) in group {
- let mut chunks: [Option<&mut Chunk>; 9] = NEIGHBORHOOD_OFFSETS.map(|(dx, dy)| {
- world
- .chunk_position_to_chunk_idx
- .get(&(cx + dx, cy + dy))
- .map(|&idx| unsafe { access.get(idx) })
- });
+ let access = ChunkAccess::new(&mut world.chunks);
- sim_tick_chunk(&mut chunks, seqno);
- }
+ for color in &columns_by_color {
+ puffin::profile_scope!("chunk_color");
+
+ let chunk_closure = |(cx, cys): &(i32, Vec<i32>)| {
+ let cx = *cx;
+ for &cy in cys {
+ let mut chunks: [Option<&mut Chunk>; 9] = NEIGHBORHOOD_OFFSETS.map(|(dx, dy)| {
+ world
+ .chunk_position_to_chunk_idx
+ .get(&(cx + dx, cy + dy))
+ .map(|&idx| unsafe { access.get(idx) })
+ });
+
+ if let Some(target) = &mut chunks[4] {
+ if target.sleeping {
+ continue;
+ }
+ target.sleeping = true;
+ }
+
+ sim_tick_chunk(&mut chunks, seqno);
+ }
+ };
+
+ if use_threading {
+ color.par_iter().for_each(chunk_closure);
+ } else {
+ color.iter().for_each(chunk_closure);
+ };
}
}