diff --git a/src/voxel/brickmap.rs b/src/voxel/brickmap.rs index 9fc8dbc..19c6977 100644 --- a/src/voxel/brickmap.rs +++ b/src/voxel/brickmap.rs @@ -295,3 +295,137 @@ impl BrickmapManager { ); } } + +#[derive(Debug)] +struct ShadingBucket { + global_offset: u32, + slot_count: u32, + slot_size: u32, + free: Vec, + used: Vec, +} + +impl ShadingBucket { + pub fn new(global_offset: u32, slot_count: u32, slot_size: u32) -> Self { + let mut free = Vec::with_capacity(slot_count as usize); + for i in (0..slot_count).rev() { + free.push(i); + } + + let used = Vec::with_capacity(slot_count as usize); + Self { + global_offset, + slot_count, + slot_size, + free, + used, + } + } + + pub fn contains_address(&self, address: u32) -> bool { + let min = self.global_offset; + let max = min + self.slot_count * self.slot_size; + return min <= address && address < max; + } + + pub fn try_alloc(&mut self) -> Option { + // Mark the first free index as used + let bucket_index = self.free.pop()?; + self.used.push(bucket_index); + + // Convert the bucket index into a global address + let address = self.global_offset + bucket_index * self.slot_size; + return Some(address); + } + + pub fn try_dealloc(&mut self, address: u32) -> Result<(), &str> { + if !self.contains_address(address) { + return Err("Address is not within bucket range."); + } + + let local_address = address - self.global_offset; + if local_address % self.slot_size != 0 { + return Err("Address is not aligned to bucket element size."); + } + + let bucket_index = local_address / self.slot_size; + if !self.used.contains(&bucket_index) { + return Err("Address is not currently allocated."); + } + + // All the potential errors are out of the way, time to actually deallocate + let position = self.used.iter().position(|x| *x == bucket_index).unwrap(); + self.used.swap_remove(position); + self.free.push(bucket_index); + Ok(()) + } +} + +#[derive(Debug)] +struct ShadingTableAllocator { + buckets: Vec, + bucket_count: u32, + elements_per_bucket: u32, + total_elements: u32, + used_elements: u32, +} + +impl ShadingTableAllocator { + pub fn new(bucket_count: u32, elements_per_bucket: u32) -> Self { + let total_elements = bucket_count * elements_per_bucket; + let used_elements = 0; + + // Build the buckets. Ordered in ascending size + let mut buckets = Vec::with_capacity(bucket_count as usize); + for i in (0..bucket_count).rev() { + let global_offset = i * elements_per_bucket; + let slot_size = u32::pow(2, 9 - i); + let slot_count = elements_per_bucket / slot_size; + log::info!( + "Creating bucket: offset({}), slot_size({}), slot_count({})", + global_offset, + slot_size, + slot_count + ); + buckets.push(ShadingBucket::new(global_offset, slot_count, slot_size)); + } + + Self { + buckets, + bucket_count, + elements_per_bucket, + total_elements, + used_elements, + } + } + + pub fn try_alloc(&mut self, size: u32) -> Option { + for i in 0..self.bucket_count as usize { + let bucket = &mut self.buckets[i]; + if bucket.slot_size < size { + continue; + } + + let idx = bucket.try_alloc(); + if idx.is_some() { + self.used_elements += bucket.slot_size; + log::info!( + "Allocated to shader table at {}. {}/{}", + idx.unwrap(), + self.used_elements, + self.total_elements + ); + return idx; + } + } + + None + } + + pub fn try_dealloc(&mut self, address: u32) -> Result<(), &str> { + let bucket_idx = address / self.elements_per_bucket; + let bucket = &mut self.buckets[bucket_idx as usize]; + self.used_elements -= bucket.slot_size; + bucket.try_dealloc(address) + } +}