render: simplify light buffer updating

This commit is contained in:
SeanOMik 2024-06-15 23:52:46 -04:00
parent 7576979797
commit 6182a4b9c8
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
2 changed files with 22 additions and 79 deletions

View File

@ -1,13 +1,13 @@
{
"rdocCaptureSettings": 1,
"settings": {
"autoStart": true,
"autoStart": false,
"commandLine": "",
"environment": [
],
"executable": "/media/data_drive/Development/Rust/lyra-engine/target/debug/testbed",
"inject": false,
"numQueuedFrames": 1,
"numQueuedFrames": 0,
"options": {
"allowFullscreen": true,
"allowVSync": true,
@ -22,7 +22,7 @@
"softMemoryLimit": 0,
"verifyBufferAccess": false
},
"queuedFrameCap": 5,
"queuedFrameCap": 0,
"workingDir": "/media/data_drive/Development/Rust/lyra-engine/examples/testbed"
}
}

View File

@ -5,6 +5,7 @@ pub mod spotlight;
use lyra_ecs::{Entity, Tick, World, query::{Entities, TickOf}};
pub use point::*;
pub use spotlight::*;
use tracing::debug;
use std::{collections::{HashMap, VecDeque}, marker::PhantomData, mem, rc::Rc};
@ -99,12 +100,9 @@ impl<U: Default + bytemuck::Pod + bytemuck::Zeroable> LightBuffer<U> {
pub(crate) struct LightUniformBuffers {
pub buffer: Rc<wgpu::Buffer>,
//pub bind_group_pair: BindGroupPair,
pub bind_group: Rc<wgpu::BindGroup>,
pub bind_group_layout: Rc<wgpu::BindGroupLayout>,
pub light_indexes: HashMap<Entity, u32>,
dead_indices: VecDeque<u32>,
pub current_light_idx: u32,
max_light_count: u64,
}
impl LightUniformBuffers {
@ -161,90 +159,35 @@ impl LightUniformBuffers {
buffer: Rc::new(buffer),
bind_group: Rc::new(bindgroup),
bind_group_layout: Rc::new(bindgroup_layout),
light_indexes: Default::default(),
current_light_idx: 0,
dead_indices: VecDeque::new(),
max_light_count: max_buffer_sizes / mem::size_of::<LightUniform>() as u64,
}
}
/// Returns the index for the entity, and if this index is new
fn get_index_for(&mut self, missed: &mut HashMap<Entity, u32>, entity: Entity) -> (bool, u32) {
let idx = missed.remove(&entity)
.map(|v| (false, v))
.or_else(||
self.dead_indices.pop_front()
.map(|v| (true, v))
)
.unwrap_or_else(|| {
let t = self.current_light_idx;
self.current_light_idx += 1;
(true, t)
});
idx
}
pub fn update_lights(&mut self, queue: &wgpu::Queue, world_tick: Tick, world: &World) {
// used to detect what lights were removed
let mut missed_lights: HashMap<Entity, u32> = self.light_indexes.drain().collect();
for (entity, point_light, transform, light_epoch, transform_epoch)
in world.view_iter::<(Entities, &PointLight, &Transform, TickOf<PointLight>, TickOf<Transform>)>() {
let _ = world_tick;
let mut lights = vec![];
let (new, idx) = self.get_index_for(&mut missed_lights, entity);
self.light_indexes.insert(entity, idx);
if new || light_epoch == world_tick || transform_epoch == world_tick {
let uniform = LightUniform::from_point_light_bundle(&point_light, &transform);
let offset = mem::size_of::<u32>() * 4 + mem::size_of::<LightUniform>() * idx as usize;
queue.write_buffer(&self.buffer, offset as _, bytemuck::cast_slice(&[uniform]));
}
for (point_light, transform) in world.view_iter::<(&PointLight, &Transform)>() {
let uniform = LightUniform::from_point_light_bundle(&point_light, &transform);
lights.push(uniform);
}
for (entity, spot_light, transform, light_epoch, transform_epoch)
in world.view_iter::<(Entities, &SpotLight, &Transform, TickOf<SpotLight>, TickOf<Transform>)>() {
let (new, idx) = self.get_index_for(&mut missed_lights, entity);
self.light_indexes.insert(entity, idx);
if new || light_epoch == world_tick || transform_epoch == world_tick {
let uniform = LightUniform::from_spot_light_bundle(&spot_light, &transform);
let offset = mem::size_of::<u32>() * 4 + mem::size_of::<LightUniform>() * idx as usize;
queue.write_buffer(&self.buffer, offset as _, bytemuck::cast_slice(&[uniform]));
}
for (spot_light, transform) in world.view_iter::<(&SpotLight, &Transform)>() {
let uniform = LightUniform::from_spot_light_bundle(&spot_light, &transform);
lights.push(uniform);
}
for (entity, dir_light, transform, light_epoch, transform_epoch)
in world.view_iter::<(Entities, &DirectionalLight, &Transform, TickOf<DirectionalLight>, TickOf<Transform>)>() {
let (new, idx) = self.get_index_for(&mut missed_lights, entity);
self.light_indexes.insert(entity, idx);
if new || light_epoch == world_tick || transform_epoch == world_tick {
let uniform = LightUniform::from_directional_bundle(&dir_light, &transform);
let offset = mem::size_of::<u32>() * 4 + mem::size_of::<LightUniform>() * idx as usize;
queue.write_buffer(&self.buffer, offset as _, bytemuck::cast_slice(&[uniform]));
}
for (dir_light, transform) in world.view_iter::<(&DirectionalLight, &Transform)>() {
let uniform = LightUniform::from_directional_bundle(&dir_light, &transform);
lights.push(uniform);
}
// anything left in missed_lights were lights that were deleted
let len = missed_lights.len();
self.dead_indices.reserve(len);
assert!(lights.len() < self.max_light_count as _); // ensure we dont overwrite the buffer
for (_, v) in missed_lights.drain() {
// write zeros in place of this now dead light, the enabled boolean will be set to false
let mut zeros = Vec::new();
zeros.resize(mem::size_of::<LightUniform>(), 0u32);
let offset = mem::size_of::<u32>() * 4 + mem::size_of::<LightUniform>() * v as usize;
queue.write_buffer(&self.buffer, offset as _, bytemuck::cast_slice(zeros.as_slice()));
self.dead_indices.push_back(v);
}
// update the amount of lights, then the array of lights
queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(&[self.current_light_idx as u32]));
// write the amount of lights to the buffer, and right after that the list of lights.
queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(&[lights.len()]));
// the size of u32 is multiplied by 4 because of gpu alignment requirements
queue.write_buffer(&self.buffer, mem::size_of::<u32>() as u64 * 4, bytemuck::cast_slice(lights.as_slice()));
}
}