Add basic CPU to GPU streaming system

This commit is contained in:
Jarrod Doyle 2023-05-03 14:44:42 +01:00
parent 3fb089d392
commit af0ab3bb66
Signed by: Jayrude
GPG Key ID: 38B57B16E7C0ADF7
5 changed files with 181 additions and 43 deletions

View File

@ -1,9 +1,10 @@
@group(0) @binding(0) var output: texture_storage_2d<rgba8unorm, write>; @group(0) @binding(0) var output: texture_storage_2d<rgba8unorm, write>;
@group(0) @binding(1) var<uniform> world_state: WorldState; @group(0) @binding(1) var<uniform> world_state: WorldState;
@group(0) @binding(2) var<storage, read> brickgrid: array<u32>; @group(0) @binding(2) var<storage, read_write> brickgrid: array<atomic<u32>>;
@group(0) @binding(3) var<storage, read> brickmap_cache: array<Brickmap>; @group(0) @binding(3) var<storage, read> brickmap_cache: array<Brickmap>;
@group(0) @binding(4) var<storage, read> shading_table: array<ShadingElement>; @group(0) @binding(4) var<storage, read> shading_table: array<ShadingElement>;
@group(0) @binding(5) var<uniform> camera: Camera; @group(0) @binding(5) var<storage, read_write> cpu_feedback: Feedback;
@group(0) @binding(6) var<uniform> camera: Camera;
struct ShadingElement { struct ShadingElement {
albedo: u32, albedo: u32,
@ -38,6 +39,26 @@ struct AabbHitInfo {
distance: f32, distance: f32,
}; };
struct Feedback {
max_count: u32,
count: atomic<u32>,
_pad1: u32,
_pad2: u32,
positions: array<vec4<i32>>,
}
// struct UnpackElement {
// pos: vec3<i32>,
// cache_idx: u32,
// map: Brickmap,
// }
// struct Unpack {
// max_count: u32,
// count: u32,
// maps: array<UnpackElement>
// }
// Utility function. Converts a position in 3d to a 1d index. // Utility function. Converts a position in 3d to a 1d index.
fn to_1d_index(p: vec3<i32>, dims: vec3<i32>) -> u32 { fn to_1d_index(p: vec3<i32>, dims: vec3<i32>) -> u32 {
return u32(p.x + p.y * dims.x + p.z * dims.x * dims.y); 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<f32>, ray_dir: vec3<f32>) -> HitInfo {
let grid_idx = to_1d_index(map_pos, vec3<i32>(world_state.brickmap_cache_dims)); let grid_idx = to_1d_index(map_pos, vec3<i32>(world_state.brickmap_cache_dims));
let brick_ptr = brickgrid[grid_idx]; let brick_ptr = brickgrid[grid_idx];
// If brick pointers loaded flag is set // Ptr = 28 bits LOD colour / brickmap index + 4 bits load flags
if ((brick_ptr & 1u) == 1u) { // 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<i32>(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 brickmap_idx = brick_ptr >> 8u;
let tmp_voxel_hit = brick_ray_cast(map_pos, brickmap_idx, orig_ray_pos, ray_dir); let tmp_voxel_hit = brick_ray_cast(map_pos, brickmap_idx, orig_ray_pos, ray_dir);

View File

@ -60,7 +60,7 @@ impl App {
0.25, 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 cumulative_dt = 0.0;
let mut frames_accumulated = 0.0; let mut frames_accumulated = 0.0;
@ -86,6 +86,7 @@ impl App {
camera_controller.update(dt); camera_controller.update(dt);
camera_controller.update_buffer(&self.render_ctx); camera_controller.update_buffer(&self.render_ctx);
renderer.render(&self.render_ctx); renderer.render(&self.render_ctx);
renderer.update(&dt, &self.render_ctx);
// Simple framerate tracking // Simple framerate tracking
cumulative_dt += dt.as_secs_f32(); cumulative_dt += dt.as_secs_f32();

View File

@ -1,6 +1,6 @@
use std::time::Duration; use std::time::Duration;
pub trait Renderer { 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); fn render(&self, context: &super::Context);
} }

View File

@ -1,3 +1,5 @@
use std::future;
use wgpu::util::DeviceExt; use wgpu::util::DeviceExt;
use crate::render; use crate::render;
@ -27,6 +29,8 @@ pub struct BrickmapManager {
brickmap_buffer: wgpu::Buffer, brickmap_buffer: wgpu::Buffer,
shading_table: Vec<u32>, shading_table: Vec<u32>,
shading_table_buffer: wgpu::Buffer, shading_table_buffer: wgpu::Buffer,
feedback_buffer: wgpu::Buffer,
feedback_result_buffer: wgpu::Buffer,
} }
impl BrickmapManager { impl BrickmapManager {
@ -38,7 +42,7 @@ impl BrickmapManager {
brickmap_cache.resize(32768, Brickmap::default()); brickmap_cache.resize(32768, Brickmap::default());
let mut brickgrid = Vec::<u32>::with_capacity(32768); let mut brickgrid = Vec::<u32>::with_capacity(32768);
brickgrid.resize(brickgrid.capacity(), 0); brickgrid.resize(brickgrid.capacity(), 1);
let device = &context.device; let device = &context.device;
let state_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { let state_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
@ -68,6 +72,22 @@ impl BrickmapManager {
let mut shading_table = Vec::<u32>::with_capacity(25000000); let mut shading_table = Vec::<u32>::with_capacity(25000000);
shading_table.resize(shading_table.capacity(), 0); 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 { Self {
state_uniform, state_uniform,
state_buffer, state_buffer,
@ -77,6 +97,8 @@ impl BrickmapManager {
brickmap_buffer, brickmap_buffer,
shading_table, shading_table,
shading_table_buffer, shading_table_buffer,
feedback_buffer,
feedback_result_buffer,
} }
} }
@ -87,7 +109,7 @@ impl BrickmapManager {
.unwrap(); .unwrap();
let shading_idx = idx * 512; let shading_idx = idx * 512;
self.brickgrid 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].bitmask = *data;
self.brickmap_cache[idx].shading_table_offset = shading_idx as u32; self.brickmap_cache[idx].shading_table_offset = shading_idx as u32;
self.shading_table.splice( 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) { pub fn update_buffer(&self, context: &render::Context) {
let queue = &context.queue; let queue = &context.queue;
queue.write_buffer( queue.write_buffer(
@ -130,4 +159,79 @@ impl BrickmapManager {
pub fn get_shading_buffer(&self) -> &wgpu::Buffer { pub fn get_shading_buffer(&self) -> &wgpu::Buffer {
&self.shading_table_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<u32> = 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::<u32>::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);
}
} }

View File

@ -60,39 +60,7 @@ impl VoxelRenderer {
}); });
log::info!("Creating brickmap manager..."); log::info!("Creating brickmap manager...");
let mut brickmap_manager = super::brickmap::BrickmapManager::new(context); let 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::<u32>::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);
}
brickmap_manager.update_buffer(context); brickmap_manager.update_buffer(context);
log::info!("Creating compute pipeline..."); log::info!("Creating compute pipeline...");
@ -120,7 +88,7 @@ impl VoxelRenderer {
.with_entry( .with_entry(
wgpu::ShaderStages::COMPUTE, wgpu::ShaderStages::COMPUTE,
wgpu::BindingType::Buffer { wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true }, ty: wgpu::BufferBindingType::Storage { read_only: false },
has_dynamic_offset: false, has_dynamic_offset: false,
min_binding_size: None, min_binding_size: None,
}, },
@ -144,6 +112,15 @@ impl VoxelRenderer {
}, },
None, 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( .with_entry(
wgpu::ShaderStages::COMPUTE, wgpu::ShaderStages::COMPUTE,
wgpu::BindingType::Buffer { wgpu::BindingType::Buffer {
@ -161,6 +138,7 @@ impl VoxelRenderer {
.with_entry(brickmap_manager.get_brickgrid_buffer().as_entire_binding()) .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_brickmap_buffer().as_entire_binding())
.with_entry(brickmap_manager.get_shading_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()) .with_entry(camera_controller.get_buffer().as_entire_binding())
.build(context); .build(context);
let compute_pipeline = let compute_pipeline =
@ -226,9 +204,19 @@ impl render::Renderer for VoxelRenderer {
drop(render_pass); 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())); context.queue.submit(Some(encoder.finish()));
frame.present(); 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);
}
} }