pub use self::buffers::BuffersDefinition;
pub use self::collection::VertexBuffersCollection;
pub use self::definition::IncompatibleVertexDefinitionError;
pub use self::definition::VertexDefinition;
pub use self::impl_vertex::VertexMember;
pub use self::vertex::Vertex;
pub use self::vertex::VertexMemberInfo;
pub use self::vertex::VertexMemberTy;
use crate::device::Device;
use crate::format::Format;
use crate::pipeline::graphics::GraphicsPipelineCreationError;
use crate::pipeline::DynamicState;
use smallvec::SmallVec;
use std::collections::HashMap;
use std::ptr;
mod buffers;
mod collection;
mod definition;
mod impl_vertex;
mod vertex;
#[derive(Clone, Debug, Default)]
pub struct VertexInputState {
pub bindings: HashMap<u32, VertexInputBindingDescription>,
pub attributes: HashMap<u32, VertexInputAttributeDescription>,
}
impl VertexInputState {
#[inline]
pub fn new() -> VertexInputState {
VertexInputState {
bindings: Default::default(),
attributes: Default::default(),
}
}
#[inline]
pub fn binding(mut self, binding: u32, description: VertexInputBindingDescription) -> Self {
self.bindings.insert(binding, description);
self
}
#[inline]
pub fn bindings(
mut self,
bindings: impl IntoIterator<Item = (u32, VertexInputBindingDescription)>,
) -> Self {
self.bindings = bindings.into_iter().collect();
self
}
#[inline]
pub fn attribute(
mut self,
location: u32,
description: VertexInputAttributeDescription,
) -> Self {
self.attributes.insert(location, description);
self
}
#[inline]
pub fn attributes(
mut self,
attributes: impl IntoIterator<Item = (u32, VertexInputAttributeDescription)>,
) -> Self {
self.attributes = attributes.into_iter().collect();
self
}
pub(crate) fn to_vulkan(
&self,
device: &Device,
dynamic_state_modes: &mut HashMap<DynamicState, bool>,
binding_descriptions: &[ash::vk::VertexInputBindingDescription],
attribute_descriptions: &[ash::vk::VertexInputAttributeDescription],
binding_divisor_state: Option<&ash::vk::PipelineVertexInputDivisorStateCreateInfoEXT>,
) -> ash::vk::PipelineVertexInputStateCreateInfo {
dynamic_state_modes.insert(DynamicState::VertexInput, false);
ash::vk::PipelineVertexInputStateCreateInfo {
p_next: if let Some(next) = binding_divisor_state {
next as *const _ as *const _
} else {
ptr::null()
},
flags: ash::vk::PipelineVertexInputStateCreateFlags::empty(),
vertex_binding_description_count: binding_descriptions.len() as u32,
p_vertex_binding_descriptions: binding_descriptions.as_ptr(),
vertex_attribute_description_count: attribute_descriptions.len() as u32,
p_vertex_attribute_descriptions: attribute_descriptions.as_ptr(),
..Default::default()
}
}
pub(crate) fn to_vulkan_bindings(
&self,
device: &Device,
) -> Result<SmallVec<[ash::vk::VertexInputBindingDescription; 8]>, GraphicsPipelineCreationError>
{
let binding_descriptions: SmallVec<[_; 8]> = self
.bindings
.iter()
.map(|(&binding, binding_desc)| {
if binding
>= device
.physical_device()
.properties()
.max_vertex_input_bindings
{
return Err(
GraphicsPipelineCreationError::MaxVertexInputBindingsExceeded {
max: device
.physical_device()
.properties()
.max_vertex_input_bindings,
obtained: binding,
},
);
}
if binding_desc.stride
> device
.physical_device()
.properties()
.max_vertex_input_binding_stride
{
return Err(
GraphicsPipelineCreationError::MaxVertexInputBindingStrideExceeded {
binding,
max: device
.physical_device()
.properties()
.max_vertex_input_binding_stride,
obtained: binding_desc.stride,
},
);
}
Ok(ash::vk::VertexInputBindingDescription {
binding,
stride: binding_desc.stride,
input_rate: binding_desc.input_rate.into(),
})
})
.collect::<Result<_, _>>()?;
if binding_descriptions.len()
> device
.physical_device()
.properties()
.max_vertex_input_bindings as usize
{
return Err(
GraphicsPipelineCreationError::MaxVertexInputBindingsExceeded {
max: device
.physical_device()
.properties()
.max_vertex_input_bindings,
obtained: binding_descriptions.len() as u32,
},
);
}
Ok(binding_descriptions)
}
pub(crate) fn to_vulkan_attributes(
&self,
device: &Device,
) -> Result<
SmallVec<[ash::vk::VertexInputAttributeDescription; 8]>,
GraphicsPipelineCreationError,
> {
let attribute_descriptions: SmallVec<[_; 8]> = self
.attributes
.iter()
.map(|(&location, attribute_desc)| {
if !self.bindings.contains_key(&attribute_desc.binding) {
return Err(
GraphicsPipelineCreationError::VertexInputAttributeInvalidBinding {
location,
binding: attribute_desc.binding,
},
);
}
if attribute_desc.offset
> device
.physical_device()
.properties()
.max_vertex_input_attribute_offset
{
return Err(
GraphicsPipelineCreationError::MaxVertexInputAttributeOffsetExceeded {
max: device
.physical_device()
.properties()
.max_vertex_input_attribute_offset,
obtained: attribute_desc.offset,
},
);
}
let format_features = device
.physical_device()
.format_properties(attribute_desc.format)
.buffer_features;
if !format_features.vertex_buffer {
return Err(
GraphicsPipelineCreationError::VertexInputAttributeUnsupportedFormat {
location,
format: attribute_desc.format,
},
);
}
Ok(ash::vk::VertexInputAttributeDescription {
location,
binding: attribute_desc.binding,
format: attribute_desc.format.into(),
offset: attribute_desc.offset,
})
})
.collect::<Result<_, _>>()?;
if attribute_descriptions.len()
> device
.physical_device()
.properties()
.max_vertex_input_attributes as usize
{
return Err(
GraphicsPipelineCreationError::MaxVertexInputAttributesExceeded {
max: device
.physical_device()
.properties()
.max_vertex_input_attributes,
obtained: attribute_descriptions.len(),
},
);
}
Ok(attribute_descriptions)
}
pub(crate) fn to_vulkan_binding_divisor_state(
&self,
binding_divisor_descriptions: &[ash::vk::VertexInputBindingDivisorDescriptionEXT],
) -> Option<ash::vk::PipelineVertexInputDivisorStateCreateInfoEXT> {
if !binding_divisor_descriptions.is_empty() {
Some(ash::vk::PipelineVertexInputDivisorStateCreateInfoEXT {
vertex_binding_divisor_count: binding_divisor_descriptions.len() as u32,
p_vertex_binding_divisors: binding_divisor_descriptions.as_ptr(),
..Default::default()
})
} else {
None
}
}
pub(crate) fn to_vulkan_binding_divisors(
&self,
device: &Device,
) -> Result<
SmallVec<[ash::vk::VertexInputBindingDivisorDescriptionEXT; 8]>,
GraphicsPipelineCreationError,
> {
self.bindings
.iter()
.filter_map(|(&binding, binding_desc)| match binding_desc.input_rate {
VertexInputRate::Instance { divisor } if divisor != 1 => Some((binding, divisor)),
_ => None,
})
.map(|(binding, divisor)| {
if !device
.enabled_features()
.vertex_attribute_instance_rate_divisor
{
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "vertex_attribute_instance_rate_divisor",
reason: "VertexInputRate::Instance::divisor was not 1",
});
}
if divisor == 0
&& !device
.enabled_features()
.vertex_attribute_instance_rate_zero_divisor
{
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "vertex_attribute_instance_rate_zero_divisor",
reason: "VertexInputRate::Instance::divisor was 0",
});
}
if divisor
> device
.physical_device()
.properties()
.max_vertex_attrib_divisor
.unwrap()
{
return Err(
GraphicsPipelineCreationError::MaxVertexAttribDivisorExceeded {
binding,
max: device
.physical_device()
.properties()
.max_vertex_attrib_divisor
.unwrap(),
obtained: divisor,
},
);
}
Ok(ash::vk::VertexInputBindingDivisorDescriptionEXT { binding, divisor })
})
.collect()
}
}
#[derive(Clone, Debug)]
pub struct VertexInputBindingDescription {
pub stride: u32,
pub input_rate: VertexInputRate,
}
#[derive(Clone, Copy, Debug)]
pub struct VertexInputAttributeDescription {
pub binding: u32,
pub format: Format,
pub offset: u32,
}
#[derive(Clone, Copy, Debug)]
pub enum VertexInputRate {
Vertex,
Instance { divisor: u32 },
}
impl From<VertexInputRate> for ash::vk::VertexInputRate {
#[inline]
fn from(val: VertexInputRate) -> Self {
match val {
VertexInputRate::Vertex => ash::vk::VertexInputRate::VERTEX,
VertexInputRate::Instance { .. } => ash::vk::VertexInputRate::INSTANCE,
}
}
}