mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-10-29 21:42:26 +00:00
Refactor DescriptorRequirements
(#2081)
This commit is contained in:
parent
021540e6b6
commit
4ccfc925fa
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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,68 +87,77 @@ 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 (set_num, binding_num) = loc;
|
||||
let DescriptorRequirements {
|
||||
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;
|
||||
let descriptor_binding_requirements =
|
||||
descriptor_binding_requirements
|
||||
.iter()
|
||||
.map(|(loc, binding_reqs)| {
|
||||
let (set_num, binding_num) = loc;
|
||||
let DescriptorBindingRequirements {
|
||||
descriptor_types,
|
||||
descriptor_count,
|
||||
image_format,
|
||||
image_multisampled,
|
||||
image_scalar_type,
|
||||
image_view_type,
|
||||
stages,
|
||||
descriptors,
|
||||
} = binding_reqs;
|
||||
|
||||
let descriptor_types = descriptor_types.iter().map(|ty| {
|
||||
let ident = format_ident!("{}", format!("{:?}", ty));
|
||||
quote! { ::vulkano::descriptor_set::layout::DescriptorType::#ident }
|
||||
});
|
||||
let descriptor_count = match descriptor_count {
|
||||
Some(descriptor_count) => quote! { Some(#descriptor_count) },
|
||||
None => quote! { None },
|
||||
};
|
||||
let image_format = match image_format {
|
||||
Some(image_format) => {
|
||||
let ident = format_ident!("{}", format!("{:?}", image_format));
|
||||
quote! { Some(::vulkano::format::Format::#ident) }
|
||||
}
|
||||
None => quote! { None },
|
||||
};
|
||||
let image_scalar_type = match image_scalar_type {
|
||||
Some(image_scalar_type) => {
|
||||
let ident = format_ident!("{}", format!("{:?}", image_scalar_type));
|
||||
quote! { Some(::vulkano::shader::ShaderScalarType::#ident) }
|
||||
}
|
||||
None => quote! { None },
|
||||
};
|
||||
let image_view_type = match image_view_type {
|
||||
Some(image_view_type) => {
|
||||
let ident = format_ident!("{}", format!("{:?}", image_view_type));
|
||||
quote! { Some(::vulkano::image::view::ImageViewType::#ident) }
|
||||
}
|
||||
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 {
|
||||
set,
|
||||
binding,
|
||||
index,
|
||||
}| {
|
||||
let descriptor_types_items = descriptor_types.iter().map(|ty| {
|
||||
let ident = format_ident!("{}", format!("{:?}", ty));
|
||||
quote! { ::vulkano::descriptor_set::layout::DescriptorType::#ident }
|
||||
});
|
||||
let descriptor_count = match descriptor_count {
|
||||
Some(descriptor_count) => quote! { Some(#descriptor_count) },
|
||||
None => quote! { None },
|
||||
};
|
||||
let image_format = match image_format {
|
||||
Some(image_format) => {
|
||||
let ident = format_ident!("{}", format!("{:?}", image_format));
|
||||
quote! { Some(::vulkano::format::Format::#ident) }
|
||||
}
|
||||
None => quote! { None },
|
||||
};
|
||||
let image_scalar_type = match image_scalar_type {
|
||||
Some(image_scalar_type) => {
|
||||
let ident = format_ident!("{}", format!("{:?}", image_scalar_type));
|
||||
quote! { Some(::vulkano::shader::ShaderScalarType::#ident) }
|
||||
}
|
||||
None => quote! { None },
|
||||
};
|
||||
let image_view_type = match image_view_type {
|
||||
Some(image_view_type) => {
|
||||
let ident = format_ident!("{}", format!("{:?}", image_view_type));
|
||||
quote! { Some(::vulkano::image::view::ImageViewType::#ident) }
|
||||
}
|
||||
None => quote! { None },
|
||||
};
|
||||
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,
|
||||
}| {
|
||||
quote! {
|
||||
::vulkano::shader::DescriptorIdentifier {
|
||||
set: #set,
|
||||
@ -154,90 +165,44 @@ fn write_descriptor_requirements(
|
||||
index: #index,
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
quote! {
|
||||
(
|
||||
#index,
|
||||
::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,
|
||||
}
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
(
|
||||
#index,
|
||||
[#(#identifiers),*].into_iter().collect(),
|
||||
)
|
||||
(#set_num, #binding_num),
|
||||
::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,
|
||||
stages: #stages,
|
||||
descriptors: [#(#descriptor_items),*].into_iter().collect(),
|
||||
},
|
||||
),
|
||||
}
|
||||
})
|
||||
};
|
||||
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),*],
|
||||
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(),
|
||||
},
|
||||
),
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
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)|* }
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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>| {
|
||||
if layout_binding.descriptor_type == DescriptorType::StorageTexelBuffer {
|
||||
// VUID-vkCmdDispatch-OpTypeImage-06423
|
||||
if reqs.image_format.is_none()
|
||||
&& reqs.storage_write.contains(&index)
|
||||
&& !buffer_view
|
||||
.format_features()
|
||||
.intersects(FormatFeatures::STORAGE_WRITE_WITHOUT_FORMAT)
|
||||
{
|
||||
return Err(
|
||||
DescriptorResourceInvalidError::StorageWriteWithoutFormatNotSupported,
|
||||
);
|
||||
}
|
||||
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 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);
|
||||
}
|
||||
|
||||
// VUID-vkCmdDispatch-OpTypeImage-06424
|
||||
if reqs.image_format.is_none()
|
||||
&& reqs.storage_read.contains(&index)
|
||||
&& !buffer_view
|
||||
.format_features()
|
||||
.intersects(FormatFeatures::STORAGE_READ_WITHOUT_FORMAT)
|
||||
{
|
||||
return Err(
|
||||
DescriptorResourceInvalidError::StorageReadWithoutFormatNotSupported,
|
||||
);
|
||||
// VUID-vkCmdDispatch-OpTypeImage-06424
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -683,38 +703,42 @@ where
|
||||
};
|
||||
|
||||
let check_image_view_common = |index: u32, image_view: &Arc<dyn ImageViewAbstract>| {
|
||||
// VUID-vkCmdDispatch-None-02691
|
||||
if reqs.storage_image_atomic.contains(&index)
|
||||
&& !image_view
|
||||
.format_features()
|
||||
.intersects(FormatFeatures::STORAGE_IMAGE_ATOMIC)
|
||||
for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter())
|
||||
.chain(binding_reqs.descriptors.get(&None))
|
||||
{
|
||||
return Err(DescriptorResourceInvalidError::StorageImageAtomicNotSupported);
|
||||
}
|
||||
|
||||
if layout_binding.descriptor_type == DescriptorType::StorageImage {
|
||||
// VUID-vkCmdDispatch-OpTypeImage-06423
|
||||
if reqs.image_format.is_none()
|
||||
&& reqs.storage_write.contains(&index)
|
||||
// VUID-vkCmdDispatch-None-02691
|
||||
if desc_reqs.storage_image_atomic
|
||||
&& !image_view
|
||||
.format_features()
|
||||
.intersects(FormatFeatures::STORAGE_WRITE_WITHOUT_FORMAT)
|
||||
.intersects(FormatFeatures::STORAGE_IMAGE_ATOMIC)
|
||||
{
|
||||
return Err(
|
||||
DescriptorResourceInvalidError::StorageWriteWithoutFormatNotSupported,
|
||||
);
|
||||
return Err(DescriptorResourceInvalidError::StorageImageAtomicNotSupported);
|
||||
}
|
||||
|
||||
// VUID-vkCmdDispatch-OpTypeImage-06424
|
||||
if reqs.image_format.is_none()
|
||||
&& reqs.storage_read.contains(&index)
|
||||
&& !image_view
|
||||
.format_features()
|
||||
.intersects(FormatFeatures::STORAGE_READ_WITHOUT_FORMAT)
|
||||
{
|
||||
return Err(
|
||||
if layout_binding.descriptor_type == DescriptorType::StorageImage {
|
||||
// VUID-vkCmdDispatch-OpTypeImage-06423
|
||||
if binding_reqs.image_format.is_none()
|
||||
&& !desc_reqs.memory_write.is_empty()
|
||||
&& !image_view
|
||||
.format_features()
|
||||
.intersects(FormatFeatures::STORAGE_WRITE_WITHOUT_FORMAT)
|
||||
{
|
||||
return Err(
|
||||
DescriptorResourceInvalidError::StorageWriteWithoutFormatNotSupported,
|
||||
);
|
||||
}
|
||||
|
||||
// VUID-vkCmdDispatch-OpTypeImage-06424
|
||||
if binding_reqs.image_format.is_none()
|
||||
&& !desc_reqs.memory_read.is_empty()
|
||||
&& !image_view
|
||||
.format_features()
|
||||
.intersects(FormatFeatures::STORAGE_READ_WITHOUT_FORMAT)
|
||||
{
|
||||
return Err(
|
||||
DescriptorResourceInvalidError::StorageReadWithoutFormatNotSupported,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -724,7 +748,7 @@ where
|
||||
*/
|
||||
|
||||
// The SPIR-V Image Format is not compatible with the image view’s 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 image’s 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,40 +822,46 @@ where
|
||||
};
|
||||
|
||||
let check_sampler_common = |index: u32, sampler: &Arc<Sampler>| {
|
||||
// VUID-vkCmdDispatch-None-02703
|
||||
// VUID-vkCmdDispatch-None-02704
|
||||
if reqs.sampler_no_unnormalized_coordinates.contains(&index)
|
||||
&& sampler.unnormalized_coordinates()
|
||||
for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter())
|
||||
.chain(binding_reqs.descriptors.get(&None))
|
||||
{
|
||||
return Err(
|
||||
// VUID-vkCmdDispatch-None-02703
|
||||
// VUID-vkCmdDispatch-None-02704
|
||||
if desc_reqs.sampler_no_unnormalized_coordinates
|
||||
&& sampler.unnormalized_coordinates()
|
||||
{
|
||||
return Err(
|
||||
DescriptorResourceInvalidError::SamplerUnnormalizedCoordinatesNotAllowed,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// - OpImageFetch, OpImageSparseFetch, OpImage*Gather, and OpImageSparse*Gather must not
|
||||
// be used with a sampler that enables sampler Y′CBCR conversion.
|
||||
// - The ConstOffset and Offset operands must not be used with a sampler that enables
|
||||
// sampler Y′CBCR conversion.
|
||||
if reqs.sampler_no_ycbcr_conversion.contains(&index)
|
||||
&& sampler.sampler_ycbcr_conversion().is_some()
|
||||
{
|
||||
return Err(DescriptorResourceInvalidError::SamplerYcbcrConversionNotAllowed);
|
||||
}
|
||||
// - OpImageFetch, OpImageSparseFetch, OpImage*Gather, and OpImageSparse*Gather must not
|
||||
// be used with a sampler that enables sampler Y′CBCR conversion.
|
||||
// - The ConstOffset and Offset operands must not be used with a sampler that enables
|
||||
// sampler Y′CBCR conversion.
|
||||
if desc_reqs.sampler_no_ycbcr_conversion
|
||||
&& sampler.sampler_ycbcr_conversion().is_some()
|
||||
{
|
||||
return Err(
|
||||
DescriptorResourceInvalidError::SamplerYcbcrConversionNotAllowed,
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
Instruction/Sampler/Image View Validation
|
||||
https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap16.html#textures-input-validation
|
||||
*/
|
||||
/*
|
||||
Instruction/Sampler/Image View Validation
|
||||
https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap16.html#textures-input-validation
|
||||
*/
|
||||
|
||||
// - The SPIR-V instruction is one of the OpImage*Dref* instructions and the sampler
|
||||
// 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() {
|
||||
return Err(DescriptorResourceInvalidError::SamplerCompareMismatch {
|
||||
required: reqs.sampler_compare.contains(&index),
|
||||
provided: sampler.compare().is_some(),
|
||||
});
|
||||
// - The SPIR-V instruction is one of the OpImage*Dref* instructions and the sampler
|
||||
// compareEnable is VK_FALSE
|
||||
// - The SPIR-V instruction is not one of the OpImage*Dref* instructions and the sampler
|
||||
// compareEnable is VK_TRUE
|
||||
if desc_reqs.sampler_compare != sampler.compare().is_some() {
|
||||
return Err(DescriptorResourceInvalidError::SamplerCompareMismatch {
|
||||
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;
|
||||
|
||||
// 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::StorageTexelBuffer
|
||||
| DescriptorType::StorageBuffer
|
||||
| DescriptorType::StorageBufferDynamic => AccessFlags::SHADER_READ,
|
||||
DescriptorType::InputAttachment => AccessFlags::INPUT_ATTACHMENT_READ,
|
||||
DescriptorType::UniformBuffer | DescriptorType::UniformBufferDynamic => {
|
||||
AccessFlags::UNIFORM_READ
|
||||
}
|
||||
},
|
||||
exclusive: false,
|
||||
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.
|
||||
continue;
|
||||
}
|
||||
DescriptorType::CombinedImageSampler
|
||||
| DescriptorType::SampledImage
|
||||
| DescriptorType::UniformTexelBuffer => (Some(AccessFlags::SHADER_READ), None),
|
||||
DescriptorType::StorageImage
|
||||
| DescriptorType::StorageTexelBuffer
|
||||
| DescriptorType::StorageBuffer
|
||||
| DescriptorType::StorageBufferDynamic => (
|
||||
Some(AccessFlags::SHADER_READ),
|
||||
Some(AccessFlags::SHADER_WRITE),
|
||||
),
|
||||
DescriptorType::UniformBuffer | DescriptorType::UniformBufferDynamic => {
|
||||
(Some(AccessFlags::UNIFORM_READ), None)
|
||||
}
|
||||
};
|
||||
|
||||
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,
|
||||
)| {
|
||||
(
|
||||
format!("Buffer bound to set {} descriptor {}", set, binding).into(),
|
||||
Resource::Buffer {
|
||||
buffer,
|
||||
range,
|
||||
memory,
|
||||
},
|
||||
)
|
||||
[memory_read, memory_write].into_iter().flatten()
|
||||
};
|
||||
let image_resource = move |(image, subresource_range, memory): (
|
||||
let buffer_resource =
|
||||
|(index, buffer, range): (usize, Arc<dyn BufferAccess>, Range<DeviceSize>)| {
|
||||
memory_iter(index).map(move |memory| {
|
||||
(
|
||||
format!(
|
||||
"Buffer bound to set {} descriptor {} index {}",
|
||||
set, binding, index
|
||||
)
|
||||
.into(),
|
||||
Resource::Buffer {
|
||||
buffer: buffer.clone(),
|
||||
range: range.clone(),
|
||||
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);
|
||||
(
|
||||
format!("Image bound to set {} descriptor {}", set, binding).into(),
|
||||
Resource::Image {
|
||||
image,
|
||||
subresource_range,
|
||||
memory,
|
||||
start_layout: layout,
|
||||
end_layout: layout,
|
||||
},
|
||||
)
|
||||
|
||||
memory_iter(index).map(move |memory| {
|
||||
(
|
||||
format!(
|
||||
"Image bound to set {} descriptor {} index {}",
|
||||
set, binding, index
|
||||
)
|
||||
.into(),
|
||||
Resource::Image {
|
||||
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(_) => (),
|
||||
|
@ -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),
|
||||
|
@ -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>,
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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",)
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user