aboutsummaryrefslogtreecommitdiff
path: root/src/pipeline/graphics/vertex_input/definition.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/pipeline/graphics/vertex_input/definition.rs')
-rw-r--r--src/pipeline/graphics/vertex_input/definition.rs226
1 files changed, 226 insertions, 0 deletions
diff --git a/src/pipeline/graphics/vertex_input/definition.rs b/src/pipeline/graphics/vertex_input/definition.rs
new file mode 100644
index 0000000..2372670
--- /dev/null
+++ b/src/pipeline/graphics/vertex_input/definition.rs
@@ -0,0 +1,226 @@
+// Copyright (c) 2016 The vulkano developers
+// Licensed under the Apache License, Version 2.0
+// <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
+// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
+// at your option. All files in the project carrying such
+// notice may not be copied, modified, or distributed except
+// according to those terms.
+
+//! Definition for creating a [`VertexInputState`] based on a [`ShaderInterface`].
+//!
+//! # Implementing `Vertex`
+//!
+//! The implementations of the `VertexDefinition` trait that are provided by vulkano require you to
+//! use a buffer whose content is `[V]` where `V` implements the `Vertex` trait.
+//!
+//! The `Vertex` trait is unsafe, but can be implemented on a struct with the `impl_vertex!`
+//! macro.
+//!
+//! # Examples
+//!
+//! ```ignore // TODO:
+//! # #[macro_use] extern crate vulkano
+//! # fn main() {
+//! # use std::sync::Arc;
+//! # use vulkano::device::Device;
+//! # use vulkano::device::Queue;
+//! use vulkano::buffer::BufferAccess;
+//! use vulkano::buffer::BufferUsage;
+//! use vulkano::memory::HostVisible;
+//! use vulkano::pipeline::vertex::;
+//! # let device: Arc<Device> = return;
+//! # let queue: Arc<Queue> = return;
+//!
+//! struct Vertex {
+//! position: [f32; 2]
+//! }
+//!
+//! impl_vertex!(Vertex, position);
+//!
+//! let usage = BufferUsage {
+//! vertex_buffer: true,
+//! ..BufferUsage::empty()
+//! };
+//!
+//! let vertex_buffer = BufferAccess::<[Vertex], _>::array(&device, 128, &usage, HostVisible, &queue)
+//! .expect("failed to create buffer");
+//!
+//! // TODO: finish example
+//! # }
+//! ```
+
+use super::{
+ VertexBufferDescription, VertexInputAttributeDescription, VertexInputBindingDescription,
+};
+use crate::{
+ pipeline::graphics::vertex_input::{VertexInputState, VertexMemberInfo},
+ shader::{ShaderInterface, ShaderInterfaceEntryType},
+ DeviceSize,
+};
+use std::{
+ error::Error,
+ fmt::{Display, Error as FmtError, Formatter},
+};
+
+/// Trait for types that can create a [`VertexInputState`] from a [`ShaderInterface`].
+pub unsafe trait VertexDefinition {
+ /// Builds the vertex definition to use to link this definition to a vertex shader's input
+ /// interface.
+ // TODO: remove error return, move checks to GraphicsPipelineBuilder
+ fn definition(
+ &self,
+ interface: &ShaderInterface,
+ ) -> Result<VertexInputState, IncompatibleVertexDefinitionError>;
+}
+
+unsafe impl VertexDefinition for VertexInputState {
+ fn definition(
+ &self,
+ _interface: &ShaderInterface,
+ ) -> Result<VertexInputState, IncompatibleVertexDefinitionError> {
+ Ok(self.clone())
+ }
+}
+
+/// Error that can happen when the vertex definition doesn't match the input of the vertex shader.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum IncompatibleVertexDefinitionError {
+ /// An attribute of the vertex shader is missing in the vertex source.
+ MissingAttribute {
+ /// Name of the missing attribute.
+ attribute: String,
+ },
+
+ /// The format of an attribute does not match.
+ FormatMismatch {
+ /// Name of the attribute.
+ attribute: String,
+ /// The format in the vertex shader.
+ shader: ShaderInterfaceEntryType,
+ /// The format in the vertex definition.
+ definition: VertexMemberInfo,
+ },
+}
+
+impl Error for IncompatibleVertexDefinitionError {}
+
+impl Display for IncompatibleVertexDefinitionError {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
+ match self {
+ IncompatibleVertexDefinitionError::MissingAttribute { .. } => {
+ write!(f, "an attribute is missing")
+ }
+ IncompatibleVertexDefinitionError::FormatMismatch { .. } => {
+ write!(f, "the format of an attribute does not match")
+ }
+ }
+ }
+}
+
+unsafe impl VertexDefinition for &[VertexBufferDescription] {
+ #[inline]
+ fn definition(
+ &self,
+ interface: &ShaderInterface,
+ ) -> Result<VertexInputState, IncompatibleVertexDefinitionError> {
+ let bindings = self.iter().enumerate().map(|(binding, buffer)| {
+ (
+ binding as u32,
+ VertexInputBindingDescription {
+ stride: buffer.stride,
+ input_rate: buffer.input_rate,
+ },
+ )
+ });
+ let mut attributes: Vec<(u32, VertexInputAttributeDescription)> = Vec::new();
+
+ for element in interface.elements() {
+ let name = element.name.as_ref().unwrap().clone().into_owned();
+
+ let (infos, binding) = self
+ .iter()
+ .enumerate()
+ .find_map(|(binding, buffer)| {
+ buffer
+ .members
+ .get(&name)
+ .map(|infos| (infos.clone(), binding as u32))
+ })
+ .ok_or_else(||
+ // TODO: move this check to GraphicsPipelineBuilder
+ IncompatibleVertexDefinitionError::MissingAttribute {
+ attribute: name.clone(),
+ })?;
+
+ // TODO: ShaderInterfaceEntryType does not properly support 64bit.
+ // Once it does the below logic around num_elements and num_locations
+ // might have to be updated.
+ if infos.num_components() != element.ty.num_components
+ || infos.num_elements != element.ty.num_locations()
+ {
+ return Err(IncompatibleVertexDefinitionError::FormatMismatch {
+ attribute: name,
+ shader: element.ty,
+ definition: infos,
+ });
+ }
+
+ let mut offset = infos.offset as DeviceSize;
+ let block_size = infos.format.block_size().unwrap();
+ // Double precision formats can exceed a single location.
+ // R64B64G64A64_SFLOAT requires two locations, so we need to adapt how we bind
+ let location_range = if block_size > 16 {
+ (element.location..element.location + 2 * element.ty.num_locations()).step_by(2)
+ } else {
+ (element.location..element.location + element.ty.num_locations()).step_by(1)
+ };
+
+ for location in location_range {
+ attributes.push((
+ location,
+ VertexInputAttributeDescription {
+ binding,
+ format: infos.format,
+ offset: offset as u32,
+ },
+ ));
+ offset += block_size;
+ }
+ }
+
+ Ok(VertexInputState::new()
+ .bindings(bindings)
+ .attributes(attributes))
+ }
+}
+
+unsafe impl<const N: usize> VertexDefinition for [VertexBufferDescription; N] {
+ #[inline]
+ fn definition(
+ &self,
+ interface: &ShaderInterface,
+ ) -> Result<VertexInputState, IncompatibleVertexDefinitionError> {
+ self.as_slice().definition(interface)
+ }
+}
+
+unsafe impl VertexDefinition for Vec<VertexBufferDescription> {
+ #[inline]
+ fn definition(
+ &self,
+ interface: &ShaderInterface,
+ ) -> Result<VertexInputState, IncompatibleVertexDefinitionError> {
+ self.as_slice().definition(interface)
+ }
+}
+
+unsafe impl VertexDefinition for VertexBufferDescription {
+ #[inline]
+ fn definition(
+ &self,
+ interface: &ShaderInterface,
+ ) -> Result<VertexInputState, IncompatibleVertexDefinitionError> {
+ std::slice::from_ref(self).definition(interface)
+ }
+}