From 1823d8b52ce091cbdf725936481f69b5c1de1b91 Mon Sep 17 00:00:00 2001 From: Jarrod Doyle Date: Tue, 25 Apr 2023 12:23:55 +0100 Subject: [PATCH] Add initial multi-brickmap support --- assets/shaders/voxel_volume.wgsl | 48 ++++++++++++---- src/voxel/brickmap.rs | 97 +++++++++++++++++++++----------- src/voxel/voxel_renderer.rs | 55 +++++++++++------- 3 files changed, 136 insertions(+), 64 deletions(-) diff --git a/assets/shaders/voxel_volume.wgsl b/assets/shaders/voxel_volume.wgsl index 60dc58c..2751f47 100644 --- a/assets/shaders/voxel_volume.wgsl +++ b/assets/shaders/voxel_volume.wgsl @@ -1,7 +1,8 @@ @group(0) @binding(0) var output: texture_storage_2d; -@group(0) @binding(1) var brickmap: Brickmap; -@group(0) @binding(2) var shading_table: array; -@group(0) @binding(3) var camera: Camera; +@group(0) @binding(1) var world_state: WorldState; +@group(0) @binding(2) var brickmap_cache: array; +@group(0) @binding(3) var shading_table: array; +@group(0) @binding(4) var camera: Camera; struct ShadingElement { albedo: u32, @@ -20,6 +21,11 @@ struct Camera { _pad: f32, }; +struct WorldState { + brickmap_cache_dims: vec3, + _pad: u32, +}; + struct HitInfo { hit: bool, hit_pos: vec3, @@ -32,21 +38,37 @@ struct AabbHitInfo { }; fn get_shading_offset(p: vec3) -> u32 { - let local_index = u32(p.x + p.y * 8 + p.z * 64); + // return 0u; + + // What brickmap are we in? + let brickmap_index = (p.x / 8) + (p.y / 8) * 32 + (p.z / 8) * 1024; + let local_index = u32((p.x % 8) + (p.y % 8) * 8 + (p.z % 8) * 64); + let brickmap = &brickmap_cache[brickmap_index]; let bitmask_index = local_index / 32u; var map_voxel_idx = 0u; for (var i: i32 = 0; i < i32(bitmask_index); i++) { - map_voxel_idx += countOneBits(brickmap.bitmask[i]); + map_voxel_idx += countOneBits((*brickmap).bitmask[i]); } - let extracted_bits = extractBits(brickmap.bitmask[bitmask_index], 0u, (local_index % 32u)); + let extracted_bits = extractBits((*brickmap).bitmask[bitmask_index], 0u, (local_index % 32u)); map_voxel_idx += countOneBits(extracted_bits); - return brickmap.shading_table_offset + map_voxel_idx; + return (*brickmap).shading_table_offset + map_voxel_idx; + + + // let local_index = u32(p.x + p.y * 8 + p.z * 64); + // let bitmask_index = local_index / 32u; + // var map_voxel_idx = 0u; + // for (var i: i32 = 0; i < i32(bitmask_index); i++) { + // map_voxel_idx += countOneBits(brickmap.bitmask[i]); + // } + // let extracted_bits = extractBits(brickmap.bitmask[bitmask_index], 0u, (local_index % 32u)); + // map_voxel_idx += countOneBits(extracted_bits); + // return brickmap.shading_table_offset + map_voxel_idx; } fn ray_intersect_aabb(ray_pos: vec3, ray_dir: vec3) -> AabbHitInfo { let ray_dir_inv = 1.0 / ray_dir; let t1 = (vec3(0.0) - ray_pos) * ray_dir_inv; - let t2 = (vec3(8.0) - ray_pos) * ray_dir_inv; + let t2 = ((vec3(8.0) * vec3(world_state.brickmap_cache_dims)) - ray_pos) * ray_dir_inv; let t_min = min(t1, t2); let t_max = max(t1, t2); let tmin = max(max(t_min.x, 0.0), max(t_min.y, t_min.z)); @@ -55,13 +77,15 @@ fn ray_intersect_aabb(ray_pos: vec3, ray_dir: vec3) -> AabbHitInfo { } fn point_inside_aabb(p: vec3) -> bool { - let clamped = clamp(p, vec3(0), vec3(8) - vec3(1)); + let max = vec3(world_state.brickmap_cache_dims) * 8; + let clamped = clamp(p, vec3(0), max - vec3(1)); return clamped.x == p.x && clamped.y == p.y && clamped.z == p.z; } fn voxel_hit(p: vec3) -> bool { - let local_index = u32(p.x + p.y * 8 + p.z * 64); - return (brickmap.bitmask[local_index / 32u] >> (local_index % 32u) & 1u) != 0u; + let brickmap_index = (p.x / 8) + (p.y / 8) * 32 + (p.z / 8) * 1024; + let local_index = u32((p.x % 8) + (p.y % 8) * 8 + (p.z % 8) * 64); + return (brickmap_cache[brickmap_index].bitmask[local_index / 32u] >> (local_index % 32u) & 1u) != 0u; } fn cast_ray(orig_ray_pos: vec3, ray_dir: vec3) -> HitInfo { @@ -84,7 +108,7 @@ fn cast_ray(orig_ray_pos: vec3, ray_dir: vec3) -> HitInfo { var side_dist = (sign(ray_dir) * (vec3(map_pos) - ray_pos) + (sign(ray_dir) * 0.5) + 0.5) * delta_dist; // TODO: don't hardcode max ray depth - for (var i: i32 = 0; i < 64; i++) { + for (var i: i32 = 0; i < 2048; i++) { if (side_dist.x < side_dist.y) { if (side_dist.x < side_dist.z) { side_dist.x += delta_dist.x; diff --git a/src/voxel/brickmap.rs b/src/voxel/brickmap.rs index d391110..94f7e08 100644 --- a/src/voxel/brickmap.rs +++ b/src/voxel/brickmap.rs @@ -1,18 +1,16 @@ -use std::mem::size_of; - use wgpu::util::DeviceExt; use crate::render; #[repr(C)] -#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] -pub struct BrickmapUniform { +#[derive(Debug, Default, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +struct Brickmap { pub bitmask: [u32; 16], pub shading_table_offset: u32, pub lod_color: u32, } -impl BrickmapUniform { +impl Brickmap { pub fn new() -> Self { Self { bitmask: [0; 16], @@ -22,53 +20,84 @@ impl BrickmapUniform { } } +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +struct WorldState { + brickmap_cache_dims: [u32; 3], + _pad: u32, +} + #[derive(Debug)] pub struct BrickmapManager { - uniform: BrickmapUniform, - buffer: wgpu::Buffer, + state_uniform: WorldState, + state_buffer: wgpu::Buffer, + brickmap_cache: Vec, + brickmap_buffer: wgpu::Buffer, shading_table: Vec, shading_table_buffer: wgpu::Buffer, } impl BrickmapManager { pub fn new(context: &render::Context) -> Self { - let uniform = BrickmapUniform::new(); - let buffer = context - .device - .create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: None, - contents: bytemuck::cast_slice(&[uniform]), - usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, - }); + let mut state_uniform = WorldState::default(); + state_uniform.brickmap_cache_dims = [32, 32, 32]; - let shading_table_buffer = - context - .device - .create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: None, - contents: &[0; 25000000], - usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, - }); + let mut brickmap_cache = Vec::::with_capacity(32768); + brickmap_cache.resize(32768, Brickmap::default()); - let shading_table = Vec::::new(); + let device = &context.device; + let state_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice(&[state_uniform]), + usage: wgpu::BufferUsages::UNIFORM, + }); + + let brickmap_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice(&brickmap_cache), + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + }); + + let shading_table_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice(&[0u32; 25000000]), + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + }); + + let mut shading_table = Vec::::with_capacity(25000000); + shading_table.resize(shading_table.capacity(), 0); Self { - uniform, - buffer, + state_uniform, + state_buffer, + brickmap_cache, + brickmap_buffer, shading_table, shading_table_buffer, } } // TODO: Ideally this should take a generic voxel format and do the data mapping here - pub fn set_data(&mut self, data: &[u32; 16], colours: &[u32]) { - self.uniform.bitmask = *data; - self.shading_table = colours.to_vec(); + pub fn set_data(&mut self, chunk_pos: glam::UVec3, data: &[u32; 16], colours: &[u32]) { + let idx: usize = (chunk_pos.x + chunk_pos.y * 32 + chunk_pos.z * 1024) + .try_into() + .unwrap(); + let shading_idx = idx * 512; + self.brickmap_cache[idx].bitmask = *data; + self.brickmap_cache[idx].shading_table_offset = shading_idx as u32; + self.shading_table.splice( + shading_idx..(shading_idx + colours.len()), + colours.to_owned(), + ); } pub fn update_buffer(&self, context: &render::Context) { let queue = &context.queue; - queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(&[self.uniform])); + queue.write_buffer( + &self.brickmap_buffer, + 0, + bytemuck::cast_slice(&self.brickmap_cache), + ); queue.write_buffer( &self.shading_table_buffer, 0, @@ -76,8 +105,12 @@ impl BrickmapManager { ); } - pub fn get_buffer(&self) -> &wgpu::Buffer { - &self.buffer + pub fn get_worldstate_buffer(&self) -> &wgpu::Buffer { + &self.state_buffer + } + + pub fn get_brickmap_buffer(&self) -> &wgpu::Buffer { + &self.brickmap_buffer } pub fn get_shading_buffer(&self) -> &wgpu::Buffer { diff --git a/src/voxel/voxel_renderer.rs b/src/voxel/voxel_renderer.rs index ab4e9db..1e704d9 100644 --- a/src/voxel/voxel_renderer.rs +++ b/src/voxel/voxel_renderer.rs @@ -61,29 +61,34 @@ impl VoxelRenderer { log::info!("Creating brickmap manager..."); let mut brickmap_manager = super::brickmap::BrickmapManager::new(context); - let mut bitmask_data = [0xFFFFFFFF as u32; 16]; - let mut albedo_data = Vec::::new(); - for z in 0..8 { - let mut entry = 0u64; - for y in 0..8 { - for x in 0..8 { - let idx = x + y * 8; - let pos = glam::vec3(x as f32, y as f32, z as f32) - glam::vec3(3.5, 3.5, 3.5); - if pos.length_squared() <= (u32::pow(4, 2) as f32) { - entry += 1 << idx; - let mut albedo = 0u32; - albedo += ((x + 1) * 32 - 1) << 24; - albedo += ((y + 1) * 32 - 1) << 16; - albedo += ((z + 1) * 32 - 1) << 8; - albedo += 255; - albedo_data.push(albedo); + let sphere_center = glam::vec3(3.5, 3.5, 3.5); + let sphere_r2 = u32::pow(4, 2) as f32; + for chunk_idx in 0..32768 { + let chunk_pos = glam::uvec3(chunk_idx % 32, (chunk_idx / 32) % 32, chunk_idx / 1024); + let mut bitmask_data = [0xFFFFFFFF as u32; 16]; + let mut albedo_data = Vec::::new(); + for z in 0..8 { + let mut entry = 0u64; + for y in 0..8 { + for x in 0..8 { + let idx = x + y * 8; + let pos = glam::vec3(x as f32, y as f32, z as f32); + if (pos - sphere_center).length_squared() <= sphere_r2 { + entry += 1 << idx; + let mut albedo = 0u32; + albedo += ((chunk_pos.x + 1) * 8 - 1) << 24; + albedo += ((chunk_pos.y + 1) * 8 - 1) << 16; + albedo += ((chunk_pos.z + 1) * 8 - 1) << 8; + albedo += 255; + albedo_data.push(albedo); + } } } + bitmask_data[2 * z as usize] = (entry & 0xFFFFFFFF).try_into().unwrap(); + bitmask_data[2 * z as usize + 1] = ((entry >> 32) & 0xFFFFFFFF).try_into().unwrap(); } - bitmask_data[2 * z as usize] = (entry & 0xFFFFFFFF).try_into().unwrap(); - bitmask_data[2 * z as usize + 1] = ((entry >> 32) & 0xFFFFFFFF).try_into().unwrap(); + brickmap_manager.set_data(chunk_pos, &bitmask_data, &albedo_data); } - brickmap_manager.set_data(&bitmask_data, &albedo_data); brickmap_manager.update_buffer(context); log::info!("Creating compute pipeline..."); @@ -99,6 +104,15 @@ impl VoxelRenderer { }, None, ) + .with_entry( + wgpu::ShaderStages::COMPUTE, + wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + None, + ) .with_entry( wgpu::ShaderStages::COMPUTE, wgpu::BindingType::Buffer { @@ -130,7 +144,8 @@ impl VoxelRenderer { let compute_bind_group = render::BindGroupBuilder::new() .with_layout(&compute_layout) .with_entry(wgpu::BindingResource::TextureView(&render_texture.view)) - .with_entry(brickmap_manager.get_buffer().as_entire_binding()) + .with_entry(brickmap_manager.get_worldstate_buffer().as_entire_binding()) + .with_entry(brickmap_manager.get_brickmap_buffer().as_entire_binding()) .with_entry(brickmap_manager.get_shading_buffer().as_entire_binding()) .with_entry(camera_controller.get_buffer().as_entire_binding()) .build(context);