diff --git a/assets/shaders/voxel_volume.wgsl b/assets/shaders/voxel_volume.wgsl index 82f7d0d..7748f1d 100644 --- a/assets/shaders/voxel_volume.wgsl +++ b/assets/shaders/voxel_volume.wgsl @@ -1,9 +1,10 @@ @group(0) @binding(0) var output: texture_storage_2d; @group(0) @binding(1) var world_state: WorldState; -@group(0) @binding(2) var brickgrid: array; +@group(0) @binding(2) var brickgrid: array>; @group(0) @binding(3) var brickmap_cache: array; @group(0) @binding(4) var shading_table: array; -@group(0) @binding(5) var camera: Camera; +@group(0) @binding(5) var cpu_feedback: Feedback; +@group(0) @binding(6) var camera: Camera; struct ShadingElement { albedo: u32, @@ -38,6 +39,26 @@ struct AabbHitInfo { distance: f32, }; +struct Feedback { + max_count: u32, + count: atomic, + _pad1: u32, + _pad2: u32, + positions: array>, +} + +// struct UnpackElement { +// pos: vec3, +// cache_idx: u32, +// map: Brickmap, +// } + +// struct Unpack { +// max_count: u32, +// count: u32, +// maps: array +// } + // Utility function. Converts a position in 3d to a 1d index. fn to_1d_index(p: vec3, dims: vec3) -> u32 { return u32(p.x + p.y * dims.x + p.z * dims.x * dims.y); @@ -190,8 +211,32 @@ fn grid_cast_ray(orig_ray_pos: vec3, ray_dir: vec3) -> HitInfo { let grid_idx = to_1d_index(map_pos, vec3(world_state.brickmap_cache_dims)); let brick_ptr = brickgrid[grid_idx]; - // If brick pointers loaded flag is set - if ((brick_ptr & 1u) == 1u) { + // Ptr = 28 bits LOD colour / brickmap index + 4 bits load flags + // Flags: + // 0 = empty + // 1 = unloaded + // 2 = loading + // 4 = loaded + let flags = brick_ptr & 0xFu; + if flags == 1u { + // Add to the load queue + if (atomicLoad(&cpu_feedback.count) < cpu_feedback.max_count) { + if ((atomicOr(&brickgrid[grid_idx], 2u) & 0x2u) == 0u) { + let index = atomicAdd(&cpu_feedback.count, 1u); + if (index < cpu_feedback.max_count) { + cpu_feedback.positions[index] = vec4(map_pos, 0); + } + else { + atomicSub(&cpu_feedback.count, 1u); + atomicXor(&brickgrid[grid_idx], 2u); + } + } + } + + // Set hit info stuff? + break; + } + else if flags == 4u { let brickmap_idx = brick_ptr >> 8u; let tmp_voxel_hit = brick_ray_cast(map_pos, brickmap_idx, orig_ray_pos, ray_dir); diff --git a/src/core/app.rs b/src/core/app.rs index 9fc7d67..b59917e 100644 --- a/src/core/app.rs +++ b/src/core/app.rs @@ -60,7 +60,7 @@ impl App { 0.25, ); - let renderer = voxel::VoxelRenderer::new(&self.render_ctx, &camera_controller); + let mut renderer = voxel::VoxelRenderer::new(&self.render_ctx, &camera_controller); let mut cumulative_dt = 0.0; let mut frames_accumulated = 0.0; @@ -86,6 +86,7 @@ impl App { camera_controller.update(dt); camera_controller.update_buffer(&self.render_ctx); renderer.render(&self.render_ctx); + renderer.update(&dt, &self.render_ctx); // Simple framerate tracking cumulative_dt += dt.as_secs_f32(); diff --git a/src/render/renderer.rs b/src/render/renderer.rs index 4f017aa..ff5363d 100644 --- a/src/render/renderer.rs +++ b/src/render/renderer.rs @@ -1,6 +1,6 @@ use std::time::Duration; pub trait Renderer { - fn update(&self, dt: &Duration, context: &super::Context); + fn update(&mut self, dt: &Duration, context: &super::Context); fn render(&self, context: &super::Context); } diff --git a/src/voxel/brickmap.rs b/src/voxel/brickmap.rs index 3982272..b5cbe94 100644 --- a/src/voxel/brickmap.rs +++ b/src/voxel/brickmap.rs @@ -1,3 +1,5 @@ +use std::future; + use wgpu::util::DeviceExt; use crate::render; @@ -27,6 +29,8 @@ pub struct BrickmapManager { brickmap_buffer: wgpu::Buffer, shading_table: Vec, shading_table_buffer: wgpu::Buffer, + feedback_buffer: wgpu::Buffer, + feedback_result_buffer: wgpu::Buffer, } impl BrickmapManager { @@ -38,7 +42,7 @@ impl BrickmapManager { brickmap_cache.resize(32768, Brickmap::default()); let mut brickgrid = Vec::::with_capacity(32768); - brickgrid.resize(brickgrid.capacity(), 0); + brickgrid.resize(brickgrid.capacity(), 1); let device = &context.device; let state_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { @@ -68,6 +72,22 @@ impl BrickmapManager { let mut shading_table = Vec::::with_capacity(25000000); shading_table.resize(shading_table.capacity(), 0); + let mut arr = [0u32; 1028]; + arr[0] = 256; + let feedback_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice(&arr), + usage: wgpu::BufferUsages::STORAGE + | wgpu::BufferUsages::COPY_DST + | wgpu::BufferUsages::COPY_SRC, + }); + let feedback_result_buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: 1028 * 4, + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, + mapped_at_creation: false, + }); + Self { state_uniform, state_buffer, @@ -77,6 +97,8 @@ impl BrickmapManager { brickmap_buffer, shading_table, shading_table_buffer, + feedback_buffer, + feedback_result_buffer, } } @@ -87,7 +109,7 @@ impl BrickmapManager { .unwrap(); let shading_idx = idx * 512; self.brickgrid - .splice(idx..idx + 1, [((idx as u32) << 8) + 1]); + .splice(idx..idx + 1, [((idx as u32) << 8) + 4]); self.brickmap_cache[idx].bitmask = *data; self.brickmap_cache[idx].shading_table_offset = shading_idx as u32; self.shading_table.splice( @@ -96,6 +118,13 @@ impl BrickmapManager { ); } + pub fn set_empty(&mut self, chunk_pos: glam::UVec3) { + let idx: usize = (chunk_pos.x + chunk_pos.y * 32 + chunk_pos.z * 1024) + .try_into() + .unwrap(); + self.brickgrid.splice(idx..idx + 1, [0]); + } + pub fn update_buffer(&self, context: &render::Context) { let queue = &context.queue; queue.write_buffer( @@ -130,4 +159,79 @@ impl BrickmapManager { 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 + } + + // TODO: this writes the entirety of every buffer. Very slow! + pub fn process_feedback_buffer(&mut self, context: &render::Context) { + // Get request count + let mut slice = self.feedback_result_buffer.slice(0..16); + slice.map_async(wgpu::MapMode::Read, |_| {}); + context.device.poll(wgpu::Maintain::Wait); + let mut data: Vec = bytemuck::cast_slice(slice.get_mapped_range().as_ref()).to_vec(); + self.feedback_result_buffer.unmap(); + + let request_count = data[1] as usize; + if request_count == 0 { + return; + } + + // Get the position data + slice = self.feedback_result_buffer.slice(16..); + slice.map_async(wgpu::MapMode::Read, |_| {}); + context.device.poll(wgpu::Maintain::Wait); + data = bytemuck::cast_slice(slice.get_mapped_range().as_ref()).to_vec(); + self.feedback_result_buffer.unmap(); + + // Generate a sphere of voxels + let sphere_center = glam::vec3(3.5, 3.5, 3.5); + let sphere_r2 = u32::pow(4, 2) as f32; + for i in 0..request_count { + let chunk_x = data[i * 4]; + let chunk_y = data[i * 4 + 1]; + let chunk_z = data[i * 4 + 2]; + + let chunk_pos = glam::uvec3(chunk_x, chunk_y, chunk_z); + let chunk_idx = chunk_x + chunk_y * 32 + chunk_z * 1024; + if chunk_idx % 3 == 0 || chunk_idx % 5 == 0 || chunk_idx % 7 == 0 { + self.set_empty(chunk_pos); + } else { + 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 += ((x + 1) * 32 - 1) << 24; + albedo += ((y + 1) * 32 - 1) << 16; + albedo += ((z + 1) * 32 - 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(); + } + self.set_data(chunk_pos, &bitmask_data, &albedo_data); + } + } + + // Reset the request count on the gpu buffer + let data = &[0, 0, 0, 0]; + context.queue.write_buffer(&self.feedback_buffer, 4, data); + self.update_buffer(context); + } } diff --git a/src/voxel/voxel_renderer.rs b/src/voxel/voxel_renderer.rs index 62b844d..58dd9f3 100644 --- a/src/voxel/voxel_renderer.rs +++ b/src/voxel/voxel_renderer.rs @@ -60,39 +60,7 @@ impl VoxelRenderer { }); log::info!("Creating brickmap manager..."); - let mut brickmap_manager = super::brickmap::BrickmapManager::new(context); - 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 { - if chunk_idx % 3 == 0 || chunk_idx % 5 == 0 || chunk_idx % 7 == 0 { - continue; - } - - 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 += ((x + 1) * 32 - 1) << 24; - albedo += ((y + 1) * 32 - 1) << 16; - albedo += ((z + 1) * 32 - 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(); - } - brickmap_manager.set_data(chunk_pos, &bitmask_data, &albedo_data); - } + let brickmap_manager = super::brickmap::BrickmapManager::new(context); brickmap_manager.update_buffer(context); log::info!("Creating compute pipeline..."); @@ -120,7 +88,7 @@ impl VoxelRenderer { .with_entry( wgpu::ShaderStages::COMPUTE, wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Storage { read_only: true }, + ty: wgpu::BufferBindingType::Storage { read_only: false }, has_dynamic_offset: false, min_binding_size: None, }, @@ -144,6 +112,15 @@ impl VoxelRenderer { }, None, ) + .with_entry( + wgpu::ShaderStages::COMPUTE, + wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: false }, + has_dynamic_offset: false, + min_binding_size: None, + }, + None, + ) .with_entry( wgpu::ShaderStages::COMPUTE, wgpu::BindingType::Buffer { @@ -161,6 +138,7 @@ impl VoxelRenderer { .with_entry(brickmap_manager.get_brickgrid_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(brickmap_manager.get_feedback_buffer().as_entire_binding()) .with_entry(camera_controller.get_buffer().as_entire_binding()) .build(context); let compute_pipeline = @@ -226,9 +204,19 @@ impl render::Renderer for VoxelRenderer { drop(render_pass); + encoder.copy_buffer_to_buffer( + self.brickmap_manager.get_feedback_buffer(), + 0, + self.brickmap_manager.get_feedback_result_buffer(), + 0, + self.brickmap_manager.get_feedback_result_buffer().size(), + ); + context.queue.submit(Some(encoder.finish())); frame.present(); } - fn update(&self, dt: &Duration, context: &render::Context) {} + fn update(&mut self, dt: &Duration, context: &render::Context) { + self.brickmap_manager.process_feedback_buffer(context); + } }