diff --git a/examples/src/bin/runtime_array/main.rs b/examples/src/bin/runtime_array/main.rs index 6b8666bc..b843769f 100644 --- a/examples/src/bin/runtime_array/main.rs +++ b/examples/src/bin/runtime_array/main.rs @@ -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 diff --git a/vulkano-shaders/src/codegen.rs b/vulkano-shaders/src/codegen.rs index ac9be9a0..90b2b6c7 100644 --- a/vulkano-shaders/src/codegen.rs +++ b/vulkano-shaders/src/codegen.rs @@ -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); diff --git a/vulkano-shaders/src/entry_point.rs b/vulkano-shaders/src/entry_point.rs index b30f5898..916a4af3 100644 --- a/vulkano-shaders/src/entry_point.rs +++ b/vulkano-shaders/src/entry_point.rs @@ -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)|* } + } +} diff --git a/vulkano-shaders/src/structs.rs b/vulkano-shaders/src/structs.rs index 1d2613af..edc5af66 100644 --- a/vulkano-shaders/src/structs.rs +++ b/vulkano-shaders/src/structs.rs @@ -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, bool) { +fn write_struct_members(spirv: &Spirv, struct_id: Id, members: &[Id]) -> (Vec, 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 { /// /// 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) { @@ -558,7 +551,7 @@ pub(super) fn type_from_id( .. } => { debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::()); - 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::()); - 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::()); 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::()); 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::()), mem::align_of::(), ), - _ => 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"); diff --git a/vulkano/src/command_buffer/commands/pipeline.rs b/vulkano/src/command_buffer/commands/pipeline.rs index e9bffb4d..67a8f83c 100644 --- a/vulkano/src/command_buffer/commands/pipeline.rs +++ b/vulkano/src/command_buffer/commands/pipeline.rs @@ -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, + descriptor_binding_requirements: impl IntoIterator< + Item = ((u32, u32), &'a DescriptorBindingRequirements), + >, ) -> Result<(), PipelineExecutionError> { fn validate_resources( set_num: u32, binding_num: u32, - reqs: &DescriptorRequirements, + binding_reqs: &DescriptorBindingRequirements, elements: &[Option], 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| Ok(()); let check_buffer_view = |index: u32, buffer_view: &Arc| { - 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| { - // 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| { - // 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| { 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, + 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, - Range, - 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, Range)| { + 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, 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(_) => (), diff --git a/vulkano/src/descriptor_set/layout.rs b/vulkano/src/descriptor_set/layout.rs index 8bbfe3a5..369a4d6a 100644 --- a/vulkano/src/descriptor_set/layout.rs +++ b/vulkano/src/descriptor_set/layout.rs @@ -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, + descriptor_requirements: impl IntoIterator< + Item = ((u32, u32), &'a DescriptorBindingRequirements), + >, ) -> Vec { let mut create_infos: Vec = 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), diff --git a/vulkano/src/image/sys.rs b/vulkano/src/image/sys.rs index 64a9128a..16fc84aa 100644 --- a/vulkano/src/image/sys.rs +++ b/vulkano/src/image/sys.rs @@ -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, @@ -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, diff --git a/vulkano/src/pipeline/compute.rs b/vulkano/src/pipeline/compute.rs index 39dba97c..0fa25243 100644 --- a/vulkano/src/pipeline/compute.rs +++ b/vulkano/src/pipeline/compute.rs @@ -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, id: NonZeroU64, layout: Arc, - 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 { - self.descriptor_requirements + ) -> impl ExactSizeIterator { + self.descriptor_binding_requirements .iter() .map(|(loc, reqs)| (*loc, reqs)) } diff --git a/vulkano/src/pipeline/graphics/builder.rs b/vulkano/src/pipeline/graphics/builder.rs index c628c1f0..c2651c0a 100644 --- a/vulkano/src/pipeline/graphics/builder.rs +++ b/vulkano/src/pipeline/graphics/builder.rs @@ -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, HashMap, ), @@ -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 = 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 diff --git a/vulkano/src/pipeline/graphics/mod.rs b/vulkano/src/pipeline/graphics/mod.rs index 1b9b8a0e..cda82883 100644 --- a/vulkano/src/pipeline/graphics/mod.rs +++ b/vulkano/src/pipeline/graphics/mod.rs @@ -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, - 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 { - self.descriptor_requirements + ) -> impl ExactSizeIterator { + self.descriptor_binding_requirements .iter() .map(|(loc, reqs)| (*loc, reqs)) } diff --git a/vulkano/src/pipeline/layout.rs b/vulkano/src/pipeline/layout.rs index 4ed761be..c915965f 100644 --- a/vulkano/src/pipeline/layout.rs +++ b/vulkano/src/pipeline/layout.rs @@ -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, + 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() { diff --git a/vulkano/src/shader/mod.rs b/vulkano/src/shader/mod.rs index eada678f..730cf777 100644 --- a/vulkano/src/shader/mod.rs +++ b/vulkano/src/shader/mod.rs @@ -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, pub specialization_constant_requirements: HashMap, 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 { + ) -> impl ExactSizeIterator { 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, @@ -560,51 +561,60 @@ pub struct DescriptorRequirements { /// access every array element provided in the descriptor set. pub descriptor_count: Option, - /// 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, - /// 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, - /// 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, - /// For sampler bindings, the descriptor indices that require a depth comparison sampler. - pub sampler_compare: HashSet, - - /// 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, - - /// 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, - - /// For sampler bindings, the sampled image descriptors that are used in combination with each - /// sampler descriptor index. - pub sampler_with_images: HashMap>, - - /// 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, + /// 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, DescriptorRequirements>, +} - /// For storage images and storage texel buffers, the descriptor indices that perform read - /// operations on the bound resource. - pub storage_read: HashSet, +/// 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, + /// 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, + + /// 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 { - 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",) } } diff --git a/vulkano/src/shader/reflect.rs b/vulkano/src/shader/reflect.rs index 21623bb6..fc1a8ceb 100644 --- a/vulkano/src/shader/reflect.rs +++ b/vulkano/src/shader/reflect.rs @@ -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, + descriptor_binding: HashMap, } // 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,449 +210,471 @@ fn interface_variables(spirv: &Spirv) -> InterfaceVariables { } fn inspect_entry_point( - global: &HashMap, + global: &HashMap, spirv: &Spirv, + stage: ShaderStage, entry_point: Id, -) -> HashMap<(u32, u32), DescriptorRequirements> { - fn instruction_chain<'a, const N: usize>( - result: &'a mut HashMap, - global: &HashMap, - spirv: &Spirv, - chain: [fn(&Spirv, Id) -> Option; N], - id: Id, - ) -> Option<(&'a mut DescriptorVariable, Option)> { - let id = chain.into_iter().try_fold(id, |id, func| func(spirv, id))?; +) -> HashMap<(u32, u32), DescriptorBindingRequirements> { + struct Context<'a> { + global: &'a HashMap, + spirv: &'a Spirv, + stage: ShaderStage, + inspected_functions: HashSet, + result: HashMap, + } - if let Some(variable) = global.get(&id) { - // Variable was accessed without an access chain, return with index 0. - let variable = result.entry(id).or_insert_with(|| variable.clone()); - return Some((variable, Some(0))); - } + impl<'a> Context<'a> { + fn instruction_chain( + &mut self, + chain: [fn(&Spirv, Id) -> Option; N], + id: Id, + ) -> Option<(&mut DescriptorBindingVariable, Option)> { + let id = chain + .into_iter() + .try_fold(id, |id, func| func(self.spirv, id))?; - let (id, indexes) = match *spirv.id(id).instruction() { - Instruction::AccessChain { - base, ref indexes, .. - } => (base, indexes), - _ => return None, - }; + if let Some(variable) = self.global.get(&id) { + // Variable was accessed without an access chain, return with index 0. + let variable = self.result.entry(id).or_insert_with(|| variable.clone()); + variable.reqs.stages = self.stage.into(); + return Some((variable, Some(0))); + } - if let Some(variable) = 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() { - Instruction::Constant { ref value, .. } => Some(value[0]), - _ => None, + let (id, indexes) = match *self.spirv.id(id).instruction() { + Instruction::AccessChain { + base, ref indexes, .. + } => (base, indexes), + _ => return None, }; - let variable = result.entry(id).or_insert_with(|| variable.clone()); - return Some((variable, index)); + + 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 *self.spirv.id(*indexes.first().unwrap()).instruction() { + Instruction::Constant { ref value, .. } => Some(value[0]), + _ => None, + }; + let variable = self.result.entry(id).or_insert_with(|| variable.clone()); + variable.reqs.stages = self.stage.into(); + return Some((variable, index)); + } + + None } - None - } + fn inspect_entry_point_r(&mut self, function: Id) { + fn desc_reqs( + descriptor_variable: Option<(&mut DescriptorBindingVariable, Option)>, + ) -> 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 { - match *spirv.id(id).instruction() { - Instruction::ImageTexelPointer { image, .. } => Some(image), - _ => None, - } - } - - fn inst_load(spirv: &Spirv, id: Id) -> Option { - match *spirv.id(id).instruction() { - Instruction::Load { pointer, .. } => Some(pointer), - _ => None, - } - } - - fn inst_sampled_image(spirv: &Spirv, id: Id) -> Option { - match *spirv.id(id).instruction() { - Instruction::SampledImage { sampler, .. } => Some(sampler), - _ => Some(id), - } - } - - fn inspect_entry_point_r( - result: &mut HashMap, - inspected_functions: &mut HashSet, - global: &HashMap, - spirv: &Spirv, - function: Id, - ) { - inspected_functions.insert(function); - let mut in_function = false; - for instruction in spirv.instructions() { - if !in_function { - match *instruction { - Instruction::Function { result_id, .. } if result_id == function => { - in_function = true; - } - _ => {} + fn inst_image_texel_pointer(spirv: &Spirv, id: Id) -> Option { + match *spirv.id(id).instruction() { + Instruction::ImageTexelPointer { image, .. } => Some(image), + _ => None, } - } else { - match *instruction { - Instruction::AtomicLoad { pointer, .. } => { - // Storage buffer - instruction_chain(result, global, spirv, [], pointer); + } - // Storage image - if let Some((variable, Some(index))) = instruction_chain( - result, - global, - spirv, - [inst_image_texel_pointer], - pointer, - ) { - variable.reqs.storage_image_atomic.insert(index); + fn inst_load(spirv: &Spirv, id: Id) -> Option { + match *spirv.id(id).instruction() { + Instruction::Load { pointer, .. } => Some(pointer), + _ => None, + } + } + + fn inst_sampled_image(spirv: &Spirv, id: Id) -> Option { + match *spirv.id(id).instruction() { + Instruction::SampledImage { sampler, .. } => Some(sampler), + _ => Some(id), + } + } + + self.inspected_functions.insert(function); + let mut in_function = false; + + for instruction in self.spirv.instructions() { + if !in_function { + match *instruction { + Instruction::Function { result_id, .. } if result_id == function => { + in_function = true; } + _ => {} } + } else { + let stage = self.stage; - Instruction::AtomicStore { pointer, .. } - | Instruction::AtomicExchange { pointer, .. } - | Instruction::AtomicCompareExchange { pointer, .. } - | Instruction::AtomicCompareExchangeWeak { pointer, .. } - | Instruction::AtomicIIncrement { pointer, .. } - | Instruction::AtomicIDecrement { pointer, .. } - | Instruction::AtomicIAdd { pointer, .. } - | Instruction::AtomicISub { pointer, .. } - | Instruction::AtomicSMin { pointer, .. } - | Instruction::AtomicUMin { pointer, .. } - | Instruction::AtomicSMax { pointer, .. } - | Instruction::AtomicUMax { pointer, .. } - | Instruction::AtomicAnd { pointer, .. } - | Instruction::AtomicOr { pointer, .. } - | Instruction::AtomicXor { pointer, .. } - | Instruction::AtomicFlagTestAndSet { pointer, .. } - | Instruction::AtomicFlagClear { pointer, .. } - | Instruction::AtomicFMinEXT { pointer, .. } - | Instruction::AtomicFMaxEXT { pointer, .. } - | Instruction::AtomicFAddEXT { pointer, .. } => { - // Storage buffer - if let Some((variable, Some(index))) = - instruction_chain(result, global, spirv, [], pointer) - { - variable.reqs.storage_write.insert(index); + match *instruction { + Instruction::AtomicLoad { pointer, .. } => { + // Storage buffer + if let Some(desc_reqs) = desc_reqs(self.instruction_chain([], pointer)) + { + desc_reqs.memory_read = stage.into(); + } + + // Storage image + if let Some(desc_reqs) = desc_reqs( + self.instruction_chain([inst_image_texel_pointer], pointer), + ) { + desc_reqs.memory_read = stage.into(); + desc_reqs.storage_image_atomic = true; + } } - // Storage image - if let Some((variable, Some(index))) = instruction_chain( - result, - global, - spirv, - [inst_image_texel_pointer], - pointer, - ) { - variable.reqs.storage_write.insert(index); - variable.reqs.storage_image_atomic.insert(index); - } - } + Instruction::AtomicStore { pointer, .. } => { + // Storage buffer + if let Some(desc_reqs) = desc_reqs(self.instruction_chain([], pointer)) + { + desc_reqs.memory_write = stage.into(); + } - Instruction::CopyMemory { target, source, .. } => { - instruction_chain(result, global, spirv, [], target); - instruction_chain(result, global, spirv, [], source); - } - - Instruction::CopyObject { operand, .. } => { - instruction_chain(result, global, spirv, [], 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); - } - } - - Instruction::FunctionCall { - function, - ref arguments, - .. - } => { - // 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); + // 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; + } } - if !inspected_functions.contains(&function) { - inspect_entry_point_r( - result, - inspected_functions, - global, - spirv, - function, - ); + Instruction::AtomicExchange { pointer, .. } + | Instruction::AtomicCompareExchange { pointer, .. } + | Instruction::AtomicCompareExchangeWeak { pointer, .. } + | Instruction::AtomicIIncrement { pointer, .. } + | Instruction::AtomicIDecrement { pointer, .. } + | Instruction::AtomicIAdd { pointer, .. } + | Instruction::AtomicISub { pointer, .. } + | Instruction::AtomicSMin { pointer, .. } + | Instruction::AtomicUMin { pointer, .. } + | Instruction::AtomicSMax { pointer, .. } + | Instruction::AtomicUMax { pointer, .. } + | Instruction::AtomicAnd { pointer, .. } + | Instruction::AtomicOr { pointer, .. } + | Instruction::AtomicXor { pointer, .. } + | Instruction::AtomicFlagTestAndSet { pointer, .. } + | Instruction::AtomicFlagClear { pointer, .. } + | Instruction::AtomicFMinEXT { pointer, .. } + | Instruction::AtomicFMaxEXT { pointer, .. } + | Instruction::AtomicFAddEXT { pointer, .. } => { + // Storage buffer + if let Some(desc_reqs) = desc_reqs(self.instruction_chain([], pointer)) + { + desc_reqs.memory_read = stage.into(); + 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_read = stage.into(); + desc_reqs.memory_write = stage.into(); + desc_reqs.storage_image_atomic = true; + } } - } - Instruction::FunctionEnd => return, + Instruction::CopyMemory { target, source, .. } => { + self.instruction_chain([], target); + self.instruction_chain([], source); + } - Instruction::ImageGather { - sampled_image, - image_operands, - .. - } - | Instruction::ImageSparseGather { - sampled_image, - image_operands, - .. - } => { - if let Some((variable, Some(index))) = instruction_chain( - result, - global, - spirv, - [inst_sampled_image, inst_load], + Instruction::CopyObject { 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 { + self.instruction_chain([], operand); + } + } + + Instruction::FunctionCall { + function, + ref arguments, + .. + } => { + // Rather than trying to figure out the type of each argument, we just + // try all of them as pointers. + for &argument in arguments { + self.instruction_chain([], argument); + } + + if !self.inspected_functions.contains(&function) { + self.inspect_entry_point_r(function); + } + } + + Instruction::FunctionEnd => return, + + Instruction::ImageGather { sampled_image, - ) { - variable.reqs.sampler_no_ycbcr_conversion.insert(index); + image_operands, + .. + } + | Instruction::ImageSparseGather { + sampled_image, + image_operands, + .. + } => { + if let Some(desc_reqs) = + desc_reqs(self.instruction_chain( + [inst_sampled_image, inst_load], + sampled_image, + )) + { + 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() + 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() + }) { + desc_reqs.sampler_no_unnormalized_coordinates = true; + } + } + } + + Instruction::ImageDrefGather { sampled_image, .. } + | Instruction::ImageSparseDrefGather { sampled_image, .. } => { + if let Some(desc_reqs) = + desc_reqs(self.instruction_chain( + [inst_sampled_image, inst_load], + sampled_image, + )) + { + desc_reqs.memory_read = stage.into(); + desc_reqs.sampler_no_unnormalized_coordinates = true; + desc_reqs.sampler_no_ycbcr_conversion = true; + } + } + + Instruction::ImageSampleImplicitLod { + sampled_image, + image_operands, + .. + } + | Instruction::ImageSampleProjImplicitLod { + sampled_image, + image_operands, + .. + } + | Instruction::ImageSparseSampleProjImplicitLod { + sampled_image, + image_operands, + .. + } + | Instruction::ImageSparseSampleImplicitLod { + sampled_image, + image_operands, + .. + } => { + if let Some(desc_reqs) = + desc_reqs(self.instruction_chain( + [inst_sampled_image, inst_load], + sampled_image, + )) + { + 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() + }) { + desc_reqs.sampler_no_ycbcr_conversion = true; + } + } + } + + Instruction::ImageSampleProjExplicitLod { + sampled_image, + image_operands, + .. + } + | Instruction::ImageSparseSampleProjExplicitLod { + sampled_image, + image_operands, + .. + } => { + if let Some(desc_reqs) = + desc_reqs(self.instruction_chain( + [inst_sampled_image, inst_load], + sampled_image, + )) + { + 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() + { + desc_reqs.sampler_no_ycbcr_conversion = true; + } + } + } + + Instruction::ImageSampleDrefImplicitLod { + sampled_image, + image_operands, + .. + } + | Instruction::ImageSampleProjDrefImplicitLod { + sampled_image, + image_operands, + .. + } + | Instruction::ImageSparseSampleDrefImplicitLod { + sampled_image, + image_operands, + .. + } + | Instruction::ImageSparseSampleProjDrefImplicitLod { + sampled_image, + image_operands, + .. + } => { + if let Some(desc_reqs) = + desc_reqs(self.instruction_chain( + [inst_sampled_image, inst_load], + sampled_image, + )) + { + 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() + }) { + desc_reqs.sampler_no_ycbcr_conversion = true; + } + } + } + + Instruction::ImageSampleDrefExplicitLod { + sampled_image, + image_operands, + .. + } + | Instruction::ImageSampleProjDrefExplicitLod { + sampled_image, + image_operands, + .. + } + | Instruction::ImageSparseSampleDrefExplicitLod { + sampled_image, + image_operands, + .. + } + | Instruction::ImageSparseSampleProjDrefExplicitLod { + sampled_image, + image_operands, + .. + } => { + if let Some(desc_reqs) = + desc_reqs(self.instruction_chain( + [inst_sampled_image, inst_load], + sampled_image, + )) + { + 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() + { + desc_reqs.sampler_no_ycbcr_conversion = true; + } + } + } + + Instruction::ImageSampleExplicitLod { + sampled_image, + image_operands, + .. + } + | Instruction::ImageSparseSampleExplicitLod { + sampled_image, + image_operands, + .. + } => { + 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; + } - Instruction::ImageDrefGather { sampled_image, .. } - | Instruction::ImageSparseDrefGather { sampled_image, .. } => { - if let Some((variable, Some(index))) = instruction_chain( - result, - global, - spirv, - [inst_sampled_image, inst_load], - sampled_image, - ) { - variable - .reqs - .sampler_no_unnormalized_coordinates - .insert(index); - variable.reqs.sampler_no_ycbcr_conversion.insert(index); - } - } - - Instruction::ImageSampleImplicitLod { - sampled_image, - image_operands, - .. - } - | Instruction::ImageSampleProjImplicitLod { - sampled_image, - image_operands, - .. - } - | Instruction::ImageSparseSampleProjImplicitLod { - sampled_image, - image_operands, - .. - } - | Instruction::ImageSparseSampleImplicitLod { - sampled_image, - image_operands, - .. - } => { - if let Some((variable, Some(index))) = instruction_chain( - result, - global, - spirv, - [inst_sampled_image, inst_load], - sampled_image, - ) { - variable - .reqs - .sampler_no_unnormalized_coordinates - .insert(index); - - if image_operands.as_ref().map_or(false, |image_operands| { - image_operands.const_offset.is_some() + 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::ImageSampleProjExplicitLod { - sampled_image, - image_operands, - .. - } - | Instruction::ImageSparseSampleProjExplicitLod { - sampled_image, - image_operands, - .. - } => { - if let Some((variable, Some(index))) = instruction_chain( - result, - global, - spirv, - [inst_sampled_image, inst_load], - sampled_image, - ) { - variable - .reqs - .sampler_no_unnormalized_coordinates - .insert(index); + Instruction::ImageTexelPointer { image, .. } => { + self.instruction_chain([], image); + } - if image_operands.const_offset.is_some() - || image_operands.offset.is_some() + Instruction::ImageRead { image, .. } => { + if let Some(desc_reqs) = + desc_reqs(self.instruction_chain([inst_load], image)) { - variable.reqs.sampler_no_ycbcr_conversion.insert(index); + desc_reqs.memory_read = stage.into(); } } - } - Instruction::ImageSampleDrefImplicitLod { - sampled_image, - image_operands, - .. - } - | Instruction::ImageSampleProjDrefImplicitLod { - sampled_image, - image_operands, - .. - } - | Instruction::ImageSparseSampleDrefImplicitLod { - sampled_image, - image_operands, - .. - } - | Instruction::ImageSparseSampleProjDrefImplicitLod { - sampled_image, - image_operands, - .. - } => { - if let Some((variable, Some(index))) = instruction_chain( - result, - global, - spirv, - [inst_sampled_image, inst_load], - sampled_image, - ) { - variable - .reqs - .sampler_no_unnormalized_coordinates - .insert(index); - variable.reqs.sampler_compare.insert(index); - - 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); - } - } - } - - Instruction::ImageSampleDrefExplicitLod { - sampled_image, - image_operands, - .. - } - | Instruction::ImageSampleProjDrefExplicitLod { - sampled_image, - image_operands, - .. - } - | Instruction::ImageSparseSampleDrefExplicitLod { - sampled_image, - image_operands, - .. - } - | Instruction::ImageSparseSampleProjDrefExplicitLod { - sampled_image, - image_operands, - .. - } => { - if let Some((variable, Some(index))) = instruction_chain( - result, - global, - spirv, - [inst_sampled_image, inst_load], - sampled_image, - ) { - variable - .reqs - .sampler_no_unnormalized_coordinates - .insert(index); - variable.reqs.sampler_compare.insert(index); - - if image_operands.const_offset.is_some() - || image_operands.offset.is_some() + Instruction::ImageWrite { image, .. } => { + if let Some(desc_reqs) = + desc_reqs(self.instruction_chain([inst_load], image)) { - variable.reqs.sampler_no_ycbcr_conversion.insert(index); + desc_reqs.memory_write = stage.into(); } } - } - Instruction::ImageSampleExplicitLod { - sampled_image, - image_operands, - .. - } - | Instruction::ImageSparseSampleExplicitLod { - sampled_image, - image_operands, - .. - } => { - if let Some((variable, Some(index))) = instruction_chain( - result, - global, - spirv, - [inst_sampled_image, inst_load], - sampled_image, - ) { - if image_operands.bias.is_some() - || image_operands.const_offset.is_some() - || image_operands.offset.is_some() + Instruction::Load { pointer, .. } => { + if let Some((binding_variable, index)) = + self.instruction_chain([], pointer) { - variable - .reqs - .sampler_no_unnormalized_coordinates - .insert(index); - } - - if image_operands.const_offset.is_some() - || image_operands.offset.is_some() - { - variable.reqs.sampler_no_ycbcr_conversion.insert(index); + // 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::ImageTexelPointer { image, .. } => { - instruction_chain(result, global, spirv, [], image); - } - - Instruction::ImageRead { image, .. } => { - if let Some((variable, Some(index))) = - instruction_chain(result, global, spirv, [inst_load], image) - { - variable.reqs.storage_read.insert(index); - } - } - - Instruction::ImageWrite { image, .. } => { - if let Some((variable, Some(index))) = - instruction_chain(result, global, spirv, [inst_load], image) - { - variable.reqs.storage_write.insert(index); - } - } - - Instruction::Load { pointer, .. } => { - instruction_chain(result, global, spirv, [], pointer); - } - - Instruction::SampledImage { image, sampler, .. } => { - let identifier = - match instruction_chain(result, global, spirv, [inst_load], image) { + Instruction::SampledImage { image, sampler, .. } => { + let identifier = match self.instruction_chain([inst_load], image) { Some((variable, Some(index))) => DescriptorIdentifier { set: variable.set, binding: variable.binding, @@ -660,55 +683,51 @@ fn inspect_entry_point( _ => continue, }; - if let Some((variable, Some(index))) = - instruction_chain(result, global, spirv, [inst_load], sampler) - { - variable - .reqs - .sampler_with_images - .entry(index) - .or_default() - .insert(identifier); + if let Some(desc_reqs) = + desc_reqs(self.instruction_chain([inst_load], sampler)) + { + desc_reqs.sampler_with_images.insert(identifier); + } } - } - Instruction::Store { pointer, .. } => { - if let Some((variable, Some(index))) = - instruction_chain(result, global, spirv, [], pointer) - { - variable.reqs.storage_write.insert(index); + Instruction::Store { pointer, .. } => { + // This can only apply to buffers, right? + if let Some(desc_reqs) = desc_reqs(self.instruction_chain([], pointer)) + { + desc_reqs.memory_write = stage.into(); + } } - } - _ => (), + _ => (), + } } } } } - 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 {