Refactor DescriptorRequirements (#2081)

This commit is contained in:
Rua 2022-11-09 10:35:50 +01:00 committed by GitHub
parent 021540e6b6
commit 4ccfc925fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1125 additions and 1055 deletions

View File

@ -348,7 +348,9 @@ fn main() {
let pipeline_layout = {
let mut layout_create_infos: Vec<_> = DescriptorSetLayoutCreateInfo::from_requirements(
fs.entry_point("main").unwrap().descriptor_requirements(),
fs.entry_point("main")
.unwrap()
.descriptor_binding_requirements(),
);
// Set 0, Binding 0

View File

@ -692,7 +692,7 @@ mod tests {
let mut descriptors = Vec::new();
for (_, _, info) in reflect::entry_points(&spirv) {
descriptors.push(info.descriptor_requirements);
descriptors.push(info.descriptor_binding_requirements);
}
// Check first entrypoint
@ -764,7 +764,7 @@ mod tests {
if let Some((_, _, info)) = reflect::entry_points(&spirv).next() {
let mut bindings = Vec::new();
for (loc, _reqs) in info.descriptor_requirements {
for (loc, _reqs) in info.descriptor_binding_requirements {
bindings.push(loc);
}
assert_eq!(bindings.len(), 4);

View File

@ -12,9 +12,10 @@ use proc_macro2::TokenStream;
use vulkano::{
pipeline::layout::PushConstantRange,
shader::{
spirv::ExecutionModel, DescriptorIdentifier, DescriptorRequirements, EntryPointInfo,
ShaderExecution, ShaderInterface, ShaderInterfaceEntry, ShaderInterfaceEntryType,
ShaderStages, SpecializationConstantRequirements,
spirv::ExecutionModel, DescriptorBindingRequirements, DescriptorIdentifier,
DescriptorRequirements, EntryPointInfo, ShaderExecution, ShaderInterface,
ShaderInterfaceEntry, ShaderInterfaceEntryType, ShaderStages,
SpecializationConstantRequirements,
},
};
@ -29,7 +30,8 @@ pub(super) fn write_entry_point(
model
))
.unwrap();
let descriptor_requirements = write_descriptor_requirements(&info.descriptor_requirements);
let descriptor_binding_requirements =
write_descriptor_binding_requirements(&info.descriptor_binding_requirements);
let push_constant_requirements =
write_push_constant_requirements(&info.push_constant_requirements);
let specialization_constant_requirements =
@ -43,7 +45,7 @@ pub(super) fn write_entry_point(
#model,
::vulkano::shader::EntryPointInfo {
execution: #execution,
descriptor_requirements: #descriptor_requirements.into_iter().collect(),
descriptor_binding_requirements: #descriptor_binding_requirements.into_iter().collect(),
push_constant_requirements: #push_constant_requirements,
specialization_constant_requirements: #specialization_constant_requirements.into_iter().collect(),
input_interface: #input_interface,
@ -85,29 +87,26 @@ fn write_shader_execution(execution: &ShaderExecution) -> TokenStream {
}
}
fn write_descriptor_requirements(
descriptor_requirements: &HashMap<(u32, u32), DescriptorRequirements>,
fn write_descriptor_binding_requirements(
descriptor_binding_requirements: &HashMap<(u32, u32), DescriptorBindingRequirements>,
) -> TokenStream {
let descriptor_requirements = descriptor_requirements.iter().map(|(loc, reqs)| {
let descriptor_binding_requirements =
descriptor_binding_requirements
.iter()
.map(|(loc, binding_reqs)| {
let (set_num, binding_num) = loc;
let DescriptorRequirements {
let DescriptorBindingRequirements {
descriptor_types,
descriptor_count,
image_format,
image_multisampled,
image_scalar_type,
image_view_type,
sampler_compare,
sampler_no_unnormalized_coordinates,
sampler_no_ycbcr_conversion,
sampler_with_images,
stages,
storage_image_atomic,
storage_read,
storage_write,
} = reqs;
descriptors,
} = binding_reqs;
let descriptor_types = descriptor_types.iter().map(|ty| {
let descriptor_types_items = descriptor_types.iter().map(|ty| {
let ident = format_ident!("{}", format!("{:?}", ty));
quote! { ::vulkano::descriptor_set::layout::DescriptorType::#ident }
});
@ -136,13 +135,25 @@ fn write_descriptor_requirements(
}
None => quote! { None },
};
let sampler_compare = sampler_compare.iter();
let sampler_no_unnormalized_coordinates = sampler_no_unnormalized_coordinates.iter();
let sampler_no_ycbcr_conversion = sampler_no_ycbcr_conversion.iter();
let sampler_with_images = {
sampler_with_images.iter().map(|(&index, identifiers)| {
let identifiers = identifiers.iter().map(
|DescriptorIdentifier {
let stages = stages_to_items(*stages);
let descriptor_items = descriptors.iter().map(|(index, desc_reqs)| {
let DescriptorRequirements {
memory_read,
memory_write,
sampler_compare,
sampler_no_unnormalized_coordinates,
sampler_no_ycbcr_conversion,
sampler_with_images,
storage_image_atomic,
} = desc_reqs;
let index = match index {
Some(index) => quote! { Some(#index) },
None => quote! { None },
};
let memory_read = stages_to_items(*memory_read);
let memory_write = stages_to_items(*memory_write);
let sampler_with_images_items = sampler_with_images.iter().map(|DescriptorIdentifier {
set,
binding,
index,
@ -154,82 +165,36 @@ fn write_descriptor_requirements(
index: #index,
}
}
},
);
});
quote! {
(
#index,
[#(#identifiers),*].into_iter().collect(),
::vulkano::shader::DescriptorRequirements {
memory_read: #memory_read,
memory_write: #memory_write,
sampler_compare: #sampler_compare,
sampler_no_unnormalized_coordinates: #sampler_no_unnormalized_coordinates,
sampler_no_ycbcr_conversion: #sampler_no_ycbcr_conversion,
sampler_with_images: [#(#sampler_with_images_items),*].into_iter().collect(),
storage_image_atomic: #storage_image_atomic,
}
)
}
})
};
let stages = {
let stages_items = [
stages.intersects(ShaderStages::VERTEX).then(|| quote! {
::vulkano::shader::ShaderStages::VERTEX
}),
stages.intersects(ShaderStages::TESSELLATION_CONTROL).then(|| quote! {
::vulkano::shader::ShaderStages::TESSELLATION_CONTROL
}),
stages.intersects(ShaderStages::TESSELLATION_EVALUATION).then(|| quote! {
::vulkano::shader::ShaderStages::TESSELLATION_EVALUATION
}),
stages.intersects(ShaderStages::GEOMETRY).then(|| quote! {
::vulkano::shader::ShaderStages::GEOMETRY
}),
stages.intersects(ShaderStages::FRAGMENT).then(|| quote! {
::vulkano::shader::ShaderStages::FRAGMENT
}),
stages.intersects(ShaderStages::COMPUTE).then(|| quote! {
::vulkano::shader::ShaderStages::COMPUTE
}),
stages.intersects(ShaderStages::RAYGEN).then(|| quote! {
::vulkano::shader::ShaderStages::RAYGEN
}),
stages.intersects(ShaderStages::ANY_HIT).then(|| quote! {
::vulkano::shader::ShaderStages::ANY_HIT
}),
stages.intersects(ShaderStages::CLOSEST_HIT).then(|| quote! {
::vulkano::shader::ShaderStages::CLOSEST_HIT
}),
stages.intersects(ShaderStages::MISS).then(|| quote! {
::vulkano::shader::ShaderStages::MISS
}),
stages.intersects(ShaderStages::INTERSECTION).then(|| quote! {
::vulkano::shader::ShaderStages::INTERSECTION
}),
stages.intersects(ShaderStages::CALLABLE).then(|| quote! {
::vulkano::shader::ShaderStages::CALLABLE
}),
].into_iter().flatten();
quote! {
#(#stages_items)|*
}
};
let storage_image_atomic = storage_image_atomic.iter();
let storage_read = storage_read.iter();
let storage_write = storage_write.iter();
});
quote! {
(
(#set_num, #binding_num),
::vulkano::shader::DescriptorRequirements {
descriptor_types: vec![#(#descriptor_types),*],
::vulkano::shader::DescriptorBindingRequirements {
descriptor_types: vec![#(#descriptor_types_items),*],
descriptor_count: #descriptor_count,
image_format: #image_format,
image_multisampled: #image_multisampled,
image_scalar_type: #image_scalar_type,
image_view_type: #image_view_type,
sampler_compare: [#(#sampler_compare),*].into_iter().collect(),
sampler_no_unnormalized_coordinates: [#(#sampler_no_unnormalized_coordinates),*].into_iter().collect(),
sampler_no_ycbcr_conversion: [#(#sampler_no_ycbcr_conversion),*].into_iter().collect(),
sampler_with_images: [#(#sampler_with_images),*].into_iter().collect(),
stages: #stages,
storage_image_atomic: [#(#storage_image_atomic),*].into_iter().collect(),
storage_read: [#(#storage_read),*].into_iter().collect(),
storage_write: [#(#storage_write),*].into_iter().collect(),
descriptors: [#(#descriptor_items),*].into_iter().collect(),
},
),
}
@ -237,7 +202,7 @@ fn write_descriptor_requirements(
quote! {
[
#( #descriptor_requirements )*
#( #descriptor_binding_requirements )*
]
}
}
@ -251,80 +216,7 @@ fn write_push_constant_requirements(
size,
stages,
}) => {
let stages = {
let stages_items = [
stages.intersects(ShaderStages::VERTEX).then(|| {
quote! {
::vulkano::shader::ShaderStages::VERTEX
}
}),
stages
.intersects(ShaderStages::TESSELLATION_CONTROL)
.then(|| {
quote! {
::vulkano::shader::ShaderStages::TESSELLATION_CONTROL
}
}),
stages
.intersects(ShaderStages::TESSELLATION_EVALUATION)
.then(|| {
quote! {
::vulkano::shader::ShaderStages::TESSELLATION_EVALUATION
}
}),
stages.intersects(ShaderStages::GEOMETRY).then(|| {
quote! {
::vulkano::shader::ShaderStages::GEOMETRY
}
}),
stages.intersects(ShaderStages::FRAGMENT).then(|| {
quote! {
::vulkano::shader::ShaderStages::FRAGMENT
}
}),
stages.intersects(ShaderStages::COMPUTE).then(|| {
quote! {
::vulkano::shader::ShaderStages::COMPUTE
}
}),
stages.intersects(ShaderStages::RAYGEN).then(|| {
quote! {
::vulkano::shader::ShaderStages::RAYGEN
}
}),
stages.intersects(ShaderStages::ANY_HIT).then(|| {
quote! {
::vulkano::shader::ShaderStages::ANY_HIT
}
}),
stages.intersects(ShaderStages::CLOSEST_HIT).then(|| {
quote! {
::vulkano::shader::ShaderStages::CLOSEST_HIT
}
}),
stages.intersects(ShaderStages::MISS).then(|| {
quote! {
::vulkano::shader::ShaderStages::MISS
}
}),
stages.intersects(ShaderStages::INTERSECTION).then(|| {
quote! {
::vulkano::shader::ShaderStages::INTERSECTION
}
}),
stages.intersects(ShaderStages::CALLABLE).then(|| {
quote! {
::vulkano::shader::ShaderStages::CALLABLE
}
}),
]
.into_iter()
.flatten();
quote! {
#(#stages_items)|*
}
};
let stages = stages_to_items(*stages);
quote! {
Some(::vulkano::pipeline::layout::PushConstantRange {
@ -403,3 +295,56 @@ fn write_interface(interface: &ShaderInterface) -> TokenStream {
])
}
}
fn stages_to_items(stages: ShaderStages) -> TokenStream {
if stages.is_empty() {
quote! { ::vulkano::shader::ShaderStages::empty() }
} else {
let stages_items = [
stages.intersects(ShaderStages::VERTEX).then(|| {
quote! { ::vulkano::shader::ShaderStages::VERTEX }
}),
stages
.intersects(ShaderStages::TESSELLATION_CONTROL)
.then(|| {
quote! { ::vulkano::shader::ShaderStages::TESSELLATION_CONTROL }
}),
stages
.intersects(ShaderStages::TESSELLATION_EVALUATION)
.then(|| {
quote! { ::vulkano::shader::ShaderStages::TESSELLATION_EVALUATION }
}),
stages.intersects(ShaderStages::GEOMETRY).then(|| {
quote! { ::vulkano::shader::ShaderStages::GEOMETRY }
}),
stages.intersects(ShaderStages::FRAGMENT).then(|| {
quote! { ::vulkano::shader::ShaderStages::FRAGMENT }
}),
stages.intersects(ShaderStages::COMPUTE).then(|| {
quote! { ::vulkano::shader::ShaderStages::COMPUTE }
}),
stages.intersects(ShaderStages::RAYGEN).then(|| {
quote! { ::vulkano::shader::ShaderStages::RAYGEN }
}),
stages.intersects(ShaderStages::ANY_HIT).then(|| {
quote! { ::vulkano::shader::ShaderStages::ANY_HIT }
}),
stages.intersects(ShaderStages::CLOSEST_HIT).then(|| {
quote! { ::vulkano::shader::ShaderStages::CLOSEST_HIT }
}),
stages.intersects(ShaderStages::MISS).then(|| {
quote! { ::vulkano::shader::ShaderStages::MISS }
}),
stages.intersects(ShaderStages::INTERSECTION).then(|| {
quote! { ::vulkano::shader::ShaderStages::INTERSECTION }
}),
stages.intersects(ShaderStages::CALLABLE).then(|| {
quote! { ::vulkano::shader::ShaderStages::CALLABLE }
}),
]
.into_iter()
.flatten();
quote! { #(#stages_items)|* }
}
}

View File

@ -33,8 +33,7 @@ pub(super) fn write_structs<'a>(
})
.filter(|&(struct_id, _member_types)| has_defined_layout(spirv, struct_id))
.filter_map(|(struct_id, member_types)| {
let (rust_members, is_sized) =
write_struct_members(shader, spirv, struct_id, member_types);
let (rust_members, is_sized) = write_struct_members(spirv, struct_id, member_types);
let struct_name = spirv
.id(struct_id)
@ -86,12 +85,7 @@ struct Member {
signature: Cow<'static, str>,
}
fn write_struct_members<'a>(
shader: &'a str,
spirv: &Spirv,
struct_id: Id,
members: &[Id],
) -> (Vec<Member>, bool) {
fn write_struct_members(spirv: &Spirv, struct_id: Id, members: &[Id]) -> (Vec<Member>, bool) {
let mut rust_members = Vec::with_capacity(members.len());
// Dummy members will be named `_dummyN` where `N` is determined by this variable.
@ -107,7 +101,7 @@ fn write_struct_members<'a>(
.enumerate()
{
// Compute infos about the member.
let (ty, signature, rust_size, rust_align) = type_from_id(shader, spirv, member);
let (ty, signature, rust_size, rust_align) = type_from_id(spirv, member);
let member_name = member_info
.iter_name()
.find_map(|instruction| match instruction {
@ -404,7 +398,6 @@ fn struct_size_from_array_stride(spirv: &Spirv, type_id: Id) -> Option<u32> {
///
/// The size can be `None` if it's only known at runtime.
pub(super) fn type_from_id(
shader: &str,
spirv: &Spirv,
type_id: Id,
) -> (TokenStream, Cow<'static, str>, Option<usize>, usize) {
@ -558,7 +551,7 @@ pub(super) fn type_from_id(
..
} => {
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
let (ty, item, t_size, t_align) = type_from_id(shader, spirv, component_type);
let (ty, item, t_size, t_align) = type_from_id(spirv, component_type);
let array_length = component_count as usize;
let size = t_size.map(|s| s * component_count as usize);
(
@ -575,7 +568,7 @@ pub(super) fn type_from_id(
} => {
// FIXME: row-major or column-major
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
let (ty, item, t_size, t_align) = type_from_id(shader, spirv, column_type);
let (ty, item, t_size, t_align) = type_from_id(spirv, column_type);
let array_length = column_count as usize;
let size = t_size.map(|s| s * column_count as usize);
(
@ -593,7 +586,7 @@ pub(super) fn type_from_id(
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
let (element_type, element_type_string, element_size, element_align) =
type_from_id(shader, spirv, element_type);
type_from_id(spirv, element_type);
let element_size = element_size.expect("array components must be sized");
let array_length = match spirv.id(length).instruction() {
@ -631,7 +624,7 @@ pub(super) fn type_from_id(
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
let (element_type, element_type_string, _, element_align) =
type_from_id(shader, spirv, element_type);
type_from_id(spirv, element_type);
(
quote! { [#element_type] },
@ -667,7 +660,7 @@ pub(super) fn type_from_id(
_ => None,
})
.unwrap();
let (_, _, rust_size, _) = type_from_id(shader, spirv, member);
let (_, _, rust_size, _) = type_from_id(spirv, member);
rust_size.map(|rust_size| spirv_offset + rust_size)
})
})
@ -675,7 +668,7 @@ pub(super) fn type_from_id(
let align = member_types
.iter()
.map(|&t| type_from_id(shader, spirv, t).3)
.map(|&t| type_from_id(spirv, t).3)
.max()
.unwrap_or(1);
@ -764,7 +757,7 @@ pub(super) fn write_specialization_constants<'a>(
Some(mem::size_of::<u32>()),
mem::align_of::<u32>(),
),
_ => type_from_id(shader, spirv, result_type_id),
_ => type_from_id(spirv, result_type_id),
};
let rust_size = rust_size.expect("Found runtime-sized specialization constant");

View File

@ -34,7 +34,7 @@ use crate::{
PipelineLayout,
},
sampler::{Sampler, SamplerImageViewIncompatibleError},
shader::{DescriptorRequirements, ShaderScalarType, ShaderStage},
shader::{DescriptorBindingRequirements, ShaderScalarType, ShaderStage},
sync::{AccessFlags, PipelineMemoryAccess, PipelineStages},
DeviceSize, RequiresOneOf, VulkanObject,
};
@ -95,7 +95,10 @@ where
None => return Err(PipelineExecutionError::PipelineNotBound),
};
self.validate_pipeline_descriptor_sets(pipeline, pipeline.descriptor_requirements())?;
self.validate_pipeline_descriptor_sets(
pipeline,
pipeline.descriptor_binding_requirements(),
)?;
self.validate_pipeline_push_constants(pipeline.layout())?;
let max = self
@ -164,7 +167,10 @@ where
None => return Err(PipelineExecutionError::PipelineNotBound),
};
self.validate_pipeline_descriptor_sets(pipeline, pipeline.descriptor_requirements())?;
self.validate_pipeline_descriptor_sets(
pipeline,
pipeline.descriptor_binding_requirements(),
)?;
self.validate_pipeline_push_constants(pipeline.layout())?;
self.validate_indirect_buffer(indirect_buffer)?;
@ -224,7 +230,10 @@ where
None => return Err(PipelineExecutionError::PipelineNotBound),
};
self.validate_pipeline_descriptor_sets(pipeline, pipeline.descriptor_requirements())?;
self.validate_pipeline_descriptor_sets(
pipeline,
pipeline.descriptor_binding_requirements(),
)?;
self.validate_pipeline_push_constants(pipeline.layout())?;
self.validate_pipeline_graphics_dynamic_state(pipeline)?;
self.validate_pipeline_graphics_render_pass(pipeline, render_pass_state)?;
@ -295,7 +304,10 @@ where
None => return Err(PipelineExecutionError::PipelineNotBound),
};
self.validate_pipeline_descriptor_sets(pipeline, pipeline.descriptor_requirements())?;
self.validate_pipeline_descriptor_sets(
pipeline,
pipeline.descriptor_binding_requirements(),
)?;
self.validate_pipeline_push_constants(pipeline.layout())?;
self.validate_pipeline_graphics_dynamic_state(pipeline)?;
self.validate_pipeline_graphics_render_pass(pipeline, render_pass_state)?;
@ -406,7 +418,10 @@ where
None => return Err(PipelineExecutionError::PipelineNotBound),
};
self.validate_pipeline_descriptor_sets(pipeline, pipeline.descriptor_requirements())?;
self.validate_pipeline_descriptor_sets(
pipeline,
pipeline.descriptor_binding_requirements(),
)?;
self.validate_pipeline_push_constants(pipeline.layout())?;
self.validate_pipeline_graphics_dynamic_state(pipeline)?;
self.validate_pipeline_graphics_render_pass(pipeline, render_pass_state)?;
@ -484,7 +499,10 @@ where
None => return Err(PipelineExecutionError::PipelineNotBound),
};
self.validate_pipeline_descriptor_sets(pipeline, pipeline.descriptor_requirements())?;
self.validate_pipeline_descriptor_sets(
pipeline,
pipeline.descriptor_binding_requirements(),
)?;
self.validate_pipeline_push_constants(pipeline.layout())?;
self.validate_pipeline_graphics_dynamic_state(pipeline)?;
self.validate_pipeline_graphics_render_pass(pipeline, render_pass_state)?;
@ -569,16 +587,18 @@ where
fn validate_pipeline_descriptor_sets<'a, Pl: Pipeline>(
&self,
pipeline: &Pl,
descriptor_requirements: impl IntoIterator<Item = ((u32, u32), &'a DescriptorRequirements)>,
descriptor_binding_requirements: impl IntoIterator<
Item = ((u32, u32), &'a DescriptorBindingRequirements),
>,
) -> Result<(), PipelineExecutionError> {
fn validate_resources<T>(
set_num: u32,
binding_num: u32,
reqs: &DescriptorRequirements,
binding_reqs: &DescriptorBindingRequirements,
elements: &[Option<T>],
mut extra_check: impl FnMut(u32, &T) -> Result<(), DescriptorResourceInvalidError>,
) -> Result<(), PipelineExecutionError> {
let elements_to_check = if let Some(descriptor_count) = reqs.descriptor_count {
let elements_to_check = if let Some(descriptor_count) = binding_reqs.descriptor_count {
// The shader has a fixed-sized array, so it will never access more than
// the first `descriptor_count` elements.
elements.get(..descriptor_count as usize).ok_or({
@ -646,36 +666,36 @@ where
return Err(PipelineExecutionError::PipelineLayoutNotCompatible);
}
for ((set_num, binding_num), reqs) in descriptor_requirements {
for ((set_num, binding_num), binding_reqs) in descriptor_binding_requirements {
let layout_binding =
&pipeline.layout().set_layouts()[set_num as usize].bindings()[&binding_num];
let check_buffer = |_index: u32, _buffer: &Arc<dyn BufferAccess>| Ok(());
let check_buffer_view = |index: u32, buffer_view: &Arc<dyn BufferViewAbstract>| {
for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter())
.chain(binding_reqs.descriptors.get(&None))
{
if layout_binding.descriptor_type == DescriptorType::StorageTexelBuffer {
// VUID-vkCmdDispatch-OpTypeImage-06423
if reqs.image_format.is_none()
&& reqs.storage_write.contains(&index)
if binding_reqs.image_format.is_none()
&& !desc_reqs.memory_write.is_empty()
&& !buffer_view
.format_features()
.intersects(FormatFeatures::STORAGE_WRITE_WITHOUT_FORMAT)
{
return Err(
DescriptorResourceInvalidError::StorageWriteWithoutFormatNotSupported,
);
return Err(DescriptorResourceInvalidError::StorageWriteWithoutFormatNotSupported);
}
// VUID-vkCmdDispatch-OpTypeImage-06424
if reqs.image_format.is_none()
&& reqs.storage_read.contains(&index)
if binding_reqs.image_format.is_none()
&& !desc_reqs.memory_read.is_empty()
&& !buffer_view
.format_features()
.intersects(FormatFeatures::STORAGE_READ_WITHOUT_FORMAT)
{
return Err(
DescriptorResourceInvalidError::StorageReadWithoutFormatNotSupported,
);
return Err(DescriptorResourceInvalidError::StorageReadWithoutFormatNotSupported);
}
}
}
@ -683,8 +703,11 @@ where
};
let check_image_view_common = |index: u32, image_view: &Arc<dyn ImageViewAbstract>| {
for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter())
.chain(binding_reqs.descriptors.get(&None))
{
// VUID-vkCmdDispatch-None-02691
if reqs.storage_image_atomic.contains(&index)
if desc_reqs.storage_image_atomic
&& !image_view
.format_features()
.intersects(FormatFeatures::STORAGE_IMAGE_ATOMIC)
@ -694,8 +717,8 @@ where
if layout_binding.descriptor_type == DescriptorType::StorageImage {
// VUID-vkCmdDispatch-OpTypeImage-06423
if reqs.image_format.is_none()
&& reqs.storage_write.contains(&index)
if binding_reqs.image_format.is_none()
&& !desc_reqs.memory_write.is_empty()
&& !image_view
.format_features()
.intersects(FormatFeatures::STORAGE_WRITE_WITHOUT_FORMAT)
@ -706,8 +729,8 @@ where
}
// VUID-vkCmdDispatch-OpTypeImage-06424
if reqs.image_format.is_none()
&& reqs.storage_read.contains(&index)
if binding_reqs.image_format.is_none()
&& !desc_reqs.memory_read.is_empty()
&& !image_view
.format_features()
.intersects(FormatFeatures::STORAGE_READ_WITHOUT_FORMAT)
@ -717,6 +740,7 @@ where
);
}
}
}
/*
Instruction/Sampler/Image View Validation
@ -724,7 +748,7 @@ where
*/
// The SPIR-V Image Format is not compatible with the image views format.
if let Some(format) = reqs.image_format {
if let Some(format) = binding_reqs.image_format {
if image_view.format() != Some(format) {
return Err(DescriptorResourceInvalidError::ImageViewFormatMismatch {
required: format,
@ -734,7 +758,7 @@ where
}
// Rules for viewType
if let Some(image_view_type) = reqs.image_view_type {
if let Some(image_view_type) = binding_reqs.image_view_type {
if image_view.view_type() != image_view_type {
return Err(DescriptorResourceInvalidError::ImageViewTypeMismatch {
required: image_view_type,
@ -747,11 +771,12 @@ where
// VK_SAMPLE_COUNT_1_BIT, the instruction must have MS = 0.
// - If the image was created with VkImageCreateInfo::samples not equal to
// VK_SAMPLE_COUNT_1_BIT, the instruction must have MS = 1.
if reqs.image_multisampled != (image_view.image().samples() != SampleCount::Sample1)
if binding_reqs.image_multisampled
!= (image_view.image().samples() != SampleCount::Sample1)
{
return Err(
DescriptorResourceInvalidError::ImageViewMultisampledMismatch {
required: reqs.image_multisampled,
required: binding_reqs.image_multisampled,
provided: image_view.image().samples() != SampleCount::Sample1,
},
);
@ -762,7 +787,7 @@ where
// Interpretation of Numeric Format table.
// - If the signedness of any read or sample operation does not match the signedness of
// the images format.
if let Some(scalar_type) = reqs.image_scalar_type {
if let Some(scalar_type) = binding_reqs.image_scalar_type {
let aspects = image_view.subresource_range().aspects;
let view_scalar_type = ShaderScalarType::from(
if aspects.intersects(
@ -797,9 +822,12 @@ where
};
let check_sampler_common = |index: u32, sampler: &Arc<Sampler>| {
for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter())
.chain(binding_reqs.descriptors.get(&None))
{
// VUID-vkCmdDispatch-None-02703
// VUID-vkCmdDispatch-None-02704
if reqs.sampler_no_unnormalized_coordinates.contains(&index)
if desc_reqs.sampler_no_unnormalized_coordinates
&& sampler.unnormalized_coordinates()
{
return Err(
@ -811,10 +839,12 @@ where
// be used with a sampler that enables sampler YCBCR conversion.
// - The ConstOffset and Offset operands must not be used with a sampler that enables
// sampler YCBCR conversion.
if reqs.sampler_no_ycbcr_conversion.contains(&index)
if desc_reqs.sampler_no_ycbcr_conversion
&& sampler.sampler_ycbcr_conversion().is_some()
{
return Err(DescriptorResourceInvalidError::SamplerYcbcrConversionNotAllowed);
return Err(
DescriptorResourceInvalidError::SamplerYcbcrConversionNotAllowed,
);
}
/*
@ -826,12 +856,13 @@ where
// compareEnable is VK_FALSE
// - The SPIR-V instruction is not one of the OpImage*Dref* instructions and the sampler
// compareEnable is VK_TRUE
if reqs.sampler_compare.contains(&index) != sampler.compare().is_some() {
if desc_reqs.sampler_compare != sampler.compare().is_some() {
return Err(DescriptorResourceInvalidError::SamplerCompareMismatch {
required: reqs.sampler_compare.contains(&index),
required: desc_reqs.sampler_compare,
provided: sampler.compare().is_some(),
});
}
}
Ok(())
};
@ -857,12 +888,15 @@ where
let check_sampler = |index: u32, sampler: &Arc<Sampler>| {
check_sampler_common(index, sampler)?;
// Check sampler-image compatibility. Only done for separate samplers; combined image
// samplers are checked when updating the descriptor set.
if let Some(with_images) = reqs.sampler_with_images.get(&index) {
for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter())
.chain(binding_reqs.descriptors.get(&None))
{
// Check sampler-image compatibility. Only done for separate samplers;
// combined image samplers are checked when updating the descriptor set.
// If the image view isn't actually present in the resources, then just skip it.
// It will be caught later by check_resources.
let iter = with_images.iter().filter_map(|id| {
let iter = desc_reqs.sampler_with_images.iter().filter_map(|id| {
current_state
.descriptor_set(pipeline.bind_point(), id.set)
.and_then(|set| set.resources().binding(id.binding))
@ -908,28 +942,46 @@ where
match binding_resources {
DescriptorBindingResources::None(elements) => {
validate_resources(set_num, binding_num, reqs, elements, check_none)?;
validate_resources(set_num, binding_num, binding_reqs, elements, check_none)?;
}
DescriptorBindingResources::Buffer(elements) => {
validate_resources(set_num, binding_num, reqs, elements, check_buffer)?;
validate_resources(set_num, binding_num, binding_reqs, elements, check_buffer)?;
}
DescriptorBindingResources::BufferView(elements) => {
validate_resources(set_num, binding_num, reqs, elements, check_buffer_view)?;
validate_resources(
set_num,
binding_num,
binding_reqs,
elements,
check_buffer_view,
)?;
}
DescriptorBindingResources::ImageView(elements) => {
validate_resources(set_num, binding_num, reqs, elements, check_image_view)?;
validate_resources(
set_num,
binding_num,
binding_reqs,
elements,
check_image_view,
)?;
}
DescriptorBindingResources::ImageViewSampler(elements) => {
validate_resources(
set_num,
binding_num,
reqs,
binding_reqs,
elements,
check_image_view_sampler,
)?;
}
DescriptorBindingResources::Sampler(elements) => {
validate_resources(set_num, binding_num, reqs, elements, check_sampler)?;
validate_resources(
set_num,
binding_num,
binding_reqs,
elements,
check_sampler,
)?;
}
}
}
@ -1634,7 +1686,7 @@ impl SyncCommandBufferBuilder {
self.add_descriptor_set_resources(
&mut resources,
PipelineBindPoint::Compute,
pipeline.descriptor_requirements(),
pipeline.descriptor_binding_requirements(),
);
for resource in &resources {
@ -1676,7 +1728,7 @@ impl SyncCommandBufferBuilder {
self.add_descriptor_set_resources(
&mut resources,
PipelineBindPoint::Compute,
pipeline.descriptor_requirements(),
pipeline.descriptor_binding_requirements(),
);
self.add_indirect_buffer_resources(&mut resources, &indirect_buffer);
@ -1730,7 +1782,7 @@ impl SyncCommandBufferBuilder {
self.add_descriptor_set_resources(
&mut resources,
PipelineBindPoint::Graphics,
pipeline.descriptor_requirements(),
pipeline.descriptor_binding_requirements(),
);
self.add_vertex_buffer_resources(&mut resources, pipeline.vertex_input_state());
@ -1792,7 +1844,7 @@ impl SyncCommandBufferBuilder {
self.add_descriptor_set_resources(
&mut resources,
PipelineBindPoint::Graphics,
pipeline.descriptor_requirements(),
pipeline.descriptor_binding_requirements(),
);
self.add_vertex_buffer_resources(&mut resources, pipeline.vertex_input_state());
self.add_index_buffer_resources(&mut resources);
@ -1846,7 +1898,7 @@ impl SyncCommandBufferBuilder {
self.add_descriptor_set_resources(
&mut resources,
PipelineBindPoint::Graphics,
pipeline.descriptor_requirements(),
pipeline.descriptor_binding_requirements(),
);
self.add_vertex_buffer_resources(&mut resources, pipeline.vertex_input_state());
self.add_indirect_buffer_resources(&mut resources, &indirect_buffer);
@ -1902,7 +1954,7 @@ impl SyncCommandBufferBuilder {
self.add_descriptor_set_resources(
&mut resources,
PipelineBindPoint::Graphics,
pipeline.descriptor_requirements(),
pipeline.descriptor_binding_requirements(),
);
self.add_vertex_buffer_resources(&mut resources, pipeline.vertex_input_state());
self.add_index_buffer_resources(&mut resources);
@ -1929,93 +1981,116 @@ impl SyncCommandBufferBuilder {
&self,
resources: &mut Vec<(Cow<'static, str>, Resource)>,
pipeline_bind_point: PipelineBindPoint,
descriptor_requirements: impl IntoIterator<Item = ((u32, u32), &'a DescriptorRequirements)>,
descriptor_binding_requirements: impl IntoIterator<
Item = ((u32, u32), &'a DescriptorBindingRequirements),
>,
) {
let state = match self.current_state.descriptor_sets.get(&pipeline_bind_point) {
Some(x) => x,
None => return,
};
for ((set, binding), reqs) in descriptor_requirements {
for ((set, binding), binding_reqs) in descriptor_binding_requirements {
// TODO: Can things be refactored so that the pipeline layout isn't needed at all?
let descriptor_type = state.pipeline_layout.set_layouts()[set as usize].bindings()
[&binding]
.descriptor_type;
let (access_read, access_write) = match descriptor_type {
DescriptorType::Sampler => continue,
DescriptorType::InputAttachment => {
// FIXME: This is tricky. Since we read from the input attachment
// and this input attachment is being written in an earlier pass,
// vulkano will think that it needs to put a pipeline barrier and will
// return a `Conflict` error. For now as a work-around we simply ignore
// input attachments.
if descriptor_type == DescriptorType::InputAttachment {
continue;
}
// TODO: Maybe include this on DescriptorRequirements?
let access = PipelineMemoryAccess {
stages: reqs.stages.into(),
access: match descriptor_type {
DescriptorType::Sampler => continue,
DescriptorType::CombinedImageSampler
| DescriptorType::SampledImage
| DescriptorType::StorageImage
| DescriptorType::UniformTexelBuffer
| DescriptorType::UniformTexelBuffer => (Some(AccessFlags::SHADER_READ), None),
DescriptorType::StorageImage
| DescriptorType::StorageTexelBuffer
| DescriptorType::StorageBuffer
| DescriptorType::StorageBufferDynamic => AccessFlags::SHADER_READ,
DescriptorType::InputAttachment => AccessFlags::INPUT_ATTACHMENT_READ,
| DescriptorType::StorageBufferDynamic => (
Some(AccessFlags::SHADER_READ),
Some(AccessFlags::SHADER_WRITE),
),
DescriptorType::UniformBuffer | DescriptorType::UniformBufferDynamic => {
AccessFlags::UNIFORM_READ
(Some(AccessFlags::UNIFORM_READ), None)
}
},
exclusive: false,
};
let access = (0..).map(|index| {
let mut access = access;
let mutable = reqs.storage_write.contains(&index);
access.exclusive = mutable;
let memory_iter = move |index: usize| {
let mut stages_read = PipelineStages::empty();
let mut stages_write = PipelineStages::empty();
if mutable {
access.access |= AccessFlags::SHADER_WRITE;
for desc_reqs in (binding_reqs
.descriptors
.get(&Some(index as u32))
.into_iter())
.chain(binding_reqs.descriptors.get(&None))
{
stages_read |= desc_reqs.memory_read.into();
stages_write |= desc_reqs.memory_write.into();
}
access
let memory_read = (!stages_read.is_empty()).then(|| PipelineMemoryAccess {
stages: stages_read,
access: access_read.unwrap(),
exclusive: false,
});
let memory_write = (!stages_write.is_empty()).then(|| PipelineMemoryAccess {
stages: stages_write,
access: access_write.unwrap(),
exclusive: true,
});
let buffer_resource = move |(buffer, range, memory): (
Arc<dyn BufferAccess>,
Range<DeviceSize>,
PipelineMemoryAccess,
)| {
[memory_read, memory_write].into_iter().flatten()
};
let buffer_resource =
|(index, buffer, range): (usize, Arc<dyn BufferAccess>, Range<DeviceSize>)| {
memory_iter(index).map(move |memory| {
(
format!("Buffer bound to set {} descriptor {}", set, binding).into(),
format!(
"Buffer bound to set {} descriptor {} index {}",
set, binding, index
)
.into(),
Resource::Buffer {
buffer,
range,
buffer: buffer.clone(),
range: range.clone(),
memory,
},
)
})
};
let image_resource = move |(image, subresource_range, memory): (
let image_resource = |(index, image, subresource_range): (
usize,
Arc<dyn ImageAccess>,
ImageSubresourceRange,
PipelineMemoryAccess,
)| {
let layout = image
.descriptor_layouts()
.expect("descriptor_layouts must return Some when used in an image view")
.layout_for(descriptor_type);
memory_iter(index).map(move |memory| {
(
format!("Image bound to set {} descriptor {}", set, binding).into(),
format!(
"Image bound to set {} descriptor {} index {}",
set, binding, index
)
.into(),
Resource::Image {
image,
subresource_range,
image: image.clone(),
subresource_range: subresource_range.clone(),
memory,
start_layout: layout,
end_layout: layout,
},
)
})
};
match state.descriptor_sets[&set]
@ -2026,62 +2101,58 @@ impl SyncCommandBufferBuilder {
DescriptorBindingResources::None(_) => continue,
DescriptorBindingResources::Buffer(elements) => {
resources.extend(
access
.zip(elements)
.filter_map(|(access, element)| {
(elements.iter().enumerate())
.filter_map(|(index, element)| {
element.as_ref().map(|buffer| {
(
index,
buffer.clone(),
0..buffer.size(), // TODO:
access,
)
})
})
.map(buffer_resource),
.flat_map(buffer_resource),
);
}
DescriptorBindingResources::BufferView(elements) => {
resources.extend(
access
.zip(elements)
.filter_map(|(access, element)| {
(elements.iter().enumerate())
.filter_map(|(index, element)| {
element.as_ref().map(|buffer_view| {
(buffer_view.buffer(), buffer_view.range(), access)
(index, buffer_view.buffer(), buffer_view.range())
})
})
.map(buffer_resource),
.flat_map(buffer_resource),
);
}
DescriptorBindingResources::ImageView(elements) => {
resources.extend(
access
.zip(elements)
.filter_map(|(access, element)| {
(elements.iter().enumerate())
.filter_map(|(index, element)| {
element.as_ref().map(|image_view| {
(
index,
image_view.image(),
image_view.subresource_range().clone(),
access,
)
})
})
.map(image_resource),
.flat_map(image_resource),
);
}
DescriptorBindingResources::ImageViewSampler(elements) => {
resources.extend(
access
.zip(elements)
.filter_map(|(access, element)| {
(elements.iter().enumerate())
.filter_map(|(index, element)| {
element.as_ref().map(|(image_view, _)| {
(
index,
image_view.image(),
image_view.subresource_range().clone(),
access,
)
})
})
.map(image_resource),
.flat_map(image_resource),
);
}
DescriptorBindingResources::Sampler(_) => (),

View File

@ -15,7 +15,7 @@ use crate::{
device::{Device, DeviceOwned},
macros::vulkan_enum,
sampler::Sampler,
shader::{DescriptorRequirements, ShaderStages},
shader::{DescriptorBindingRequirements, ShaderStages},
OomError, RequirementNotMet, RequiresOneOf, Version, VulkanError, VulkanObject,
};
use ahash::HashMap;
@ -632,10 +632,12 @@ impl Default for DescriptorSetLayoutCreateInfo {
}
impl DescriptorSetLayoutCreateInfo {
/// Builds a list of `DescriptorSetLayoutCreateInfo` from an iterator of `DescriptorRequirement`
/// originating from a shader.
/// Builds a list of `DescriptorSetLayoutCreateInfo` from an iterator of
/// `DescriptorBindingRequirements` originating from a shader.
pub fn from_requirements<'a>(
descriptor_requirements: impl IntoIterator<Item = ((u32, u32), &'a DescriptorRequirements)>,
descriptor_requirements: impl IntoIterator<
Item = ((u32, u32), &'a DescriptorBindingRequirements),
>,
) -> Vec<Self> {
let mut create_infos: Vec<Self> = Vec::new();
@ -722,24 +724,18 @@ impl DescriptorSetLayoutBinding {
#[inline]
pub fn ensure_compatible_with_shader(
&self,
descriptor_requirements: &DescriptorRequirements,
binding_requirements: &DescriptorBindingRequirements,
) -> Result<(), DescriptorRequirementsNotMet> {
let &DescriptorRequirements {
let &DescriptorBindingRequirements {
ref descriptor_types,
descriptor_count,
image_format: _,
image_multisampled: _,
image_scalar_type: _,
image_view_type: _,
sampler_compare: _,
sampler_no_unnormalized_coordinates: _,
sampler_no_ycbcr_conversion: _,
sampler_with_images: _,
stages,
storage_image_atomic: _,
storage_read: _,
storage_write: _,
} = descriptor_requirements;
descriptors: _,
} = binding_requirements;
if !descriptor_types.contains(&self.descriptor_type) {
return Err(DescriptorRequirementsNotMet::DescriptorType {
@ -768,9 +764,9 @@ impl DescriptorSetLayoutBinding {
}
}
impl From<&DescriptorRequirements> for DescriptorSetLayoutBinding {
impl From<&DescriptorBindingRequirements> for DescriptorSetLayoutBinding {
#[inline]
fn from(reqs: &DescriptorRequirements) -> Self {
fn from(reqs: &DescriptorBindingRequirements) -> Self {
Self {
descriptor_type: reqs.descriptor_types[0],
descriptor_count: reqs.descriptor_count.unwrap_or(0),

View File

@ -1333,6 +1333,7 @@ impl RawImage {
/// - If `self.flags().disjoint` is set, then `allocations` must contain exactly
/// `self.format().unwrap().planes().len()` elements. These elements must not be dedicated
/// allocations.
#[allow(clippy::result_large_err)]
pub fn bind_memory(
self,
allocations: impl IntoIterator<Item = MemoryAlloc>,
@ -1497,6 +1498,7 @@ impl RawImage {
/// - If `self.flags().disjoint` is set, then `allocations` must contain exactly
/// `self.format().unwrap().planes().len()` elements.
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
#[allow(clippy::result_large_err)]
pub unsafe fn bind_memory_unchecked(
self,
allocations: impl IntoIterator<Item = MemoryAlloc>,

View File

@ -33,7 +33,7 @@ use crate::{
layout::{PipelineLayout, PipelineLayoutCreationError, PipelineLayoutSupersetError},
Pipeline, PipelineBindPoint,
},
shader::{DescriptorRequirements, EntryPoint, SpecializationConstants},
shader::{DescriptorBindingRequirements, EntryPoint, SpecializationConstants},
DeviceSize, OomError, VulkanError, VulkanObject,
};
use ahash::HashMap;
@ -60,7 +60,7 @@ pub struct ComputePipeline {
device: Arc<Device>,
id: NonZeroU64,
layout: Arc<PipelineLayout>,
descriptor_requirements: HashMap<(u32, u32), DescriptorRequirements>,
descriptor_binding_requirements: HashMap<(u32, u32), DescriptorBindingRequirements>,
num_used_descriptor_sets: u32,
}
@ -81,8 +81,9 @@ impl ComputePipeline {
Css: SpecializationConstants,
F: FnOnce(&mut [DescriptorSetLayoutCreateInfo]),
{
let mut set_layout_create_infos =
DescriptorSetLayoutCreateInfo::from_requirements(shader.descriptor_requirements());
let mut set_layout_create_infos = DescriptorSetLayoutCreateInfo::from_requirements(
shader.descriptor_binding_requirements(),
);
func(&mut set_layout_create_infos);
let set_layouts = set_layout_create_infos
.iter()
@ -141,7 +142,7 @@ impl ComputePipeline {
}
layout.ensure_compatible_with_shader(
shader.descriptor_requirements(),
shader.descriptor_binding_requirements(),
shader.push_constant_requirements(),
)?;
@ -220,11 +221,11 @@ impl ComputePipeline {
output.assume_init()
};
let descriptor_requirements: HashMap<_, _> = shader
.descriptor_requirements()
let descriptor_binding_requirements: HashMap<_, _> = shader
.descriptor_binding_requirements()
.map(|(loc, reqs)| (loc, reqs.clone()))
.collect();
let num_used_descriptor_sets = descriptor_requirements
let num_used_descriptor_sets = descriptor_binding_requirements
.keys()
.map(|loc| loc.0)
.max()
@ -236,7 +237,7 @@ impl ComputePipeline {
device: device.clone(),
id: Self::next_id(),
layout,
descriptor_requirements,
descriptor_binding_requirements,
num_used_descriptor_sets,
}))
}
@ -249,10 +250,10 @@ impl ComputePipeline {
/// Returns an iterator over the descriptor requirements for this pipeline.
#[inline]
pub fn descriptor_requirements(
pub fn descriptor_binding_requirements(
&self,
) -> impl ExactSizeIterator<Item = ((u32, u32), &DescriptorRequirements)> {
self.descriptor_requirements
) -> impl ExactSizeIterator<Item = ((u32, u32), &DescriptorBindingRequirements)> {
self.descriptor_binding_requirements
.iter()
.map(|(loc, reqs)| (*loc, reqs))
}

View File

@ -47,8 +47,8 @@ use crate::{
DynamicState, PartialStateMode, PipelineLayout, StateMode,
},
shader::{
DescriptorRequirements, EntryPoint, ShaderExecution, ShaderStage, SpecializationConstants,
SpecializationMapEntry,
DescriptorBindingRequirements, EntryPoint, ShaderExecution, ShaderStage,
SpecializationConstants, SpecializationMapEntry,
},
DeviceSize, RequiresOneOf, Version, VulkanError, VulkanObject,
};
@ -183,22 +183,22 @@ where
.flatten()
.collect();
// Produce `DescriptorRequirements` for each binding, by iterating over all shaders
// and adding the requirements of each.
let mut descriptor_requirements: HashMap<(u32, u32), DescriptorRequirements> =
HashMap::default();
// Produce `DescriptorBindingRequirements` for each binding, by iterating over all
// shaders and adding the requirements of each.
let mut descriptor_binding_requirements: HashMap<
(u32, u32),
DescriptorBindingRequirements,
> = HashMap::default();
for (loc, reqs) in stages
.iter()
.flat_map(|shader| shader.descriptor_requirements())
.flat_map(|shader| shader.descriptor_binding_requirements())
{
match descriptor_requirements.entry(loc) {
match descriptor_binding_requirements.entry(loc) {
Entry::Occupied(entry) => {
// Previous shaders already added requirements, so we produce the
// intersection of the previous requirements and those of the
// current shader.
let previous = entry.into_mut();
*previous = previous.intersection(reqs).expect("Could not produce an intersection of the shader descriptor requirements");
// Previous shaders already added requirements, so we merge requirements of
// the current shader into the requirements of the previous one.
entry.into_mut().merge(reqs).expect("Could not produce an intersection of the shader descriptor requirements");
}
Entry::Vacant(entry) => {
// No previous shader had this descriptor yet, so we just insert the
@ -211,7 +211,7 @@ where
// Build a description of a descriptor set layout from the shader requirements, then
// feed it to the user-provided closure to allow tweaking.
let mut set_layout_create_infos = DescriptorSetLayoutCreateInfo::from_requirements(
descriptor_requirements
descriptor_binding_requirements
.iter()
.map(|(&loc, reqs)| (loc, reqs)),
);
@ -399,7 +399,7 @@ where
layout: pipeline_layout,
render_pass: render_pass.take().expect("Missing render pass"),
shaders,
descriptor_requirements,
descriptor_binding_requirements: descriptor_requirements,
num_used_descriptor_sets,
vertex_input_state, // Can be None if there's a mesh shader, but we don't support that yet
input_assembly_state, // Can be None if there's a mesh shader, but we don't support that yet
@ -2477,7 +2477,7 @@ where
for stage_info in &shader_stages {
// VUID-VkGraphicsPipelineCreateInfo-layout-00756
pipeline_layout.ensure_compatible_with_shader(
stage_info.entry_point.descriptor_requirements(),
stage_info.entry_point.descriptor_binding_requirements(),
stage_info.entry_point.push_constant_requirements(),
)?;
@ -2537,7 +2537,7 @@ where
) -> Result<
(
ash::vk::Pipeline,
HashMap<(u32, u32), DescriptorRequirements>,
HashMap<(u32, u32), DescriptorBindingRequirements>,
HashMap<DynamicState, bool>,
HashMap<ShaderStage, ()>,
),
@ -2565,8 +2565,10 @@ where
let render_pass = render_pass.as_ref().unwrap();
let mut descriptor_requirements: HashMap<(u32, u32), DescriptorRequirements> =
HashMap::default();
let mut descriptor_binding_requirements: HashMap<
(u32, u32),
DescriptorBindingRequirements,
> = HashMap::default();
let mut dynamic_state: HashMap<DynamicState, bool> = HashMap::default();
let mut stages = HashMap::default();
let mut stages_vk: SmallVec<[_; 5]> = SmallVec::new();
@ -2770,11 +2772,10 @@ where
p_data: specialization_data.as_ptr() as *const _,
});
for (loc, reqs) in entry_point.descriptor_requirements() {
match descriptor_requirements.entry(loc) {
for (loc, reqs) in entry_point.descriptor_binding_requirements() {
match descriptor_binding_requirements.entry(loc) {
Entry::Occupied(entry) => {
let previous = entry.into_mut();
*previous = previous.intersection(reqs).expect("Could not produce an intersection of the shader descriptor requirements");
entry.into_mut().merge(reqs).expect("Could not produce an intersection of the shader descriptor requirements");
}
Entry::Vacant(entry) => {
entry.insert(reqs.clone());
@ -2811,11 +2812,10 @@ where
p_data: specialization_data.as_ptr() as *const _,
});
for (loc, reqs) in entry_point.descriptor_requirements() {
match descriptor_requirements.entry(loc) {
for (loc, reqs) in entry_point.descriptor_binding_requirements() {
match descriptor_binding_requirements.entry(loc) {
Entry::Occupied(entry) => {
let previous = entry.into_mut();
*previous = previous.intersection(reqs).expect("Could not produce an intersection of the shader descriptor requirements");
entry.into_mut().merge(reqs).expect("Could not produce an intersection of the shader descriptor requirements");
}
Entry::Vacant(entry) => {
entry.insert(reqs.clone());
@ -2850,11 +2850,10 @@ where
p_data: specialization_data.as_ptr() as *const _,
});
for (loc, reqs) in entry_point.descriptor_requirements() {
match descriptor_requirements.entry(loc) {
for (loc, reqs) in entry_point.descriptor_binding_requirements() {
match descriptor_binding_requirements.entry(loc) {
Entry::Occupied(entry) => {
let previous = entry.into_mut();
*previous = previous.intersection(reqs).expect("Could not produce an intersection of the shader descriptor requirements");
entry.into_mut().merge(reqs).expect("Could not produce an intersection of the shader descriptor requirements");
}
Entry::Vacant(entry) => {
entry.insert(reqs.clone());
@ -2890,11 +2889,10 @@ where
p_data: specialization_data.as_ptr() as *const _,
});
for (loc, reqs) in entry_point.descriptor_requirements() {
match descriptor_requirements.entry(loc) {
for (loc, reqs) in entry_point.descriptor_binding_requirements() {
match descriptor_binding_requirements.entry(loc) {
Entry::Occupied(entry) => {
let previous = entry.into_mut();
*previous = previous.intersection(reqs).expect("Could not produce an intersection of the shader descriptor requirements");
entry.into_mut().merge(reqs).expect("Could not produce an intersection of the shader descriptor requirements");
}
Entry::Vacant(entry) => {
entry.insert(reqs.clone());
@ -3231,11 +3229,10 @@ where
p_data: specialization_data.as_ptr() as *const _,
});
for (loc, reqs) in entry_point.descriptor_requirements() {
match descriptor_requirements.entry(loc) {
for (loc, reqs) in entry_point.descriptor_binding_requirements() {
match descriptor_binding_requirements.entry(loc) {
Entry::Occupied(entry) => {
let previous = entry.into_mut();
*previous = previous.intersection(reqs).expect("Could not produce an intersection of the shader descriptor requirements");
entry.into_mut().merge(reqs).expect("Could not produce an intersection of the shader descriptor requirements");
}
Entry::Vacant(entry) => {
entry.insert(reqs.clone());
@ -3698,7 +3695,12 @@ where
panic!("vkCreateGraphicsPipelines provided a NULL handle");
}
Ok((handle, descriptor_requirements, dynamic_state, stages))
Ok((
handle,
descriptor_binding_requirements,
dynamic_state,
stages,
))
}
// TODO: add build_with_cache method

View File

@ -67,7 +67,7 @@ use self::{
use super::{DynamicState, Pipeline, PipelineBindPoint, PipelineLayout};
use crate::{
device::{Device, DeviceOwned},
shader::{DescriptorRequirements, ShaderStage},
shader::{DescriptorBindingRequirements, ShaderStage},
VulkanObject,
};
use ahash::HashMap;
@ -106,7 +106,7 @@ pub struct GraphicsPipeline {
// TODO: replace () with an object that describes the shaders in some way.
shaders: HashMap<ShaderStage, ()>,
descriptor_requirements: HashMap<(u32, u32), DescriptorRequirements>,
descriptor_binding_requirements: HashMap<(u32, u32), DescriptorBindingRequirements>,
num_used_descriptor_sets: u32,
vertex_input_state: VertexInputState,
@ -165,12 +165,12 @@ impl GraphicsPipeline {
self.shaders.get(&stage).copied()
}
/// Returns an iterator over the descriptor requirements for this pipeline.
/// Returns an iterator over the descriptor binding requirements for this pipeline.
#[inline]
pub fn descriptor_requirements(
pub fn descriptor_binding_requirements(
&self,
) -> impl ExactSizeIterator<Item = ((u32, u32), &DescriptorRequirements)> {
self.descriptor_requirements
) -> impl ExactSizeIterator<Item = ((u32, u32), &DescriptorBindingRequirements)> {
self.descriptor_binding_requirements
.iter()
.map(|(loc, reqs)| (*loc, reqs))
}

View File

@ -66,7 +66,7 @@
use crate::{
descriptor_set::layout::{DescriptorRequirementsNotMet, DescriptorSetLayout, DescriptorType},
device::{Device, DeviceOwned},
shader::{DescriptorRequirements, ShaderStages},
shader::{DescriptorBindingRequirements, ShaderStages},
OomError, RequirementNotMet, RequiresOneOf, VulkanError, VulkanObject,
};
use smallvec::SmallVec;
@ -619,7 +619,9 @@ impl PipelineLayout {
/// constant ranges. Returns an `Err` if this is not the case.
pub fn ensure_compatible_with_shader<'a>(
&self,
descriptor_requirements: impl IntoIterator<Item = ((u32, u32), &'a DescriptorRequirements)>,
descriptor_requirements: impl IntoIterator<
Item = ((u32, u32), &'a DescriptorBindingRequirements),
>,
push_constant_range: Option<&PushConstantRange>,
) -> Result<(), PipelineLayoutSupersetError> {
for ((set_num, binding_num), reqs) in descriptor_requirements.into_iter() {

View File

@ -31,6 +31,7 @@ use crate::{
use ahash::{HashMap, HashSet};
use std::{
borrow::Cow,
collections::hash_map::Entry,
error::Error,
ffi::{CStr, CString},
fmt::{Display, Error as FmtError, Formatter},
@ -381,7 +382,7 @@ impl Display for ShaderSupportError {
#[derive(Clone, Debug)]
pub struct EntryPointInfo {
pub execution: ShaderExecution,
pub descriptor_requirements: HashMap<(u32, u32), DescriptorRequirements>,
pub descriptor_binding_requirements: HashMap<(u32, u32), DescriptorBindingRequirements>,
pub push_constant_requirements: Option<PushConstantRange>,
pub specialization_constant_requirements: HashMap<u32, SpecializationConstantRequirements>,
pub input_interface: ShaderInterface,
@ -417,13 +418,13 @@ impl<'a> EntryPoint<'a> {
&self.info.execution
}
/// Returns the descriptor requirements.
/// Returns the descriptor binding requirements.
#[inline]
pub fn descriptor_requirements(
pub fn descriptor_binding_requirements(
&self,
) -> impl ExactSizeIterator<Item = ((u32, u32), &DescriptorRequirements)> {
) -> impl ExactSizeIterator<Item = ((u32, u32), &DescriptorBindingRequirements)> {
self.info
.descriptor_requirements
.descriptor_binding_requirements
.iter()
.map(|(k, v)| (*k, v))
}
@ -546,10 +547,10 @@ pub enum GeometryShaderOutput {
TriangleStrip,
}*/
/// The requirements imposed by a shader on a descriptor within a descriptor set layout, and on any
/// resource that is bound to that descriptor.
/// The requirements imposed by a shader on a binding within a descriptor set layout, and on any
/// resource that is bound to that binding.
#[derive(Clone, Debug, Default)]
pub struct DescriptorRequirements {
pub struct DescriptorBindingRequirements {
/// The descriptor types that are allowed.
pub descriptor_types: Vec<DescriptorType>,
@ -560,51 +561,60 @@ pub struct DescriptorRequirements {
/// access every array element provided in the descriptor set.
pub descriptor_count: Option<u32>,
/// The image format that is required for image views bound to this descriptor. If this is
/// The image format that is required for image views bound to this binding. If this is
/// `None`, then any image format is allowed.
pub image_format: Option<Format>,
/// Whether image views bound to this descriptor must have multisampling enabled or disabled.
/// Whether image views bound to this binding must have multisampling enabled or disabled.
pub image_multisampled: bool,
/// The base scalar type required for the format of image views bound to this descriptor.
/// This is `None` for non-image descriptors.
/// The base scalar type required for the format of image views bound to this binding.
/// This is `None` for non-image bindings.
pub image_scalar_type: Option<ShaderScalarType>,
/// The view type that is required for image views bound to this descriptor.
/// This is `None` for non-image descriptors.
/// The view type that is required for image views bound to this binding.
/// This is `None` for non-image bindings.
pub image_view_type: Option<ImageViewType>,
/// For sampler bindings, the descriptor indices that require a depth comparison sampler.
pub sampler_compare: HashSet<u32>,
/// For sampler bindings, the descriptor indices that perform sampling operations that are not
/// permitted with unnormalized coordinates. This includes sampling with `ImplicitLod`,
/// `Dref` or `Proj` SPIR-V instructions or with an LOD bias or offset.
pub sampler_no_unnormalized_coordinates: HashSet<u32>,
/// For sampler bindings, the descriptor indices that perform sampling operations that are not
/// permitted with a sampler YCbCr conversion. This includes sampling with `Gather` SPIR-V
/// instructions or with an offset.
pub sampler_no_ycbcr_conversion: HashSet<u32>,
/// For sampler bindings, the sampled image descriptors that are used in combination with each
/// sampler descriptor index.
pub sampler_with_images: HashMap<u32, HashSet<DescriptorIdentifier>>,
/// The shader stages that the descriptor must be declared for.
/// The shader stages that the binding must be declared for.
pub stages: ShaderStages,
/// For storage image bindings, the descriptor indices that atomic operations are used with.
pub storage_image_atomic: HashSet<u32>,
/// The requirements for individual descriptors within a binding.
///
/// Keys with `Some` hold requirements for a specific descriptor index, if it is statically
/// known in the shader (a constant). The key `None` holds requirements for indices that are
/// not statically known, but determined only at runtime (calculated from an input variable).
pub descriptors: HashMap<Option<u32>, DescriptorRequirements>,
}
/// For storage images and storage texel buffers, the descriptor indices that perform read
/// operations on the bound resource.
pub storage_read: HashSet<u32>,
/// The requirements imposed by a shader on resources bound to a descriptor.
#[derive(Clone, Debug, Default)]
pub struct DescriptorRequirements {
/// For buffers and images, which shader stages perform read operations.
pub memory_read: ShaderStages,
/// For storage buffers, storage images and storage texel buffers, the descriptor indices that
/// perform write operations on the bound resource.
pub storage_write: HashSet<u32>,
/// For buffers and images, which shader stages perform write operations.
pub memory_write: ShaderStages,
/// For sampler bindings, whether the shader performs depth comparison operations.
pub sampler_compare: bool,
/// For sampler bindings, whether the shader performs sampling operations that are not
/// permitted with unnormalized coordinates. This includes sampling with `ImplicitLod`,
/// `Dref` or `Proj` SPIR-V instructions or with an LOD bias or offset.
pub sampler_no_unnormalized_coordinates: bool,
/// For sampler bindings, whether the shader performs sampling operations that are not
/// permitted with a sampler YCbCr conversion. This includes sampling with `Gather` SPIR-V
/// instructions or with an offset.
pub sampler_no_ycbcr_conversion: bool,
/// For sampler bindings, the sampled image descriptors that are used in combination with this
/// sampler.
pub sampler_with_images: HashSet<DescriptorIdentifier>,
/// For storage image bindings, whether the shader performs atomic operations.
pub storage_image_atomic: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@ -614,79 +624,106 @@ pub struct DescriptorIdentifier {
pub index: u32,
}
impl DescriptorRequirements {
/// Produces the intersection of two descriptor requirements, so that the requirements of both
/// are satisfied. An error is returned if the requirements conflict.
impl DescriptorBindingRequirements {
/// Merges `other` into `self`, so that `self` satisfies the requirements of both.
/// An error is returned if the requirements conflict.
#[inline]
pub fn intersection(&self, other: &Self) -> Result<Self, DescriptorRequirementsIncompatible> {
let descriptor_types: Vec<_> = self
.descriptor_types
.iter()
.copied()
.filter(|ty| other.descriptor_types.contains(ty))
.collect();
if descriptor_types.is_empty() {
return Err(DescriptorRequirementsIncompatible::DescriptorType);
}
if let (Some(first), Some(second)) = (self.image_format, other.image_format) {
if first != second {
return Err(DescriptorRequirementsIncompatible::ImageFormat);
}
}
if let (Some(first), Some(second)) = (self.image_scalar_type, other.image_scalar_type) {
if first != second {
return Err(DescriptorRequirementsIncompatible::ImageScalarType);
}
}
if let (Some(first), Some(second)) = (self.image_view_type, other.image_view_type) {
if first != second {
return Err(DescriptorRequirementsIncompatible::ImageViewType);
}
}
if self.image_multisampled != other.image_multisampled {
return Err(DescriptorRequirementsIncompatible::ImageMultisampled);
}
let sampler_with_images = {
let mut result = self.sampler_with_images.clone();
for (&index, other_identifiers) in &other.sampler_with_images {
result.entry(index).or_default().extend(other_identifiers);
}
result
};
Ok(Self {
pub fn merge(&mut self, other: &Self) -> Result<(), DescriptorBindingRequirementsIncompatible> {
let Self {
descriptor_types,
descriptor_count: self.descriptor_count.max(other.descriptor_count),
image_format: self.image_format.or(other.image_format),
image_multisampled: self.image_multisampled,
image_scalar_type: self.image_scalar_type.or(other.image_scalar_type),
image_view_type: self.image_view_type.or(other.image_view_type),
sampler_compare: &self.sampler_compare | &other.sampler_compare,
sampler_no_unnormalized_coordinates: &self.sampler_no_unnormalized_coordinates
| &other.sampler_no_unnormalized_coordinates,
sampler_no_ycbcr_conversion: &self.sampler_no_ycbcr_conversion
| &other.sampler_no_ycbcr_conversion,
descriptor_count,
image_format,
image_multisampled,
image_scalar_type,
image_view_type,
stages,
descriptors,
} = self;
/* Checks */
if !descriptor_types
.iter()
.any(|ty| other.descriptor_types.contains(ty))
{
return Err(DescriptorBindingRequirementsIncompatible::DescriptorType);
}
if let (Some(first), Some(second)) = (*image_format, other.image_format) {
if first != second {
return Err(DescriptorBindingRequirementsIncompatible::ImageFormat);
}
}
if let (Some(first), Some(second)) = (*image_scalar_type, other.image_scalar_type) {
if first != second {
return Err(DescriptorBindingRequirementsIncompatible::ImageScalarType);
}
}
if let (Some(first), Some(second)) = (*image_view_type, other.image_view_type) {
if first != second {
return Err(DescriptorBindingRequirementsIncompatible::ImageViewType);
}
}
if *image_multisampled != other.image_multisampled {
return Err(DescriptorBindingRequirementsIncompatible::ImageMultisampled);
}
/* Merge */
descriptor_types.retain(|ty| other.descriptor_types.contains(ty));
*descriptor_count = (*descriptor_count).max(other.descriptor_count);
*image_format = image_format.or(other.image_format);
*image_scalar_type = image_scalar_type.or(other.image_scalar_type);
*image_view_type = image_view_type.or(other.image_view_type);
*stages |= other.stages;
for (&index, other) in &other.descriptors {
match descriptors.entry(index) {
Entry::Vacant(entry) => {
entry.insert(other.clone());
}
Entry::Occupied(entry) => {
entry.into_mut().merge(other);
}
}
}
Ok(())
}
}
impl DescriptorRequirements {
/// Merges `other` into `self`, so that `self` satisfies the requirements of both.
#[inline]
pub fn merge(&mut self, other: &Self) {
let Self {
memory_read,
memory_write,
sampler_compare,
sampler_no_unnormalized_coordinates,
sampler_no_ycbcr_conversion,
sampler_with_images,
stages: self.stages | other.stages,
storage_image_atomic: &self.storage_image_atomic | &other.storage_image_atomic,
storage_read: &self.storage_read | &other.storage_read,
storage_write: &self.storage_write | &other.storage_write,
})
storage_image_atomic,
} = self;
*memory_read |= other.memory_read;
*memory_write |= other.memory_write;
*sampler_compare |= other.sampler_compare;
*sampler_no_unnormalized_coordinates |= other.sampler_no_unnormalized_coordinates;
*sampler_no_ycbcr_conversion |= other.sampler_no_ycbcr_conversion;
sampler_with_images.extend(&other.sampler_with_images);
*storage_image_atomic |= other.storage_image_atomic;
}
}
/// An error that can be returned when trying to create the intersection of two
/// `DescriptorRequirements` values.
/// `DescriptorBindingRequirements` values.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DescriptorRequirementsIncompatible {
pub enum DescriptorBindingRequirementsIncompatible {
/// The allowed descriptor types of the descriptors do not overlap.
DescriptorType,
/// The descriptors require different formats.
@ -699,26 +736,26 @@ pub enum DescriptorRequirementsIncompatible {
ImageViewType,
}
impl Error for DescriptorRequirementsIncompatible {}
impl Error for DescriptorBindingRequirementsIncompatible {}
impl Display for DescriptorRequirementsIncompatible {
impl Display for DescriptorBindingRequirementsIncompatible {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
DescriptorRequirementsIncompatible::DescriptorType => write!(
DescriptorBindingRequirementsIncompatible::DescriptorType => write!(
f,
"the allowed descriptor types of the two descriptors do not overlap",
),
DescriptorRequirementsIncompatible::ImageFormat => {
DescriptorBindingRequirementsIncompatible::ImageFormat => {
write!(f, "the descriptors require different formats",)
}
DescriptorRequirementsIncompatible::ImageMultisampled => write!(
DescriptorBindingRequirementsIncompatible::ImageMultisampled => write!(
f,
"the multisampling requirements of the descriptors differ",
),
DescriptorRequirementsIncompatible::ImageScalarType => {
DescriptorBindingRequirementsIncompatible::ImageScalarType => {
write!(f, "the descriptors require different scalar types",)
}
DescriptorRequirementsIncompatible::ImageViewType => {
DescriptorBindingRequirementsIncompatible::ImageViewType => {
write!(f, "the descriptors require different image view types",)
}
}

View File

@ -9,6 +9,7 @@
//! Extraction of information from SPIR-V modules, that is needed by the rest of Vulkano.
use super::DescriptorBindingRequirements;
use crate::{
descriptor_set::layout::DescriptorType,
image::view::ImageViewType,
@ -72,13 +73,12 @@ pub fn entry_points(
let execution = shader_execution(spirv, execution_model, function_id);
let stage = ShaderStage::from(execution);
let mut descriptor_requirements =
inspect_entry_point(&interface_variables.descriptor, spirv, function_id);
for reqs in descriptor_requirements.values_mut() {
reqs.stages = stage.into();
}
let descriptor_binding_requirements = inspect_entry_point(
&interface_variables.descriptor_binding,
spirv,
stage,
function_id,
);
let push_constant_requirements = push_constant_requirements(spirv, stage);
let specialization_constant_requirements = specialization_constant_requirements(spirv);
let input_interface = shader_interface(
@ -104,7 +104,7 @@ pub fn entry_points(
execution_model,
EntryPointInfo {
execution,
descriptor_requirements,
descriptor_binding_requirements,
push_constant_requirements,
specialization_constant_requirements,
input_interface,
@ -170,15 +170,15 @@ fn shader_execution(
#[derive(Clone, Debug, Default)]
struct InterfaceVariables {
descriptor: HashMap<Id, DescriptorVariable>,
descriptor_binding: HashMap<Id, DescriptorBindingVariable>,
}
// See also section 14.5.2 of the Vulkan specs: Descriptor Set Interface.
#[derive(Clone, Debug)]
struct DescriptorVariable {
struct DescriptorBindingVariable {
set: u32,
binding: u32,
reqs: DescriptorRequirements,
reqs: DescriptorBindingRequirements,
}
fn interface_variables(spirv: &Spirv) -> InterfaceVariables {
@ -196,9 +196,10 @@ fn interface_variables(spirv: &Spirv) -> InterfaceVariables {
StorageClass::StorageBuffer
| StorageClass::Uniform
| StorageClass::UniformConstant => {
variables
.descriptor
.insert(*result_id, descriptor_requirements_of(spirv, *result_id));
variables.descriptor_binding.insert(
*result_id,
descriptor_binding_requirements_of(spirv, *result_id),
);
}
_ => (),
}
@ -209,47 +210,67 @@ fn interface_variables(spirv: &Spirv) -> InterfaceVariables {
}
fn inspect_entry_point(
global: &HashMap<Id, DescriptorVariable>,
global: &HashMap<Id, DescriptorBindingVariable>,
spirv: &Spirv,
stage: ShaderStage,
entry_point: Id,
) -> HashMap<(u32, u32), DescriptorRequirements> {
fn instruction_chain<'a, const N: usize>(
result: &'a mut HashMap<Id, DescriptorVariable>,
global: &HashMap<Id, DescriptorVariable>,
spirv: &Spirv,
) -> HashMap<(u32, u32), DescriptorBindingRequirements> {
struct Context<'a> {
global: &'a HashMap<Id, DescriptorBindingVariable>,
spirv: &'a Spirv,
stage: ShaderStage,
inspected_functions: HashSet<Id>,
result: HashMap<Id, DescriptorBindingVariable>,
}
impl<'a> Context<'a> {
fn instruction_chain<const N: usize>(
&mut self,
chain: [fn(&Spirv, Id) -> Option<Id>; N],
id: Id,
) -> Option<(&'a mut DescriptorVariable, Option<u32>)> {
let id = chain.into_iter().try_fold(id, |id, func| func(spirv, id))?;
) -> Option<(&mut DescriptorBindingVariable, Option<u32>)> {
let id = chain
.into_iter()
.try_fold(id, |id, func| func(self.spirv, id))?;
if let Some(variable) = global.get(&id) {
if let Some(variable) = self.global.get(&id) {
// Variable was accessed without an access chain, return with index 0.
let variable = result.entry(id).or_insert_with(|| variable.clone());
let variable = self.result.entry(id).or_insert_with(|| variable.clone());
variable.reqs.stages = self.stage.into();
return Some((variable, Some(0)));
}
let (id, indexes) = match *spirv.id(id).instruction() {
let (id, indexes) = match *self.spirv.id(id).instruction() {
Instruction::AccessChain {
base, ref indexes, ..
} => (base, indexes),
_ => return None,
};
if let Some(variable) = global.get(&id) {
if let Some(variable) = self.global.get(&id) {
// Variable was accessed with an access chain.
// Retrieve index from instruction if it's a constant value.
// TODO: handle a `None` index too?
let index = match *spirv.id(*indexes.first().unwrap()).instruction() {
let index = match *self.spirv.id(*indexes.first().unwrap()).instruction() {
Instruction::Constant { ref value, .. } => Some(value[0]),
_ => None,
};
let variable = result.entry(id).or_insert_with(|| variable.clone());
let variable = self.result.entry(id).or_insert_with(|| variable.clone());
variable.reqs.stages = self.stage.into();
return Some((variable, index));
}
None
}
fn inspect_entry_point_r(&mut self, function: Id) {
fn desc_reqs(
descriptor_variable: Option<(&mut DescriptorBindingVariable, Option<u32>)>,
) -> Option<&mut DescriptorRequirements> {
descriptor_variable
.map(|(variable, index)| variable.reqs.descriptors.entry(index).or_default())
}
fn inst_image_texel_pointer(spirv: &Spirv, id: Id) -> Option<Id> {
match *spirv.id(id).instruction() {
Instruction::ImageTexelPointer { image, .. } => Some(image),
@ -271,16 +292,10 @@ fn inspect_entry_point(
}
}
fn inspect_entry_point_r(
result: &mut HashMap<Id, DescriptorVariable>,
inspected_functions: &mut HashSet<Id>,
global: &HashMap<Id, DescriptorVariable>,
spirv: &Spirv,
function: Id,
) {
inspected_functions.insert(function);
self.inspected_functions.insert(function);
let mut in_function = false;
for instruction in spirv.instructions() {
for instruction in self.spirv.instructions() {
if !in_function {
match *instruction {
Instruction::Function { result_id, .. } if result_id == function => {
@ -289,25 +304,42 @@ fn inspect_entry_point(
_ => {}
}
} else {
let stage = self.stage;
match *instruction {
Instruction::AtomicLoad { pointer, .. } => {
// Storage buffer
instruction_chain(result, global, spirv, [], pointer);
if let Some(desc_reqs) = desc_reqs(self.instruction_chain([], pointer))
{
desc_reqs.memory_read = stage.into();
}
// Storage image
if let Some((variable, Some(index))) = instruction_chain(
result,
global,
spirv,
[inst_image_texel_pointer],
pointer,
if let Some(desc_reqs) = desc_reqs(
self.instruction_chain([inst_image_texel_pointer], pointer),
) {
variable.reqs.storage_image_atomic.insert(index);
desc_reqs.memory_read = stage.into();
desc_reqs.storage_image_atomic = true;
}
}
Instruction::AtomicStore { pointer, .. }
| Instruction::AtomicExchange { pointer, .. }
Instruction::AtomicStore { pointer, .. } => {
// Storage buffer
if let Some(desc_reqs) = desc_reqs(self.instruction_chain([], pointer))
{
desc_reqs.memory_write = stage.into();
}
// Storage image
if let Some(desc_reqs) = desc_reqs(
self.instruction_chain([inst_image_texel_pointer], pointer),
) {
desc_reqs.memory_write = stage.into();
desc_reqs.storage_image_atomic = true;
}
}
Instruction::AtomicExchange { pointer, .. }
| Instruction::AtomicCompareExchange { pointer, .. }
| Instruction::AtomicCompareExchangeWeak { pointer, .. }
| Instruction::AtomicIIncrement { pointer, .. }
@ -327,39 +359,36 @@ fn inspect_entry_point(
| Instruction::AtomicFMaxEXT { pointer, .. }
| Instruction::AtomicFAddEXT { pointer, .. } => {
// Storage buffer
if let Some((variable, Some(index))) =
instruction_chain(result, global, spirv, [], pointer)
if let Some(desc_reqs) = desc_reqs(self.instruction_chain([], pointer))
{
variable.reqs.storage_write.insert(index);
desc_reqs.memory_read = stage.into();
desc_reqs.memory_write = stage.into();
}
// Storage image
if let Some((variable, Some(index))) = instruction_chain(
result,
global,
spirv,
[inst_image_texel_pointer],
pointer,
if let Some(desc_reqs) = desc_reqs(
self.instruction_chain([inst_image_texel_pointer], pointer),
) {
variable.reqs.storage_write.insert(index);
variable.reqs.storage_image_atomic.insert(index);
desc_reqs.memory_read = stage.into();
desc_reqs.memory_write = stage.into();
desc_reqs.storage_image_atomic = true;
}
}
Instruction::CopyMemory { target, source, .. } => {
instruction_chain(result, global, spirv, [], target);
instruction_chain(result, global, spirv, [], source);
self.instruction_chain([], target);
self.instruction_chain([], source);
}
Instruction::CopyObject { operand, .. } => {
instruction_chain(result, global, spirv, [], operand);
self.instruction_chain([], operand);
}
Instruction::ExtInst { ref operands, .. } => {
// We don't know which extended instructions take pointers,
// so we must interpret every operand as a pointer.
for &operand in operands {
instruction_chain(result, global, spirv, [], operand);
self.instruction_chain([], operand);
}
}
@ -371,17 +400,11 @@ fn inspect_entry_point(
// Rather than trying to figure out the type of each argument, we just
// try all of them as pointers.
for &argument in arguments {
instruction_chain(result, global, spirv, [], argument);
self.instruction_chain([], argument);
}
if !inspected_functions.contains(&function) {
inspect_entry_point_r(
result,
inspected_functions,
global,
spirv,
function,
);
if !self.inspected_functions.contains(&function) {
self.inspect_entry_point_r(function);
}
}
@ -397,42 +420,36 @@ fn inspect_entry_point(
image_operands,
..
} => {
if let Some((variable, Some(index))) = instruction_chain(
result,
global,
spirv,
if let Some(desc_reqs) =
desc_reqs(self.instruction_chain(
[inst_sampled_image, inst_load],
sampled_image,
) {
variable.reqs.sampler_no_ycbcr_conversion.insert(index);
))
{
desc_reqs.memory_read = stage.into();
desc_reqs.sampler_no_ycbcr_conversion = true;
if image_operands.as_ref().map_or(false, |image_operands| {
image_operands.bias.is_some()
|| image_operands.const_offset.is_some()
|| image_operands.offset.is_some()
}) {
variable
.reqs
.sampler_no_unnormalized_coordinates
.insert(index);
desc_reqs.sampler_no_unnormalized_coordinates = true;
}
}
}
Instruction::ImageDrefGather { sampled_image, .. }
| Instruction::ImageSparseDrefGather { sampled_image, .. } => {
if let Some((variable, Some(index))) = instruction_chain(
result,
global,
spirv,
if let Some(desc_reqs) =
desc_reqs(self.instruction_chain(
[inst_sampled_image, inst_load],
sampled_image,
) {
variable
.reqs
.sampler_no_unnormalized_coordinates
.insert(index);
variable.reqs.sampler_no_ycbcr_conversion.insert(index);
))
{
desc_reqs.memory_read = stage.into();
desc_reqs.sampler_no_unnormalized_coordinates = true;
desc_reqs.sampler_no_ycbcr_conversion = true;
}
}
@ -456,23 +473,20 @@ fn inspect_entry_point(
image_operands,
..
} => {
if let Some((variable, Some(index))) = instruction_chain(
result,
global,
spirv,
if let Some(desc_reqs) =
desc_reqs(self.instruction_chain(
[inst_sampled_image, inst_load],
sampled_image,
) {
variable
.reqs
.sampler_no_unnormalized_coordinates
.insert(index);
))
{
desc_reqs.memory_read = stage.into();
desc_reqs.sampler_no_unnormalized_coordinates = true;
if image_operands.as_ref().map_or(false, |image_operands| {
image_operands.const_offset.is_some()
|| image_operands.offset.is_some()
}) {
variable.reqs.sampler_no_ycbcr_conversion.insert(index);
desc_reqs.sampler_no_ycbcr_conversion = true;
}
}
}
@ -487,22 +501,19 @@ fn inspect_entry_point(
image_operands,
..
} => {
if let Some((variable, Some(index))) = instruction_chain(
result,
global,
spirv,
if let Some(desc_reqs) =
desc_reqs(self.instruction_chain(
[inst_sampled_image, inst_load],
sampled_image,
) {
variable
.reqs
.sampler_no_unnormalized_coordinates
.insert(index);
))
{
desc_reqs.memory_read = stage.into();
desc_reqs.sampler_no_unnormalized_coordinates = true;
if image_operands.const_offset.is_some()
|| image_operands.offset.is_some()
{
variable.reqs.sampler_no_ycbcr_conversion.insert(index);
desc_reqs.sampler_no_ycbcr_conversion = true;
}
}
}
@ -527,24 +538,21 @@ fn inspect_entry_point(
image_operands,
..
} => {
if let Some((variable, Some(index))) = instruction_chain(
result,
global,
spirv,
if let Some(desc_reqs) =
desc_reqs(self.instruction_chain(
[inst_sampled_image, inst_load],
sampled_image,
) {
variable
.reqs
.sampler_no_unnormalized_coordinates
.insert(index);
variable.reqs.sampler_compare.insert(index);
))
{
desc_reqs.memory_read = stage.into();
desc_reqs.sampler_no_unnormalized_coordinates = true;
desc_reqs.sampler_compare = true;
if image_operands.as_ref().map_or(false, |image_operands| {
image_operands.const_offset.is_some()
|| image_operands.offset.is_some()
}) {
variable.reqs.sampler_no_ycbcr_conversion.insert(index);
desc_reqs.sampler_no_ycbcr_conversion = true;
}
}
}
@ -569,23 +577,20 @@ fn inspect_entry_point(
image_operands,
..
} => {
if let Some((variable, Some(index))) = instruction_chain(
result,
global,
spirv,
if let Some(desc_reqs) =
desc_reqs(self.instruction_chain(
[inst_sampled_image, inst_load],
sampled_image,
) {
variable
.reqs
.sampler_no_unnormalized_coordinates
.insert(index);
variable.reqs.sampler_compare.insert(index);
))
{
desc_reqs.memory_read = stage.into();
desc_reqs.sampler_no_unnormalized_coordinates = true;
desc_reqs.sampler_compare = true;
if image_operands.const_offset.is_some()
|| image_operands.offset.is_some()
{
variable.reqs.sampler_no_ycbcr_conversion.insert(index);
desc_reqs.sampler_no_ycbcr_conversion = true;
}
}
}
@ -600,58 +605,76 @@ fn inspect_entry_point(
image_operands,
..
} => {
if let Some((variable, Some(index))) = instruction_chain(
result,
global,
spirv,
if let Some(desc_reqs) =
desc_reqs(self.instruction_chain(
[inst_sampled_image, inst_load],
sampled_image,
) {
))
{
desc_reqs.memory_read = stage.into();
if image_operands.bias.is_some()
|| image_operands.const_offset.is_some()
|| image_operands.offset.is_some()
{
variable
.reqs
.sampler_no_unnormalized_coordinates
.insert(index);
desc_reqs.sampler_no_unnormalized_coordinates = true;
}
if image_operands.const_offset.is_some()
|| image_operands.offset.is_some()
{
variable.reqs.sampler_no_ycbcr_conversion.insert(index);
desc_reqs.sampler_no_ycbcr_conversion = true;
}
}
}
Instruction::ImageTexelPointer { image, .. } => {
instruction_chain(result, global, spirv, [], image);
self.instruction_chain([], image);
}
Instruction::ImageRead { image, .. } => {
if let Some((variable, Some(index))) =
instruction_chain(result, global, spirv, [inst_load], image)
if let Some(desc_reqs) =
desc_reqs(self.instruction_chain([inst_load], image))
{
variable.reqs.storage_read.insert(index);
desc_reqs.memory_read = stage.into();
}
}
Instruction::ImageWrite { image, .. } => {
if let Some((variable, Some(index))) =
instruction_chain(result, global, spirv, [inst_load], image)
if let Some(desc_reqs) =
desc_reqs(self.instruction_chain([inst_load], image))
{
variable.reqs.storage_write.insert(index);
desc_reqs.memory_write = stage.into();
}
}
Instruction::Load { pointer, .. } => {
instruction_chain(result, global, spirv, [], pointer);
if let Some((binding_variable, index)) =
self.instruction_chain([], pointer)
{
// Only loads on buffers access memory directly.
// Loads on images load the image object itself, but don't touch
// the texels in memory yet.
if binding_variable.reqs.descriptor_types.iter().any(|ty| {
matches!(
ty,
DescriptorType::UniformBuffer
| DescriptorType::UniformBufferDynamic
| DescriptorType::StorageBuffer
| DescriptorType::StorageBufferDynamic
)
}) {
if let Some(desc_reqs) =
desc_reqs(Some((binding_variable, index)))
{
desc_reqs.memory_read = stage.into();
}
}
}
}
Instruction::SampledImage { image, sampler, .. } => {
let identifier =
match instruction_chain(result, global, spirv, [inst_load], image) {
let identifier = match self.instruction_chain([inst_load], image) {
Some((variable, Some(index))) => DescriptorIdentifier {
set: variable.set,
binding: variable.binding,
@ -660,23 +683,18 @@ fn inspect_entry_point(
_ => continue,
};
if let Some((variable, Some(index))) =
instruction_chain(result, global, spirv, [inst_load], sampler)
if let Some(desc_reqs) =
desc_reqs(self.instruction_chain([inst_load], sampler))
{
variable
.reqs
.sampler_with_images
.entry(index)
.or_default()
.insert(identifier);
desc_reqs.sampler_with_images.insert(identifier);
}
}
Instruction::Store { pointer, .. } => {
if let Some((variable, Some(index))) =
instruction_chain(result, global, spirv, [], pointer)
// This can only apply to buffers, right?
if let Some(desc_reqs) = desc_reqs(self.instruction_chain([], pointer))
{
variable.reqs.storage_write.insert(index);
desc_reqs.memory_write = stage.into();
}
}
@ -685,30 +703,31 @@ fn inspect_entry_point(
}
}
}
}
let mut result = HashMap::default();
let mut inspected_functions = HashSet::default();
inspect_entry_point_r(
&mut result,
&mut inspected_functions,
let mut context = Context {
global,
spirv,
entry_point,
);
stage,
inspected_functions: HashSet::default(),
result: HashMap::default(),
};
context.inspect_entry_point_r(entry_point);
result
context
.result
.into_iter()
.map(|(_, variable)| ((variable.set, variable.binding), variable.reqs))
.collect()
}
/// Returns a `DescriptorRequirements` value for the pointed type.
/// Returns a `DescriptorBindingRequirements` value for the pointed type.
///
/// See also section 14.5.2 of the Vulkan specs: Descriptor Set Interface
fn descriptor_requirements_of(spirv: &Spirv, variable_id: Id) -> DescriptorVariable {
fn descriptor_binding_requirements_of(spirv: &Spirv, variable_id: Id) -> DescriptorBindingVariable {
let variable_id_info = spirv.id(variable_id);
let mut reqs = DescriptorRequirements {
let mut reqs = DescriptorBindingRequirements {
descriptor_count: Some(1),
..Default::default()
};
@ -916,7 +935,7 @@ fn descriptor_requirements_of(spirv: &Spirv, variable_id: Id) -> DescriptorVaria
};
}
DescriptorVariable {
DescriptorBindingVariable {
set: variable_id_info
.iter_decoration()
.find_map(|instruction| match *instruction {