use {
crate::{
layer::Layer,
pipeline::Lights as Bindings,
render::State,
shader::{Shader, ShaderInfo},
shader_data::source::{SetLenError, Source, SourceArray, UpdateError as ArrayUpdateError},
},
std::{marker::PhantomData, sync::Arc},
wgpu::{BindGroup, BindGroupLayout, Buffer, Queue},
};
pub struct Lights<S> {
group: u32,
bind_group: BindGroup,
source_arrays: Vec<(SourceArray, SourceArrayBuffers)>,
queue: Arc<Queue>,
ty: PhantomData<S>,
}
impl<S> Lights<S> {
fn new(params: Parameters, state: &State) -> Self {
use {
std::iter,
wgpu::{
util::{BufferInitDescriptor, DeviceExt},
BindGroupDescriptor, BindGroupEntry, BufferUsages,
},
};
let Parameters {
bindings,
variables,
layout,
} = params;
let device = state.device();
let source_arrays: Vec<_> = iter::zip(variables.source_arrays, &bindings.source_arrays)
.map(|(var, bind)| {
let array = SourceArray::new(var, bind.size as usize);
let array_buf = device.create_buffer_init(&BufferInitDescriptor {
label: Some("source array buffer"),
contents: bytemuck::cast_slice(array.sources()),
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
});
let len_buf = device.create_buffer_init(&BufferInitDescriptor {
label: Some("source len buffer"),
contents: bytemuck::cast_slice(&[array.len()]),
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
});
(
array,
SourceArrayBuffers {
array: array_buf,
len: len_buf,
},
)
})
.collect();
let entries: Vec<_> = iter::zip(&source_arrays, &bindings.source_arrays)
.flat_map(|((_, buf), bind)| {
[
BindGroupEntry {
binding: bind.binding_array,
resource: buf.array.as_entire_binding(),
},
BindGroupEntry {
binding: bind.binding_len,
resource: buf.len.as_entire_binding(),
},
]
})
.collect();
Self {
group: bindings.group,
bind_group: device.create_bind_group(&BindGroupDescriptor {
layout,
entries: &entries,
label: Some("lights bind group"),
}),
source_arrays,
queue: Arc::clone(state.queue()),
ty: PhantomData,
}
}
pub fn update_sources(
&mut self,
index: usize,
offset: usize,
sources: &[Source],
) -> Result<(), UpdateError>
where
S: Shader,
{
use std::mem;
let info = ShaderInfo::new::<S>();
assert!(info.has_lights(), "the shader has no light sources");
let (array, buffers) = self
.source_arrays
.get_mut(index)
.ok_or(UpdateError::Index)?;
array.update(offset, sources)?;
let data = &array.sources()[offset..];
self.queue.write_buffer(
&buffers.array,
(offset * mem::size_of::<Source>()) as _,
bytemuck::cast_slice(data),
);
let old_len = array.len();
let new_len = (offset + sources.len()) as u32;
if old_len.get() < new_len {
array.set_len(new_len)?;
self.queue
.write_buffer(&buffers.len, 0, bytemuck::cast_slice(&[array.len()]));
}
Ok(())
}
pub(crate) fn bind(&self) -> (u32, &BindGroup) {
(self.group, &self.bind_group)
}
}
#[derive(Debug)]
pub enum UpdateError {
Index,
Array(ArrayUpdateError),
SetLen(SetLenError),
}
impl From<ArrayUpdateError> for UpdateError {
fn from(v: ArrayUpdateError) -> Self {
Self::Array(v)
}
}
impl From<SetLenError> for UpdateError {
fn from(v: SetLenError) -> Self {
Self::SetLen(v)
}
}
struct SourceArrayBuffers {
array: Buffer,
len: Buffer,
}
struct Parameters<'a> {
variables: Variables,
bindings: &'a Bindings,
layout: &'a BindGroupLayout,
}
#[derive(Default)]
struct Variables {
source_arrays: Vec<Vec<Source>>,
}
#[must_use]
pub struct Builder<'a> {
state: &'a State,
variables: Variables,
}
impl<'a> Builder<'a> {
pub(crate) fn new(state: &'a State) -> Self {
Self {
state,
variables: Variables::default(),
}
}
pub fn with_sources(mut self, sources: Vec<Source>) -> Self {
self.variables.source_arrays.push(sources);
self
}
pub fn build<S, T>(self, layer: &Layer<S, T>) -> Lights<S>
where
S: Shader,
{
let actual = self.variables.source_arrays.len();
let info = ShaderInfo::new::<S>();
let expected = info.source_arrays();
assert_eq!(
actual, expected,
"the shader requires {expected} source arrays, but {actual} is set",
);
let lights = layer.pipeline().lights().expect("the shader has no lights");
let params = Parameters {
variables: self.variables,
bindings: &lights.bindings,
layout: &lights.layout,
};
Lights::new(params, self.state)
}
}