237 lines
8.1 KiB
Rust
237 lines
8.1 KiB
Rust
use crate::{
|
|
gfx::{self, BufferExt},
|
|
math,
|
|
voxel::world::WorldManager,
|
|
};
|
|
|
|
use super::{
|
|
brickgrid::{Brickgrid, BrickgridElement, BrickgridFlag},
|
|
brickmap_cache::{BrickmapCache, BrickmapCacheEntry},
|
|
shading_table::ShadingTableAllocator,
|
|
};
|
|
|
|
#[repr(C)]
|
|
#[derive(Debug, Default, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
|
struct WorldState {
|
|
brickgrid_dims: [u32; 3],
|
|
_pad: u32,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct BrickmapManager {
|
|
state_uniform: WorldState,
|
|
state_buffer: wgpu::Buffer,
|
|
brickgrid: Brickgrid,
|
|
brickmap_cache: BrickmapCache,
|
|
shading_table_buffer: wgpu::Buffer,
|
|
shading_table_allocator: ShadingTableAllocator,
|
|
feedback_buffer: wgpu::Buffer,
|
|
feedback_result_buffer: wgpu::Buffer,
|
|
unpack_max_count: usize,
|
|
}
|
|
|
|
// TODO:
|
|
// - Brickworld system
|
|
impl BrickmapManager {
|
|
pub fn new(
|
|
context: &gfx::Context,
|
|
brickgrid_dims: glam::UVec3,
|
|
brickmap_cache_size: usize,
|
|
shading_table_bucket_size: u32,
|
|
max_requested_brickmaps: u32,
|
|
max_uploaded_brickmaps: u32,
|
|
) -> Self {
|
|
let state_uniform = WorldState {
|
|
brickgrid_dims: [brickgrid_dims.x, brickgrid_dims.y, brickgrid_dims.z],
|
|
..Default::default()
|
|
};
|
|
|
|
let brickgrid = Brickgrid::new(context, brickgrid_dims, max_uploaded_brickmaps as usize);
|
|
let brickmap_cache = BrickmapCache::new(
|
|
context,
|
|
brickmap_cache_size,
|
|
max_uploaded_brickmaps as usize,
|
|
);
|
|
|
|
let shading_table_allocator = ShadingTableAllocator::new(4, shading_table_bucket_size);
|
|
let shading_table = vec![0u32; shading_table_allocator.total_elements as usize];
|
|
|
|
let mut feedback_data = vec![0u32; 4 + 4 * max_requested_brickmaps as usize];
|
|
feedback_data[0] = max_requested_brickmaps;
|
|
let feedback_data_u8 = bytemuck::cast_slice(&feedback_data);
|
|
|
|
let mut brickmap_upload_data = vec![0u32; 4 + 532 * max_uploaded_brickmaps as usize];
|
|
brickmap_upload_data[0] = max_uploaded_brickmaps;
|
|
|
|
let mut buffers = gfx::BulkBufferBuilder::new()
|
|
.with_init_buffer_bm("Brick World State", &[state_uniform])
|
|
.set_usage(wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST)
|
|
.with_init_buffer_bm("Shading Table", &shading_table)
|
|
.set_usage(
|
|
wgpu::BufferUsages::STORAGE
|
|
| wgpu::BufferUsages::COPY_DST
|
|
| wgpu::BufferUsages::COPY_SRC,
|
|
)
|
|
.with_init_buffer("Feedback", feedback_data_u8)
|
|
.set_usage(wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ)
|
|
.with_buffer("Feedback Read", feedback_data_u8.len() as u64, false)
|
|
.build(context);
|
|
|
|
Self {
|
|
state_uniform,
|
|
brickgrid,
|
|
brickmap_cache,
|
|
shading_table_allocator,
|
|
unpack_max_count: max_uploaded_brickmaps as usize,
|
|
|
|
state_buffer: buffers.remove(0),
|
|
shading_table_buffer: buffers.remove(0),
|
|
feedback_buffer: buffers.remove(0),
|
|
feedback_result_buffer: buffers.remove(0),
|
|
}
|
|
}
|
|
|
|
pub fn get_brickgrid_buffer(&self) -> &wgpu::Buffer {
|
|
self.brickgrid.get_buffer()
|
|
}
|
|
|
|
pub fn get_worldstate_buffer(&self) -> &wgpu::Buffer {
|
|
&self.state_buffer
|
|
}
|
|
|
|
pub fn get_brickmap_buffer(&self) -> &wgpu::Buffer {
|
|
self.brickmap_cache.get_buffer()
|
|
}
|
|
|
|
pub fn get_shading_buffer(&self) -> &wgpu::Buffer {
|
|
&self.shading_table_buffer
|
|
}
|
|
|
|
pub fn get_feedback_buffer(&self) -> &wgpu::Buffer {
|
|
&self.feedback_buffer
|
|
}
|
|
|
|
pub fn get_feedback_result_buffer(&self) -> &wgpu::Buffer {
|
|
&self.feedback_result_buffer
|
|
}
|
|
|
|
pub fn get_brickmap_unpack_buffer(&self) -> &wgpu::Buffer {
|
|
self.brickmap_cache.get_upload_buffer()
|
|
}
|
|
|
|
pub fn get_brickgrid_unpack_buffer(&self) -> &wgpu::Buffer {
|
|
self.brickgrid.get_upload_buffer()
|
|
}
|
|
|
|
pub fn get_unpack_max_count(&self) -> usize {
|
|
self.unpack_max_count
|
|
}
|
|
|
|
pub fn process_feedback_buffer(&mut self, context: &gfx::Context, world: &mut WorldManager) {
|
|
let data: Vec<u32> = self.feedback_result_buffer.get_mapped_range(context, 0..16);
|
|
let request_count = data[1] as usize;
|
|
|
|
if request_count > 0 {
|
|
// Reset the request count for next frame
|
|
context
|
|
.queue
|
|
.write_buffer(&self.feedback_buffer, 4, &[0, 0, 0, 0]);
|
|
|
|
let range = 16..(16 + 16 * request_count as u64);
|
|
let data = self.feedback_result_buffer.get_mapped_range(context, range);
|
|
for i in 0..request_count {
|
|
let request_data = &data[(i * 4)..(i * 4 + 3)];
|
|
self.handle_request(world, request_data);
|
|
}
|
|
}
|
|
|
|
// TODO: Why do we call this here rather than doing it outside of here?
|
|
self.upload_unpack_buffers(context);
|
|
|
|
log::info!("Num loaded brickmaps: {}", self.brickmap_cache.num_loaded);
|
|
}
|
|
|
|
fn handle_request(&mut self, world: &mut WorldManager, data: &[u32]) {
|
|
let grid_dims = self.state_uniform.brickgrid_dims;
|
|
|
|
// Extract brickgrid position of the requested brickmap
|
|
let grid_pos = glam::uvec3(data[0], data[1], data[2]);
|
|
let grid_idx = math::to_1d_index(
|
|
grid_pos,
|
|
glam::uvec3(grid_dims[0], grid_dims[1], grid_dims[2]),
|
|
);
|
|
|
|
// We only want to upload voxels that are on the surface, so we cull anything
|
|
// that is surrounded by solid voxels
|
|
let grid_pos = grid_pos.as_ivec3();
|
|
let (bitmask_data, albedo_data) = super::util::cull_interior_voxels(world, grid_pos);
|
|
|
|
// If there's no voxel colour data post-culling it means the brickmap is
|
|
// empty. We don't need to upload it, just mark the relevant brickgrid entry.
|
|
if albedo_data.is_empty() {
|
|
if let Some(entry) = self.update_brickgrid_element(grid_idx, 0) {
|
|
// TODO: We need to actually remove the cache entry lmao
|
|
// The brickgrid element had a brickmap entry so we need to unload it's
|
|
// shading data
|
|
if let Err(e) = self
|
|
.shading_table_allocator
|
|
.try_dealloc(entry.shading_table_offset)
|
|
{
|
|
log::warn!("{}", e)
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Update the shading table
|
|
let shading_idx = self
|
|
.shading_table_allocator
|
|
.try_alloc(albedo_data.len() as u32)
|
|
.unwrap() as usize;
|
|
|
|
if let Some(entry) =
|
|
self.brickmap_cache
|
|
.add_entry(grid_idx, shading_idx as u32, bitmask_data, albedo_data)
|
|
{
|
|
self.update_brickgrid_element(entry.grid_idx, 1);
|
|
}
|
|
|
|
// Update the brickgrid index
|
|
if let Some(old_entry) = self.update_brickgrid_element(
|
|
grid_idx,
|
|
super::util::to_brickgrid_element(
|
|
self.brickmap_cache.index as u32,
|
|
BrickgridFlag::Loaded,
|
|
),
|
|
) {
|
|
// TODO: We need to actually remove the cache entry lmao
|
|
// The brickgrid element had a brickmap entry so we need to unload it's
|
|
// shading data
|
|
if let Err(e) = self
|
|
.shading_table_allocator
|
|
.try_dealloc(old_entry.shading_table_offset)
|
|
{
|
|
log::warn!("{}", e)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn update_brickgrid_element(&mut self, index: usize, data: u32) -> Option<BrickmapCacheEntry> {
|
|
let mut brickmap_cache_entry = None;
|
|
let current = self.brickgrid.get(index);
|
|
if current.get_flag() == BrickgridFlag::Loaded {
|
|
let cache_index = current.get_pointer();
|
|
brickmap_cache_entry = self.brickmap_cache.get_entry(cache_index);
|
|
}
|
|
|
|
// We're safe to overwrite the CPU brickgrid and mark for GPU upload now
|
|
self.brickgrid.set(index, BrickgridElement(data));
|
|
brickmap_cache_entry
|
|
}
|
|
|
|
fn upload_unpack_buffers(&mut self, context: &gfx::Context) {
|
|
self.brickgrid.upload(context);
|
|
self.brickmap_cache.upload(context);
|
|
}
|
|
}
|