// Copyright (c) 2016 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // at your option. All files in the project carrying such // notice may not be copied, modified, or distributed except // according to those terms. //! Low-level descriptor set. use crate::buffer::BufferAccess; use crate::buffer::BufferInner; use crate::buffer::BufferView; use crate::descriptor_set::layout::DescriptorType; use crate::device::Device; use crate::device::DeviceOwned; use crate::image::view::ImageViewAbstract; use crate::sampler::Sampler; use crate::DeviceSize; use crate::VulkanObject; use smallvec::SmallVec; use std::fmt; use std::ptr; use std::sync::Arc; /// Low-level descriptor set. /// /// Contrary to most other objects in this library, this one doesn't free itself automatically and /// doesn't hold the pool or the device it is associated to. /// Instead it is an object meant to be used with the `UnsafeDescriptorPool`. pub struct UnsafeDescriptorSet { pub(super) set: ash::vk::DescriptorSet, } impl UnsafeDescriptorSet { // 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 /// Modifies a descriptor set. Doesn't check that the writes or copies are correct, and /// doesn't check whether the descriptor set is in use. /// /// **Important**: You must ensure that the `DescriptorSetLayout` object is alive before /// updating a descriptor set. /// /// # Safety /// /// - The `Device` must be the device the pool of this set was created with. /// - The `DescriptorSetLayout` object this set was created with must be alive. /// - Doesn't verify that the things you write in the descriptor set match its layout. /// - Doesn't keep the resources alive. You have to do that yourself. /// - 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(&mut self, device: &Device, writes: I) where I: Iterator, { let fns = device.fns(); // In this function, we build 4 arrays: one array of image descriptors (image_descriptors), // one for buffer descriptors (buffer_descriptors), one for buffer view descriptors // (buffer_views_descriptors), and one for the final list of writes (raw_writes). // Only the final list is passed to Vulkan, but it will contain pointers to the first three // lists in `pImageInfo`, `pBufferInfo` and `pTexelBufferView`. // // In order to handle that, we start by writing null pointers as placeholders in the final // writes, and we store in `raw_writes_img_infos`, `raw_writes_buf_infos` and // `raw_writes_buf_view_infos` the offsets of the pointers compared to the start of the // list. // Once we have finished iterating all the writes requested by the user, we modify // `raw_writes` to point to the correct locations. let mut buffer_descriptors: SmallVec<[_; 64]> = SmallVec::new(); let mut image_descriptors: SmallVec<[_; 64]> = SmallVec::new(); let mut buffer_views_descriptors: SmallVec<[_; 64]> = SmallVec::new(); let mut raw_writes: SmallVec<[_; 64]> = SmallVec::new(); let mut raw_writes_img_infos: SmallVec<[_; 64]> = SmallVec::new(); let mut raw_writes_buf_infos: SmallVec<[_; 64]> = SmallVec::new(); let mut raw_writes_buf_view_infos: SmallVec<[_; 64]> = SmallVec::new(); for indiv_write in writes { // Since the `DescriptorWrite` objects are built only through functions, we know for // sure that it's impossible to have an empty descriptor write. debug_assert!(!indiv_write.inner.is_empty()); // The whole struct thats written here is valid, except for pImageInfo, pBufferInfo // and pTexelBufferView which are placeholder values. raw_writes.push(ash::vk::WriteDescriptorSet { dst_set: self.set, dst_binding: indiv_write.binding, dst_array_element: indiv_write.first_array_element, descriptor_count: indiv_write.inner.len() as u32, descriptor_type: indiv_write.ty().into(), p_image_info: ptr::null(), p_buffer_info: ptr::null(), p_texel_buffer_view: ptr::null(), ..Default::default() }); match indiv_write.inner[0] { DescriptorWriteInner::Sampler(_) | DescriptorWriteInner::CombinedImageSampler(_, _, _) | DescriptorWriteInner::SampledImage(_, _) | DescriptorWriteInner::StorageImage(_, _) | DescriptorWriteInner::InputAttachment(_, _) => { raw_writes_img_infos.push(Some(image_descriptors.len())); raw_writes_buf_infos.push(None); raw_writes_buf_view_infos.push(None); } DescriptorWriteInner::UniformBuffer(_, _, _) | DescriptorWriteInner::StorageBuffer(_, _, _) | DescriptorWriteInner::DynamicUniformBuffer(_, _, _) | DescriptorWriteInner::DynamicStorageBuffer(_, _, _) => { raw_writes_img_infos.push(None); raw_writes_buf_infos.push(Some(buffer_descriptors.len())); raw_writes_buf_view_infos.push(None); } DescriptorWriteInner::UniformTexelBuffer(_) | DescriptorWriteInner::StorageTexelBuffer(_) => { raw_writes_img_infos.push(None); raw_writes_buf_infos.push(None); raw_writes_buf_view_infos.push(Some(buffer_views_descriptors.len())); } } for elem in indiv_write.inner.iter() { match *elem { DescriptorWriteInner::UniformBuffer(buffer, offset, size) | DescriptorWriteInner::DynamicUniformBuffer(buffer, offset, size) => { buffer_descriptors.push(ash::vk::DescriptorBufferInfo { buffer, offset, range: size, }); } DescriptorWriteInner::StorageBuffer(buffer, offset, size) | DescriptorWriteInner::DynamicStorageBuffer(buffer, offset, size) => { buffer_descriptors.push(ash::vk::DescriptorBufferInfo { buffer, offset, range: size, }); } DescriptorWriteInner::Sampler(sampler) => { image_descriptors.push(ash::vk::DescriptorImageInfo { sampler, image_view: ash::vk::ImageView::null(), image_layout: ash::vk::ImageLayout::UNDEFINED, }); } DescriptorWriteInner::CombinedImageSampler(sampler, view, layout) => { image_descriptors.push(ash::vk::DescriptorImageInfo { sampler, image_view: view, image_layout: layout, }); } DescriptorWriteInner::StorageImage(view, layout) => { image_descriptors.push(ash::vk::DescriptorImageInfo { sampler: ash::vk::Sampler::null(), image_view: view, image_layout: layout, }); } DescriptorWriteInner::SampledImage(view, layout) => { image_descriptors.push(ash::vk::DescriptorImageInfo { sampler: ash::vk::Sampler::null(), image_view: view, image_layout: layout, }); } DescriptorWriteInner::InputAttachment(view, layout) => { image_descriptors.push(ash::vk::DescriptorImageInfo { sampler: ash::vk::Sampler::null(), image_view: view, image_layout: layout, }); } DescriptorWriteInner::UniformTexelBuffer(view) | DescriptorWriteInner::StorageTexelBuffer(view) => { buffer_views_descriptors.push(view); } } } } // Now that `image_descriptors`, `buffer_descriptors` and `buffer_views_descriptors` are // entirely filled and will never move again, we can fill the pointers in `raw_writes`. for (i, write) in raw_writes.iter_mut().enumerate() { write.p_image_info = match raw_writes_img_infos[i] { Some(off) => image_descriptors.as_ptr().offset(off as isize), None => ptr::null(), }; write.p_buffer_info = match raw_writes_buf_infos[i] { Some(off) => buffer_descriptors.as_ptr().offset(off as isize), None => ptr::null(), }; write.p_texel_buffer_view = match raw_writes_buf_view_infos[i] { Some(off) => buffer_views_descriptors.as_ptr().offset(off as isize), None => ptr::null(), }; } // It is forbidden to call `vkUpdateDescriptorSets` with 0 writes, so we need to perform // this emptiness check. if !raw_writes.is_empty() { fns.v1_0.update_descriptor_sets( device.internal_object(), raw_writes.len() as u32, raw_writes.as_ptr(), 0, ptr::null(), ); } } } unsafe impl VulkanObject for UnsafeDescriptorSet { type Object = ash::vk::DescriptorSet; #[inline] fn internal_object(&self) -> ash::vk::DescriptorSet { self.set } } impl fmt::Debug for UnsafeDescriptorSet { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(fmt, "", self.set) } } /// Represents a single write entry to a descriptor set. /// /// Use the various constructors to build a `DescriptorWrite`. While it is safe to build a /// `DescriptorWrite`, it is unsafe to actually use it to write to a descriptor set. // TODO: allow binding whole arrays at once pub struct DescriptorWrite { binding: u32, first_array_element: u32, inner: SmallVec<[DescriptorWriteInner; 1]>, } #[derive(Debug, Clone)] enum DescriptorWriteInner { Sampler(ash::vk::Sampler), StorageImage(ash::vk::ImageView, ash::vk::ImageLayout), SampledImage(ash::vk::ImageView, ash::vk::ImageLayout), CombinedImageSampler(ash::vk::Sampler, ash::vk::ImageView, ash::vk::ImageLayout), UniformTexelBuffer(ash::vk::BufferView), StorageTexelBuffer(ash::vk::BufferView), UniformBuffer(ash::vk::Buffer, DeviceSize, DeviceSize), StorageBuffer(ash::vk::Buffer, DeviceSize, DeviceSize), DynamicUniformBuffer(ash::vk::Buffer, DeviceSize, DeviceSize), DynamicStorageBuffer(ash::vk::Buffer, DeviceSize, DeviceSize), InputAttachment(ash::vk::ImageView, ash::vk::ImageLayout), } macro_rules! smallvec { ($elem:expr) => {{ let mut s = SmallVec::new(); s.push($elem); s }}; } impl DescriptorWrite { #[inline] pub fn storage_image(binding: u32, array_element: u32, image_view: &I) -> DescriptorWrite where I: ImageViewAbstract, { let layouts = image_view .image() .descriptor_layouts() .expect("descriptor_layouts must return Some when used in an image view"); DescriptorWrite { binding, first_array_element: array_element, inner: smallvec!({ DescriptorWriteInner::StorageImage( image_view.inner().internal_object(), layouts.storage_image.into(), ) }), } } #[inline] pub fn sampler(binding: u32, array_element: u32, sampler: &Arc) -> DescriptorWrite { DescriptorWrite { binding, first_array_element: array_element, inner: smallvec!(DescriptorWriteInner::Sampler(sampler.internal_object())), } } #[inline] pub fn sampled_image(binding: u32, array_element: u32, image_view: &I) -> DescriptorWrite where I: ImageViewAbstract, { let layouts = image_view .image() .descriptor_layouts() .expect("descriptor_layouts must return Some when used in an image view"); DescriptorWrite { binding, first_array_element: array_element, inner: smallvec!({ DescriptorWriteInner::SampledImage( image_view.inner().internal_object(), layouts.sampled_image.into(), ) }), } } #[inline] pub fn combined_image_sampler( binding: u32, array_element: u32, sampler: &Arc, image_view: &I, ) -> DescriptorWrite where I: ImageViewAbstract, { let layouts = image_view .image() .descriptor_layouts() .expect("descriptor_layouts must return Some when used in an image view"); DescriptorWrite { binding, first_array_element: array_element, inner: smallvec!({ DescriptorWriteInner::CombinedImageSampler( sampler.internal_object(), image_view.inner().internal_object(), layouts.combined_image_sampler.into(), ) }), } } #[inline] pub fn uniform_texel_buffer<'a, B>( binding: u32, array_element: u32, view: &BufferView, ) -> DescriptorWrite where B: BufferAccess, { assert!(view.uniform_texel_buffer()); DescriptorWrite { binding, first_array_element: array_element, inner: smallvec!(DescriptorWriteInner::UniformTexelBuffer( view.internal_object() )), } } #[inline] pub fn storage_texel_buffer<'a, B>( binding: u32, array_element: u32, view: &BufferView, ) -> DescriptorWrite where B: BufferAccess, { assert!(view.storage_texel_buffer()); DescriptorWrite { binding, first_array_element: array_element, inner: smallvec!(DescriptorWriteInner::StorageTexelBuffer( view.internal_object() )), } } #[inline] pub unsafe fn uniform_buffer(binding: u32, array_element: u32, buffer: &B) -> DescriptorWrite where B: BufferAccess, { let size = buffer.size(); let BufferInner { buffer, offset } = buffer.inner(); debug_assert_eq!( offset % buffer .device() .physical_device() .properties() .min_uniform_buffer_offset_alignment, 0 ); debug_assert!( size <= buffer .device() .physical_device() .properties() .max_uniform_buffer_range as DeviceSize ); DescriptorWrite { binding, first_array_element: array_element, inner: smallvec!({ DescriptorWriteInner::UniformBuffer(buffer.internal_object(), offset, size) }), } } #[inline] pub unsafe fn storage_buffer(binding: u32, array_element: u32, buffer: &B) -> DescriptorWrite where B: BufferAccess, { let size = buffer.size(); let BufferInner { buffer, offset } = buffer.inner(); debug_assert_eq!( offset % buffer .device() .physical_device() .properties() .min_storage_buffer_offset_alignment, 0 ); debug_assert!( size <= buffer .device() .physical_device() .properties() .max_storage_buffer_range as DeviceSize ); DescriptorWrite { binding, first_array_element: array_element, inner: smallvec!({ DescriptorWriteInner::StorageBuffer(buffer.internal_object(), offset, size) }), } } #[inline] pub unsafe fn dynamic_uniform_buffer( binding: u32, array_element: u32, buffer: &B, ) -> DescriptorWrite where B: BufferAccess, { let size = buffer.size(); let BufferInner { buffer, offset } = buffer.inner(); debug_assert_eq!( offset % buffer .device() .physical_device() .properties() .min_uniform_buffer_offset_alignment, 0 ); debug_assert!( size <= buffer .device() .physical_device() .properties() .max_uniform_buffer_range as DeviceSize ); DescriptorWrite { binding, first_array_element: array_element, inner: smallvec!(DescriptorWriteInner::DynamicUniformBuffer( buffer.internal_object(), offset, size )), } } #[inline] pub unsafe fn dynamic_storage_buffer( binding: u32, array_element: u32, buffer: &B, ) -> DescriptorWrite where B: BufferAccess, { let size = buffer.size(); let BufferInner { buffer, offset } = buffer.inner(); debug_assert_eq!( offset % buffer .device() .physical_device() .properties() .min_storage_buffer_offset_alignment, 0 ); debug_assert!( size <= buffer .device() .physical_device() .properties() .max_storage_buffer_range as DeviceSize ); DescriptorWrite { binding, first_array_element: array_element, inner: smallvec!(DescriptorWriteInner::DynamicStorageBuffer( buffer.internal_object(), offset, size )), } } #[inline] pub fn input_attachment(binding: u32, array_element: u32, image_view: &I) -> DescriptorWrite where I: ImageViewAbstract, { let layouts = image_view .image() .descriptor_layouts() .expect("descriptor_layouts must return Some when used in an image view"); DescriptorWrite { binding, first_array_element: array_element, inner: smallvec!({ DescriptorWriteInner::InputAttachment( image_view.inner().internal_object(), layouts.input_attachment.into(), ) }), } } /// Returns the type corresponding to this write. #[inline] pub fn ty(&self) -> DescriptorType { match self.inner[0] { DescriptorWriteInner::Sampler(_) => DescriptorType::Sampler, DescriptorWriteInner::CombinedImageSampler(_, _, _) => { DescriptorType::CombinedImageSampler } DescriptorWriteInner::SampledImage(_, _) => DescriptorType::SampledImage, DescriptorWriteInner::StorageImage(_, _) => DescriptorType::StorageImage, DescriptorWriteInner::UniformTexelBuffer(_) => DescriptorType::UniformTexelBuffer, DescriptorWriteInner::StorageTexelBuffer(_) => DescriptorType::StorageTexelBuffer, DescriptorWriteInner::UniformBuffer(_, _, _) => DescriptorType::UniformBuffer, DescriptorWriteInner::StorageBuffer(_, _, _) => DescriptorType::StorageBuffer, DescriptorWriteInner::DynamicUniformBuffer(_, _, _) => { DescriptorType::UniformBufferDynamic } DescriptorWriteInner::DynamicStorageBuffer(_, _, _) => { DescriptorType::StorageBufferDynamic } DescriptorWriteInner::InputAttachment(_, _) => DescriptorType::InputAttachment, } } }