Add DescriptorSetLayoutCreateInfo and PipelineLayoutCreateInfo (#1834)

* Add `DescriptorSetLayoutCreateInfo` and `PipelineLayoutCreateInfo`

* DescriptorType non_exhaustive
This commit is contained in:
Rua 2022-02-25 23:52:44 +01:00 committed by GitHub
parent 51c5d197bf
commit 786683c2a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 1905 additions and 1960 deletions

View File

@ -148,7 +148,7 @@ fn main() {
//
// If you want to run the pipeline on multiple different buffers, you need to create multiple
// descriptor sets that each contain the buffer you want to run the shader on.
let layout = pipeline.layout().descriptor_set_layouts().get(0).unwrap();
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
layout.clone(),
[WriteDescriptorSet::buffer(0, data_buffer.clone())],

View File

@ -112,12 +112,7 @@ impl AmbientLightingSystem {
color: [ambient_color[0], ambient_color[1], ambient_color[2], 1.0],
};
let layout = self
.pipeline
.layout()
.descriptor_set_layouts()
.get(0)
.unwrap();
let layout = self.pipeline.layout().set_layouts().get(0).unwrap();
let descriptor_set = PersistentDescriptorSet::new(
layout.clone(),
[WriteDescriptorSet::image_view(0, color_input)],

View File

@ -123,12 +123,7 @@ impl DirectionalLightingSystem {
direction: direction.extend(0.0).into(),
};
let layout = self
.pipeline
.layout()
.descriptor_set_layouts()
.get(0)
.unwrap();
let layout = self.pipeline.layout().set_layouts().get(0).unwrap();
let descriptor_set = PersistentDescriptorSet::new(
layout.clone(),
[

View File

@ -134,12 +134,7 @@ impl PointLightingSystem {
position: position.extend(0.0).into(),
};
let layout = self
.pipeline
.layout()
.descriptor_set_layouts()
.get(0)
.unwrap();
let layout = self.pipeline.layout().set_layouts().get(0).unwrap();
let descriptor_set = PersistentDescriptorSet::new(
layout.clone(),
[

View File

@ -17,6 +17,7 @@
use std::mem;
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer};
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage};
use vulkano::descriptor_set::layout::DescriptorType;
use vulkano::descriptor_set::{DescriptorSet, PersistentDescriptorSet, WriteDescriptorSet};
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo};
@ -102,8 +103,9 @@ fn main() {
shader.entry_point("main").unwrap(),
&(),
None,
|set_descs| {
set_descs[0].set_buffer_dynamic(0);
|layout_create_infos| {
let binding = layout_create_infos[0].bindings.get_mut(&0).unwrap();
binding.descriptor_type = DescriptorType::UniformBufferDynamic;
},
)
.unwrap();
@ -155,7 +157,7 @@ fn main() {
)
.unwrap();
let layout = pipeline.layout().descriptor_set_layouts().get(0).unwrap();
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
layout.clone(),
[

View File

@ -194,7 +194,7 @@ fn main() {
.unwrap();
let view = ImageView::new(image.clone()).unwrap();
let layout = pipeline.layout().descriptor_set_layouts().get(0).unwrap();
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
layout.clone(),
[WriteDescriptorSet::image_view(0, view.clone())],

View File

@ -179,7 +179,7 @@ fn main() {
}
});
let layout = pipeline.layout().descriptor_set_layouts().get(0).unwrap();
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
layout.clone(),

View File

@ -92,7 +92,9 @@ fn main() {
let queue = queues.next().unwrap();
let (mut swapchain, images) = {
let surface_capabilities = physical_device.surface_capabilities(&surface, Default::default()).unwrap();
let surface_capabilities = physical_device
.surface_capabilities(&surface, Default::default())
.unwrap();
let image_format = Some(
physical_device
.surface_formats(&surface, Default::default())
@ -279,7 +281,7 @@ fn main() {
.build(device.clone())
.unwrap();
let layout = pipeline.layout().descriptor_set_layouts().get(0).unwrap();
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
layout.clone(),
[WriteDescriptorSet::image_view_sampler(

View File

@ -215,7 +215,7 @@ fn main() {
.build(device.clone())
.unwrap();
let layout = pipeline.layout().descriptor_set_layouts().get(0).unwrap();
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
layout.clone(),
[WriteDescriptorSet::image_view_sampler(

View File

@ -145,7 +145,7 @@ void main() {
immutable_data_buffer
};
let layout = pipeline.layout().descriptor_set_layouts().get(0).unwrap();
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
layout.clone(),
[

View File

@ -218,14 +218,15 @@ fn main() {
.fragment_shader(fs.entry_point("main").unwrap(), ())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha())
.render_pass(subpass)
.with_auto_layout(device.clone(), |set_descs| {
.with_auto_layout(device.clone(), |layout_create_infos| {
// Modify the auto-generated layout by setting an immutable sampler to
// set 0 binding 0.
set_descs[0].set_immutable_samplers(0, [sampler]);
let binding = layout_create_infos[0].bindings.get_mut(&0).unwrap();
binding.immutable_samplers = vec![sampler];
})
.unwrap();
let layout = pipeline.layout().descriptor_set_layouts().get(0).unwrap();
let layout = pipeline.layout().set_layouts().get(0).unwrap();
// Use `image_view` instead of `image_view_sampler`, since the sampler is already in the layout.
let set = PersistentDescriptorSet::new(

View File

@ -347,11 +347,7 @@ fn main() {
.unwrap();
// Pass the two buffers to the compute shader
let layout = compute_pipeline
.layout()
.descriptor_set_layouts()
.get(0)
.unwrap();
let layout = compute_pipeline.layout().set_layouts().get(0).unwrap();
let cs_desciptor_set = PersistentDescriptorSet::new(
layout.clone(),
[

View File

@ -97,7 +97,7 @@ impl FractalComputePipeline {
// Resize image if needed
let img_dims = image.image().dimensions().width_height();
let pipeline_layout = self.pipeline.layout();
let desc_layout = pipeline_layout.descriptor_set_layouts().get(0).unwrap();
let desc_layout = pipeline_layout.set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
desc_layout.clone(),
[

View File

@ -106,12 +106,7 @@ impl PixelsDrawPipeline {
&self,
image: Arc<dyn ImageViewAbstract>,
) -> Arc<PersistentDescriptorSet> {
let layout = self
.pipeline
.layout()
.descriptor_set_layouts()
.get(0)
.unwrap();
let layout = self.pipeline.layout().set_layouts().get(0).unwrap();
let sampler = Sampler::start(self.gfx_queue.device().clone())
.filter(Filter::Linear)
.address_mode(SamplerAddressMode::Repeat)

View File

@ -134,7 +134,7 @@ impl GameOfLifeComputePipeline {
// Resize image if needed
let img_dims = self.image.image().dimensions().width_height();
let pipeline_layout = self.compute_life_pipeline.layout();
let desc_layout = pipeline_layout.descriptor_set_layouts().get(0).unwrap();
let desc_layout = pipeline_layout.set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
desc_layout.clone(),
[

View File

@ -106,12 +106,7 @@ impl PixelsDrawPipeline {
&self,
image: Arc<dyn ImageViewAbstract>,
) -> Arc<PersistentDescriptorSet> {
let layout = self
.pipeline
.layout()
.descriptor_set_layouts()
.get(0)
.unwrap();
let layout = self.pipeline.layout().set_layouts().get(0).unwrap();
let sampler = Sampler::start(self.gfx_queue.device().clone())
.filter(Filter::Nearest)
.address_mode(SamplerAddressMode::Repeat)

View File

@ -106,7 +106,7 @@ fn main() {
.unwrap()
};
let layout = pipeline.layout().descriptor_set_layouts().get(0).unwrap();
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
layout.clone(),
[WriteDescriptorSet::buffer(0, data_buffer.clone())],

View File

@ -210,9 +210,11 @@ fn main() {
.fragment_shader(fs.entry_point("main").unwrap(), ())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha())
.render_pass(subpass)
.with_auto_layout(device.clone(), |set_descs| {
set_descs[0].set_push_descriptor(true);
set_descs[0].set_immutable_samplers(0, [sampler]);
.with_auto_layout(device.clone(), |layout_create_infos| {
let create_info = &mut layout_create_infos[0];
let binding = create_info.bindings.get_mut(&0).unwrap();
create_info.push_descriptor = true;
binding.immutable_samplers = vec![sampler];
})
.unwrap();

View File

@ -13,7 +13,7 @@ use std::sync::Arc;
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess};
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, SubpassContents};
use vulkano::descriptor_set::layout::{
DescriptorSetDesc, DescriptorSetLayout, DescriptorSetLayoutError,
DescriptorSetLayout, DescriptorSetLayoutCreateInfo, DescriptorSetLayoutCreationError,
};
use vulkano::descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet};
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
@ -27,7 +27,7 @@ use vulkano::instance::{Instance, InstanceCreateInfo};
use vulkano::pipeline::graphics::color_blend::ColorBlendState;
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
use vulkano::pipeline::layout::PipelineLayout;
use vulkano::pipeline::layout::{PipelineLayout, PipelineLayoutCreateInfo};
use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint};
use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass};
use vulkano::sampler::{Filter, Sampler, SamplerAddressMode};
@ -294,26 +294,34 @@ fn main() {
.unwrap();
let pipeline_layout = {
let mut descriptor_set_descs: Vec<_> = DescriptorSetDesc::from_requirements(
let mut layout_create_infos: Vec<_> = DescriptorSetLayoutCreateInfo::from_requirements(
fs.entry_point("main").unwrap().descriptor_requirements(),
);
// Set 0, Binding 0
descriptor_set_descs[0].set_variable_descriptor_count(0, 2);
let binding = layout_create_infos[0].bindings.get_mut(&0).unwrap();
binding.variable_descriptor_count = true;
binding.descriptor_count = 2;
let descriptor_set_layouts = descriptor_set_descs
let set_layouts = layout_create_infos
.into_iter()
.map(|desc| Ok(DescriptorSetLayout::new(device.clone(), desc.clone())?))
.collect::<Result<Vec<_>, DescriptorSetLayoutError>>()
.collect::<Result<Vec<_>, DescriptorSetLayoutCreationError>>()
.unwrap();
PipelineLayout::new(
device.clone(),
descriptor_set_layouts,
fs.entry_point("main")
.unwrap()
.push_constant_requirements()
.cloned(),
PipelineLayoutCreateInfo {
set_layouts,
push_constant_ranges: fs
.entry_point("main")
.unwrap()
.push_constant_requirements()
.cloned()
.into_iter()
.collect(),
..Default::default()
},
)
.unwrap()
};
@ -329,7 +337,7 @@ fn main() {
.with_pipeline_layout(device.clone(), pipeline_layout)
.unwrap();
let layout = pipeline.layout().descriptor_set_layouts().get(0).unwrap();
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new_variable(
layout.clone(),
2,

View File

@ -111,7 +111,7 @@ fn main() {
.unwrap()
};
let layout = pipeline.layout().descriptor_set_layouts().get(0).unwrap();
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
layout.clone(),
[WriteDescriptorSet::buffer(0, data_buffer.clone())],

View File

@ -108,7 +108,7 @@ fn main() {
.unwrap()
};
let layout = pipeline.layout().descriptor_set_layouts().get(0).unwrap();
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
layout.clone(),
[WriteDescriptorSet::buffer(0, data_buffer.clone())],

View File

@ -172,7 +172,7 @@ fn main() {
data_buffer: Arc<CpuAccessibleBuffer<[u32]>>,
parameters: shaders::ty::Parameters,
) {
let layout = pipeline.layout().descriptor_set_layouts().get(0).unwrap();
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
layout.clone(),
[WriteDescriptorSet::buffer(0, data_buffer.clone())],

View File

@ -110,7 +110,7 @@ fn main() {
.unwrap()
};
let layout = pipeline.layout().descriptor_set_layouts().get(0).unwrap();
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
layout.clone(),
[WriteDescriptorSet::buffer(0, data_buffer.clone())],

View File

@ -244,7 +244,7 @@ fn main() {
uniform_buffer.next(uniform_data).unwrap()
};
let layout = pipeline.layout().descriptor_set_layouts().get(0).unwrap();
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
layout.clone(),
[WriteDescriptorSet::buffer(0, uniform_buffer_subbuffer)],

View File

@ -223,7 +223,7 @@ fn main() {
.build(device.clone())
.unwrap();
let layout = pipeline.layout().descriptor_set_layouts().get(0).unwrap();
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
layout.clone(),
[WriteDescriptorSet::image_view_sampler(

View File

@ -9,7 +9,7 @@
use fnv::FnvHashMap;
use proc_macro2::TokenStream;
use vulkano::pipeline::layout::PipelineLayoutPcRange;
use vulkano::pipeline::layout::PushConstantRange;
use vulkano::shader::spirv::ExecutionModel;
use vulkano::shader::{
DescriptorIdentifier, DescriptorRequirements, ShaderExecution, ShaderInterfaceEntry,
@ -219,10 +219,10 @@ fn write_descriptor_requirements(
}
fn write_push_constant_requirements(
push_constant_requirements: &Option<PipelineLayoutPcRange>,
push_constant_requirements: &Option<PushConstantRange>,
) -> TokenStream {
match push_constant_requirements {
Some(PipelineLayoutPcRange {
Some(PushConstantRange {
offset,
size,
stages,
@ -262,10 +262,10 @@ fn write_push_constant_requirements(
};
quote! {
Some(::vulkano::pipeline::layout::PipelineLayoutPcRange {
Some(::vulkano::pipeline::layout::PushConstantRange {
stages: #stages,
offset: #offset,
size: #size,
stages: #stages,
})
}
}

View File

@ -538,7 +538,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
assert!(
first_set as usize + descriptor_sets.len()
<= pipeline_layout.descriptor_set_layouts().len(),
<= pipeline_layout.set_layouts().len(),
"the highest descriptor set slot being bound must be less than the number of sets in pipeline_layout"
);
@ -548,7 +548,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
self.device().internal_object()
);
let pipeline_set = &pipeline_layout.descriptor_set_layouts()[first_set as usize + num];
let pipeline_set = &pipeline_layout.set_layouts()[first_set as usize + num];
assert!(
pipeline_set.is_compatible_with(set.as_ref().0.layout()),
"the element of descriptor_sets being bound to slot {} is not compatible with the corresponding slot in pipeline_layout",
@ -1916,12 +1916,12 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
"the khr_push_descriptor extension must be enabled on the device"
);
assert!(
set_num as usize <= pipeline_layout.descriptor_set_layouts().len(),
set_num as usize <= pipeline_layout.set_layouts().len(),
"the descriptor set slot being bound must be less than the number of sets in pipeline_layout"
);
let descriptor_writes: SmallVec<[_; 8]> = descriptor_writes.into_iter().collect();
let descriptor_set_layout = &pipeline_layout.descriptor_set_layouts()[set_num as usize];
let descriptor_set_layout = &pipeline_layout.set_layouts()[set_num as usize];
for write in &descriptor_writes {
check_descriptor_write(write, descriptor_set_layout, 0).unwrap();

View File

@ -796,8 +796,8 @@ impl CurrentState {
// isn't compatible with the corresponding set in the new pipeline layout.
// If an incompatible set was found, all bound sets from that slot onwards will
// be disturbed.
let current_layouts = state.pipeline_layout.descriptor_set_layouts();
let new_layouts = pipeline_layout.descriptor_set_layouts();
let current_layouts = state.pipeline_layout.set_layouts();
let new_layouts = pipeline_layout.set_layouts();
let max = (current_layouts.len() as u32).min(first_set + num_descriptor_sets);
(0..max).find(|&num| {
let num = num as usize;
@ -812,7 +812,7 @@ impl CurrentState {
.retain(|&num, _| num < invalidate_from);
state.pipeline_layout = pipeline_layout;
} else if (first_set + num_descriptor_sets) as usize
>= state.pipeline_layout.descriptor_set_layouts().len()
>= state.pipeline_layout.set_layouts().len()
{
// New layout is a superset of the old one.
state.pipeline_layout = pipeline_layout;

View File

@ -1697,8 +1697,8 @@ impl SyncCommandBufferBuilder {
set_num,
1,
);
let layout = state.pipeline_layout.descriptor_set_layouts()[set_num as usize].as_ref();
debug_assert!(layout.desc().is_push_descriptor());
let layout = state.pipeline_layout.set_layouts()[set_num as usize].as_ref();
debug_assert!(layout.push_descriptor());
let set_resources = match state
.descriptor_sets
@ -2698,10 +2698,9 @@ impl SyncCommandBufferBuilder {
for ((set, binding), reqs) in descriptor_requirements {
// TODO: Can things be refactored so that the pipeline layout isn't needed at all?
let descriptor_type = state.pipeline_layout.descriptor_set_layouts()[set as usize]
.descriptor(binding)
.unwrap()
.ty;
let descriptor_type = state.pipeline_layout.set_layouts()[set as usize].bindings()
[&binding]
.descriptor_type;
// TODO: Maybe include this on DescriptorRequirements?
let access = PipelineMemoryAccess {

View File

@ -488,13 +488,15 @@ mod tests {
use crate::command_buffer::AutoCommandBufferBuilder;
use crate::command_buffer::CommandBufferLevel;
use crate::command_buffer::CommandBufferUsage;
use crate::descriptor_set::layout::DescriptorDesc;
use crate::descriptor_set::layout::DescriptorSetLayout;
use crate::descriptor_set::layout::DescriptorSetLayoutBinding;
use crate::descriptor_set::layout::DescriptorSetLayoutCreateInfo;
use crate::descriptor_set::layout::DescriptorType;
use crate::descriptor_set::PersistentDescriptorSet;
use crate::descriptor_set::WriteDescriptorSet;
use crate::device::Device;
use crate::pipeline::layout::PipelineLayout;
use crate::pipeline::layout::PipelineLayoutCreateInfo;
use crate::pipeline::PipelineBindPoint;
use crate::sampler::Sampler;
use crate::shader::ShaderStages;
@ -647,18 +649,27 @@ mod tests {
.unwrap();
let set_layout = DescriptorSetLayout::new(
device.clone(),
[Some(DescriptorDesc {
ty: DescriptorType::Sampler,
descriptor_count: 1,
variable_count: false,
stages: ShaderStages::all(),
immutable_samplers: Vec::new(),
})],
DescriptorSetLayoutCreateInfo {
bindings: [(
0,
DescriptorSetLayoutBinding {
stages: ShaderStages::all(),
..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::Sampler)
},
)]
.into(),
..Default::default()
},
)
.unwrap();
let pipeline_layout = PipelineLayout::new(
device.clone(),
PipelineLayoutCreateInfo {
set_layouts: [set_layout.clone(), set_layout.clone()].into(),
..Default::default()
},
)
.unwrap();
let pipeline_layout =
PipelineLayout::new(device.clone(), [set_layout.clone(), set_layout.clone()], [])
.unwrap();
let set = PersistentDescriptorSet::new(
set_layout.clone(),
@ -705,11 +716,14 @@ mod tests {
let pipeline_layout = PipelineLayout::new(
device.clone(),
[
DescriptorSetLayout::new(device.clone(), []).unwrap(),
set_layout.clone(),
],
[],
PipelineLayoutCreateInfo {
set_layouts: [
DescriptorSetLayout::new(device.clone(), Default::default()).unwrap(),
set_layout.clone(),
]
.into(),
..Default::default()
},
)
.unwrap();

View File

@ -337,9 +337,7 @@ impl UnsafeCommandBufferBuilder {
let dynamic_offsets: SmallVec<[u32; 32]> = dynamic_offsets.into_iter().collect();
let num_bindings = sets.len() as u32;
debug_assert!(
first_set + num_bindings <= pipeline_layout.descriptor_set_layouts().len() as u32
);
debug_assert!(first_set + num_bindings <= pipeline_layout.set_layouts().len() as u32);
fns.v1_0.cmd_bind_descriptor_sets(
cmd,
@ -1393,13 +1391,12 @@ impl UnsafeCommandBufferBuilder {
let (infos, mut writes): (SmallVec<[_; 8]>, SmallVec<[_; 8]>) = descriptor_writes
.into_iter()
.map(|write| {
let descriptor = pipeline_layout.descriptor_set_layouts()[set_num as usize]
.descriptor(write.binding())
.unwrap();
let binding =
&pipeline_layout.set_layouts()[set_num as usize].bindings()[&write.binding()];
(
write.to_vulkan_info(descriptor.ty),
write.to_vulkan(ash::vk::DescriptorSet::null(), descriptor.ty),
write.to_vulkan_info(binding.descriptor_type),
write.to_vulkan(ash::vk::DescriptorSet::null(), binding.descriptor_type),
)
})
.unzip();

View File

@ -50,15 +50,13 @@ pub(in super::super) fn check_descriptor_sets_validity<'a, P: Pipeline>(
}
for ((set_num, binding_num), reqs) in descriptor_requirements {
let layout_binding = pipeline.layout().descriptor_set_layouts()[set_num as usize]
.desc()
.descriptor(binding_num)
.unwrap();
let layout_binding =
&pipeline.layout().set_layouts()[set_num as usize].bindings()[&binding_num];
let check_buffer = |index: u32, buffer: &Arc<dyn BufferAccess>| Ok(());
let check_buffer_view = |index: u32, buffer_view: &Arc<dyn BufferViewAbstract>| {
if layout_binding.ty == DescriptorType::StorageTexelBuffer {
if layout_binding.descriptor_type == DescriptorType::StorageTexelBuffer {
// VUID-vkCmdDispatch-OpTypeImage-06423
if reqs.image_format.is_none()
&& reqs.storage_write.contains(&index)
@ -87,7 +85,7 @@ pub(in super::super) fn check_descriptor_sets_validity<'a, P: Pipeline>(
return Err(InvalidDescriptorResource::StorageImageAtomicNotSupported);
}
if layout_binding.ty == DescriptorType::StorageImage {
if layout_binding.descriptor_type == DescriptorType::StorageImage {
// VUID-vkCmdDispatch-OpTypeImage-06423
if reqs.image_format.is_none()
&& reqs.storage_write.contains(&index)

View File

@ -0,0 +1,852 @@
// Copyright (c) 2021 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.
//! Describes the layout of all descriptors within a descriptor set.
//!
//! When creating a new descriptor set, you must provide a *layout* object to create it from.
use super::pool::DescriptorsCount;
use crate::{
check_errors,
device::{Device, DeviceOwned},
sampler::Sampler,
shader::{DescriptorRequirements, ShaderStages},
OomError, Version, VulkanObject,
};
use std::{
collections::BTreeMap,
error, fmt,
hash::{Hash, Hasher},
mem::MaybeUninit,
ptr,
sync::Arc,
};
/// Describes to the Vulkan implementation the layout of all descriptors within a descriptor set.
#[derive(Debug)]
pub struct DescriptorSetLayout {
handle: ash::vk::DescriptorSetLayout,
device: Arc<Device>,
bindings: BTreeMap<u32, DescriptorSetLayoutBinding>,
push_descriptor: bool,
descriptors_count: DescriptorsCount,
}
impl DescriptorSetLayout {
/// Creates a new `DescriptorSetLayout`.
pub fn new(
device: Arc<Device>,
mut create_info: DescriptorSetLayoutCreateInfo,
) -> Result<Arc<DescriptorSetLayout>, DescriptorSetLayoutCreationError> {
let descriptors_count = Self::validate(&device, &mut create_info)?;
let handle = unsafe { Self::create(&device, &create_info)? };
let DescriptorSetLayoutCreateInfo {
bindings,
push_descriptor,
_ne: _,
} = create_info;
Ok(Arc::new(DescriptorSetLayout {
handle,
device,
bindings,
push_descriptor,
descriptors_count,
}))
}
fn validate(
device: &Device,
create_info: &mut DescriptorSetLayoutCreateInfo,
) -> Result<DescriptorsCount, DescriptorSetLayoutCreationError> {
let &mut DescriptorSetLayoutCreateInfo {
ref bindings,
push_descriptor,
_ne: _,
} = create_info;
let mut descriptors_count = DescriptorsCount::zero();
if push_descriptor {
if !device.enabled_extensions().khr_push_descriptor {
return Err(DescriptorSetLayoutCreationError::ExtensionNotEnabled {
extension: "khr_push_descriptor",
reason: "description was set to be a push descriptor",
});
}
}
let highest_binding_num = bindings.keys().copied().next_back();
for (&binding_num, binding) in bindings.iter() {
descriptors_count.add_num(binding.descriptor_type, binding.descriptor_count);
if push_descriptor {
// VUID-VkDescriptorSetLayoutCreateInfo-flags-00280
if matches!(
binding.descriptor_type,
DescriptorType::StorageBufferDynamic | DescriptorType::UniformBufferDynamic
) {
return Err(
DescriptorSetLayoutCreationError::PushDescriptorDescriptorTypeIncompatible {
binding_num,
},
);
}
// VUID-VkDescriptorSetLayoutBindingFlagsCreateInfo-flags-03003
if binding.variable_descriptor_count {
return Err(
DescriptorSetLayoutCreationError::PushDescriptorVariableDescriptorCount {
binding_num,
},
);
}
}
if !binding.immutable_samplers.is_empty() {
if binding
.immutable_samplers
.iter()
.any(|sampler| sampler.sampler_ycbcr_conversion().is_some())
{
if !matches!(
binding.descriptor_type,
DescriptorType::CombinedImageSampler
) {
return Err(
DescriptorSetLayoutCreationError::ImmutableSamplersDescriptorTypeIncompatible {
binding_num,
},
);
}
} else {
if !matches!(
binding.descriptor_type,
DescriptorType::Sampler | DescriptorType::CombinedImageSampler
) {
return Err(
DescriptorSetLayoutCreationError::ImmutableSamplersDescriptorTypeIncompatible {
binding_num,
},
);
}
}
// VUID-VkDescriptorSetLayoutBinding-descriptorType-00282
if binding.descriptor_count != binding.immutable_samplers.len() as u32 {
return Err(
DescriptorSetLayoutCreationError::ImmutableSamplersCountMismatch {
binding_num,
sampler_count: binding.immutable_samplers.len() as u32,
descriptor_count: binding.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 binding.variable_descriptor_count {
// VUID-VkDescriptorSetLayoutBindingFlagsCreateInfo-descriptorBindingVariableDescriptorCount-03014
if !device
.enabled_features()
.descriptor_binding_variable_descriptor_count
{
return Err(DescriptorSetLayoutCreationError::FeatureNotEnabled {
feature: "descriptor_binding_variable_descriptor_count",
reason: "binding has a variable count",
});
}
// 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!(
binding.descriptor_type,
DescriptorType::UniformBufferDynamic | DescriptorType::StorageBufferDynamic
) {
return Err(
DescriptorSetLayoutCreationError::VariableDescriptorCountDescriptorTypeIncompatible {
binding_num,
},
);
}
}
}
// VUID-VkDescriptorSetLayoutCreateInfo-flags-00281
if push_descriptor
&& descriptors_count.total()
> device
.physical_device()
.properties()
.max_push_descriptors
.unwrap_or(0)
{
return Err(
DescriptorSetLayoutCreationError::MaxPushDescriptorsExceeded {
provided: descriptors_count.total(),
max_supported: device
.physical_device()
.properties()
.max_push_descriptors
.unwrap_or(0),
},
);
}
Ok(descriptors_count)
}
unsafe fn create(
device: &Device,
create_info: &DescriptorSetLayoutCreateInfo,
) -> Result<ash::vk::DescriptorSetLayout, DescriptorSetLayoutCreationError> {
let &DescriptorSetLayoutCreateInfo {
ref bindings,
push_descriptor,
_ne: _,
} = create_info;
let mut bindings_vk = Vec::with_capacity(bindings.len());
let mut binding_flags_vk = Vec::with_capacity(bindings.len());
let mut immutable_samplers_vk: Vec<Box<[ash::vk::Sampler]>> = 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.internal_object())
.collect::<Vec<_>>()
.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;
}
// 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,
});
binding_flags_vk.push(binding_flags);
}
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
};
let mut create_info = ash::vk::DescriptorSetLayoutCreateInfo {
flags,
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.p_next;
create_info.p_next = binding_flags_create_info as *const _ as *const _;
}
let handle = {
let fns = device.fns();
let mut output = MaybeUninit::uninit();
check_errors(fns.v1_0.create_descriptor_set_layout(
device.internal_object(),
&create_info,
ptr::null(),
output.as_mut_ptr(),
))
.map_err(|e| OomError::from(e))?;
output.assume_init()
};
Ok(handle)
}
/// Returns the bindings of the descriptor set layout.
#[inline]
pub fn bindings(&self) -> &BTreeMap<u32, DescriptorSetLayoutBinding> {
&self.bindings
}
/// Returns whether the descriptor set layout is for push descriptors or regular descriptor
/// sets.
#[inline]
pub fn push_descriptor(&self) -> bool {
self.push_descriptor
}
/// Returns the number of descriptors of each type.
#[inline]
pub fn descriptors_count(&self) -> &DescriptorsCount {
&self.descriptors_count
}
/// If the highest-numbered binding has a variable count, returns its `descriptor_count`.
/// Otherwise returns `0`.
#[inline]
pub fn variable_descriptor_count(&self) -> u32 {
self.bindings
.values()
.next_back()
.map(|binding| {
if binding.variable_descriptor_count {
binding.descriptor_count
} else {
0
}
})
.unwrap_or(0)
}
/// Returns whether `self` is compatible with `other`.
///
/// "Compatible" in this sense is defined by the Vulkan specification under the section
/// "Pipeline layout compatibility": either the two are the same descriptor set layout object,
/// 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)
}
}
impl Drop for DescriptorSetLayout {
#[inline]
fn drop(&mut self) {
unsafe {
let fns = self.device.fns();
fns.v1_0.destroy_descriptor_set_layout(
self.device.internal_object(),
self.handle,
ptr::null(),
);
}
}
}
unsafe impl VulkanObject for DescriptorSetLayout {
type Object = ash::vk::DescriptorSetLayout;
#[inline]
fn internal_object(&self) -> ash::vk::DescriptorSetLayout {
self.handle
}
}
unsafe impl DeviceOwned for DescriptorSetLayout {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
impl PartialEq for DescriptorSetLayout {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.handle == other.handle && self.device() == other.device()
}
}
impl Eq for DescriptorSetLayout {}
impl Hash for DescriptorSetLayout {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.handle.hash(state);
self.device().hash(state);
}
}
/// Error related to descriptor set layout.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DescriptorSetLayoutCreationError {
/// Out of Memory.
OomError(OomError),
ExtensionNotEnabled {
extension: &'static str,
reason: &'static str,
},
FeatureNotEnabled {
feature: &'static str,
reason: &'static str,
},
/// A binding includes immutable samplers but their number differs from `descriptor_count`.
ImmutableSamplersCountMismatch {
binding_num: u32,
sampler_count: u32,
descriptor_count: u32,
},
/// A binding includes immutable samplers but it has an incompatible `descriptor_type`.
ImmutableSamplersDescriptorTypeIncompatible { binding_num: u32 },
/// 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 },
/// `push_descriptor` is enabled, but a binding has an incompatible `descriptor_type`.
PushDescriptorDescriptorTypeIncompatible { binding_num: u32 },
/// `push_descriptor` is enabled, but a binding has `variable_descriptor_count` enabled.
PushDescriptorVariableDescriptorCount { binding_num: u32 },
/// A binding has `variable_descriptor_count` enabled, but it is not the highest-numbered
/// binding.
VariableDescriptorCountBindingNotHighest {
binding_num: u32,
highest_binding_num: u32,
},
/// A binding has `variable_descriptor_count` enabled, but it has an incompatible
/// `descriptor_type`.
VariableDescriptorCountDescriptorTypeIncompatible { binding_num: u32 },
}
impl From<OomError> for DescriptorSetLayoutCreationError {
fn from(error: OomError) -> Self {
Self::OomError(error)
}
}
impl std::error::Error for DescriptorSetLayoutCreationError {}
impl std::fmt::Display for DescriptorSetLayoutCreationError {
#[inline]
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match *self {
Self::OomError(_) => {
write!(fmt, "out of memory")
}
Self::ExtensionNotEnabled { extension, reason } => write!(
fmt,
"the extension {} must be enabled: {}",
extension, reason
),
Self::FeatureNotEnabled { feature, reason } => {
write!(fmt, "the feature {} must be enabled: {}", feature, reason)
}
Self::ImmutableSamplersCountMismatch { binding_num, sampler_count, descriptor_count } => write!(
fmt,
"binding {} includes immutable samplers but their number ({}) differs from `descriptor_count` ({})",
binding_num, sampler_count, descriptor_count,
),
Self::ImmutableSamplersDescriptorTypeIncompatible { binding_num } => write!(
fmt,
"binding {} includes immutable samplers but it has an incompatible `descriptor_type`",
binding_num,
),
Self::MaxPushDescriptorsExceeded {
provided,
max_supported,
} => write!(
fmt,
"more descriptors were provided in all bindings ({}) than the `max_push_descriptors` limit ({})",
provided, max_supported,
),
Self::PushDescriptorDescriptorTypeIncompatible { binding_num } => write!(
fmt,
"`push_descriptor` is enabled, but binding {} has an incompatible `descriptor_type`",
binding_num,
),
Self::PushDescriptorVariableDescriptorCount { binding_num } => write!(
fmt,
"`push_descriptor` is enabled, but binding {} has `variable_descriptor_count` enabled",
binding_num,
),
Self::VariableDescriptorCountBindingNotHighest { binding_num, highest_binding_num } => write!(
fmt,
"binding {} has `variable_descriptor_count` enabled, but it is not the highest-numbered binding ({})",
binding_num, highest_binding_num,
),
Self::VariableDescriptorCountDescriptorTypeIncompatible { binding_num } => write!(
fmt,
"binding {} has `variable_descriptor_count` enabled, but it has an incompatible `descriptor_type`",
binding_num,
),
}
}
}
/// 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<u32, DescriptorSetLayoutBinding>,
/// 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
/// 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:
/// - 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,
pub _ne: crate::NonExhaustive,
}
impl Default for DescriptorSetLayoutCreateInfo {
#[inline]
fn default() -> Self {
Self {
bindings: BTreeMap::new(),
push_descriptor: false,
_ne: crate::NonExhaustive(()),
}
}
}
impl DescriptorSetLayoutCreateInfo {
/// Builds a list of `DescriptorSetLayoutCreateInfo` from an iterator of `DescriptorRequirement`
/// originating from a shader.
#[inline]
pub fn from_requirements<'a>(
descriptor_requirements: impl IntoIterator<Item = ((u32, u32), &'a DescriptorRequirements)>,
) -> Vec<Self> {
let mut create_infos: Vec<Self> = Vec::new();
for ((set_num, binding_num), reqs) in descriptor_requirements {
let set_num = set_num as usize;
if set_num >= create_infos.len() {
create_infos.resize(set_num + 1, Self::default());
}
let bindings = &mut create_infos[set_num].bindings;
bindings.insert(binding_num, reqs.into());
}
create_infos
}
}
/// A binding in a descriptor set layout.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DescriptorSetLayoutBinding {
/// The content and layout of each array element of a binding.
///
/// There is no default value.
pub descriptor_type: DescriptorType,
/// How many descriptors (array elements) this binding is made of.
///
/// If the binding is a single element rather than an array, then you must specify `1`.
///
/// 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`](crate::device::Features::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`.
pub variable_descriptor_count: bool,
/// Which shader stages are going to access the descriptors in this binding.
///
/// The default value is [`ShaderStages::none()`], which must be overridden.
pub stages: ShaderStages,
/// Samplers that are included as a fixed part of the descriptor set layout. Once bound, they
/// do not need to be provided when creating a descriptor set.
///
/// The list must be either empty, or contain exactly `descriptor_count` samplers. It can only
/// be non-empty if `descriptor_type` is [`DescriptorType::Sampler`] or
/// [`DescriptorType::CombinedImageSampler`]. If any of the samplers has an attached sampler
/// YCbCr conversion, then only [`DescriptorType::CombinedImageSampler`] is allowed.
///
/// The default value is empty.
pub immutable_samplers: Vec<Arc<Sampler>>,
pub _ne: crate::NonExhaustive,
}
impl DescriptorSetLayoutBinding {
/// Returns a `DescriptorSetLayoutBinding` with the given type.
#[inline]
pub fn descriptor_type(descriptor_type: DescriptorType) -> Self {
Self {
descriptor_type,
descriptor_count: 1,
variable_descriptor_count: false,
stages: ShaderStages::none(),
immutable_samplers: Vec::new(),
_ne: crate::NonExhaustive(()),
}
}
/// Checks whether the descriptor of a pipeline layout `self` is compatible with the
/// requirements of a shader `other`.
#[inline]
pub fn ensure_compatible_with_shader(
&self,
descriptor_requirements: &DescriptorRequirements,
) -> Result<(), DescriptorRequirementsNotMet> {
let DescriptorRequirements {
descriptor_types,
descriptor_count,
image_format,
image_multisampled,
image_scalar_type,
image_view_type,
sampler_compare,
sampler_no_unnormalized_coordinates,
sampler_no_ycbcr_conversion,
sampler_with_images,
stages,
storage_image_atomic,
storage_read,
storage_write,
} = descriptor_requirements;
if !descriptor_types.contains(&self.descriptor_type) {
return Err(DescriptorRequirementsNotMet::DescriptorType {
required: descriptor_types.clone(),
obtained: self.descriptor_type,
});
}
if self.descriptor_count < *descriptor_count {
return Err(DescriptorRequirementsNotMet::DescriptorCount {
required: *descriptor_count,
obtained: self.descriptor_count,
});
}
if !self.stages.is_superset_of(stages) {
return Err(DescriptorRequirementsNotMet::ShaderStages {
required: *stages,
obtained: self.stages,
});
}
Ok(())
}
}
impl From<&DescriptorRequirements> for DescriptorSetLayoutBinding {
fn from(reqs: &DescriptorRequirements) -> Self {
Self {
descriptor_type: reqs.descriptor_types[0],
descriptor_count: reqs.descriptor_count,
variable_descriptor_count: false,
stages: reqs.stages,
immutable_samplers: Vec::new(),
_ne: crate::NonExhaustive(()),
}
}
}
/// 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<DescriptorType>,
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::Error for DescriptorRequirementsNotMet {}
impl fmt::Display for DescriptorRequirementsNotMet {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
Self::DescriptorType { required, obtained } => write!(
fmt,
"the descriptor's type ({:?}) is not one of those required ({:?})",
obtained, required
),
Self::DescriptorCount { required, obtained } => write!(
fmt,
"the descriptor count ({}) is less than what is required ({})",
obtained, required
),
Self::ShaderStages { required, obtained } => write!(
fmt,
"the descriptor's shader stages do not contain the stages that are required",
),
}
}
}
/// Describes what kind of resource may later be bound to a descriptor.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(i32)]
#[non_exhaustive]
pub enum DescriptorType {
/// Describes how a `SampledImage` descriptor should be read.
Sampler = ash::vk::DescriptorType::SAMPLER.as_raw(),
/// Combines `SampledImage` and `Sampler` in one descriptor.
CombinedImageSampler = ash::vk::DescriptorType::COMBINED_IMAGE_SAMPLER.as_raw(),
/// Gives read-only access to an image via a sampler. The image must be combined with a sampler
/// inside the shader.
SampledImage = ash::vk::DescriptorType::SAMPLED_IMAGE.as_raw(),
/// Gives read and/or write access to individual pixels in an image. The image cannot be
/// sampled, so you have exactly specify which pixel to read or write.
StorageImage = ash::vk::DescriptorType::STORAGE_IMAGE.as_raw(),
/// Gives read-only access to the content of a buffer, interpreted as an array of texel data.
UniformTexelBuffer = ash::vk::DescriptorType::UNIFORM_TEXEL_BUFFER.as_raw(),
/// Gives read and/or write access to the content of a buffer, interpreted as an array of texel
/// data. Less restrictive but sometimes slower than a uniform texel buffer.
StorageTexelBuffer = ash::vk::DescriptorType::STORAGE_TEXEL_BUFFER.as_raw(),
/// Gives read-only access to the content of a buffer, interpreted as a structure.
UniformBuffer = ash::vk::DescriptorType::UNIFORM_BUFFER.as_raw(),
/// Gives read and/or write access to the content of a buffer, interpreted as a structure. Less
/// restrictive but sometimes slower than a uniform buffer.
StorageBuffer = ash::vk::DescriptorType::STORAGE_BUFFER.as_raw(),
/// As `UniformBuffer`, but the offset within the buffer is specified at the time the descriptor
/// set is bound, rather than when the descriptor set is updated.
UniformBufferDynamic = ash::vk::DescriptorType::UNIFORM_BUFFER_DYNAMIC.as_raw(),
/// As `StorageBuffer`, but the offset within the buffer is specified at the time the descriptor
/// set is bound, rather than when the descriptor set is updated.
StorageBufferDynamic = ash::vk::DescriptorType::STORAGE_BUFFER_DYNAMIC.as_raw(),
/// Gives access to an image inside a fragment shader via a render pass. You can only access the
/// pixel that is currently being processed by the fragment shader.
InputAttachment = ash::vk::DescriptorType::INPUT_ATTACHMENT.as_raw(),
}
impl From<DescriptorType> for ash::vk::DescriptorType {
#[inline]
fn from(val: DescriptorType) -> Self {
Self::from_raw(val as i32)
}
}
#[cfg(test)]
mod tests {
use crate::descriptor_set::layout::DescriptorSetLayout;
use crate::descriptor_set::layout::DescriptorSetLayoutBinding;
use crate::descriptor_set::layout::DescriptorSetLayoutCreateInfo;
use crate::descriptor_set::layout::DescriptorType;
use crate::descriptor_set::pool::DescriptorsCount;
use crate::shader::ShaderStages;
#[test]
fn empty() {
let (device, _) = gfx_dev_and_queue!();
let _layout = DescriptorSetLayout::new(device, Default::default());
}
#[test]
fn basic_create() {
let (device, _) = gfx_dev_and_queue!();
let sl = DescriptorSetLayout::new(
device.clone(),
DescriptorSetLayoutCreateInfo {
bindings: [(
0,
DescriptorSetLayoutBinding {
stages: ShaderStages::all_graphics(),
..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer)
},
)]
.into(),
..Default::default()
},
)
.unwrap();
assert_eq!(
sl.descriptors_count(),
&DescriptorsCount {
uniform_buffer: 1,
..DescriptorsCount::zero()
}
);
}
}

View File

@ -1,446 +0,0 @@
// 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.
//! Description of a single descriptor.
//!
//! This module contains traits and structs related to describing a single descriptor. A descriptor
//! is a slot where you can bind a buffer or an image so that it can be accessed from your shaders.
//! In order to specify which buffer or image to bind to a descriptor, see the `descriptor_set`
//! module.
//!
//! There are four different kinds of descriptors that give access to buffers:
//!
//! - Uniform texel buffers. Gives read-only access to the content of a buffer. Only supports
//! certain buffer formats.
//! - Storage texel buffers. Gives read and/or write access to the content of a buffer. Only
//! supports certain buffer formats. Less restrictive but sometimes slower than uniform texel
//! buffers.
//! - Uniform buffers. Gives read-only access to the content of a buffer. Less restrictive but
//! sometimes slower than uniform texel buffers.
//! - Storage buffers. Gives read and/or write access to the content of a buffer. Less restrictive
//! but sometimes slower than uniform buffers and storage texel buffers.
//!
//! There are five different kinds of descriptors related to images:
//!
//! - Storage images. Gives read and/or write access to individual pixels in an image. The image
//! cannot be sampled. In other words, you have exactly specify which pixel to read or write.
//! - Sampled images. Gives read-only access to an image. Before you can use a sampled image in a
//! a shader, you have to combine it with a sampler (see below). The sampler describes how
//! reading the image will behave.
//! - Samplers. Doesn't contain an image but a sampler object that describes how an image will be
//! accessed. This is meant to be combined with a sampled image (see above).
//! - Combined image and sampler. Similar to a sampled image, but also directly includes the
//! sampler which indicates how the sampling is done.
//! - Input attachments. The fastest but also most restrictive access to images. Must be integrated
//! in a render pass. Can only give access to the same pixel as the one you're processing.
//!
use crate::sampler::Sampler;
use crate::shader::DescriptorRequirements;
use crate::shader::ShaderStages;
use smallvec::SmallVec;
use std::cmp;
use std::error;
use std::fmt;
use std::sync::Arc;
#[derive(Clone, Debug, Default)]
pub struct DescriptorSetDesc {
descriptors: SmallVec<[Option<DescriptorDesc>; 4]>,
push_descriptor: bool,
}
impl DescriptorSetDesc {
/// Builds a new `DescriptorSetDesc` with the given descriptors.
///
/// The descriptors must be passed in the order of the bindings. In order words, descriptor
/// at bind point 0 first, then descriptor at bind point 1, and so on. If a binding must remain
/// empty, you can make the iterator yield `None` for an element.
#[inline]
pub fn new<I>(descriptors: I) -> Self
where
I: IntoIterator<Item = Option<DescriptorDesc>>,
{
Self {
descriptors: descriptors.into_iter().collect(),
push_descriptor: false,
}
}
/// Builds a list of `DescriptorSetDesc` from an iterator of `DescriptorRequirement` originating
/// from a shader.
#[inline]
pub fn from_requirements<'a>(
descriptor_requirements: impl IntoIterator<Item = ((u32, u32), &'a DescriptorRequirements)>,
) -> Vec<Self> {
let mut descriptor_sets: Vec<Self> = Vec::new();
for ((set_num, binding_num), reqs) in descriptor_requirements {
let set_num = set_num as usize;
let binding_num = binding_num as usize;
if set_num >= descriptor_sets.len() {
descriptor_sets.resize(set_num + 1, Self::default());
}
let descriptors = &mut descriptor_sets[set_num].descriptors;
if binding_num >= descriptors.len() {
descriptors.resize(binding_num + 1, None);
}
descriptors[binding_num] = Some(reqs.into());
}
descriptor_sets
}
/// Builds a new empty `DescriptorSetDesc`.
#[inline]
pub fn empty() -> DescriptorSetDesc {
DescriptorSetDesc {
descriptors: SmallVec::new(),
push_descriptor: false,
}
}
/// Returns the descriptors in the set.
#[inline]
pub fn bindings(&self) -> &[Option<DescriptorDesc>] {
&self.descriptors
}
/// Returns the descriptor with the given binding number, or `None` if the binding is empty.
#[inline]
pub fn descriptor(&self, num: u32) -> Option<&DescriptorDesc> {
self.descriptors.get(num as usize).and_then(|b| b.as_ref())
}
/// Returns whether the description is set to be a push descriptor.
#[inline]
pub fn is_push_descriptor(&self) -> bool {
self.push_descriptor
}
/// Changes a buffer descriptor's type to dynamic.
///
/// # Panics
///
/// - Panics if the description is set to be a push descriptor.
/// - Panics if `binding_num` does not refer to a `StorageBuffer` or `UniformBuffer` descriptor.
pub fn set_buffer_dynamic(&mut self, binding_num: u32) {
assert!(
!self.push_descriptor,
"push descriptor is enabled, which does not allow dynamic buffer descriptors"
);
assert!(
self.descriptor(binding_num).map_or(false, |desc| matches!(
desc.ty,
DescriptorType::StorageBuffer | DescriptorType::UniformBuffer
)),
"tried to make the non-buffer descriptor at binding {} a dynamic buffer",
binding_num
);
let binding = self
.descriptors
.get_mut(binding_num as usize)
.and_then(|b| b.as_mut());
if let Some(desc) = binding {
match &desc.ty {
DescriptorType::StorageBuffer => {
desc.ty = DescriptorType::StorageBufferDynamic;
}
DescriptorType::UniformBuffer => {
desc.ty = DescriptorType::UniformBufferDynamic;
}
_ => (),
};
}
}
/// Sets the immutable samplers for a sampler or combined image sampler descriptor.
///
/// # Panics
///
/// - Panics if the binding number does not refer to a sampler or combined image sampler
/// descriptor.
pub fn set_immutable_samplers(
&mut self,
binding_num: u32,
samplers: impl IntoIterator<Item = Arc<Sampler>>,
) {
let immutable_samplers = self
.descriptors
.get_mut(binding_num as usize)
.and_then(|b| b.as_mut())
.and_then(|desc| match desc.ty {
DescriptorType::Sampler | DescriptorType::CombinedImageSampler => {
Some(&mut desc.immutable_samplers)
}
_ => None,
})
.expect("binding_num does not refer to a sampler or combined image sampler descriptor");
immutable_samplers.clear();
immutable_samplers.extend(samplers.into_iter());
}
/// Sets the descriptor set layout to use push descriptors instead of descriptor sets.
///
/// If set to enabled, the
/// [`khr_push_descriptor`](crate::device::DeviceExtensions::khr_push_descriptor) extension must
/// be enabled on the device.
///
/// # Panics
///
/// - If enabled, panics if the description contains a dynamic buffer descriptor.
pub fn set_push_descriptor(&mut self, enabled: bool) {
if enabled {
assert!(
!self.descriptors.iter().flatten().any(|desc| {
matches!(
desc.ty,
DescriptorType::UniformBufferDynamic | DescriptorType::StorageBufferDynamic
)
}),
"descriptor set contains a dynamic buffer descriptor"
);
}
self.push_descriptor = enabled;
}
/// Sets the descriptor count for a descriptor that has a variable count.
pub fn set_variable_descriptor_count(&mut self, binding_num: u32, descriptor_count: u32) {
// TODO: Errors instead of panic
match self
.descriptors
.get_mut(binding_num as usize)
.and_then(|b| b.as_mut())
{
Some(desc) => {
desc.variable_count = true;
desc.descriptor_count = descriptor_count;
}
None => panic!("descriptor is empty"),
}
}
/// Returns whether `self` is compatible with `other`.
///
/// "Compatible" in this sense is defined by the Vulkan specification under the section
/// "Pipeline layout compatibility": the two must be identically defined to the Vulkan API,
/// meaning that all descriptors are compatible and flags are identical.
#[inline]
pub fn is_compatible_with(&self, other: &DescriptorSetDesc) -> bool {
if self.push_descriptor != other.push_descriptor {
return false;
}
let num_bindings = cmp::max(self.descriptors.len(), other.descriptors.len()) as u32;
(0..num_bindings).all(|binding_num| {
match (self.descriptor(binding_num), other.descriptor(binding_num)) {
(None, None) => true,
(Some(first), Some(second)) => first == second,
_ => false,
}
})
}
}
impl<I> From<I> for DescriptorSetDesc
where
I: IntoIterator<Item = Option<DescriptorDesc>>,
{
#[inline]
fn from(val: I) -> Self {
DescriptorSetDesc {
descriptors: val.into_iter().collect(),
push_descriptor: false,
}
}
}
/// Contains the exact description of a single descriptor.
///
/// > **Note**: You are free to fill a `DescriptorDesc` struct the way you want, but its validity
/// > will be checked when you create a pipeline layout, a descriptor set, or when you try to bind
/// > a descriptor set.
// TODO: add example
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DescriptorDesc {
/// Describes the content and layout of each array element of a descriptor.
pub ty: DescriptorType,
/// How many array elements this descriptor is made of. The value 0 is invalid and may trigger
/// a panic depending on the situation.
pub descriptor_count: u32,
/// True if the descriptor has a variable descriptor count. The value of `descriptor_count`
/// is taken as 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 last binding.
pub variable_count: bool,
/// Which shader stages are going to access this descriptor.
pub stages: ShaderStages,
/// Samplers that are included as a fixed part of the descriptor set layout. Once bound, they
/// do not need to be provided when creating a descriptor set.
///
/// The list must be either empty, or contain exactly `descriptor_count` samplers. It must be
/// empty if `ty` is something other than `Sampler` or `CombinedImageSampler`. If any of the
/// samplers has an attached sampler YCbCr conversion, then only `CombinedImageSampler` is
/// allowed.
pub immutable_samplers: Vec<Arc<Sampler>>,
}
impl DescriptorDesc {
/// Checks whether the descriptor of a pipeline layout `self` is compatible with the descriptor
/// of a shader `other`.
#[inline]
pub fn ensure_compatible_with_shader(
&self,
descriptor_requirements: &DescriptorRequirements,
) -> Result<(), DescriptorRequirementsNotMet> {
let DescriptorRequirements {
descriptor_types,
descriptor_count,
image_format,
image_multisampled,
image_scalar_type,
image_view_type,
sampler_compare,
sampler_no_unnormalized_coordinates,
sampler_no_ycbcr_conversion,
sampler_with_images,
stages,
storage_image_atomic,
storage_read,
storage_write,
} = descriptor_requirements;
if !descriptor_types.contains(&self.ty) {
return Err(DescriptorRequirementsNotMet::DescriptorType {
required: descriptor_types.clone(),
obtained: self.ty,
});
}
if self.descriptor_count < *descriptor_count {
return Err(DescriptorRequirementsNotMet::DescriptorCount {
required: *descriptor_count,
obtained: self.descriptor_count,
});
}
if !self.stages.is_superset_of(stages) {
return Err(DescriptorRequirementsNotMet::ShaderStages {
required: *stages,
obtained: self.stages,
});
}
Ok(())
}
}
impl From<&DescriptorRequirements> for DescriptorDesc {
fn from(reqs: &DescriptorRequirements) -> Self {
let ty = match reqs.descriptor_types[0] {
DescriptorType::Sampler => DescriptorType::Sampler,
DescriptorType::CombinedImageSampler => DescriptorType::CombinedImageSampler,
DescriptorType::SampledImage => DescriptorType::SampledImage,
DescriptorType::StorageImage => DescriptorType::StorageImage,
DescriptorType::UniformTexelBuffer => DescriptorType::UniformTexelBuffer,
DescriptorType::StorageTexelBuffer => DescriptorType::StorageTexelBuffer,
DescriptorType::UniformBuffer => DescriptorType::UniformBuffer,
DescriptorType::StorageBuffer => DescriptorType::StorageBuffer,
DescriptorType::UniformBufferDynamic => DescriptorType::UniformBufferDynamic,
DescriptorType::StorageBufferDynamic => DescriptorType::StorageBufferDynamic,
DescriptorType::InputAttachment => DescriptorType::InputAttachment,
};
Self {
ty,
descriptor_count: reqs.descriptor_count,
variable_count: false,
stages: reqs.stages,
immutable_samplers: Vec::new(),
}
}
}
/// Describes what kind of resource may later be bound to a descriptor.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(i32)]
pub enum DescriptorType {
Sampler = ash::vk::DescriptorType::SAMPLER.as_raw(),
CombinedImageSampler = ash::vk::DescriptorType::COMBINED_IMAGE_SAMPLER.as_raw(),
SampledImage = ash::vk::DescriptorType::SAMPLED_IMAGE.as_raw(),
StorageImage = ash::vk::DescriptorType::STORAGE_IMAGE.as_raw(),
UniformTexelBuffer = ash::vk::DescriptorType::UNIFORM_TEXEL_BUFFER.as_raw(),
StorageTexelBuffer = ash::vk::DescriptorType::STORAGE_TEXEL_BUFFER.as_raw(),
UniformBuffer = ash::vk::DescriptorType::UNIFORM_BUFFER.as_raw(),
StorageBuffer = ash::vk::DescriptorType::STORAGE_BUFFER.as_raw(),
UniformBufferDynamic = ash::vk::DescriptorType::UNIFORM_BUFFER_DYNAMIC.as_raw(),
StorageBufferDynamic = ash::vk::DescriptorType::STORAGE_BUFFER_DYNAMIC.as_raw(),
InputAttachment = ash::vk::DescriptorType::INPUT_ATTACHMENT.as_raw(),
}
impl From<DescriptorType> for ash::vk::DescriptorType {
#[inline]
fn from(val: DescriptorType) -> Self {
Self::from_raw(val as i32)
}
}
/// Error when checking whether the requirements for a descriptor have been met.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DescriptorRequirementsNotMet {
/// The descriptor's type is not one of those required.
DescriptorType {
required: Vec<DescriptorType>,
obtained: DescriptorType,
},
/// The descriptor count is less than what is required.
DescriptorCount { required: u32, obtained: u32 },
/// The descriptor's shader stages do not contain the stages that are required.
ShaderStages {
required: ShaderStages,
obtained: ShaderStages,
},
}
impl error::Error for DescriptorRequirementsNotMet {}
impl fmt::Display for DescriptorRequirementsNotMet {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
Self::DescriptorType { required, obtained } => write!(
fmt,
"the descriptor's type ({:?}) is not one of those required ({:?})",
obtained, required
),
Self::DescriptorCount { required, obtained } => write!(
fmt,
"the descriptor count ({}) is less than what is required ({})",
obtained, required
),
Self::ShaderStages { required, obtained } => write!(
fmt,
"the descriptor's shader stages do not contain the stages that are required",
),
}
}
}

View File

@ -1,24 +0,0 @@
// Copyright (c) 2021 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.
//! Describes the layout of all descriptors within a descriptor set.
//!
//! When creating a new descriptor set, you must provide a *layout* object to create it from. You
//! can create a descriptor set layout manually, but it is normally created automatically by each
//! pipeline layout.
pub use self::desc::DescriptorDesc;
pub use self::desc::DescriptorRequirementsNotMet;
pub use self::desc::DescriptorSetDesc;
pub use self::desc::DescriptorType;
pub use self::sys::DescriptorSetLayout;
pub use self::sys::DescriptorSetLayoutError;
mod desc;
mod sys;

View File

@ -1,504 +0,0 @@
// 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.
use crate::check_errors;
use crate::descriptor_set::layout::DescriptorDesc;
use crate::descriptor_set::layout::DescriptorSetDesc;
use crate::descriptor_set::layout::DescriptorType;
use crate::descriptor_set::pool::DescriptorsCount;
use crate::device::Device;
use crate::device::DeviceOwned;
use crate::OomError;
use crate::Version;
use crate::VulkanObject;
use std::mem::MaybeUninit;
use std::ptr;
use std::sync::Arc;
/// Describes to the Vulkan implementation the layout of all descriptors within a descriptor set.
#[derive(Debug)]
pub struct DescriptorSetLayout {
// The layout.
handle: ash::vk::DescriptorSetLayout,
// The device this layout belongs to.
device: Arc<Device>,
// Descriptors.
desc: DescriptorSetDesc,
// Number of descriptors of each type.
descriptors_count: DescriptorsCount,
}
impl DescriptorSetLayout {
/// Builds a new `DescriptorSetLayout` with the given descriptors.
///
/// The descriptors must be passed in the order of the bindings. In order words, descriptor
/// at bind point 0 first, then descriptor at bind point 1, and so on. If a binding must remain
/// empty, you can make the iterator yield `None` for an element.
pub fn new<D>(
device: Arc<Device>,
set_desc: D,
) -> Result<Arc<DescriptorSetLayout>, DescriptorSetLayoutError>
where
D: Into<DescriptorSetDesc>,
{
let set_desc = set_desc.into();
let mut descriptors_count = DescriptorsCount::zero();
let bindings = set_desc.bindings();
let mut bindings_vk = Vec::with_capacity(bindings.len());
let mut binding_flags_vk = Vec::with_capacity(bindings.len());
let mut immutable_samplers_vk: Vec<Box<[ash::vk::Sampler]>> = Vec::new(); // only to keep the arrays of handles alive
let mut flags = ash::vk::DescriptorSetLayoutCreateFlags::empty();
if set_desc.is_push_descriptor() {
if !device.enabled_extensions().khr_push_descriptor {
return Err(DescriptorSetLayoutError::ExtensionNotEnabled {
extension: "khr_push_descriptor",
reason: "description was set to be a push descriptor",
});
}
// TODO: VUID-VkDescriptorSetLayoutCreateInfo-flags-04590
// If flags contains VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR, flags must
// not contain VK_DESCRIPTOR_SET_LAYOUT_CREATE_HOST_ONLY_POOL_BIT_VALVE
flags |= ash::vk::DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR_KHR;
}
for (binding, binding_desc) in bindings.iter().enumerate() {
let binding_desc = match binding_desc {
Some(d) => d,
None => continue,
};
// FIXME: it is not legal to pass eg. the TESSELLATION_SHADER bit when the device
// doesn't have tess shaders enabled
let ty = binding_desc.ty;
if set_desc.is_push_descriptor() {
if matches!(
ty,
DescriptorType::StorageBufferDynamic | DescriptorType::UniformBufferDynamic
) {
return Err(DescriptorSetLayoutError::PushDescriptorDynamicBuffer);
}
if binding_desc.variable_count {
return Err(DescriptorSetLayoutError::PushDescriptorVariableCount);
}
// TODO: VUID-VkDescriptorSetLayoutBindingFlagsCreateInfo-flags-03003
// If VkDescriptorSetLayoutCreateInfo::flags includes
// VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR,
// then all elements of pBindingFlags must not include
// VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT,
// VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT, or
// VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT
// TODO: VUID-VkDescriptorSetLayoutCreateInfo-flags-02208
// If flags contains VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR, then all
// elements of pBindings must not have a descriptorType of
// VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT
// TODO: VUID-VkDescriptorSetLayoutCreateInfo-flags-04591
// If flags contains VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR,
// pBindings must not have a descriptorType of VK_DESCRIPTOR_TYPE_MUTABLE_VALVE
}
descriptors_count.add_num(ty, binding_desc.descriptor_count);
let mut binding_flags = ash::vk::DescriptorBindingFlags::empty();
let p_immutable_samplers = if !binding_desc.immutable_samplers.is_empty() {
if binding_desc
.immutable_samplers
.iter()
.any(|sampler| sampler.sampler_ycbcr_conversion().is_some())
{
if !matches!(ty, DescriptorType::CombinedImageSampler) {
return Err(DescriptorSetLayoutError::ImmutableSamplersWrongDescriptorType);
}
} else {
if !matches!(
ty,
DescriptorType::Sampler | DescriptorType::CombinedImageSampler
) {
return Err(DescriptorSetLayoutError::ImmutableSamplersWrongDescriptorType);
}
}
if binding_desc.descriptor_count != binding_desc.immutable_samplers.len() as u32 {
return Err(DescriptorSetLayoutError::ImmutableSamplersCountMismatch {
descriptor_count: binding_desc.descriptor_count,
sampler_count: binding_desc.immutable_samplers.len() as u32,
});
}
// TODO: VUID-VkDescriptorSetLayoutBinding-pImmutableSamplers-04009
// The sampler objects indicated by pImmutableSamplers must not have a borderColor
// with one of the values VK_BORDER_COLOR_FLOAT_CUSTOM_EXT or
// VK_BORDER_COLOR_INT_CUSTOM_EXT
let sampler_handles = binding_desc
.immutable_samplers
.iter()
.map(|s| s.internal_object())
.collect::<Vec<_>>()
.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_desc.variable_count {
if binding != bindings.len() - 1 {
return Err(DescriptorSetLayoutError::VariableCountDescMustBeLast);
}
if binding_desc.ty == DescriptorType::UniformBufferDynamic
|| binding_desc.ty == DescriptorType::StorageBufferDynamic
{
return Err(DescriptorSetLayoutError::VariableCountDescMustNotBeDynamic);
}
if !device.enabled_features().runtime_descriptor_array {
return Err(DescriptorSetLayoutError::FeatureNotEnabled {
feature: "runtime_descriptor_array",
reason: "binding has a variable count",
});
}
if !device
.enabled_features()
.descriptor_binding_variable_descriptor_count
{
return Err(DescriptorSetLayoutError::FeatureNotEnabled {
feature: "descriptor_binding_variable_descriptor_count",
reason: "binding has a variable count",
});
}
binding_flags |= ash::vk::DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT;
}
bindings_vk.push(ash::vk::DescriptorSetLayoutBinding {
binding: binding as u32,
descriptor_type: ty.into(),
descriptor_count: binding_desc.descriptor_count,
stage_flags: binding_desc.stages.into(),
p_immutable_samplers,
});
binding_flags_vk.push(binding_flags);
}
if set_desc.is_push_descriptor()
&& descriptors_count.total()
> device
.physical_device()
.properties()
.max_push_descriptors
.unwrap_or(0)
{
return Err(DescriptorSetLayoutError::MaxPushDescriptorsExceeded {
max: device
.physical_device()
.properties()
.max_push_descriptors
.unwrap_or(0),
obtained: descriptors_count.total(),
});
}
let handle = unsafe {
let binding_flags_infos = if device.api_version() >= Version::V1_2
|| device.enabled_extensions().ext_descriptor_indexing
{
Some(ash::vk::DescriptorSetLayoutBindingFlagsCreateInfo {
// Note that it seems legal to have no descriptor at all in the set.
binding_count: binding_flags_vk.len() as u32,
p_binding_flags: binding_flags_vk.as_ptr(),
..Default::default()
})
} else {
None
};
let infos = ash::vk::DescriptorSetLayoutCreateInfo {
flags,
binding_count: bindings_vk.len() as u32,
p_bindings: bindings_vk.as_ptr(),
p_next: if let Some(next) = binding_flags_infos.as_ref() {
next as *const _ as *const _
} else {
ptr::null()
},
..Default::default()
};
let mut output = MaybeUninit::uninit();
let fns = device.fns();
check_errors(fns.v1_0.create_descriptor_set_layout(
device.internal_object(),
&infos,
ptr::null(),
output.as_mut_ptr(),
))
.map_err(|e| OomError::from(e))?;
output.assume_init()
};
Ok(Arc::new(DescriptorSetLayout {
handle,
device,
desc: set_desc,
descriptors_count,
}))
}
pub(crate) fn desc(&self) -> &DescriptorSetDesc {
&self.desc
}
/// Returns the number of descriptors of each type.
#[inline]
pub fn descriptors_count(&self) -> &DescriptorsCount {
&self.descriptors_count
}
/// If the last binding has a variable count, returns its `descriptor_count`. Otherwise returns
/// 0.
#[inline]
pub fn variable_descriptor_count(&self) -> u32 {
self.desc
.bindings()
.last()
.and_then(|binding| binding.as_ref())
.map(|binding| {
if binding.variable_count {
binding.descriptor_count
} else {
0
}
})
.unwrap_or(0)
}
/// Returns the number of binding slots in the set.
#[inline]
pub fn num_bindings(&self) -> u32 {
self.desc.bindings().len() as u32
}
/// Returns a description of a descriptor, or `None` if out of range.
#[inline]
pub fn descriptor(&self, binding: u32) -> Option<DescriptorDesc> {
self.desc
.bindings()
.get(binding as usize)
.cloned()
.unwrap_or(None)
}
/// Returns whether `self` is compatible with `other`.
///
/// "Compatible" in this sense is defined by the Vulkan specification under the section
/// "Pipeline layout compatibility": either the two are the same descriptor set layout, or they
/// must be identically defined to the Vulkan API.
#[inline]
pub fn is_compatible_with(&self, other: &DescriptorSetLayout) -> bool {
self.handle == other.handle || self.desc.is_compatible_with(&other.desc)
}
}
unsafe impl DeviceOwned for DescriptorSetLayout {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
unsafe impl VulkanObject for DescriptorSetLayout {
type Object = ash::vk::DescriptorSetLayout;
#[inline]
fn internal_object(&self) -> ash::vk::DescriptorSetLayout {
self.handle
}
}
impl Drop for DescriptorSetLayout {
#[inline]
fn drop(&mut self) {
unsafe {
let fns = self.device.fns();
fns.v1_0.destroy_descriptor_set_layout(
self.device.internal_object(),
self.handle,
ptr::null(),
);
}
}
}
/// Error related to descriptor set layout.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DescriptorSetLayoutError {
ExtensionNotEnabled {
extension: &'static str,
reason: &'static str,
},
FeatureNotEnabled {
feature: &'static str,
reason: &'static str,
},
/// The number of immutable samplers does not match the descriptor count.
ImmutableSamplersCountMismatch {
descriptor_count: u32,
sampler_count: u32,
},
/// Immutable samplers were included on a descriptor type other than `Sampler` or
/// `CombinedImageSampler`, or, if any of the samplers had a sampler YCbCr conversion, were
/// included on a descriptor type other than `CombinedImageSampler`.
ImmutableSamplersWrongDescriptorType,
/// The maximum number of push descriptors has been exceeded.
MaxPushDescriptorsExceeded {
/// Maximum allowed value.
max: u32,
/// Value that was passed.
obtained: u32,
},
/// Out of Memory.
OomError(OomError),
/// The layout was being created for push descriptors, but included a dynamic buffer binding.
PushDescriptorDynamicBuffer,
/// The layout was being created for push descriptors, but included a variable count binding.
PushDescriptorVariableCount,
/// Variable count descriptor must be last binding.
VariableCountDescMustBeLast,
/// Variable count descriptor must not be a dynamic buffer.
VariableCountDescMustNotBeDynamic,
}
impl From<OomError> for DescriptorSetLayoutError {
fn from(error: OomError) -> Self {
Self::OomError(error)
}
}
impl std::error::Error for DescriptorSetLayoutError {}
impl std::fmt::Display for DescriptorSetLayoutError {
#[inline]
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match *self {
Self::ExtensionNotEnabled { extension, reason } => {
write!(
fmt,
"the extension {} must be enabled: {}",
extension, reason
)
}
Self::FeatureNotEnabled { feature, reason } => {
write!(fmt, "the feature {} must be enabled: {}", feature, reason)
}
Self::ImmutableSamplersCountMismatch { .. } => {
write!(
fmt,
"the number of immutable samplers does not match the descriptor count"
)
}
Self::ImmutableSamplersWrongDescriptorType => {
write!(
fmt,
"immutable samplers were included on a descriptor type other than Sampler or CombinedImageSampler, or, if any of the samplers had a sampler YCbCr conversion, were included on a descriptor type other than CombinedImageSampler"
)
}
Self::MaxPushDescriptorsExceeded { .. } => {
write!(
fmt,
"the maximum number of push descriptors has been exceeded"
)
}
Self::PushDescriptorDynamicBuffer => {
write!(fmt, "the layout was being created for push descriptors, but included a dynamic buffer binding")
}
Self::PushDescriptorVariableCount => {
write!(fmt, "the layout was being created for push descriptors, but included a variable count binding")
}
Self::OomError(_) => {
write!(fmt, "out of memory")
}
Self::VariableCountDescMustBeLast => {
write!(fmt, "variable count descriptor must be last binding")
}
Self::VariableCountDescMustNotBeDynamic => {
write!(
fmt,
"variable count descriptor must not be a dynamic buffer"
)
}
}
}
}
#[cfg(test)]
mod tests {
use crate::descriptor_set::layout::DescriptorDesc;
use crate::descriptor_set::layout::DescriptorSetDesc;
use crate::descriptor_set::layout::DescriptorSetLayout;
use crate::descriptor_set::layout::DescriptorType;
use crate::descriptor_set::pool::DescriptorsCount;
use crate::shader::ShaderStages;
use std::iter;
#[test]
fn empty() {
let (device, _) = gfx_dev_and_queue!();
let _layout = DescriptorSetLayout::new(device, DescriptorSetDesc::empty());
}
#[test]
fn basic_create() {
let (device, _) = gfx_dev_and_queue!();
let layout = DescriptorDesc {
ty: DescriptorType::UniformBuffer,
descriptor_count: 1,
variable_count: false,
stages: ShaderStages::all_graphics(),
immutable_samplers: Vec::new(),
};
let sl = DescriptorSetLayout::new(
device.clone(),
DescriptorSetDesc::new(iter::once(Some(layout))),
)
.unwrap();
assert_eq!(
sl.descriptors_count(),
&DescriptorsCount {
uniform_buffer: 1,
..DescriptorsCount::zero()
}
);
}
}

View File

@ -159,7 +159,7 @@ impl DescriptorSetInner {
descriptor_writes: impl IntoIterator<Item = WriteDescriptorSet>,
) -> Result<Self, DescriptorSetUpdateError> {
assert!(
!layout.desc().is_push_descriptor(),
!layout.push_descriptor(),
"the provided descriptor set layout is for push descriptors, and cannot be used to build a descriptor set object"
);
@ -184,8 +184,8 @@ impl DescriptorSetInner {
check_descriptor_write(&write, &layout, variable_descriptor_count)?;
resources.update(&write);
descriptor_write_info.push(write.to_vulkan_info(layout_binding.ty));
write_descriptor_set.push(write.to_vulkan(handle, layout_binding.ty));
descriptor_write_info.push(write.to_vulkan_info(layout_binding.descriptor_type));
write_descriptor_set.push(write.to_vulkan(handle, layout_binding.descriptor_type));
}
if !write_descriptor_set.is_empty() {
@ -256,19 +256,16 @@ impl DescriptorSetResources {
assert!(variable_descriptor_count <= layout.variable_descriptor_count());
let binding_resources = layout
.desc()
.bindings()
.iter()
.enumerate()
.filter_map(|(b, d)| d.as_ref().map(|d| (b as u32, d)))
.map(|(binding_num, binding_desc)| {
let count = if binding_desc.variable_count {
.map(|(&binding_num, binding)| {
let count = if binding.variable_descriptor_count {
variable_descriptor_count
} else {
binding_desc.descriptor_count
binding.descriptor_count
} as usize;
let binding_resources = match binding_desc.ty {
let binding_resources = match binding.descriptor_type {
DescriptorType::UniformBuffer
| DescriptorType::StorageBuffer
| DescriptorType::UniformBufferDynamic
@ -284,16 +281,16 @@ impl DescriptorSetResources {
DescriptorBindingResources::ImageView(smallvec![None; count])
}
DescriptorType::CombinedImageSampler => {
if binding_desc.immutable_samplers.is_empty() {
if binding.immutable_samplers.is_empty() {
DescriptorBindingResources::ImageViewSampler(smallvec![None; count])
} else {
DescriptorBindingResources::ImageView(smallvec![None; count])
}
}
DescriptorType::Sampler => {
if binding_desc.immutable_samplers.is_empty() {
if binding.immutable_samplers.is_empty() {
DescriptorBindingResources::Sampler(smallvec![None; count])
} else if layout.desc().is_push_descriptor() {
} else if layout.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])
@ -429,8 +426,8 @@ impl DescriptorSetWithOffsets {
// Ensure that the number of dynamic_offsets is correct and that each
// dynamic offset is a multiple of the minimum offset alignment specified
// by the physical device.
for desc in layout.desc().bindings() {
match desc.as_ref().unwrap().ty {
for binding in layout.bindings().values() {
match binding.descriptor_type {
DescriptorType::StorageBufferDynamic => {
// Don't check alignment if there are not enough offsets anyway
if dynamic_offsets.len() > dynamic_offset_index {

View File

@ -87,7 +87,7 @@ impl PersistentDescriptorSet {
P: ?Sized + DescriptorPool,
{
assert!(
!layout.desc().is_push_descriptor(),
!layout.push_descriptor(),
"the provided descriptor set layout is for push descriptors, and cannot be used to build a descriptor set object"
);

View File

@ -73,7 +73,7 @@ unsafe impl DescriptorPool for Arc<StdDescriptorPool> {
variable_descriptor_count: u32,
) -> Result<StdDescriptorPoolAlloc, OomError> {
assert!(
!layout.desc().is_push_descriptor(),
!layout.push_descriptor(),
"the provided descriptor set layout is for push descriptors, and cannot be used to build a descriptor set object",
);
@ -204,14 +204,13 @@ impl Drop for StdDescriptorPoolAlloc {
#[cfg(test)]
mod tests {
use crate::descriptor_set::layout::DescriptorDesc;
use crate::descriptor_set::layout::DescriptorSetDesc;
use crate::descriptor_set::layout::DescriptorSetLayout;
use crate::descriptor_set::layout::DescriptorSetLayoutBinding;
use crate::descriptor_set::layout::DescriptorSetLayoutCreateInfo;
use crate::descriptor_set::layout::DescriptorType;
use crate::descriptor_set::pool::DescriptorPool;
use crate::descriptor_set::pool::StdDescriptorPool;
use crate::shader::ShaderStages;
use std::iter;
use std::sync::Arc;
#[test]
@ -219,16 +218,19 @@ mod tests {
// Test that the `StdDescriptorPool` is kept alive by its allocations.
let (device, _) = gfx_dev_and_queue!();
let desc = DescriptorDesc {
ty: DescriptorType::Sampler,
descriptor_count: 1,
variable_count: false,
stages: ShaderStages::all(),
immutable_samplers: Vec::new(),
};
let layout = DescriptorSetLayout::new(
device.clone(),
DescriptorSetDesc::new(iter::once(Some(desc))),
DescriptorSetLayoutCreateInfo {
bindings: [(
0,
DescriptorSetLayoutBinding {
stages: ShaderStages::all(),
..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::Sampler)
},
)]
.into(),
..Default::default()
},
)
.unwrap();

View File

@ -162,7 +162,7 @@ impl UnsafeDescriptorPool {
info.layout.device().internal_object(),
"Tried to allocate from a pool with a set layout of a different device"
);
debug_assert!(!info.layout.desc().is_push_descriptor());
debug_assert!(!info.layout.push_descriptor());
debug_assert!(
info.variable_descriptor_count <= info.layout.variable_descriptor_count()
);
@ -373,9 +373,9 @@ impl fmt::Display for DescriptorPoolAllocError {
#[cfg(test)]
mod tests {
use crate::descriptor_set::layout::DescriptorDesc;
use crate::descriptor_set::layout::DescriptorSetDesc;
use crate::descriptor_set::layout::DescriptorSetLayout;
use crate::descriptor_set::layout::DescriptorSetLayoutBinding;
use crate::descriptor_set::layout::DescriptorSetLayoutCreateInfo;
use crate::descriptor_set::layout::DescriptorType;
use crate::descriptor_set::pool::sys::DescriptorSetAllocateInfo;
use crate::descriptor_set::pool::DescriptorsCount;
@ -420,17 +420,19 @@ mod tests {
fn basic_alloc() {
let (device, _) = gfx_dev_and_queue!();
let layout = DescriptorDesc {
ty: DescriptorType::UniformBuffer,
descriptor_count: 1,
variable_count: false,
stages: ShaderStages::all_graphics(),
immutable_samplers: Vec::new(),
};
let set_layout = DescriptorSetLayout::new(
device.clone(),
DescriptorSetDesc::new(iter::once(Some(layout))),
DescriptorSetLayoutCreateInfo {
bindings: [(
0,
DescriptorSetLayoutBinding {
stages: ShaderStages::all_graphics(),
..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer)
},
)]
.into(),
..Default::default()
},
)
.unwrap();
@ -456,17 +458,21 @@ mod tests {
let (device1, _) = gfx_dev_and_queue!();
let (device2, _) = gfx_dev_and_queue!();
let layout = DescriptorDesc {
ty: DescriptorType::UniformBuffer,
descriptor_count: 1,
variable_count: false,
stages: ShaderStages::all_graphics(),
immutable_samplers: Vec::new(),
};
let set_layout =
DescriptorSetLayout::new(device1, DescriptorSetDesc::new(iter::once(Some(layout))))
.unwrap();
let set_layout = DescriptorSetLayout::new(
device1,
DescriptorSetLayoutCreateInfo {
bindings: [(
0,
DescriptorSetLayoutBinding {
stages: ShaderStages::all_graphics(),
..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer)
},
)]
.into(),
..Default::default()
},
)
.unwrap();
let desc = DescriptorsCount {
uniform_buffer: 10,

View File

@ -49,7 +49,7 @@ impl SingleLayoutDescSetPool {
/// - Panics if the provided `layout` has a binding with a variable descriptor count.
pub fn new(layout: Arc<DescriptorSetLayout>) -> Self {
assert!(
!layout.desc().is_push_descriptor(),
!layout.push_descriptor(),
"the provided descriptor set layout is for push descriptors, and cannot be used to build a descriptor set object"
);
assert!(

View File

@ -51,7 +51,7 @@ impl UnsafeDescriptorSet {
let (infos, mut writes): (SmallVec<[_; 8]>, SmallVec<[_; 8]>) = writes
.into_iter()
.map(|write| {
let descriptor_type = layout.descriptor(write.binding()).unwrap().ty;
let descriptor_type = layout.bindings()[&write.binding()].descriptor_type;
(
write.to_vulkan_info(descriptor_type),

View File

@ -9,7 +9,7 @@
use crate::buffer::view::BufferViewAbstract;
use crate::buffer::{BufferAccess, BufferInner};
use crate::descriptor_set::layout::{DescriptorDesc, DescriptorType};
use crate::descriptor_set::layout::{DescriptorSetLayoutBinding, DescriptorType};
use crate::descriptor_set::DescriptorSetLayout;
use crate::device::DeviceOwned;
use crate::image::view::{ImageViewAbstract, ImageViewType};
@ -400,8 +400,8 @@ pub(crate) fn check_descriptor_write<'a>(
write: &WriteDescriptorSet,
layout: &'a DescriptorSetLayout,
variable_descriptor_count: u32,
) -> Result<&'a DescriptorDesc, DescriptorSetUpdateError> {
let layout_binding = match layout.desc().descriptor(write.binding()) {
) -> Result<&'a DescriptorSetLayoutBinding, DescriptorSetUpdateError> {
let layout_binding = match layout.bindings().get(&write.binding()) {
Some(binding) => binding,
None => {
return Err(DescriptorSetUpdateError::InvalidBinding {
@ -410,7 +410,7 @@ pub(crate) fn check_descriptor_write<'a>(
}
};
let max_descriptor_count = if layout_binding.variable_count {
let max_descriptor_count = if layout_binding.variable_descriptor_count {
variable_descriptor_count
} else {
layout_binding.descriptor_count
@ -432,10 +432,9 @@ pub(crate) fn check_descriptor_write<'a>(
}
match elements {
WriteDescriptorSetElements::None(num_elements) => match layout_binding.ty {
WriteDescriptorSetElements::None(num_elements) => match layout_binding.descriptor_type {
DescriptorType::Sampler
if layout.desc().is_push_descriptor()
&& !layout_binding.immutable_samplers.is_empty() => {}
if layout.push_descriptor() && !layout_binding.immutable_samplers.is_empty() => {}
_ => {
return Err(DescriptorSetUpdateError::IncompatibleDescriptorType {
binding: write.binding(),
@ -443,7 +442,7 @@ pub(crate) fn check_descriptor_write<'a>(
}
},
WriteDescriptorSetElements::Buffer(elements) => {
match layout_binding.ty {
match layout_binding.descriptor_type {
DescriptorType::StorageBuffer | DescriptorType::StorageBufferDynamic => {
for (index, buffer) in elements.iter().enumerate() {
assert_eq!(
@ -495,7 +494,7 @@ pub(crate) fn check_descriptor_write<'a>(
assert!(layout.device().enabled_features().robust_buffer_access);
}
WriteDescriptorSetElements::BufferView(elements) => {
match layout_binding.ty {
match layout_binding.descriptor_type {
DescriptorType::StorageTexelBuffer => {
for (index, buffer_view) in elements.iter().enumerate() {
assert_eq!(
@ -548,7 +547,7 @@ pub(crate) fn check_descriptor_write<'a>(
}
}
}
WriteDescriptorSetElements::ImageView(elements) => match layout_binding.ty {
WriteDescriptorSetElements::ImageView(elements) => match layout_binding.descriptor_type {
DescriptorType::CombinedImageSampler
if !layout_binding.immutable_samplers.is_empty() =>
{
@ -776,7 +775,9 @@ pub(crate) fn check_descriptor_write<'a>(
})
}
},
WriteDescriptorSetElements::ImageViewSampler(elements) => match layout_binding.ty {
WriteDescriptorSetElements::ImageViewSampler(elements) => match layout_binding
.descriptor_type
{
DescriptorType::CombinedImageSampler => {
if !layout_binding.immutable_samplers.is_empty() {
return Err(DescriptorSetUpdateError::SamplerIsImmutable {
@ -855,7 +856,7 @@ pub(crate) fn check_descriptor_write<'a>(
})
}
},
WriteDescriptorSetElements::Sampler(elements) => match layout_binding.ty {
WriteDescriptorSetElements::Sampler(elements) => match layout_binding.descriptor_type {
DescriptorType::Sampler => {
if !layout_binding.immutable_samplers.is_empty() {
return Err(DescriptorSetUpdateError::SamplerIsImmutable {

View File

@ -22,8 +22,11 @@
//! any descriptor sets and/or push constants that the pipeline needs, and then issuing a `dispatch`
//! command on the command buffer.
use super::layout::PipelineLayoutCreateInfo;
use crate::check_errors;
use crate::descriptor_set::layout::{DescriptorSetDesc, DescriptorSetLayout};
use crate::descriptor_set::layout::{
DescriptorSetLayout, DescriptorSetLayoutCreateInfo, DescriptorSetLayoutCreationError,
};
use crate::device::{Device, DeviceOwned};
use crate::pipeline::cache::PipelineCache;
use crate::pipeline::layout::{
@ -74,20 +77,27 @@ impl ComputePipeline {
) -> Result<Arc<ComputePipeline>, ComputePipelineCreationError>
where
Css: SpecializationConstants,
F: FnOnce(&mut [DescriptorSetDesc]),
F: FnOnce(&mut [DescriptorSetLayoutCreateInfo]),
{
let mut descriptor_set_layout_descs =
DescriptorSetDesc::from_requirements(shader.descriptor_requirements());
func(&mut descriptor_set_layout_descs);
let descriptor_set_layouts = descriptor_set_layout_descs
let mut set_layout_create_infos =
DescriptorSetLayoutCreateInfo::from_requirements(shader.descriptor_requirements());
func(&mut set_layout_create_infos);
let set_layouts = set_layout_create_infos
.iter()
.map(|desc| Ok(DescriptorSetLayout::new(device.clone(), desc.clone())?))
.collect::<Result<Vec<_>, PipelineLayoutCreationError>>()?;
.map(|desc| DescriptorSetLayout::new(device.clone(), desc.clone()))
.collect::<Result<Vec<_>, _>>()?;
let layout = PipelineLayout::new(
device.clone(),
descriptor_set_layouts,
shader.push_constant_requirements().cloned(),
PipelineLayoutCreateInfo {
set_layouts,
push_constant_ranges: shader
.push_constant_requirements()
.cloned()
.into_iter()
.collect(),
..Default::default()
},
)?;
unsafe {
@ -308,6 +318,8 @@ impl Drop for ComputePipeline {
pub enum ComputePipelineCreationError {
/// Not enough memory.
OomError(OomError),
/// 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.
@ -320,10 +332,11 @@ impl error::Error for ComputePipelineCreationError {
#[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
ComputePipelineCreationError::OomError(ref err) => Some(err),
ComputePipelineCreationError::PipelineLayoutCreationError(ref err) => Some(err),
ComputePipelineCreationError::IncompatiblePipelineLayout(ref err) => Some(err),
ComputePipelineCreationError::IncompatibleSpecializationConstants => None,
Self::OomError(ref err) => Some(err),
Self::DescriptorSetLayoutCreationError(ref err) => Some(err),
Self::PipelineLayoutCreationError(ref err) => Some(err),
Self::IncompatiblePipelineLayout(ref err) => Some(err),
Self::IncompatibleSpecializationConstants => None,
}
}
}
@ -336,6 +349,9 @@ impl fmt::Display for ComputePipelineCreationError {
"{}",
match *self {
ComputePipelineCreationError::OomError(_) => "not enough memory available",
ComputePipelineCreationError::DescriptorSetLayoutCreationError(_) => {
"error while creating a descriptor set layout object"
}
ComputePipelineCreationError::PipelineLayoutCreationError(_) => {
"error while creating the pipeline layout object"
}
@ -353,21 +369,28 @@ impl fmt::Display for ComputePipelineCreationError {
impl From<OomError> for ComputePipelineCreationError {
#[inline]
fn from(err: OomError) -> ComputePipelineCreationError {
ComputePipelineCreationError::OomError(err)
Self::OomError(err)
}
}
impl From<DescriptorSetLayoutCreationError> for ComputePipelineCreationError {
#[inline]
fn from(err: DescriptorSetLayoutCreationError) -> Self {
Self::DescriptorSetLayoutCreationError(err)
}
}
impl From<PipelineLayoutCreationError> for ComputePipelineCreationError {
#[inline]
fn from(err: PipelineLayoutCreationError) -> ComputePipelineCreationError {
ComputePipelineCreationError::PipelineLayoutCreationError(err)
fn from(err: PipelineLayoutCreationError) -> Self {
Self::PipelineLayoutCreationError(err)
}
}
impl From<PipelineLayoutSupersetError> for ComputePipelineCreationError {
#[inline]
fn from(err: PipelineLayoutSupersetError) -> ComputePipelineCreationError {
ComputePipelineCreationError::IncompatiblePipelineLayout(err)
fn from(err: PipelineLayoutSupersetError) -> Self {
Self::IncompatiblePipelineLayout(err)
}
}
@ -375,12 +398,8 @@ impl From<Error> for ComputePipelineCreationError {
#[inline]
fn from(err: Error) -> ComputePipelineCreationError {
match err {
err @ Error::OutOfHostMemory => {
ComputePipelineCreationError::OomError(OomError::from(err))
}
err @ Error::OutOfDeviceMemory => {
ComputePipelineCreationError::OomError(OomError::from(err))
}
err @ Error::OutOfHostMemory => Self::OomError(OomError::from(err)),
err @ Error::OutOfDeviceMemory => Self::OomError(OomError::from(err)),
_ => panic!("unexpected error: {:?}", err),
}
}
@ -484,12 +503,7 @@ mod tests {
CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), false, 0).unwrap();
let set = PersistentDescriptorSet::new(
pipeline
.layout()
.descriptor_set_layouts()
.get(0)
.unwrap()
.clone(),
pipeline.layout().set_layouts().get(0).unwrap().clone(),
[WriteDescriptorSet::buffer(0, data_buffer.clone())],
)
.unwrap();

View File

@ -12,7 +12,7 @@
#![allow(deprecated)]
use crate::check_errors;
use crate::descriptor_set::layout::{DescriptorSetDesc, DescriptorSetLayout};
use crate::descriptor_set::layout::{DescriptorSetLayout, DescriptorSetLayoutCreateInfo};
use crate::device::Device;
use crate::format::NumericType;
use crate::pipeline::cache::PipelineCache;
@ -32,7 +32,7 @@ use crate::pipeline::graphics::vertex_input::{
};
use crate::pipeline::graphics::viewport::{Scissor, Viewport, ViewportState};
use crate::pipeline::graphics::{GraphicsPipeline, GraphicsPipelineCreationError};
use crate::pipeline::layout::{PipelineLayout, PipelineLayoutCreationError, PipelineLayoutPcRange};
use crate::pipeline::layout::{PipelineLayout, PipelineLayoutCreateInfo, PushConstantRange};
use crate::pipeline::{DynamicState, PartialStateMode, StateMode};
use crate::render_pass::Subpass;
use crate::shader::{
@ -144,9 +144,9 @@ where
func: F,
) -> Result<Arc<GraphicsPipeline>, GraphicsPipelineCreationError>
where
F: FnOnce(&mut [DescriptorSetDesc]),
F: FnOnce(&mut [DescriptorSetLayoutCreateInfo]),
{
let (descriptor_set_layout_descs, push_constant_ranges) = {
let (set_layout_create_infos, push_constant_ranges) = {
let stages: SmallVec<[&EntryPoint; 5]> = [
self.vertex_shader.as_ref().map(|s| &s.0),
self.tessellation_shaders.as_ref().map(|s| &s.control.0),
@ -186,12 +186,12 @@ where
// Build a description of a descriptor set layout from the shader requirements, then
// feed it to the user-provided closure to allow tweaking.
let mut descriptor_set_layout_descs = DescriptorSetDesc::from_requirements(
let mut set_layout_create_infos = DescriptorSetLayoutCreateInfo::from_requirements(
descriptor_requirements
.iter()
.map(|(&loc, reqs)| (loc, reqs)),
);
func(&mut descriptor_set_layout_descs);
func(&mut set_layout_create_infos);
// We want to union each push constant range into a set of ranges that do not have intersecting stage flags.
// e.g. The range [0, 16) is either made available to Vertex | Fragment or we only make [0, 16) available to
@ -211,23 +211,29 @@ where
}
let push_constant_ranges: Vec<_> = range_map
.iter()
.map(|((offset, size), stages)| PipelineLayoutPcRange {
.map(|((offset, size), stages)| PushConstantRange {
stages: *stages,
offset: *offset,
size: *size,
stages: *stages,
})
.collect();
(descriptor_set_layout_descs, push_constant_ranges)
(set_layout_create_infos, push_constant_ranges)
};
let descriptor_set_layouts = descriptor_set_layout_descs
let set_layouts = set_layout_create_infos
.into_iter()
.map(|desc| Ok(DescriptorSetLayout::new(device.clone(), desc)?))
.collect::<Result<Vec<_>, PipelineLayoutCreationError>>()?;
let pipeline_layout =
PipelineLayout::new(device.clone(), descriptor_set_layouts, push_constant_ranges)
.unwrap();
.map(|desc| DescriptorSetLayout::new(device.clone(), desc))
.collect::<Result<Vec<_>, _>>()?;
let pipeline_layout = PipelineLayout::new(
device.clone(),
PipelineLayoutCreateInfo {
set_layouts,
push_constant_ranges,
..Default::default()
},
)
.unwrap();
self.with_pipeline_layout(device, pipeline_layout)
}

View File

@ -7,6 +7,7 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
use crate::descriptor_set::layout::DescriptorSetLayoutCreationError;
use crate::format::Format;
use crate::format::NumericType;
use crate::pipeline::graphics::vertex_input::IncompatibleVertexDefinitionError;
@ -134,6 +135,9 @@ pub enum GraphicsPipelineCreationError {
/// Not enough memory.
OomError(OomError),
/// Error while creating a descriptor set layout object.
DescriptorSetLayoutCreationError(DescriptorSetLayoutCreationError),
/// Error while creating the pipeline layout object.
PipelineLayoutCreationError(PipelineLayoutCreationError),
@ -278,6 +282,10 @@ impl fmt::Display for GraphicsPipelineCreationError {
fmt,
"not enough memory available",
),
Self::DescriptorSetLayoutCreationError(_) => write!(
fmt,
"error while creating a descriptor set layout object",
),
Self::PipelineLayoutCreationError(_) => write!(
fmt,
"error while creating the pipeline layout object",
@ -341,30 +349,37 @@ impl From<OomError> for GraphicsPipelineCreationError {
}
}
impl From<DescriptorSetLayoutCreationError> for GraphicsPipelineCreationError {
#[inline]
fn from(err: DescriptorSetLayoutCreationError) -> Self {
Self::DescriptorSetLayoutCreationError(err)
}
}
impl From<PipelineLayoutCreationError> for GraphicsPipelineCreationError {
#[inline]
fn from(err: PipelineLayoutCreationError) -> GraphicsPipelineCreationError {
fn from(err: PipelineLayoutCreationError) -> Self {
Self::PipelineLayoutCreationError(err)
}
}
impl From<PipelineLayoutSupersetError> for GraphicsPipelineCreationError {
#[inline]
fn from(err: PipelineLayoutSupersetError) -> GraphicsPipelineCreationError {
fn from(err: PipelineLayoutSupersetError) -> Self {
Self::IncompatiblePipelineLayout(err)
}
}
impl From<IncompatibleVertexDefinitionError> for GraphicsPipelineCreationError {
#[inline]
fn from(err: IncompatibleVertexDefinitionError) -> GraphicsPipelineCreationError {
fn from(err: IncompatibleVertexDefinitionError) -> Self {
Self::IncompatibleVertexDefinition(err)
}
}
impl From<Error> for GraphicsPipelineCreationError {
#[inline]
fn from(err: Error) -> GraphicsPipelineCreationError {
fn from(err: Error) -> Self {
match err {
err @ Error::OutOfHostMemory => Self::OomError(OomError::from(err)),
err @ Error::OutOfDeviceMemory => Self::OomError(OomError::from(err)),

File diff suppressed because it is too large Load Diff

View File

@ -27,7 +27,7 @@
//! # let image_data: Vec<u8> = return;
//! # let queue: std::sync::Arc<vulkano::device::Queue> = return;
//! use vulkano::descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet};
//! use vulkano::descriptor_set::layout::{DescriptorDesc, DescriptorSetLayout, DescriptorSetDesc, DescriptorType};
//! use vulkano::descriptor_set::layout::{DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType};
//! use vulkano::format::Format;
//! use vulkano::image::{ImmutableImage, ImageCreateFlags, ImageDimensions, ImageUsage, MipmapsCount};
//! use vulkano::image::view::ImageView;
@ -46,13 +46,18 @@
//!
//! let descriptor_set_layout = DescriptorSetLayout::new(
//! device.clone(),
//! DescriptorSetDesc::new([Some(DescriptorDesc {
//! ty: DescriptorType::CombinedImageSampler,
//! descriptor_count: 1,
//! variable_count: false,
//! stages: ShaderStage::Fragment.into(),
//! immutable_samplers: vec![sampler],
//! })]),
//! DescriptorSetLayoutCreateInfo {
//! bindings: [(
//! 0,
//! DescriptorSetLayoutBinding {
//! stages: ShaderStage::Fragment.into(),
//! immutable_samplers: vec![sampler],
//! ..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::CombinedImageSampler)
//! },
//! )]
//! .into(),
//! ..Default::default()
//! },
//! ).unwrap();
//!
//! let (image, future) = ImmutableImage::from_iter(

View File

@ -23,7 +23,7 @@ use crate::device::Device;
use crate::format::{Format, NumericType};
use crate::image::view::ImageViewType;
use crate::pipeline::graphics::input_assembly::PrimitiveTopology;
use crate::pipeline::layout::PipelineLayoutPcRange;
use crate::pipeline::layout::PushConstantRange;
use crate::shader::spirv::{Capability, Spirv, SpirvError};
use crate::sync::PipelineStages;
use crate::DeviceSize;
@ -369,7 +369,7 @@ impl Display for ShaderSupportError {
pub struct EntryPointInfo {
pub execution: ShaderExecution,
pub descriptor_requirements: FnvHashMap<(u32, u32), DescriptorRequirements>,
pub push_constant_requirements: Option<PipelineLayoutPcRange>,
pub push_constant_requirements: Option<PushConstantRange>,
pub specialization_constant_requirements: FnvHashMap<u32, SpecializationConstantRequirements>,
pub input_interface: ShaderInterface,
pub output_interface: ShaderInterface,
@ -417,7 +417,7 @@ impl<'a> EntryPoint<'a> {
/// Returns the push constant requirements.
#[inline]
pub fn push_constant_requirements(&self) -> Option<&PipelineLayoutPcRange> {
pub fn push_constant_requirements(&self) -> Option<&PushConstantRange> {
self.info.push_constant_requirements.as_ref()
}

View File

@ -14,7 +14,7 @@ use crate::image::view::ImageViewType;
use crate::shader::ShaderScalarType;
use crate::DeviceSize;
use crate::{
pipeline::layout::PipelineLayoutPcRange,
pipeline::layout::PushConstantRange,
shader::{
spirv::{
Capability, Decoration, Dim, ExecutionMode, ExecutionModel, Id, Instruction, Spirv,
@ -909,8 +909,8 @@ fn descriptor_requirements_of(spirv: &Spirv, variable_id: Id) -> DescriptorVaria
}
}
/// Extracts the `PipelineLayoutPcRange` from `spirv`.
fn push_constant_requirements(spirv: &Spirv, stage: ShaderStage) -> Option<PipelineLayoutPcRange> {
/// Extracts the `PushConstantRange` from `spirv`.
fn push_constant_requirements(spirv: &Spirv, stage: ShaderStage) -> Option<PushConstantRange> {
spirv
.iter_global()
.find_map(|instruction| match instruction {
@ -927,10 +927,10 @@ fn push_constant_requirements(spirv: &Spirv, stage: ShaderStage) -> Option<Pipel
let start = offset_of_struct(spirv, ty);
let end =
size_of_type(spirv, ty).expect("Found runtime-sized push constants") as u32;
Some(PipelineLayoutPcRange {
Some(PushConstantRange {
stages: stage.into(),
offset: start,
size: end - start,
stages: stage.into(),
})
}
_ => None,