Better custom vertex buffer layouts support (#2119)

* Implement VertexDefinition for VertexBufferInfo, change Vertex signature to return VertexInfo and implement VertexInput-trait helper for Vertex to construct VertexBufferInfo objects from Vertex types. Updated teapot example to show usage.

* Reimplement BuffersDefinition leveraging VertexBufferInfo

* Reduce the amount of types required, remove VertexInfo, expose VertexBufferInfo methods on Vertex and use std HashMap transparently instead.

* Fix teapot example

* Fix outdated docs and use full path to Vertex trait to avoid issues

* Fix formatting of imports

* Remove clone and directly instantiate VertexInputBindingDescription.

* Rename VertexBufferInfo to VertexBufferDescription and being update of examples to use it

* Run cargo clippy fix to cleanup imports in examples

* Remove obsolete comment

* cargo fmt
This commit is contained in:
Nik Voss 2023-01-03 18:49:12 +01:00 committed by GitHub
parent 21f850dc99
commit 834cc1eea5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 374 additions and 317 deletions

View File

@ -33,7 +33,7 @@ use vulkano::{
pipeline::{
graphics::{
input_assembly::InputAssemblyState,
vertex_input::{BuffersDefinition, Vertex},
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline,
@ -225,7 +225,7 @@ fn main() {
.unwrap();
let pipeline = GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
.vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
@ -277,7 +277,7 @@ fn main() {
}) {
Ok(r) => r,
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return,
Err(e) => panic!("Failed to recreate swapchain: {:?}", e),
Err(e) => panic!("Failed to recreate swapchain: {e:?}"),
};
swapchain = new_swapchain;
@ -296,7 +296,7 @@ fn main() {
recreate_swapchain = true;
return;
}
Err(e) => panic!("Failed to acquire next image: {:?}", e),
Err(e) => panic!("Failed to acquire next image: {e:?}"),
};
if suboptimal {
@ -385,7 +385,7 @@ fn main() {
previous_frame_end = Some(Box::new(sync::now(device.clone())) as Box<_>);
}
Err(e) => {
println!("Failed to flush future: {:?}", e);
println!("Failed to flush future: {e:?}");
previous_frame_end = Some(Box::new(sync::now(device.clone())) as Box<_>);
}
}

View File

@ -192,7 +192,7 @@ fn main() {
}) {
Ok(r) => r,
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return,
Err(e) => panic!("Failed to recreate swapchain: {:?}", e),
Err(e) => panic!("Failed to recreate swapchain: {e:?}"),
};
swapchain = new_swapchain;
@ -209,7 +209,7 @@ fn main() {
recreate_swapchain = true;
return;
}
Err(e) => panic!("Failed to acquire next image: {:?}", e),
Err(e) => panic!("Failed to acquire next image: {e:?}"),
};
if suboptimal {
@ -289,7 +289,7 @@ fn main() {
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
Err(e) => {
println!("Failed to flush future: {:?}", e);
println!("Failed to flush future: {e:?}");
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
}

View File

@ -24,7 +24,7 @@ use vulkano::{
graphics::{
color_blend::{AttachmentBlend, BlendFactor, BlendOp, ColorBlendState},
input_assembly::InputAssemblyState,
vertex_input::BuffersDefinition,
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline, Pipeline, PipelineBindPoint,
@ -81,7 +81,7 @@ impl AmbientLightingSystem {
let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module");
GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<LightingVertex>())
.vertex_input_state(LightingVertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())

View File

@ -25,7 +25,7 @@ use vulkano::{
graphics::{
color_blend::{AttachmentBlend, BlendFactor, BlendOp, ColorBlendState},
input_assembly::InputAssemblyState,
vertex_input::BuffersDefinition,
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline, Pipeline, PipelineBindPoint,
@ -82,7 +82,7 @@ impl DirectionalLightingSystem {
let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module");
GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<LightingVertex>())
.vertex_input_state(LightingVertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())

View File

@ -25,7 +25,7 @@ use vulkano::{
graphics::{
color_blend::{AttachmentBlend, BlendFactor, BlendOp, ColorBlendState},
input_assembly::InputAssemblyState,
vertex_input::BuffersDefinition,
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline, Pipeline, PipelineBindPoint,
@ -81,7 +81,7 @@ impl PointLightingSystem {
let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module");
GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<LightingVertex>())
.vertex_input_state(LightingVertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())

View File

@ -216,7 +216,7 @@ fn main() {
}) {
Ok(r) => r,
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return,
Err(e) => panic!("Failed to recreate swapchain: {:?}", e),
Err(e) => panic!("Failed to recreate swapchain: {e:?}"),
};
let new_images = new_images
.into_iter()
@ -235,7 +235,7 @@ fn main() {
recreate_swapchain = true;
return;
}
Err(e) => panic!("Failed to acquire next image: {:?}", e),
Err(e) => panic!("Failed to acquire next image: {e:?}"),
};
if suboptimal {
@ -285,7 +285,7 @@ fn main() {
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
Err(e) => {
println!("Failed to flush future: {:?}", e);
println!("Failed to flush future: {e:?}");
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
}

View File

@ -21,7 +21,7 @@ use vulkano::{
graphics::{
depth_stencil::DepthStencilState,
input_assembly::InputAssemblyState,
vertex_input::{BuffersDefinition, Vertex},
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline,
@ -71,7 +71,7 @@ impl TriangleDrawSystem {
let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module");
GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<TriangleVertex>())
.vertex_input_state(TriangleVertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())

View File

@ -146,11 +146,8 @@ fn main() {
.physical_device()
.properties()
.min_uniform_buffer_offset_alignment as usize;
println!(
"Minimum uniform buffer offset alignment: {}",
min_dynamic_align
);
println!("Input: {:?}", data);
println!("Minimum uniform buffer offset alignment: {min_dynamic_align}");
println!("Input: {data:?}");
// Round size up to the next multiple of align.
let align = (size_of::<u32>() + min_dynamic_align - 1) & !(min_dynamic_align - 1);
let aligned_data = {

View File

@ -166,7 +166,7 @@ fn main() {
// local size can lead to significant performance penalty.
let (local_size_x, local_size_y) = match device.physical_device().properties().subgroup_size {
Some(subgroup_size) => {
println!("Subgroup size is {}", subgroup_size);
println!("Subgroup size is {subgroup_size}");
// Most of the subgroup values are divisors of 8
(8, subgroup_size / 8)
@ -179,10 +179,7 @@ fn main() {
}
};
println!(
"Local size will be set to: ({}, {}, 1)",
local_size_x, local_size_y
);
println!("Local size will be set to: ({local_size_x}, {local_size_y}, 1)");
let spec_consts = cs::SpecializationConstants {
red: 0.2,

View File

@ -39,7 +39,7 @@ mod linux {
graphics::{
color_blend::ColorBlendState,
input_assembly::{InputAssemblyState, PrimitiveTopology},
vertex_input::{BuffersDefinition, Vertex},
vertex_input::Vertex,
viewport::{Scissor, Viewport, ViewportState},
},
GraphicsPipeline, Pipeline, PipelineBindPoint,
@ -293,7 +293,7 @@ mod linux {
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => {
return
}
Err(e) => panic!("Failed to recreate swapchain: {:?}", e),
Err(e) => panic!("Failed to recreate swapchain: {e:?}"),
};
swapchain = new_swapchain;
@ -312,7 +312,7 @@ mod linux {
recreate_swapchain = true;
return;
}
Err(e) => panic!("Failed to acquire next image: {:?}", e),
Err(e) => panic!("Failed to acquire next image: {e:?}"),
};
if suboptimal {
@ -375,7 +375,7 @@ mod linux {
previous_frame_end = Some(vulkano::sync::now(device.clone()).boxed());
}
Err(e) => {
println!("Failed to flush future: {:?}", e);
println!("Failed to flush future: {e:?}");
previous_frame_end = Some(vulkano::sync::now(device.clone()).boxed());
}
};
@ -608,7 +608,7 @@ mod linux {
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<MyVertex>())
.vertex_input_state(MyVertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(
InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip),

View File

@ -35,7 +35,7 @@ use vulkano::{
graphics::{
color_blend::ColorBlendState,
input_assembly::{InputAssemblyState, PrimitiveTopology},
vertex_input::{BuffersDefinition, Vertex},
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline, Pipeline, PipelineBindPoint,
@ -326,7 +326,7 @@ fn main() {
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
.vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip))
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
@ -390,7 +390,7 @@ fn main() {
}) {
Ok(r) => r,
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return,
Err(e) => panic!("Failed to recreate swapchain: {:?}", e),
Err(e) => panic!("Failed to recreate swapchain: {e:?}"),
};
swapchain = new_swapchain;
@ -406,7 +406,7 @@ fn main() {
recreate_swapchain = true;
return;
}
Err(e) => panic!("Failed to acquire next image: {:?}", e),
Err(e) => panic!("Failed to acquire next image: {e:?}"),
};
if suboptimal {
@ -466,7 +466,7 @@ fn main() {
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
Err(e) => {
println!("Failed to flush future: {:?}", e);
println!("Failed to flush future: {e:?}");
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
}

View File

@ -33,7 +33,7 @@ use vulkano::{
graphics::{
color_blend::ColorBlendState,
input_assembly::{InputAssemblyState, PrimitiveTopology},
vertex_input::{BuffersDefinition, Vertex},
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline, Pipeline, PipelineBindPoint,
@ -257,7 +257,7 @@ fn main() {
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
.vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip))
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
@ -321,7 +321,7 @@ fn main() {
}) {
Ok(r) => r,
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return,
Err(e) => panic!("Failed to recreate swapchain: {:?}", e),
Err(e) => panic!("Failed to recreate swapchain: {e:?}"),
};
swapchain = new_swapchain;
@ -337,7 +337,7 @@ fn main() {
recreate_swapchain = true;
return;
}
Err(e) => panic!("Failed to acquire next image: {:?}", e),
Err(e) => panic!("Failed to acquire next image: {e:?}"),
};
if suboptimal {
@ -397,7 +397,7 @@ fn main() {
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
Err(e) => {
println!("Failed to flush future: {:?}", e);
println!("Failed to flush future: {e:?}");
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
}

View File

@ -42,7 +42,7 @@ use vulkano::{
graphics::{
color_blend::ColorBlendState,
input_assembly::{InputAssemblyState, PrimitiveTopology},
vertex_input::{BuffersDefinition, Vertex},
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline, Pipeline, PipelineBindPoint,
@ -263,7 +263,7 @@ fn main() {
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
.vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip))
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
@ -334,7 +334,7 @@ fn main() {
}) {
Ok(r) => r,
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return,
Err(e) => panic!("Failed to recreate swapchain: {:?}", e),
Err(e) => panic!("Failed to recreate swapchain: {e:?}"),
};
swapchain = new_swapchain;
@ -350,7 +350,7 @@ fn main() {
recreate_swapchain = true;
return;
}
Err(e) => panic!("Failed to acquire next image: {:?}", e),
Err(e) => panic!("Failed to acquire next image: {e:?}"),
};
if suboptimal {
@ -410,7 +410,7 @@ fn main() {
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
Err(e) => {
println!("Failed to flush future: {:?}", e);
println!("Failed to flush future: {e:?}");
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
}

View File

@ -48,7 +48,7 @@ use vulkano::{
pipeline::{
graphics::{
input_assembly::InputAssemblyState,
vertex_input::{BuffersDefinition, Vertex},
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
ComputePipeline, GraphicsPipeline, Pipeline, PipelineBindPoint,
@ -302,7 +302,7 @@ fn main() {
}
let render_pipeline = GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
.vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
@ -355,7 +355,7 @@ fn main() {
}) {
Ok(r) => r,
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return,
Err(e) => panic!("Failed to recreate swapchain: {:?}", e),
Err(e) => panic!("Failed to recreate swapchain: {e:?}"),
};
swapchain = new_swapchain;
@ -374,7 +374,7 @@ fn main() {
recreate_swapchain = true;
return;
}
Err(e) => panic!("Failed to acquire next image: {:?}", e),
Err(e) => panic!("Failed to acquire next image: {e:?}"),
};
if suboptimal {
@ -470,7 +470,7 @@ fn main() {
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
Err(e) => {
println!("Failed to flush future: {:?}", e);
println!("Failed to flush future: {e:?}");
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
}

View File

@ -30,7 +30,7 @@ use vulkano::{
pipeline::{
graphics::{
input_assembly::InputAssemblyState,
vertex_input::{BuffersDefinition, Vertex},
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline,
@ -290,11 +290,7 @@ fn main() {
let pipeline = GraphicsPipeline::start()
// Use the `BuffersDefinition` to describe to vulkano how the two vertex types
// are expected to be used.
.vertex_input_state(
BuffersDefinition::new()
.vertex::<TriangleVertex>()
.instance::<InstanceData>(),
)
.vertex_input_state([TriangleVertex::per_vertex(), InstanceData::per_instance()])
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
@ -346,7 +342,7 @@ fn main() {
}) {
Ok(r) => r,
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return,
Err(e) => panic!("Failed to recreate swapchain: {:?}", e),
Err(e) => panic!("Failed to recreate swapchain: {e:?}"),
};
swapchain = new_swapchain;
@ -365,7 +361,7 @@ fn main() {
recreate_swapchain = true;
return;
}
Err(e) => panic!("Failed to acquire next image: {:?}", e),
Err(e) => panic!("Failed to acquire next image: {e:?}"),
};
if suboptimal {
@ -425,7 +421,7 @@ fn main() {
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
Err(e) => {
println!("Failed to flush future: {:?}", e);
println!("Failed to flush future: {e:?}");
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
}

View File

@ -138,7 +138,7 @@ fn compute_then_render(
// Start frame
let before_pipeline_future = match renderer.acquire() {
Err(e) => {
println!("{}", e);
println!("{e}");
return;
}
Ok(future) => future,

View File

@ -24,7 +24,7 @@ use vulkano::{
pipeline::{
graphics::{
input_assembly::InputAssemblyState,
vertex_input::{BuffersDefinition, Vertex},
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline, Pipeline, PipelineBindPoint,
@ -106,7 +106,7 @@ impl PixelsDrawPipeline {
let vs = vs::load(gfx_queue.device().clone()).expect("failed to create shader module");
let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module");
GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<TexturedVertex>())
.vertex_input_state(TexturedVertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new())
.fragment_shader(fs.entry_point("main").unwrap(), ())

View File

@ -83,7 +83,7 @@ use vulkano::{
pipeline::{
graphics::{
multisample::MultisampleState,
vertex_input::{BuffersDefinition, Vertex},
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline,
@ -296,7 +296,7 @@ fn main() {
let subpass = Subpass::from(render_pass, 0).unwrap();
let pipeline = GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
.vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.entry_point("main").unwrap(), ())

View File

@ -34,7 +34,7 @@ use vulkano::{
pipeline::{
graphics::{
input_assembly::InputAssemblyState,
vertex_input::{BuffersDefinition, Vertex},
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline,
@ -257,7 +257,7 @@ fn main() {
.unwrap();
let pipeline = GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
.vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
@ -399,7 +399,7 @@ fn main() {
}) {
Ok(r) => r,
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return,
Err(e) => panic!("Failed to recreate swapchain: {:?}", e),
Err(e) => panic!("Failed to recreate swapchain: {e:?}"),
};
*swapchain = new_swapchain;
@ -415,7 +415,7 @@ fn main() {
*recreate_swapchain = true;
return;
}
Err(e) => panic!("Failed to acquire next image: {:?}", e),
Err(e) => panic!("Failed to acquire next image: {e:?}"),
};
if suboptimal {
@ -470,7 +470,7 @@ fn main() {
*previous_frame_end = Some(sync::now(device.clone()).boxed());
}
Err(e) => {
println!("Failed to flush future: {:?}", e);
println!("Failed to flush future: {e:?}");
*previous_frame_end = Some(sync::now(device.clone()).boxed());
}
}

View File

@ -198,7 +198,7 @@ fn compute_then_render(
// Start frame
let before_pipeline_future = match window_renderer.acquire() {
Err(e) => {
println!("{}", e);
println!("{e}");
return;
}
Ok(future) => future,

View File

@ -24,7 +24,7 @@ use vulkano::{
pipeline::{
graphics::{
input_assembly::InputAssemblyState,
vertex_input::{BuffersDefinition, Vertex},
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline, Pipeline, PipelineBindPoint,
@ -106,7 +106,7 @@ impl PixelsDrawPipeline {
let vs = vs::load(gfx_queue.device().clone()).expect("failed to create shader module");
let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module");
GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<TexturedVertex>())
.vertex_input_state(TexturedVertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new())
.fragment_shader(fs.entry_point("main").unwrap(), ())

View File

@ -35,7 +35,7 @@ use vulkano::{
pipeline::{
graphics::{
input_assembly::InputAssemblyState,
vertex_input::{BuffersDefinition, Vertex},
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline,
@ -254,7 +254,7 @@ fn main() {
.unwrap();
let pipeline = GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
.vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_fixed_scissor_irrelevant([

View File

@ -31,7 +31,7 @@ use vulkano::{
graphics::{
depth_stencil::DepthStencilState,
input_assembly::InputAssemblyState,
vertex_input::{BuffersDefinition, Vertex},
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline,
@ -302,7 +302,7 @@ fn main() {
.unwrap();
let pipeline = GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
.vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
@ -363,7 +363,7 @@ fn main() {
}) {
Ok(r) => r,
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return,
Err(e) => panic!("Failed to recreate swapchain: {:?}", e),
Err(e) => panic!("Failed to recreate swapchain: {e:?}"),
};
swapchain = new_swapchain;
@ -383,7 +383,7 @@ fn main() {
recreate_swapchain = true;
return;
}
Err(e) => panic!("Failed to acquire next image: {:?}", e),
Err(e) => panic!("Failed to acquire next image: {e:?}"),
};
if suboptimal {
@ -475,7 +475,7 @@ fn main() {
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
Err(e) => {
println!("Failed to flush future: {:?}", e);
println!("Failed to flush future: {e:?}");
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
}

View File

@ -31,7 +31,7 @@ use vulkano::{
graphics::{
color_blend::ColorBlendState,
input_assembly::{InputAssemblyState, PrimitiveTopology},
vertex_input::{BuffersDefinition, Vertex},
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline, Pipeline, PipelineBindPoint,
@ -252,7 +252,7 @@ fn main() {
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
.vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip))
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
@ -313,7 +313,7 @@ fn main() {
}) {
Ok(r) => r,
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return,
Err(e) => panic!("Failed to recreate swapchain: {:?}", e),
Err(e) => panic!("Failed to recreate swapchain: {e:?}"),
};
swapchain = new_swapchain;
@ -329,7 +329,7 @@ fn main() {
recreate_swapchain = true;
return;
}
Err(e) => panic!("Failed to acquire next image: {:?}", e),
Err(e) => panic!("Failed to acquire next image: {e:?}"),
};
if suboptimal {
@ -389,7 +389,7 @@ fn main() {
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
Err(e) => {
println!("Failed to flush future: {:?}", e);
println!("Failed to flush future: {e:?}");
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
}

View File

@ -38,7 +38,7 @@ use vulkano::{
graphics::{
input_assembly::InputAssemblyState,
rasterization::{CullMode, FrontFace, RasterizationState},
vertex_input::{BuffersDefinition, Vertex},
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline,
@ -195,7 +195,7 @@ fn main() {
};
let graphics_pipeline = GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
.vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
@ -288,7 +288,7 @@ fn main() {
}) {
Ok(r) => r,
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return,
Err(e) => panic!("Failed to recreate swapchain: {:?}", e),
Err(e) => panic!("Failed to recreate swapchain: {e:?}"),
};
swapchain = new_swapchain;
@ -304,7 +304,7 @@ fn main() {
recreate_swapchain = true;
return;
}
Err(e) => panic!("Failed to acquire next image: {:?}", e),
Err(e) => panic!("Failed to acquire next image: {e:?}"),
};
if suboptimal {
@ -358,7 +358,7 @@ fn main() {
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
Err(e) => {
println!("Failed to flush future: {:?}", e);
println!("Failed to flush future: {e:?}");
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
}

View File

@ -36,7 +36,7 @@ use vulkano::{
pipeline::{
graphics::{
color_blend::ColorBlendState,
vertex_input::{BuffersDefinition, Vertex},
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
layout::PipelineLayoutCreateInfo,
@ -384,7 +384,7 @@ fn main() {
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
.vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.entry_point("main").unwrap(), ())
@ -455,7 +455,7 @@ fn main() {
}) {
Ok(r) => r,
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return,
Err(e) => panic!("Failed to recreate swapchain: {:?}", e),
Err(e) => panic!("Failed to recreate swapchain: {e:?}"),
};
swapchain = new_swapchain;
@ -471,7 +471,7 @@ fn main() {
recreate_swapchain = true;
return;
}
Err(e) => panic!("Failed to acquire next image: {:?}", e),
Err(e) => panic!("Failed to acquire next image: {e:?}"),
};
if suboptimal {
@ -531,7 +531,7 @@ fn main() {
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
Err(e) => {
println!("Failed to flush future: {:?}", e);
println!("Failed to flush future: {e:?}");
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
}

View File

@ -131,7 +131,7 @@ fn main() {
let serialized = to_string_pretty(&bar, PrettyConfig::default()).unwrap();
println!("Serialized Bar: {}", serialized);
println!("Serialized Bar: {serialized}");
let deserialized = from_str::<Bar>(&serialized).unwrap();

View File

@ -34,7 +34,7 @@ use vulkano::{
pipeline::{
graphics::{
input_assembly::{InputAssemblyState, PrimitiveTopology},
vertex_input::{BuffersDefinition, Vertex},
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline, PipelineBindPoint,
@ -415,7 +415,7 @@ fn main() {
// Create a basic graphics pipeline for rendering particles.
let graphics_pipeline = GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
.vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::PointList)) // Vertices will be rendered as a list of points.
.viewport_state(ViewportState::viewport_fixed_scissor_irrelevant([viewport]))
@ -464,7 +464,7 @@ fn main() {
None, /*timeout*/
) {
Ok(tuple) => tuple,
Err(e) => panic!("Failed to acquire next image: {:?}", e),
Err(e) => panic!("Failed to acquire next image: {e:?}"),
};
// Since we disallow resizing, assert the swapchain and surface are optimally configured.
@ -542,7 +542,7 @@ fn main() {
Ok(future) => Some(Arc::new(future)),
// Unknown failure.
Err(e) => panic!("Failed to flush future: {:?}", e),
Err(e) => panic!("Failed to flush future: {e:?}"),
};
previous_fence_index = image_index;
}

View File

@ -34,7 +34,7 @@ use vulkano::{
graphics::{
depth_stencil::DepthStencilState,
input_assembly::InputAssemblyState,
vertex_input::BuffersDefinition,
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline, Pipeline, PipelineBindPoint,
@ -258,7 +258,7 @@ fn main() {
}) {
Ok(r) => r,
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return,
Err(e) => panic!("Failed to recreate swapchain: {:?}", e),
Err(e) => panic!("Failed to recreate swapchain: {e:?}"),
};
swapchain = new_swapchain;
@ -321,7 +321,7 @@ fn main() {
recreate_swapchain = true;
return;
}
Err(e) => panic!("Failed to acquire next image: {:?}", e),
Err(e) => panic!("Failed to acquire next image: {e:?}"),
};
if suboptimal {
@ -384,7 +384,7 @@ fn main() {
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
Err(e) => {
println!("Failed to flush future: {:?}", e);
println!("Failed to flush future: {e:?}");
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
}
@ -429,11 +429,7 @@ fn window_size_dependent_setup(
// This allows the driver to optimize things, at the cost of slower window resizes.
// https://computergraphics.stackexchange.com/questions/5742/vulkan-best-way-of-updating-pipeline-viewport
let pipeline = GraphicsPipeline::start()
.vertex_input_state(
BuffersDefinition::new()
.vertex::<Position>()
.vertex::<Normal>(),
)
.vertex_input_state([Position::per_vertex(), Normal::per_vertex()])
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_fixed_scissor_irrelevant([

View File

@ -38,7 +38,7 @@ use vulkano::{
input_assembly::{InputAssemblyState, PrimitiveTopology},
rasterization::{PolygonMode, RasterizationState},
tessellation::TessellationState,
vertex_input::{BuffersDefinition, Vertex},
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline,
@ -329,7 +329,7 @@ fn main() {
.unwrap();
let pipeline = GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
.vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
// Actually use the tessellation shaders.
.tessellation_shaders(
@ -394,7 +394,7 @@ fn main() {
}) {
Ok(r) => r,
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return,
Err(e) => panic!("Failed to recreate swapchain: {:?}", e),
Err(e) => panic!("Failed to recreate swapchain: {e:?}"),
};
swapchain = new_swapchain;
@ -410,7 +410,7 @@ fn main() {
recreate_swapchain = true;
return;
}
Err(e) => panic!("Failed to acquire next image: {:?}", e),
Err(e) => panic!("Failed to acquire next image: {e:?}"),
};
if suboptimal {
@ -464,7 +464,7 @@ fn main() {
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
Err(e) => {
println!("Failed to flush future: {:?}", e);
println!("Failed to flush future: {e:?}");
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
}

View File

@ -33,7 +33,7 @@ use vulkano::{
graphics::{
color_blend::ColorBlendState,
input_assembly::{InputAssemblyState, PrimitiveTopology},
vertex_input::{BuffersDefinition, Vertex},
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline, Pipeline, PipelineBindPoint,
@ -258,7 +258,7 @@ fn main() {
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
.vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip))
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
@ -322,7 +322,7 @@ fn main() {
}) {
Ok(r) => r,
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return,
Err(e) => panic!("Failed to recreate swapchain: {:?}", e),
Err(e) => panic!("Failed to recreate swapchain: {e:?}"),
};
swapchain = new_swapchain;
@ -338,7 +338,7 @@ fn main() {
recreate_swapchain = true;
return;
}
Err(e) => panic!("Failed to acquire next image: {:?}", e),
Err(e) => panic!("Failed to acquire next image: {e:?}"),
};
if suboptimal {
@ -398,7 +398,7 @@ fn main() {
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
Err(e) => {
println!("Failed to flush future: {:?}", e);
println!("Failed to flush future: {e:?}");
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
}

View File

@ -40,7 +40,7 @@ use vulkano::{
graphics::{
input_assembly::InputAssemblyState,
render_pass::PipelineRenderingCreateInfo,
vertex_input::{BuffersDefinition, Vertex},
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline,
@ -375,7 +375,7 @@ fn main() {
..Default::default()
})
// We need to indicate the layout of the vertices.
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
.vertex_input_state(Vertex::per_vertex())
// The content of the vertex buffer describes a list of triangles.
.input_assembly_state(InputAssemblyState::new())
// A Vulkan shader can in theory contain multiple entry points, so we have to specify
@ -467,7 +467,7 @@ fn main() {
// This error tends to happen when the user is manually resizing the window.
// Simply restarting the loop is the easiest way to fix this issue.
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return,
Err(e) => panic!("Failed to recreate swapchain: {:?}", e),
Err(e) => panic!("Failed to recreate swapchain: {e:?}"),
};
swapchain = new_swapchain;
@ -492,7 +492,7 @@ fn main() {
recreate_swapchain = true;
return;
}
Err(e) => panic!("Failed to acquire next image: {:?}", e),
Err(e) => panic!("Failed to acquire next image: {e:?}"),
};
// acquire_next_image can be successful, but suboptimal. This means that the swapchain image
@ -590,7 +590,7 @@ fn main() {
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
Err(e) => {
println!("Failed to flush future: {:?}", e);
println!("Failed to flush future: {e:?}");
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
}

View File

@ -34,7 +34,7 @@ use vulkano::{
pipeline::{
graphics::{
input_assembly::InputAssemblyState,
vertex_input::{BuffersDefinition, Vertex},
vertex_input::Vertex,
viewport::{Viewport, ViewportState},
},
GraphicsPipeline,
@ -383,7 +383,7 @@ fn main() {
// in. The pipeline will only be usable from this particular subpass.
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
// We need to indicate the layout of the vertices.
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
.vertex_input_state(Vertex::per_vertex())
// The content of the vertex buffer describes a list of triangles.
.input_assembly_state(InputAssemblyState::new())
// A Vulkan shader can in theory contain multiple entry points, so we have to specify
@ -482,7 +482,7 @@ fn main() {
// This error tends to happen when the user is manually resizing the window.
// Simply restarting the loop is the easiest way to fix this issue.
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return,
Err(e) => panic!("Failed to recreate swapchain: {:?}", e),
Err(e) => panic!("Failed to recreate swapchain: {e:?}"),
};
swapchain = new_swapchain;
@ -510,7 +510,7 @@ fn main() {
recreate_swapchain = true;
return;
}
Err(e) => panic!("Failed to acquire next image: {:?}", e),
Err(e) => panic!("Failed to acquire next image: {e:?}"),
};
// acquire_next_image can be successful, but suboptimal. This means that the swapchain image
@ -601,7 +601,7 @@ fn main() {
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
Err(e) => {
panic!("Failed to flush future: {:?}", e);
panic!("Failed to flush future: {e:?}");
// previous_frame_end = Some(sync::now(device.clone()).boxed());
}
}

View File

@ -41,14 +41,16 @@ pub fn derive_vertex(ast: syn::DeriveInput) -> Result<TokenStream> {
FoundCrate::Name(name) => Ident::new(&name, Span::call_site()),
};
let mut member_cases = quote! {
let mut members = quote! {
let mut offset = 0;
let mut members = HashMap::default();
};
for field in fields.iter() {
let field_name = field.ident.to_owned().unwrap();
let field_name_lit = LitStr::new(&field_name.to_string(), Span::call_site());
let field_ty = &field.ty;
let mut names = vec![LitStr::new(&field_name.to_string(), Span::call_site())];
let mut names = vec![field_name_lit.clone()];
let mut format = quote! {};
for attr in &field.attrs {
let attr_ident = if let Some(ident) = attr.path.get_ident() {
@ -73,42 +75,61 @@ pub fn derive_vertex(ast: syn::DeriveInput) -> Result<TokenStream> {
));
}
for name in &names {
member_cases = quote! {
#member_cases
members = quote! {
#members
let field_size = std::mem::size_of::<#field_ty>() as u32;
if name == #name {
{
#format
let format_size = format.block_size().expect("no block size for format") as u32;
let num_elements = field_size / format_size;
let remainder = field_size % format_size;
assert!(remainder == 0, "struct field `{}` size does not fit multiple of format size", name);
return Some(VertexMemberInfo {
offset,
format,
num_elements,
});
assert!(remainder == 0, "struct field `{}` size does not fit multiple of format size", #field_name_lit);
members.insert(
#name.to_string(),
VertexMemberInfo {
offset,
format,
num_elements,
},
);
}
offset += field_size as usize;
};
}
}
Ok(TokenStream::from(quote! {
#[allow(unsafe_code)]
unsafe impl #crate_ident::pipeline::graphics::vertex_input::Vertex for #struct_name {
#[inline(always)]
fn member(name: &str) -> Option<#crate_ident::pipeline::graphics::vertex_input::VertexMemberInfo> {
#[allow(unused_imports)]
use #crate_ident::format::Format;
use #crate_ident::pipeline::graphics::vertex_input::VertexMemberInfo;
use #crate_ident::pipeline::graphics::vertex_input::VertexMember;
let function_body = quote! {
#[allow(unused_imports)]
use std::collections::HashMap;
use #crate_ident::format::Format;
use #crate_ident::pipeline::graphics::vertex_input::{VertexInputRate, VertexMemberInfo};
#member_cases
#members
None
}
#crate_ident::pipeline::graphics::vertex_input::VertexBufferDescription {
members,
stride: std::mem::size_of::<#struct_name>() as u32,
input_rate: VertexInputRate::Vertex,
}
};
Ok(TokenStream::from(quote! {
#[allow(unsafe_code)]
unsafe impl #crate_ident::pipeline::graphics::vertex_input::Vertex for #struct_name {
#[inline(always)]
fn per_vertex() -> #crate_ident::pipeline::graphics::vertex_input::VertexBufferDescription {
#function_body
}
#[inline(always)]
fn per_instance() -> #crate_ident::pipeline::graphics::vertex_input::VertexBufferDescription {
#function_body.per_instance()
}
#[inline(always)]
fn per_instance_with_divisor(divisor: u32) -> #crate_ident::pipeline::graphics::vertex_input::VertexBufferDescription {
#function_body.per_instance_with_divisor(divisor)
}
}
}))
}

View File

@ -71,14 +71,19 @@
//! use vulkano::command_buffer::PrimaryCommandBufferAbstract;
//! use vulkano::command_buffer::SubpassContents;
//!
//! # use vulkano::pipeline::graphics::vertex_input::Vertex;
//! # use bytemuck::{Pod, Zeroable};
//!
//! # #[repr(C)]
//! # #[derive(Clone, Copy, Debug, Default, bytemuck::Zeroable, bytemuck::Pod)]
//! # struct Vertex { position: [f32; 3] };
//! # vulkano::impl_vertex!(Vertex, position);
//! # #[derive(Clone, Copy, Debug, Default, Zeroable, Pod, Vertex)]
//! # struct PosVertex {
//! # #[format(R32G32B32_SFLOAT)]
//! # position: [f32; 3]
//! # };
//! # use vulkano::buffer::TypedBufferAccess;
//! # let device: std::sync::Arc<vulkano::device::Device> = return;
//! # let queue: std::sync::Arc<vulkano::device::Queue> = return;
//! # let vertex_buffer: std::sync::Arc<vulkano::buffer::CpuAccessibleBuffer<[Vertex]>> = return;
//! # let vertex_buffer: std::sync::Arc<vulkano::buffer::CpuAccessibleBuffer<[PosVertex]>> = return;
//! # let render_pass_begin_info: vulkano::command_buffer::RenderPassBeginInfo = return;
//! # let graphics_pipeline: std::sync::Arc<vulkano::pipeline::graphics::GraphicsPipeline> = return;
//! # let command_buffer_allocator: vulkano::command_buffer::allocator::StandardCommandBufferAllocator = return;

View File

@ -25,8 +25,8 @@ use super::{
render_pass::{PipelineRenderPassType, PipelineRenderingCreateInfo},
tessellation::TessellationState,
vertex_input::{
BuffersDefinition, Vertex, VertexDefinition, VertexInputAttributeDescription,
VertexInputBindingDescription, VertexInputState,
VertexDefinition, VertexInputAttributeDescription, VertexInputBindingDescription,
VertexInputState,
},
viewport::{Scissor, Viewport, ViewportState},
GraphicsPipeline, GraphicsPipelineCreationError,
@ -3997,29 +3997,6 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss>
self
}
/// Sets the vertex input to a single vertex buffer.
///
/// You will most likely need to explicitly specify the template parameter to the type of a
/// vertex.
#[deprecated(since = "0.27.0", note = "Use `vertex_input_state` instead")]
pub fn vertex_input_single_buffer<V: Vertex>(
self,
) -> GraphicsPipelineBuilder<
'vs,
'tcs,
'tes,
'gs,
'fs,
BuffersDefinition,
Vss,
Tcss,
Tess,
Gss,
Fss,
> {
self.vertex_input_state(BuffersDefinition::new().vertex::<V>())
}
/// Sets whether primitive restart is enabled.
#[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")]
#[inline]

View File

@ -7,50 +7,23 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
use super::VertexMemberInfo;
use super::VertexBufferDescription;
use crate::{
pipeline::graphics::vertex_input::{
IncompatibleVertexDefinitionError, Vertex, VertexDefinition,
VertexInputAttributeDescription, VertexInputBindingDescription, VertexInputRate,
VertexInputState,
IncompatibleVertexDefinitionError, Vertex, VertexDefinition, VertexInputState,
},
shader::ShaderInterface,
DeviceSize,
};
use std::{
fmt::{Debug, Error as FmtError, Formatter},
mem,
};
/// A vertex definition for any number of vertex and instance buffers.
#[deprecated(
since = "0.33.0",
note = "Use `VertexBufferDescription` directly instead as returned by `Vertex::per_vertex` or `Vertex::per_instance`"
)]
#[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,
}
}
}
pub struct BuffersDefinition(Vec<VertexBufferDescription>);
#[allow(deprecated)]
impl BuffersDefinition {
/// Constructs a new definition.
#[inline]
@ -60,23 +33,13 @@ impl BuffersDefinition {
/// Adds a new vertex buffer containing elements of type `V` to the definition.
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.0.push(V::per_vertex());
self
}
/// Adds a new instance buffer containing elements of type `V` to the definition.
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.0.push(V::per_instance());
self
}
@ -92,83 +55,18 @@ impl BuffersDefinition {
/// [`vertex_attribute_instance_rate_divisor`]: crate::device::Features::vertex_attribute_instance_rate_divisor
/// [`vertex_attribute_instance_rate_zero_divisor`]: crate::device::Features::vertex_attribute_instance_rate_zero_divisor
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.0.push(V::per_instance_with_divisor(divisor));
self
}
}
#[allow(deprecated)]
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(||
// TODO: move this check to GraphicsPipelineBuilder
IncompatibleVertexDefinitionError::MissingAttribute {
attribute: name.clone().into_owned(),
})?;
// 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.clone().into_owned(),
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))
self.0.definition(interface)
}
}

View File

@ -50,9 +50,13 @@
//! # }
//! ```
use super::{
VertexBufferDescription, VertexInputAttributeDescription, VertexInputBindingDescription,
};
use crate::{
pipeline::graphics::vertex_input::{VertexInputState, VertexMemberInfo},
shader::{ShaderInterface, ShaderInterfaceEntryType},
DeviceSize,
};
use std::{
error::Error,
@ -113,3 +117,110 @@ impl Display for IncompatibleVertexDefinitionError {
}
}
}
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)
}
}

View File

@ -35,14 +35,16 @@ macro_rules! impl_vertex {
unsafe impl $crate::pipeline::graphics::vertex_input::Vertex for $out {
#[inline(always)]
#[allow(deprecated)]
fn member(name: &str) -> Option<$crate::pipeline::graphics::vertex_input::VertexMemberInfo> {
fn per_vertex() -> $crate::pipeline::graphics::vertex_input::VertexBufferDescription {
#[allow(unused_imports)]
use std::collections::HashMap;
use $crate::format::Format;
use $crate::pipeline::graphics::vertex_input::VertexMemberInfo;
use $crate::pipeline::graphics::vertex_input::VertexMember;
use $crate::pipeline::graphics::vertex_input::{VertexInputRate, VertexMemberInfo};
let mut members = HashMap::default();
$(
if name == stringify!($member) {
{
let dummy = <$out>::default();
#[inline] fn f<T: VertexMember>(_: &T) -> Format { T::format() }
let format = f(&dummy.$member);
@ -58,12 +60,12 @@ macro_rules! impl_vertex {
let format_size = format.block_size().expect("no block size for format") as u32;
let num_elements = field_size / format_size;
let remainder = field_size % format_size;
assert!(remainder == 0, "struct field `{}` size does not fit multiple of format size", name);
assert!(remainder == 0, "struct field `{}` size does not fit multiple of format size", stringify!($member));
let dummy_ptr = (&dummy) as *const _;
let member_ptr = (&dummy.$member) as *const _;
return Some(VertexMemberInfo {
members.insert(stringify!($member).to_string(), VertexMemberInfo {
offset: member_ptr as usize - dummy_ptr as usize,
format,
num_elements,
@ -71,8 +73,23 @@ macro_rules! impl_vertex {
}
)*
None
$crate::pipeline::graphics::vertex_input::VertexBufferDescription {
members,
stride: std::mem::size_of::<$out>() as u32,
input_rate: VertexInputRate::Vertex,
}
}
#[inline(always)]
#[allow(deprecated)]
fn per_instance() -> $crate::pipeline::graphics::vertex_input::VertexBufferDescription {
<$out as $crate::pipeline::graphics::vertex_input::Vertex>::per_vertex().per_instance()
}
#[inline(always)]
#[allow(deprecated)]
fn per_instance_with_divisor(divisor: u32) -> $crate::pipeline::graphics::vertex_input::VertexBufferDescription {
<$out as $crate::pipeline::graphics::vertex_input::Vertex>::per_vertex().per_instance_with_divisor(divisor)
}
}
)
}
@ -223,9 +240,10 @@ mod tests {
}
impl_vertex!(TestVertex, scalar, vector, matrix);
let matrix = TestVertex::member("matrix").unwrap();
let vector = TestVertex::member("vector").unwrap();
let scalar = TestVertex::member("scalar").unwrap();
let info = TestVertex::per_vertex();
let matrix = info.members.get("matrix").unwrap();
let vector = info.members.get("vector").unwrap();
let scalar = info.members.get("scalar").unwrap();
assert_eq!(matrix.format, Format::R32G32B32A32_SFLOAT);
assert_eq!(matrix.offset, 0);
assert_eq!(matrix.num_elements, 4);

View File

@ -104,7 +104,7 @@ pub use self::{
collection::VertexBuffersCollection,
definition::{IncompatibleVertexDefinitionError, VertexDefinition},
impl_vertex::VertexMember,
vertex::{Vertex, VertexMemberInfo},
vertex::{Vertex, VertexBufferDescription, VertexMemberInfo},
};
use crate::format::Format;
use ahash::HashMap;

View File

@ -7,8 +7,10 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
use super::VertexInputRate;
use crate::format::Format;
use bytemuck::Pod;
use std::collections::HashMap;
pub use vulkano_macros::Vertex;
/// Describes an individual `Vertex`. In other words a collection of attributes that can be read
@ -37,14 +39,51 @@ pub use vulkano_macros::Vertex;
/// }
/// ```
pub unsafe trait Vertex: Pod + Send + Sync + 'static {
/// Returns the characteristics of a vertex member by its name.
fn member(name: &str) -> Option<VertexMemberInfo>;
/// Returns the information about this Vertex type.
fn per_vertex() -> VertexBufferDescription;
fn per_instance() -> VertexBufferDescription;
fn per_instance_with_divisor(divisor: u32) -> VertexBufferDescription;
}
unsafe impl Vertex for () {
/// Describes the contents of a VertexBuffer.
#[derive(Clone, Debug)]
pub struct VertexBufferDescription {
/// List of member names with their detailed information.
pub members: HashMap<String, VertexMemberInfo>,
/// Stride of the vertex type in a buffer.
pub stride: u32,
/// How the vertex buffer is unrolled in the shader.
pub input_rate: VertexInputRate,
}
impl VertexBufferDescription {
#[inline]
fn member(_: &str) -> Option<VertexMemberInfo> {
None
pub fn per_vertex(self) -> VertexBufferDescription {
let VertexBufferDescription {
members, stride, ..
} = self;
VertexBufferDescription {
members,
stride,
input_rate: VertexInputRate::Vertex,
}
}
#[inline]
pub fn per_instance(self) -> VertexBufferDescription {
self.per_instance_with_divisor(1)
}
#[inline]
pub fn per_instance_with_divisor(self, divisor: u32) -> VertexBufferDescription {
let VertexBufferDescription {
members, stride, ..
} = self;
VertexBufferDescription {
members,
stride,
input_rate: VertexInputRate::Instance { divisor },
}
}
}
@ -87,8 +126,9 @@ mod tests {
a: [f32; 16],
}
let b = TestVertex::member("b").unwrap();
let c = TestVertex::member("c").unwrap();
let info = TestVertex::per_vertex();
let b = info.members.get("b").unwrap();
let c = info.members.get("c").unwrap();
assert_eq!(b.format, Format::R32G32B32A32_SFLOAT);
assert_eq!(c.format, Format::R32G32B32A32_SFLOAT);
assert_eq!(b.num_elements, 4);
@ -104,7 +144,8 @@ mod tests {
unorm: u8,
}
let unorm = TestVertex::member("unorm").unwrap();
let info = TestVertex::per_instance();
let unorm = info.members.get("unorm").unwrap();
assert_eq!(unorm.format, Format::R8_UNORM);
assert_eq!(unorm.num_elements, 1);
}