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,
|
||||
rasterization::{CullMode, DepthBiasState, FrontFace, LineStipple},
|
||||
subpass::PipelineRenderingCreateInfo,
|
||||
vertex_input::VertexInputState,
|
||||
viewport::{Scissor, Viewport},
|
||||
},
|
||||
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_test_enable: Option<bool>,
|
||||
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_with_count: Option<SmallVec<[Viewport; 2]>>,
|
||||
|
||||
@ -1369,7 +1371,7 @@ impl CommandBufferBuilderState {
|
||||
DynamicState::StencilReference => self.stencil_reference = Default::default(),
|
||||
DynamicState::StencilTestEnable => self.stencil_test_enable = None,
|
||||
DynamicState::StencilWriteMask => self.stencil_write_mask = Default::default(),
|
||||
// DynamicState::VertexInput => todo!(),
|
||||
DynamicState::VertexInput => self.vertex_input = None,
|
||||
// DynamicState::VertexInputBindingStride => todo!(),
|
||||
DynamicState::Viewport => self.viewport.clear(),
|
||||
// DynamicState::ViewportCoarseSampleOrder => todo!(),
|
||||
|
@ -13,10 +13,10 @@ use crate::{
|
||||
graphics::vertex_input::VertexBuffersCollection, ComputePipeline, GraphicsPipeline,
|
||||
PipelineBindPoint, PipelineLayout,
|
||||
},
|
||||
DeviceSize, Requires, RequiresAllOf, RequiresOneOf, ValidationError, VulkanObject,
|
||||
DeviceSize, Requires, RequiresAllOf, RequiresOneOf, ValidationError, Version, VulkanObject,
|
||||
};
|
||||
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.
|
||||
///
|
||||
@ -954,19 +954,58 @@ impl UnsafeCommandBufferBuilder {
|
||||
return self;
|
||||
}
|
||||
|
||||
let (buffers_vk, offsets_vk): (SmallVec<[_; 2]>, SmallVec<[_; 2]>) = vertex_buffers
|
||||
.iter()
|
||||
.map(|buffer| (buffer.buffer().handle(), buffer.offset()))
|
||||
.unzip();
|
||||
let device = self.device();
|
||||
|
||||
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(),
|
||||
);
|
||||
if device.api_version() >= Version::V1_3
|
||||
|| device.enabled_extensions().ext_extended_dynamic_state
|
||||
|| device.enabled_extensions().ext_shader_object
|
||||
{
|
||||
let mut buffers_vk: SmallVec<[_; 2]> = SmallVec::with_capacity(vertex_buffers.len());
|
||||
let mut offsets_vk: SmallVec<[_; 2]> = SmallVec::with_capacity(vertex_buffers.len());
|
||||
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
|
||||
}
|
||||
|
@ -7,6 +7,10 @@ use crate::{
|
||||
depth_stencil::{CompareOp, StencilFaces, StencilOp, StencilOps},
|
||||
input_assembly::PrimitiveTopology,
|
||||
rasterization::{CullMode, DepthBiasState, FrontFace, LineStipple},
|
||||
vertex_input::{
|
||||
VertexInputAttributeDescription, VertexInputBindingDescription, VertexInputRate,
|
||||
VertexInputState,
|
||||
},
|
||||
viewport::{Scissor, Viewport},
|
||||
},
|
||||
DynamicState,
|
||||
@ -1069,6 +1073,46 @@ impl<L> AutoCommandBufferBuilder<L> {
|
||||
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.
|
||||
pub fn set_viewport(
|
||||
&mut self,
|
||||
@ -2837,6 +2881,121 @@ impl UnsafeCommandBufferBuilder {
|
||||
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]
|
||||
pub unsafe fn set_viewport(
|
||||
&mut self,
|
||||
|
@ -16,8 +16,9 @@ use crate::{
|
||||
image::{sampler::Sampler, view::ImageView, ImageAspects, ImageLayout, SampleCount},
|
||||
pipeline::{
|
||||
graphics::{
|
||||
input_assembly::PrimitiveTopology, subpass::PipelineSubpassType,
|
||||
vertex_input::VertexInputRate,
|
||||
input_assembly::PrimitiveTopology,
|
||||
subpass::PipelineSubpassType,
|
||||
vertex_input::{self, RequiredVertexInputsVUIDs, VertexInputRate},
|
||||
},
|
||||
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];
|
||||
|
||||
// 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];
|
||||
|
||||
// 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::Viewport => {
|
||||
let viewport_state = pipeline.viewport_state().unwrap();
|
||||
@ -2437,9 +2488,13 @@ impl<L> AutoCommandBufferBuilder<L> {
|
||||
vuid_type: VUIDType,
|
||||
pipeline: &GraphicsPipeline,
|
||||
) -> 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) {
|
||||
return Err(Box::new(ValidationError {
|
||||
problem: format!(
|
||||
@ -2663,20 +2718,24 @@ impl<L> AutoCommandBufferBuilder<L> {
|
||||
used_resources: &mut Vec<(ResourceUseRef2, Resource)>,
|
||||
pipeline: &GraphicsPipeline,
|
||||
) {
|
||||
used_resources.extend(pipeline.vertex_input_state().bindings.iter().map(
|
||||
|(&binding, _)| {
|
||||
let vertex_buffer = &self.builder_state.vertex_buffers[&binding];
|
||||
(
|
||||
ResourceInCommand::VertexBuffer { binding }.into(),
|
||||
Resource::Buffer {
|
||||
buffer: vertex_buffer.clone(),
|
||||
range: 0..vertex_buffer.size(), // TODO:
|
||||
memory_access:
|
||||
PipelineStageAccessFlags::VertexAttributeInput_VertexAttributeRead,
|
||||
},
|
||||
)
|
||||
},
|
||||
));
|
||||
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());
|
||||
|
||||
used_resources.extend(vertex_input_state.bindings.iter().map(|(&binding, _)| {
|
||||
let vertex_buffer = &self.builder_state.vertex_buffers[&binding];
|
||||
(
|
||||
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)>) {
|
||||
|
@ -56,7 +56,7 @@ use self::{
|
||||
rasterization::RasterizationState,
|
||||
subpass::PipelineSubpassType,
|
||||
tessellation::TessellationState,
|
||||
vertex_input::VertexInputState,
|
||||
vertex_input::{RequiredVertexInputsVUIDs, VertexInputLocationRequirements, VertexInputState},
|
||||
viewport::ViewportState,
|
||||
};
|
||||
use super::{
|
||||
@ -75,7 +75,9 @@ use crate::{
|
||||
rasterization::{CullMode, DepthBiasState},
|
||||
subpass::PipelineRenderingCreateInfo,
|
||||
tessellation::TessellationDomainOrigin,
|
||||
vertex_input::VertexInputRate,
|
||||
vertex_input::{
|
||||
VertexInputAttributeDescription, VertexInputBindingDescription, VertexInputRate,
|
||||
},
|
||||
},
|
||||
shader::{
|
||||
spirv::{ExecutionMode, ExecutionModel, Instruction},
|
||||
@ -116,11 +118,7 @@ pub struct GraphicsPipeline {
|
||||
flags: PipelineCreateFlags,
|
||||
// TODO: replace () with an object that describes the shaders in some way.
|
||||
shaders: HashMap<ShaderStage, ()>,
|
||||
descriptor_binding_requirements: HashMap<(u32, u32), DescriptorBindingRequirements>,
|
||||
num_used_descriptor_sets: u32,
|
||||
fragment_tests_stages: Option<FragmentTestsStages>,
|
||||
|
||||
vertex_input_state: VertexInputState,
|
||||
vertex_input_state: Option<VertexInputState>,
|
||||
input_assembly_state: InputAssemblyState,
|
||||
tessellation_state: Option<TessellationState>,
|
||||
viewport_state: Option<ViewportState>,
|
||||
@ -134,7 +132,12 @@ pub struct GraphicsPipeline {
|
||||
|
||||
discard_rectangle_state: Option<DiscardRectangleState>,
|
||||
|
||||
descriptor_binding_requirements: HashMap<(u32, u32), DescriptorBindingRequirements>,
|
||||
num_used_descriptor_sets: u32,
|
||||
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 {
|
||||
@ -305,19 +308,36 @@ impl GraphicsPipeline {
|
||||
} = vertex_input_state;
|
||||
|
||||
vertex_binding_descriptions_vk.extend(bindings.iter().map(
|
||||
|(&binding, binding_desc)| ash::vk::VertexInputBindingDescription {
|
||||
binding,
|
||||
stride: binding_desc.stride,
|
||||
input_rate: binding_desc.input_rate.into(),
|
||||
|(&binding, binding_desc)| {
|
||||
let &VertexInputBindingDescription {
|
||||
stride,
|
||||
input_rate,
|
||||
_ne: _,
|
||||
} = binding_desc;
|
||||
|
||||
ash::vk::VertexInputBindingDescription {
|
||||
binding,
|
||||
stride,
|
||||
input_rate: input_rate.into(),
|
||||
}
|
||||
},
|
||||
));
|
||||
|
||||
vertex_attribute_descriptions_vk.extend(attributes.iter().map(
|
||||
|(&location, attribute_desc)| ash::vk::VertexInputAttributeDescription {
|
||||
location,
|
||||
binding: attribute_desc.binding,
|
||||
format: attribute_desc.format.into(),
|
||||
offset: attribute_desc.offset,
|
||||
|(&location, attribute_desc)| {
|
||||
let &VertexInputAttributeDescription {
|
||||
binding,
|
||||
format,
|
||||
offset,
|
||||
_ne: _,
|
||||
} = attribute_desc;
|
||||
|
||||
ash::vk::VertexInputAttributeDescription {
|
||||
location,
|
||||
binding,
|
||||
format: format.into(),
|
||||
offset,
|
||||
}
|
||||
},
|
||||
));
|
||||
|
||||
@ -902,6 +922,7 @@ impl GraphicsPipeline {
|
||||
DescriptorBindingRequirements,
|
||||
> = HashMap::default();
|
||||
let mut fragment_tests_stages = None;
|
||||
let mut required_vertex_inputs = None;
|
||||
|
||||
for stage in &stages {
|
||||
let &PipelineShaderStageCreateInfo {
|
||||
@ -915,22 +936,33 @@ impl GraphicsPipeline {
|
||||
let spirv = entry_point.module().spirv();
|
||||
let entry_point_function = spirv.function(entry_point.id());
|
||||
|
||||
if matches!(entry_point_info.execution_model, ExecutionModel::Fragment) {
|
||||
fragment_tests_stages = Some(FragmentTestsStages::Late);
|
||||
match entry_point_info.execution_model {
|
||||
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() {
|
||||
if let Instruction::ExecutionMode { mode, .. } = *instruction {
|
||||
match mode {
|
||||
ExecutionMode::EarlyFragmentTests => {
|
||||
fragment_tests_stages = Some(FragmentTestsStages::Early);
|
||||
for instruction in entry_point_function.execution_modes() {
|
||||
if let Instruction::ExecutionMode { mode, .. } = *instruction {
|
||||
match mode {
|
||||
ExecutionMode::EarlyFragmentTests => {
|
||||
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 {
|
||||
@ -1022,11 +1054,7 @@ impl GraphicsPipeline {
|
||||
|
||||
flags,
|
||||
shaders,
|
||||
descriptor_binding_requirements,
|
||||
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
|
||||
vertex_input_state,
|
||||
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,
|
||||
viewport_state,
|
||||
@ -1040,7 +1068,11 @@ impl GraphicsPipeline {
|
||||
|
||||
discard_rectangle_state,
|
||||
|
||||
descriptor_binding_requirements,
|
||||
num_used_descriptor_sets,
|
||||
fixed_state,
|
||||
fragment_tests_stages,
|
||||
required_vertex_inputs,
|
||||
})
|
||||
}
|
||||
|
||||
@ -1070,8 +1102,8 @@ impl GraphicsPipeline {
|
||||
|
||||
/// Returns the vertex input state used to create this pipeline.
|
||||
#[inline]
|
||||
pub fn vertex_input_state(&self) -> &VertexInputState {
|
||||
&self.vertex_input_state
|
||||
pub fn vertex_input_state(&self) -> Option<&VertexInputState> {
|
||||
self.vertex_input_state.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the input assembly state used to create this pipeline.
|
||||
@ -1145,6 +1177,14 @@ impl GraphicsPipeline {
|
||||
pub(crate) fn fixed_state(&self) -> &HashSet<DynamicState> {
|
||||
&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 {
|
||||
@ -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) => {
|
||||
return Err(Box::new(ValidationError {
|
||||
problem: "the pipeline is not being created with \
|
||||
vertex input state, but \
|
||||
problem: "the pipeline is not being created with vertex input state, or \
|
||||
`dynamic_state` includes `DynamicState::VertexInput`, but \
|
||||
`vertex_input_state` is `Some`"
|
||||
.into(),
|
||||
..Default::default()
|
||||
@ -1655,8 +1698,8 @@ impl GraphicsPipelineCreateInfo {
|
||||
}
|
||||
(false, true) => {
|
||||
return Err(Box::new(ValidationError {
|
||||
problem: "the pipeline is being created with \
|
||||
vertex input state, but \
|
||||
problem: "the pipeline is being created with vertex input state, and \
|
||||
`dynamic_state` does not include `DynamicState::VertexInput`, but \
|
||||
`vertex_input_state` is `None`"
|
||||
.into(),
|
||||
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) {
|
||||
for element in vertex_stage.entry_point.info().input_interface.elements() {
|
||||
assert!(!element.ty.is_64bit); // TODO: implement
|
||||
let location_range =
|
||||
element.location..element.location + element.ty.num_locations();
|
||||
let required_vertex_inputs = vertex_input::required_vertex_inputs(
|
||||
vertex_stage.entry_point.module().spirv(),
|
||||
vertex_stage.entry_point.id(),
|
||||
);
|
||||
|
||||
for location in location_range {
|
||||
let attribute_desc = match vertex_input_state.attributes.get(&location) {
|
||||
Some(attribute_desc) => attribute_desc,
|
||||
None => {
|
||||
return Err(Box::new(ValidationError {
|
||||
problem: format!(
|
||||
"the vertex shader has an input variable with location {0}, but \
|
||||
`vertex_input_state.attributes` does not contain {0}",
|
||||
location,
|
||||
)
|
||||
.into(),
|
||||
vuids: &["VUID-VkGraphicsPipelineCreateInfo-Input-07905"],
|
||||
..Default::default()
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Check component assignments too. Multiple variables can occupy the
|
||||
// same location but in different components.
|
||||
|
||||
let shader_type = element.ty.base_type;
|
||||
let attribute_type = attribute_desc
|
||||
.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()
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
vertex_input::validate_required_vertex_inputs(
|
||||
&vertex_input_state.attributes,
|
||||
&required_vertex_inputs,
|
||||
RequiredVertexInputsVUIDs {
|
||||
not_present: &["VUID-VkGraphicsPipelineCreateInfo-Input-07904"],
|
||||
numeric_type: &["VUID-VkGraphicsPipelineCreateInfo-Input-08733"],
|
||||
requires32: &["VUID-VkGraphicsPipelineCreateInfo-pVertexInputState-08929"],
|
||||
requires64: &["VUID-VkGraphicsPipelineCreateInfo-pVertexInputState-08930"],
|
||||
requires_second_half: &[
|
||||
"VUID-VkGraphicsPipelineCreateInfo-pVertexInputState-09198",
|
||||
],
|
||||
},
|
||||
)
|
||||
.map_err(|mut err| {
|
||||
err.problem = format!(
|
||||
"{}: {}",
|
||||
"`vertex_input_state` does not meet the requirements \
|
||||
of the vertex shader in `stages`",
|
||||
err.problem,
|
||||
)
|
||||
.into();
|
||||
err
|
||||
})?;
|
||||
}
|
||||
|
||||
if let (Some(_), Some(_)) = (tessellation_control_stage, tessellation_evaluation_stage) {
|
||||
|
@ -27,6 +27,7 @@ unsafe impl VertexDefinition for &[VertexBufferDescription] {
|
||||
VertexInputBindingDescription {
|
||||
stride: buffer.stride,
|
||||
input_rate: buffer.input_rate,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
});
|
||||
@ -91,6 +92,7 @@ unsafe impl VertexDefinition for &[VertexBufferDescription] {
|
||||
binding,
|
||||
format: infos.format,
|
||||
offset: offset as u32,
|
||||
..Default::default()
|
||||
},
|
||||
));
|
||||
offset += block_size;
|
||||
|
@ -99,10 +99,15 @@ pub use self::{
|
||||
};
|
||||
use crate::{
|
||||
device::Device,
|
||||
format::{Format, FormatFeatures},
|
||||
format::{Format, FormatFeatures, NumericType},
|
||||
shader::{
|
||||
reflect::get_constant,
|
||||
spirv::{Decoration, Id, Instruction, Spirv, StorageClass},
|
||||
},
|
||||
DeviceSize, Requires, RequiresAllOf, RequiresOneOf, ValidationError,
|
||||
};
|
||||
use ahash::HashMap;
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
mod buffers;
|
||||
mod collection;
|
||||
@ -185,15 +190,33 @@ impl VertexInputState {
|
||||
problem: "the length exceeds the `max_vertex_input_bindings` limit".into(),
|
||||
vuids: &[
|
||||
"VUID-VkPipelineVertexInputStateCreateInfo-vertexBindingDescriptionCount-00613",
|
||||
"VUID-vkCmdSetVertexInputEXT-vertexBindingDescriptionCount-04791",
|
||||
],
|
||||
..Default::default()
|
||||
}));
|
||||
}
|
||||
|
||||
// VUID-VkPipelineVertexInputStateCreateInfo-pVertexBindingDescriptions-00616
|
||||
// VUID-vkCmdSetVertexInputEXT-pVertexBindingDescriptions-04794
|
||||
// Ensured by HashMap.
|
||||
|
||||
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
|
||||
.validate(device)
|
||||
.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(),
|
||||
vuids: &[
|
||||
"VUID-VkPipelineVertexInputStateCreateInfo-vertexAttributeDescriptionCount-00614",
|
||||
"VUID-vkCmdSetVertexInputEXT-vertexAttributeDescriptionCount-04792",
|
||||
],
|
||||
..Default::default()
|
||||
}));
|
||||
}
|
||||
|
||||
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
|
||||
.validate(device)
|
||||
.map_err(|err| err.add_context(format!("attributes[{}]", location)))?;
|
||||
@ -219,21 +259,9 @@ impl VertexInputState {
|
||||
binding,
|
||||
format,
|
||||
offset,
|
||||
_ne: _,
|
||||
} = 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(|| {
|
||||
Box::new(ValidationError {
|
||||
problem: format!(
|
||||
@ -241,7 +269,10 @@ impl VertexInputState {
|
||||
binding
|
||||
)
|
||||
.into(),
|
||||
vuids: &["VUID-VkPipelineVertexInputStateCreateInfo-binding-00615"],
|
||||
vuids: &[
|
||||
"VUID-VkPipelineVertexInputStateCreateInfo-binding-00615",
|
||||
"VUID-vkCmdSetVertexInputEXT-binding-04793",
|
||||
],
|
||||
..Default::default()
|
||||
})
|
||||
})?;
|
||||
@ -263,7 +294,10 @@ impl VertexInputState {
|
||||
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
|
||||
"vertex_attribute_access_beyond_stride",
|
||||
)])]),
|
||||
vuids: &["VUID-VkVertexInputAttributeDescription-vertexAttributeAccessBeyondStride-04457"],
|
||||
vuids: &[
|
||||
"VUID-VkVertexInputAttributeDescription-vertexAttributeAccessBeyondStride-04457",
|
||||
"VUID-VkVertexInputAttributeDescription2EXT-vertexAttributeAccessBeyondStride-04806",
|
||||
],
|
||||
..Default::default()
|
||||
}));
|
||||
}
|
||||
@ -285,7 +319,10 @@ impl VertexInputState {
|
||||
location - 1, location,
|
||||
)
|
||||
.into(),
|
||||
vuids: &["VUID-VkPipelineVertexInputStateCreateInfo-pVertexAttributeDescriptions-00617"],
|
||||
vuids: &[
|
||||
"VUID-VkPipelineVertexInputStateCreateInfo-pVertexAttributeDescriptions-00617",
|
||||
"VUID-vkCmdSetVertexInputEXT-pVertexAttributeDescriptions-04795",
|
||||
],
|
||||
..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
|
||||
/// next element. This can be simply the size of the data in each element, but larger strides
|
||||
/// are possible.
|
||||
///
|
||||
/// The default value is `0`, which must be overridden.
|
||||
pub stride: u32,
|
||||
|
||||
/// How often the vertex input should advance to the next element.
|
||||
///
|
||||
/// The default value is [`VertexInputRate::Vertex`].
|
||||
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 {
|
||||
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();
|
||||
|
||||
@ -328,7 +386,10 @@ impl VertexInputBindingDescription {
|
||||
return Err(Box::new(ValidationError {
|
||||
context: "stride".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()
|
||||
}));
|
||||
}
|
||||
@ -364,7 +425,10 @@ impl VertexInputBindingDescription {
|
||||
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
|
||||
"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(
|
||||
"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 \
|
||||
its `divisor` value exceeds the `max_vertex_attrib_divisor` limit"
|
||||
.into(),
|
||||
vuids: &["VUID-VkVertexInputBindingDivisorDescriptionEXT-divisor-01870"],
|
||||
vuids: &[
|
||||
"VUID-VkVertexInputBindingDivisorDescriptionEXT-divisor-01870",
|
||||
"VUID-VkVertexInputBindingDescription2EXT-divisor-06226",
|
||||
],
|
||||
..Default::default()
|
||||
}));
|
||||
}
|
||||
@ -406,9 +476,13 @@ impl VertexInputBindingDescription {
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct VertexInputAttributeDescription {
|
||||
/// The vertex buffer binding number that this attribute should take its data from.
|
||||
///
|
||||
/// The default value is `0`.
|
||||
pub binding: u32,
|
||||
|
||||
/// The size and type of the vertex data.
|
||||
///
|
||||
/// The default value is [`Format::UNDEFINED`], which must be overridden.
|
||||
pub format: Format,
|
||||
|
||||
/// 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
|
||||
/// [`vertex_attribute_access_beyond_stride`](crate::device::Features::vertex_attribute_access_beyond_stride)
|
||||
/// feature must be enabled on the device.
|
||||
///
|
||||
/// The default value is `0`.
|
||||
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 {
|
||||
@ -427,20 +517,26 @@ impl VertexInputAttributeDescription {
|
||||
binding,
|
||||
format,
|
||||
offset,
|
||||
_ne: _,
|
||||
} = self;
|
||||
|
||||
let properties = device.physical_device().properties();
|
||||
|
||||
format.validate_device(device).map_err(|err| {
|
||||
err.add_context("format")
|
||||
.set_vuids(&["VUID-VkVertexInputAttributeDescription-format-parameter"])
|
||||
err.add_context("format").set_vuids(&[
|
||||
"VUID-VkVertexInputAttributeDescription-format-parameter",
|
||||
"VUID-VkVertexInputAttributeDescription2EXT-format-parameter",
|
||||
])
|
||||
})?;
|
||||
|
||||
if binding > properties.max_vertex_input_bindings {
|
||||
return Err(Box::new(ValidationError {
|
||||
context: "binding".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()
|
||||
}));
|
||||
}
|
||||
@ -449,7 +545,10 @@ impl VertexInputAttributeDescription {
|
||||
return Err(Box::new(ValidationError {
|
||||
context: "offset".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()
|
||||
}));
|
||||
}
|
||||
@ -466,7 +565,10 @@ impl VertexInputAttributeDescription {
|
||||
context: "format".into(),
|
||||
problem: "the format features do not include `FormatFeatures::VERTEX_BUFFER`"
|
||||
.into(),
|
||||
vuids: &["VUID-VkVertexInputAttributeDescription-format-00623"],
|
||||
vuids: &[
|
||||
"VUID-VkVertexInputAttributeDescription-format-00623",
|
||||
"VUID-VkVertexInputAttributeDescription2EXT-format-04805",
|
||||
],
|
||||
..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)]),
|
||||
]),
|
||||
|
||||
/* TODO: enable
|
||||
// TODO: document
|
||||
/// The `Option` variant of
|
||||
/// [`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
|
||||
RequiresOneOf([
|
||||
RequiresAllOf([DeviceExtension(ext_vertex_input_dynamic_state)]),
|
||||
]), */
|
||||
]),
|
||||
|
||||
/// The value of
|
||||
/// [`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)]
|
||||
mod tests {
|
||||
use super::{HashMap, PushConstantRange, ShaderStages, Version};
|
||||
|
Loading…
Reference in New Issue
Block a user