Merge pull request #30 from eliasstepanik/codex/multithread-rebuild_dirty_chunks-und-enqueue_visible_chunks

Parallelize chunk systems
This commit is contained in:
Elias Stepanik 2025-06-15 03:00:21 +02:00 committed by GitHub
commit 292d0508a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 77 additions and 57 deletions

View File

@ -1,23 +1,26 @@
use bevy::prelude::*;
use crate::plugins::environment::systems::voxels::helper::world_to_chunk; use crate::plugins::environment::systems::voxels::helper::world_to_chunk;
use crate::plugins::environment::systems::voxels::structure::*; use crate::plugins::environment::systems::voxels::structure::*;
use bevy::prelude::*;
use rayon::prelude::*;
/// enqueue chunks that *should* be visible but are not yet spawned /// enqueue chunks that *should* be visible but are not yet spawned
/// enqueue chunks that *should* be visible but are not yet spawned /// enqueue chunks that *should* be visible but are not yet spawned
pub fn enqueue_visible_chunks( pub fn enqueue_visible_chunks(
mut queue : ResMut<ChunkQueue>, mut queue: ResMut<ChunkQueue>,
spawned : Res<SpawnedChunks>, spawned: Res<SpawnedChunks>,
mut prev_cam : ResMut<PrevCameraChunk>, mut prev_cam: ResMut<PrevCameraChunk>,
cfg : Res<ChunkCullingCfg>, cfg: Res<ChunkCullingCfg>,
cam_q : Query<&GlobalTransform, With<Camera>>, cam_q: Query<&GlobalTransform, With<Camera>>,
tree_q : Query<&SparseVoxelOctree>, tree_q: Query<&SparseVoxelOctree>,
) { ) {
let Ok(tree) = tree_q.get_single() else { return }; let Ok(tree) = tree_q.get_single() else {
let Ok(cam_tf) = cam_q.get_single() else { return }; return;
let cam_pos = cam_tf.translation(); };
let centre = world_to_chunk(tree, cam_pos); let Ok(cam_tf) = cam_q.get_single() else {
return;
};
let cam_pos = cam_tf.translation();
let centre = world_to_chunk(tree, cam_pos);
if prev_cam.0 == Some(centre) { if prev_cam.0 == Some(centre) {
return; return;
@ -28,14 +31,18 @@ pub fn enqueue_visible_chunks(
let mut keys: Vec<(ChunkKey, i32)> = tree let mut keys: Vec<(ChunkKey, i32)> = tree
.occupied_chunks .occupied_chunks
.iter() .par_iter()
.filter_map(|key| { .filter_map(|key| {
let dx = key.0 - centre.0; let dx = key.0 - centre.0;
let dy = key.1 - centre.1; let dy = key.1 - centre.1;
let dz = key.2 - centre.2; let dz = key.2 - centre.2;
if dx.abs() > r || dy.abs() > r || dz.abs() > r { return None; } if dx.abs() > r || dy.abs() > r || dz.abs() > r {
if spawned.0.contains_key(key) { return None; } return None;
Some((*key, dx*dx + dy*dy + dz*dz)) }
if spawned.0.contains_key(key) {
return None;
}
Some((*key, dx * dx + dy * dy + dz * dz))
}) })
.collect(); .collect();
@ -51,15 +58,19 @@ pub fn enqueue_visible_chunks(
/// move a limited number of keys from the queue into the octrees dirty set /// move a limited number of keys from the queue into the octrees dirty set
pub fn process_chunk_queue( pub fn process_chunk_queue(
mut queue : ResMut<ChunkQueue>, mut queue: ResMut<ChunkQueue>,
budget : Res<ChunkBudget>, budget: Res<ChunkBudget>,
mut tree_q : Query<&mut SparseVoxelOctree>, mut tree_q: Query<&mut SparseVoxelOctree>,
) { ) {
let Ok(mut tree) = tree_q.get_single_mut() else { return }; let Ok(mut tree) = tree_q.get_single_mut() else {
return;
};
for _ in 0..budget.per_frame { for _ in 0..budget.per_frame {
if let Some(key) = queue.keys.pop_front() { if let Some(key) = queue.keys.pop_front() {
queue.set.remove(&key); queue.set.remove(&key);
tree.dirty_chunks.insert(key); tree.dirty_chunks.insert(key);
} else { break; } } else {
break;
}
} }
} }

View File

@ -1,12 +1,13 @@
use crate::plugins::big_space::big_space_plugin::RootGrid; use crate::plugins::big_space::big_space_plugin::RootGrid;
use crate::plugins::environment::systems::voxels::atlas::VoxelTextureAtlas;
use crate::plugins::environment::systems::voxels::meshing::mesh_chunk; use crate::plugins::environment::systems::voxels::meshing::mesh_chunk;
use crate::plugins::environment::systems::voxels::structure::*; use crate::plugins::environment::systems::voxels::structure::*;
use crate::plugins::environment::systems::voxels::atlas::VoxelTextureAtlas;
use bevy::pbr::wireframe::Wireframe; use bevy::pbr::wireframe::Wireframe;
use bevy::prelude::*; use bevy::prelude::*;
use bevy::render::mesh::Mesh; use bevy::render::mesh::Mesh;
use big_space::prelude::GridCell; use big_space::prelude::GridCell;
use itertools::Itertools; use itertools::Itertools;
use rayon::prelude::*;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::format; use std::fmt::format;
@ -41,38 +42,46 @@ pub fn rebuild_dirty_chunks(
} }
//------------------------------------------------ collect voxel data //------------------------------------------------ collect voxel data
let mut bufs = Vec::new(); let tree_ref = &*tree;
for key in tree.dirty_chunks.iter().copied() { let bufs: Vec<_> = tree
let lod = existing.get(&key).map(|v| v.3).unwrap_or(0); .dirty_chunks
let mut buf = [[[None; CHUNK_SIZE as usize]; CHUNK_SIZE as usize]; CHUNK_SIZE as usize]; .par_iter()
.copied()
.map(|key| {
let lod = existing.get(&key).map(|v| v.3).unwrap_or(0);
let mut buf =
[[[None; CHUNK_SIZE as usize]; CHUNK_SIZE as usize]; CHUNK_SIZE as usize];
let half = tree.size * 0.5; let half = tree_ref.size * 0.5;
let step = tree.get_spacing_at_depth(tree.max_depth); let step = tree_ref.get_spacing_at_depth(tree_ref.max_depth);
let origin = Vec3::new( let origin = Vec3::new(
key.0 as f32 * CHUNK_SIZE as f32 * step - half, key.0 as f32 * CHUNK_SIZE as f32 * step - half,
key.1 as f32 * CHUNK_SIZE as f32 * step - half, key.1 as f32 * CHUNK_SIZE as f32 * step - half,
key.2 as f32 * CHUNK_SIZE as f32 * step - half, key.2 as f32 * CHUNK_SIZE as f32 * step - half,
); );
let mult = 1 << lod; let mult = 1 << lod;
for gx in (0..CHUNK_SIZE).step_by(mult as usize) { for gx in (0..CHUNK_SIZE).step_by(mult as usize) {
for gy in (0..CHUNK_SIZE).step_by(mult as usize) { for gy in (0..CHUNK_SIZE).step_by(mult as usize) {
for gz in (0..CHUNK_SIZE).step_by(mult as usize) { for gz in (0..CHUNK_SIZE).step_by(mult as usize) {
let center = origin let center = origin
+ Vec3::new( + Vec3::new(
(gx + mult / 2) as f32 * step, (gx + mult / 2) as f32 * step,
(gy + mult / 2) as f32 * step, (gy + mult / 2) as f32 * step,
(gz + mult / 2) as f32 * step, (gz + mult / 2) as f32 * step,
); );
if let Some(v) = tree.get_voxel_at_world_coords(center) { if let Some(v) = tree_ref.get_voxel_at_world_coords(center) {
for lx in 0..mult { for lx in 0..mult {
for ly in 0..mult { for ly in 0..mult {
for lz in 0..mult { for lz in 0..mult {
let ix = gx + lx; let ix = gx + lx;
let iy = gy + ly; let iy = gy + ly;
let iz = gz + lz; let iz = gz + lz;
if ix < CHUNK_SIZE && iy < CHUNK_SIZE && iz < CHUNK_SIZE { if ix < CHUNK_SIZE && iy < CHUNK_SIZE && iz < CHUNK_SIZE
buf[ix as usize][iy as usize][iz as usize] = Some(*v); {
buf[ix as usize][iy as usize][iz as usize] =
Some(*v);
}
} }
} }
} }
@ -80,10 +89,10 @@ pub fn rebuild_dirty_chunks(
} }
} }
} }
}
bufs.push((key, buf, origin, step, lod)); (key, buf, origin, step, lod)
} })
.collect();
//------------------------------------------------ create / update //------------------------------------------------ create / update
for (key, buf, origin, step, lod) in bufs { for (key, buf, origin, step, lod) in bufs {