mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-21 22:34:43 +00:00
Improve validation for vertex input, add dynamic state (#2417)
This commit is contained in:
parent
a17d8f5cb5
commit
80e4afe073
@ -24,6 +24,7 @@ use crate::{
|
|||||||
input_assembly::PrimitiveTopology,
|
input_assembly::PrimitiveTopology,
|
||||||
rasterization::{CullMode, DepthBiasState, FrontFace, LineStipple},
|
rasterization::{CullMode, DepthBiasState, FrontFace, LineStipple},
|
||||||
subpass::PipelineRenderingCreateInfo,
|
subpass::PipelineRenderingCreateInfo,
|
||||||
|
vertex_input::VertexInputState,
|
||||||
viewport::{Scissor, Viewport},
|
viewport::{Scissor, Viewport},
|
||||||
},
|
},
|
||||||
ComputePipeline, DynamicState, GraphicsPipeline, PipelineBindPoint, PipelineLayout,
|
ComputePipeline, DynamicState, GraphicsPipeline, PipelineBindPoint, PipelineLayout,
|
||||||
@ -1318,6 +1319,7 @@ pub(in crate::command_buffer) struct CommandBufferBuilderState {
|
|||||||
pub(in crate::command_buffer) stencil_reference: StencilStateDynamic,
|
pub(in crate::command_buffer) stencil_reference: StencilStateDynamic,
|
||||||
pub(in crate::command_buffer) stencil_test_enable: Option<bool>,
|
pub(in crate::command_buffer) stencil_test_enable: Option<bool>,
|
||||||
pub(in crate::command_buffer) stencil_write_mask: StencilStateDynamic,
|
pub(in crate::command_buffer) stencil_write_mask: StencilStateDynamic,
|
||||||
|
pub(in crate::command_buffer) vertex_input: Option<VertexInputState>,
|
||||||
pub(in crate::command_buffer) viewport: HashMap<u32, Viewport>,
|
pub(in crate::command_buffer) viewport: HashMap<u32, Viewport>,
|
||||||
pub(in crate::command_buffer) viewport_with_count: Option<SmallVec<[Viewport; 2]>>,
|
pub(in crate::command_buffer) viewport_with_count: Option<SmallVec<[Viewport; 2]>>,
|
||||||
|
|
||||||
@ -1369,7 +1371,7 @@ impl CommandBufferBuilderState {
|
|||||||
DynamicState::StencilReference => self.stencil_reference = Default::default(),
|
DynamicState::StencilReference => self.stencil_reference = Default::default(),
|
||||||
DynamicState::StencilTestEnable => self.stencil_test_enable = None,
|
DynamicState::StencilTestEnable => self.stencil_test_enable = None,
|
||||||
DynamicState::StencilWriteMask => self.stencil_write_mask = Default::default(),
|
DynamicState::StencilWriteMask => self.stencil_write_mask = Default::default(),
|
||||||
// DynamicState::VertexInput => todo!(),
|
DynamicState::VertexInput => self.vertex_input = None,
|
||||||
// DynamicState::VertexInputBindingStride => todo!(),
|
// DynamicState::VertexInputBindingStride => todo!(),
|
||||||
DynamicState::Viewport => self.viewport.clear(),
|
DynamicState::Viewport => self.viewport.clear(),
|
||||||
// DynamicState::ViewportCoarseSampleOrder => todo!(),
|
// DynamicState::ViewportCoarseSampleOrder => todo!(),
|
||||||
|
@ -13,10 +13,10 @@ use crate::{
|
|||||||
graphics::vertex_input::VertexBuffersCollection, ComputePipeline, GraphicsPipeline,
|
graphics::vertex_input::VertexBuffersCollection, ComputePipeline, GraphicsPipeline,
|
||||||
PipelineBindPoint, PipelineLayout,
|
PipelineBindPoint, PipelineLayout,
|
||||||
},
|
},
|
||||||
DeviceSize, Requires, RequiresAllOf, RequiresOneOf, ValidationError, VulkanObject,
|
DeviceSize, Requires, RequiresAllOf, RequiresOneOf, ValidationError, Version, VulkanObject,
|
||||||
};
|
};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{cmp::min, ffi::c_void, mem::size_of, sync::Arc};
|
use std::{cmp::min, ffi::c_void, mem::size_of, ptr, sync::Arc};
|
||||||
|
|
||||||
/// # Commands to bind or push state for pipeline execution commands.
|
/// # Commands to bind or push state for pipeline execution commands.
|
||||||
///
|
///
|
||||||
@ -954,19 +954,58 @@ impl UnsafeCommandBufferBuilder {
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (buffers_vk, offsets_vk): (SmallVec<[_; 2]>, SmallVec<[_; 2]>) = vertex_buffers
|
let device = self.device();
|
||||||
.iter()
|
|
||||||
.map(|buffer| (buffer.buffer().handle(), buffer.offset()))
|
|
||||||
.unzip();
|
|
||||||
|
|
||||||
let fns = self.device().fns();
|
if device.api_version() >= Version::V1_3
|
||||||
(fns.v1_0.cmd_bind_vertex_buffers)(
|
|| device.enabled_extensions().ext_extended_dynamic_state
|
||||||
self.handle(),
|
|| device.enabled_extensions().ext_shader_object
|
||||||
first_binding,
|
{
|
||||||
buffers_vk.len() as u32,
|
let mut buffers_vk: SmallVec<[_; 2]> = SmallVec::with_capacity(vertex_buffers.len());
|
||||||
buffers_vk.as_ptr(),
|
let mut offsets_vk: SmallVec<[_; 2]> = SmallVec::with_capacity(vertex_buffers.len());
|
||||||
offsets_vk.as_ptr(),
|
let mut sizes_vk: SmallVec<[_; 2]> = SmallVec::with_capacity(vertex_buffers.len());
|
||||||
);
|
|
||||||
|
for buffer in vertex_buffers {
|
||||||
|
buffers_vk.push(buffer.buffer().handle());
|
||||||
|
offsets_vk.push(buffer.offset());
|
||||||
|
sizes_vk.push(buffer.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
let fns = self.device().fns();
|
||||||
|
let cmd_bind_vertex_buffers2 = if device.api_version() >= Version::V1_3 {
|
||||||
|
fns.v1_3.cmd_bind_vertex_buffers2
|
||||||
|
} else if device.enabled_extensions().ext_extended_dynamic_state {
|
||||||
|
fns.ext_extended_dynamic_state.cmd_bind_vertex_buffers2_ext
|
||||||
|
} else {
|
||||||
|
fns.ext_shader_object.cmd_bind_vertex_buffers2_ext
|
||||||
|
};
|
||||||
|
|
||||||
|
cmd_bind_vertex_buffers2(
|
||||||
|
self.handle(),
|
||||||
|
first_binding,
|
||||||
|
buffers_vk.len() as u32,
|
||||||
|
buffers_vk.as_ptr(),
|
||||||
|
offsets_vk.as_ptr(),
|
||||||
|
sizes_vk.as_ptr(),
|
||||||
|
ptr::null(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let mut buffers_vk: SmallVec<[_; 2]> = SmallVec::with_capacity(vertex_buffers.len());
|
||||||
|
let mut offsets_vk: SmallVec<[_; 2]> = SmallVec::with_capacity(vertex_buffers.len());
|
||||||
|
|
||||||
|
for buffer in vertex_buffers {
|
||||||
|
buffers_vk.push(buffer.buffer().handle());
|
||||||
|
offsets_vk.push(buffer.offset());
|
||||||
|
}
|
||||||
|
|
||||||
|
let fns = self.device().fns();
|
||||||
|
(fns.v1_0.cmd_bind_vertex_buffers)(
|
||||||
|
self.handle(),
|
||||||
|
first_binding,
|
||||||
|
buffers_vk.len() as u32,
|
||||||
|
buffers_vk.as_ptr(),
|
||||||
|
offsets_vk.as_ptr(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,10 @@ use crate::{
|
|||||||
depth_stencil::{CompareOp, StencilFaces, StencilOp, StencilOps},
|
depth_stencil::{CompareOp, StencilFaces, StencilOp, StencilOps},
|
||||||
input_assembly::PrimitiveTopology,
|
input_assembly::PrimitiveTopology,
|
||||||
rasterization::{CullMode, DepthBiasState, FrontFace, LineStipple},
|
rasterization::{CullMode, DepthBiasState, FrontFace, LineStipple},
|
||||||
|
vertex_input::{
|
||||||
|
VertexInputAttributeDescription, VertexInputBindingDescription, VertexInputRate,
|
||||||
|
VertexInputState,
|
||||||
|
},
|
||||||
viewport::{Scissor, Viewport},
|
viewport::{Scissor, Viewport},
|
||||||
},
|
},
|
||||||
DynamicState,
|
DynamicState,
|
||||||
@ -1069,6 +1073,46 @@ impl<L> AutoCommandBufferBuilder<L> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the dynamic vertex input for future draw calls.
|
||||||
|
#[inline]
|
||||||
|
pub fn set_vertex_input(
|
||||||
|
&mut self,
|
||||||
|
vertex_input_state: VertexInputState,
|
||||||
|
) -> Result<&mut Self, Box<ValidationError>> {
|
||||||
|
self.validate_set_vertex_input(&vertex_input_state)?;
|
||||||
|
|
||||||
|
unsafe { Ok(self.set_vertex_input_unchecked(vertex_input_state)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_set_vertex_input(
|
||||||
|
&self,
|
||||||
|
vertex_input_state: &VertexInputState,
|
||||||
|
) -> Result<(), Box<ValidationError>> {
|
||||||
|
self.inner.validate_set_vertex_input(vertex_input_state)?;
|
||||||
|
|
||||||
|
self.validate_graphics_pipeline_fixed_state(DynamicState::VertexInput)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
|
||||||
|
pub unsafe fn set_vertex_input_unchecked(
|
||||||
|
&mut self,
|
||||||
|
vertex_input_state: VertexInputState,
|
||||||
|
) -> &mut Self {
|
||||||
|
self.builder_state.vertex_input = Some(vertex_input_state.clone());
|
||||||
|
|
||||||
|
self.add_command(
|
||||||
|
"set_vertex_input",
|
||||||
|
Default::default(),
|
||||||
|
move |out: &mut UnsafeCommandBufferBuilder| {
|
||||||
|
out.set_vertex_input_unchecked(&vertex_input_state);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the dynamic viewports for future draw calls.
|
/// Sets the dynamic viewports for future draw calls.
|
||||||
pub fn set_viewport(
|
pub fn set_viewport(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -2837,6 +2881,121 @@ impl UnsafeCommandBufferBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn set_vertex_input(
|
||||||
|
&mut self,
|
||||||
|
vertex_input_state: &VertexInputState,
|
||||||
|
) -> Result<&mut Self, Box<ValidationError>> {
|
||||||
|
self.validate_set_vertex_input(vertex_input_state)?;
|
||||||
|
|
||||||
|
Ok(self.set_vertex_input_unchecked(vertex_input_state))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_set_vertex_input(
|
||||||
|
&self,
|
||||||
|
vertex_input_state: &VertexInputState,
|
||||||
|
) -> Result<(), Box<ValidationError>> {
|
||||||
|
if !(self.device().enabled_features().vertex_input_dynamic_state
|
||||||
|
|| self.device().enabled_features().shader_object)
|
||||||
|
{
|
||||||
|
return Err(Box::new(ValidationError {
|
||||||
|
requires_one_of: RequiresOneOf(&[
|
||||||
|
RequiresAllOf(&[Requires::Feature("vertex_input_dynamic_state")]),
|
||||||
|
RequiresAllOf(&[Requires::Feature("shader_object")]),
|
||||||
|
]),
|
||||||
|
vuids: &["VUID-vkCmdSetVertexInputEXT-None-08546"],
|
||||||
|
..Default::default()
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self
|
||||||
|
.queue_family_properties()
|
||||||
|
.queue_flags
|
||||||
|
.intersects(QueueFlags::GRAPHICS)
|
||||||
|
{
|
||||||
|
return Err(Box::new(ValidationError {
|
||||||
|
problem: "the queue family of the command buffer does not support \
|
||||||
|
graphics operations"
|
||||||
|
.into(),
|
||||||
|
vuids: &["VUID-vkCmdSetVertexInputEXT-commandBuffer-cmdpool"],
|
||||||
|
..Default::default()
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex_input_state
|
||||||
|
.validate(self.device())
|
||||||
|
.map_err(|err| err.add_context("vertex_input_state"))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
|
||||||
|
pub unsafe fn set_vertex_input_unchecked(
|
||||||
|
&mut self,
|
||||||
|
vertex_input_state: &VertexInputState,
|
||||||
|
) -> &mut Self {
|
||||||
|
let mut vertex_binding_descriptions_vk: SmallVec<[_; 8]> = SmallVec::new();
|
||||||
|
let mut vertex_attribute_descriptions_vk: SmallVec<[_; 8]> = SmallVec::new();
|
||||||
|
|
||||||
|
let VertexInputState {
|
||||||
|
bindings,
|
||||||
|
attributes,
|
||||||
|
_ne: _,
|
||||||
|
} = vertex_input_state;
|
||||||
|
|
||||||
|
vertex_binding_descriptions_vk.extend(bindings.iter().map(|(&binding, binding_desc)| {
|
||||||
|
let &VertexInputBindingDescription {
|
||||||
|
stride,
|
||||||
|
input_rate,
|
||||||
|
_ne: _,
|
||||||
|
} = binding_desc;
|
||||||
|
|
||||||
|
let divisor = match input_rate {
|
||||||
|
// VUID-VkVertexInputBindingDescription2EXT-divisor-06227
|
||||||
|
VertexInputRate::Vertex => 1,
|
||||||
|
VertexInputRate::Instance { divisor } => divisor,
|
||||||
|
};
|
||||||
|
|
||||||
|
ash::vk::VertexInputBindingDescription2EXT {
|
||||||
|
binding,
|
||||||
|
stride,
|
||||||
|
input_rate: input_rate.into(),
|
||||||
|
divisor,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
vertex_attribute_descriptions_vk.extend(attributes.iter().map(
|
||||||
|
|(&location, attribute_desc)| {
|
||||||
|
let &VertexInputAttributeDescription {
|
||||||
|
binding,
|
||||||
|
format,
|
||||||
|
offset,
|
||||||
|
_ne: _,
|
||||||
|
} = attribute_desc;
|
||||||
|
|
||||||
|
ash::vk::VertexInputAttributeDescription2EXT {
|
||||||
|
location,
|
||||||
|
binding,
|
||||||
|
format: format.into(),
|
||||||
|
offset,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
|
let fns = self.device().fns();
|
||||||
|
(fns.ext_vertex_input_dynamic_state.cmd_set_vertex_input_ext)(
|
||||||
|
self.handle(),
|
||||||
|
vertex_binding_descriptions_vk.len() as u32,
|
||||||
|
vertex_binding_descriptions_vk.as_ptr(),
|
||||||
|
vertex_attribute_descriptions_vk.len() as u32,
|
||||||
|
vertex_attribute_descriptions_vk.as_ptr(),
|
||||||
|
);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn set_viewport(
|
pub unsafe fn set_viewport(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -16,8 +16,9 @@ use crate::{
|
|||||||
image::{sampler::Sampler, view::ImageView, ImageAspects, ImageLayout, SampleCount},
|
image::{sampler::Sampler, view::ImageView, ImageAspects, ImageLayout, SampleCount},
|
||||||
pipeline::{
|
pipeline::{
|
||||||
graphics::{
|
graphics::{
|
||||||
input_assembly::PrimitiveTopology, subpass::PipelineSubpassType,
|
input_assembly::PrimitiveTopology,
|
||||||
vertex_input::VertexInputRate,
|
subpass::PipelineSubpassType,
|
||||||
|
vertex_input::{self, RequiredVertexInputsVUIDs, VertexInputRate},
|
||||||
},
|
},
|
||||||
DynamicState, GraphicsPipeline, Pipeline, PipelineLayout,
|
DynamicState, GraphicsPipeline, Pipeline, PipelineLayout,
|
||||||
},
|
},
|
||||||
@ -270,7 +271,13 @@ impl<L> AutoCommandBufferBuilder<L> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (&binding_num, binding_desc) in &pipeline.vertex_input_state().bindings {
|
let vertex_input_state = pipeline
|
||||||
|
.dynamic_state()
|
||||||
|
.contains(&DynamicState::VertexInput)
|
||||||
|
.then(|| self.builder_state.vertex_input.as_ref().unwrap())
|
||||||
|
.unwrap_or_else(|| pipeline.vertex_input_state().unwrap());
|
||||||
|
|
||||||
|
for (&binding_num, binding_desc) in &vertex_input_state.bindings {
|
||||||
let vertex_buffer = &self.builder_state.vertex_buffers[&binding_num];
|
let vertex_buffer = &self.builder_state.vertex_buffers[&binding_num];
|
||||||
|
|
||||||
// Per spec:
|
// Per spec:
|
||||||
@ -604,7 +611,13 @@ impl<L> AutoCommandBufferBuilder<L> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (&binding_num, binding_desc) in &pipeline.vertex_input_state().bindings {
|
let vertex_input_state = pipeline
|
||||||
|
.dynamic_state()
|
||||||
|
.contains(&DynamicState::VertexInput)
|
||||||
|
.then(|| self.builder_state.vertex_input.as_ref().unwrap())
|
||||||
|
.unwrap_or_else(|| pipeline.vertex_input_state().unwrap());
|
||||||
|
|
||||||
|
for (&binding_num, binding_desc) in &vertex_input_state.bindings {
|
||||||
let vertex_buffer = &self.builder_state.vertex_buffers[&binding_num];
|
let vertex_buffer = &self.builder_state.vertex_buffers[&binding_num];
|
||||||
|
|
||||||
// Per spec:
|
// Per spec:
|
||||||
@ -2148,7 +2161,45 @@ impl<L> AutoCommandBufferBuilder<L> {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// DynamicState::VertexInput => todo!(),
|
DynamicState::VertexInput => {
|
||||||
|
if let Some(vertex_input_state) = &self.builder_state.vertex_input {
|
||||||
|
vertex_input::validate_required_vertex_inputs(
|
||||||
|
&vertex_input_state.attributes,
|
||||||
|
pipeline.required_vertex_inputs().unwrap(),
|
||||||
|
RequiredVertexInputsVUIDs {
|
||||||
|
not_present: vuids!(vuid_type, "Input-07939"),
|
||||||
|
numeric_type: vuids!(vuid_type, "Input-08734"),
|
||||||
|
requires32: vuids!(vuid_type, "format-08936"),
|
||||||
|
requires64: vuids!(vuid_type, "format-08937"),
|
||||||
|
requires_second_half: vuids!(vuid_type, "None-09203"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.map_err(|mut err| {
|
||||||
|
err.problem = format!(
|
||||||
|
"the currently bound graphics pipeline requires the \
|
||||||
|
`DynamicState::VertexInput` dynamic state, but \
|
||||||
|
the dynamic vertex input does not meet the requirements of the \
|
||||||
|
vertex shader in the pipeline: {}",
|
||||||
|
err.problem,
|
||||||
|
)
|
||||||
|
.into();
|
||||||
|
err
|
||||||
|
})?;
|
||||||
|
} else {
|
||||||
|
return Err(Box::new(ValidationError {
|
||||||
|
problem: format!(
|
||||||
|
"the currently bound graphics pipeline requires the \
|
||||||
|
`DynamicState::{:?}` dynamic state, but \
|
||||||
|
this state was either not set, or it was overwritten by a \
|
||||||
|
more recent `bind_pipeline_graphics` command",
|
||||||
|
dynamic_state
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
vuids: vuids!(vuid_type, "None-04914"),
|
||||||
|
..Default::default()
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
// DynamicState::VertexInputBindingStride => todo!(),
|
// DynamicState::VertexInputBindingStride => todo!(),
|
||||||
DynamicState::Viewport => {
|
DynamicState::Viewport => {
|
||||||
let viewport_state = pipeline.viewport_state().unwrap();
|
let viewport_state = pipeline.viewport_state().unwrap();
|
||||||
@ -2437,9 +2488,13 @@ impl<L> AutoCommandBufferBuilder<L> {
|
|||||||
vuid_type: VUIDType,
|
vuid_type: VUIDType,
|
||||||
pipeline: &GraphicsPipeline,
|
pipeline: &GraphicsPipeline,
|
||||||
) -> Result<(), Box<ValidationError>> {
|
) -> Result<(), Box<ValidationError>> {
|
||||||
let vertex_input = pipeline.vertex_input_state();
|
let vertex_input_state = pipeline
|
||||||
|
.dynamic_state()
|
||||||
|
.contains(&DynamicState::VertexInput)
|
||||||
|
.then(|| self.builder_state.vertex_input.as_ref().unwrap())
|
||||||
|
.unwrap_or_else(|| pipeline.vertex_input_state().unwrap());
|
||||||
|
|
||||||
for &binding_num in vertex_input.bindings.keys() {
|
for &binding_num in vertex_input_state.bindings.keys() {
|
||||||
if !self.builder_state.vertex_buffers.contains_key(&binding_num) {
|
if !self.builder_state.vertex_buffers.contains_key(&binding_num) {
|
||||||
return Err(Box::new(ValidationError {
|
return Err(Box::new(ValidationError {
|
||||||
problem: format!(
|
problem: format!(
|
||||||
@ -2663,20 +2718,24 @@ impl<L> AutoCommandBufferBuilder<L> {
|
|||||||
used_resources: &mut Vec<(ResourceUseRef2, Resource)>,
|
used_resources: &mut Vec<(ResourceUseRef2, Resource)>,
|
||||||
pipeline: &GraphicsPipeline,
|
pipeline: &GraphicsPipeline,
|
||||||
) {
|
) {
|
||||||
used_resources.extend(pipeline.vertex_input_state().bindings.iter().map(
|
let vertex_input_state = pipeline
|
||||||
|(&binding, _)| {
|
.dynamic_state()
|
||||||
let vertex_buffer = &self.builder_state.vertex_buffers[&binding];
|
.contains(&DynamicState::VertexInput)
|
||||||
(
|
.then(|| self.builder_state.vertex_input.as_ref().unwrap())
|
||||||
ResourceInCommand::VertexBuffer { binding }.into(),
|
.unwrap_or_else(|| pipeline.vertex_input_state().unwrap());
|
||||||
Resource::Buffer {
|
|
||||||
buffer: vertex_buffer.clone(),
|
used_resources.extend(vertex_input_state.bindings.iter().map(|(&binding, _)| {
|
||||||
range: 0..vertex_buffer.size(), // TODO:
|
let vertex_buffer = &self.builder_state.vertex_buffers[&binding];
|
||||||
memory_access:
|
(
|
||||||
PipelineStageAccessFlags::VertexAttributeInput_VertexAttributeRead,
|
ResourceInCommand::VertexBuffer { binding }.into(),
|
||||||
},
|
Resource::Buffer {
|
||||||
)
|
buffer: vertex_buffer.clone(),
|
||||||
},
|
range: 0..vertex_buffer.size(), // TODO:
|
||||||
));
|
memory_access:
|
||||||
|
PipelineStageAccessFlags::VertexAttributeInput_VertexAttributeRead,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_index_buffer_resources(&self, used_resources: &mut Vec<(ResourceUseRef2, Resource)>) {
|
fn add_index_buffer_resources(&self, used_resources: &mut Vec<(ResourceUseRef2, Resource)>) {
|
||||||
|
@ -56,7 +56,7 @@ use self::{
|
|||||||
rasterization::RasterizationState,
|
rasterization::RasterizationState,
|
||||||
subpass::PipelineSubpassType,
|
subpass::PipelineSubpassType,
|
||||||
tessellation::TessellationState,
|
tessellation::TessellationState,
|
||||||
vertex_input::VertexInputState,
|
vertex_input::{RequiredVertexInputsVUIDs, VertexInputLocationRequirements, VertexInputState},
|
||||||
viewport::ViewportState,
|
viewport::ViewportState,
|
||||||
};
|
};
|
||||||
use super::{
|
use super::{
|
||||||
@ -75,7 +75,9 @@ use crate::{
|
|||||||
rasterization::{CullMode, DepthBiasState},
|
rasterization::{CullMode, DepthBiasState},
|
||||||
subpass::PipelineRenderingCreateInfo,
|
subpass::PipelineRenderingCreateInfo,
|
||||||
tessellation::TessellationDomainOrigin,
|
tessellation::TessellationDomainOrigin,
|
||||||
vertex_input::VertexInputRate,
|
vertex_input::{
|
||||||
|
VertexInputAttributeDescription, VertexInputBindingDescription, VertexInputRate,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
shader::{
|
shader::{
|
||||||
spirv::{ExecutionMode, ExecutionModel, Instruction},
|
spirv::{ExecutionMode, ExecutionModel, Instruction},
|
||||||
@ -116,11 +118,7 @@ pub struct GraphicsPipeline {
|
|||||||
flags: PipelineCreateFlags,
|
flags: PipelineCreateFlags,
|
||||||
// TODO: replace () with an object that describes the shaders in some way.
|
// TODO: replace () with an object that describes the shaders in some way.
|
||||||
shaders: HashMap<ShaderStage, ()>,
|
shaders: HashMap<ShaderStage, ()>,
|
||||||
descriptor_binding_requirements: HashMap<(u32, u32), DescriptorBindingRequirements>,
|
vertex_input_state: Option<VertexInputState>,
|
||||||
num_used_descriptor_sets: u32,
|
|
||||||
fragment_tests_stages: Option<FragmentTestsStages>,
|
|
||||||
|
|
||||||
vertex_input_state: VertexInputState,
|
|
||||||
input_assembly_state: InputAssemblyState,
|
input_assembly_state: InputAssemblyState,
|
||||||
tessellation_state: Option<TessellationState>,
|
tessellation_state: Option<TessellationState>,
|
||||||
viewport_state: Option<ViewportState>,
|
viewport_state: Option<ViewportState>,
|
||||||
@ -134,7 +132,12 @@ pub struct GraphicsPipeline {
|
|||||||
|
|
||||||
discard_rectangle_state: Option<DiscardRectangleState>,
|
discard_rectangle_state: Option<DiscardRectangleState>,
|
||||||
|
|
||||||
|
descriptor_binding_requirements: HashMap<(u32, u32), DescriptorBindingRequirements>,
|
||||||
|
num_used_descriptor_sets: u32,
|
||||||
fixed_state: HashSet<DynamicState>,
|
fixed_state: HashSet<DynamicState>,
|
||||||
|
fragment_tests_stages: Option<FragmentTestsStages>,
|
||||||
|
// Note: this is only `Some` if `vertex_input_state` is `None`.
|
||||||
|
required_vertex_inputs: Option<HashMap<u32, VertexInputLocationRequirements>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GraphicsPipeline {
|
impl GraphicsPipeline {
|
||||||
@ -305,19 +308,36 @@ impl GraphicsPipeline {
|
|||||||
} = vertex_input_state;
|
} = vertex_input_state;
|
||||||
|
|
||||||
vertex_binding_descriptions_vk.extend(bindings.iter().map(
|
vertex_binding_descriptions_vk.extend(bindings.iter().map(
|
||||||
|(&binding, binding_desc)| ash::vk::VertexInputBindingDescription {
|
|(&binding, binding_desc)| {
|
||||||
binding,
|
let &VertexInputBindingDescription {
|
||||||
stride: binding_desc.stride,
|
stride,
|
||||||
input_rate: binding_desc.input_rate.into(),
|
input_rate,
|
||||||
|
_ne: _,
|
||||||
|
} = binding_desc;
|
||||||
|
|
||||||
|
ash::vk::VertexInputBindingDescription {
|
||||||
|
binding,
|
||||||
|
stride,
|
||||||
|
input_rate: input_rate.into(),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
vertex_attribute_descriptions_vk.extend(attributes.iter().map(
|
vertex_attribute_descriptions_vk.extend(attributes.iter().map(
|
||||||
|(&location, attribute_desc)| ash::vk::VertexInputAttributeDescription {
|
|(&location, attribute_desc)| {
|
||||||
location,
|
let &VertexInputAttributeDescription {
|
||||||
binding: attribute_desc.binding,
|
binding,
|
||||||
format: attribute_desc.format.into(),
|
format,
|
||||||
offset: attribute_desc.offset,
|
offset,
|
||||||
|
_ne: _,
|
||||||
|
} = attribute_desc;
|
||||||
|
|
||||||
|
ash::vk::VertexInputAttributeDescription {
|
||||||
|
location,
|
||||||
|
binding,
|
||||||
|
format: format.into(),
|
||||||
|
offset,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -902,6 +922,7 @@ impl GraphicsPipeline {
|
|||||||
DescriptorBindingRequirements,
|
DescriptorBindingRequirements,
|
||||||
> = HashMap::default();
|
> = HashMap::default();
|
||||||
let mut fragment_tests_stages = None;
|
let mut fragment_tests_stages = None;
|
||||||
|
let mut required_vertex_inputs = None;
|
||||||
|
|
||||||
for stage in &stages {
|
for stage in &stages {
|
||||||
let &PipelineShaderStageCreateInfo {
|
let &PipelineShaderStageCreateInfo {
|
||||||
@ -915,22 +936,33 @@ impl GraphicsPipeline {
|
|||||||
let spirv = entry_point.module().spirv();
|
let spirv = entry_point.module().spirv();
|
||||||
let entry_point_function = spirv.function(entry_point.id());
|
let entry_point_function = spirv.function(entry_point.id());
|
||||||
|
|
||||||
if matches!(entry_point_info.execution_model, ExecutionModel::Fragment) {
|
match entry_point_info.execution_model {
|
||||||
fragment_tests_stages = Some(FragmentTestsStages::Late);
|
ExecutionModel::Vertex => {
|
||||||
|
if vertex_input_state.is_none() {
|
||||||
|
required_vertex_inputs = Some(vertex_input::required_vertex_inputs(
|
||||||
|
entry_point.module().spirv(),
|
||||||
|
entry_point.id(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExecutionModel::Fragment => {
|
||||||
|
fragment_tests_stages = Some(FragmentTestsStages::Late);
|
||||||
|
|
||||||
for instruction in entry_point_function.execution_modes() {
|
for instruction in entry_point_function.execution_modes() {
|
||||||
if let Instruction::ExecutionMode { mode, .. } = *instruction {
|
if let Instruction::ExecutionMode { mode, .. } = *instruction {
|
||||||
match mode {
|
match mode {
|
||||||
ExecutionMode::EarlyFragmentTests => {
|
ExecutionMode::EarlyFragmentTests => {
|
||||||
fragment_tests_stages = Some(FragmentTestsStages::Early);
|
fragment_tests_stages = Some(FragmentTestsStages::Early);
|
||||||
|
}
|
||||||
|
ExecutionMode::EarlyAndLateFragmentTestsAMD => {
|
||||||
|
fragment_tests_stages = Some(FragmentTestsStages::EarlyAndLate);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
ExecutionMode::EarlyAndLateFragmentTestsAMD => {
|
|
||||||
fragment_tests_stages = Some(FragmentTestsStages::EarlyAndLate);
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
for (&loc, reqs) in &entry_point_info.descriptor_binding_requirements {
|
for (&loc, reqs) in &entry_point_info.descriptor_binding_requirements {
|
||||||
@ -1022,11 +1054,7 @@ impl GraphicsPipeline {
|
|||||||
|
|
||||||
flags,
|
flags,
|
||||||
shaders,
|
shaders,
|
||||||
descriptor_binding_requirements,
|
vertex_input_state,
|
||||||
num_used_descriptor_sets,
|
|
||||||
fragment_tests_stages,
|
|
||||||
|
|
||||||
vertex_input_state: vertex_input_state.unwrap(), // Can be None if there's a mesh shader, but we don't support that yet
|
|
||||||
input_assembly_state: input_assembly_state.unwrap(), // Can be None if there's a mesh shader, but we don't support that yet
|
input_assembly_state: input_assembly_state.unwrap(), // Can be None if there's a mesh shader, but we don't support that yet
|
||||||
tessellation_state,
|
tessellation_state,
|
||||||
viewport_state,
|
viewport_state,
|
||||||
@ -1040,7 +1068,11 @@ impl GraphicsPipeline {
|
|||||||
|
|
||||||
discard_rectangle_state,
|
discard_rectangle_state,
|
||||||
|
|
||||||
|
descriptor_binding_requirements,
|
||||||
|
num_used_descriptor_sets,
|
||||||
fixed_state,
|
fixed_state,
|
||||||
|
fragment_tests_stages,
|
||||||
|
required_vertex_inputs,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1070,8 +1102,8 @@ impl GraphicsPipeline {
|
|||||||
|
|
||||||
/// Returns the vertex input state used to create this pipeline.
|
/// Returns the vertex input state used to create this pipeline.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn vertex_input_state(&self) -> &VertexInputState {
|
pub fn vertex_input_state(&self) -> Option<&VertexInputState> {
|
||||||
&self.vertex_input_state
|
self.vertex_input_state.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the input assembly state used to create this pipeline.
|
/// Returns the input assembly state used to create this pipeline.
|
||||||
@ -1145,6 +1177,14 @@ impl GraphicsPipeline {
|
|||||||
pub(crate) fn fixed_state(&self) -> &HashSet<DynamicState> {
|
pub(crate) fn fixed_state(&self) -> &HashSet<DynamicState> {
|
||||||
&self.fixed_state
|
&self.fixed_state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the required vertex inputs.
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn required_vertex_inputs(
|
||||||
|
&self,
|
||||||
|
) -> Option<&HashMap<u32, VertexInputLocationRequirements>> {
|
||||||
|
self.required_vertex_inputs.as_ref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pipeline for GraphicsPipeline {
|
impl Pipeline for GraphicsPipeline {
|
||||||
@ -1643,11 +1683,14 @@ impl GraphicsPipelineCreateInfo {
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
match (vertex_input_state.is_some(), need_vertex_input_state) {
|
match (
|
||||||
|
vertex_input_state.is_some(),
|
||||||
|
need_vertex_input_state && !dynamic_state.contains(&DynamicState::VertexInput),
|
||||||
|
) {
|
||||||
(true, false) => {
|
(true, false) => {
|
||||||
return Err(Box::new(ValidationError {
|
return Err(Box::new(ValidationError {
|
||||||
problem: "the pipeline is not being created with \
|
problem: "the pipeline is not being created with vertex input state, or \
|
||||||
vertex input state, but \
|
`dynamic_state` includes `DynamicState::VertexInput`, but \
|
||||||
`vertex_input_state` is `Some`"
|
`vertex_input_state` is `Some`"
|
||||||
.into(),
|
.into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -1655,8 +1698,8 @@ impl GraphicsPipelineCreateInfo {
|
|||||||
}
|
}
|
||||||
(false, true) => {
|
(false, true) => {
|
||||||
return Err(Box::new(ValidationError {
|
return Err(Box::new(ValidationError {
|
||||||
problem: "the pipeline is being created with \
|
problem: "the pipeline is being created with vertex input state, and \
|
||||||
vertex input state, but \
|
`dynamic_state` does not include `DynamicState::VertexInput`, but \
|
||||||
`vertex_input_state` is `None`"
|
`vertex_input_state` is `None`"
|
||||||
.into(),
|
.into(),
|
||||||
vuids: &["VUID-VkGraphicsPipelineCreateInfo-pStages-02097"],
|
vuids: &["VUID-VkGraphicsPipelineCreateInfo-pStages-02097"],
|
||||||
@ -2357,54 +2400,34 @@ impl GraphicsPipelineCreateInfo {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
if let (Some(vertex_stage), Some(vertex_input_state)) = (vertex_stage, vertex_input_state) {
|
if let (Some(vertex_stage), Some(vertex_input_state)) = (vertex_stage, vertex_input_state) {
|
||||||
for element in vertex_stage.entry_point.info().input_interface.elements() {
|
let required_vertex_inputs = vertex_input::required_vertex_inputs(
|
||||||
assert!(!element.ty.is_64bit); // TODO: implement
|
vertex_stage.entry_point.module().spirv(),
|
||||||
let location_range =
|
vertex_stage.entry_point.id(),
|
||||||
element.location..element.location + element.ty.num_locations();
|
);
|
||||||
|
|
||||||
for location in location_range {
|
vertex_input::validate_required_vertex_inputs(
|
||||||
let attribute_desc = match vertex_input_state.attributes.get(&location) {
|
&vertex_input_state.attributes,
|
||||||
Some(attribute_desc) => attribute_desc,
|
&required_vertex_inputs,
|
||||||
None => {
|
RequiredVertexInputsVUIDs {
|
||||||
return Err(Box::new(ValidationError {
|
not_present: &["VUID-VkGraphicsPipelineCreateInfo-Input-07904"],
|
||||||
problem: format!(
|
numeric_type: &["VUID-VkGraphicsPipelineCreateInfo-Input-08733"],
|
||||||
"the vertex shader has an input variable with location {0}, but \
|
requires32: &["VUID-VkGraphicsPipelineCreateInfo-pVertexInputState-08929"],
|
||||||
`vertex_input_state.attributes` does not contain {0}",
|
requires64: &["VUID-VkGraphicsPipelineCreateInfo-pVertexInputState-08930"],
|
||||||
location,
|
requires_second_half: &[
|
||||||
)
|
"VUID-VkGraphicsPipelineCreateInfo-pVertexInputState-09198",
|
||||||
.into(),
|
],
|
||||||
vuids: &["VUID-VkGraphicsPipelineCreateInfo-Input-07905"],
|
},
|
||||||
..Default::default()
|
)
|
||||||
}));
|
.map_err(|mut err| {
|
||||||
}
|
err.problem = format!(
|
||||||
};
|
"{}: {}",
|
||||||
|
"`vertex_input_state` does not meet the requirements \
|
||||||
// TODO: Check component assignments too. Multiple variables can occupy the
|
of the vertex shader in `stages`",
|
||||||
// same location but in different components.
|
err.problem,
|
||||||
|
)
|
||||||
let shader_type = element.ty.base_type;
|
.into();
|
||||||
let attribute_type = attribute_desc
|
err
|
||||||
.format
|
})?;
|
||||||
.numeric_format_color()
|
|
||||||
.unwrap()
|
|
||||||
.numeric_type();
|
|
||||||
|
|
||||||
// VUID?
|
|
||||||
if shader_type != attribute_type {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
problem: format!(
|
|
||||||
"`vertex_input_state.attributes[{}].format` has a different \
|
|
||||||
numeric type than the vertex shader input variable with \
|
|
||||||
location {0}",
|
|
||||||
location,
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
vuids: &["VUID-VkGraphicsPipelineCreateInfo-Input-07905"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (Some(_), Some(_)) = (tessellation_control_stage, tessellation_evaluation_stage) {
|
if let (Some(_), Some(_)) = (tessellation_control_stage, tessellation_evaluation_stage) {
|
||||||
|
@ -27,6 +27,7 @@ unsafe impl VertexDefinition for &[VertexBufferDescription] {
|
|||||||
VertexInputBindingDescription {
|
VertexInputBindingDescription {
|
||||||
stride: buffer.stride,
|
stride: buffer.stride,
|
||||||
input_rate: buffer.input_rate,
|
input_rate: buffer.input_rate,
|
||||||
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
@ -91,6 +92,7 @@ unsafe impl VertexDefinition for &[VertexBufferDescription] {
|
|||||||
binding,
|
binding,
|
||||||
format: infos.format,
|
format: infos.format,
|
||||||
offset: offset as u32,
|
offset: offset as u32,
|
||||||
|
..Default::default()
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
offset += block_size;
|
offset += block_size;
|
||||||
|
@ -99,10 +99,15 @@ pub use self::{
|
|||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
device::Device,
|
device::Device,
|
||||||
format::{Format, FormatFeatures},
|
format::{Format, FormatFeatures, NumericType},
|
||||||
|
shader::{
|
||||||
|
reflect::get_constant,
|
||||||
|
spirv::{Decoration, Id, Instruction, Spirv, StorageClass},
|
||||||
|
},
|
||||||
DeviceSize, Requires, RequiresAllOf, RequiresOneOf, ValidationError,
|
DeviceSize, Requires, RequiresAllOf, RequiresOneOf, ValidationError,
|
||||||
};
|
};
|
||||||
use ahash::HashMap;
|
use ahash::HashMap;
|
||||||
|
use std::collections::hash_map::Entry;
|
||||||
|
|
||||||
mod buffers;
|
mod buffers;
|
||||||
mod collection;
|
mod collection;
|
||||||
@ -185,15 +190,33 @@ impl VertexInputState {
|
|||||||
problem: "the length exceeds the `max_vertex_input_bindings` limit".into(),
|
problem: "the length exceeds the `max_vertex_input_bindings` limit".into(),
|
||||||
vuids: &[
|
vuids: &[
|
||||||
"VUID-VkPipelineVertexInputStateCreateInfo-vertexBindingDescriptionCount-00613",
|
"VUID-VkPipelineVertexInputStateCreateInfo-vertexBindingDescriptionCount-00613",
|
||||||
|
"VUID-vkCmdSetVertexInputEXT-vertexBindingDescriptionCount-04791",
|
||||||
],
|
],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// VUID-VkPipelineVertexInputStateCreateInfo-pVertexBindingDescriptions-00616
|
// VUID-VkPipelineVertexInputStateCreateInfo-pVertexBindingDescriptions-00616
|
||||||
|
// VUID-vkCmdSetVertexInputEXT-pVertexBindingDescriptions-04794
|
||||||
// Ensured by HashMap.
|
// Ensured by HashMap.
|
||||||
|
|
||||||
for (&binding, binding_desc) in bindings {
|
for (&binding, binding_desc) in bindings {
|
||||||
|
if binding >= properties.max_vertex_input_bindings {
|
||||||
|
return Err(Box::new(ValidationError {
|
||||||
|
context: format!("bindings[{}]", binding).into(),
|
||||||
|
problem: format!(
|
||||||
|
"the binding {} exceeds the `max_vertex_input_bindings` limit",
|
||||||
|
binding
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
vuids: &[
|
||||||
|
"VUID-VkVertexInputBindingDescription-binding-00618",
|
||||||
|
"VUID-VkVertexInputBindingDescription2EXT-binding-04796",
|
||||||
|
],
|
||||||
|
..Default::default()
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
binding_desc
|
binding_desc
|
||||||
.validate(device)
|
.validate(device)
|
||||||
.map_err(|err| err.add_context(format!("bindings[{}]", binding)))?;
|
.map_err(|err| err.add_context(format!("bindings[{}]", binding)))?;
|
||||||
@ -205,12 +228,29 @@ impl VertexInputState {
|
|||||||
problem: "the length exceeds the `max_vertex_input_attributes` limit".into(),
|
problem: "the length exceeds the `max_vertex_input_attributes` limit".into(),
|
||||||
vuids: &[
|
vuids: &[
|
||||||
"VUID-VkPipelineVertexInputStateCreateInfo-vertexAttributeDescriptionCount-00614",
|
"VUID-VkPipelineVertexInputStateCreateInfo-vertexAttributeDescriptionCount-00614",
|
||||||
|
"VUID-vkCmdSetVertexInputEXT-vertexAttributeDescriptionCount-04792",
|
||||||
],
|
],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (&location, attribute_desc) in attributes {
|
for (&location, attribute_desc) in attributes {
|
||||||
|
if location >= properties.max_vertex_input_attributes {
|
||||||
|
return Err(Box::new(ValidationError {
|
||||||
|
context: format!("attributes[{}]", location).into(),
|
||||||
|
problem: format!(
|
||||||
|
"the location {} exceeds the `max_vertex_input_attributes` limit",
|
||||||
|
location
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
vuids: &[
|
||||||
|
"VUID-VkVertexInputAttributeDescription-location-00620",
|
||||||
|
"VUID-VkVertexInputAttributeDescription2EXT-location-06228",
|
||||||
|
],
|
||||||
|
..Default::default()
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
attribute_desc
|
attribute_desc
|
||||||
.validate(device)
|
.validate(device)
|
||||||
.map_err(|err| err.add_context(format!("attributes[{}]", location)))?;
|
.map_err(|err| err.add_context(format!("attributes[{}]", location)))?;
|
||||||
@ -219,21 +259,9 @@ impl VertexInputState {
|
|||||||
binding,
|
binding,
|
||||||
format,
|
format,
|
||||||
offset,
|
offset,
|
||||||
|
_ne: _,
|
||||||
} = attribute_desc;
|
} = attribute_desc;
|
||||||
|
|
||||||
if location > properties.max_vertex_input_attributes {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
context: "attributes".into(),
|
|
||||||
problem: format!(
|
|
||||||
"the location {} exceeds the `max_vertex_input_attributes` limit",
|
|
||||||
location
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
vuids: &["VUID-VkVertexInputAttributeDescription-location-00620"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
let binding_desc = bindings.get(&binding).ok_or_else(|| {
|
let binding_desc = bindings.get(&binding).ok_or_else(|| {
|
||||||
Box::new(ValidationError {
|
Box::new(ValidationError {
|
||||||
problem: format!(
|
problem: format!(
|
||||||
@ -241,7 +269,10 @@ impl VertexInputState {
|
|||||||
binding
|
binding
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
vuids: &["VUID-VkPipelineVertexInputStateCreateInfo-binding-00615"],
|
vuids: &[
|
||||||
|
"VUID-VkPipelineVertexInputStateCreateInfo-binding-00615",
|
||||||
|
"VUID-vkCmdSetVertexInputEXT-binding-04793",
|
||||||
|
],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
@ -263,7 +294,10 @@ impl VertexInputState {
|
|||||||
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
|
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
|
||||||
"vertex_attribute_access_beyond_stride",
|
"vertex_attribute_access_beyond_stride",
|
||||||
)])]),
|
)])]),
|
||||||
vuids: &["VUID-VkVertexInputAttributeDescription-vertexAttributeAccessBeyondStride-04457"],
|
vuids: &[
|
||||||
|
"VUID-VkVertexInputAttributeDescription-vertexAttributeAccessBeyondStride-04457",
|
||||||
|
"VUID-VkVertexInputAttributeDescription2EXT-vertexAttributeAccessBeyondStride-04806",
|
||||||
|
],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -285,7 +319,10 @@ impl VertexInputState {
|
|||||||
location - 1, location,
|
location - 1, location,
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
vuids: &["VUID-VkPipelineVertexInputStateCreateInfo-pVertexAttributeDescriptions-00617"],
|
vuids: &[
|
||||||
|
"VUID-VkPipelineVertexInputStateCreateInfo-pVertexAttributeDescriptions-00617",
|
||||||
|
"VUID-vkCmdSetVertexInputEXT-pVertexAttributeDescriptions-04795",
|
||||||
|
],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -312,15 +349,36 @@ pub struct VertexInputBindingDescription {
|
|||||||
/// The number of bytes from the start of one element in the vertex buffer to the start of the
|
/// The number of bytes from the start of one element in the vertex buffer to the start of the
|
||||||
/// next element. This can be simply the size of the data in each element, but larger strides
|
/// next element. This can be simply the size of the data in each element, but larger strides
|
||||||
/// are possible.
|
/// are possible.
|
||||||
|
///
|
||||||
|
/// The default value is `0`, which must be overridden.
|
||||||
pub stride: u32,
|
pub stride: u32,
|
||||||
|
|
||||||
/// How often the vertex input should advance to the next element.
|
/// How often the vertex input should advance to the next element.
|
||||||
|
///
|
||||||
|
/// The default value is [`VertexInputRate::Vertex`].
|
||||||
pub input_rate: VertexInputRate,
|
pub input_rate: VertexInputRate,
|
||||||
|
|
||||||
|
pub _ne: crate::NonExhaustive,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for VertexInputBindingDescription {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
stride: 0,
|
||||||
|
input_rate: VertexInputRate::Vertex,
|
||||||
|
_ne: crate::NonExhaustive(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VertexInputBindingDescription {
|
impl VertexInputBindingDescription {
|
||||||
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
|
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
|
||||||
let &Self { stride, input_rate } = self;
|
let &Self {
|
||||||
|
stride,
|
||||||
|
input_rate,
|
||||||
|
_ne: _,
|
||||||
|
} = self;
|
||||||
|
|
||||||
let properties = device.physical_device().properties();
|
let properties = device.physical_device().properties();
|
||||||
|
|
||||||
@ -328,7 +386,10 @@ impl VertexInputBindingDescription {
|
|||||||
return Err(Box::new(ValidationError {
|
return Err(Box::new(ValidationError {
|
||||||
context: "stride".into(),
|
context: "stride".into(),
|
||||||
problem: "exceeds the `max_vertex_input_binding_stride` limit".into(),
|
problem: "exceeds the `max_vertex_input_binding_stride` limit".into(),
|
||||||
vuids: &["VUID-VkVertexInputBindingDescription-stride-00619"],
|
vuids: &[
|
||||||
|
"VUID-VkVertexInputBindingDescription-stride-00619",
|
||||||
|
"VUID-VkVertexInputBindingDescription2EXT-stride-04797",
|
||||||
|
],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -364,7 +425,10 @@ impl VertexInputBindingDescription {
|
|||||||
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
|
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
|
||||||
"vertex_attribute_instance_rate_divisor",
|
"vertex_attribute_instance_rate_divisor",
|
||||||
)])]),
|
)])]),
|
||||||
vuids: &["VUID-VkVertexInputBindingDivisorDescriptionEXT-vertexAttributeInstanceRateDivisor-02229"],
|
vuids: &[
|
||||||
|
"VUID-VkVertexInputBindingDivisorDescriptionEXT-vertexAttributeInstanceRateDivisor-02229",
|
||||||
|
"VUID-VkVertexInputBindingDescription2EXT-divisor-04799",
|
||||||
|
],
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,7 +444,10 @@ impl VertexInputBindingDescription {
|
|||||||
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
|
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
|
||||||
"vertex_attribute_instance_rate_zero_divisor",
|
"vertex_attribute_instance_rate_zero_divisor",
|
||||||
)])]),
|
)])]),
|
||||||
vuids: &["VUID-VkVertexInputBindingDivisorDescriptionEXT-vertexAttributeInstanceRateZeroDivisor-02228"],
|
vuids: &[
|
||||||
|
"VUID-VkVertexInputBindingDivisorDescriptionEXT-vertexAttributeInstanceRateZeroDivisor-02228",
|
||||||
|
"VUID-VkVertexInputBindingDescription2EXT-divisor-04798",
|
||||||
|
],
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,7 +457,10 @@ impl VertexInputBindingDescription {
|
|||||||
problem: "is `VertexInputRate::Instance`, and \
|
problem: "is `VertexInputRate::Instance`, and \
|
||||||
its `divisor` value exceeds the `max_vertex_attrib_divisor` limit"
|
its `divisor` value exceeds the `max_vertex_attrib_divisor` limit"
|
||||||
.into(),
|
.into(),
|
||||||
vuids: &["VUID-VkVertexInputBindingDivisorDescriptionEXT-divisor-01870"],
|
vuids: &[
|
||||||
|
"VUID-VkVertexInputBindingDivisorDescriptionEXT-divisor-01870",
|
||||||
|
"VUID-VkVertexInputBindingDescription2EXT-divisor-06226",
|
||||||
|
],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -406,9 +476,13 @@ impl VertexInputBindingDescription {
|
|||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct VertexInputAttributeDescription {
|
pub struct VertexInputAttributeDescription {
|
||||||
/// The vertex buffer binding number that this attribute should take its data from.
|
/// The vertex buffer binding number that this attribute should take its data from.
|
||||||
|
///
|
||||||
|
/// The default value is `0`.
|
||||||
pub binding: u32,
|
pub binding: u32,
|
||||||
|
|
||||||
/// The size and type of the vertex data.
|
/// The size and type of the vertex data.
|
||||||
|
///
|
||||||
|
/// The default value is [`Format::UNDEFINED`], which must be overridden.
|
||||||
pub format: Format,
|
pub format: Format,
|
||||||
|
|
||||||
/// Number of bytes between the start of a vertex buffer element and the location of attribute.
|
/// Number of bytes between the start of a vertex buffer element and the location of attribute.
|
||||||
@ -418,7 +492,23 @@ pub struct VertexInputAttributeDescription {
|
|||||||
/// `binding`, the
|
/// `binding`, the
|
||||||
/// [`vertex_attribute_access_beyond_stride`](crate::device::Features::vertex_attribute_access_beyond_stride)
|
/// [`vertex_attribute_access_beyond_stride`](crate::device::Features::vertex_attribute_access_beyond_stride)
|
||||||
/// feature must be enabled on the device.
|
/// feature must be enabled on the device.
|
||||||
|
///
|
||||||
|
/// The default value is `0`.
|
||||||
pub offset: u32,
|
pub offset: u32,
|
||||||
|
|
||||||
|
pub _ne: crate::NonExhaustive,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for VertexInputAttributeDescription {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
binding: 0,
|
||||||
|
format: Format::UNDEFINED,
|
||||||
|
offset: 0,
|
||||||
|
_ne: crate::NonExhaustive(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VertexInputAttributeDescription {
|
impl VertexInputAttributeDescription {
|
||||||
@ -427,20 +517,26 @@ impl VertexInputAttributeDescription {
|
|||||||
binding,
|
binding,
|
||||||
format,
|
format,
|
||||||
offset,
|
offset,
|
||||||
|
_ne: _,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let properties = device.physical_device().properties();
|
let properties = device.physical_device().properties();
|
||||||
|
|
||||||
format.validate_device(device).map_err(|err| {
|
format.validate_device(device).map_err(|err| {
|
||||||
err.add_context("format")
|
err.add_context("format").set_vuids(&[
|
||||||
.set_vuids(&["VUID-VkVertexInputAttributeDescription-format-parameter"])
|
"VUID-VkVertexInputAttributeDescription-format-parameter",
|
||||||
|
"VUID-VkVertexInputAttributeDescription2EXT-format-parameter",
|
||||||
|
])
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if binding > properties.max_vertex_input_bindings {
|
if binding > properties.max_vertex_input_bindings {
|
||||||
return Err(Box::new(ValidationError {
|
return Err(Box::new(ValidationError {
|
||||||
context: "binding".into(),
|
context: "binding".into(),
|
||||||
problem: "exceeds the `max_vertex_input_bindings` limit".into(),
|
problem: "exceeds the `max_vertex_input_bindings` limit".into(),
|
||||||
vuids: &["VUID-VkVertexInputAttributeDescription-binding-00621"],
|
vuids: &[
|
||||||
|
"VUID-VkVertexInputAttributeDescription-binding-00621",
|
||||||
|
"VUID-VkVertexInputAttributeDescription2EXT-binding-06229",
|
||||||
|
],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -449,7 +545,10 @@ impl VertexInputAttributeDescription {
|
|||||||
return Err(Box::new(ValidationError {
|
return Err(Box::new(ValidationError {
|
||||||
context: "offset".into(),
|
context: "offset".into(),
|
||||||
problem: "exceeds the `max_vertex_input_attribute_offset` limit".into(),
|
problem: "exceeds the `max_vertex_input_attribute_offset` limit".into(),
|
||||||
vuids: &["VUID-VkVertexInputAttributeDescription-offset-00622"],
|
vuids: &[
|
||||||
|
"VUID-VkVertexInputAttributeDescription-offset-00622",
|
||||||
|
"VUID-VkVertexInputAttributeDescription2EXT-offset-06230",
|
||||||
|
],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -466,7 +565,10 @@ impl VertexInputAttributeDescription {
|
|||||||
context: "format".into(),
|
context: "format".into(),
|
||||||
problem: "the format features do not include `FormatFeatures::VERTEX_BUFFER`"
|
problem: "the format features do not include `FormatFeatures::VERTEX_BUFFER`"
|
||||||
.into(),
|
.into(),
|
||||||
vuids: &["VUID-VkVertexInputAttributeDescription-format-00623"],
|
vuids: &[
|
||||||
|
"VUID-VkVertexInputAttributeDescription-format-00623",
|
||||||
|
"VUID-VkVertexInputAttributeDescription2EXT-format-04805",
|
||||||
|
],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -504,3 +606,402 @@ impl From<VertexInputRate> for ash::vk::VertexInputRate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub(crate) struct VertexInputLocationRequirements {
|
||||||
|
pub(crate) numeric_type: NumericType,
|
||||||
|
pub(crate) width: VertexInputLocationWidth,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub(crate) enum VertexInputLocationWidth {
|
||||||
|
/// The shader requires a 32-bit or smaller value at this location.
|
||||||
|
Requires32,
|
||||||
|
|
||||||
|
/// The shader requires a 64-bit value at this location.
|
||||||
|
/// The boolean indicates whether the shader requires a format that fills the second half
|
||||||
|
/// of the location.
|
||||||
|
Requires64 { requires_second_half: bool },
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn required_vertex_inputs(
|
||||||
|
spirv: &Spirv,
|
||||||
|
entry_point_id: Id,
|
||||||
|
) -> HashMap<u32, VertexInputLocationRequirements> {
|
||||||
|
let interface = match spirv.function(entry_point_id).entry_point() {
|
||||||
|
Some(Instruction::EntryPoint { interface, .. }) => interface,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut required_vertex_inputs = HashMap::default();
|
||||||
|
|
||||||
|
for &variable_id in interface {
|
||||||
|
let variable_id_info = spirv.id(variable_id);
|
||||||
|
let pointer_type_id = match *variable_id_info.instruction() {
|
||||||
|
Instruction::Variable {
|
||||||
|
result_type_id,
|
||||||
|
storage_class: StorageClass::Input,
|
||||||
|
..
|
||||||
|
} => result_type_id,
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
let pointer_type_id_info = spirv.id(pointer_type_id);
|
||||||
|
let type_id = match *pointer_type_id_info.instruction() {
|
||||||
|
Instruction::TypePointer { ty, .. } => ty,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut variable_location = None;
|
||||||
|
let mut variable_component = 0;
|
||||||
|
|
||||||
|
for instruction in variable_id_info.decorations() {
|
||||||
|
if let Instruction::Decorate { ref decoration, .. } = *instruction {
|
||||||
|
match *decoration {
|
||||||
|
Decoration::Location { location } => variable_location = Some(location),
|
||||||
|
Decoration::Component { component } => variable_component = component,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(variable_location) = variable_location {
|
||||||
|
add_type_location(
|
||||||
|
&mut required_vertex_inputs,
|
||||||
|
spirv,
|
||||||
|
variable_location,
|
||||||
|
variable_component,
|
||||||
|
type_id,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let block_type_id_info = spirv.id(type_id);
|
||||||
|
let member_types = match block_type_id_info.instruction() {
|
||||||
|
Instruction::TypeStruct { member_types, .. } => member_types,
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (&type_id, member_info) in member_types.iter().zip(block_type_id_info.members()) {
|
||||||
|
let mut member_location = None;
|
||||||
|
let mut member_component = 0;
|
||||||
|
|
||||||
|
for instruction in member_info.decorations() {
|
||||||
|
if let Instruction::MemberDecorate { ref decoration, .. } = *instruction {
|
||||||
|
match *decoration {
|
||||||
|
Decoration::Location { location } => member_location = Some(location),
|
||||||
|
Decoration::Component { component } => member_component = component,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(member_location) = member_location {
|
||||||
|
add_type_location(
|
||||||
|
&mut required_vertex_inputs,
|
||||||
|
spirv,
|
||||||
|
member_location,
|
||||||
|
member_component,
|
||||||
|
type_id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
required_vertex_inputs
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_type_location(
|
||||||
|
required_vertex_inputs: &mut HashMap<u32, VertexInputLocationRequirements>,
|
||||||
|
spirv: &Spirv,
|
||||||
|
mut location: u32,
|
||||||
|
mut component: u32,
|
||||||
|
type_id: Id,
|
||||||
|
) -> (u32, u32) {
|
||||||
|
debug_assert!(component < 4);
|
||||||
|
|
||||||
|
let mut add_scalar = |numeric_type: NumericType, width: u32| -> (u32, u32) {
|
||||||
|
if width > 32 {
|
||||||
|
debug_assert!(component & 1 == 0);
|
||||||
|
let half_index = component as usize / 2;
|
||||||
|
|
||||||
|
match required_vertex_inputs.entry(location) {
|
||||||
|
Entry::Occupied(mut entry) => {
|
||||||
|
let requirements = entry.get_mut();
|
||||||
|
debug_assert_eq!(requirements.numeric_type, numeric_type);
|
||||||
|
|
||||||
|
match &mut requirements.width {
|
||||||
|
VertexInputLocationWidth::Requires32 => unreachable!(),
|
||||||
|
VertexInputLocationWidth::Requires64 {
|
||||||
|
requires_second_half,
|
||||||
|
} => {
|
||||||
|
if component == 2 {
|
||||||
|
debug_assert!(!*requires_second_half);
|
||||||
|
*requires_second_half = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
let mut required_halves = [false; 2];
|
||||||
|
required_halves[half_index] = true;
|
||||||
|
entry.insert(VertexInputLocationRequirements {
|
||||||
|
numeric_type,
|
||||||
|
width: VertexInputLocationWidth::Requires64 {
|
||||||
|
requires_second_half: component == 2,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(1, 2)
|
||||||
|
} else {
|
||||||
|
match required_vertex_inputs.entry(location) {
|
||||||
|
Entry::Occupied(entry) => {
|
||||||
|
let requirements = *entry.get();
|
||||||
|
debug_assert_eq!(requirements.numeric_type, numeric_type);
|
||||||
|
debug_assert_eq!(requirements.width, VertexInputLocationWidth::Requires32);
|
||||||
|
}
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
entry.insert(VertexInputLocationRequirements {
|
||||||
|
numeric_type,
|
||||||
|
width: VertexInputLocationWidth::Requires32,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(1, 1)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match *spirv.id(type_id).instruction() {
|
||||||
|
Instruction::TypeInt {
|
||||||
|
width, signedness, ..
|
||||||
|
} => {
|
||||||
|
let numeric_type = if signedness == 1 {
|
||||||
|
NumericType::Int
|
||||||
|
} else {
|
||||||
|
NumericType::Uint
|
||||||
|
};
|
||||||
|
|
||||||
|
add_scalar(numeric_type, width)
|
||||||
|
}
|
||||||
|
Instruction::TypeFloat { width, .. } => add_scalar(NumericType::Float, width),
|
||||||
|
Instruction::TypeVector {
|
||||||
|
component_type,
|
||||||
|
component_count,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let mut total_locations_added = 1;
|
||||||
|
|
||||||
|
for _ in 0..component_count {
|
||||||
|
// Overflow into next location
|
||||||
|
if component == 4 {
|
||||||
|
component = 0;
|
||||||
|
location += 1;
|
||||||
|
total_locations_added += 1;
|
||||||
|
} else {
|
||||||
|
debug_assert!(component < 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (_, components_added) = add_type_location(
|
||||||
|
required_vertex_inputs,
|
||||||
|
spirv,
|
||||||
|
location,
|
||||||
|
component,
|
||||||
|
component_type,
|
||||||
|
);
|
||||||
|
component += components_added;
|
||||||
|
}
|
||||||
|
|
||||||
|
(total_locations_added, 0)
|
||||||
|
}
|
||||||
|
Instruction::TypeMatrix {
|
||||||
|
column_type,
|
||||||
|
column_count,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let mut total_locations_added = 0;
|
||||||
|
|
||||||
|
for _ in 0..column_count {
|
||||||
|
let (locations_added, _) = add_type_location(
|
||||||
|
required_vertex_inputs,
|
||||||
|
spirv,
|
||||||
|
location,
|
||||||
|
component,
|
||||||
|
column_type,
|
||||||
|
);
|
||||||
|
location += locations_added;
|
||||||
|
total_locations_added += locations_added;
|
||||||
|
}
|
||||||
|
|
||||||
|
(total_locations_added, 0)
|
||||||
|
}
|
||||||
|
Instruction::TypeArray {
|
||||||
|
element_type,
|
||||||
|
length,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let length = get_constant(spirv, length).unwrap();
|
||||||
|
let mut total_locations_added = 0;
|
||||||
|
|
||||||
|
for _ in 0..length {
|
||||||
|
let (locations_added, _) = add_type_location(
|
||||||
|
required_vertex_inputs,
|
||||||
|
spirv,
|
||||||
|
location,
|
||||||
|
component,
|
||||||
|
element_type,
|
||||||
|
);
|
||||||
|
location += locations_added;
|
||||||
|
total_locations_added += locations_added;
|
||||||
|
}
|
||||||
|
|
||||||
|
(total_locations_added, 0)
|
||||||
|
}
|
||||||
|
Instruction::TypeStruct {
|
||||||
|
ref member_types, ..
|
||||||
|
} => {
|
||||||
|
let mut total_locations_added = 0;
|
||||||
|
|
||||||
|
for &member_type in member_types {
|
||||||
|
let (locations_added, _) = add_type_location(
|
||||||
|
required_vertex_inputs,
|
||||||
|
spirv,
|
||||||
|
location,
|
||||||
|
component,
|
||||||
|
member_type,
|
||||||
|
);
|
||||||
|
location += locations_added;
|
||||||
|
total_locations_added += locations_added;
|
||||||
|
}
|
||||||
|
|
||||||
|
(total_locations_added, 0)
|
||||||
|
}
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct RequiredVertexInputsVUIDs {
|
||||||
|
pub(crate) not_present: &'static [&'static str],
|
||||||
|
pub(crate) numeric_type: &'static [&'static str],
|
||||||
|
pub(crate) requires32: &'static [&'static str],
|
||||||
|
pub(crate) requires64: &'static [&'static str],
|
||||||
|
pub(crate) requires_second_half: &'static [&'static str],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn validate_required_vertex_inputs(
|
||||||
|
attribute_descs: &HashMap<u32, VertexInputAttributeDescription>,
|
||||||
|
required_vertex_inputs: &HashMap<u32, VertexInputLocationRequirements>,
|
||||||
|
vuids: RequiredVertexInputsVUIDs,
|
||||||
|
) -> Result<(), Box<ValidationError>> {
|
||||||
|
for (&location, location_info) in required_vertex_inputs {
|
||||||
|
let (is_previous, attribute_desc) = (attribute_descs.get(&location).map(|d| (false, d)))
|
||||||
|
.or_else(|| {
|
||||||
|
// If the previous location has at least three 64-bit components,
|
||||||
|
// then it extends into the current location, so try that instead.
|
||||||
|
location.checked_sub(1).and_then(|location| {
|
||||||
|
attribute_descs
|
||||||
|
.get(&location)
|
||||||
|
.filter(|attribute_desc| {
|
||||||
|
attribute_desc
|
||||||
|
.format
|
||||||
|
.components()
|
||||||
|
.starts_with(&[64, 64, 64])
|
||||||
|
})
|
||||||
|
.map(|d| (true, d))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.ok_or_else(|| {
|
||||||
|
Box::new(ValidationError {
|
||||||
|
problem: format!(
|
||||||
|
"the vertex shader has an input variable with location {0}, but \
|
||||||
|
the vertex input attributes do not contain {0}",
|
||||||
|
location,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
vuids: vuids.not_present,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let attribute_numeric_type = attribute_desc
|
||||||
|
.format
|
||||||
|
.numeric_format_color()
|
||||||
|
.unwrap()
|
||||||
|
.numeric_type();
|
||||||
|
|
||||||
|
if attribute_numeric_type != location_info.numeric_type {
|
||||||
|
return Err(Box::new(ValidationError {
|
||||||
|
problem: format!(
|
||||||
|
"the numeric type of the format of vertex input attribute {0} ({1:?}) \
|
||||||
|
does not equal the numeric type of the vertex shader input variable with \
|
||||||
|
location {0} ({2:?})",
|
||||||
|
location, attribute_numeric_type, location_info.numeric_type,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
vuids: vuids.numeric_type,
|
||||||
|
..Default::default()
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
let attribute_components = attribute_desc.format.components();
|
||||||
|
|
||||||
|
// 64-bit in the shader must match with 64-bit in the attribute.
|
||||||
|
match location_info.width {
|
||||||
|
VertexInputLocationWidth::Requires32 => {
|
||||||
|
if attribute_components[0] > 32 {
|
||||||
|
return Err(Box::new(ValidationError {
|
||||||
|
problem: format!(
|
||||||
|
"the vertex shader input variable location {0} requires a non-64-bit \
|
||||||
|
format, but the format of vertex input attribute {0} is 64-bit",
|
||||||
|
location,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
vuids: vuids.requires32,
|
||||||
|
..Default::default()
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VertexInputLocationWidth::Requires64 {
|
||||||
|
requires_second_half,
|
||||||
|
} => {
|
||||||
|
if attribute_components[0] <= 32 {
|
||||||
|
return Err(Box::new(ValidationError {
|
||||||
|
problem: format!(
|
||||||
|
"the vertex shader input variable location {0} requires a 64-bit \
|
||||||
|
format, but the format of vertex input attribute {0} is not 64-bit",
|
||||||
|
location,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
vuids: vuids.requires64,
|
||||||
|
..Default::default()
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// For 64-bit values, there are no default values for missing components.
|
||||||
|
// If the shader uses the 64-bit value in the second half of the location, then
|
||||||
|
// the attribute must provide it.
|
||||||
|
if requires_second_half {
|
||||||
|
let second_half_attribute_component = if is_previous { 3 } else { 1 };
|
||||||
|
|
||||||
|
if attribute_components[second_half_attribute_component] != 64 {
|
||||||
|
return Err(Box::new(ValidationError {
|
||||||
|
problem: format!(
|
||||||
|
"the vertex shader input variable location {0} requires a format \
|
||||||
|
with at least {1} 64-bit components, but the format of \
|
||||||
|
vertex input attribute {0} contains only {2} components",
|
||||||
|
location,
|
||||||
|
second_half_attribute_component + 1,
|
||||||
|
attribute_components.into_iter().filter(|&c| c != 0).count(),
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
vuids: vuids.requires_second_half,
|
||||||
|
..Default::default()
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
@ -1251,12 +1251,15 @@ vulkan_enum! {
|
|||||||
RequiresAllOf([DeviceExtension(ext_line_rasterization)]),
|
RequiresAllOf([DeviceExtension(ext_line_rasterization)]),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
/* TODO: enable
|
/// The `Option` variant of
|
||||||
// TODO: document
|
/// [`GraphicsPipelineCreateInfo::vertex_input_state`](crate::pipeline::graphics::GraphicsPipelineCreateInfo::vertex_input_state).
|
||||||
|
///
|
||||||
|
/// Set with
|
||||||
|
/// [`set_vertex_input`](crate::command_buffer::AutoCommandBufferBuilder::set_vertex_input).
|
||||||
VertexInput = VERTEX_INPUT_EXT
|
VertexInput = VERTEX_INPUT_EXT
|
||||||
RequiresOneOf([
|
RequiresOneOf([
|
||||||
RequiresAllOf([DeviceExtension(ext_vertex_input_dynamic_state)]),
|
RequiresAllOf([DeviceExtension(ext_vertex_input_dynamic_state)]),
|
||||||
]), */
|
]),
|
||||||
|
|
||||||
/// The value of
|
/// The value of
|
||||||
/// [`TessellationState::patch_control_points`](crate::pipeline::graphics::tessellation::TessellationState::patch_control_points).
|
/// [`TessellationState::patch_control_points`](crate::pipeline::graphics::tessellation::TessellationState::patch_control_points).
|
||||||
|
@ -1444,6 +1444,17 @@ fn is_builtin(spirv: &Spirv, id: Id) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_constant(spirv: &Spirv, id: Id) -> Option<u64> {
|
||||||
|
match spirv.id(id).instruction() {
|
||||||
|
Instruction::Constant { value, .. } => match value.len() {
|
||||||
|
1 => Some(value[0] as u64),
|
||||||
|
2 => Some(value[0] as u64 | (value[1] as u64) << 32),
|
||||||
|
_ => panic!("constant {} is larger than 64 bits", id),
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{HashMap, PushConstantRange, ShaderStages, Version};
|
use super::{HashMap, PushConstantRange, ShaderStages, Version};
|
||||||
|
Loading…
Reference in New Issue
Block a user