Add basic CPU to GPU streaming system
This commit is contained in:
parent
3fb089d392
commit
af0ab3bb66
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue