blob: d1c7cc14a389593eee0591777482fd0d913fede6 [file] [log] [blame]
// 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,
}
}
}