use super::VertexMemberInfo;
use crate::{
pipeline::graphics::vertex_input::{
IncompatibleVertexDefinitionError, Vertex, VertexDefinition,
VertexInputAttributeDescription, VertexInputBindingDescription, VertexInputRate,
VertexInputState,
},
shader::ShaderInterface,
DeviceSize,
};
use std::{
fmt::{Debug, Error as FmtError, Formatter},
mem,
};
#[derive(Clone, Debug, Default)]
pub struct BuffersDefinition(Vec<VertexBuffer>);
#[derive(Clone, Copy)]
struct VertexBuffer {
info_fn: fn(&str) -> Option<VertexMemberInfo>,
stride: u32,
input_rate: VertexInputRate,
}
impl Debug for VertexBuffer {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
f.debug_struct("VertexBuffer")
.field("stride", &self.stride)
.field("input_rate", &self.input_rate)
.finish()
}
}
impl From<VertexBuffer> for VertexInputBindingDescription {
fn from(val: VertexBuffer) -> Self {
Self {
stride: val.stride,
input_rate: val.input_rate,
}
}
}
impl BuffersDefinition {
#[inline]
pub fn new() -> Self {
BuffersDefinition(Vec::new())
}
pub fn vertex<V: Vertex>(mut self) -> Self {
self.0.push(VertexBuffer {
info_fn: V::member,
stride: mem::size_of::<V>() as u32,
input_rate: VertexInputRate::Vertex,
});
self
}
pub fn instance<V: Vertex>(mut self) -> Self {
self.0.push(VertexBuffer {
info_fn: V::member,
stride: mem::size_of::<V>() as u32,
input_rate: VertexInputRate::Instance { divisor: 1 },
});
self
}
pub fn instance_with_divisor<V: Vertex>(mut self, divisor: u32) -> Self {
self.0.push(VertexBuffer {
info_fn: V::member,
stride: mem::size_of::<V>() as u32,
input_rate: VertexInputRate::Instance { divisor },
});
self
}
}
unsafe impl VertexDefinition for BuffersDefinition {
#[inline]
fn definition(
&self,
interface: &ShaderInterface,
) -> Result<VertexInputState, IncompatibleVertexDefinitionError> {
let bindings = self
.0
.iter()
.enumerate()
.map(|(binding, &buffer)| (binding as u32, buffer.into()));
let mut attributes: Vec<(u32, VertexInputAttributeDescription)> = Vec::new();
for element in interface.elements() {
let name = element.name.as_ref().unwrap();
let (infos, binding) = self
.0
.iter()
.enumerate()
.find_map(|(binding, buffer)| {
(buffer.info_fn)(name).map(|infos| (infos, binding as u32))
})
.ok_or_else(||
IncompatibleVertexDefinitionError::MissingAttribute {
attribute: name.clone().into_owned(),
})?;
if !infos.ty.matches(
infos.array_size,
element.ty.to_format(),
element.ty.num_locations(),
) {
return Err(IncompatibleVertexDefinitionError::FormatMismatch {
attribute: name.clone().into_owned(),
shader: (
element.ty.to_format(),
(element.ty.num_locations()) as usize,
),
definition: (infos.ty, infos.array_size),
});
}
let mut offset = infos.offset as DeviceSize;
let location_range = element.location..element.location + element.ty.num_locations();
for location in location_range {
attributes.push((
location,
VertexInputAttributeDescription {
binding,
format: element.ty.to_format(),
offset: offset as u32,
},
));
offset += element.ty.to_format().block_size().unwrap();
}
}
Ok(VertexInputState::new()
.bindings(bindings)
.attributes(attributes))
}
}