From 96f0201d31ef86dcad0ef3f7dd5873cdfe767466 Mon Sep 17 00:00:00 2001 From: Rua Date: Sat, 3 Jun 2023 09:46:01 +0200 Subject: [PATCH 1/5] Add support for descriptor set copies and other things --- examples/src/bin/async-update.rs | 2 + examples/src/bin/basic-compute-shader.rs | 1 + .../deferred/frame/ambient_lighting_system.rs | 1 + .../frame/directional_lighting_system.rs | 1 + .../deferred/frame/point_lighting_system.rs | 1 + examples/src/bin/dynamic-buffers.rs | 1 + examples/src/bin/dynamic-local-size.rs | 1 + examples/src/bin/gl-interop.rs | 1 + examples/src/bin/image-self-copy-blit/main.rs | 1 + examples/src/bin/image/main.rs | 1 + examples/src/bin/immutable-sampler/main.rs | 1 + examples/src/bin/indirect.rs | 1 + .../fractal_compute_pipeline.rs | 1 + .../pixels_draw_pipeline.rs | 1 + .../multi_window_game_of_life/game_of_life.rs | 1 + .../multi_window_game_of_life/pixels_draw.rs | 1 + examples/src/bin/push-constants.rs | 1 + examples/src/bin/push-descriptors/main.rs | 4 +- examples/src/bin/runtime_array/main.rs | 6 +- examples/src/bin/self-copy-buffer.rs | 1 + examples/src/bin/shader-include/main.rs | 1 + examples/src/bin/shader-types-sharing.rs | 1 + examples/src/bin/simple-particles.rs | 1 + examples/src/bin/specialization-constants.rs | 1 + examples/src/bin/teapot/main.rs | 1 + examples/src/bin/texture_array/main.rs | 1 + vulkano/src/command_buffer/auto/mod.rs | 2 + .../src/command_buffer/commands/bind_push.rs | 156 +- .../src/command_buffer/commands/pipeline.rs | 26 +- vulkano/src/descriptor_set/allocator.rs | 33 +- vulkano/src/descriptor_set/layout.rs | 927 ++++--- vulkano/src/descriptor_set/mod.rs | 286 ++- vulkano/src/descriptor_set/persistent.rs | 21 +- vulkano/src/descriptor_set/pool.rs | 15 +- vulkano/src/descriptor_set/sys.rs | 63 +- vulkano/src/descriptor_set/update.rs | 2253 +++++++++-------- vulkano/src/device/mod.rs | 150 ++ vulkano/src/pipeline/compute.rs | 99 +- vulkano/src/pipeline/layout.rs | 1248 ++++----- vulkano/src/sampler/mod.rs | 203 +- 40 files changed, 2757 insertions(+), 2760 deletions(-) diff --git a/examples/src/bin/async-update.rs b/examples/src/bin/async-update.rs index 38cd56568a..dfe957c255 100644 --- a/examples/src/bin/async-update.rs +++ b/examples/src/bin/async-update.rs @@ -488,6 +488,7 @@ fn main() { &descriptor_set_allocator, pipeline.layout().set_layouts()[0].clone(), [WriteDescriptorSet::buffer(0, buffer.clone())], + [], ) .unwrap() }) @@ -504,6 +505,7 @@ fn main() { ImageView::new_default(texture).unwrap(), sampler.clone(), )], + [], ) .unwrap() }); diff --git a/examples/src/bin/basic-compute-shader.rs b/examples/src/bin/basic-compute-shader.rs index 9daed69293..c73ae1e816 100644 --- a/examples/src/bin/basic-compute-shader.rs +++ b/examples/src/bin/basic-compute-shader.rs @@ -192,6 +192,7 @@ fn main() { &descriptor_set_allocator, layout.clone(), [WriteDescriptorSet::buffer(0, data_buffer.clone())], + [], ) .unwrap(); diff --git a/examples/src/bin/deferred/frame/ambient_lighting_system.rs b/examples/src/bin/deferred/frame/ambient_lighting_system.rs index 689c43ebc3..7f64b089b0 100644 --- a/examples/src/bin/deferred/frame/ambient_lighting_system.rs +++ b/examples/src/bin/deferred/frame/ambient_lighting_system.rs @@ -174,6 +174,7 @@ impl AmbientLightingSystem { &self.descriptor_set_allocator, layout.clone(), [WriteDescriptorSet::image_view(0, color_input)], + [], ) .unwrap(); diff --git a/examples/src/bin/deferred/frame/directional_lighting_system.rs b/examples/src/bin/deferred/frame/directional_lighting_system.rs index 6c8c30825c..5f145ba146 100644 --- a/examples/src/bin/deferred/frame/directional_lighting_system.rs +++ b/examples/src/bin/deferred/frame/directional_lighting_system.rs @@ -190,6 +190,7 @@ impl DirectionalLightingSystem { WriteDescriptorSet::image_view(0, color_input), WriteDescriptorSet::image_view(1, normals_input), ], + [], ) .unwrap(); diff --git a/examples/src/bin/deferred/frame/point_lighting_system.rs b/examples/src/bin/deferred/frame/point_lighting_system.rs index 9d5dec503f..4c742e9298 100644 --- a/examples/src/bin/deferred/frame/point_lighting_system.rs +++ b/examples/src/bin/deferred/frame/point_lighting_system.rs @@ -201,6 +201,7 @@ impl PointLightingSystem { WriteDescriptorSet::image_view(1, normals_input), WriteDescriptorSet::image_view(2, depth_input), ], + [], ) .unwrap(); diff --git a/examples/src/bin/dynamic-buffers.rs b/examples/src/bin/dynamic-buffers.rs index a90cedf014..8f22a4753f 100644 --- a/examples/src/bin/dynamic-buffers.rs +++ b/examples/src/bin/dynamic-buffers.rs @@ -233,6 +233,7 @@ fn main() { ), WriteDescriptorSet::buffer(1, output_buffer.clone()), ], + [], ) .unwrap(); diff --git a/examples/src/bin/dynamic-local-size.rs b/examples/src/bin/dynamic-local-size.rs index 199ac88282..d28a56da6e 100644 --- a/examples/src/bin/dynamic-local-size.rs +++ b/examples/src/bin/dynamic-local-size.rs @@ -232,6 +232,7 @@ fn main() { &descriptor_set_allocator, layout.clone(), [WriteDescriptorSet::image_view(0, view)], + [], ) .unwrap(); diff --git a/examples/src/bin/gl-interop.rs b/examples/src/bin/gl-interop.rs index 4c50569b8a..077ff33784 100644 --- a/examples/src/bin/gl-interop.rs +++ b/examples/src/bin/gl-interop.rs @@ -233,6 +233,7 @@ mod linux { [WriteDescriptorSet::image_view_sampler( 0, image_view, sampler, )], + [], ) .unwrap(); diff --git a/examples/src/bin/image-self-copy-blit/main.rs b/examples/src/bin/image-self-copy-blit/main.rs index 9e3dfcfe45..4513ecfd61 100644 --- a/examples/src/bin/image-self-copy-blit/main.rs +++ b/examples/src/bin/image-self-copy-blit/main.rs @@ -386,6 +386,7 @@ fn main() { &descriptor_set_allocator, layout.clone(), [WriteDescriptorSet::image_view_sampler(0, texture, sampler)], + [], ) .unwrap(); diff --git a/examples/src/bin/image/main.rs b/examples/src/bin/image/main.rs index dd569939ef..eea80d8c99 100644 --- a/examples/src/bin/image/main.rs +++ b/examples/src/bin/image/main.rs @@ -312,6 +312,7 @@ fn main() { &descriptor_set_allocator, layout.clone(), [WriteDescriptorSet::image_view_sampler(0, texture, sampler)], + [], ) .unwrap(); diff --git a/examples/src/bin/immutable-sampler/main.rs b/examples/src/bin/immutable-sampler/main.rs index d2b2c744f5..fec119ad43 100644 --- a/examples/src/bin/immutable-sampler/main.rs +++ b/examples/src/bin/immutable-sampler/main.rs @@ -333,6 +333,7 @@ fn main() { &descriptor_set_allocator, layout.clone(), [WriteDescriptorSet::image_view(0, texture)], + [], ) .unwrap(); diff --git a/examples/src/bin/indirect.rs b/examples/src/bin/indirect.rs index 053434566a..0b1fb8d5ba 100644 --- a/examples/src/bin/indirect.rs +++ b/examples/src/bin/indirect.rs @@ -459,6 +459,7 @@ fn main() { WriteDescriptorSet::buffer(0, vertices.clone()), WriteDescriptorSet::buffer(1, indirect_buffer.clone()), ], + [], ) .unwrap(); diff --git a/examples/src/bin/interactive_fractal/fractal_compute_pipeline.rs b/examples/src/bin/interactive_fractal/fractal_compute_pipeline.rs index 877a50d076..6281cb4679 100644 --- a/examples/src/bin/interactive_fractal/fractal_compute_pipeline.rs +++ b/examples/src/bin/interactive_fractal/fractal_compute_pipeline.rs @@ -153,6 +153,7 @@ impl FractalComputePipeline { WriteDescriptorSet::image_view(0, image), WriteDescriptorSet::buffer(1, self.palette.clone()), ], + [], ) .unwrap(); let mut builder = AutoCommandBufferBuilder::primary( diff --git a/examples/src/bin/interactive_fractal/pixels_draw_pipeline.rs b/examples/src/bin/interactive_fractal/pixels_draw_pipeline.rs index 68b2a7698c..44dc7b75f3 100644 --- a/examples/src/bin/interactive_fractal/pixels_draw_pipeline.rs +++ b/examples/src/bin/interactive_fractal/pixels_draw_pipeline.rs @@ -197,6 +197,7 @@ impl PixelsDrawPipeline { image.clone(), sampler, )], + [], ) .unwrap() } diff --git a/examples/src/bin/multi_window_game_of_life/game_of_life.rs b/examples/src/bin/multi_window_game_of_life/game_of_life.rs index 188c423475..cbc8561bdb 100644 --- a/examples/src/bin/multi_window_game_of_life/game_of_life.rs +++ b/examples/src/bin/multi_window_game_of_life/game_of_life.rs @@ -184,6 +184,7 @@ impl GameOfLifeComputePipeline { WriteDescriptorSet::buffer(1, self.life_in.clone()), WriteDescriptorSet::buffer(2, self.life_out.clone()), ], + [], ) .unwrap(); diff --git a/examples/src/bin/multi_window_game_of_life/pixels_draw.rs b/examples/src/bin/multi_window_game_of_life/pixels_draw.rs index 8dc17f7dd6..fcc2d460f7 100644 --- a/examples/src/bin/multi_window_game_of_life/pixels_draw.rs +++ b/examples/src/bin/multi_window_game_of_life/pixels_draw.rs @@ -193,6 +193,7 @@ impl PixelsDrawPipeline { image.clone(), sampler, )], + [], ) .unwrap() } diff --git a/examples/src/bin/push-constants.rs b/examples/src/bin/push-constants.rs index fad43a1b4d..7b89646b26 100644 --- a/examples/src/bin/push-constants.rs +++ b/examples/src/bin/push-constants.rs @@ -167,6 +167,7 @@ fn main() { &descriptor_set_allocator, layout.clone(), [WriteDescriptorSet::buffer(0, data_buffer.clone())], + [], ) .unwrap(); diff --git a/examples/src/bin/push-descriptors/main.rs b/examples/src/bin/push-descriptors/main.rs index 7025d99dac..e20a2265ec 100644 --- a/examples/src/bin/push-descriptors/main.rs +++ b/examples/src/bin/push-descriptors/main.rs @@ -14,7 +14,7 @@ use vulkano::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract, RenderPassBeginInfo, SubpassContents, }, - descriptor_set::WriteDescriptorSet, + descriptor_set::{layout::DescriptorSetLayoutCreateFlags, WriteDescriptorSet}, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, QueueFlags, @@ -276,7 +276,7 @@ fn main() { let mut layout_create_info = PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages); let set_layout = &mut layout_create_info.set_layouts[0]; - set_layout.push_descriptor = true; + set_layout.flags |= DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR; set_layout.bindings.get_mut(&0).unwrap().immutable_samplers = vec![sampler]; PipelineLayout::new( diff --git a/examples/src/bin/runtime_array/main.rs b/examples/src/bin/runtime_array/main.rs index f32344f43b..e992be7111 100644 --- a/examples/src/bin/runtime_array/main.rs +++ b/examples/src/bin/runtime_array/main.rs @@ -15,7 +15,8 @@ use vulkano::{ PrimaryCommandBufferAbstract, RenderPassBeginInfo, SubpassContents, }, descriptor_set::{ - allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet, + allocator::StandardDescriptorSetAllocator, layout::DescriptorBindingFlags, + PersistentDescriptorSet, WriteDescriptorSet, }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Features, @@ -374,7 +375,7 @@ fn main() { .bindings .get_mut(&0) .unwrap(); - binding.variable_descriptor_count = true; + binding.binding_flags |= DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT; binding.descriptor_count = 2; PipelineLayout::new( @@ -419,6 +420,7 @@ fn main() { (vulkano_texture as _, sampler), ], )], + [], ) .unwrap(); diff --git a/examples/src/bin/self-copy-buffer.rs b/examples/src/bin/self-copy-buffer.rs index cfdde23b86..c67259fbb6 100644 --- a/examples/src/bin/self-copy-buffer.rs +++ b/examples/src/bin/self-copy-buffer.rs @@ -160,6 +160,7 @@ fn main() { &descriptor_set_allocator, layout.clone(), [WriteDescriptorSet::buffer(0, data_buffer.clone())], + [], ) .unwrap(); diff --git a/examples/src/bin/shader-include/main.rs b/examples/src/bin/shader-include/main.rs index cb5972090f..f5764692d6 100644 --- a/examples/src/bin/shader-include/main.rs +++ b/examples/src/bin/shader-include/main.rs @@ -167,6 +167,7 @@ fn main() { &descriptor_set_allocator, layout.clone(), [WriteDescriptorSet::buffer(0, data_buffer.clone())], + [], ) .unwrap(); diff --git a/examples/src/bin/shader-types-sharing.rs b/examples/src/bin/shader-types-sharing.rs index cb19a104ca..09643c6114 100644 --- a/examples/src/bin/shader-types-sharing.rs +++ b/examples/src/bin/shader-types-sharing.rs @@ -197,6 +197,7 @@ fn main() { descriptor_set_allocator, layout.clone(), [WriteDescriptorSet::buffer(0, data_buffer)], + [], ) .unwrap(); diff --git a/examples/src/bin/simple-particles.rs b/examples/src/bin/simple-particles.rs index efff9beb3a..d0b0e1f10a 100644 --- a/examples/src/bin/simple-particles.rs +++ b/examples/src/bin/simple-particles.rs @@ -453,6 +453,7 @@ fn main() { // 0 is the binding of the data in this set. We bind the `Buffer` of vertices here. WriteDescriptorSet::buffer(0, vertex_buffer.clone()), ], + [], ) .unwrap(); diff --git a/examples/src/bin/specialization-constants.rs b/examples/src/bin/specialization-constants.rs index 0ff319ebd4..555f891ac1 100644 --- a/examples/src/bin/specialization-constants.rs +++ b/examples/src/bin/specialization-constants.rs @@ -167,6 +167,7 @@ fn main() { &descriptor_set_allocator, layout.clone(), [WriteDescriptorSet::buffer(0, data_buffer.clone())], + [], ) .unwrap(); diff --git a/examples/src/bin/teapot/main.rs b/examples/src/bin/teapot/main.rs index cb1ccc38b0..0e5ebca667 100644 --- a/examples/src/bin/teapot/main.rs +++ b/examples/src/bin/teapot/main.rs @@ -346,6 +346,7 @@ fn main() { &descriptor_set_allocator, layout.clone(), [WriteDescriptorSet::buffer(0, uniform_buffer_subbuffer)], + [], ) .unwrap(); diff --git a/examples/src/bin/texture_array/main.rs b/examples/src/bin/texture_array/main.rs index 38ce71fa17..59f922413b 100644 --- a/examples/src/bin/texture_array/main.rs +++ b/examples/src/bin/texture_array/main.rs @@ -317,6 +317,7 @@ fn main() { &descriptor_set_allocator, layout.clone(), [WriteDescriptorSet::image_view_sampler(0, texture, sampler)], + [], ) .unwrap(); diff --git a/vulkano/src/command_buffer/auto/mod.rs b/vulkano/src/command_buffer/auto/mod.rs index 6fbeef35fc..44982d10ac 100644 --- a/vulkano/src/command_buffer/auto/mod.rs +++ b/vulkano/src/command_buffer/auto/mod.rs @@ -779,6 +779,7 @@ mod tests { Sampler::new(device.clone(), SamplerCreateInfo::simple_repeat_linear()) .unwrap(), )], + [], ) .unwrap(); @@ -848,6 +849,7 @@ mod tests { 0, Sampler::new(device, SamplerCreateInfo::simple_repeat_linear()).unwrap(), )], + [], ) .unwrap(); diff --git a/vulkano/src/command_buffer/commands/bind_push.rs b/vulkano/src/command_buffer/commands/bind_push.rs index 9b942dd7fb..06a7f54054 100644 --- a/vulkano/src/command_buffer/commands/bind_push.rs +++ b/vulkano/src/command_buffer/commands/bind_push.rs @@ -16,10 +16,10 @@ use crate::{ AutoCommandBufferBuilder, }, descriptor_set::{ - layout::DescriptorType, set_descriptor_write_image_layouts, validate_descriptor_write, + layout::{DescriptorBindingFlags, DescriptorSetLayoutCreateFlags, DescriptorType}, DescriptorBindingResources, DescriptorBufferInfo, DescriptorSetResources, - DescriptorSetUpdateError, DescriptorSetWithOffsets, DescriptorSetsCollection, - DescriptorWriteInfo, WriteDescriptorSet, + DescriptorSetWithOffsets, DescriptorSetsCollection, DescriptorWriteInfo, + WriteDescriptorSet, }, device::{DeviceOwned, QueueFlags}, memory::{is_aligned, DeviceAlignment}, @@ -27,7 +27,7 @@ use crate::{ graphics::{subpass::PipelineSubpassType, vertex_input::VertexBuffersCollection}, ComputePipeline, GraphicsPipeline, PipelineBindPoint, PipelineLayout, }, - DeviceSize, RequirementNotMet, RequiresOneOf, VulkanObject, + DeviceSize, RequirementNotMet, RequiresOneOf, ValidationError, VulkanObject, }; use smallvec::SmallVec; use std::{ @@ -152,7 +152,10 @@ where _ => continue, }; - let count = if binding.variable_descriptor_count { + let count = if binding + .binding_flags + .intersects(DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT) + { set.variable_descriptor_count() } else { binding.descriptor_count @@ -740,15 +743,8 @@ where pipeline_bind_point: PipelineBindPoint, pipeline_layout: Arc, set_num: u32, - mut descriptor_writes: SmallVec<[WriteDescriptorSet; 8]>, + descriptor_writes: SmallVec<[WriteDescriptorSet; 8]>, ) -> &mut Self { - // Set the image layouts - if let Some(set_layout) = pipeline_layout.set_layouts().get(set_num as usize) { - for write in &mut descriptor_writes { - set_descriptor_write_image_layouts(write, set_layout); - } - } - self.validate_push_descriptor_set( pipeline_bind_point, &pipeline_layout, @@ -775,31 +771,44 @@ where pipeline_layout: &PipelineLayout, set_num: u32, descriptor_writes: &[WriteDescriptorSet], - ) -> Result<(), BindPushError> { + ) -> Result<(), ValidationError> { if !self.device().enabled_extensions().khr_push_descriptor { - return Err(BindPushError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::push_descriptor_set`", - requires_one_of: RequiresOneOf { + return Err(ValidationError { + requires_one_of: Some(RequiresOneOf { device_extensions: &["khr_push_descriptor"], ..Default::default() - }, + }), + ..Default::default() }); } - // VUID-vkCmdPushDescriptorSetKHR-pipelineBindPoint-parameter - pipeline_bind_point.validate_device(self.device())?; + pipeline_bind_point + .validate_device(self.device()) + .map_err(|err| ValidationError { + context: "pipeline_bind_point".into(), + vuids: &["VUID-vkCmdPushDescriptorSetKHR-pipelineBindPoint-parameter"], + ..ValidationError::from_requirement(err) + })?; let queue_family_properties = self.queue_family_properties(); - // VUID-vkCmdPushDescriptorSetKHR-commandBuffer-cmdpool - // VUID-vkCmdPushDescriptorSetKHR-pipelineBindPoint-00363 match pipeline_bind_point { PipelineBindPoint::Compute => { if !queue_family_properties .queue_flags .intersects(QueueFlags::COMPUTE) { - return Err(BindPushError::NotSupportedByQueueFamily); + return Err(ValidationError { + context: "self".into(), + problem: "pipeline_bind_point is PipelineBindPoint::Compute, and the \ + queue family does not support compute operations" + .into(), + vuids: &[ + "VUID-vkCmdPushDescriptorSetKHR-pipelineBindPoint-00363", + "VUID-vkCmdPushDescriptorSetKHR-commandBuffer-cmdpool", + ], + ..Default::default() + }); } } PipelineBindPoint::Graphics => { @@ -807,7 +816,17 @@ where .queue_flags .intersects(QueueFlags::GRAPHICS) { - return Err(BindPushError::NotSupportedByQueueFamily); + return Err(ValidationError { + context: "self".into(), + problem: "pipeline_bind_point is PipelineBindPoint::Graphics, and the \ + queue family does not support graphics operations" + .into(), + vuids: &[ + "VUID-vkCmdPushDescriptorSetKHR-pipelineBindPoint-00363", + "VUID-vkCmdPushDescriptorSetKHR-commandBuffer-cmdpool", + ], + ..Default::default() + }); } } } @@ -815,23 +834,36 @@ where // VUID-vkCmdPushDescriptorSetKHR-commonparent assert_eq!(self.device(), pipeline_layout.device()); - // VUID-vkCmdPushDescriptorSetKHR-set-00364 if set_num as usize > pipeline_layout.set_layouts().len() { - return Err(BindPushError::DescriptorSetOutOfRange { - set_num, - pipeline_layout_set_count: pipeline_layout.set_layouts().len() as u32, + return Err(ValidationError { + problem: "set_num is greater than the number of descriptor set layouts in \ + pipeline_layout" + .into(), + vuids: &["VUID-vkCmdPushDescriptorSetKHR-set-00364"], + ..Default::default() }); } let descriptor_set_layout = &pipeline_layout.set_layouts()[set_num as usize]; - // VUID-vkCmdPushDescriptorSetKHR-set-00365 - if !descriptor_set_layout.push_descriptor() { - return Err(BindPushError::DescriptorSetNotPush { set_num }); + if !descriptor_set_layout + .flags() + .intersects(DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR) + { + return Err(ValidationError { + problem: "the descriptor set layout with the number set_num in pipeline_layout \ + was not created with the DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR \ + flag" + .into(), + vuids: &["VUID-vkCmdPushDescriptorSetKHR-set-00365"], + ..Default::default() + }); } - for write in descriptor_writes { - validate_descriptor_write(write, descriptor_set_layout, 0)?; + for (index, write) in descriptor_writes.iter().enumerate() { + write + .validate(descriptor_set_layout, 0) + .map_err(|err| err.add_context(format!("descriptor_writes[{}]", index)))?; } Ok(()) @@ -843,15 +875,8 @@ where pipeline_bind_point: PipelineBindPoint, pipeline_layout: Arc, set_num: u32, - mut descriptor_writes: SmallVec<[WriteDescriptorSet; 8]>, + descriptor_writes: SmallVec<[WriteDescriptorSet; 8]>, ) -> &mut Self { - // Set the image layouts - if let Some(set_layout) = pipeline_layout.set_layouts().get(set_num as usize) { - for write in &mut descriptor_writes { - set_descriptor_write_image_layouts(write, set_layout); - } - } - let state = self.builder_state.invalidate_descriptor_sets( pipeline_bind_point, pipeline_layout.clone(), @@ -859,7 +884,9 @@ where 1, ); let layout = state.pipeline_layout.set_layouts()[set_num as usize].as_ref(); - debug_assert!(layout.push_descriptor()); + debug_assert!(layout + .flags() + .intersects(DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR)); let set_resources = match state .descriptor_sets @@ -871,7 +898,7 @@ where }; for write in &descriptor_writes { - set_resources.update(write); + set_resources.write(write, layout); } self.add_command( @@ -1158,8 +1185,6 @@ where #[derive(Clone, Debug)] pub(in super::super) enum BindPushError { - DescriptorSetUpdateError(DescriptorSetUpdateError), - RequirementNotMet { required_for: &'static str, requires_one_of: RequiresOneOf, @@ -1167,15 +1192,7 @@ pub(in super::super) enum BindPushError { /// The element of `descriptor_sets` being bound to a slot is not compatible with the /// corresponding slot in `pipeline_layout`. - DescriptorSetNotCompatible { - set_num: u32, - }, - - /// The descriptor set number being pushed is not defined for push descriptor sets in the - /// pipeline layout. - DescriptorSetNotPush { - set_num: u32, - }, + DescriptorSetNotCompatible { set_num: u32 }, /// The highest descriptor set slot being bound is greater than the number of sets in /// `pipeline_layout`. @@ -1221,10 +1238,7 @@ pub(in super::super) enum BindPushError { IndexBufferMissingUsage, /// The `max_vertex_input_bindings` limit has been exceeded. - MaxVertexInputBindingsExceeded { - _binding_count: u32, - _max: u32, - }, + MaxVertexInputBindingsExceeded { _binding_count: u32, _max: u32 }, /// The queue family doesn't allow this operation. NotSupportedByQueueFamily, @@ -1243,9 +1257,7 @@ pub(in super::super) enum BindPushError { /// The push constants data to be written at an offset is not included in any push constant /// range of the pipeline layout. - PushConstantsDataOutOfRange { - offset: u32, - }, + PushConstantsDataOutOfRange { offset: u32 }, /// The push constants offset is not a multiple of 4. PushConstantsOffsetNotAligned, @@ -1257,14 +1269,7 @@ pub(in super::super) enum BindPushError { VertexBufferMissingUsage, } -impl error::Error for BindPushError { - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match self { - BindPushError::DescriptorSetUpdateError(err) => Some(err), - _ => None, - } - } -} +impl error::Error for BindPushError {} impl Display for BindPushError { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { @@ -1277,19 +1282,12 @@ impl Display for BindPushError { "a requirement was not met for: {}; requires one of: {}", required_for, requires_one_of, ), - Self::DescriptorSetUpdateError(_) => write!(f, "a DescriptorSetUpdateError"), Self::DescriptorSetNotCompatible { set_num } => write!( f, "the element of `descriptor_sets` being bound to slot {} is not compatible with \ the corresponding slot in `pipeline_layout`", set_num, ), - Self::DescriptorSetNotPush { set_num } => write!( - f, - "the descriptor set number being pushed ({}) is not defined for push descriptor \ - sets in the pipeline layout", - set_num, - ), Self::DescriptorSetOutOfRange { set_num, pipeline_layout_set_count, @@ -1381,12 +1379,6 @@ impl Display for BindPushError { } } -impl From for BindPushError { - fn from(err: DescriptorSetUpdateError) -> Self { - Self::DescriptorSetUpdateError(err) - } -} - impl From for BindPushError { fn from(err: RequirementNotMet) -> Self { Self::RequirementNotMet { diff --git a/vulkano/src/command_buffer/commands/pipeline.rs b/vulkano/src/command_buffer/commands/pipeline.rs index c6270ed3b4..ef9d3edce0 100644 --- a/vulkano/src/command_buffer/commands/pipeline.rs +++ b/vulkano/src/command_buffer/commands/pipeline.rs @@ -23,7 +23,9 @@ use crate::{ }, device::{DeviceOwned, QueueFlags}, format::{Format, FormatFeatures}, - image::{view::ImageViewType, ImageAccess, ImageAspects, ImageViewAbstract, SampleCount}, + image::{ + view::ImageViewType, ImageAccess, ImageAspects, ImageLayout, ImageViewAbstract, SampleCount, + }, pipeline::{ graphics::{ input_assembly::{PrimitiveTopology, PrimitiveTopologyClass}, @@ -32,10 +34,10 @@ use crate::{ }, DynamicState, GraphicsPipeline, PartialStateMode, Pipeline, PipelineLayout, }, - sampler::{Sampler, SamplerImageViewIncompatibleError}, + sampler::Sampler, shader::{DescriptorBindingRequirements, ShaderScalarType, ShaderStage, ShaderStages}, sync::{PipelineStageAccess, PipelineStageAccessFlags}, - DeviceSize, RequiresOneOf, VulkanObject, + DeviceSize, RequiresOneOf, ValidationError, VulkanObject, }; use std::{ cmp::min, @@ -1909,6 +1911,8 @@ where continue; } + let default_image_layout = descriptor_type.default_image_layout(); + let use_iter = move |index: u32| { let (stages_read, stages_write) = [Some(index), None] .into_iter() @@ -2019,9 +2023,13 @@ where if let Some(image_view_info) = element { let &DescriptorImageViewInfo { ref image_view, - image_layout, + mut image_layout, } = image_view_info; + if image_layout == ImageLayout::Undefined { + image_layout = default_image_layout; + } + let image = image_view.image(); let (use_ref, memory_access) = use_iter(index as u32); @@ -2043,9 +2051,13 @@ where if let Some((image_view_info, _sampler)) = element { let &DescriptorImageViewInfo { ref image_view, - image_layout, + mut image_layout, } = image_view_info; + if image_layout == ImageLayout::Undefined { + image_layout = default_image_layout; + } + let image = image_view.image(); let (use_ref, memory_access) = use_iter(index as u32); @@ -2637,7 +2649,7 @@ impl Display for PipelineExecutionError { } } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Debug)] pub enum DescriptorResourceInvalidError { ImageViewFormatMismatch { required: Format, @@ -2664,7 +2676,7 @@ pub enum DescriptorResourceInvalidError { image_view_set_num: u32, image_view_binding_num: u32, image_view_index: u32, - error: SamplerImageViewIncompatibleError, + error: ValidationError, }, SamplerUnnormalizedCoordinatesNotAllowed, SamplerYcbcrConversionNotAllowed, diff --git a/vulkano/src/descriptor_set/allocator.rs b/vulkano/src/descriptor_set/allocator.rs index 954db3fbed..9f1c2cd110 100644 --- a/vulkano/src/descriptor_set/allocator.rs +++ b/vulkano/src/descriptor_set/allocator.rs @@ -26,8 +26,9 @@ use super::{ sys::UnsafeDescriptorSet, }; use crate::{ + descriptor_set::layout::DescriptorSetLayoutCreateFlags, device::{Device, DeviceOwned}, - OomError, + RuntimeError, }; use crossbeam_queue::ArrayQueue; use std::{cell::UnsafeCell, mem::ManuallyDrop, num::NonZeroU64, sync::Arc, thread}; @@ -61,7 +62,7 @@ pub unsafe trait DescriptorSetAllocator: DeviceOwned { &self, layout: &Arc, variable_descriptor_count: u32, - ) -> Result; + ) -> Result; } /// An allocated descriptor set. @@ -155,9 +156,11 @@ unsafe impl DescriptorSetAllocator for StandardDescriptorSetAllocator { &self, layout: &Arc, variable_descriptor_count: u32, - ) -> Result { + ) -> Result { assert!( - !layout.push_descriptor(), + !layout + .flags() + .intersects(DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR), "the provided descriptor set layout is for push descriptors, and cannot be used to \ build a descriptor set object", ); @@ -196,7 +199,7 @@ unsafe impl DescriptorSetAllocator for Arc { &self, layout: &Arc, variable_descriptor_count: u32, - ) -> Result { + ) -> Result { (**self).allocate(layout, variable_descriptor_count) } } @@ -220,7 +223,7 @@ struct FixedEntry { } impl FixedEntry { - fn new(layout: Arc) -> Result { + fn new(layout: Arc) -> Result { Ok(FixedEntry { pool: FixedPool::new(&layout, MAX_SETS)?, set_count: MAX_SETS, @@ -228,7 +231,7 @@ impl FixedEntry { }) } - fn allocate(&mut self) -> Result { + fn allocate(&mut self) -> Result { let inner = if let Some(inner) = self.pool.reserve.pop() { inner } else { @@ -256,7 +259,7 @@ struct FixedPool { } impl FixedPool { - fn new(layout: &Arc, set_count: usize) -> Result, OomError> { + fn new(layout: &Arc, set_count: usize) -> Result, RuntimeError> { let inner = DescriptorPool::new( layout.device().clone(), DescriptorPoolCreateInfo { @@ -285,10 +288,10 @@ impl FixedPool { reserve } Err(DescriptorPoolAllocError::OutOfHostMemory) => { - return Err(OomError::OutOfHostMemory); + return Err(RuntimeError::OutOfHostMemory); } Err(DescriptorPoolAllocError::OutOfDeviceMemory) => { - return Err(OomError::OutOfDeviceMemory); + return Err(RuntimeError::OutOfDeviceMemory); } Err(DescriptorPoolAllocError::FragmentedPool) => { // This can't happen as we don't free individual sets. @@ -321,7 +324,7 @@ struct VariableEntry { } impl VariableEntry { - fn new(layout: Arc) -> Result { + fn new(layout: Arc) -> Result { let reserve = Arc::new(ArrayQueue::new(MAX_POOLS)); Ok(VariableEntry { @@ -335,7 +338,7 @@ impl VariableEntry { fn allocate( &mut self, variable_descriptor_count: u32, - ) -> Result { + ) -> Result { if self.allocations >= MAX_SETS { self.pool = if let Some(inner) = self.reserve.pop() { Arc::new(VariablePool { @@ -356,10 +359,10 @@ impl VariableEntry { let inner = match unsafe { self.pool.inner.allocate_descriptor_sets([allocate_info]) } { Ok(mut sets) => sets.next().unwrap(), Err(DescriptorPoolAllocError::OutOfHostMemory) => { - return Err(OomError::OutOfHostMemory); + return Err(RuntimeError::OutOfHostMemory); } Err(DescriptorPoolAllocError::OutOfDeviceMemory) => { - return Err(OomError::OutOfDeviceMemory); + return Err(RuntimeError::OutOfDeviceMemory); } Err(DescriptorPoolAllocError::FragmentedPool) => { // This can't happen as we don't free individual sets. @@ -392,7 +395,7 @@ impl VariablePool { fn new( layout: &Arc, reserve: Arc>, - ) -> Result, OomError> { + ) -> Result, RuntimeError> { DescriptorPool::new( layout.device().clone(), DescriptorPoolCreateInfo { diff --git a/vulkano/src/descriptor_set/layout.rs b/vulkano/src/descriptor_set/layout.rs index a5b0a45492..edf66d8429 100644 --- a/vulkano/src/descriptor_set/layout.rs +++ b/vulkano/src/descriptor_set/layout.rs @@ -13,10 +13,11 @@ use crate::{ device::{Device, DeviceOwned}, - macros::{impl_id_counter, vulkan_enum}, + image::ImageLayout, + macros::{impl_id_counter, vulkan_bitflags, vulkan_enum}, sampler::Sampler, shader::{DescriptorBindingRequirements, ShaderStages}, - OomError, RequirementNotMet, RequiresOneOf, RuntimeError, Version, VulkanObject, + RequiresOneOf, RuntimeError, ValidationError, Version, VulkanError, VulkanObject, }; use ahash::HashMap; use std::{ @@ -36,8 +37,8 @@ pub struct DescriptorSetLayout { device: Arc, id: NonZeroU64, + flags: DescriptorSetLayoutCreateFlags, bindings: BTreeMap, - push_descriptor: bool, descriptor_counts: HashMap, } @@ -48,180 +49,51 @@ impl DescriptorSetLayout { pub fn new( device: Arc, create_info: DescriptorSetLayoutCreateInfo, - ) -> Result, DescriptorSetLayoutCreationError> { + ) -> Result, VulkanError> { Self::validate_new(&device, &create_info)?; + unsafe { Ok(Self::new_unchecked(device, create_info)?) } } fn validate_new( device: &Device, create_info: &DescriptorSetLayoutCreateInfo, - ) -> Result<(), DescriptorSetLayoutCreationError> { - let &DescriptorSetLayoutCreateInfo { - ref bindings, - push_descriptor, - _ne: _, - } = create_info; - - if push_descriptor { - if !device.enabled_extensions().khr_push_descriptor { - return Err(DescriptorSetLayoutCreationError::RequirementNotMet { - required_for: "`create_info.push_descriptor` is set", - requires_one_of: RequiresOneOf { - device_extensions: &["khr_push_descriptor"], - ..Default::default() - }, - }); - } - } - - let mut descriptor_counts: HashMap = HashMap::default(); - let highest_binding_num = bindings.keys().copied().next_back(); - - for (&binding_num, binding) in bindings.iter() { - let &DescriptorSetLayoutBinding { - descriptor_type, - descriptor_count, - variable_descriptor_count, - stages, - ref immutable_samplers, - _ne: _, - } = binding; - - // VUID-VkDescriptorSetLayoutBinding-descriptorType-parameter - descriptor_type.validate_device(device)?; - - if descriptor_count != 0 { - // VUID-VkDescriptorSetLayoutBinding-descriptorCount-00283 - stages.validate_device(device)?; - - *descriptor_counts.entry(descriptor_type).or_default() += descriptor_count; - } - - if push_descriptor { - // VUID-VkDescriptorSetLayoutCreateInfo-flags-00280 - if matches!( - descriptor_type, - DescriptorType::StorageBufferDynamic | DescriptorType::UniformBufferDynamic - ) { - return Err( - DescriptorSetLayoutCreationError::PushDescriptorDescriptorTypeIncompatible { - binding_num, - }, - ); - } - - // VUID-VkDescriptorSetLayoutBindingFlagsCreateInfo-flags-03003 - if variable_descriptor_count { - return Err( - DescriptorSetLayoutCreationError::PushDescriptorVariableDescriptorCount { - binding_num, - }, - ); - } - } - - if !immutable_samplers.is_empty() { - if immutable_samplers - .iter() - .any(|sampler| sampler.sampler_ycbcr_conversion().is_some()) - { - if !matches!(descriptor_type, DescriptorType::CombinedImageSampler) { - return Err( - DescriptorSetLayoutCreationError::ImmutableSamplersDescriptorTypeIncompatible { - binding_num, - }, - ); - } - } else { - if !matches!( - descriptor_type, - DescriptorType::Sampler | DescriptorType::CombinedImageSampler - ) { - return Err( - DescriptorSetLayoutCreationError::ImmutableSamplersDescriptorTypeIncompatible { - binding_num, - }, - ); - } - } - - // VUID-VkDescriptorSetLayoutBinding-descriptorType-00282 - if descriptor_count != immutable_samplers.len() as u32 { - return Err( - DescriptorSetLayoutCreationError::ImmutableSamplersCountMismatch { - binding_num, - sampler_count: immutable_samplers.len() as u32, - descriptor_count, - }, - ); - } - } - - // VUID-VkDescriptorSetLayoutBinding-descriptorType-01510 - // If descriptorType is VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT and descriptorCount is not 0, then stageFlags must be 0 or VK_SHADER_STAGE_FRAGMENT_BIT - - if variable_descriptor_count { - // VUID-VkDescriptorSetLayoutBindingFlagsCreateInfo-descriptorBindingVariableDescriptorCount-03014 - if !device - .enabled_features() - .descriptor_binding_variable_descriptor_count - { - return Err(DescriptorSetLayoutCreationError::RequirementNotMet { - required_for: "`create_info.bindings` has an element where \ - `variable_descriptor_count` is set", - requires_one_of: RequiresOneOf { - features: &["descriptor_binding_variable_descriptor_count"], - ..Default::default() - }, - }); - } - - // VUID-VkDescriptorSetLayoutBindingFlagsCreateInfo-pBindingFlags-03004 - if Some(binding_num) != highest_binding_num { - return Err( - DescriptorSetLayoutCreationError::VariableDescriptorCountBindingNotHighest { - binding_num, - highest_binding_num: highest_binding_num.unwrap(), - }, - ); - } - - // VUID-VkDescriptorSetLayoutBindingFlagsCreateInfo-pBindingFlags-03015 - if matches!( - descriptor_type, - DescriptorType::UniformBufferDynamic | DescriptorType::StorageBufferDynamic - ) { - return Err( - DescriptorSetLayoutCreationError::VariableDescriptorCountDescriptorTypeIncompatible { - binding_num, - }, - ); + ) -> Result<(), ValidationError> { + // VUID-vkCreateDescriptorSetLayout-pCreateInfo-parameter + create_info + .validate(device) + .map_err(|err| err.add_context("create_info"))?; + + if let Some(max_per_set_descriptors) = device + .physical_device() + .properties() + .max_per_set_descriptors + { + let total_descriptor_count: u32 = create_info + .bindings + .values() + .map(|binding| binding.descriptor_count) + .sum(); + + // Safety: create_info is validated, and we only enter here if the + // max_per_set_descriptors property exists (which means this function exists too). + if total_descriptor_count > max_per_set_descriptors + && unsafe { + device + .descriptor_set_layout_support_unchecked(create_info) + .is_none() } + { + return Err(ValidationError { + problem: "the total number of descriptors across all bindings is greater than \ + the max_per_set_descriptors limit, and \ + device.descriptor_set_layout_support returned None" + .into(), + ..Default::default() + }); } } - // VUID-VkDescriptorSetLayoutCreateInfo-flags-00281 - if push_descriptor - && descriptor_counts.values().copied().sum::() - > device - .physical_device() - .properties() - .max_push_descriptors - .unwrap_or(0) - { - return Err( - DescriptorSetLayoutCreationError::MaxPushDescriptorsExceeded { - provided: descriptor_counts.values().copied().sum(), - max_supported: device - .physical_device() - .properties() - .max_push_descriptors - .unwrap_or(0), - }, - ); - } - Ok(()) } @@ -231,77 +103,75 @@ impl DescriptorSetLayout { create_info: DescriptorSetLayoutCreateInfo, ) -> Result, RuntimeError> { let &DescriptorSetLayoutCreateInfo { + flags, ref bindings, - push_descriptor, _ne: _, } = &create_info; + struct PerBinding { + immutable_samplers_vk: Vec, + } + let mut bindings_vk = Vec::with_capacity(bindings.len()); + let mut per_binding_vk = Vec::with_capacity(bindings.len()); + let mut binding_flags_info_vk = None; let mut binding_flags_vk = Vec::with_capacity(bindings.len()); - let mut immutable_samplers_vk: Vec> = Vec::new(); // only to keep the arrays of handles alive - let mut flags = ash::vk::DescriptorSetLayoutCreateFlags::empty(); - - if push_descriptor { - flags |= ash::vk::DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR_KHR; - } for (&binding_num, binding) in bindings.iter() { - let mut binding_flags = ash::vk::DescriptorBindingFlags::empty(); - - let p_immutable_samplers = if !binding.immutable_samplers.is_empty() { - // VUID-VkDescriptorSetLayoutBinding-descriptorType-00282 - let sampler_handles = binding - .immutable_samplers - .iter() - .map(|s| s.handle()) - .collect::>() - .into_boxed_slice(); - let p_immutable_samplers = sampler_handles.as_ptr(); - immutable_samplers_vk.push(sampler_handles); - p_immutable_samplers - } else { - ptr::null() - }; - - if binding.variable_descriptor_count { - binding_flags |= ash::vk::DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT; - } + let &DescriptorSetLayoutBinding { + binding_flags, + descriptor_type, + descriptor_count, + stages, + ref immutable_samplers, + _ne: _, + } = binding; - // VUID-VkDescriptorSetLayoutCreateInfo-binding-00279 - // Guaranteed by BTreeMap bindings_vk.push(ash::vk::DescriptorSetLayoutBinding { binding: binding_num, - descriptor_type: binding.descriptor_type.into(), - descriptor_count: binding.descriptor_count, - stage_flags: binding.stages.into(), - p_immutable_samplers, + descriptor_type: descriptor_type.into(), + descriptor_count, + stage_flags: stages.into(), + p_immutable_samplers: ptr::null(), + }); + per_binding_vk.push(PerBinding { + immutable_samplers_vk: immutable_samplers + .iter() + .map(VulkanObject::handle) + .collect(), }); - binding_flags_vk.push(binding_flags); + binding_flags_vk.push(binding_flags.into()); } - let mut binding_flags_create_info = if device.api_version() >= Version::V1_2 - || device.enabled_extensions().ext_descriptor_indexing - { - Some(ash::vk::DescriptorSetLayoutBindingFlagsCreateInfo { - // VUID-VkDescriptorSetLayoutBindingFlagsCreateInfo-bindingCount-03002 - binding_count: binding_flags_vk.len() as u32, - p_binding_flags: binding_flags_vk.as_ptr(), - ..Default::default() - }) - } else { - None - }; + for (binding_vk, per_binding_vk) in bindings_vk.iter_mut().zip(per_binding_vk.iter()) { + let PerBinding { + immutable_samplers_vk, + } = per_binding_vk; + + if !immutable_samplers_vk.is_empty() { + binding_vk.p_immutable_samplers = immutable_samplers_vk.as_ptr(); + } + } let mut create_info_vk = ash::vk::DescriptorSetLayoutCreateInfo { - flags, + flags: flags.into(), binding_count: bindings_vk.len() as u32, p_bindings: bindings_vk.as_ptr(), ..Default::default() }; - if let Some(binding_flags_create_info) = binding_flags_create_info.as_mut() { - binding_flags_create_info.p_next = create_info_vk.p_next; - create_info_vk.p_next = binding_flags_create_info as *const _ as *const _; + if device.api_version() >= Version::V1_2 + || device.enabled_extensions().ext_descriptor_indexing + { + let next = + binding_flags_info_vk.insert(ash::vk::DescriptorSetLayoutBindingFlagsCreateInfo { + binding_count: binding_flags_vk.len() as u32, + p_binding_flags: binding_flags_vk.as_ptr(), + ..Default::default() + }); + + next.p_next = create_info_vk.p_next; + create_info_vk.p_next = next as *const _ as *const _; } let handle = { @@ -334,8 +204,8 @@ impl DescriptorSetLayout { create_info: DescriptorSetLayoutCreateInfo, ) -> Arc { let DescriptorSetLayoutCreateInfo { + flags, bindings, - push_descriptor, _ne: _, } = create_info; @@ -352,8 +222,8 @@ impl DescriptorSetLayout { handle, device, id: Self::next_id(), + flags, bindings, - push_descriptor, descriptor_counts, }) } @@ -362,17 +232,16 @@ impl DescriptorSetLayout { self.id } - /// Returns the bindings of the descriptor set layout. + /// Returns the flags that the descriptor set layout was created with. #[inline] - pub fn bindings(&self) -> &BTreeMap { - &self.bindings + pub fn flags(&self) -> DescriptorSetLayoutCreateFlags { + self.flags } - /// Returns whether the descriptor set layout is for push descriptors or regular descriptor - /// sets. + /// Returns the bindings of the descriptor set layout. #[inline] - pub fn push_descriptor(&self) -> bool { - self.push_descriptor + pub fn bindings(&self) -> &BTreeMap { + &self.bindings } /// Returns the number of descriptors of each type. @@ -391,7 +260,10 @@ impl DescriptorSetLayout { .values() .next_back() .map(|binding| { - if binding.variable_descriptor_count { + if binding + .binding_flags + .intersects(DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT) + { binding.descriptor_count } else { 0 @@ -407,8 +279,7 @@ impl DescriptorSetLayout { /// or they must be identically defined to the Vulkan API. #[inline] pub fn is_compatible_with(&self, other: &DescriptorSetLayout) -> bool { - self == other - || (self.bindings == other.bindings && self.push_descriptor == other.push_descriptor) + self == other || (self.flags == other.flags && self.bindings == other.bindings) } } @@ -444,181 +315,201 @@ unsafe impl DeviceOwned for DescriptorSetLayout { impl_id_counter!(DescriptorSetLayout); -/// Error related to descriptor set layout. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum DescriptorSetLayoutCreationError { - /// Out of Memory. - OomError(OomError), +/// Parameters to create a new `DescriptorSetLayout`. +#[derive(Clone, Debug)] +pub struct DescriptorSetLayoutCreateInfo { + /// Specifies how to create the descriptor set layout. + pub flags: DescriptorSetLayoutCreateFlags, - RequirementNotMet { - required_for: &'static str, - requires_one_of: RequiresOneOf, - }, + /// The bindings of the desriptor set layout. These are specified according to binding number. + /// + /// It is generally advisable to keep the binding numbers low. Higher binding numbers may + /// use more memory inside Vulkan. + /// + /// The default value is empty. + pub bindings: BTreeMap, - /// A binding includes immutable samplers but their number differs from `descriptor_count`. - ImmutableSamplersCountMismatch { - binding_num: u32, - sampler_count: u32, - descriptor_count: u32, - }, + pub _ne: crate::NonExhaustive, +} - /// A binding includes immutable samplers but it has an incompatible `descriptor_type`. - ImmutableSamplersDescriptorTypeIncompatible { binding_num: u32 }, +impl DescriptorSetLayoutCreateInfo { + pub(crate) fn validate(&self, device: &Device) -> Result<(), ValidationError> { + let &Self { + flags, + ref bindings, + _ne: _, + } = self; - /// More descriptors were provided in all bindings than the - /// [`max_push_descriptors`](crate::device::Properties::max_push_descriptors) limit. - MaxPushDescriptorsExceeded { provided: u32, max_supported: u32 }, + flags + .validate_device(device) + .map_err(|err| ValidationError { + context: "flags".into(), + vuids: &["VUID-VkDescriptorSetLayoutCreateInfo-flags-parameter"], + ..ValidationError::from_requirement(err) + })?; - /// `push_descriptor` is enabled, but a binding has an incompatible `descriptor_type`. - PushDescriptorDescriptorTypeIncompatible { binding_num: u32 }, + // VUID-VkDescriptorSetLayoutCreateInfo-binding-00279 + // Ensured because it is a map - /// `push_descriptor` is enabled, but a binding has `variable_descriptor_count` enabled. - PushDescriptorVariableDescriptorCount { binding_num: u32 }, + let mut total_descriptor_count = 0; + let highest_binding_num = bindings.keys().copied().next_back(); - /// A binding has `variable_descriptor_count` enabled, but it is not the highest-numbered - /// binding. - VariableDescriptorCountBindingNotHighest { - binding_num: u32, - highest_binding_num: u32, - }, + for (&binding_num, binding) in bindings.iter() { + binding + .validate(device) + .map_err(|err| err.add_context(format!("bindings[{}]", binding_num)))?; - /// A binding has `variable_descriptor_count` enabled, but it has an incompatible - /// `descriptor_type`. - VariableDescriptorCountDescriptorTypeIncompatible { binding_num: u32 }, -} + let &DescriptorSetLayoutBinding { + binding_flags, + descriptor_type, + descriptor_count, + stages: _, + immutable_samplers: _, + _ne: _, + } = binding; -impl From for DescriptorSetLayoutCreationError { - fn from(error: RuntimeError) -> Self { - Self::OomError(error.into()) - } -} + total_descriptor_count += descriptor_count; -impl Error for DescriptorSetLayoutCreationError {} + if flags.intersects(DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR) { + if matches!( + descriptor_type, + DescriptorType::UniformBufferDynamic | DescriptorType::StorageBufferDynamic + ) { + return Err(ValidationError { + problem: format!( + "flags contains DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR, \ + and bindings[{}].descriptor_type is + DescriptorType::UniformBufferDynamic or \ + DescriptorType::StorageBufferDynamic", + binding_num + ) + .into(), + vuids: &["VUID-VkDescriptorSetLayoutCreateInfo-flags-00280"], + ..Default::default() + }); + } -impl Display for DescriptorSetLayoutCreationError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::OomError(_) => { - write!(f, "out of memory") + if binding_flags.intersects(DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT) { + return Err(ValidationError { + problem: format!( + "flags contains DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR, \ + and bindings[{}].flags contains \ + DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT", + binding_num + ) + .into(), + vuids: &["VUID-VkDescriptorSetLayoutBindingFlagsCreateInfo-flags-03003"], + ..Default::default() + }); + } + } + + if binding_flags.intersects(DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT) + && Some(binding_num) != highest_binding_num + { + return Err(ValidationError { + problem: format!( + "bindings[{}].flags contains \ + DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT, but {0} is not the + highest binding number in bindings", + binding_num + ) + .into(), + vuids: &[ + "VUID-VkDescriptorSetLayoutBindingFlagsCreateInfo-pBindingFlags-03004", + ], + ..Default::default() + }); } - Self::RequirementNotMet { - required_for, - requires_one_of, - } => write!( - f, - "a requirement was not met for: {}; requires one of: {}", - required_for, requires_one_of, - ), - Self::ImmutableSamplersCountMismatch { - binding_num, - sampler_count, - descriptor_count, - } => write!( - f, - "binding {} includes immutable samplers but their number ({}) differs from \ - `descriptor_count` ({})", - binding_num, sampler_count, descriptor_count, - ), - Self::ImmutableSamplersDescriptorTypeIncompatible { binding_num } => write!( - f, - "binding {} includes immutable samplers but it has an incompatible \ - `descriptor_type`", - binding_num, - ), - Self::MaxPushDescriptorsExceeded { - provided, - max_supported, - } => write!( - f, - "more descriptors were provided in all bindings ({}) than the \ - `max_push_descriptors` limit ({})", - provided, max_supported, - ), - Self::PushDescriptorDescriptorTypeIncompatible { binding_num } => write!( - f, - "`push_descriptor` is enabled, but binding {} has an incompatible \ - `descriptor_type`", - binding_num, - ), - Self::PushDescriptorVariableDescriptorCount { binding_num } => write!( - f, - "`push_descriptor` is enabled, but binding {} has `variable_descriptor_count` \ - enabled", - binding_num, - ), - Self::VariableDescriptorCountBindingNotHighest { - binding_num, - highest_binding_num, - } => write!( - f, - "binding {} has `variable_descriptor_count` enabled, but it is not the \ - highest-numbered binding ({})", - binding_num, highest_binding_num, - ), - Self::VariableDescriptorCountDescriptorTypeIncompatible { binding_num } => write!( - f, - "binding {} has `variable_descriptor_count` enabled, but it has an incompatible \ - `descriptor_type`", - binding_num, - ), } + + let max_push_descriptors = device + .physical_device() + .properties() + .max_push_descriptors + .unwrap_or(0); + + if flags.intersects(DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR) + && total_descriptor_count > max_push_descriptors + { + return Err(ValidationError { + problem: "flags contains DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR, and the \ + total number of descriptors in bindings exceeds the max_push_descriptors \ + limit" + .into(), + vuids: &["VUID-VkDescriptorSetLayoutCreateInfo-flags-00281"], + ..Default::default() + }); + } + + Ok(()) } } -impl From for DescriptorSetLayoutCreationError { - fn from(err: RequirementNotMet) -> Self { - Self::RequirementNotMet { - required_for: err.required_for, - requires_one_of: err.requires_one_of, +impl Default for DescriptorSetLayoutCreateInfo { + #[inline] + fn default() -> Self { + Self { + flags: DescriptorSetLayoutCreateFlags::empty(), + bindings: BTreeMap::new(), + _ne: crate::NonExhaustive(()), } } } -/// Parameters to create a new `DescriptorSetLayout`. -#[derive(Clone, Debug)] -pub struct DescriptorSetLayoutCreateInfo { - /// The bindings of the desriptor set layout. These are specified according to binding number. - /// - /// It is generally advisable to keep the binding numbers low. Higher binding numbers may - /// use more memory inside Vulkan. - /// - /// The default value is empty. - pub bindings: BTreeMap, +vulkan_bitflags! { + #[non_exhaustive] + + /// Flags that control how a descriptor set layout is created. + DescriptorSetLayoutCreateFlags = DescriptorSetLayoutCreateFlags(u32); + + /* TODO: enable + // TODO: document + UPDATE_AFTER_BIND_POOL = UPDATE_AFTER_BIND_POOL { + api_version: V1_2, + device_extensions: [ext_descriptor_indexing], + }, */ /// Whether the descriptor set layout should be created for push descriptors. /// - /// If `true`, the layout can only be used for push descriptors, and if `false`, it can only + /// If set, the layout can only be used for push descriptors, and if not set, it can only /// be used for regular descriptor sets. /// - /// If set to `true`, the - /// [`khr_push_descriptor`](crate::device::DeviceExtensions::khr_push_descriptor) extension must - /// be enabled on the device, and there are several restrictions: + /// If set, there are several restrictions: /// - There must be no bindings with a type of [`DescriptorType::UniformBufferDynamic`] /// or [`DescriptorType::StorageBufferDynamic`]. /// - There must be no bindings with `variable_descriptor_count` enabled. /// - The total number of descriptors across all bindings must be less than the /// [`max_push_descriptors`](crate::device::Properties::max_push_descriptors) limit. - /// - /// The default value is `false`. - pub push_descriptor: bool, + PUSH_DESCRIPTOR = PUSH_DESCRIPTOR_KHR { + device_extensions: [khr_push_descriptor], + }, - pub _ne: crate::NonExhaustive, -} + /* TODO: enable + // TODO: document + DESCRIPTOR_BUFFER = DESCRIPTOR_BUFFER_EXT { + device_extensions: [ext_descriptor_buffer], + }, */ -impl Default for DescriptorSetLayoutCreateInfo { - #[inline] - fn default() -> Self { - Self { - bindings: BTreeMap::new(), - push_descriptor: false, - _ne: crate::NonExhaustive(()), - } - } + /* TODO: enable + // TODO: document + EMBEDDED_IMMUTABLE_SAMPLERS = EMBEDDED_IMMUTABLE_SAMPLERS_EXT { + device_extensions: [ext_descriptor_buffer], + }, */ + + /* TODO: enable + // TODO: document + HOST_ONLY_POOL = HOST_ONLY_POOL_EXT { + device_extensions: [ext_mutable_descriptor_type, valve_mutable_descriptor_type], + }, */ } /// A binding in a descriptor set layout. #[derive(Clone, Debug, PartialEq, Eq)] pub struct DescriptorSetLayoutBinding { + /// Specifies how to create the binding. + pub binding_flags: DescriptorBindingFlags, + /// The content and layout of each array element of a binding. /// /// There is no default value. @@ -631,21 +522,6 @@ pub struct DescriptorSetLayoutBinding { /// The default value is `1`. pub descriptor_count: u32, - /// Whether the binding has a variable number of descriptors. - /// - /// If set to `true`, the [`descriptor_binding_variable_descriptor_count`] feature must be - /// enabled. The value of `descriptor_count` specifies the maximum number of descriptors - /// allowed. - /// - /// There may only be one binding with a variable count in a descriptor set, and it must be the - /// binding with the highest binding number. The `descriptor_type` must not be - /// [`DescriptorType::UniformBufferDynamic`] or [`DescriptorType::StorageBufferDynamic`]. - /// - /// The default value is `false`. - /// - /// [`descriptor_binding_variable_descriptor_count`]: crate::device::Features::descriptor_binding_variable_descriptor_count - pub variable_descriptor_count: bool, - /// Which shader stages are going to access the descriptors in this binding. /// /// The default value is [`ShaderStages::empty()`], which must be overridden. @@ -670,9 +546,9 @@ impl DescriptorSetLayoutBinding { #[inline] pub fn descriptor_type(descriptor_type: DescriptorType) -> Self { Self { + binding_flags: DescriptorBindingFlags::empty(), descriptor_type, descriptor_count: 1, - variable_descriptor_count: false, stages: ShaderStages::empty(), immutable_samplers: Vec::new(), _ne: crate::NonExhaustive(()), @@ -722,15 +598,148 @@ impl DescriptorSetLayoutBinding { Ok(()) } + + pub(crate) fn validate(&self, device: &Device) -> Result<(), ValidationError> { + let &Self { + binding_flags, + descriptor_type, + descriptor_count, + stages, + ref immutable_samplers, + _ne: _, + } = self; + + binding_flags + .validate_device(device) + .map_err(|err| ValidationError { + context: "binding_flags".into(), + vuids: &[ + "VUID-VkDescriptorSetLayoutBindingFlagsCreateInfo-pBindingFlags-parameter", + ], + ..ValidationError::from_requirement(err) + })?; + + descriptor_type + .validate_device(device) + .map_err(|err| ValidationError { + context: "descriptor_type".into(), + vuids: &["VUID-VkDescriptorSetLayoutBinding-descriptorType-parameter"], + ..ValidationError::from_requirement(err) + })?; + + if descriptor_count != 0 { + stages + .validate_device(device) + .map_err(|err| ValidationError { + context: "stages".into(), + vuids: &["VUID-VkDescriptorSetLayoutBinding-descriptorCount-00283"], + ..ValidationError::from_requirement(err) + })?; + + if descriptor_type == DescriptorType::InputAttachment + && !(stages.is_empty() || stages == ShaderStages::FRAGMENT) + { + return Err(ValidationError { + problem: "descriptor_type is DescriptorType::InputAttachment, but stages is not + either empty or equal to ShaderStages::FRAGMENT" + .into(), + vuids: &["VUID-VkDescriptorSetLayoutBinding-descriptorType-01510"], + ..Default::default() + }); + } + } + + if !immutable_samplers.is_empty() { + if descriptor_count != immutable_samplers.len() as u32 { + return Err(ValidationError { + problem: "immutable_samplers is not empty, but its length does not equal \ + descriptor_count" + .into(), + vuids: &["VUID-VkDescriptorSetLayoutBinding-descriptorType-00282"], + ..Default::default() + }); + } + + let mut has_sampler_ycbcr_conversion = false; + + for sampler in immutable_samplers { + assert_eq!(device, sampler.device().as_ref()); + + has_sampler_ycbcr_conversion |= sampler.sampler_ycbcr_conversion().is_some(); + } + + if has_sampler_ycbcr_conversion { + if !matches!(descriptor_type, DescriptorType::CombinedImageSampler) { + return Err(ValidationError { + problem: "immutable_samplers contains a sampler with a \ + sampler YCbCr conversion, but descriptor_type is not \ + DescriptorType::CombinedImageSampler" + .into(), + vuids: &["VUID-VkDescriptorSetLayoutBinding-descriptorType-00282"], + ..Default::default() + }); + } + } else { + if !matches!( + descriptor_type, + DescriptorType::Sampler | DescriptorType::CombinedImageSampler + ) { + return Err(ValidationError { + problem: "immutable_samplers is not empty, but descriptor_type is not \ + DescriptorType::Sampler or DescriptorType::CombinedImageSampler" + .into(), + vuids: &["VUID-VkDescriptorSetLayoutBinding-descriptorType-00282"], + ..Default::default() + }); + } + } + } + + if binding_flags.intersects(DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT) { + if !device + .enabled_features() + .descriptor_binding_variable_descriptor_count + { + return Err(ValidationError { + context: "binding_flags".into(), + problem: "contains DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT".into(), + requires_one_of: Some(RequiresOneOf { + features: &["descriptor_binding_variable_descriptor_count"], + ..Default::default() + }), + vuids: &["VUID-VkDescriptorSetLayoutBindingFlagsCreateInfo-descriptorBindingVariableDescriptorCount-03014"], + }); + } + + if matches!( + descriptor_type, + DescriptorType::UniformBufferDynamic | DescriptorType::StorageBufferDynamic + ) { + return Err(ValidationError { + problem: "binding_flags contains \ + DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT, and descriptor_type is + DescriptorType::UniformBufferDynamic or \ + DescriptorType::StorageBufferDynamic" + .into(), + vuids: &[ + "VUID-VkDescriptorSetLayoutBindingFlagsCreateInfo-pBindingFlags-03015", + ], + ..Default::default() + }); + } + } + + Ok(()) + } } impl From<&DescriptorBindingRequirements> for DescriptorSetLayoutBinding { #[inline] fn from(reqs: &DescriptorBindingRequirements) -> Self { Self { + binding_flags: DescriptorBindingFlags::empty(), descriptor_type: reqs.descriptor_types[0], descriptor_count: reqs.descriptor_count.unwrap_or(0), - variable_descriptor_count: false, stages: reqs.stages, immutable_samplers: Vec::new(), _ne: crate::NonExhaustive(()), @@ -738,46 +747,48 @@ impl From<&DescriptorBindingRequirements> for DescriptorSetLayoutBinding { } } -/// Error when checking whether the requirements for a binding have been met. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum DescriptorRequirementsNotMet { - /// The binding's `descriptor_type` is not one of those required. - DescriptorType { - required: Vec, - obtained: DescriptorType, - }, +vulkan_bitflags! { + #[non_exhaustive] - /// The binding's `descriptor_count` is less than what is required. - DescriptorCount { required: u32, obtained: u32 }, + /// Flags that control how a binding in a descriptor set layout is created. + DescriptorBindingFlags = DescriptorBindingFlags(u32); - /// The binding's `stages` does not contain the stages that are required. - ShaderStages { - required: ShaderStages, - obtained: ShaderStages, - }, -} + /* TODO: enable + // TODO: document + UPDATE_AFTER_BIND = UPDATE_AFTER_BIND { + api_version: V1_2, + device_extensions: [ext_descriptor_indexing], + }, */ -impl Error for DescriptorRequirementsNotMet {} + /* TODO: enable + // TODO: document + UPDATE_UNUSED_WHILE_PENDING = UPDATE_UNUSED_WHILE_PENDING { + api_version: V1_2, + device_extensions: [ext_descriptor_indexing], + }, */ -impl Display for DescriptorRequirementsNotMet { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::DescriptorType { required, obtained } => write!( - f, - "the descriptor's type ({:?}) is not one of those required ({:?})", - obtained, required, - ), - Self::DescriptorCount { required, obtained } => write!( - f, - "the descriptor count ({}) is less than what is required ({})", - obtained, required, - ), - Self::ShaderStages { .. } => write!( - f, - "the descriptor's shader stages do not contain the stages that are required", - ), - } - } + /* TODO: enable + // TODO: document + PARTIALLY_BOUND = PARTIALLY_BOUND { + api_version: V1_2, + device_extensions: [ext_descriptor_indexing], + }, */ + + /// Whether the binding has a variable number of descriptors. + /// + /// If set, the [`descriptor_binding_variable_descriptor_count`] feature must be + /// enabled. The value of `descriptor_count` specifies the maximum number of descriptors + /// allowed. + /// + /// There may only be one binding with a variable count in a descriptor set, and it must be the + /// binding with the highest binding number. The `descriptor_type` must not be + /// [`DescriptorType::UniformBufferDynamic`] or [`DescriptorType::StorageBufferDynamic`]. + /// + /// [`descriptor_binding_variable_descriptor_count`]: crate::device::Features::descriptor_binding_variable_descriptor_count + VARIABLE_DESCRIPTOR_COUNT = VARIABLE_DESCRIPTOR_COUNT { + api_version: V1_2, + device_extensions: [ext_descriptor_indexing], + }, } vulkan_enum! { @@ -863,6 +874,84 @@ vulkan_enum! { },*/ } +impl DescriptorType { + pub(crate) fn default_image_layout(self) -> ImageLayout { + match self { + DescriptorType::CombinedImageSampler + | DescriptorType::SampledImage + | DescriptorType::InputAttachment => ImageLayout::ShaderReadOnlyOptimal, + DescriptorType::StorageImage => ImageLayout::General, + DescriptorType::Sampler + | DescriptorType::UniformTexelBuffer + | DescriptorType::StorageTexelBuffer + | DescriptorType::UniformBuffer + | DescriptorType::StorageBuffer + | DescriptorType::UniformBufferDynamic + | DescriptorType::StorageBufferDynamic + | DescriptorType::AccelerationStructure => ImageLayout::Undefined, + } + } +} + +/// Contains information about the level of support a device has for a particular descriptor set. +#[derive(Clone, Debug)] +pub struct DescriptorSetLayoutSupport { + /// If the queried descriptor set layout has a binding with the + /// [`DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT`] flag set, then this indicates the + /// maximum number of descriptors that binding could have. This is always at least as large + /// as the descriptor count of the create info that was queried; if the queried descriptor + /// count is higher than supported, `None` is returned instead of this structure. + /// + /// If the queried descriptor set layout does not have such a binding, or if the + /// [`descriptor_binding_variable_descriptor_count`] feature isn't enabled on the device, this + /// will be 0. + /// + /// [`descriptor_binding_variable_descriptor_count`]: crate::device::Features::descriptor_binding_variable_descriptor_count + pub max_variable_descriptor_count: u32, +} + +/// Error when checking whether the requirements for a binding have been met. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum DescriptorRequirementsNotMet { + /// The binding's `descriptor_type` is not one of those required. + DescriptorType { + required: Vec, + obtained: DescriptorType, + }, + + /// The binding's `descriptor_count` is less than what is required. + DescriptorCount { required: u32, obtained: u32 }, + + /// The binding's `stages` does not contain the stages that are required. + ShaderStages { + required: ShaderStages, + obtained: ShaderStages, + }, +} + +impl Error for DescriptorRequirementsNotMet {} + +impl Display for DescriptorRequirementsNotMet { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + match self { + Self::DescriptorType { required, obtained } => write!( + f, + "the descriptor's type ({:?}) is not one of those required ({:?})", + obtained, required, + ), + Self::DescriptorCount { required, obtained } => write!( + f, + "the descriptor count ({}) is less than what is required ({})", + obtained, required, + ), + Self::ShaderStages { .. } => write!( + f, + "the descriptor's shader stages do not contain the stages that are required", + ), + } + } +} + #[cfg(test)] mod tests { use crate::{ diff --git a/vulkano/src/descriptor_set/mod.rs b/vulkano/src/descriptor_set/mod.rs index 0473499483..b85723c883 100644 --- a/vulkano/src/descriptor_set/mod.rs +++ b/vulkano/src/descriptor_set/mod.rs @@ -78,30 +78,31 @@ //! [`DescriptorSetAllocator`]: allocator::DescriptorSetAllocator //! [`StandardDescriptorSetAllocator`]: allocator::StandardDescriptorSetAllocator -pub(crate) use self::update::{ - set_descriptor_write_image_layouts, validate_descriptor_write, DescriptorWriteInfo, -}; +pub(crate) use self::update::DescriptorWriteInfo; pub use self::{ collection::DescriptorSetsCollection, persistent::PersistentDescriptorSet, update::{ - DescriptorBufferInfo, DescriptorImageViewInfo, DescriptorSetUpdateError, - WriteDescriptorSet, WriteDescriptorSetElements, + CopyDescriptorSet, DescriptorBufferInfo, DescriptorImageViewInfo, WriteDescriptorSet, + WriteDescriptorSetElements, }, }; use self::{layout::DescriptorSetLayout, sys::UnsafeDescriptorSet}; use crate::{ - acceleration_structure::AccelerationStructure, buffer::view::BufferView, - descriptor_set::layout::DescriptorType, device::DeviceOwned, sampler::Sampler, OomError, - VulkanObject, + acceleration_structure::AccelerationStructure, + buffer::view::BufferView, + descriptor_set::layout::{ + DescriptorBindingFlags, DescriptorSetLayoutCreateFlags, DescriptorType, + }, + device::DeviceOwned, + image::ImageLayout, + sampler::Sampler, + ValidationError, VulkanObject, }; use ahash::HashMap; use smallvec::{smallvec, SmallVec}; use std::{ - error::Error, - fmt::{Display, Error as FmtError, Formatter}, hash::{Hash, Hasher}, - ptr, sync::Arc, }; @@ -168,55 +169,55 @@ impl DescriptorSetInner { layout: Arc, variable_descriptor_count: u32, descriptor_writes: impl IntoIterator, - ) -> Result { + descriptor_copies: impl IntoIterator, + ) -> Result { assert!( - !layout.push_descriptor(), + !layout + .flags() + .intersects(DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR), "the provided descriptor set layout is for push descriptors, and cannot be used to \ build a descriptor set object", ); - let max_count = layout.variable_descriptor_count(); + let max_variable_descriptor_count = layout.variable_descriptor_count(); assert!( - variable_descriptor_count <= max_count, + variable_descriptor_count <= max_variable_descriptor_count, "the provided variable_descriptor_count ({}) is greater than the maximum number of \ variable count descriptors in the layout ({})", variable_descriptor_count, - max_count, + max_variable_descriptor_count, ); + let mut resources = DescriptorSetResources::new(&layout, variable_descriptor_count); + struct PerDescriptorWrite { + write_info: DescriptorWriteInfo, acceleration_structures: ash::vk::WriteDescriptorSetAccelerationStructureKHR, } - let mut resources = DescriptorSetResources::new(&layout, variable_descriptor_count); - let writes_iter = descriptor_writes.into_iter(); let (lower_size_bound, _) = writes_iter.size_hint(); - let mut write_infos_vk: SmallVec<[_; 8]> = SmallVec::with_capacity(lower_size_bound); let mut writes_vk: SmallVec<[_; 8]> = SmallVec::with_capacity(lower_size_bound); let mut per_writes_vk: SmallVec<[_; 8]> = SmallVec::with_capacity(lower_size_bound); - for mut write in writes_iter { - set_descriptor_write_image_layouts(&mut write, &layout); - let layout_binding = - validate_descriptor_write(&write, &layout, variable_descriptor_count)?; + for (index, write) in writes_iter.enumerate() { + write + .validate(&layout, variable_descriptor_count) + .map_err(|err| err.add_context(format!("descriptor_writes[{}]", index)))?; + resources.write(&write, &layout); - resources.update(&write); - write_infos_vk.push(write.to_vulkan_info(layout_binding.descriptor_type)); + let layout_binding = &layout.bindings()[&write.binding()]; writes_vk.push(write.to_vulkan(handle, layout_binding.descriptor_type)); per_writes_vk.push(PerDescriptorWrite { + write_info: write.to_vulkan_info(layout_binding.descriptor_type), acceleration_structures: Default::default(), }); } if !writes_vk.is_empty() { - for ((info_vk, write_vk), per_write_vk) in write_infos_vk - .iter() - .zip(writes_vk.iter_mut()) - .zip(per_writes_vk.iter_mut()) - { - match info_vk { + for (write_vk, per_write_vk) in writes_vk.iter_mut().zip(per_writes_vk.iter_mut()) { + match &mut per_write_vk.write_info { DescriptorWriteInfo::Image(info) => { write_vk.descriptor_count = info.len() as u32; write_vk.p_image_info = info.as_ptr(); @@ -243,15 +244,45 @@ impl DescriptorSetInner { } } + let copies_iter = descriptor_copies.into_iter(); + let (lower_size_bound, _) = copies_iter.size_hint(); + let mut copies_vk: SmallVec<[_; 8]> = SmallVec::with_capacity(lower_size_bound); + + for (index, copy) in copies_iter.enumerate() { + copy.validate(&layout, variable_descriptor_count) + .map_err(|err| err.add_context(format!("descriptor_copies[{}]", index)))?; + resources.copy(©); + + let &CopyDescriptorSet { + ref src_set, + src_binding, + src_first_array_element, + dst_binding, + dst_first_array_element, + descriptor_count, + _ne: _, + } = © + + copies_vk.push(ash::vk::CopyDescriptorSet { + src_set: src_set.inner().handle(), + src_binding, + src_array_element: src_first_array_element, + dst_set: handle, + dst_binding, + dst_array_element: dst_first_array_element, + descriptor_count, + ..Default::default() + }); + } + unsafe { let fns = layout.device().fns(); - (fns.v1_0.update_descriptor_sets)( layout.device().handle(), writes_vk.len() as u32, writes_vk.as_ptr(), - 0, - ptr::null(), + copies_vk.len() as u32, + copies_vk.as_ptr(), ); } @@ -288,7 +319,10 @@ impl DescriptorSetResources { .bindings() .iter() .map(|(&binding_num, binding)| { - let count = if binding.variable_descriptor_count { + let count = if binding + .binding_flags + .intersects(DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT) + { variable_descriptor_count } else { binding.descriptor_count @@ -319,7 +353,10 @@ impl DescriptorSetResources { DescriptorType::Sampler => { if binding.immutable_samplers.is_empty() { DescriptorBindingResources::Sampler(smallvec![None; count]) - } else if layout.push_descriptor() { + } else if layout + .flags() + .intersects(DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR) + { // For push descriptors, no resource is written by default, this needs // to be done explicitly via a dummy write. DescriptorBindingResources::None(smallvec![None; count]) @@ -340,25 +377,43 @@ impl DescriptorSetResources { Self { binding_resources } } - /// Applies a descriptor write to the resources. - /// - /// # Panics - /// - /// - Panics if the binding number of a write does not exist in the resources. - /// - See also [`DescriptorBindingResources::update`]. + /// Returns a reference to the bound resources for `binding`. Returns `None` if the binding + /// doesn't exist. + #[inline] + pub fn binding(&self, binding: u32) -> Option<&DescriptorBindingResources> { + self.binding_resources.get(&binding) + } + #[inline] - pub fn update(&mut self, write: &WriteDescriptorSet) { + pub(crate) fn write(&mut self, write: &WriteDescriptorSet, layout: &DescriptorSetLayout) { + let descriptor_type = layout + .bindings() + .get(&write.binding()) + .expect("descriptor write has invalid binding number") + .descriptor_type; self.binding_resources .get_mut(&write.binding()) .expect("descriptor write has invalid binding number") - .update(write) + .write(write, descriptor_type) } - /// Returns a reference to the bound resources for `binding`. Returns `None` if the binding - /// doesn't exist. #[inline] - pub fn binding(&self, binding: u32) -> Option<&DescriptorBindingResources> { - self.binding_resources.get(&binding) + pub(crate) fn copy(&mut self, copy: &CopyDescriptorSet) { + let src = copy + .src_set + .resources() + .binding_resources + .get(©.src_binding) + .expect("descriptor copy has invalid src_binding number"); + self.binding_resources + .get_mut(©.dst_binding) + .expect("descriptor copy has invalid dst_binding number") + .copy( + src, + copy.src_first_array_element, + copy.dst_first_array_element, + copy.descriptor_count, + ); } } @@ -377,25 +432,24 @@ pub enum DescriptorBindingResources { type Elements = SmallVec<[Option; 1]>; impl DescriptorBindingResources { - /// Applies a descriptor write to the resources. - /// - /// # Panics - /// - /// - Panics if the resource types do not match. - /// - Panics if the write goes out of bounds. - #[inline] - pub fn update(&mut self, write: &WriteDescriptorSet) { - fn write_resources(first: usize, resources: &mut [Option], elements: &[T]) { + pub(crate) fn write(&mut self, write: &WriteDescriptorSet, descriptor_type: DescriptorType) { + fn write_resources( + first: usize, + resources: &mut [Option], + elements: &[T], + element_func: impl Fn(&T) -> T, + ) { resources .get_mut(first..first + elements.len()) .expect("descriptor write for binding out of bounds") .iter_mut() .zip(elements) .for_each(|(resource, element)| { - *resource = Some(element.clone()); + *resource = Some(element_func(element)); }); } + let default_image_layout = descriptor_type.default_image_layout(); let first = write.first_array_element() as usize; match (self, write.elements()) { @@ -414,29 +468,97 @@ impl DescriptorBindingResources { ( DescriptorBindingResources::Buffer(resources), WriteDescriptorSetElements::Buffer(elements), - ) => write_resources(first, resources, elements), + ) => write_resources(first, resources, elements, Clone::clone), ( DescriptorBindingResources::BufferView(resources), WriteDescriptorSetElements::BufferView(elements), - ) => write_resources(first, resources, elements), + ) => write_resources(first, resources, elements, Clone::clone), ( DescriptorBindingResources::ImageView(resources), WriteDescriptorSetElements::ImageView(elements), - ) => write_resources(first, resources, elements), + ) => write_resources(first, resources, elements, |element| { + let mut element = element.clone(); + + if element.image_layout == ImageLayout::Undefined { + element.image_layout = default_image_layout; + } + + element + }), ( DescriptorBindingResources::ImageViewSampler(resources), WriteDescriptorSetElements::ImageViewSampler(elements), - ) => write_resources(first, resources, elements), + ) => write_resources(first, resources, elements, |element| { + let mut element = element.clone(); + + if element.0.image_layout == ImageLayout::Undefined { + element.0.image_layout = default_image_layout; + } + + element + }), ( DescriptorBindingResources::Sampler(resources), WriteDescriptorSetElements::Sampler(elements), - ) => write_resources(first, resources, elements), + ) => write_resources(first, resources, elements, Clone::clone), _ => panic!( "descriptor write for binding {} has wrong resource type", write.binding(), ), } } + + pub(crate) fn copy( + &mut self, + src: &DescriptorBindingResources, + src_start: u32, + dst_start: u32, + count: u32, + ) { + let src_start = src_start as usize; + let dst_start = dst_start as usize; + let count = count as usize; + + match (src, self) { + (DescriptorBindingResources::None(src), DescriptorBindingResources::None(dst)) => { + dst[dst_start..dst_start + count] + .clone_from_slice(&src[src_start..src_start + count]); + } + (DescriptorBindingResources::Buffer(src), DescriptorBindingResources::Buffer(dst)) => { + dst[dst_start..dst_start + count] + .clone_from_slice(&src[src_start..src_start + count]); + } + ( + DescriptorBindingResources::BufferView(src), + DescriptorBindingResources::BufferView(dst), + ) => { + dst[dst_start..dst_start + count] + .clone_from_slice(&src[src_start..src_start + count]); + } + ( + DescriptorBindingResources::ImageView(src), + DescriptorBindingResources::ImageView(dst), + ) => { + dst[dst_start..dst_start + count] + .clone_from_slice(&src[src_start..src_start + count]); + } + ( + DescriptorBindingResources::ImageViewSampler(src), + DescriptorBindingResources::ImageViewSampler(dst), + ) => { + dst[dst_start..dst_start + count] + .clone_from_slice(&src[src_start..src_start + count]); + } + ( + DescriptorBindingResources::Sampler(src), + DescriptorBindingResources::Sampler(dst), + ) => { + dst[dst_start..dst_start + count] + .clone_from_slice(&src[src_start..src_start + count]); + } + _ => panic!("descriptor copy has wrong resource type"), + } + } } #[derive(Clone)] @@ -475,41 +597,3 @@ where DescriptorSetWithOffsets::new(descriptor_set, std::iter::empty()) } } - -#[derive(Clone, Copy, Debug)] -pub enum DescriptorSetCreationError { - DescriptorSetUpdateError(DescriptorSetUpdateError), - OomError(OomError), -} - -impl Error for DescriptorSetCreationError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::DescriptorSetUpdateError(err) => Some(err), - Self::OomError(err) => Some(err), - } - } -} - -impl Display for DescriptorSetCreationError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::DescriptorSetUpdateError(_) => { - write!(f, "an error occurred while updating the descriptor set") - } - Self::OomError(_) => write!(f, "out of memory"), - } - } -} - -impl From for DescriptorSetCreationError { - fn from(err: DescriptorSetUpdateError) -> Self { - Self::DescriptorSetUpdateError(err) - } -} - -impl From for DescriptorSetCreationError { - fn from(err: OomError) -> Self { - Self::OomError(err) - } -} diff --git a/vulkano/src/descriptor_set/persistent.rs b/vulkano/src/descriptor_set/persistent.rs index cfa3f07685..137fb79964 100644 --- a/vulkano/src/descriptor_set/persistent.rs +++ b/vulkano/src/descriptor_set/persistent.rs @@ -21,15 +21,17 @@ //! # Examples //! TODO: +use super::CopyDescriptorSet; use crate::{ descriptor_set::{ allocator::{DescriptorSetAlloc, DescriptorSetAllocator, StandardDescriptorSetAlloc}, + layout::DescriptorSetLayoutCreateFlags, update::WriteDescriptorSet, - DescriptorSet, DescriptorSetCreationError, DescriptorSetInner, DescriptorSetLayout, - DescriptorSetResources, UnsafeDescriptorSet, + DescriptorSet, DescriptorSetInner, DescriptorSetLayout, DescriptorSetResources, + UnsafeDescriptorSet, }, device::{Device, DeviceOwned}, - VulkanObject, + VulkanError, VulkanObject, }; use std::{ hash::{Hash, Hasher}, @@ -51,11 +53,12 @@ impl PersistentDescriptorSet { allocator: &A, layout: Arc, descriptor_writes: impl IntoIterator, - ) -> Result>, DescriptorSetCreationError> + descriptor_copies: impl IntoIterator, + ) -> Result>, VulkanError> where A: DescriptorSetAllocator + ?Sized, { - Self::new_variable(allocator, layout, 0, descriptor_writes) + Self::new_variable(allocator, layout, 0, descriptor_writes, descriptor_copies) } /// Creates and returns a new descriptor set with the requested variable descriptor count, @@ -70,12 +73,15 @@ impl PersistentDescriptorSet { layout: Arc, variable_descriptor_count: u32, descriptor_writes: impl IntoIterator, - ) -> Result>, DescriptorSetCreationError> + descriptor_copies: impl IntoIterator, + ) -> Result>, VulkanError> where A: DescriptorSetAllocator + ?Sized, { assert!( - !layout.push_descriptor(), + !layout + .flags() + .intersects(DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR), "the provided descriptor set layout is for push descriptors, and cannot be used to \ build a descriptor set object", ); @@ -96,6 +102,7 @@ impl PersistentDescriptorSet { layout, variable_descriptor_count, descriptor_writes, + descriptor_copies, )?; Ok(Arc::new(PersistentDescriptorSet { alloc, inner })) diff --git a/vulkano/src/descriptor_set/pool.rs b/vulkano/src/descriptor_set/pool.rs index 167cd037f3..4f6e7443f7 100644 --- a/vulkano/src/descriptor_set/pool.rs +++ b/vulkano/src/descriptor_set/pool.rs @@ -9,12 +9,12 @@ use crate::{ descriptor_set::{ - layout::{DescriptorSetLayout, DescriptorType}, + layout::{DescriptorSetLayout, DescriptorSetLayoutCreateFlags, DescriptorType}, sys::UnsafeDescriptorSet, }, device::{Device, DeviceOwned}, macros::impl_id_counter, - OomError, RuntimeError, Version, VulkanObject, + RuntimeError, Version, VulkanObject, }; use ahash::HashMap; use smallvec::SmallVec; @@ -57,7 +57,7 @@ impl DescriptorPool { pub fn new( device: Arc, create_info: DescriptorPoolCreateInfo, - ) -> Result { + ) -> Result { let DescriptorPoolCreateInfo { max_sets, pool_sizes, @@ -200,7 +200,10 @@ impl DescriptorPool { .into_iter() .map(|info| { assert_eq!(self.device.handle(), info.layout.device().handle(),); - debug_assert!(!info.layout.push_descriptor()); + debug_assert!(!info + .layout + .flags() + .intersects(DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR)); debug_assert!( info.variable_descriptor_count <= info.layout.variable_descriptor_count() ); @@ -286,7 +289,7 @@ impl DescriptorPool { pub unsafe fn free_descriptor_sets( &self, descriptor_sets: impl IntoIterator, - ) -> Result<(), OomError> { + ) -> Result<(), RuntimeError> { let sets: SmallVec<[_; 8]> = descriptor_sets.into_iter().map(|s| s.handle()).collect(); if !sets.is_empty() { let fns = self.device.fns(); @@ -307,7 +310,7 @@ impl DescriptorPool { /// /// This destroys all descriptor sets and empties the pool. #[inline] - pub unsafe fn reset(&self) -> Result<(), OomError> { + pub unsafe fn reset(&self) -> Result<(), RuntimeError> { let fns = self.device.fns(); (fns.v1_0.reset_descriptor_pool)( self.device.handle(), diff --git a/vulkano/src/descriptor_set/sys.rs b/vulkano/src/descriptor_set/sys.rs index 6eb836b82f..464ee9d0c2 100644 --- a/vulkano/src/descriptor_set/sys.rs +++ b/vulkano/src/descriptor_set/sys.rs @@ -9,6 +9,7 @@ //! Low-level descriptor set. +use super::CopyDescriptorSet; use crate::{ descriptor_set::{ layout::DescriptorSetLayout, @@ -22,7 +23,6 @@ use smallvec::SmallVec; use std::{ fmt::{Debug, Error as FmtError, Formatter}, num::NonZeroU64, - ptr, }; /// Low-level descriptor set. @@ -56,30 +56,27 @@ impl UnsafeDescriptorSet { /// - Updating a descriptor set obeys synchronization rules that aren't checked here. Once a /// command buffer contains a pointer/reference to a descriptor set, it is illegal to write /// to it. - pub unsafe fn write<'a>( + pub unsafe fn update<'a>( &mut self, layout: &DescriptorSetLayout, - writes: impl IntoIterator, + descriptor_writes: impl IntoIterator, + descriptor_copies: impl IntoIterator, ) { struct PerDescriptorWrite { + write_info: DescriptorWriteInfo, acceleration_structures: ash::vk::WriteDescriptorSetAccelerationStructureKHR, } - let writes_iter = writes.into_iter(); + let writes_iter = descriptor_writes.into_iter(); let (lower_size_bound, _) = writes_iter.size_hint(); - let mut infos_vk: SmallVec<[_; 8]> = SmallVec::with_capacity(lower_size_bound); let mut writes_vk: SmallVec<[_; 8]> = SmallVec::with_capacity(lower_size_bound); let mut per_writes_vk: SmallVec<[_; 8]> = SmallVec::with_capacity(lower_size_bound); for write in writes_iter { let layout_binding = &layout.bindings()[&write.binding()]; - - infos_vk.push(write.to_vulkan_info(layout_binding.descriptor_type)); - writes_vk.push(write.to_vulkan( - ash::vk::DescriptorSet::null(), - layout_binding.descriptor_type, - )); + writes_vk.push(write.to_vulkan(self.handle, layout_binding.descriptor_type)); per_writes_vk.push(PerDescriptorWrite { + write_info: write.to_vulkan_info(layout_binding.descriptor_type), acceleration_structures: Default::default(), }); } @@ -90,12 +87,8 @@ impl UnsafeDescriptorSet { return; } - for ((info_vk, write_vk), per_write_vk) in infos_vk - .iter() - .zip(writes_vk.iter_mut()) - .zip(per_writes_vk.iter_mut()) - { - match info_vk { + for (write_vk, per_write_vk) in writes_vk.iter_mut().zip(per_writes_vk.iter_mut()) { + match &mut per_write_vk.write_info { DescriptorWriteInfo::Image(info) => { write_vk.descriptor_count = info.len() as u32; write_vk.p_image_info = info.as_ptr(); @@ -123,20 +116,42 @@ impl UnsafeDescriptorSet { debug_assert!(write_vk.descriptor_count != 0); } - let fns = layout.device().fns(); + let copies_iter = descriptor_copies.into_iter(); + let (lower_size_bound, _) = copies_iter.size_hint(); + let mut copies_vk: SmallVec<[_; 8]> = SmallVec::with_capacity(lower_size_bound); + + for copy in copies_iter { + let &CopyDescriptorSet { + ref src_set, + src_binding, + src_first_array_element, + dst_binding, + dst_first_array_element, + descriptor_count, + _ne: _, + } = copy; + + copies_vk.push(ash::vk::CopyDescriptorSet { + src_set: src_set.inner().handle(), + src_binding, + src_array_element: src_first_array_element, + dst_set: self.handle, + dst_binding, + dst_array_element: dst_first_array_element, + descriptor_count, + ..Default::default() + }); + } + let fns = layout.device().fns(); (fns.v1_0.update_descriptor_sets)( layout.device().handle(), writes_vk.len() as u32, writes_vk.as_ptr(), - 0, - ptr::null(), + copies_vk.len() as u32, + copies_vk.as_ptr(), ); } - - // TODO: add copying from other descriptor sets - // add a `copy` method that just takes a copy, and an `update` method that takes both - // writes and copies and that actually performs the operation } unsafe impl VulkanObject for UnsafeDescriptorSet { diff --git a/vulkano/src/descriptor_set/update.rs b/vulkano/src/descriptor_set/update.rs index dea3cbff5f..552bad01e9 100644 --- a/vulkano/src/descriptor_set/update.rs +++ b/vulkano/src/descriptor_set/update.rs @@ -7,25 +7,23 @@ // notice may not be copied, modified, or distributed except // according to those terms. -use super::layout::{DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorType}; +use super::{ + layout::{DescriptorSetLayout, DescriptorType}, + DescriptorSet, +}; use crate::{ acceleration_structure::{AccelerationStructure, AccelerationStructureType}, buffer::{view::BufferView, BufferUsage, Subbuffer}, + descriptor_set::layout::{DescriptorBindingFlags, DescriptorSetLayoutCreateFlags}, device::DeviceOwned, image::{ view::ImageViewType, ImageAspects, ImageLayout, ImageType, ImageUsage, ImageViewAbstract, }, - sampler::{Sampler, SamplerImageViewIncompatibleError}, - DeviceSize, RequiresOneOf, VulkanObject, + sampler::Sampler, + DeviceSize, RequiresOneOf, ValidationError, VulkanObject, }; use smallvec::SmallVec; -use std::{ - error::Error, - fmt::{Display, Error as FmtError, Formatter}, - ops::Range, - ptr, - sync::Arc, -}; +use std::{ops::Range, ptr, sync::Arc}; /// Represents a single write operation to the binding of a descriptor set. /// @@ -40,7 +38,7 @@ use std::{ pub struct WriteDescriptorSet { binding: u32, first_array_element: u32, - pub(crate) elements: WriteDescriptorSetElements, // public so that the image layouts can be changed + elements: WriteDescriptorSetElements, } impl WriteDescriptorSet { @@ -353,542 +351,810 @@ impl WriteDescriptorSet { &self.elements } - pub(crate) fn to_vulkan_info(&self, descriptor_type: DescriptorType) -> DescriptorWriteInfo { - match &self.elements { - WriteDescriptorSetElements::None(num_elements) => { - debug_assert!(matches!(descriptor_type, DescriptorType::Sampler)); - DescriptorWriteInfo::Image( - std::iter::repeat_with(|| ash::vk::DescriptorImageInfo { - sampler: ash::vk::Sampler::null(), - image_view: ash::vk::ImageView::null(), - image_layout: ash::vk::ImageLayout::UNDEFINED, - }) - .take(*num_elements as usize) - .collect(), - ) + pub(crate) fn validate( + &self, + layout: &DescriptorSetLayout, + variable_descriptor_count: u32, + ) -> Result<(), ValidationError> { + fn provided_element_type(elements: &WriteDescriptorSetElements) -> &'static str { + match elements { + WriteDescriptorSetElements::None(_) => "none", + WriteDescriptorSetElements::Buffer(_) => "buffer", + WriteDescriptorSetElements::BufferView(_) => "buffer_view", + WriteDescriptorSetElements::ImageView(_) => "image_view", + WriteDescriptorSetElements::ImageViewSampler(_) => "image_view_sampler", + WriteDescriptorSetElements::Sampler(_) => "sampler", + WriteDescriptorSetElements::AccelerationStructure(_) => "acceleration_structure", } - WriteDescriptorSetElements::Buffer(elements) => { - debug_assert!(matches!( - descriptor_type, - DescriptorType::UniformBuffer - | DescriptorType::StorageBuffer - | DescriptorType::UniformBufferDynamic - | DescriptorType::StorageBufferDynamic - )); - DescriptorWriteInfo::Buffer( - elements - .iter() - .map(|buffer_info| { - let DescriptorBufferInfo { buffer, range } = buffer_info; - - debug_assert!(!range.is_empty()); - debug_assert!(range.end <= buffer.buffer().size()); + } - ash::vk::DescriptorBufferInfo { - buffer: buffer.buffer().handle(), - offset: buffer.offset() + range.start, - range: range.end - range.start, - } - }) - .collect(), - ) - } - WriteDescriptorSetElements::BufferView(elements) => { - debug_assert!(matches!( - descriptor_type, - DescriptorType::UniformTexelBuffer | DescriptorType::StorageTexelBuffer - )); - DescriptorWriteInfo::BufferView( - elements - .iter() - .map(|buffer_view| buffer_view.handle()) - .collect(), - ) + let &Self { + binding, + first_array_element, + ref elements, + } = self; + + let device = layout.device(); + + let layout_binding = match layout.bindings().get(&binding) { + Some(layout_binding) => layout_binding, + None => { + return Err(ValidationError { + context: "binding".into(), + problem: "does not exist in the descriptor set layout".into(), + vuids: &["VUID-VkWriteDescriptorSet-dstBinding-00315"], + ..Default::default() + }); } - WriteDescriptorSetElements::ImageView(elements) => { - // Note: combined image sampler can occur with immutable samplers - debug_assert!(matches!( - descriptor_type, - DescriptorType::CombinedImageSampler - | DescriptorType::SampledImage - | DescriptorType::StorageImage - | DescriptorType::InputAttachment - )); - DescriptorWriteInfo::Image( - elements - .iter() - .map(|image_view_info| { - let &DescriptorImageViewInfo { - ref image_view, - image_layout, - } = image_view_info; + }; + + let max_descriptor_count = if layout_binding + .binding_flags + .intersects(DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT) + { + variable_descriptor_count + } else { + layout_binding.descriptor_count + }; + + let array_element_count = elements.len(); + debug_assert!(array_element_count != 0); + + // VUID-VkWriteDescriptorSet-dstArrayElement-00321 + if first_array_element + array_element_count > max_descriptor_count { + return Err(ValidationError { + problem: "first_array_element + the number of provided elements is greater than \ + the number of descriptors in the descriptor set binding" + .into(), + vuids: &["VUID-VkWriteDescriptorSet-dstArrayElement-00321"], + ..Default::default() + }); + } - ash::vk::DescriptorImageInfo { - sampler: ash::vk::Sampler::null(), - image_view: image_view.handle(), - image_layout: image_layout.into(), + let validate_image_view = + |image_view: &dyn ImageViewAbstract, index: usize| -> Result<(), ValidationError> { + if image_view.image().inner().dimensions().image_type() == ImageType::Dim3d { + if image_view.view_type() == ImageViewType::Dim2dArray { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the image view's type is ImageViewType::Dim2dArray, and was \ + created from a 3D image" + .into(), + vuids: &["VUID-VkDescriptorImageInfo-imageView-00343"], + ..Default::default() + }); + } else if image_view.view_type() == ImageViewType::Dim2d { + // VUID-VkDescriptorImageInfo-imageView-07796 + // Already checked at image view creation by + // VUID-VkImageViewCreateInfo-image-06728 + + match layout_binding.descriptor_type { + DescriptorType::StorageImage => { + if !device.enabled_features().image2_d_view_of3_d { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: format!( + "the descriptor type is DescriptorType::{:?}, and the \ + image view's type is ImageViewType::Dim2d, + and was created from a 3D image", + layout_binding.descriptor_type, + ) + .into(), + requires_one_of: Some(RequiresOneOf { + features: &["image2_d_view_of3_d"], + ..Default::default() + }), + vuids: &["VUID-VkDescriptorImageInfo-descriptorType-06713"], + }); + } } - }) - .collect(), - ) - } - WriteDescriptorSetElements::ImageViewSampler(elements) => { - debug_assert!(matches!( - descriptor_type, - DescriptorType::CombinedImageSampler - )); - DescriptorWriteInfo::Image( + DescriptorType::SampledImage | DescriptorType::CombinedImageSampler => { + if !device.enabled_features().sampler2_d_view_of3_d { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: format!( + "the descriptor type is DescriptorType::{:?}, and the \ + image view's type is ImageViewType::Dim2d, + and was created from a 3D image", + layout_binding.descriptor_type, + ) + .into(), + requires_one_of: Some(RequiresOneOf { + features: &["sampler2_d_view_of3_d"], + ..Default::default() + }), + vuids: &["VUID-VkDescriptorImageInfo-descriptorType-06714"], + }); + } + } + _ => { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the descriptor type is not \ + DescriptorType::StorageImage, \ + DescriptorType::SampledImage \ + or DescriptorType::CombinedImageSampler,\ + and the image view's type is ImageViewType::Dim2D, \ + and was created from a 3D image" + .into(), + vuids: &["VUID-VkDescriptorImageInfo-imageView-07795"], + ..Default::default() + }); + } + } + } + } + + if image_view + .subresource_range() + .aspects + .contains(ImageAspects::DEPTH | ImageAspects::STENCIL) + { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the image view's aspects include both a depth and a \ + stencil component" + .into(), + vuids: &["VUID-VkDescriptorImageInfo-imageView-01976"], + ..Default::default() + }); + } + + Ok(()) + }; + + let default_image_layout = layout_binding.descriptor_type.default_image_layout(); + + match layout_binding.descriptor_type { + DescriptorType::Sampler => { + if !layout_binding.immutable_samplers.is_empty() { + if layout + .flags() + .intersects(DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR) + { + // For push descriptors, we must write a dummy element. + if let WriteDescriptorSetElements::None(_) = elements { + // Do nothing + } else { + return Err(ValidationError { + context: "elements".into(), + problem: format!( + "contains `{}` elements, but the descriptor set \ + binding requires `none` elements", + provided_element_type(elements) + ) + .into(), + // vuids? + ..Default::default() + }); + } + } else { + // For regular descriptors, no element must be written. + return Err(ValidationError { + context: "binding".into(), + problem: "no descriptors must be written to this \ + descriptor set binding" + .into(), + // vuids? + ..Default::default() + }); + } + } + + let elements = if let WriteDescriptorSetElements::Sampler(elements) = elements { elements - .iter() - .map(|(image_view_info, sampler)| { - let &DescriptorImageViewInfo { - ref image_view, - image_layout, - } = image_view_info; + } else { + return Err(ValidationError { + context: "elements".into(), + problem: format!( + "contains `{}` elements, but the descriptor set \ + binding requires `sampler` elements", + provided_element_type(elements) + ) + .into(), + // vuids? + ..Default::default() + }); + }; - ash::vk::DescriptorImageInfo { - sampler: sampler.handle(), - image_view: image_view.handle(), - image_layout: image_layout.into(), - } - }) - .collect(), - ) + for (index, sampler) in elements.iter().enumerate() { + assert_eq!(device, sampler.device()); + + if sampler.sampler_ycbcr_conversion().is_some() { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the descriptor type is not \ + DescriptorType::CombinedImageSampler, and the sampler has a \ + sampler YCbCr conversion" + .into(), + // vuids? + ..Default::default() + }); + } + + if device.enabled_extensions().khr_portability_subset + && !device.enabled_features().mutable_comparison_samplers + && sampler.compare().is_some() + { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "this device is a portability subset device, and \ + the sampler has depth comparison enabled" + .into(), + requires_one_of: Some(RequiresOneOf { + features: &["mutable_comparison_samplers"], + ..Default::default() + }), + vuids: &["VUID-VkDescriptorImageInfo-mutableComparisonSamplers-04450"], + }); + } + } } - WriteDescriptorSetElements::Sampler(elements) => { - debug_assert!(matches!(descriptor_type, DescriptorType::Sampler)); - DescriptorWriteInfo::Image( - elements - .iter() - .map(|sampler| ash::vk::DescriptorImageInfo { - sampler: sampler.handle(), - image_view: ash::vk::ImageView::null(), - image_layout: ash::vk::ImageLayout::UNDEFINED, - }) - .collect(), - ) + + DescriptorType::CombinedImageSampler => { + if layout_binding.immutable_samplers.is_empty() { + let elements = + if let WriteDescriptorSetElements::ImageViewSampler(elements) = elements { + elements + } else { + return Err(ValidationError { + context: "elements".into(), + problem: format!( + "contains `{}` elements, but the descriptor set \ + binding requires `image_view_sampler` elements", + provided_element_type(elements) + ) + .into(), + // vuids? + ..Default::default() + }); + }; + + for (index, (image_view_info, sampler)) in elements.iter().enumerate() { + let &DescriptorImageViewInfo { + ref image_view, + mut image_layout, + } = image_view_info; + + if image_layout == ImageLayout::Undefined { + image_layout = default_image_layout; + } + + assert_eq!(device, image_view.device()); + assert_eq!(device, sampler.device()); + + validate_image_view(image_view.as_ref(), index)?; + + if !image_view.usage().intersects(ImageUsage::SAMPLED) { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the descriptor type is \ + DescriptorType::SampledImage or \ + DescriptorType::CombinedImageSampler, and the image was not \ + created with the ImageUsage::SAMPLED usage" + .into(), + vuids: &["VUID-VkWriteDescriptorSet-descriptorType-00337"], + ..Default::default() + }); + } + + if !matches!( + image_layout, + ImageLayout::DepthStencilReadOnlyOptimal + | ImageLayout::ShaderReadOnlyOptimal + | ImageLayout::General + | ImageLayout::DepthReadOnlyStencilAttachmentOptimal + | ImageLayout::DepthAttachmentStencilReadOnlyOptimal, + ) { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the descriptor type is \ + DescriptorType::CombinedImageSampler, and the image layout is \ + not valid with this type" + .into(), + vuids: &["VUID-VkWriteDescriptorSet-descriptorType-04150"], + ..Default::default() + }); + } + + if device.enabled_extensions().khr_portability_subset + && !device.enabled_features().mutable_comparison_samplers + && sampler.compare().is_some() + { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "this device is a portability subset device, and \ + the sampler has depth comparison enabled" + .into(), + requires_one_of: Some(RequiresOneOf { + features: &["mutable_comparison_samplers"], + ..Default::default() + }), + vuids: &[ + "VUID-VkDescriptorImageInfo-mutableComparisonSamplers-04450", + ], + }); + } + + if image_view.sampler_ycbcr_conversion().is_some() { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the image view has a sampler YCbCr conversion, and the \ + descriptor set layout was not created with immutable samplers" + .into(), + vuids: &["VUID-VkWriteDescriptorSet-descriptorType-02738"], + ..Default::default() + }); + } + + if sampler.sampler_ycbcr_conversion().is_some() { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the sampler has a sampler YCbCr conversion".into(), + // vuids? + ..Default::default() + }); + } + + sampler + .check_can_sample(image_view.as_ref()) + .map_err(|err| err.add_context(format!("elements[{}]", index)))?; + } + } else { + let elements = if let WriteDescriptorSetElements::ImageView(elements) = elements + { + elements + } else { + return Err(ValidationError { + context: "elements".into(), + problem: format!( + "contains `{}` elements, but the descriptor set \ + binding requires `image_view` elements", + provided_element_type(elements) + ) + .into(), + ..Default::default() + }); + }; + + let immutable_samplers = &layout_binding.immutable_samplers + [first_array_element as usize..][..array_element_count as usize]; + + for (index, (image_view_info, sampler)) in + elements.iter().zip(immutable_samplers).enumerate() + { + let &DescriptorImageViewInfo { + ref image_view, + mut image_layout, + } = image_view_info; + + if image_layout == ImageLayout::Undefined { + image_layout = default_image_layout; + } + + assert_eq!(device, image_view.device()); + + validate_image_view(image_view.as_ref(), index)?; + + if !image_view.usage().intersects(ImageUsage::SAMPLED) { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the descriptor type is \ + DescriptorType::SampledImage or \ + DescriptorType::CombinedImageSampler, and the image was not \ + created with the ImageUsage::SAMPLED usage" + .into(), + vuids: &["VUID-VkWriteDescriptorSet-descriptorType-00337"], + ..Default::default() + }); + } + + if !matches!( + image_layout, + ImageLayout::DepthStencilReadOnlyOptimal + | ImageLayout::ShaderReadOnlyOptimal + | ImageLayout::General + | ImageLayout::DepthReadOnlyStencilAttachmentOptimal + | ImageLayout::DepthAttachmentStencilReadOnlyOptimal, + ) { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the descriptor type is \ + DescriptorType::CombinedImageSampler, and the image layout is \ + not valid with this type" + .into(), + vuids: &["VUID-VkWriteDescriptorSet-descriptorType-04150"], + ..Default::default() + }); + } + + sampler + .check_can_sample(image_view.as_ref()) + .map_err(|err| err.add_context(format!("elements[{}]", index)))?; + } + } } - WriteDescriptorSetElements::AccelerationStructure(elements) => { - debug_assert!(matches!( - descriptor_type, - DescriptorType::AccelerationStructure - )); - DescriptorWriteInfo::AccelerationStructure( + + DescriptorType::SampledImage => { + let elements = if let WriteDescriptorSetElements::ImageView(elements) = elements { elements - .iter() - .map(|acceleration_structure| acceleration_structure.handle()) - .collect(), - ) - } - } - } + } else { + return Err(ValidationError { + context: "elements".into(), + problem: format!( + "contains `{}` elements, but the descriptor set \ + binding requires `image_view` elements", + provided_element_type(elements) + ) + .into(), + // vuids? + ..Default::default() + }); + }; - pub(crate) fn to_vulkan( - &self, - dst_set: ash::vk::DescriptorSet, - descriptor_type: DescriptorType, - ) -> ash::vk::WriteDescriptorSet { - ash::vk::WriteDescriptorSet { - dst_set, - dst_binding: self.binding, - dst_array_element: self.first_array_element, - descriptor_count: 0, - descriptor_type: descriptor_type.into(), - p_image_info: ptr::null(), - p_buffer_info: ptr::null(), - p_texel_buffer_view: ptr::null(), - ..Default::default() - } - } -} + for (index, image_view_info) in elements.iter().enumerate() { + let &DescriptorImageViewInfo { + ref image_view, + mut image_layout, + } = image_view_info; -/// The elements held by a `WriteDescriptorSet`. -#[derive(Clone, Debug)] -pub enum WriteDescriptorSetElements { - None(u32), - Buffer(SmallVec<[DescriptorBufferInfo; 1]>), - BufferView(SmallVec<[Arc; 1]>), - ImageView(SmallVec<[DescriptorImageViewInfo; 1]>), - ImageViewSampler(SmallVec<[(DescriptorImageViewInfo, Arc); 1]>), - Sampler(SmallVec<[Arc; 1]>), - AccelerationStructure(SmallVec<[Arc; 1]>), -} + if image_layout == ImageLayout::Undefined { + image_layout = default_image_layout; + } -impl WriteDescriptorSetElements { - /// Returns the number of elements. - #[inline] - pub fn len(&self) -> u32 { - match self { - Self::None(num_elements) => *num_elements, - Self::Buffer(elements) => elements.len() as u32, - Self::BufferView(elements) => elements.len() as u32, - Self::ImageView(elements) => elements.len() as u32, - Self::ImageViewSampler(elements) => elements.len() as u32, - Self::Sampler(elements) => elements.len() as u32, - Self::AccelerationStructure(elements) => elements.len() as u32, - } - } -} + assert_eq!(device, image_view.device()); -/// Parameters to write a buffer reference to a descriptor. -#[derive(Clone, Debug)] -pub struct DescriptorBufferInfo { - /// The buffer to write to the descriptor. - pub buffer: Subbuffer<[u8]>, + validate_image_view(image_view.as_ref(), index)?; - /// The slice of bytes in `buffer` that will be made available to the shader. - /// `range` must not be outside the range `buffer`. - /// - /// For dynamic buffer bindings, `range` specifies the slice that is to be bound if the - /// dynamic offset were zero. When binding the descriptor set, the effective value of `range` - /// shifts forward by the offset that was provided. For example, if `range` is specified as - /// `0..8` when writing the descriptor set, and then when binding the descriptor set the - /// offset `16` is used, then the range of `buffer` that will actually be bound is `16..24`. - pub range: Range, -} - -/// Parameters to write an image view reference to a descriptor. -#[derive(Clone, Debug)] -pub struct DescriptorImageViewInfo { - /// The image view to write to the descriptor. - pub image_view: Arc, - - /// The layout that the image is expected to be in when it's accessed in the shader. - /// - /// Only certain layouts are allowed, depending on the type of descriptor. - /// - /// For `SampledImage`, `CombinedImageSampler` and `InputAttachment`: - /// - `General` - /// - `ShaderReadOnlyOptimal` - /// - `DepthStencilReadOnlyOptimal` - /// - `DepthReadOnlyStencilAttachmentOptimal` - /// - `DepthAttachmentStencilReadOnlyOptimal` - /// - /// For `StorageImage`: - /// - `General` - /// - /// If the `Undefined` layout is provided, then it will be automatically replaced with - /// `General` for `StorageImage` descriptors, and with `ShaderReadOnlyOptimal` for any other - /// descriptor type. - pub image_layout: ImageLayout, -} + if !image_view.usage().intersects(ImageUsage::SAMPLED) { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the descriptor type is \ + DescriptorType::SampledImage or \ + DescriptorType::CombinedImageSampler, and the image was not \ + created with the ImageUsage::SAMPLED usage" + .into(), + vuids: &["VUID-VkWriteDescriptorSet-descriptorType-00337"], + ..Default::default() + }); + } -#[derive(Clone, Debug)] -pub(crate) enum DescriptorWriteInfo { - Image(SmallVec<[ash::vk::DescriptorImageInfo; 1]>), - Buffer(SmallVec<[ash::vk::DescriptorBufferInfo; 1]>), - BufferView(SmallVec<[ash::vk::BufferView; 1]>), - AccelerationStructure(SmallVec<[ash::vk::AccelerationStructureKHR; 1]>), -} + if !matches!( + image_layout, + ImageLayout::DepthStencilReadOnlyOptimal + | ImageLayout::ShaderReadOnlyOptimal + | ImageLayout::General + | ImageLayout::DepthReadOnlyStencilAttachmentOptimal + | ImageLayout::DepthAttachmentStencilReadOnlyOptimal, + ) { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the descriptor type is \ + DescriptorType::SampledImage, and the image layout is \ + not valid with this type" + .into(), + vuids: &["VUID-VkWriteDescriptorSet-descriptorType-04149"], + ..Default::default() + }); + } -pub(crate) fn set_descriptor_write_image_layouts( - write: &mut WriteDescriptorSet, - layout: &DescriptorSetLayout, -) { - let default_layout = if let Some(layout_binding) = layout.bindings().get(&write.binding()) { - match layout_binding.descriptor_type { - DescriptorType::CombinedImageSampler - | DescriptorType::SampledImage - | DescriptorType::InputAttachment => ImageLayout::ShaderReadOnlyOptimal, - DescriptorType::StorageImage => ImageLayout::General, - _ => return, - } - } else { - return; - }; - - match &mut write.elements { - WriteDescriptorSetElements::ImageView(elements) => { - for image_view_info in elements { - let DescriptorImageViewInfo { - image_view: _, - image_layout, - } = image_view_info; - - if *image_layout == ImageLayout::Undefined { - *image_layout = default_layout; - } - } - } - WriteDescriptorSetElements::ImageViewSampler(elements) => { - for (image_view_info, _sampler) in elements { - let DescriptorImageViewInfo { - image_view: _, - image_layout, - } = image_view_info; - - if *image_layout == ImageLayout::Undefined { - *image_layout = default_layout; + if image_view.sampler_ycbcr_conversion().is_some() { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the descriptor type is DescriptorType::SampledImage, and \ + the image view has a sampler YCbCr conversion" + .into(), + vuids: &["VUID-VkWriteDescriptorSet-descriptorType-01946"], + ..Default::default() + }); + } } } - } - _ => (), - } -} -pub(crate) fn validate_descriptor_write<'a>( - write: &WriteDescriptorSet, - layout: &'a DescriptorSetLayout, - variable_descriptor_count: u32, -) -> Result<&'a DescriptorSetLayoutBinding, DescriptorSetUpdateError> { - fn provided_element_type(elements: &WriteDescriptorSetElements) -> &'static str { - match elements { - WriteDescriptorSetElements::None(_) => "none", - WriteDescriptorSetElements::Buffer(_) => "buffer", - WriteDescriptorSetElements::BufferView(_) => "buffer_view", - WriteDescriptorSetElements::ImageView(_) => "image_view", - WriteDescriptorSetElements::ImageViewSampler(_) => "image_view_sampler", - WriteDescriptorSetElements::Sampler(_) => "sampler", - WriteDescriptorSetElements::AccelerationStructure(_) => "acceleration_structure", - } - } + DescriptorType::StorageImage => { + let elements = if let WriteDescriptorSetElements::ImageView(elements) = elements { + elements + } else { + return Err(ValidationError { + context: "elements".into(), + problem: format!( + "contains `{}` elements, but the descriptor set \ + binding requires `image_view` elements", + provided_element_type(elements) + ) + .into(), + // vuids? + ..Default::default() + }); + }; + + for (index, image_view_info) in elements.iter().enumerate() { + let &DescriptorImageViewInfo { + ref image_view, + mut image_layout, + } = image_view_info; - let device = layout.device(); + if image_layout == ImageLayout::Undefined { + image_layout = default_image_layout; + } - let layout_binding = match layout.bindings().get(&write.binding()) { - Some(binding) => binding, - None => { - return Err(DescriptorSetUpdateError::InvalidBinding { - binding: write.binding(), - }) - } - }; + assert_eq!(device, image_view.device()); - let max_descriptor_count = if layout_binding.variable_descriptor_count { - variable_descriptor_count - } else { - layout_binding.descriptor_count - }; + validate_image_view(image_view.as_ref(), index)?; + + if !image_view.usage().intersects(ImageUsage::STORAGE) { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the descriptor type is \ + DescriptorType::StorageImage, and the image was not \ + created with the ImageUsage::STORAGE usage" + .into(), + vuids: &["VUID-VkWriteDescriptorSet-descriptorType-00339"], + ..Default::default() + }); + } - let binding = write.binding(); - let elements = write.elements(); - let num_elements = elements.len(); - debug_assert!(num_elements != 0); + if !matches!(image_layout, ImageLayout::General) { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the descriptor type is \ + DescriptorType::StorageImage, and the image layout is \ + not valid with this type" + .into(), + vuids: &["VUID-VkWriteDescriptorSet-descriptorType-04152"], + ..Default::default() + }); + } - let descriptor_range_start = write.first_array_element(); - let descriptor_range_end = descriptor_range_start + num_elements; + if !image_view.component_mapping().is_identity() { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the descriptor type is DescriptorType::StorageImage or \ + DescriptorType::InputAttachment, and the image view is not \ + identity swizzled" + .into(), + vuids: &["VUID-VkWriteDescriptorSet-descriptorType-00336"], + ..Default::default() + }); + } - if descriptor_range_end > max_descriptor_count { - return Err(DescriptorSetUpdateError::ArrayIndexOutOfBounds { - binding, - available_count: max_descriptor_count, - written_count: descriptor_range_end, - }); - } + if image_view.sampler_ycbcr_conversion().is_some() { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "image_view has a sampler YCbCr conversion".into(), + // vuids? + ..Default::default() + }); + } + } + } - match layout_binding.descriptor_type { - DescriptorType::Sampler => { - if layout_binding.immutable_samplers.is_empty() { - let elements = if let WriteDescriptorSetElements::Sampler(elements) = elements { + DescriptorType::UniformTexelBuffer => { + let elements = if let WriteDescriptorSetElements::BufferView(elements) = elements { elements } else { - return Err(DescriptorSetUpdateError::IncompatibleElementType { - binding, - provided_element_type: provided_element_type(elements), - allowed_element_types: &["sampler"], + return Err(ValidationError { + context: "elements".into(), + problem: format!( + "contains `{}` elements, but the descriptor set \ + binding requires `buffer_view` elements", + provided_element_type(elements) + ) + .into(), + // vuids? + ..Default::default() }); }; - for (index, sampler) in elements.iter().enumerate() { - assert_eq!(device, sampler.device()); + for (index, buffer_view) in elements.iter().enumerate() { + assert_eq!(device, buffer_view.device()); - if sampler.sampler_ycbcr_conversion().is_some() { - return Err(DescriptorSetUpdateError::SamplerHasSamplerYcbcrConversion { - binding: write.binding(), - index: descriptor_range_start + index as u32, + if !buffer_view + .buffer() + .buffer() + .usage() + .intersects(BufferUsage::UNIFORM_TEXEL_BUFFER) + { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the descriptor type is DescriptorType::UniformTexelBuffer, \ + and the buffer was not created with the \ + BufferUsage::UNIFORM_TEXEL_BUFFER usage" + .into(), + vuids: &["VUID-VkWriteDescriptorSet-descriptorType-00334"], + ..Default::default() }); } } - } else if layout.push_descriptor() { - // For push descriptors, we must write a dummy element. - if let WriteDescriptorSetElements::None(_) = elements { - // Do nothing + } + + DescriptorType::StorageTexelBuffer => { + let elements = if let WriteDescriptorSetElements::BufferView(elements) = elements { + elements } else { - return Err(DescriptorSetUpdateError::IncompatibleElementType { - binding, - provided_element_type: provided_element_type(elements), - allowed_element_types: &["none"], + return Err(ValidationError { + context: "elements".into(), + problem: format!( + "contains `{}` elements, but the descriptor set \ + binding requires `buffer_view` elements", + provided_element_type(elements) + ) + .into(), + // vuids? + ..Default::default() }); - } - } else { - // For regular descriptors, no element must be written. - return Err(DescriptorSetUpdateError::IncompatibleElementType { - binding, - provided_element_type: provided_element_type(elements), - allowed_element_types: &[], - }); - } - } + }; - DescriptorType::CombinedImageSampler => { - if layout_binding.immutable_samplers.is_empty() { - let elements = - if let WriteDescriptorSetElements::ImageViewSampler(elements) = elements { - elements - } else { - return Err(DescriptorSetUpdateError::IncompatibleElementType { - binding, - provided_element_type: provided_element_type(elements), - allowed_element_types: &["image_view_sampler"], + for (index, buffer_view) in elements.iter().enumerate() { + assert_eq!(device, buffer_view.device()); + + // TODO: storage_texel_buffer_atomic + if !buffer_view + .buffer() + .buffer() + .usage() + .intersects(BufferUsage::STORAGE_TEXEL_BUFFER) + { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the descriptor type is DescriptorType::StorageTexelBuffer, \ + and the buffer was not created with the \ + BufferUsage::STORAGE_TEXEL_BUFFER usage" + .into(), + vuids: &["VUID-VkWriteDescriptorSet-descriptorType-00335"], + ..Default::default() }); - }; + } + } + } - for (index, (image_view_info, sampler)) in elements.iter().enumerate() { - let &DescriptorImageViewInfo { - ref image_view, - image_layout, - } = image_view_info; + DescriptorType::UniformBuffer | DescriptorType::UniformBufferDynamic => { + let elements = if let WriteDescriptorSetElements::Buffer(elements) = elements { + elements + } else { + return Err(ValidationError { + context: "elements".into(), + problem: format!( + "contains `{}` elements, but the descriptor set \ + binding requires `buffer` elements", + provided_element_type(elements) + ) + .into(), + // vuids? + ..Default::default() + }); + }; - assert_eq!(device, image_view.device()); - assert_eq!(device, sampler.device()); + for (index, buffer_info) in elements.iter().enumerate() { + let DescriptorBufferInfo { buffer, range } = buffer_info; - // VUID-VkWriteDescriptorSet-descriptorType-00337 - if !image_view.usage().intersects(ImageUsage::SAMPLED) { - return Err(DescriptorSetUpdateError::MissingUsage { - binding: write.binding(), - index: descriptor_range_start + index as u32, - usage: "sampled", - }); - } + assert_eq!(device, buffer.device()); - // VUID-VkDescriptorImageInfo-imageView-00343 - if matches!( - image_view.view_type(), - ImageViewType::Dim2d | ImageViewType::Dim2dArray - ) && image_view.image().inner().dimensions().image_type() == ImageType::Dim3d + if !buffer + .buffer() + .usage() + .intersects(BufferUsage::UNIFORM_BUFFER) { - return Err(DescriptorSetUpdateError::ImageView2dFrom3d { - binding: write.binding(), - index: descriptor_range_start + index as u32, + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the descriptor type is DescriptorType::UniformBuffer or \ + DescriptorType::UniformBufferDynamic, and the buffer was not \ + created with the BufferUsage::UNIFORM_BUFFER usage" + .into(), + vuids: &["VUID-VkWriteDescriptorSet-descriptorType-00330"], + ..Default::default() }); } - // VUID-VkDescriptorImageInfo-imageView-01976 - if image_view - .subresource_range() - .aspects - .contains(ImageAspects::DEPTH | ImageAspects::STENCIL) - { - return Err(DescriptorSetUpdateError::ImageViewDepthAndStencil { - binding: write.binding(), - index: descriptor_range_start + index as u32, - }); - } + assert!(!range.is_empty()); - // VUID-VkWriteDescriptorSet-descriptorType-04150 - if !matches!( - image_layout, - ImageLayout::DepthStencilReadOnlyOptimal - | ImageLayout::ShaderReadOnlyOptimal - | ImageLayout::General - | ImageLayout::DepthReadOnlyStencilAttachmentOptimal - | ImageLayout::DepthAttachmentStencilReadOnlyOptimal, - ) { - return Err(DescriptorSetUpdateError::ImageLayoutInvalid { - binding: write.binding(), - index: descriptor_range_start + index as u32, + if range.end > buffer.size() { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the end of the range is greater than the size of the buffer" + .into(), + vuids: &["VUID-VkDescriptorBufferInfo-range-00342"], + ..Default::default() }); } + } + } - // VUID-VkDescriptorImageInfo-mutableComparisonSamplers-04450 - if device.enabled_extensions().khr_portability_subset - && !device.enabled_features().mutable_comparison_samplers - && sampler.compare().is_some() - { - return Err(DescriptorSetUpdateError::RequirementNotMet { - binding: write.binding(), - index: descriptor_range_start + index as u32, - required_for: "this device is a portability subset device, and \ - `sampler.compare()` is `Some`", - requires_one_of: RequiresOneOf { - features: &["mutable_comparison_samplers"], - ..Default::default() - }, - }); - } + DescriptorType::StorageBuffer | DescriptorType::StorageBufferDynamic => { + let elements = if let WriteDescriptorSetElements::Buffer(elements) = elements { + elements + } else { + return Err(ValidationError { + context: "elements".into(), + problem: format!( + "contains `{}` elements, but the descriptor set \ + binding requires `buffer` elements", + provided_element_type(elements) + ) + .into(), + // vuids? + ..Default::default() + }); + }; - if image_view.sampler_ycbcr_conversion().is_some() { - return Err( - DescriptorSetUpdateError::ImageViewHasSamplerYcbcrConversion { - binding: write.binding(), - index: descriptor_range_start + index as u32, - }, - ); - } + for (index, buffer_info) in elements.iter().enumerate() { + let DescriptorBufferInfo { buffer, range } = buffer_info; - if sampler.sampler_ycbcr_conversion().is_some() { - return Err(DescriptorSetUpdateError::SamplerHasSamplerYcbcrConversion { - binding: write.binding(), - index: descriptor_range_start + index as u32, + assert_eq!(device, buffer.device()); + + if !buffer + .buffer() + .usage() + .intersects(BufferUsage::STORAGE_BUFFER) + { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the descriptor type is DescriptorType::StorageBuffer or \ + DescriptorType::StorageBufferDynamic, and the buffer was not \ + created with the BufferUsage::STORAGE_BUFFER usage" + .into(), + vuids: &["VUID-VkWriteDescriptorSet-descriptorType-00331"], + ..Default::default() }); } - if let Err(error) = sampler.check_can_sample(image_view.as_ref()) { - return Err(DescriptorSetUpdateError::ImageViewIncompatibleSampler { - binding: write.binding(), - index: descriptor_range_start + index as u32, - error, + assert!(!range.is_empty()); + + if range.end > buffer.size() { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the end of the range is greater than the size of the buffer" + .into(), + vuids: &["VUID-VkDescriptorBufferInfo-range-00342"], + ..Default::default() }); } } - } else { + } + + DescriptorType::InputAttachment => { let elements = if let WriteDescriptorSetElements::ImageView(elements) = elements { elements } else { - return Err(DescriptorSetUpdateError::IncompatibleElementType { - binding, - provided_element_type: provided_element_type(elements), - allowed_element_types: &["image_view"], + return Err(ValidationError { + context: "elements".into(), + problem: format!( + "contains `{}` elements, but the descriptor set \ + binding requires `image_view` elements", + provided_element_type(elements) + ) + .into(), + // vuids? + ..Default::default() }); }; - let immutable_samplers = &layout_binding.immutable_samplers - [descriptor_range_start as usize..descriptor_range_end as usize]; - - for (index, (image_view_info, sampler)) in - elements.iter().zip(immutable_samplers).enumerate() - { + for (index, image_view_info) in elements.iter().enumerate() { let &DescriptorImageViewInfo { ref image_view, - image_layout, + mut image_layout, } = image_view_info; - assert_eq!(device, image_view.device()); - - // VUID-VkWriteDescriptorSet-descriptorType-00337 - if !image_view.usage().intersects(ImageUsage::SAMPLED) { - return Err(DescriptorSetUpdateError::MissingUsage { - binding: write.binding(), - index: descriptor_range_start + index as u32, - usage: "sampled", - }); + if image_layout == ImageLayout::Undefined { + image_layout = default_image_layout; } - // VUID-VkDescriptorImageInfo-imageView-00343 - if matches!( - image_view.view_type(), - ImageViewType::Dim2d | ImageViewType::Dim2dArray - ) && image_view.image().inner().dimensions().image_type() == ImageType::Dim3d - { - return Err(DescriptorSetUpdateError::ImageView2dFrom3d { - binding: write.binding(), - index: descriptor_range_start + index as u32, - }); - } + assert_eq!(device, image_view.device()); - // VUID-VkDescriptorImageInfo-imageView-01976 - if image_view - .subresource_range() - .aspects - .contains(ImageAspects::DEPTH | ImageAspects::STENCIL) - { - return Err(DescriptorSetUpdateError::ImageViewDepthAndStencil { - binding: write.binding(), - index: descriptor_range_start + index as u32, + validate_image_view(image_view.as_ref(), index)?; + + if !image_view.usage().intersects(ImageUsage::INPUT_ATTACHMENT) { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the descriptor type is \ + DescriptorType::InputAttachment, and the image was not \ + created with the ImageUsage::INPUT_ATTACHMENT usage" + .into(), + vuids: &["VUID-VkWriteDescriptorSet-descriptorType-00338"], + ..Default::default() }); } - // VUID-VkWriteDescriptorSet-descriptorType-04150 if !matches!( image_layout, ImageLayout::DepthStencilReadOnlyOptimal @@ -897,691 +1163,476 @@ pub(crate) fn validate_descriptor_write<'a>( | ImageLayout::DepthReadOnlyStencilAttachmentOptimal | ImageLayout::DepthAttachmentStencilReadOnlyOptimal, ) { - return Err(DescriptorSetUpdateError::ImageLayoutInvalid { - binding: write.binding(), - index: descriptor_range_start + index as u32, + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the descriptor type is \ + DescriptorType::InputAttachment, and the image layout is \ + not valid with this type" + .into(), + vuids: &["VUID-VkWriteDescriptorSet-descriptorType-04151"], + ..Default::default() }); } - if let Err(error) = sampler.check_can_sample(image_view.as_ref()) { - return Err(DescriptorSetUpdateError::ImageViewIncompatibleSampler { - binding: write.binding(), - index: descriptor_range_start + index as u32, - error, + if !image_view.component_mapping().is_identity() { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the descriptor type is DescriptorType::StorageImage or \ + DescriptorType::InputAttachment, and the image view is not \ + identity swizzled" + .into(), + vuids: &["VUID-VkWriteDescriptorSet-descriptorType-00336"], + ..Default::default() }); } - } - } - } - - DescriptorType::SampledImage => { - let elements = if let WriteDescriptorSetElements::ImageView(elements) = elements { - elements - } else { - return Err(DescriptorSetUpdateError::IncompatibleElementType { - binding, - provided_element_type: provided_element_type(elements), - allowed_element_types: &["image_view"], - }); - }; - - for (index, image_view_info) in elements.iter().enumerate() { - let &DescriptorImageViewInfo { - ref image_view, - image_layout, - } = image_view_info; - - assert_eq!(device, image_view.device()); - - // VUID-VkWriteDescriptorSet-descriptorType-00337 - if !image_view.usage().intersects(ImageUsage::SAMPLED) { - return Err(DescriptorSetUpdateError::MissingUsage { - binding: write.binding(), - index: descriptor_range_start + index as u32, - usage: "sampled", - }); - } - - // VUID-VkDescriptorImageInfo-imageView-00343 - if matches!( - image_view.view_type(), - ImageViewType::Dim2d | ImageViewType::Dim2dArray - ) && image_view.image().inner().dimensions().image_type() == ImageType::Dim3d - { - return Err(DescriptorSetUpdateError::ImageView2dFrom3d { - binding: write.binding(), - index: descriptor_range_start + index as u32, - }); - } - - // VUID-VkDescriptorImageInfo-imageView-01976 - if image_view - .subresource_range() - .aspects - .contains(ImageAspects::DEPTH | ImageAspects::STENCIL) - { - return Err(DescriptorSetUpdateError::ImageViewDepthAndStencil { - binding: write.binding(), - index: descriptor_range_start + index as u32, - }); - } - - // VUID-VkWriteDescriptorSet-descriptorType-04149 - if !matches!( - image_layout, - ImageLayout::DepthStencilReadOnlyOptimal - | ImageLayout::ShaderReadOnlyOptimal - | ImageLayout::General - | ImageLayout::DepthReadOnlyStencilAttachmentOptimal - | ImageLayout::DepthAttachmentStencilReadOnlyOptimal, - ) { - return Err(DescriptorSetUpdateError::ImageLayoutInvalid { - binding: write.binding(), - index: descriptor_range_start + index as u32, - }); - } - // VUID-VkWriteDescriptorSet-descriptorType-01946 - if image_view.sampler_ycbcr_conversion().is_some() { - return Err( - DescriptorSetUpdateError::ImageViewHasSamplerYcbcrConversion { - binding: write.binding(), - index: descriptor_range_start + index as u32, - }, - ); + if image_view.sampler_ycbcr_conversion().is_some() { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the descriptor type is DescriptorType::InputAttachment, and \ + the image view has a sampler YCbCr conversion" + .into(), + // vuids? + ..Default::default() + }); + } } } - } - - DescriptorType::StorageImage => { - let elements = if let WriteDescriptorSetElements::ImageView(elements) = elements { - elements - } else { - return Err(DescriptorSetUpdateError::IncompatibleElementType { - binding, - provided_element_type: provided_element_type(elements), - allowed_element_types: &["image_view"], - }); - }; - - for (index, image_view_info) in elements.iter().enumerate() { - let &DescriptorImageViewInfo { - ref image_view, - image_layout, - } = image_view_info; - - assert_eq!(device, image_view.device()); - - // VUID-VkWriteDescriptorSet-descriptorType-00339 - if !image_view.usage().intersects(ImageUsage::STORAGE) { - return Err(DescriptorSetUpdateError::MissingUsage { - binding: write.binding(), - index: descriptor_range_start + index as u32, - usage: "storage", - }); - } - // VUID-VkDescriptorImageInfo-imageView-00343 - if matches!( - image_view.view_type(), - ImageViewType::Dim2d | ImageViewType::Dim2dArray - ) && image_view.image().inner().dimensions().image_type() == ImageType::Dim3d - { - return Err(DescriptorSetUpdateError::ImageView2dFrom3d { - binding: write.binding(), - index: descriptor_range_start + index as u32, - }); - } - - // VUID-VkDescriptorImageInfo-imageView-01976 - if image_view - .subresource_range() - .aspects - .contains(ImageAspects::DEPTH | ImageAspects::STENCIL) - { - return Err(DescriptorSetUpdateError::ImageViewDepthAndStencil { - binding: write.binding(), - index: descriptor_range_start + index as u32, - }); - } - - // VUID-VkWriteDescriptorSet-descriptorType-04152 - if !matches!(image_layout, ImageLayout::General) { - return Err(DescriptorSetUpdateError::ImageLayoutInvalid { - binding: write.binding(), - index: descriptor_range_start + index as u32, - }); - } + DescriptorType::AccelerationStructure => { + let elements = + if let WriteDescriptorSetElements::AccelerationStructure(elements) = elements { + elements + } else { + return Err(ValidationError { + context: "elements".into(), + problem: format!( + "contains `{}` elements, but the descriptor set \ + binding requires `acceleration_structure` elements", + provided_element_type(elements) + ) + .into(), + ..Default::default() + }); + }; - // VUID-VkWriteDescriptorSet-descriptorType-00336 - if !image_view.component_mapping().is_identity() { - return Err(DescriptorSetUpdateError::ImageViewNotIdentitySwizzled { - binding: write.binding(), - index: descriptor_range_start + index as u32, - }); - } + for (index, acceleration_structure) in elements.iter().enumerate() { + assert_eq!(device, acceleration_structure.device()); - // VUID?? - if image_view.sampler_ycbcr_conversion().is_some() { - return Err( - DescriptorSetUpdateError::ImageViewHasSamplerYcbcrConversion { - binding: write.binding(), - index: descriptor_range_start + index as u32, - }, - ); + if !matches!( + acceleration_structure.ty(), + AccelerationStructureType::TopLevel | AccelerationStructureType::Generic + ) { + return Err(ValidationError { + context: format!("elements[{}]", index).into(), + problem: "the acceleration structure's type is not \ + AccelerationStructureType::TopLevel or \ + AccelerationStructureType::Generic" + .into(), + vuids: &["VUID-VkWriteDescriptorSetAccelerationStructureKHR-pAccelerationStructures-03579"], + ..Default::default() + }); + } } } } - DescriptorType::UniformTexelBuffer => { - let elements = if let WriteDescriptorSetElements::BufferView(elements) = elements { - elements - } else { - return Err(DescriptorSetUpdateError::IncompatibleElementType { - binding, - provided_element_type: provided_element_type(elements), - allowed_element_types: &["buffer_view"], - }); - }; + Ok(()) + } - for (index, buffer_view) in elements.iter().enumerate() { - assert_eq!(device, buffer_view.device()); + pub(crate) fn to_vulkan_info(&self, descriptor_type: DescriptorType) -> DescriptorWriteInfo { + let default_image_layout = descriptor_type.default_image_layout(); - if !buffer_view - .buffer() - .buffer() - .usage() - .intersects(BufferUsage::UNIFORM_TEXEL_BUFFER) - { - return Err(DescriptorSetUpdateError::MissingUsage { - binding: write.binding(), - index: descriptor_range_start + index as u32, - usage: "uniform_texel_buffer", - }); - } + match &self.elements { + WriteDescriptorSetElements::None(num_elements) => { + debug_assert!(matches!(descriptor_type, DescriptorType::Sampler)); + DescriptorWriteInfo::Image( + std::iter::repeat_with(|| ash::vk::DescriptorImageInfo { + sampler: ash::vk::Sampler::null(), + image_view: ash::vk::ImageView::null(), + image_layout: ash::vk::ImageLayout::UNDEFINED, + }) + .take(*num_elements as usize) + .collect(), + ) } - } - - DescriptorType::StorageTexelBuffer => { - let elements = if let WriteDescriptorSetElements::BufferView(elements) = elements { - elements - } else { - return Err(DescriptorSetUpdateError::IncompatibleElementType { - binding, - provided_element_type: provided_element_type(elements), - allowed_element_types: &["buffer_view"], - }); - }; + WriteDescriptorSetElements::Buffer(elements) => { + debug_assert!(matches!( + descriptor_type, + DescriptorType::UniformBuffer + | DescriptorType::StorageBuffer + | DescriptorType::UniformBufferDynamic + | DescriptorType::StorageBufferDynamic + )); + DescriptorWriteInfo::Buffer( + elements + .iter() + .map(|buffer_info| { + let DescriptorBufferInfo { buffer, range } = buffer_info; - for (index, buffer_view) in elements.iter().enumerate() { - assert_eq!(device, buffer_view.device()); + debug_assert!(!range.is_empty()); + debug_assert!(range.end <= buffer.buffer().size()); - // TODO: storage_texel_buffer_atomic - if !buffer_view - .buffer() - .buffer() - .usage() - .intersects(BufferUsage::STORAGE_TEXEL_BUFFER) - { - return Err(DescriptorSetUpdateError::MissingUsage { - binding: write.binding(), - index: descriptor_range_start + index as u32, - usage: "storage_texel_buffer", - }); - } + ash::vk::DescriptorBufferInfo { + buffer: buffer.buffer().handle(), + offset: buffer.offset() + range.start, + range: range.end - range.start, + } + }) + .collect(), + ) } - } - - DescriptorType::UniformBuffer | DescriptorType::UniformBufferDynamic => { - let elements = if let WriteDescriptorSetElements::Buffer(elements) = elements { - elements - } else { - return Err(DescriptorSetUpdateError::IncompatibleElementType { - binding, - provided_element_type: provided_element_type(elements), - allowed_element_types: &["buffer"], - }); - }; - - for (index, buffer_info) in elements.iter().enumerate() { - let DescriptorBufferInfo { buffer, range } = buffer_info; - - assert_eq!(device, buffer.device()); - - if !buffer - .buffer() - .usage() - .intersects(BufferUsage::UNIFORM_BUFFER) - { - return Err(DescriptorSetUpdateError::MissingUsage { - binding: write.binding(), - index: descriptor_range_start + index as u32, - usage: "uniform_buffer", - }); - } - - assert!(!range.is_empty()); - - if range.end > buffer.size() { - return Err(DescriptorSetUpdateError::RangeOutOfBufferBounds { - binding: write.binding(), - index: descriptor_range_start + index as u32, - range_end: range.end, - buffer_size: buffer.size(), - }); - } + WriteDescriptorSetElements::BufferView(elements) => { + debug_assert!(matches!( + descriptor_type, + DescriptorType::UniformTexelBuffer | DescriptorType::StorageTexelBuffer + )); + DescriptorWriteInfo::BufferView( + elements + .iter() + .map(|buffer_view| buffer_view.handle()) + .collect(), + ) } - } - - DescriptorType::StorageBuffer | DescriptorType::StorageBufferDynamic => { - let elements = if let WriteDescriptorSetElements::Buffer(elements) = elements { - elements - } else { - return Err(DescriptorSetUpdateError::IncompatibleElementType { - binding, - provided_element_type: provided_element_type(elements), - allowed_element_types: &["buffer"], - }); - }; - - for (index, buffer_info) in elements.iter().enumerate() { - let DescriptorBufferInfo { buffer, range } = buffer_info; - - assert_eq!(device, buffer.device()); - - if !buffer - .buffer() - .usage() - .intersects(BufferUsage::STORAGE_BUFFER) - { - return Err(DescriptorSetUpdateError::MissingUsage { - binding: write.binding(), - index: descriptor_range_start + index as u32, - usage: "storage_buffer", - }); - } + WriteDescriptorSetElements::ImageView(elements) => { + // Note: combined image sampler can occur with immutable samplers + debug_assert!(matches!( + descriptor_type, + DescriptorType::CombinedImageSampler + | DescriptorType::SampledImage + | DescriptorType::StorageImage + | DescriptorType::InputAttachment + )); + DescriptorWriteInfo::Image( + elements + .iter() + .map(|image_view_info| { + let &DescriptorImageViewInfo { + ref image_view, + mut image_layout, + } = image_view_info; - assert!(!range.is_empty()); + if image_layout == ImageLayout::Undefined { + image_layout = default_image_layout; + } - if range.end > buffer.size() { - return Err(DescriptorSetUpdateError::RangeOutOfBufferBounds { - binding: write.binding(), - index: descriptor_range_start + index as u32, - range_end: range.end, - buffer_size: buffer.size(), - }); - } + ash::vk::DescriptorImageInfo { + sampler: ash::vk::Sampler::null(), + image_view: image_view.handle(), + image_layout: image_layout.into(), + } + }) + .collect(), + ) } - } - - DescriptorType::InputAttachment => { - let elements = if let WriteDescriptorSetElements::ImageView(elements) = elements { - elements - } else { - return Err(DescriptorSetUpdateError::IncompatibleElementType { - binding, - provided_element_type: provided_element_type(elements), - allowed_element_types: &["image_view"], - }); - }; - - for (index, image_view_info) in elements.iter().enumerate() { - let &DescriptorImageViewInfo { - ref image_view, - image_layout, - } = image_view_info; - - assert_eq!(device, image_view.device()); - - // VUID-VkWriteDescriptorSet-descriptorType-00338 - if !image_view.usage().intersects(ImageUsage::INPUT_ATTACHMENT) { - return Err(DescriptorSetUpdateError::MissingUsage { - binding: write.binding(), - index: descriptor_range_start + index as u32, - usage: "input_attachment", - }); - } - - // VUID-VkDescriptorImageInfo-imageView-00343 - if matches!( - image_view.view_type(), - ImageViewType::Dim2d | ImageViewType::Dim2dArray - ) && image_view.image().inner().dimensions().image_type() == ImageType::Dim3d - { - return Err(DescriptorSetUpdateError::ImageView2dFrom3d { - binding: write.binding(), - index: descriptor_range_start + index as u32, - }); - } - - // VUID-VkDescriptorImageInfo-imageView-01976 - if image_view - .subresource_range() - .aspects - .contains(ImageAspects::DEPTH | ImageAspects::STENCIL) - { - return Err(DescriptorSetUpdateError::ImageViewDepthAndStencil { - binding: write.binding(), - index: descriptor_range_start + index as u32, - }); - } - - // VUID-VkWriteDescriptorSet-descriptorType-04151 - if !matches!( - image_layout, - ImageLayout::DepthStencilReadOnlyOptimal - | ImageLayout::ShaderReadOnlyOptimal - | ImageLayout::General - | ImageLayout::DepthReadOnlyStencilAttachmentOptimal - | ImageLayout::DepthAttachmentStencilReadOnlyOptimal, - ) { - return Err(DescriptorSetUpdateError::ImageLayoutInvalid { - binding: write.binding(), - index: descriptor_range_start + index as u32, - }); - } - - // VUID-VkWriteDescriptorSet-descriptorType-00336 - if !image_view.component_mapping().is_identity() { - return Err(DescriptorSetUpdateError::ImageViewNotIdentitySwizzled { - binding: write.binding(), - index: descriptor_range_start + index as u32, - }); - } + WriteDescriptorSetElements::ImageViewSampler(elements) => { + debug_assert!(matches!( + descriptor_type, + DescriptorType::CombinedImageSampler + )); + DescriptorWriteInfo::Image( + elements + .iter() + .map(|(image_view_info, sampler)| { + let &DescriptorImageViewInfo { + ref image_view, + mut image_layout, + } = image_view_info; - // VUID?? - if image_view.sampler_ycbcr_conversion().is_some() { - return Err( - DescriptorSetUpdateError::ImageViewHasSamplerYcbcrConversion { - binding: write.binding(), - index: descriptor_range_start + index as u32, - }, - ); - } + if image_layout == ImageLayout::Undefined { + image_layout = default_image_layout; + } - // VUID?? - if image_view.view_type().is_arrayed() { - return Err(DescriptorSetUpdateError::ImageViewIsArrayed { - binding: write.binding(), - index: descriptor_range_start + index as u32, - }); - } + ash::vk::DescriptorImageInfo { + sampler: sampler.handle(), + image_view: image_view.handle(), + image_layout: image_layout.into(), + } + }) + .collect(), + ) } - } - - DescriptorType::AccelerationStructure => { - let elements = - if let WriteDescriptorSetElements::AccelerationStructure(elements) = elements { + WriteDescriptorSetElements::Sampler(elements) => { + debug_assert!(matches!(descriptor_type, DescriptorType::Sampler)); + DescriptorWriteInfo::Image( elements - } else { - return Err(DescriptorSetUpdateError::IncompatibleElementType { - binding, - provided_element_type: provided_element_type(elements), - allowed_element_types: &["acceleration_structure"], - }); - }; - - for (index, acceleration_structure) in elements.iter().enumerate() { - assert_eq!(device, acceleration_structure.device()); - - if !matches!( - acceleration_structure.ty(), - AccelerationStructureType::TopLevel | AccelerationStructureType::Generic - ) { - return Err(DescriptorSetUpdateError::AccelerationStructureNotTopLevel { - binding: write.binding(), - index: descriptor_range_start + index as u32, - }); - } + .iter() + .map(|sampler| ash::vk::DescriptorImageInfo { + sampler: sampler.handle(), + image_view: ash::vk::ImageView::null(), + image_layout: ash::vk::ImageLayout::UNDEFINED, + }) + .collect(), + ) + } + WriteDescriptorSetElements::AccelerationStructure(elements) => { + debug_assert!(matches!( + descriptor_type, + DescriptorType::AccelerationStructure + )); + DescriptorWriteInfo::AccelerationStructure( + elements + .iter() + .map(|acceleration_structure| acceleration_structure.handle()) + .collect(), + ) } } } - Ok(layout_binding) + pub(crate) fn to_vulkan( + &self, + dst_set: ash::vk::DescriptorSet, + descriptor_type: DescriptorType, + ) -> ash::vk::WriteDescriptorSet { + ash::vk::WriteDescriptorSet { + dst_set, + dst_binding: self.binding, + dst_array_element: self.first_array_element, + descriptor_count: 0, + descriptor_type: descriptor_type.into(), + p_image_info: ptr::null(), + p_buffer_info: ptr::null(), + p_texel_buffer_view: ptr::null(), + ..Default::default() + } + } } -#[derive(Clone, Copy, Debug)] -pub enum DescriptorSetUpdateError { - RequirementNotMet { - binding: u32, - index: u32, - required_for: &'static str, - requires_one_of: RequiresOneOf, - }, - - /// Tried to write an acceleration structure that is not top-level or generic. - AccelerationStructureNotTopLevel { - binding: u32, - index: u32, - }, +/// The elements held by a `WriteDescriptorSet`. +#[derive(Clone, Debug)] +pub enum WriteDescriptorSetElements { + None(u32), + Buffer(SmallVec<[DescriptorBufferInfo; 1]>), + BufferView(SmallVec<[Arc; 1]>), + ImageView(SmallVec<[DescriptorImageViewInfo; 1]>), + ImageViewSampler(SmallVec<[(DescriptorImageViewInfo, Arc); 1]>), + Sampler(SmallVec<[Arc; 1]>), + AccelerationStructure(SmallVec<[Arc; 1]>), +} - /// Tried to write more elements than were available in a binding. - ArrayIndexOutOfBounds { - /// Binding that is affected. - binding: u32, - /// Number of available descriptors in the binding. - available_count: u32, - /// The number of descriptors that were in the update. - written_count: u32, - }, +impl WriteDescriptorSetElements { + /// Returns the number of elements. + #[inline] + pub fn len(&self) -> u32 { + match self { + Self::None(num_elements) => *num_elements, + Self::Buffer(elements) => elements.len() as u32, + Self::BufferView(elements) => elements.len() as u32, + Self::ImageView(elements) => elements.len() as u32, + Self::ImageViewSampler(elements) => elements.len() as u32, + Self::Sampler(elements) => elements.len() as u32, + Self::AccelerationStructure(elements) => elements.len() as u32, + } + } +} - ImageLayoutInvalid { - binding: u32, - index: u32, - }, +/// Parameters to write a buffer reference to a descriptor. +#[derive(Clone, Debug)] +pub struct DescriptorBufferInfo { + /// The buffer to write to the descriptor. + pub buffer: Subbuffer<[u8]>, - /// Tried to write an image view with a 2D type and a 3D underlying image. - ImageView2dFrom3d { - binding: u32, - index: u32, - }, + /// The slice of bytes in `buffer` that will be made available to the shader. + /// `range` must not be outside the range `buffer`. + /// + /// For dynamic buffer bindings, `range` specifies the slice that is to be bound if the + /// dynamic offset were zero. When binding the descriptor set, the effective value of `range` + /// shifts forward by the offset that was provided. For example, if `range` is specified as + /// `0..8` when writing the descriptor set, and then when binding the descriptor set the + /// offset `16` is used, then the range of `buffer` that will actually be bound is `16..24`. + pub range: Range, +} - /// Tried to write an image view that has both the `depth` and `stencil` aspects. - ImageViewDepthAndStencil { - binding: u32, - index: u32, - }, +/// Parameters to write an image view reference to a descriptor. +#[derive(Clone, Debug)] +pub struct DescriptorImageViewInfo { + /// The image view to write to the descriptor. + pub image_view: Arc, - /// Tried to write an image view with an attached sampler YCbCr conversion to a binding that - /// does not support it. - ImageViewHasSamplerYcbcrConversion { - binding: u32, - index: u32, - }, + /// The layout that the image is expected to be in when it's accessed in the shader. + /// + /// Only certain layouts are allowed, depending on the type of descriptor. + /// + /// For `SampledImage`, `CombinedImageSampler` and `InputAttachment`: + /// - `General` + /// - `ShaderReadOnlyOptimal` + /// - `DepthStencilReadOnlyOptimal` + /// - `DepthReadOnlyStencilAttachmentOptimal` + /// - `DepthAttachmentStencilReadOnlyOptimal` + /// + /// For `StorageImage`: + /// - `General` + /// + /// If the `Undefined` layout is provided, then it will be automatically replaced with + /// `General` for `StorageImage` descriptors, and with `ShaderReadOnlyOptimal` for any other + /// descriptor type. + pub image_layout: ImageLayout, +} - /// Tried to write an image view of an arrayed type to a descriptor type that does not support - /// it. - ImageViewIsArrayed { - binding: u32, - index: u32, - }, +#[derive(Clone, Debug)] +pub(crate) enum DescriptorWriteInfo { + Image(SmallVec<[ash::vk::DescriptorImageInfo; 1]>), + Buffer(SmallVec<[ash::vk::DescriptorBufferInfo; 1]>), + BufferView(SmallVec<[ash::vk::BufferView; 1]>), + AccelerationStructure(SmallVec<[ash::vk::AccelerationStructureKHR; 1]>), +} - /// Tried to write an image view that was not compatible with the sampler that was provided as - /// part of the update or immutably in the layout. - ImageViewIncompatibleSampler { - binding: u32, - index: u32, - error: SamplerImageViewIncompatibleError, - }, +/// Represents a single copy operation to the binding of a descriptor set. +#[derive(Clone)] +pub struct CopyDescriptorSet { + /// The source descriptor set to copy from. + /// + /// There is no default value. + pub src_set: Arc, - /// Tried to write an image view to a descriptor type that requires it to be identity swizzled, - /// but it was not. - ImageViewNotIdentitySwizzled { - binding: u32, - index: u32, - }, + /// The binding number in the source descriptor set to copy from. + /// + /// The default value is 0. + pub src_binding: u32, - /// Tried to write an element type that was not compatible with the descriptor type in the - /// layout. - IncompatibleElementType { - binding: u32, - provided_element_type: &'static str, - allowed_element_types: &'static [&'static str], - }, + /// The first array element in the source descriptor set to copy from. + /// + /// The default value is 0. + pub src_first_array_element: u32, - /// Tried to write to a nonexistent binding. - InvalidBinding { - binding: u32, - }, + /// The binding number in the destination descriptor set to copy into. + /// + /// The default value is 0. + pub dst_binding: u32, - /// A resource was missing a usage flag that was required. - MissingUsage { - binding: u32, - index: u32, - usage: &'static str, - }, + /// The first array element in the destination descriptor set to copy into. + pub dst_first_array_element: u32, - /// The end of the provided `range` for a buffer is larger than the size of the buffer. - RangeOutOfBufferBounds { - binding: u32, - index: u32, - range_end: DeviceSize, - buffer_size: DeviceSize, - }, + /// The number of descriptors (array elements) to copy. + /// + /// The default value is 1. + pub descriptor_count: u32, - /// Tried to write a sampler that has an attached sampler YCbCr conversion. - SamplerHasSamplerYcbcrConversion { - binding: u32, - index: u32, - }, + pub _ne: crate::NonExhaustive, } -impl Error for DescriptorSetUpdateError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::ImageViewIncompatibleSampler { error, .. } => Some(error), - _ => None, +impl CopyDescriptorSet { + /// Returns a `CopyDescriptorSet` with the specified `src_set`. + #[inline] + pub fn new(src_set: Arc) -> Self { + Self { + src_set, + src_binding: 0, + src_first_array_element: 0, + dst_binding: 0, + dst_first_array_element: 0, + descriptor_count: 1, + _ne: crate::NonExhaustive(()), } } -} -impl Display for DescriptorSetUpdateError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::RequirementNotMet { - binding, - index, - required_for, - requires_one_of, - } => write!( - f, - "a requirement on binding {} index {} was not met for: {}; requires one of: {}", - binding, index, required_for, requires_one_of, - ), - - Self::AccelerationStructureNotTopLevel { binding, index } => write!( - f, - "tried to write an acceleration structure to binding {} index {} that is not \ - top-level or generic", - binding, index, - ), - Self::ArrayIndexOutOfBounds { - binding, - available_count, - written_count, - } => write!( - f, - "tried to write up to element {} to binding {}, but only {} descriptors are \ - available", - written_count, binding, available_count, - ), - Self::ImageLayoutInvalid { binding, index } => write!( - f, - "tried to write an image view to binding {} index {} with an image layout that is \ - not valid for that descriptor type", - binding, index, - ), - Self::ImageView2dFrom3d { binding, index } => write!( - f, - "tried to write an image view to binding {} index {} with a 2D type and a 3D \ - underlying image", - binding, index, - ), - Self::ImageViewDepthAndStencil { binding, index } => write!( - f, - "tried to write an image view to binding {} index {} that has both the `depth` and \ - `stencil` aspects", - binding, index, - ), - Self::ImageViewHasSamplerYcbcrConversion { binding, index } => write!( - f, - "tried to write an image view to binding {} index {} with an attached sampler \ - YCbCr conversion to binding that does not support it", - binding, index, - ), - Self::ImageViewIsArrayed { binding, index } => write!( - f, - "tried to write an image view of an arrayed type to binding {} index {}, but this \ - binding has a descriptor type that does not support arrayed image views", - binding, index, - ), - Self::ImageViewIncompatibleSampler { binding, index, .. } => write!( - f, - "tried to write an image view to binding {} index {}, that was not compatible with \ - the sampler that was provided as part of the update or immutably in the layout", - binding, index, - ), - Self::ImageViewNotIdentitySwizzled { binding, index } => write!( - f, - "tried to write an image view with non-identity swizzling to binding {} index {}, \ - but this binding has a descriptor type that requires it to be identity swizzled", - binding, index, - ), - Self::IncompatibleElementType { - binding, - provided_element_type, - allowed_element_types, - } => write!( - f, - "tried to write a resource to binding {} whose type ({}) was not one of the \ - resource types allowed for the descriptor type (", - binding, provided_element_type, - ) - .and_then(|_| { - let mut first = true; - - for elem_type in *allowed_element_types { - if first { - write!(f, "{}", elem_type)?; - first = false; - } else { - write!(f, ", {}", elem_type)?; - } - } + pub(crate) fn validate( + &self, + dst_set_layout: &DescriptorSetLayout, + dst_set_variable_descriptor_count: u32, + ) -> Result<(), ValidationError> { + let &Self { + ref src_set, + src_binding, + src_first_array_element, + dst_binding, + dst_first_array_element, + descriptor_count, + _ne, + } = self; + + // VUID-VkCopyDescriptorSet-commonparent + assert_eq!(src_set.device(), dst_set_layout.device()); + + let src_layout_binding = match src_set.layout().bindings().get(&src_binding) { + Some(layout_binding) => layout_binding, + None => { + return Err(ValidationError { + problem: "src_binding does not exist in the descriptor set layout of src_set" + .into(), + vuids: &["VUID-VkCopyDescriptorSet-srcBinding-00345"], + ..Default::default() + }); + } + }; + + let src_max_descriptor_count = if src_layout_binding + .binding_flags + .intersects(DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT) + { + src_set.variable_descriptor_count() + } else { + src_layout_binding.descriptor_count + }; + + if src_first_array_element + descriptor_count > src_max_descriptor_count { + return Err(ValidationError { + problem: "src_first_array_element + descriptor_count is greater than \ + the number of descriptors in src_set's descriptor set binding" + .into(), + vuids: &["VUID-VkCopyDescriptorSet-srcArrayElement-00346"], + ..Default::default() + }); + } - Ok(()) - }) - .and_then(|_| write!(f, ") that can be bound to this buffer")), - Self::InvalidBinding { binding } => { - write!(f, "tried to write to a nonexistent binding {}", binding,) + let dst_layout_binding = match dst_set_layout.bindings().get(&dst_binding) { + Some(layout_binding) => layout_binding, + None => { + return Err(ValidationError { + problem: "dst_binding does not exist in the descriptor set layout of dst_set" + .into(), + vuids: &["VUID-VkCopyDescriptorSet-dstBinding-00347"], + ..Default::default() + }); } - Self::MissingUsage { - binding, - index, - usage, - } => write!( - f, - "tried to write a resource to binding {} index {} that did not have the required \ - usage {} enabled", - binding, index, usage, - ), - Self::RangeOutOfBufferBounds { - binding, - index, - range_end, - buffer_size, - } => write!( - f, - "the end of the provided `range` for the buffer at binding {} index {} ({:?}) is - larger than the size of the buffer ({})", - binding, index, range_end, buffer_size, - ), - Self::SamplerHasSamplerYcbcrConversion { binding, index } => write!( - f, - "tried to write a sampler to binding {} index {} that has an attached sampler \ - YCbCr conversion", - binding, index, - ), + }; + + let dst_max_descriptor_count = if dst_layout_binding + .binding_flags + .intersects(DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT) + { + dst_set_variable_descriptor_count + } else { + dst_layout_binding.descriptor_count + }; + + if dst_first_array_element + descriptor_count > dst_max_descriptor_count { + return Err(ValidationError { + problem: "dst_first_array_element + descriptor_count is greater than \ + the number of descriptors in dst_set's descriptor set binding" + .into(), + vuids: &["VUID-VkCopyDescriptorSet-dstArrayElement-00348"], + ..Default::default() + }); + } + + if src_layout_binding.descriptor_type != dst_layout_binding.descriptor_type { + return Err(ValidationError { + problem: "the descriptor type of dst_binding within dst_set does not equal the \ + descriptor type of dst_binding within dst_set" + .into(), + vuids: &["VUID-VkCopyDescriptorSet-dstBinding-02632"], + ..Default::default() + }); } + + if dst_layout_binding.descriptor_type == DescriptorType::Sampler + && !dst_layout_binding.immutable_samplers.is_empty() + { + return Err(ValidationError { + problem: "the descriptor type of dst_binding within dst_set is \ + DescriptorType::Sampler, and the layout was created with immutable samplers \ + for dst_binding" + .into(), + vuids: &["VUID-VkCopyDescriptorSet-dstBinding-02753"], + ..Default::default() + }); + } + + // VUID-VkCopyDescriptorSet-srcSet-00349 + // Ensured as long as copies can only occur during descriptor set construction. + + Ok(()) } } diff --git a/vulkano/src/device/mod.rs b/vulkano/src/device/mod.rs index faf131f911..13d97b9d7f 100644 --- a/vulkano/src/device/mod.rs +++ b/vulkano/src/device/mod.rs @@ -113,6 +113,9 @@ use crate::{ AccelerationStructureBuildGeometryInfo, AccelerationStructureBuildSizesInfo, AccelerationStructureBuildType, AccelerationStructureGeometries, }, + descriptor_set::layout::{ + DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorSetLayoutSupport, + }, instance::Instance, macros::impl_id_counter, memory::ExternalMemoryHandleType, @@ -750,6 +753,153 @@ impl Device { compatibility_vk == ash::vk::AccelerationStructureCompatibilityKHR::COMPATIBLE } + /// Returns whether a descriptor set layout with the given `create_info` could be created + /// on the device, and additional supported properties where relevant. `Some` is returned if + /// the descriptor set layout is supported, `None` if it is not. + /// + /// This is primarily useful for checking whether the device supports a descriptor set layout + /// that goes beyond the [`max_per_set_descriptors`] limit. A layout that does not exceed + /// that limit is guaranteed to be supported, otherwise this function can be called. + /// + /// The device API version must be at least 1.1, or the [`khr_maintenance3`] extension must + /// be enabled on the device. + /// + /// [`max_per_set_descriptors`]: crate::device::Properties::max_per_set_descriptors + /// [`khr_maintenance3`]: crate::device::DeviceExtensions::khr_maintenance3 + #[inline] + pub fn descriptor_set_layout_support( + &self, + create_info: &DescriptorSetLayoutCreateInfo, + ) -> Result, ValidationError> { + self.validate_descriptor_set_layout_support(create_info)?; + + unsafe { Ok(self.descriptor_set_layout_support_unchecked(create_info)) } + } + + fn validate_descriptor_set_layout_support( + &self, + create_info: &DescriptorSetLayoutCreateInfo, + ) -> Result<(), ValidationError> { + if !(self.api_version() >= Version::V1_1 || self.enabled_extensions().khr_maintenance3) { + return Err(ValidationError { + requires_one_of: Some(RequiresOneOf { + api_version: Some(Version::V1_1), + device_extensions: &["khr_maintenance3"], + ..Default::default() + }), + ..Default::default() + }); + } + + // VUID-vkGetDescriptorSetLayoutSupport-pCreateInfo-parameter + create_info + .validate(self) + .map_err(|err| err.add_context("create_info"))?; + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn descriptor_set_layout_support_unchecked( + &self, + create_info: &DescriptorSetLayoutCreateInfo, + ) -> Option { + let &DescriptorSetLayoutCreateInfo { + flags, + ref bindings, + _ne: _, + } = create_info; + + struct PerBinding { + immutable_samplers_vk: Vec, + } + + let mut bindings_vk = Vec::with_capacity(bindings.len()); + let mut per_binding_vk = Vec::with_capacity(bindings.len()); + let mut binding_flags_info_vk = None; + let mut binding_flags_vk = Vec::with_capacity(bindings.len()); + + let mut support_vk = ash::vk::DescriptorSetLayoutSupport::default(); + let mut variable_descriptor_count_support_vk = None; + + for (&binding_num, binding) in bindings.iter() { + let &DescriptorSetLayoutBinding { + binding_flags, + descriptor_type, + descriptor_count, + stages, + ref immutable_samplers, + _ne: _, + } = binding; + + bindings_vk.push(ash::vk::DescriptorSetLayoutBinding { + binding: binding_num, + descriptor_type: descriptor_type.into(), + descriptor_count, + stage_flags: stages.into(), + p_immutable_samplers: ptr::null(), + }); + per_binding_vk.push(PerBinding { + immutable_samplers_vk: immutable_samplers + .iter() + .map(VulkanObject::handle) + .collect(), + }); + binding_flags_vk.push(binding_flags.into()); + } + + for (binding_vk, per_binding_vk) in bindings_vk.iter_mut().zip(per_binding_vk.iter_mut()) { + binding_vk.p_immutable_samplers = per_binding_vk.immutable_samplers_vk.as_ptr(); + } + + let mut create_info_vk = ash::vk::DescriptorSetLayoutCreateInfo { + flags: flags.into(), + binding_count: bindings_vk.len() as u32, + p_bindings: bindings_vk.as_ptr(), + ..Default::default() + }; + + if self.api_version() >= Version::V1_2 || self.enabled_extensions().ext_descriptor_indexing + { + let next = + binding_flags_info_vk.insert(ash::vk::DescriptorSetLayoutBindingFlagsCreateInfo { + binding_count: binding_flags_vk.len() as u32, + p_binding_flags: binding_flags_vk.as_ptr(), + ..Default::default() + }); + + next.p_next = create_info_vk.p_next; + create_info_vk.p_next = next as *const _ as *const _; + + let next = variable_descriptor_count_support_vk + .insert(ash::vk::DescriptorSetVariableDescriptorCountLayoutSupport::default()); + + next.p_next = support_vk.p_next; + support_vk.p_next = next as *mut _ as *mut _; + } + + let fns = self.fns(); + + if self.api_version() >= Version::V1_1 { + (fns.v1_1.get_descriptor_set_layout_support)( + self.handle(), + &create_info_vk, + &mut support_vk, + ) + } else { + (fns.khr_maintenance3.get_descriptor_set_layout_support_khr)( + self.handle(), + &create_info_vk, + &mut support_vk, + ) + } + + (support_vk.supported != ash::vk::FALSE).then(|| DescriptorSetLayoutSupport { + max_variable_descriptor_count: variable_descriptor_count_support_vk + .map_or(0, |s| s.max_variable_descriptor_count), + }) + } + /// Retrieves the properties of an external file descriptor when imported as a given external /// handle type. /// diff --git a/vulkano/src/pipeline/compute.rs b/vulkano/src/pipeline/compute.rs index c647d15d18..6ce8e31891 100644 --- a/vulkano/src/pipeline/compute.rs +++ b/vulkano/src/pipeline/compute.rs @@ -24,25 +24,18 @@ use super::PipelineCreateFlags; use crate::{ - descriptor_set::layout::DescriptorSetLayoutCreationError, device::{Device, DeviceOwned}, macros::impl_id_counter, - pipeline::{ - cache::PipelineCache, - layout::{PipelineLayout, PipelineLayoutCreationError, PipelineLayoutSupersetError}, - Pipeline, PipelineBindPoint, - }, + pipeline::{cache::PipelineCache, layout::PipelineLayout, Pipeline, PipelineBindPoint}, shader::{ DescriptorBindingRequirements, PipelineShaderStageCreateInfo, ShaderExecution, ShaderStage, - SpecializationConstant, }, - OomError, RequiresOneOf, RuntimeError, ValidationError, VulkanError, VulkanObject, + RuntimeError, ValidationError, VulkanError, VulkanObject, }; use ahash::HashMap; use std::{ - error::Error, ffi::CString, - fmt::{Debug, Display, Error as FmtError, Formatter}, + fmt::{Debug, Error as FmtError, Formatter}, mem::MaybeUninit, num::NonZeroU64, ptr, @@ -417,91 +410,6 @@ impl ComputePipelineCreateInfo { } } -/// Error that can happen when creating a compute pipeline. -#[derive(Clone, Debug, PartialEq)] -pub enum ComputePipelineCreationError { - /// Not enough memory. - OomError(OomError), - - RequirementNotMet { - required_for: &'static str, - requires_one_of: RequiresOneOf, - }, - - /// Error while creating a descriptor set layout object. - DescriptorSetLayoutCreationError(DescriptorSetLayoutCreationError), - - /// Error while creating the pipeline layout object. - PipelineLayoutCreationError(PipelineLayoutCreationError), - - /// The pipeline layout is not compatible with what the shader expects. - IncompatiblePipelineLayout(PipelineLayoutSupersetError), - - /// The value provided for a shader specialization constant has a - /// different type than the constant's default value. - ShaderSpecializationConstantTypeMismatch { - constant_id: u32, - default_value: SpecializationConstant, - provided_value: SpecializationConstant, - }, - - /// The provided shader stage is not a compute shader. - ShaderStageInvalid { stage: ShaderStage }, -} - -impl Error for ComputePipelineCreationError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::OomError(err) => Some(err), - Self::DescriptorSetLayoutCreationError(err) => Some(err), - Self::PipelineLayoutCreationError(err) => Some(err), - Self::IncompatiblePipelineLayout(err) => Some(err), - _ => None, - } - } -} - -impl Display for ComputePipelineCreationError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::OomError(_) => write!(f, "not enough memory available"), - Self::RequirementNotMet { - required_for, - requires_one_of, - } => write!( - f, - "a requirement was not met for: {}; requires one of: {}", - required_for, requires_one_of, - ), - Self::DescriptorSetLayoutCreationError(_) => { - write!(f, "error while creating a descriptor set layout object",) - } - Self::PipelineLayoutCreationError(_) => { - write!(f, "error while creating the pipeline layout object",) - } - Self::IncompatiblePipelineLayout(_) => write!( - f, - "the pipeline layout is not compatible with what the shader expects", - ), - Self::ShaderSpecializationConstantTypeMismatch { - constant_id, - default_value, - provided_value, - } => write!( - f, - "the value provided for shader specialization constant id {} ({:?}) has a \ - different type than the constant's default value ({:?})", - constant_id, provided_value, default_value, - ), - Self::ShaderStageInvalid { stage } => write!( - f, - "the provided shader stage ({:?}) is not a compute shader", - stage, - ), - } - } -} - #[cfg(test)] mod tests { use crate::{ @@ -602,6 +510,7 @@ mod tests { &ds_allocator, pipeline.layout().set_layouts().get(0).unwrap().clone(), [WriteDescriptorSet::buffer(0, data_buffer.clone())], + [], ) .unwrap(); diff --git a/vulkano/src/pipeline/layout.rs b/vulkano/src/pipeline/layout.rs index b56a697be0..cab477eab3 100644 --- a/vulkano/src/pipeline/layout.rs +++ b/vulkano/src/pipeline/layout.rs @@ -66,22 +66,23 @@ use crate::{ descriptor_set::layout::{ DescriptorRequirementsNotMet, DescriptorSetLayout, DescriptorSetLayoutBinding, - DescriptorSetLayoutCreateInfo, DescriptorSetLayoutCreationError, DescriptorType, + DescriptorSetLayoutCreateFlags, DescriptorSetLayoutCreateInfo, DescriptorType, }, - device::{Device, DeviceOwned}, - macros::impl_id_counter, + device::{Device, DeviceOwned, Properties}, + macros::{impl_id_counter, vulkan_bitflags}, shader::{ DescriptorBindingRequirements, PipelineShaderStageCreateInfo, ShaderStage, ShaderStages, }, - OomError, RequirementNotMet, RequiresOneOf, RuntimeError, VulkanObject, + RuntimeError, ValidationError, VulkanError, VulkanObject, }; use ahash::HashMap; use smallvec::SmallVec; use std::{ + array, cmp::max, collections::hash_map::Entry, error::Error, - fmt::{Display, Error as FmtError, Formatter}, + fmt::{Display, Error as FmtError, Formatter, Write}, mem::MaybeUninit, num::NonZeroU64, ptr, @@ -95,6 +96,7 @@ pub struct PipelineLayout { device: Arc, id: NonZeroU64, + flags: PipelineLayoutCreateFlags, set_layouts: Vec>, push_constant_ranges: Vec, @@ -103,354 +105,23 @@ pub struct PipelineLayout { impl PipelineLayout { /// Creates a new `PipelineLayout`. - /// - /// # Panics - /// - /// - Panics if an element of `create_info.push_constant_ranges` has an empty `stages` value. - /// - Panics if an element of `create_info.push_constant_ranges` has an `offset` or `size` - /// that's not divisible by 4. - /// - Panics if an element of `create_info.push_constant_ranges` has an `size` of zero. pub fn new( device: Arc, create_info: PipelineLayoutCreateInfo, - ) -> Result, PipelineLayoutCreationError> { + ) -> Result, VulkanError> { Self::validate_new(&device, &create_info)?; + unsafe { Ok(Self::new_unchecked(device, create_info)?) } } fn validate_new( device: &Device, create_info: &PipelineLayoutCreateInfo, - ) -> Result<(), PipelineLayoutCreationError> { - let &PipelineLayoutCreateInfo { - ref set_layouts, - ref push_constant_ranges, - _ne: _, - } = create_info; - - let properties = device.physical_device().properties(); - - /* Check descriptor set layouts */ - - // VUID-VkPipelineLayoutCreateInfo-setLayoutCount-00286 - if set_layouts.len() > properties.max_bound_descriptor_sets as usize { - return Err( - PipelineLayoutCreationError::MaxBoundDescriptorSetsExceeded { - provided: set_layouts.len() as u32, - max_supported: properties.max_bound_descriptor_sets, - }, - ); - } - - { - let mut num_resources = Counter::default(); - let mut num_samplers = Counter::default(); - let mut num_uniform_buffers = Counter::default(); - let mut num_uniform_buffers_dynamic = 0; - let mut num_storage_buffers = Counter::default(); - let mut num_storage_buffers_dynamic = 0; - let mut num_sampled_images = Counter::default(); - let mut num_storage_images = Counter::default(); - let mut num_input_attachments = Counter::default(); - let mut num_acceleration_structures = Counter::default(); - let mut push_descriptor_set = None; - - for (set_num, set_layout) in set_layouts.iter().enumerate() { - let set_num = set_num as u32; - - if set_layout.push_descriptor() { - // VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00293 - if push_descriptor_set.is_some() { - return Err(PipelineLayoutCreationError::SetLayoutsPushDescriptorMultiple); - } else { - push_descriptor_set = Some(set_num); - } - } - - for layout_binding in set_layout.bindings().values() { - num_resources.increment(layout_binding.descriptor_count, layout_binding.stages); - - match layout_binding.descriptor_type { - DescriptorType::Sampler => { - num_samplers - .increment(layout_binding.descriptor_count, layout_binding.stages); - } - DescriptorType::CombinedImageSampler => { - num_samplers - .increment(layout_binding.descriptor_count, layout_binding.stages); - num_sampled_images - .increment(layout_binding.descriptor_count, layout_binding.stages); - } - DescriptorType::SampledImage | DescriptorType::UniformTexelBuffer => { - num_sampled_images - .increment(layout_binding.descriptor_count, layout_binding.stages); - } - DescriptorType::StorageImage | DescriptorType::StorageTexelBuffer => { - num_storage_images - .increment(layout_binding.descriptor_count, layout_binding.stages); - } - DescriptorType::UniformBuffer => { - num_uniform_buffers - .increment(layout_binding.descriptor_count, layout_binding.stages); - } - DescriptorType::UniformBufferDynamic => { - num_uniform_buffers - .increment(layout_binding.descriptor_count, layout_binding.stages); - num_uniform_buffers_dynamic += 1; - } - DescriptorType::StorageBuffer => { - num_storage_buffers - .increment(layout_binding.descriptor_count, layout_binding.stages); - } - DescriptorType::StorageBufferDynamic => { - num_storage_buffers - .increment(layout_binding.descriptor_count, layout_binding.stages); - num_storage_buffers_dynamic += 1; - } - DescriptorType::InputAttachment => { - num_input_attachments - .increment(layout_binding.descriptor_count, layout_binding.stages); - } - DescriptorType::AccelerationStructure => { - num_acceleration_structures - .increment(layout_binding.descriptor_count, layout_binding.stages); - } - } - } - } - - if num_resources.max_per_stage() > properties.max_per_stage_resources { - return Err(PipelineLayoutCreationError::MaxPerStageResourcesExceeded { - provided: num_resources.max_per_stage(), - max_supported: properties.max_per_stage_resources, - }); - } - - // VUID-VkPipelineLayoutCreateInfo-descriptorType-03016 - if num_samplers.max_per_stage() > properties.max_per_stage_descriptor_samplers { - return Err( - PipelineLayoutCreationError::MaxPerStageDescriptorSamplersExceeded { - provided: num_samplers.max_per_stage(), - max_supported: properties.max_per_stage_descriptor_samplers, - }, - ); - } - - // VUID-VkPipelineLayoutCreateInfo-descriptorType-03017 - if num_uniform_buffers.max_per_stage() - > properties.max_per_stage_descriptor_uniform_buffers - { - return Err( - PipelineLayoutCreationError::MaxPerStageDescriptorUniformBuffersExceeded { - provided: num_uniform_buffers.max_per_stage(), - max_supported: properties.max_per_stage_descriptor_uniform_buffers, - }, - ); - } - - // VUID-VkPipelineLayoutCreateInfo-descriptorType-03018 - if num_storage_buffers.max_per_stage() - > properties.max_per_stage_descriptor_storage_buffers - { - return Err( - PipelineLayoutCreationError::MaxPerStageDescriptorStorageBuffersExceeded { - provided: num_storage_buffers.max_per_stage(), - max_supported: properties.max_per_stage_descriptor_storage_buffers, - }, - ); - } - - // VUID-VkPipelineLayoutCreateInfo-descriptorType-03019 - if num_sampled_images.max_per_stage() - > properties.max_per_stage_descriptor_sampled_images - { - return Err( - PipelineLayoutCreationError::MaxPerStageDescriptorSampledImagesExceeded { - provided: num_sampled_images.max_per_stage(), - max_supported: properties.max_per_stage_descriptor_sampled_images, - }, - ); - } - - // VUID-VkPipelineLayoutCreateInfo-descriptorType-03020 - if num_storage_images.max_per_stage() - > properties.max_per_stage_descriptor_storage_images - { - return Err( - PipelineLayoutCreationError::MaxPerStageDescriptorStorageImagesExceeded { - provided: num_storage_images.max_per_stage(), - max_supported: properties.max_per_stage_descriptor_storage_images, - }, - ); - } - - // VUID-VkPipelineLayoutCreateInfo-descriptorType-03021 - if num_input_attachments.max_per_stage() - > properties.max_per_stage_descriptor_input_attachments - { - return Err( - PipelineLayoutCreationError::MaxPerStageDescriptorInputAttachmentsExceeded { - provided: num_input_attachments.max_per_stage(), - max_supported: properties.max_per_stage_descriptor_input_attachments, - }, - ); - } - - // VUID-VkPipelineLayoutCreateInfo-descriptorType-03571 - if num_acceleration_structures.max_per_stage() - > properties - .max_per_stage_descriptor_acceleration_structures - .unwrap_or(0) - { - return Err( - PipelineLayoutCreationError::MaxPerStageDescriptorAccelerationStructuresExceeded { - provided: num_acceleration_structures.max_per_stage(), - max_supported: properties - .max_per_stage_descriptor_acceleration_structures - .unwrap_or(0), - }, - ); - } - - // VUID-VkPipelineLayoutCreateInfo-descriptorType-03028 - if num_samplers.total() > properties.max_descriptor_set_samplers { - return Err( - PipelineLayoutCreationError::MaxDescriptorSetSamplersExceeded { - provided: num_samplers.total(), - max_supported: properties.max_descriptor_set_samplers, - }, - ); - } - - // VUID-VkPipelineLayoutCreateInfo-descriptorType-03029 - if num_uniform_buffers.total() > properties.max_descriptor_set_uniform_buffers { - return Err( - PipelineLayoutCreationError::MaxDescriptorSetUniformBuffersExceeded { - provided: num_uniform_buffers.total(), - max_supported: properties.max_descriptor_set_uniform_buffers, - }, - ); - } - - // VUID-VkPipelineLayoutCreateInfo-descriptorType-03030 - if num_uniform_buffers_dynamic > properties.max_descriptor_set_uniform_buffers_dynamic { - return Err( - PipelineLayoutCreationError::MaxDescriptorSetUniformBuffersDynamicExceeded { - provided: num_uniform_buffers_dynamic, - max_supported: properties.max_descriptor_set_uniform_buffers_dynamic, - }, - ); - } - - // VUID-VkPipelineLayoutCreateInfo-descriptorType-03031 - if num_storage_buffers.total() > properties.max_descriptor_set_storage_buffers { - return Err( - PipelineLayoutCreationError::MaxDescriptorSetStorageBuffersExceeded { - provided: num_storage_buffers.total(), - max_supported: properties.max_descriptor_set_storage_buffers, - }, - ); - } - - // VUID-VkPipelineLayoutCreateInfo-descriptorType-03032 - if num_storage_buffers_dynamic > properties.max_descriptor_set_storage_buffers_dynamic { - return Err( - PipelineLayoutCreationError::MaxDescriptorSetStorageBuffersDynamicExceeded { - provided: num_storage_buffers_dynamic, - max_supported: properties.max_descriptor_set_storage_buffers_dynamic, - }, - ); - } - - // VUID-VkPipelineLayoutCreateInfo-descriptorType-03033 - if num_sampled_images.total() > properties.max_descriptor_set_sampled_images { - return Err( - PipelineLayoutCreationError::MaxDescriptorSetSampledImagesExceeded { - provided: num_sampled_images.total(), - max_supported: properties.max_descriptor_set_sampled_images, - }, - ); - } - - // VUID-VkPipelineLayoutCreateInfo-descriptorType-03034 - if num_storage_images.total() > properties.max_descriptor_set_storage_images { - return Err( - PipelineLayoutCreationError::MaxDescriptorSetStorageImagesExceeded { - provided: num_storage_images.total(), - max_supported: properties.max_descriptor_set_storage_images, - }, - ); - } - - // VUID-VkPipelineLayoutCreateInfo-descriptorType-03035 - if num_input_attachments.total() > properties.max_descriptor_set_input_attachments { - return Err( - PipelineLayoutCreationError::MaxDescriptorSetInputAttachmentsExceeded { - provided: num_input_attachments.total(), - max_supported: properties.max_descriptor_set_input_attachments, - }, - ); - } - - // VUID-VkPipelineLayoutCreateInfo-descriptorType-03573 - if num_acceleration_structures.total() - > properties - .max_descriptor_set_acceleration_structures - .unwrap_or(0) - { - return Err( - PipelineLayoutCreationError::MaxDescriptorSetAccelerationStructuresExceeded { - provided: num_acceleration_structures.total(), - max_supported: properties - .max_descriptor_set_acceleration_structures - .unwrap_or(0), - }, - ); - } - } - - /* Check push constant ranges */ - - push_constant_ranges - .iter() - .try_fold(ShaderStages::empty(), |total, range| { - let &PushConstantRange { - stages, - offset, - size, - } = range; - - // VUID-VkPushConstantRange-stageFlags-parameter - stages.validate_device(device)?; - - // VUID-VkPushConstantRange-stageFlags-requiredbitmask - assert!(!stages.is_empty()); - - // VUID-VkPushConstantRange-offset-00295 - assert!(offset % 4 == 0); - - // VUID-VkPushConstantRange-size-00296 - assert!(size != 0); - - // VUID-VkPushConstantRange-size-00297 - assert!(size % 4 == 0); - - // VUID-VkPushConstantRange-offset-00294 - // VUID-VkPushConstantRange-size-00298 - if offset + size > properties.max_push_constants_size { - return Err(PipelineLayoutCreationError::MaxPushConstantsSizeExceeded { - provided: offset + size, - max_supported: properties.max_push_constants_size, - }); - } - - // VUID-VkPipelineLayoutCreateInfo-pPushConstantRanges-00292 - if !(total & stages).is_empty() { - return Err(PipelineLayoutCreationError::PushConstantRangesStageMultiple); - } - - Ok(total | stages) - })?; + ) -> Result<(), ValidationError> { + // VUID-vkCreatePipelineLayout-pCreateInfo-parameter + create_info + .validate(device) + .map_err(|err| err.add_context("create_info"))?; Ok(()) } @@ -461,6 +132,7 @@ impl PipelineLayout { create_info: PipelineLayoutCreateInfo, ) -> Result, RuntimeError> { let &PipelineLayoutCreateInfo { + flags, ref set_layouts, ref push_constant_ranges, _ne: _, @@ -477,7 +149,7 @@ impl PipelineLayout { .collect(); let create_info_vk = ash::vk::PipelineLayoutCreateInfo { - flags: ash::vk::PipelineLayoutCreateFlags::empty(), + flags: flags.into(), set_layout_count: set_layouts_vk.len() as u32, p_set_layouts: set_layouts_vk.as_ptr(), push_constant_range_count: push_constant_ranges_vk.len() as u32, @@ -515,6 +187,7 @@ impl PipelineLayout { create_info: PipelineLayoutCreateInfo, ) -> Arc { let PipelineLayoutCreateInfo { + flags, set_layouts, mut push_constant_ranges, _ne: _, @@ -570,12 +243,19 @@ impl PipelineLayout { handle, device, id: Self::next_id(), + flags, set_layouts, push_constant_ranges, push_constant_ranges_disjoint, }) } + /// Returns the flags that the pipeline layout was created with. + #[inline] + pub fn flags(&self) -> PipelineLayoutCreateFlags { + self.flags + } + /// Returns the descriptor set layouts this pipeline layout was created from. #[inline] pub fn set_layouts(&self) -> &[Arc] { @@ -716,370 +396,466 @@ unsafe impl DeviceOwned for PipelineLayout { impl_id_counter!(PipelineLayout); -/// Error that can happen when creating a pipeline layout. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum PipelineLayoutCreationError { - /// Not enough memory. - OomError(OomError), +/// Parameters to create a new `PipelineLayout`. +#[derive(Clone, Debug)] +pub struct PipelineLayoutCreateInfo { + /// Specifies how to create the pipeline layout. + pub flags: PipelineLayoutCreateFlags, - RequirementNotMet { - required_for: &'static str, - requires_one_of: RequiresOneOf, - }, + /// The descriptor set layouts that should be part of the pipeline layout. + /// + /// They are provided in order of set number. + /// + /// The default value is empty. + pub set_layouts: Vec>, + + /// The ranges of push constants that the pipeline will access. + /// + /// A shader stage can only appear in one element of the list, but it is possible to combine + /// ranges for multiple shader stages if they are the same. + /// + /// The default value is empty. + pub push_constant_ranges: Vec, - /// The number of elements in `set_layouts` is greater than the - /// [`max_bound_descriptor_sets`](crate::device::Properties::max_bound_descriptor_sets) limit. - MaxBoundDescriptorSetsExceeded { provided: u32, max_supported: u32 }, - - /// The `set_layouts` contain more [`DescriptorType::Sampler`], - /// [`DescriptorType::CombinedImageSampler`] and [`DescriptorType::UniformTexelBuffer`] - /// descriptors than the - /// [`max_descriptor_set_samplers`](crate::device::Properties::max_descriptor_set_samplers) - /// limit. - MaxDescriptorSetSamplersExceeded { provided: u32, max_supported: u32 }, - - /// The `set_layouts` contain more [`DescriptorType::UniformBuffer`] descriptors than the - /// [`max_descriptor_set_uniform_buffers`](crate::device::Properties::max_descriptor_set_uniform_buffers) - /// limit. - MaxDescriptorSetUniformBuffersExceeded { provided: u32, max_supported: u32 }, - - /// The `set_layouts` contain more [`DescriptorType::UniformBufferDynamic`] descriptors than the - /// [`max_descriptor_set_uniform_buffers_dynamic`](crate::device::Properties::max_descriptor_set_uniform_buffers_dynamic) - /// limit. - MaxDescriptorSetUniformBuffersDynamicExceeded { provided: u32, max_supported: u32 }, - - /// The `set_layouts` contain more [`DescriptorType::StorageBuffer`] descriptors than the - /// [`max_descriptor_set_storage_buffers`](crate::device::Properties::max_descriptor_set_storage_buffers) - /// limit. - MaxDescriptorSetStorageBuffersExceeded { provided: u32, max_supported: u32 }, - - /// The `set_layouts` contain more [`DescriptorType::StorageBufferDynamic`] descriptors than the - /// [`max_descriptor_set_storage_buffers_dynamic`](crate::device::Properties::max_descriptor_set_storage_buffers_dynamic) - /// limit. - MaxDescriptorSetStorageBuffersDynamicExceeded { provided: u32, max_supported: u32 }, - - /// The `set_layouts` contain more [`DescriptorType::SampledImage`] and - /// [`DescriptorType::CombinedImageSampler`] descriptors than the - /// [`max_descriptor_set_sampled_images`](crate::device::Properties::max_descriptor_set_sampled_images) - /// limit. - MaxDescriptorSetSampledImagesExceeded { provided: u32, max_supported: u32 }, - - /// The `set_layouts` contain more [`DescriptorType::StorageImage`] and - /// [`DescriptorType::StorageTexelBuffer`] descriptors than the - /// [`max_descriptor_set_storage_images`](crate::device::Properties::max_descriptor_set_storage_images) - /// limit. - MaxDescriptorSetStorageImagesExceeded { provided: u32, max_supported: u32 }, - - /// The `set_layouts` contain more [`DescriptorType::InputAttachment`] descriptors than the - /// [`max_descriptor_set_input_attachments`](crate::device::Properties::max_descriptor_set_input_attachments) - /// limit. - MaxDescriptorSetInputAttachmentsExceeded { provided: u32, max_supported: u32 }, - - /// The `set_layouts` contain more [`DescriptorType::AccelerationStructure`] descriptors than the - /// [`max_descriptor_set_acceleration_structures`](crate::device::Properties::max_descriptor_set_acceleration_structures) - /// limit. - MaxDescriptorSetAccelerationStructuresExceeded { provided: u32, max_supported: u32 }, - - /// The `set_layouts` contain more bound resources in a single stage than the - /// [`max_per_stage_resources`](crate::device::Properties::max_per_stage_resources) - /// limit. - MaxPerStageResourcesExceeded { provided: u32, max_supported: u32 }, - - /// The `set_layouts` contain more [`DescriptorType::Sampler`] and - /// [`DescriptorType::CombinedImageSampler`] descriptors in a single stage than the - /// [`max_per_stage_descriptor_samplers`](crate::device::Properties::max_per_stage_descriptor_samplers) - /// limit. - MaxPerStageDescriptorSamplersExceeded { provided: u32, max_supported: u32 }, - - /// The `set_layouts` contain more [`DescriptorType::UniformBuffer`] and - /// [`DescriptorType::UniformBufferDynamic`] descriptors in a single stage than the - /// [`max_per_stage_descriptor_uniform_buffers`](crate::device::Properties::max_per_stage_descriptor_uniform_buffers) - /// limit. - MaxPerStageDescriptorUniformBuffersExceeded { provided: u32, max_supported: u32 }, - - /// The `set_layouts` contain more [`DescriptorType::StorageBuffer`] and - /// [`DescriptorType::StorageBufferDynamic`] descriptors in a single stage than the - /// [`max_per_stage_descriptor_storage_buffers`](crate::device::Properties::max_per_stage_descriptor_storage_buffers) - /// limit. - MaxPerStageDescriptorStorageBuffersExceeded { provided: u32, max_supported: u32 }, - - /// The `set_layouts` contain more [`DescriptorType::SampledImage`], - /// [`DescriptorType::CombinedImageSampler`] and [`DescriptorType::UniformTexelBuffer`] - /// descriptors in a single stage than the - /// [`max_per_stage_descriptor_sampled_images`](crate::device::Properties::max_per_stage_descriptor_sampled_images) - /// limit. - MaxPerStageDescriptorSampledImagesExceeded { provided: u32, max_supported: u32 }, - - /// The `set_layouts` contain more [`DescriptorType::StorageImage`] and - /// [`DescriptorType::StorageTexelBuffer`] descriptors in a single stage than the - /// [`max_per_stage_descriptor_storage_images`](crate::device::Properties::max_per_stage_descriptor_storage_images) - /// limit. - MaxPerStageDescriptorStorageImagesExceeded { provided: u32, max_supported: u32 }, - - /// The `set_layouts` contain more [`DescriptorType::InputAttachment`] descriptors in a single - /// stage than the - /// [`max_per_stage_descriptor_input_attachments`](crate::device::Properties::max_per_stage_descriptor_input_attachments) - /// limit. - MaxPerStageDescriptorInputAttachmentsExceeded { provided: u32, max_supported: u32 }, - - /// The `set_layouts` contain more [`DescriptorType::AccelerationStructure`] descriptors in a single - /// stage than the - /// [`max_per_stage_descriptor_acceleration_structures`](crate::device::Properties::max_per_stage_descriptor_acceleration_structures) - /// limit. - MaxPerStageDescriptorAccelerationStructuresExceeded { provided: u32, max_supported: u32 }, - - /// An element in `push_constant_ranges` has an `offset + size` greater than the - /// [`max_push_constants_size`](crate::device::Properties::max_push_constants_size) limit. - MaxPushConstantsSizeExceeded { provided: u32, max_supported: u32 }, - - /// A shader stage appears in multiple elements of `push_constant_ranges`. - PushConstantRangesStageMultiple, - - /// Multiple elements of `set_layouts` have `push_descriptor` enabled. - SetLayoutsPushDescriptorMultiple, + pub _ne: crate::NonExhaustive, } -impl Error for PipelineLayoutCreationError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::OomError(err) => Some(err), - _ => None, +impl Default for PipelineLayoutCreateInfo { + #[inline] + fn default() -> Self { + Self { + flags: PipelineLayoutCreateFlags::empty(), + set_layouts: Vec::new(), + push_constant_ranges: Vec::new(), + _ne: crate::NonExhaustive(()), } } } -impl Display for PipelineLayoutCreationError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::OomError(_) => write!(f, "not enough memory available"), - Self::RequirementNotMet { - required_for, - requires_one_of, - } => write!( - f, - "a requirement was not met for: {}; requires one of: {}", - required_for, requires_one_of, - ), - Self::MaxBoundDescriptorSetsExceeded { - provided, - max_supported, - } => write!( - f, - "the number of elements in `set_layouts` ({}) is greater than the \ - `max_bound_descriptor_sets` limit ({})", - provided, max_supported, - ), - Self::MaxDescriptorSetSamplersExceeded { - provided, - max_supported, - } => write!( - f, - "the `set_layouts` contain more `DescriptorType::Sampler` and \ - `DescriptorType::CombinedImageSampler` descriptors ({}) than the \ - `max_descriptor_set_samplers` limit ({})", - provided, max_supported, - ), - Self::MaxDescriptorSetUniformBuffersExceeded { - provided, - max_supported, - } => write!( - f, - "the `set_layouts` contain more `DescriptorType::UniformBuffer` descriptors ({}) \ - than the `max_descriptor_set_uniform_buffers` limit ({})", - provided, max_supported, - ), - Self::MaxDescriptorSetUniformBuffersDynamicExceeded { - provided, - max_supported, - } => write!( - f, - "the `set_layouts` contain more `DescriptorType::UniformBufferDynamic` descriptors \ - ({}) than the `max_descriptor_set_uniform_buffers_dynamic` limit ({})", - provided, max_supported, - ), - Self::MaxDescriptorSetStorageBuffersExceeded { - provided, - max_supported, - } => write!( - f, - "the `set_layouts` contain more `DescriptorType::StorageBuffer` descriptors ({}) \ - than the `max_descriptor_set_storage_buffers` limit ({})", - provided, max_supported, - ), - Self::MaxDescriptorSetStorageBuffersDynamicExceeded { - provided, - max_supported, - } => write!( - f, - "the `set_layouts` contain more `DescriptorType::StorageBufferDynamic` descriptors \ - ({}) than the `max_descriptor_set_storage_buffers_dynamic` limit ({})", - provided, max_supported, - ), - Self::MaxDescriptorSetSampledImagesExceeded { - provided, - max_supported, - } => write!( - f, - "the `set_layouts` contain more `DescriptorType::SampledImage`, \ - `DescriptorType::CombinedImageSampler` and `DescriptorType::UniformTexelBuffer` \ - descriptors ({}) than the `max_descriptor_set_sampled_images` limit ({})", - provided, max_supported, - ), - Self::MaxDescriptorSetStorageImagesExceeded { - provided, - max_supported, - } => write!( - f, - "the `set_layouts` contain more `DescriptorType::StorageImage` and \ - `DescriptorType::StorageTexelBuffer` descriptors ({}) than the \ - `max_descriptor_set_storage_images` limit ({})", - provided, max_supported, - ), - Self::MaxDescriptorSetInputAttachmentsExceeded { - provided, - max_supported, - } => write!( - f, - "the `set_layouts` contain more `DescriptorType::InputAttachment` descriptors ({}) \ - than the `max_descriptor_set_input_attachments` limit ({})", - provided, max_supported, - ), - Self::MaxDescriptorSetAccelerationStructuresExceeded { - provided, - max_supported, - } => write!( - f, - "the `set_layouts` contain more `DescriptorType::AccelerationStructure` descriptors ({}) \ - than the `max_descriptor_set_acceleration_structures` limit ({})", - provided, max_supported, - ), - Self::MaxPerStageResourcesExceeded { - provided, - max_supported, - } => write!( - f, - "the `set_layouts` contain more bound resources ({}) in a single stage than the \ - `max_per_stage_resources` limit ({})", - provided, max_supported, - ), - Self::MaxPerStageDescriptorSamplersExceeded { - provided, - max_supported, - } => write!( - f, - "the `set_layouts` contain more `DescriptorType::Sampler` and \ - `DescriptorType::CombinedImageSampler` descriptors ({}) in a single stage than the \ - `max_per_stage_descriptor_set_samplers` limit ({})", - provided, max_supported, - ), - Self::MaxPerStageDescriptorUniformBuffersExceeded { - provided, - max_supported, - } => write!( - f, - "the `set_layouts` contain more `DescriptorType::UniformBuffer` and \ - `DescriptorType::UniformBufferDynamic` descriptors ({}) in a single stage than the \ - `max_per_stage_descriptor_set_uniform_buffers` limit ({})", - provided, max_supported, - ), - Self::MaxPerStageDescriptorStorageBuffersExceeded { - provided, - max_supported, - } => write!( - f, - "the `set_layouts` contain more `DescriptorType::StorageBuffer` and \ - `DescriptorType::StorageBufferDynamic` descriptors ({}) in a single stage than the \ - `max_per_stage_descriptor_set_storage_buffers` limit ({})", - provided, max_supported, - ), - Self::MaxPerStageDescriptorSampledImagesExceeded { - provided, - max_supported, - } => write!( - f, - "the `set_layouts` contain more `DescriptorType::SampledImage`, \ - `DescriptorType::CombinedImageSampler` and `DescriptorType::UniformTexelBuffer` \ - descriptors ({}) in a single stage than the \ - `max_per_stage_descriptor_set_sampled_images` limit ({})", - provided, max_supported, - ), - Self::MaxPerStageDescriptorStorageImagesExceeded { - provided, - max_supported, - } => write!( - f, - "the `set_layouts` contain more `DescriptorType::StorageImage` and \ - `DescriptorType::StorageTexelBuffer` descriptors ({}) in a single stage than the \ - `max_per_stage_descriptor_set_storage_images` limit ({})", - provided, max_supported, - ), - Self::MaxPerStageDescriptorInputAttachmentsExceeded { - provided, - max_supported, - } => write!( - f, - "the `set_layouts` contain more `DescriptorType::InputAttachment` descriptors ({}) \ - in a single stage than the `max_per_stage_descriptor_set_input_attachments` limit \ - ({})", - provided, max_supported, - ), - Self::MaxPerStageDescriptorAccelerationStructuresExceeded { - provided, - max_supported, - } => write!( - f, - "the `set_layouts` contain more `DescriptorType::AccelerationStructure` descriptors ({}) \ - in a single stage than the `max_per_stage_descriptor_set_acceleration_structures` limit \ - ({})", - provided, max_supported, - ), - Self::MaxPushConstantsSizeExceeded { - provided, - max_supported, - } => write!( - f, - "an element in `push_constant_ranges` has an `offset + size` ({}) greater than the \ - `max_push_constants_size` limit ({})", - provided, max_supported, - ), - Self::PushConstantRangesStageMultiple => write!( - f, - "a shader stage appears in multiple elements of `push_constant_ranges`", - ), - Self::SetLayoutsPushDescriptorMultiple => write!( - f, - "multiple elements of `set_layouts` have `push_descriptor` enabled", - ), +impl PipelineLayoutCreateInfo { + pub(crate) fn validate(&self, device: &Device) -> Result<(), ValidationError> { + let properties = device.physical_device().properties(); + + let &Self { + flags, + ref set_layouts, + ref push_constant_ranges, + _ne: _, + } = self; + + flags + .validate_device(device) + .map_err(|err| ValidationError { + context: "flags".into(), + vuids: &["VUID-VkPipelineLayoutCreateInfo-flags-parameter"], + ..ValidationError::from_requirement(err) + })?; + + if set_layouts.len() > properties.max_bound_descriptor_sets as usize { + return Err(ValidationError { + context: "set_layouts".into(), + problem: "the length exceeds the max_bound_descriptor_sets limit".into(), + vuids: &["VUID-VkPipelineLayoutCreateInfo-setLayoutCount-00286"], + ..Default::default() + }); + } + + struct DescriptorLimit { + descriptor_types: &'static [DescriptorType], + get_limit: fn(&Properties) -> u32, + limit_name: &'static str, + vuids: &'static [&'static str], + } + + const PER_STAGE_DESCRIPTOR_LIMITS: [DescriptorLimit; 7] = [ + DescriptorLimit { + descriptor_types: &[ + DescriptorType::Sampler, + DescriptorType::CombinedImageSampler, + ], + get_limit: |p| p.max_per_stage_descriptor_samplers, + limit_name: "max_per_stage_descriptor_samplers", + vuids: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03016"], + }, + DescriptorLimit { + descriptor_types: &[ + DescriptorType::UniformBuffer, + DescriptorType::UniformBufferDynamic, + ], + get_limit: |p| p.max_per_stage_descriptor_uniform_buffers, + limit_name: "max_per_stage_descriptor_uniform_buffers", + vuids: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03017"], + }, + DescriptorLimit { + descriptor_types: &[ + DescriptorType::StorageBuffer, + DescriptorType::StorageBufferDynamic, + ], + get_limit: |p| p.max_per_stage_descriptor_storage_buffers, + limit_name: "max_per_stage_descriptor_storage_buffers", + vuids: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03018"], + }, + DescriptorLimit { + descriptor_types: &[ + DescriptorType::CombinedImageSampler, + DescriptorType::SampledImage, + DescriptorType::UniformTexelBuffer, + ], + get_limit: |p| p.max_per_stage_descriptor_sampled_images, + limit_name: "max_per_stage_descriptor_sampled_images", + vuids: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-06939"], + }, + DescriptorLimit { + descriptor_types: &[ + DescriptorType::StorageImage, + DescriptorType::StorageTexelBuffer, + ], + get_limit: |p| p.max_per_stage_descriptor_storage_images, + limit_name: "max_per_stage_descriptor_storage_images", + vuids: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03020"], + }, + DescriptorLimit { + descriptor_types: &[DescriptorType::InputAttachment], + get_limit: |p| p.max_per_stage_descriptor_input_attachments, + limit_name: "max_per_stage_descriptor_input_attachments", + vuids: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03021"], + }, + DescriptorLimit { + descriptor_types: &[DescriptorType::AccelerationStructure], + get_limit: |p| { + p.max_per_stage_descriptor_acceleration_structures + .unwrap_or(0) + }, + limit_name: "max_per_stage_descriptor_acceleration_structures", + vuids: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03571"], + }, + ]; + + const TOTAL_DESCRIPTOR_LIMITS: [DescriptorLimit; 9] = [ + DescriptorLimit { + descriptor_types: &[ + DescriptorType::Sampler, + DescriptorType::CombinedImageSampler, + ], + get_limit: |p| p.max_descriptor_set_samplers, + limit_name: "max_descriptor_set_samplers", + vuids: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03028"], + }, + DescriptorLimit { + descriptor_types: &[DescriptorType::UniformBuffer], + get_limit: |p| p.max_descriptor_set_uniform_buffers, + limit_name: "max_descriptor_set_uniform_buffers", + vuids: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03029"], + }, + DescriptorLimit { + descriptor_types: &[DescriptorType::UniformBufferDynamic], + get_limit: |p| p.max_descriptor_set_uniform_buffers_dynamic, + limit_name: "max_descriptor_set_uniform_buffers_dynamic", + vuids: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03030"], + }, + DescriptorLimit { + descriptor_types: &[DescriptorType::StorageBuffer], + get_limit: |p| p.max_descriptor_set_storage_buffers, + limit_name: "max_descriptor_set_storage_buffers", + vuids: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03031"], + }, + DescriptorLimit { + descriptor_types: &[DescriptorType::StorageBufferDynamic], + get_limit: |p| p.max_descriptor_set_storage_buffers_dynamic, + limit_name: "max_descriptor_set_storage_buffers_dynamic", + vuids: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03032"], + }, + DescriptorLimit { + descriptor_types: &[ + DescriptorType::CombinedImageSampler, + DescriptorType::SampledImage, + DescriptorType::UniformTexelBuffer, + ], + get_limit: |p| p.max_descriptor_set_sampled_images, + limit_name: "max_descriptor_set_sampled_images", + vuids: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03033"], + }, + DescriptorLimit { + descriptor_types: &[ + DescriptorType::StorageImage, + DescriptorType::StorageTexelBuffer, + ], + get_limit: |p| p.max_descriptor_set_storage_images, + limit_name: "max_descriptor_set_storage_images", + vuids: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03034"], + }, + DescriptorLimit { + descriptor_types: &[DescriptorType::InputAttachment], + get_limit: |p| p.max_descriptor_set_input_attachments, + limit_name: "max_descriptor_set_input_attachments", + vuids: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03035"], + }, + DescriptorLimit { + descriptor_types: &[DescriptorType::AccelerationStructure], + get_limit: |p| p.max_descriptor_set_acceleration_structures.unwrap_or(0), + limit_name: "max_descriptor_set_acceleration_structures", + vuids: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03573"], + }, + ]; + + let mut per_stage_descriptors: [HashMap; + PER_STAGE_DESCRIPTOR_LIMITS.len()] = array::from_fn(|_| HashMap::default()); + let mut total_descriptors = [0; TOTAL_DESCRIPTOR_LIMITS.len()]; + let mut has_push_descriptor_set = false; + + for (_set_num, set_layout) in set_layouts.iter().enumerate() { + assert_eq!(device, set_layout.device().as_ref()); + + if set_layout + .flags() + .intersects(DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR) + { + if has_push_descriptor_set { + return Err(ValidationError { + context: "set_layouts".into(), + problem: "contains more than one descriptor set layout whose flags \ + DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR" + .into(), + vuids: &["VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00293"], + ..Default::default() + }); + } + + has_push_descriptor_set = true; + } + + for layout_binding in set_layout.bindings().values() { + let &DescriptorSetLayoutBinding { + binding_flags: _, + descriptor_type, + descriptor_count, + stages, + immutable_samplers: _, + _ne: _, + } = layout_binding; + + for (limit, count) in PER_STAGE_DESCRIPTOR_LIMITS + .iter() + .zip(&mut per_stage_descriptors) + { + if limit.descriptor_types.contains(&descriptor_type) { + for stage in stages { + *count.entry(stage).or_default() += descriptor_count; + } + } + } + + for (limit, count) in TOTAL_DESCRIPTOR_LIMITS.iter().zip(&mut total_descriptors) { + if limit.descriptor_types.contains(&descriptor_type) { + *count += descriptor_count; + } + } + } } + + for (limit, count) in PER_STAGE_DESCRIPTOR_LIMITS + .iter() + .zip(per_stage_descriptors) + { + if let Some((max_stage, max_count)) = count.into_iter().max_by_key(|(_, c)| *c) { + if max_count > (limit.get_limit)(properties) { + return Err(ValidationError { + context: "set_layouts".into(), + problem: format!( + "the combined number of {} descriptors accessible to the \ + ShaderStage::{:?} stage exceeds the {} limit", + limit.descriptor_types[1..].iter().fold( + format!("DescriptorType::{:?}", limit.descriptor_types[0]), + |mut s, dt| { + write!(s, " + DescriptorType::{:?}", dt).unwrap(); + s + } + ), + max_stage, + limit.limit_name, + ) + .into(), + vuids: limit.vuids, + ..Default::default() + }); + } + } + } + + for (limit, count) in TOTAL_DESCRIPTOR_LIMITS.iter().zip(total_descriptors) { + if count > (limit.get_limit)(properties) { + return Err(ValidationError { + context: "set_layouts".into(), + problem: format!( + "the combined number of {} descriptors accessible across all \ + shader stages exceeds the {} limit", + limit.descriptor_types[1..].iter().fold( + format!("DescriptorType::{:?}", limit.descriptor_types[0]), + |mut s, dt| { + write!(s, " + DescriptorType::{:?}", dt).unwrap(); + s + } + ), + limit.limit_name, + ) + .into(), + vuids: limit.vuids, + ..Default::default() + }); + } + } + + let mut seen_stages = ShaderStages::empty(); + + for (range_index, range) in push_constant_ranges.iter().enumerate() { + range + .validate(device) + .map_err(|err| err.add_context(format!("push_constant_ranges[{}]", range_index)))?; + + let &PushConstantRange { + stages, + offset: _, + size: _, + } = range; + + if seen_stages.intersects(stages) { + return Err(ValidationError { + context: "push_constant_ranges".into(), + problem: "contains more than one range with the same stage".into(), + vuids: &["VUID-VkPipelineLayoutCreateInfo-pPushConstantRanges-00292"], + ..Default::default() + }); + } + + seen_stages |= stages; + } + + Ok(()) } } -impl From for PipelineLayoutCreationError { - fn from(err: OomError) -> PipelineLayoutCreationError { - PipelineLayoutCreationError::OomError(err) - } +vulkan_bitflags! { + #[non_exhaustive] + + /// Flags that control how a pipeline layout is created. + PipelineLayoutCreateFlags = PipelineLayoutCreateFlags(u32); + + /* TODO: enable + // TODO: document + INDEPENDENT_SETS = INDEPENDENT_SETS_EXT { + device_extensions: [ext_graphics_pipeline_library], + }, */ } -impl From for PipelineLayoutCreationError { - fn from(err: RuntimeError) -> PipelineLayoutCreationError { - match err { - err @ RuntimeError::OutOfHostMemory => { - PipelineLayoutCreationError::OomError(OomError::from(err)) - } - err @ RuntimeError::OutOfDeviceMemory => { - PipelineLayoutCreationError::OomError(OomError::from(err)) - } - _ => panic!("unexpected error: {:?}", err), +/// Description of a range of the push constants of a pipeline layout. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct PushConstantRange { + /// The stages which can access this range. A stage can access at most one push constant range. + /// + /// The default value is [`ShaderStages::empty()`], which must be overridden. + pub stages: ShaderStages, + + /// Offset in bytes from the start of the push constants to this range. + /// + /// The value must be a multiple of 4. + /// + /// The default value is `0`. + pub offset: u32, + + /// Size in bytes of the range. + /// + /// The value must be a multiple of 4, and not 0. + /// + /// The default value is `0`, which must be overridden. + pub size: u32, +} + +impl Default for PushConstantRange { + #[inline] + fn default() -> Self { + Self { + stages: ShaderStages::empty(), + offset: 0, + size: 0, } } } -impl From for PipelineLayoutCreationError { - fn from(err: RequirementNotMet) -> Self { - Self::RequirementNotMet { - required_for: err.required_for, - requires_one_of: err.requires_one_of, +impl PushConstantRange { + pub(crate) fn validate(&self, device: &Device) -> Result<(), ValidationError> { + let &Self { + stages, + offset, + size, + } = self; + + stages + .validate_device(device) + .map_err(|err| ValidationError { + context: "stages".into(), + vuids: &["VUID-VkPushConstantRange-stageFlags-parameter"], + ..ValidationError::from_requirement(err) + })?; + + if stages.is_empty() { + return Err(ValidationError { + context: "stages".into(), + problem: "is empty".into(), + vuids: &["VUID-VkPushConstantRange-stageFlags-requiredbitmask"], + ..Default::default() + }); } + + let max_push_constants_size = device + .physical_device() + .properties() + .max_push_constants_size; + + if offset >= max_push_constants_size { + return Err(ValidationError { + context: "offset".into(), + problem: "is not less than the max_push_constants_size limit".into(), + vuids: &["VUID-VkPushConstantRange-offset-00294"], + ..Default::default() + }); + } + + if offset % 4 != 0 { + return Err(ValidationError { + context: "offset".into(), + problem: "is not a multiple of 4".into(), + vuids: &["VUID-VkPushConstantRange-offset-00295"], + ..Default::default() + }); + } + + if size == 0 { + return Err(ValidationError { + context: "size".into(), + problem: "is zero".into(), + vuids: &["VUID-VkPushConstantRange-size-00296"], + ..Default::default() + }); + } + + if size % 4 != 0 { + return Err(ValidationError { + context: "size".into(), + problem: "is not a multiple of 4".into(), + vuids: &["VUID-VkPushConstantRange-size-00297"], + ..Default::default() + }); + } + + if size > max_push_constants_size - offset { + return Err(ValidationError { + problem: "size is greater than max_push_constants_size limit minus offset".into(), + vuids: &["VUID-VkPushConstantRange-size-00298"], + ..Default::default() + }); + } + + Ok(()) } } @@ -1155,42 +931,11 @@ impl Display for PipelineLayoutSupersetError { } } -/// Parameters to create a new `PipelineLayout`. -#[derive(Clone, Debug)] -pub struct PipelineLayoutCreateInfo { - /// The descriptor set layouts that should be part of the pipeline layout. - /// - /// They are provided in order of set number. - /// - /// The default value is empty. - pub set_layouts: Vec>, - - /// The ranges of push constants that the pipeline will access. - /// - /// A shader stage can only appear in one element of the list, but it is possible to combine - /// ranges for multiple shader stages if they are the same. - /// - /// The default value is empty. - pub push_constant_ranges: Vec, - - pub _ne: crate::NonExhaustive, -} - -impl Default for PipelineLayoutCreateInfo { - #[inline] - fn default() -> Self { - Self { - set_layouts: Vec::new(), - push_constant_ranges: Vec::new(), - _ne: crate::NonExhaustive(()), - } - } -} - /// Parameters to create a new `PipelineLayout` as well as its accompanying `DescriptorSetLayout` /// objects. #[derive(Clone, Debug)] pub struct PipelineDescriptorSetLayoutCreateInfo { + pub flags: PipelineLayoutCreateFlags, pub set_layouts: Vec, pub push_constant_ranges: Vec, } @@ -1259,6 +1004,7 @@ impl PipelineDescriptorSetLayoutCreateInfo { } Self { + flags: PipelineLayoutCreateFlags::empty(), set_layouts, push_constant_ranges, } @@ -1271,6 +1017,7 @@ impl PipelineDescriptorSetLayoutCreateInfo { device: Arc, ) -> Result { let PipelineDescriptorSetLayoutCreateInfo { + flags, set_layouts, push_constant_ranges, } = self; @@ -1289,6 +1036,7 @@ impl PipelineDescriptorSetLayoutCreateInfo { .collect::, _>>()?; Ok(PipelineLayoutCreateInfo { + flags, set_layouts, push_constant_ranges, _ne: crate::NonExhaustive(()), @@ -1299,7 +1047,7 @@ impl PipelineDescriptorSetLayoutCreateInfo { #[derive(Clone, Debug)] pub struct IntoPipelineLayoutCreateInfoError { pub set_num: u32, - pub error: DescriptorSetLayoutCreationError, + pub error: VulkanError, } impl Display for IntoPipelineLayoutCreateInfoError { @@ -1318,62 +1066,6 @@ impl Error for IntoPipelineLayoutCreateInfoError { } } -/// Description of a range of the push constants of a pipeline layout. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct PushConstantRange { - /// The stages which can access this range. A stage can access at most one push constant range. - /// - /// The default value is [`ShaderStages::empty()`], which must be overridden. - pub stages: ShaderStages, - - /// Offset in bytes from the start of the push constants to this range. - /// - /// The value must be a multiple of 4. - /// - /// The default value is `0`. - pub offset: u32, - - /// Size in bytes of the range. - /// - /// The value must be a multiple of 4, and not 0. - /// - /// The default value is `0`, which must be overridden. - pub size: u32, -} - -impl Default for PushConstantRange { - #[inline] - fn default() -> Self { - Self { - stages: ShaderStages::empty(), - offset: 0, - size: 0, - } - } -} - -// Helper struct for the main function. -#[derive(Default)] -struct Counter { - count: HashMap, -} - -impl Counter { - fn increment(&mut self, num: u32, stages: ShaderStages) { - for stage in stages { - *self.count.entry(stage).or_default() += num; - } - } - - fn total(&self) -> u32 { - self.count.values().copied().sum() - } - - fn max_per_stage(&self) -> u32 { - self.count.values().copied().max().unwrap_or(0) - } -} - #[cfg(test)] mod tests { diff --git a/vulkano/src/sampler/mod.rs b/vulkano/src/sampler/mod.rs index 879d763d2b..306a1de80d 100644 --- a/vulkano/src/sampler/mod.rs +++ b/vulkano/src/sampler/mod.rs @@ -54,7 +54,7 @@ use crate::{ macros::{impl_id_counter, vulkan_enum}, pipeline::graphics::depth_stencil::CompareOp, shader::ShaderScalarType, - OomError, RequirementNotMet, RequiresOneOf, RuntimeError, VulkanObject, + OomError, RequirementNotMet, RequiresOneOf, RuntimeError, ValidationError, VulkanObject, }; use std::{ error::Error, @@ -514,7 +514,7 @@ impl Sampler { pub fn check_can_sample( &self, image_view: &(impl ImageViewAbstract + ?Sized), - ) -> Result<(), SamplerImageViewIncompatibleError> { + ) -> Result<(), ValidationError> { /* Note: Most of these checks come from the Instruction/Sampler/Image View Validation section, and are not strictly VUIDs. @@ -527,7 +527,13 @@ impl Sampler { .format_features() .intersects(FormatFeatures::SAMPLED_IMAGE_DEPTH_COMPARISON) { - return Err(SamplerImageViewIncompatibleError::DepthComparisonNotSupported); + return Err(ValidationError { + problem: "the sampler has depth comparison enabled, and \ + the image view's format features do not include \ + FormatFeatures::SAMPLED_IMAGE_DEPTH_COMPARISON" + .into(), + ..Default::default() + }); } // The SPIR-V instruction is one of the OpImage*Dref* instructions, the image @@ -538,7 +544,12 @@ impl Sampler { .aspects .intersects(ImageAspects::DEPTH) { - return Err(SamplerImageViewIncompatibleError::DepthComparisonWrongAspect); + return Err(ValidationError { + problem: "the sampler has depth comparison enabled, and \ + the image view's aspects do not include ImageAspects::DEPTH" + .into(), + ..Default::default() + }); } } else { if !image_view @@ -547,12 +558,24 @@ impl Sampler { { // VUID-vkCmdDispatch-magFilter-04553 if self.mag_filter == Filter::Linear || self.min_filter == Filter::Linear { - return Err(SamplerImageViewIncompatibleError::FilterLinearNotSupported); + return Err(ValidationError { + problem: "the sampler's mag_filter or min_filter is Filter::Linear, and \ + the image view's format features do not include \ + FormatFeatures::SAMPLED_IMAGE_FILTER_LINEAR" + .into(), + ..Default::default() + }); } // VUID-vkCmdDispatch-mipmapMode-04770 if self.mipmap_mode == SamplerMipmapMode::Linear { - return Err(SamplerImageViewIncompatibleError::MipmapModeLinearNotSupported); + return Err(ValidationError { + problem: "the sampler's mipmap_mode is SamplerMipmapMpde::Linear, and \ + the image view's format features do not include \ + FormatFeatures::SAMPLED_IMAGE_FILTER_LINEAR" + .into(), + ..Default::default() + }); } } } @@ -563,12 +586,24 @@ impl Sampler { .format_features() .intersects(FormatFeatures::SAMPLED_IMAGE_FILTER_CUBIC) { - return Err(SamplerImageViewIncompatibleError::FilterCubicNotSupported); + return Err(ValidationError { + problem: "the sampler's mag_filter or min_filter is Filter::Cubic, and \ + the image view's format features do not include \ + FormatFeatures::SAMPLED_IMAGE_FILTER_CUBIC" + .into(), + ..Default::default() + }); } // VUID-vkCmdDispatch-filterCubic-02694 if !image_view.filter_cubic() { - return Err(SamplerImageViewIncompatibleError::FilterCubicNotSupported); + return Err(ValidationError { + problem: "the sampler's mag_filter or min_filter is Filter::Cubic, and \ + the image view does not support this, as returned by \ + PhysicalDevice::image_format_properties" + .into(), + ..Default::default() + }); } // VUID-vkCmdDispatch-filterCubicMinmax-02695 @@ -577,7 +612,15 @@ impl Sampler { SamplerReductionMode::Min | SamplerReductionMode::Max ) && !image_view.filter_cubic_minmax() { - return Err(SamplerImageViewIncompatibleError::FilterCubicMinmaxNotSupported); + return Err(ValidationError { + problem: "the sampler's mag_filter or min_filter is Filter::Cubic, and \ + the its reduction_mode is SamplerReductionMode::Min or \ + SamplerReductionMode::Max, and + the image view does not support this, as returned by \ + PhysicalDevice::image_format_properties" + .into(), + ..Default::default() + }); } } @@ -613,9 +656,12 @@ impl Sampler { view_scalar_type, ShaderScalarType::Sint | ShaderScalarType::Uint ) { - return Err( - SamplerImageViewIncompatibleError::BorderColorFormatNotCompatible, - ); + return Err(ValidationError { + problem: "the sampler has an integer border color, and \ + the image view does not have an integer format" + .into(), + ..Default::default() + }); } } BorderColor::FloatTransparentBlack @@ -625,9 +671,12 @@ impl Sampler { // format is not one of the VkFormat float types or a depth // component of a depth/stencil format. if !matches!(view_scalar_type, ShaderScalarType::Float) { - return Err( - SamplerImageViewIncompatibleError::BorderColorFormatNotCompatible, - ); + return Err(ValidationError { + problem: "the sampler has an floating-point border color, and \ + the image view does not have a floating-point format" + .into(), + ..Default::default() + }); } } } @@ -644,9 +693,12 @@ impl Sampler { BorderColor::FloatOpaqueBlack | BorderColor::IntOpaqueBlack ) && !image_view.component_mapping().is_identity() { - return Err( - SamplerImageViewIncompatibleError::BorderColorOpaqueBlackNotIdentitySwizzled, - ); + return Err(ValidationError { + problem: "the sampler has an opaque black border color, and \ + the image view is not identity swizzled" + .into(), + ..Default::default() + }); } } @@ -661,9 +713,12 @@ impl Sampler { image_view.view_type(), ImageViewType::Dim1d | ImageViewType::Dim2d ) { - return Err( - SamplerImageViewIncompatibleError::UnnormalizedCoordinatesViewTypeNotCompatible, - ); + return Err(ValidationError { + problem: "the sampler uses unnormalized coordinates, and \ + the image view's type is not ImageViewtype::Dim1d or ImageViewType::Dim2d" + .into(), + ..Default::default() + }); } // The image view must have a single layer and a single mip level. @@ -671,9 +726,12 @@ impl Sampler { - image_view.subresource_range().mip_levels.start != 1 { - return Err( - SamplerImageViewIncompatibleError::UnnormalizedCoordinatesMultipleMipLevels, - ); + return Err(ValidationError { + problem: "the sampler uses unnormalized coordinates, and \ + the image view has more than one mip level" + .into(), + ..Default::default() + }); } } @@ -1483,103 +1541,6 @@ vulkan_enum! { Max = MAX, } -#[derive(Clone, Copy, Debug)] -pub enum SamplerImageViewIncompatibleError { - /// The sampler has a border color with a numeric type different from the image view. - BorderColorFormatNotCompatible, - - /// The sampler has an opaque black border color, but the image view is not identity swizzled. - BorderColorOpaqueBlackNotIdentitySwizzled, - - /// The sampler has depth comparison enabled, but this is not supported by the image view. - DepthComparisonNotSupported, - - /// The sampler has depth comparison enabled, but the image view does not select the `depth` - /// aspect. - DepthComparisonWrongAspect, - - /// The sampler uses a linear filter, but this is not supported by the image view's format - /// features. - FilterLinearNotSupported, - - /// The sampler uses a cubic filter, but this is not supported by the image view's format - /// features. - FilterCubicNotSupported, - - /// The sampler uses a cubic filter with a `Min` or `Max` reduction mode, but this is not - /// supported by the image view's format features. - FilterCubicMinmaxNotSupported, - - /// The sampler uses a linear mipmap mode, but this is not supported by the image view's format - /// features. - MipmapModeLinearNotSupported, - - /// The sampler uses unnormalized coordinates, but the image view has multiple mip levels. - UnnormalizedCoordinatesMultipleMipLevels, - - /// The sampler uses unnormalized coordinates, but the image view has a type other than `Dim1d` - /// or `Dim2d`. - UnnormalizedCoordinatesViewTypeNotCompatible, -} - -impl Error for SamplerImageViewIncompatibleError {} - -impl Display for SamplerImageViewIncompatibleError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::BorderColorFormatNotCompatible => write!( - f, - "the sampler has a border color with a numeric type different from the image view", - ), - Self::BorderColorOpaqueBlackNotIdentitySwizzled => write!( - f, - "the sampler has an opaque black border color, but the image view is not identity \ - swizzled", - ), - Self::DepthComparisonNotSupported => write!( - f, - "the sampler has depth comparison enabled, but this is not supported by the image \ - view", - ), - Self::DepthComparisonWrongAspect => write!( - f, - "the sampler has depth comparison enabled, but the image view does not select the \ - `depth` aspect", - ), - Self::FilterLinearNotSupported => write!( - f, - "the sampler uses a linear filter, but this is not supported by the image view's \ - format features", - ), - Self::FilterCubicNotSupported => write!( - f, - "the sampler uses a cubic filter, but this is not supported by the image view's \ - format features", - ), - Self::FilterCubicMinmaxNotSupported => write!( - f, - "the sampler uses a cubic filter with a `Min` or `Max` reduction mode, but this is \ - not supported by the image view's format features", - ), - Self::MipmapModeLinearNotSupported => write!( - f, - "the sampler uses a linear mipmap mode, but this is not supported by the image \ - view's format features", - ), - Self::UnnormalizedCoordinatesMultipleMipLevels => write!( - f, - "the sampler uses unnormalized coordinates, but the image view has multiple mip \ - levels", - ), - Self::UnnormalizedCoordinatesViewTypeNotCompatible => write!( - f, - "the sampler uses unnormalized coordinates, but the image view has a type other \ - than `Dim1d` or `Dim2d`", - ), - } - } -} - #[cfg(test)] mod tests { use crate::{ From be249fd10cacfd421d08ef0dd48a30a52bab5ff1 Mon Sep 17 00:00:00 2001 From: Rua Date: Sat, 3 Jun 2023 10:03:26 +0200 Subject: [PATCH 2/5] Typo --- vulkano/src/descriptor_set/update.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vulkano/src/descriptor_set/update.rs b/vulkano/src/descriptor_set/update.rs index 552bad01e9..c58bc5b1e8 100644 --- a/vulkano/src/descriptor_set/update.rs +++ b/vulkano/src/descriptor_set/update.rs @@ -1609,7 +1609,7 @@ impl CopyDescriptorSet { if src_layout_binding.descriptor_type != dst_layout_binding.descriptor_type { return Err(ValidationError { - problem: "the descriptor type of dst_binding within dst_set does not equal the \ + problem: "the descriptor type of src_binding within src_set does not equal the \ descriptor type of dst_binding within dst_set" .into(), vuids: &["VUID-VkCopyDescriptorSet-dstBinding-02632"], From ce0eaf24a30d71efda1b0ada29fb361674acf679 Mon Sep 17 00:00:00 2001 From: Rua Date: Sat, 3 Jun 2023 10:18:34 +0200 Subject: [PATCH 3/5] Errors --- vulkano/src/command_buffer/commands/bind_push.rs | 4 ++-- vulkano/src/descriptor_set/layout.rs | 4 ++-- vulkano/src/descriptor_set/update.rs | 16 ++++++++-------- vulkano/src/device/mod.rs | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/vulkano/src/command_buffer/commands/bind_push.rs b/vulkano/src/command_buffer/commands/bind_push.rs index 06a7f54054..1d1dff8880 100644 --- a/vulkano/src/command_buffer/commands/bind_push.rs +++ b/vulkano/src/command_buffer/commands/bind_push.rs @@ -774,10 +774,10 @@ where ) -> Result<(), ValidationError> { if !self.device().enabled_extensions().khr_push_descriptor { return Err(ValidationError { - requires_one_of: Some(RequiresOneOf { + requires_one_of: RequiresOneOf { device_extensions: &["khr_push_descriptor"], ..Default::default() - }), + }, ..Default::default() }); } diff --git a/vulkano/src/descriptor_set/layout.rs b/vulkano/src/descriptor_set/layout.rs index edf66d8429..c88c46ecc7 100644 --- a/vulkano/src/descriptor_set/layout.rs +++ b/vulkano/src/descriptor_set/layout.rs @@ -703,10 +703,10 @@ impl DescriptorSetLayoutBinding { return Err(ValidationError { context: "binding_flags".into(), problem: "contains DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT".into(), - requires_one_of: Some(RequiresOneOf { + requires_one_of: RequiresOneOf { features: &["descriptor_binding_variable_descriptor_count"], ..Default::default() - }), + }, vuids: &["VUID-VkDescriptorSetLayoutBindingFlagsCreateInfo-descriptorBindingVariableDescriptorCount-03014"], }); } diff --git a/vulkano/src/descriptor_set/update.rs b/vulkano/src/descriptor_set/update.rs index c58bc5b1e8..99cc4ec2d6 100644 --- a/vulkano/src/descriptor_set/update.rs +++ b/vulkano/src/descriptor_set/update.rs @@ -440,10 +440,10 @@ impl WriteDescriptorSet { layout_binding.descriptor_type, ) .into(), - requires_one_of: Some(RequiresOneOf { + requires_one_of: RequiresOneOf { features: &["image2_d_view_of3_d"], ..Default::default() - }), + }, vuids: &["VUID-VkDescriptorImageInfo-descriptorType-06713"], }); } @@ -459,10 +459,10 @@ impl WriteDescriptorSet { layout_binding.descriptor_type, ) .into(), - requires_one_of: Some(RequiresOneOf { + requires_one_of: RequiresOneOf { features: &["sampler2_d_view_of3_d"], ..Default::default() - }), + }, vuids: &["VUID-VkDescriptorImageInfo-descriptorType-06714"], }); } @@ -581,10 +581,10 @@ impl WriteDescriptorSet { problem: "this device is a portability subset device, and \ the sampler has depth comparison enabled" .into(), - requires_one_of: Some(RequiresOneOf { + requires_one_of: RequiresOneOf { features: &["mutable_comparison_samplers"], ..Default::default() - }), + }, vuids: &["VUID-VkDescriptorImageInfo-mutableComparisonSamplers-04450"], }); } @@ -666,10 +666,10 @@ impl WriteDescriptorSet { problem: "this device is a portability subset device, and \ the sampler has depth comparison enabled" .into(), - requires_one_of: Some(RequiresOneOf { + requires_one_of: RequiresOneOf { features: &["mutable_comparison_samplers"], ..Default::default() - }), + }, vuids: &[ "VUID-VkDescriptorImageInfo-mutableComparisonSamplers-04450", ], diff --git a/vulkano/src/device/mod.rs b/vulkano/src/device/mod.rs index 13d97b9d7f..9e5ebe3655 100644 --- a/vulkano/src/device/mod.rs +++ b/vulkano/src/device/mod.rs @@ -782,11 +782,11 @@ impl Device { ) -> Result<(), ValidationError> { if !(self.api_version() >= Version::V1_1 || self.enabled_extensions().khr_maintenance3) { return Err(ValidationError { - requires_one_of: Some(RequiresOneOf { + requires_one_of: RequiresOneOf { api_version: Some(Version::V1_1), device_extensions: &["khr_maintenance3"], ..Default::default() - }), + }, ..Default::default() }); } From 70b860a493a0095f345c6da305b37ca633d45e90 Mon Sep 17 00:00:00 2001 From: Rua Date: Sat, 3 Jun 2023 10:56:03 +0200 Subject: [PATCH 4/5] Doctest fix --- vulkano/src/sampler/ycbcr.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/vulkano/src/sampler/ycbcr.rs b/vulkano/src/sampler/ycbcr.rs index 21e6ad6b3f..db97b6c76f 100644 --- a/vulkano/src/sampler/ycbcr.rs +++ b/vulkano/src/sampler/ycbcr.rs @@ -86,6 +86,7 @@ //! &descriptor_set_allocator, //! descriptor_set_layout.clone(), //! [WriteDescriptorSet::image_view(0, image_view)], +//! [], //! ).unwrap(); //! ``` From ab8bc03137d02df88be2ef66f799144e75a4843a Mon Sep 17 00:00:00 2001 From: Rua Date: Sat, 3 Jun 2023 14:45:52 +0200 Subject: [PATCH 5/5] Add missing non_exhaustive --- vulkano/src/descriptor_set/layout.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/vulkano/src/descriptor_set/layout.rs b/vulkano/src/descriptor_set/layout.rs index c88c46ecc7..d3da42f1a3 100644 --- a/vulkano/src/descriptor_set/layout.rs +++ b/vulkano/src/descriptor_set/layout.rs @@ -895,6 +895,7 @@ impl DescriptorType { /// Contains information about the level of support a device has for a particular descriptor set. #[derive(Clone, Debug)] +#[non_exhaustive] pub struct DescriptorSetLayoutSupport { /// If the queried descriptor set layout has a binding with the /// [`DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT`] flag set, then this indicates the