use crate::gpu::pipelines::GLOBALS_SIZE;
use crate::gpu::scene::Scene;
use bytemuck::{Pod, Zeroable};
use rustc_hash::FxHasher;
use std::hash::{Hash, Hasher};
#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable)]
struct Globals {
viewport: [f32; 2],
padding: [f32; 2],
matrix: [f32; 4],
offset: [f32; 4],
}
pub(crate) struct GlobalsSlots {
buffer: wgpu::Buffer,
bind_group: wgpu::BindGroup,
capacity: usize,
stride: u64,
scratch: Vec<u8>,
last_hash: Option<u64>,
}
fn hash_bytes(bytes: &[u8]) -> u64 {
let mut hasher = FxHasher::default();
bytes.hash(&mut hasher);
hasher.finish()
}
impl GlobalsSlots {
pub(crate) fn new(device: &wgpu::Device, layout: &wgpu::BindGroupLayout, capacity: usize) -> Self {
let stride = GLOBALS_SIZE.next_multiple_of(u64::from(device.limits().min_uniform_buffer_offset_alignment));
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("aura_globals"),
size: stride * capacity as u64,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("aura_globals"),
layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
buffer: &buffer,
offset: 0,
size: wgpu::BufferSize::new(GLOBALS_SIZE),
}),
}],
});
Self { buffer, bind_group, capacity, stride, scratch: Vec::new(), last_hash: None }
}
pub(crate) fn bind_group(&self) -> &wgpu::BindGroup {
&self.bind_group
}
pub(crate) fn stride(&self) -> u64 {
self.stride
}
pub(crate) fn upload(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
layout: &wgpu::BindGroupLayout,
scene: &Scene,
viewport: [f32; 2],
) {
let slots = scene.transforms.len().max(1);
if slots > self.capacity {
*self = Self::new(device, layout, slots.next_power_of_two());
}
self.scratch.clear();
self.scratch.resize(slots * self.stride as usize, 0);
for slot in 0..slots {
let transform = scene.transforms.get(slot).copied().unwrap_or_default();
let globals = Globals {
viewport,
padding: [0.0; 2],
matrix: [transform.a, transform.b, transform.c, transform.d],
offset: [transform.tx, transform.ty, transform.scale_factor(), 0.0],
};
let start = slot * self.stride as usize;
self.scratch[start..start + GLOBALS_SIZE as usize].copy_from_slice(bytemuck::bytes_of(&globals));
}
let hash = hash_bytes(&self.scratch);
if self.last_hash == Some(hash) {
return;
}
self.last_hash = Some(hash);
queue.write_buffer(&self.buffer, 0, &self.scratch);
}
}