Make brickmap cache cyclic, unloading loaded maps when necessary. Fix crash when cache is full

This commit is contained in:
Jarrod Doyle 2023-06-29 15:27:36 +01:00
parent 5ccebf89cb
commit bb97a5036e
Signed by: Jayrude
GPG Key ID: 38B57B16E7C0ADF7
1 changed files with 59 additions and 15 deletions

View File

@ -17,13 +17,19 @@ struct WorldState {
_pad: u32, _pad: u32,
} }
#[derive(Debug, Default, Copy, Clone)]
struct BrickmapCacheEntry {
grid_idx: usize,
shading_table_offset: u32,
}
#[derive(Debug)] #[derive(Debug)]
pub struct BrickmapManager { pub struct BrickmapManager {
state_uniform: WorldState, state_uniform: WorldState,
state_buffer: wgpu::Buffer, state_buffer: wgpu::Buffer,
brickgrid: Vec<u32>, brickgrid: Vec<u32>,
brickgrid_buffer: wgpu::Buffer, brickgrid_buffer: wgpu::Buffer,
brickmap_cache: Vec<Brickmap>, brickmap_cache_map: Vec<Option<BrickmapCacheEntry>>,
brickmap_cache_idx: usize, brickmap_cache_idx: usize,
brickmap_buffer: wgpu::Buffer, brickmap_buffer: wgpu::Buffer,
shading_table_buffer: wgpu::Buffer, shading_table_buffer: wgpu::Buffer,
@ -34,32 +40,31 @@ pub struct BrickmapManager {
// TODO: // TODO:
// - GPU side unpack buffer rather than uploading each changed brickmap part // - GPU side unpack buffer rather than uploading each changed brickmap part
// - Cyclic brickmap cache with unloading
// - Brickworld system // - Brickworld system
impl BrickmapManager { impl BrickmapManager {
pub fn new(context: &render::Context, brickgrid_dims: glam::UVec3) -> Self { pub fn new(context: &render::Context, brickgrid_dims: glam::UVec3) -> Self {
let device = &context.device;
let state_uniform = WorldState { let state_uniform = WorldState {
brickgrid_dims: [brickgrid_dims.x, brickgrid_dims.y, brickgrid_dims.z], brickgrid_dims: [brickgrid_dims.x, brickgrid_dims.y, brickgrid_dims.z],
..Default::default() ..Default::default()
}; };
let brickmap_cache = vec![Brickmap::default(); usize::pow(32, 3)];
let brickgrid =
vec![1u32; (brickgrid_dims.x * brickgrid_dims.y * brickgrid_dims.z) as usize];
let device = &context.device;
let state_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { let state_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: None, label: None,
contents: bytemuck::cast_slice(&[state_uniform]), contents: bytemuck::cast_slice(&[state_uniform]),
usage: wgpu::BufferUsages::UNIFORM, usage: wgpu::BufferUsages::UNIFORM,
}); });
let brickgrid =
vec![1u32; (brickgrid_dims.x * brickgrid_dims.y * brickgrid_dims.z) as usize];
let brickgrid_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { let brickgrid_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: None, label: None,
contents: bytemuck::cast_slice(&brickgrid), contents: bytemuck::cast_slice(&brickgrid),
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
}); });
let brickmap_cache = vec![Brickmap::default(); usize::pow(32, 3)];
let brickmap_cache_map = vec![None; brickmap_cache.capacity()];
let brickmap_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { let brickmap_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: None, label: None,
contents: bytemuck::cast_slice(&brickmap_cache), contents: bytemuck::cast_slice(&brickmap_cache),
@ -95,7 +100,7 @@ impl BrickmapManager {
state_buffer, state_buffer,
brickgrid, brickgrid,
brickgrid_buffer, brickgrid_buffer,
brickmap_cache, brickmap_cache_map,
brickmap_cache_idx: 0, brickmap_cache_idx: 0,
brickmap_buffer, brickmap_buffer,
shading_table_buffer, shading_table_buffer,
@ -247,14 +252,31 @@ impl BrickmapManager {
); );
// Update the brickmap // Update the brickmap
self.brickmap_cache[self.brickmap_cache_idx].bitmask = bitmask_data; let brickmap = Brickmap {
self.brickmap_cache[self.brickmap_cache_idx].shading_table_offset = shading_idx as u32; bitmask: bitmask_data,
shading_table_offset: shading_idx as u32,
lod_color: 0,
};
// If there's already something in the cache spot we want to write to, we
// need to unload it.
if self.brickmap_cache_map[self.brickmap_cache_idx].is_some() {
let entry = self.brickmap_cache_map[self.brickmap_cache_idx].unwrap();
self.update_brickgrid_element(context, entry.grid_idx, 1);
}
// We're all good to overwrite the cache map entry now :)
self.brickmap_cache_map[self.brickmap_cache_idx] = Some(BrickmapCacheEntry {
grid_idx: chunk_idx,
shading_table_offset: shading_idx as u32,
});
context.queue.write_buffer( context.queue.write_buffer(
&self.brickmap_buffer, &self.brickmap_buffer,
(72 * self.brickmap_cache_idx) as u64, (72 * self.brickmap_cache_idx) as u64,
bytemuck::cast_slice(&[self.brickmap_cache[self.brickmap_cache_idx]]), bytemuck::cast_slice(&[brickmap]),
); );
self.brickmap_cache_idx += 1; self.brickmap_cache_idx = (self.brickmap_cache_idx + 1) % self.brickmap_cache_map.len();
} }
// Reset the request count on the gpu buffer // Reset the request count on the gpu buffer
@ -265,11 +287,33 @@ impl BrickmapManager {
} }
fn update_brickgrid_element(&mut self, context: &render::Context, index: usize, data: u32) { fn update_brickgrid_element(&mut self, context: &render::Context, index: usize, data: u32) {
self.brickgrid.splice(index..index + 1, [data]); // If we're updating a brickgrid element, we need to make sure to deallocate anything
// that's already there. The shading table gets deallocated, and the brickmap cache entry
// is marked as None.
if (self.brickgrid[index] & 0xF) == 4 {
let brickmap_idx = (self.brickgrid[index] >> 8) as usize;
let cache_map_entry = self.brickmap_cache_map[brickmap_idx];
match cache_map_entry {
Some(entry) => {
match self
.shading_table_allocator
.try_dealloc(entry.shading_table_offset)
{
Ok(_) => (),
Err(e) => log::warn!("{}", e),
}
self.brickmap_cache_map[brickmap_idx] = None;
}
None => log::warn!("Expected brickmap cache entry, found None!"),
}
}
// We're safe to overwrite the CPU brickgrid and upload to gpu now
self.brickgrid[index] = data;
context.queue.write_buffer( context.queue.write_buffer(
&self.brickgrid_buffer, &self.brickgrid_buffer,
(index * 4).try_into().unwrap(), (index * 4).try_into().unwrap(),
bytemuck::cast_slice(&[self.brickgrid[index]]), bytemuck::cast_slice(&[data]),
); );
} }
} }