diff options
Diffstat (limited to 'src/descriptor_set/sys.rs')
-rw-r--r-- | src/descriptor_set/sys.rs | 588 |
1 files changed, 588 insertions, 0 deletions
diff --git a/src/descriptor_set/sys.rs b/src/descriptor_set/sys.rs new file mode 100644 index 0000000..d1c7cc1 --- /dev/null +++ b/src/descriptor_set/sys.rs @@ -0,0 +1,588 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, +// 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<I>(&mut self, device: &Device, writes: I) + where + I: Iterator<Item = DescriptorWrite>, + { + 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, "<Vulkan descriptor set {:?}>", 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<I>(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<Sampler>) -> DescriptorWrite { + DescriptorWrite { + binding, + first_array_element: array_element, + inner: smallvec!(DescriptorWriteInner::Sampler(sampler.internal_object())), + } + } + + #[inline] + pub fn sampled_image<I>(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<I>( + binding: u32, + array_element: u32, + sampler: &Arc<Sampler>, + 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<B>, + ) -> 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<B>, + ) -> 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<B>(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<B>(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<B>( + 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<B>( + 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<I>(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, + } + } +} |