use crate::{
descriptor_set::layout::{DescriptorRequirementsNotMet, DescriptorSetLayout, DescriptorType},
device::{Device, DeviceOwned},
shader::{DescriptorRequirements, ShaderStages},
OomError, RequirementNotMet, RequiresOneOf, VulkanError, VulkanObject,
};
use smallvec::SmallVec;
use std::{
error::Error,
fmt::{Display, Error as FmtError, Formatter},
mem::MaybeUninit,
num::NonZeroU64,
ptr,
sync::Arc,
};
#[derive(Debug)]
pub struct PipelineLayout {
handle: ash::vk::PipelineLayout,
device: Arc<Device>,
id: NonZeroU64,
set_layouts: Vec<Arc<DescriptorSetLayout>>,
push_constant_ranges: Vec<PushConstantRange>,
push_constant_ranges_disjoint: Vec<PushConstantRange>,
}
impl PipelineLayout {
pub fn new(
device: Arc<Device>,
mut create_info: PipelineLayoutCreateInfo,
) -> Result<Arc<PipelineLayout>, PipelineLayoutCreationError> {
Self::validate(&device, &mut create_info)?;
let handle = unsafe { Self::create(&device, &create_info)? };
let PipelineLayoutCreateInfo {
set_layouts,
mut push_constant_ranges,
_ne: _,
} = create_info;
push_constant_ranges.sort_unstable_by_key(|range| {
(
range.offset,
range.size,
ash::vk::ShaderStageFlags::from(range.stages),
)
});
let push_constant_ranges_disjoint =
Self::create_push_constant_ranges_disjoint(&push_constant_ranges);
Ok(Arc::new(PipelineLayout {
handle,
device,
id: Self::next_id(),
set_layouts,
push_constant_ranges,
push_constant_ranges_disjoint,
}))
}
fn create_push_constant_ranges_disjoint(
push_constant_ranges: &Vec<PushConstantRange>,
) -> Vec<PushConstantRange> {
let mut push_constant_ranges_disjoint: Vec<PushConstantRange> =
Vec::with_capacity(push_constant_ranges.len());
if !push_constant_ranges.is_empty() {
let mut min_offset = push_constant_ranges[0].offset;
loop {
let mut max_offset = u32::MAX;
let mut stages = ShaderStages::empty();
for range in push_constant_ranges.iter() {
if range.offset > min_offset {
max_offset = max_offset.min(range.offset);
break;
} else if range.offset + range.size > min_offset {
max_offset = max_offset.min(range.offset + range.size);
stages |= range.stages;
}
}
if stages.is_empty() {
break;
}
push_constant_ranges_disjoint.push(PushConstantRange {
stages,
offset: min_offset,
size: max_offset - min_offset,
});
min_offset = max_offset;
}
}
push_constant_ranges_disjoint
}
fn validate(
device: &Device,
create_info: &mut PipelineLayoutCreateInfo,
) -> Result<(), PipelineLayoutCreationError> {
let &mut PipelineLayoutCreateInfo {
ref set_layouts,
ref push_constant_ranges,
_ne: _,
} = create_info;
let properties = device.physical_device().properties();
if set_layouts.len() > properties.max_bound_descriptor_sets as usize {
return Err(
PipelineLayoutCreationError::MaxBoundDescriptorSetsExceeded {
provided: set_layouts.len() as u32,
max_supported: properties.max_bound_descriptor_sets,
},
);
}
{
let mut num_resources = Counter::default();
let mut num_samplers = Counter::default();
let mut num_uniform_buffers = Counter::default();
let mut num_uniform_buffers_dynamic = 0;
let mut num_storage_buffers = Counter::default();
let mut num_storage_buffers_dynamic = 0;
let mut num_sampled_images = Counter::default();
let mut num_storage_images = Counter::default();
let mut num_input_attachments = Counter::default();
let mut push_descriptor_set = None;
for (set_num, set_layout) in set_layouts.iter().enumerate() {
let set_num = set_num as u32;
if set_layout.push_descriptor() {
if push_descriptor_set.is_some() {
return Err(PipelineLayoutCreationError::SetLayoutsPushDescriptorMultiple);
} else {
push_descriptor_set = Some(set_num);
}
}
for layout_binding in set_layout.bindings().values() {
num_resources
.increment(layout_binding.descriptor_count, &layout_binding.stages);
match layout_binding.descriptor_type {
DescriptorType::Sampler => {
num_samplers
.increment(layout_binding.descriptor_count, &layout_binding.stages);
}
DescriptorType::CombinedImageSampler => {
num_samplers
.increment(layout_binding.descriptor_count, &layout_binding.stages);
num_sampled_images
.increment(layout_binding.descriptor_count, &layout_binding.stages);
}
DescriptorType::SampledImage | DescriptorType::UniformTexelBuffer => {
num_sampled_images
.increment(layout_binding.descriptor_count, &layout_binding.stages);
}
DescriptorType::StorageImage | DescriptorType::StorageTexelBuffer => {
num_storage_images
.increment(layout_binding.descriptor_count, &layout_binding.stages);
}
DescriptorType::UniformBuffer => {
num_uniform_buffers
.increment(layout_binding.descriptor_count, &layout_binding.stages);
}
DescriptorType::UniformBufferDynamic => {
num_uniform_buffers
.increment(layout_binding.descriptor_count, &layout_binding.stages);
num_uniform_buffers_dynamic += 1;
}
DescriptorType::StorageBuffer => {
num_storage_buffers
.increment(layout_binding.descriptor_count, &layout_binding.stages);
}
DescriptorType::StorageBufferDynamic => {
num_storage_buffers
.increment(layout_binding.descriptor_count, &layout_binding.stages);
num_storage_buffers_dynamic += 1;
}
DescriptorType::InputAttachment => {
num_input_attachments
.increment(layout_binding.descriptor_count, &layout_binding.stages);
}
}
}
}
if num_resources.max_per_stage() > properties.max_per_stage_resources {
return Err(PipelineLayoutCreationError::MaxPerStageResourcesExceeded {
provided: num_resources.max_per_stage(),
max_supported: properties.max_per_stage_resources,
});
}
if num_samplers.max_per_stage() > properties.max_per_stage_descriptor_samplers {
return Err(
PipelineLayoutCreationError::MaxPerStageDescriptorSamplersExceeded {
provided: num_samplers.max_per_stage(),
max_supported: properties.max_per_stage_descriptor_samplers,
},
);
}
if num_uniform_buffers.max_per_stage()
> properties.max_per_stage_descriptor_uniform_buffers
{
return Err(
PipelineLayoutCreationError::MaxPerStageDescriptorUniformBuffersExceeded {
provided: num_uniform_buffers.max_per_stage(),
max_supported: properties.max_per_stage_descriptor_uniform_buffers,
},
);
}
if num_storage_buffers.max_per_stage()
> properties.max_per_stage_descriptor_storage_buffers
{
return Err(
PipelineLayoutCreationError::MaxPerStageDescriptorStorageBuffersExceeded {
provided: num_storage_buffers.max_per_stage(),
max_supported: properties.max_per_stage_descriptor_storage_buffers,
},
);
}
if num_sampled_images.max_per_stage()
> properties.max_per_stage_descriptor_sampled_images
{
return Err(
PipelineLayoutCreationError::MaxPerStageDescriptorSampledImagesExceeded {
provided: num_sampled_images.max_per_stage(),
max_supported: properties.max_per_stage_descriptor_sampled_images,
},
);
}
if num_storage_images.max_per_stage()
> properties.max_per_stage_descriptor_storage_images
{
return Err(
PipelineLayoutCreationError::MaxPerStageDescriptorStorageImagesExceeded {
provided: num_storage_images.max_per_stage(),
max_supported: properties.max_per_stage_descriptor_storage_images,
},
);
}
if num_input_attachments.max_per_stage()
> properties.max_per_stage_descriptor_input_attachments
{
return Err(
PipelineLayoutCreationError::MaxPerStageDescriptorInputAttachmentsExceeded {
provided: num_input_attachments.max_per_stage(),
max_supported: properties.max_per_stage_descriptor_input_attachments,
},
);
}
if num_samplers.total > properties.max_descriptor_set_samplers {
return Err(
PipelineLayoutCreationError::MaxDescriptorSetSamplersExceeded {
provided: num_samplers.total,
max_supported: properties.max_descriptor_set_samplers,
},
);
}
if num_uniform_buffers.total > properties.max_descriptor_set_uniform_buffers {
return Err(
PipelineLayoutCreationError::MaxDescriptorSetUniformBuffersExceeded {
provided: num_uniform_buffers.total,
max_supported: properties.max_descriptor_set_uniform_buffers,
},
);
}
if num_uniform_buffers_dynamic > properties.max_descriptor_set_uniform_buffers_dynamic {
return Err(
PipelineLayoutCreationError::MaxDescriptorSetUniformBuffersDynamicExceeded {
provided: num_uniform_buffers_dynamic,
max_supported: properties.max_descriptor_set_uniform_buffers_dynamic,
},
);
}
if num_storage_buffers.total > properties.max_descriptor_set_storage_buffers {
return Err(
PipelineLayoutCreationError::MaxDescriptorSetStorageBuffersExceeded {
provided: num_storage_buffers.total,
max_supported: properties.max_descriptor_set_storage_buffers,
},
);
}
if num_storage_buffers_dynamic > properties.max_descriptor_set_storage_buffers_dynamic {
return Err(
PipelineLayoutCreationError::MaxDescriptorSetStorageBuffersDynamicExceeded {
provided: num_storage_buffers_dynamic,
max_supported: properties.max_descriptor_set_storage_buffers_dynamic,
},
);
}
if num_sampled_images.total > properties.max_descriptor_set_sampled_images {
return Err(
PipelineLayoutCreationError::MaxDescriptorSetSampledImagesExceeded {
provided: num_sampled_images.total,
max_supported: properties.max_descriptor_set_sampled_images,
},
);
}
if num_storage_images.total > properties.max_descriptor_set_storage_images {
return Err(
PipelineLayoutCreationError::MaxDescriptorSetStorageImagesExceeded {
provided: num_storage_images.total,
max_supported: properties.max_descriptor_set_storage_images,
},
);
}
if num_input_attachments.total > properties.max_descriptor_set_input_attachments {
return Err(
PipelineLayoutCreationError::MaxDescriptorSetInputAttachmentsExceeded {
provided: num_input_attachments.total,
max_supported: properties.max_descriptor_set_input_attachments,
},
);
}
}
push_constant_ranges
.iter()
.try_fold(ShaderStages::empty(), |total, range| {
let &PushConstantRange {
stages,
offset,
size,
} = range;
stages.validate_device(device)?;
assert!(!stages.is_empty());
assert!(offset % 4 == 0);
assert!(size != 0);
assert!(size % 4 == 0);
if offset + size > properties.max_push_constants_size {
return Err(PipelineLayoutCreationError::MaxPushConstantsSizeExceeded {
provided: offset + size,
max_supported: properties.max_push_constants_size,
});
}
if !(total & stages).is_empty() {
return Err(PipelineLayoutCreationError::PushConstantRangesStageMultiple);
}
Ok(total | stages)
})?;
Ok(())
}
unsafe fn create(
device: &Device,
create_info: &PipelineLayoutCreateInfo,
) -> Result<ash::vk::PipelineLayout, PipelineLayoutCreationError> {
let PipelineLayoutCreateInfo {
set_layouts,
push_constant_ranges,
_ne: _,
} = create_info;
let set_layouts: SmallVec<[_; 4]> = set_layouts.iter().map(|l| l.handle()).collect();
let push_constant_ranges: SmallVec<[_; 4]> = push_constant_ranges
.iter()
.map(|range| ash::vk::PushConstantRange {
stage_flags: range.stages.into(),
offset: range.offset,
size: range.size,
})
.collect();
let create_info = ash::vk::PipelineLayoutCreateInfo {
flags: ash::vk::PipelineLayoutCreateFlags::empty(),
set_layout_count: set_layouts.len() as u32,
p_set_layouts: set_layouts.as_ptr(),
push_constant_range_count: push_constant_ranges.len() as u32,
p_push_constant_ranges: push_constant_ranges.as_ptr(),
..Default::default()
};
let handle = {
let fns = device.fns();
let mut output = MaybeUninit::uninit();
(fns.v1_0.create_pipeline_layout)(
device.handle(),
&create_info,
ptr::null(),
output.as_mut_ptr(),
)
.result()
.map_err(VulkanError::from)?;
output.assume_init()
};
Ok(handle)
}
#[inline]
pub unsafe fn from_handle(
device: Arc<Device>,
handle: ash::vk::PipelineLayout,
create_info: PipelineLayoutCreateInfo,
) -> Arc<PipelineLayout> {
let PipelineLayoutCreateInfo {
set_layouts,
push_constant_ranges,
_ne: _,
} = create_info;
let push_constant_ranges_disjoint =
Self::create_push_constant_ranges_disjoint(&push_constant_ranges);
Arc::new(PipelineLayout {
handle,
device,
id: Self::next_id(),
set_layouts,
push_constant_ranges,
push_constant_ranges_disjoint,
})
}
#[inline]
pub fn set_layouts(&self) -> &[Arc<DescriptorSetLayout>] {
&self.set_layouts
}
#[inline]
pub fn push_constant_ranges(&self) -> &[PushConstantRange] {
&self.push_constant_ranges
}
#[inline]
pub(crate) fn push_constant_ranges_disjoint(&self) -> &[PushConstantRange] {
&self.push_constant_ranges_disjoint
}
#[inline]
pub fn is_compatible_with(&self, other: &PipelineLayout, num_sets: u32) -> bool {
let num_sets = num_sets as usize;
assert!(num_sets >= self.set_layouts.len());
if self == other {
return true;
}
if self.push_constant_ranges != other.push_constant_ranges {
return false;
}
let other_sets = match other.set_layouts.get(0..num_sets) {
Some(x) => x,
None => return false,
};
self.set_layouts
.iter()
.zip(other_sets)
.all(|(self_set_layout, other_set_layout)| {
self_set_layout.is_compatible_with(other_set_layout)
})
}
pub fn ensure_compatible_with_shader<'a>(
&self,
descriptor_requirements: impl IntoIterator<Item = ((u32, u32), &'a DescriptorRequirements)>,
push_constant_range: Option<&PushConstantRange>,
) -> Result<(), PipelineLayoutSupersetError> {
for ((set_num, binding_num), reqs) in descriptor_requirements.into_iter() {
let layout_binding = self
.set_layouts
.get(set_num as usize)
.and_then(|set_layout| set_layout.bindings().get(&binding_num));
let layout_binding = match layout_binding {
Some(x) => x,
None => {
return Err(PipelineLayoutSupersetError::DescriptorMissing {
set_num,
binding_num,
})
}
};
if let Err(error) = layout_binding.ensure_compatible_with_shader(reqs) {
return Err(PipelineLayoutSupersetError::DescriptorRequirementsNotMet {
set_num,
binding_num,
error,
});
}
}
if let Some(range) = push_constant_range {
for own_range in self.push_constant_ranges.iter() {
if range.stages.intersects(&own_range.stages) && (range.offset < own_range.offset || own_range.offset + own_range.size < range.offset + range.size)
{
return Err(PipelineLayoutSupersetError::PushConstantRange {
first_range: *own_range,
second_range: *range,
});
}
}
}
Ok(())
}
}
impl Drop for PipelineLayout {
#[inline]
fn drop(&mut self) {
unsafe {
let fns = self.device.fns();
(fns.v1_0.destroy_pipeline_layout)(self.device.handle(), self.handle, ptr::null());
}
}
}
unsafe impl VulkanObject for PipelineLayout {
type Handle = ash::vk::PipelineLayout;
#[inline]
fn handle(&self) -> Self::Handle {
self.handle
}
}
unsafe impl DeviceOwned for PipelineLayout {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
crate::impl_id_counter!(PipelineLayout);
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PipelineLayoutCreationError {
OomError(OomError),
RequirementNotMet {
required_for: &'static str,
requires_one_of: RequiresOneOf,
},
MaxBoundDescriptorSetsExceeded { provided: u32, max_supported: u32 },
MaxDescriptorSetSamplersExceeded { provided: u32, max_supported: u32 },
MaxDescriptorSetUniformBuffersExceeded { provided: u32, max_supported: u32 },
MaxDescriptorSetUniformBuffersDynamicExceeded { provided: u32, max_supported: u32 },
MaxDescriptorSetStorageBuffersExceeded { provided: u32, max_supported: u32 },
MaxDescriptorSetStorageBuffersDynamicExceeded { provided: u32, max_supported: u32 },
MaxDescriptorSetSampledImagesExceeded { provided: u32, max_supported: u32 },
MaxDescriptorSetStorageImagesExceeded { provided: u32, max_supported: u32 },
MaxDescriptorSetInputAttachmentsExceeded { provided: u32, max_supported: u32 },
MaxPerStageResourcesExceeded { provided: u32, max_supported: u32 },
MaxPerStageDescriptorSamplersExceeded { provided: u32, max_supported: u32 },
MaxPerStageDescriptorUniformBuffersExceeded { provided: u32, max_supported: u32 },
MaxPerStageDescriptorStorageBuffersExceeded { provided: u32, max_supported: u32 },
MaxPerStageDescriptorSampledImagesExceeded { provided: u32, max_supported: u32 },
MaxPerStageDescriptorStorageImagesExceeded { provided: u32, max_supported: u32 },
MaxPerStageDescriptorInputAttachmentsExceeded { provided: u32, max_supported: u32 },
MaxPushConstantsSizeExceeded { provided: u32, max_supported: u32 },
PushConstantRangesStageMultiple,
SetLayoutsPushDescriptorMultiple,
}
impl Error for PipelineLayoutCreationError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::OomError(err) => Some(err),
_ => None,
}
}
}
impl Display for PipelineLayoutCreationError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
Self::OomError(_) => write!(f, "not enough memory available"),
Self::RequirementNotMet {
required_for,
requires_one_of,
} => write!(
f,
"a requirement was not met for: {}; requires one of: {}",
required_for, requires_one_of,
),
Self::MaxBoundDescriptorSetsExceeded {
provided,
max_supported,
} => write!(
f,
"the number of elements in `set_layouts` ({}) is greater than the \
`max_bound_descriptor_sets` limit ({})",
provided, max_supported,
),
Self::MaxDescriptorSetSamplersExceeded {
provided,
max_supported,
} => write!(
f,
"the `set_layouts` contain more `DescriptorType::Sampler` and \
`DescriptorType::CombinedImageSampler` descriptors ({}) than the \
`max_descriptor_set_samplers` limit ({})",
provided, max_supported,
),
Self::MaxDescriptorSetUniformBuffersExceeded {
provided,
max_supported,
} => write!(
f,
"the `set_layouts` contain more `DescriptorType::UniformBuffer` descriptors ({}) \
than the `max_descriptor_set_uniform_buffers` limit ({})",
provided, max_supported,
),
Self::MaxDescriptorSetUniformBuffersDynamicExceeded {
provided,
max_supported,
} => write!(
f,
"the `set_layouts` contain more `DescriptorType::UniformBufferDynamic` descriptors \
({}) than the `max_descriptor_set_uniform_buffers_dynamic` limit ({})",
provided, max_supported,
),
Self::MaxDescriptorSetStorageBuffersExceeded {
provided,
max_supported,
} => write!(
f,
"the `set_layouts` contain more `DescriptorType::StorageBuffer` descriptors ({}) \
than the `max_descriptor_set_storage_buffers` limit ({})",
provided, max_supported,
),
Self::MaxDescriptorSetStorageBuffersDynamicExceeded {
provided,
max_supported,
} => write!(
f,
"the `set_layouts` contain more `DescriptorType::StorageBufferDynamic` descriptors \
({}) than the `max_descriptor_set_storage_buffers_dynamic` limit ({})",
provided, max_supported,
),
Self::MaxDescriptorSetSampledImagesExceeded {
provided,
max_supported,
} => write!(
f,
"the `set_layouts` contain more `DescriptorType::SampledImage`, \
`DescriptorType::CombinedImageSampler` and `DescriptorType::UniformTexelBuffer` \
descriptors ({}) than the `max_descriptor_set_sampled_images` limit ({})",
provided, max_supported,
),
Self::MaxDescriptorSetStorageImagesExceeded {
provided,
max_supported,
} => write!(
f,
"the `set_layouts` contain more `DescriptorType::StorageImage` and \
`DescriptorType::StorageTexelBuffer` descriptors ({}) than the \
`max_descriptor_set_storage_images` limit ({})",
provided, max_supported,
),
Self::MaxDescriptorSetInputAttachmentsExceeded {
provided,
max_supported,
} => write!(
f,
"the `set_layouts` contain more `DescriptorType::InputAttachment` descriptors ({}) \
than the `max_descriptor_set_input_attachments` limit ({})",
provided, max_supported,
),
Self::MaxPerStageResourcesExceeded {
provided,
max_supported,
} => write!(
f,
"the `set_layouts` contain more bound resources ({}) in a single stage than the \
`max_per_stage_resources` limit ({})",
provided, max_supported,
),
Self::MaxPerStageDescriptorSamplersExceeded {
provided,
max_supported,
} => write!(
f,
"the `set_layouts` contain more `DescriptorType::Sampler` and \
`DescriptorType::CombinedImageSampler` descriptors ({}) in a single stage than the \
`max_per_stage_descriptor_set_samplers` limit ({})",
provided, max_supported,
),
Self::MaxPerStageDescriptorUniformBuffersExceeded {
provided,
max_supported,
} => write!(
f,
"the `set_layouts` contain more `DescriptorType::UniformBuffer` and \
`DescriptorType::UniformBufferDynamic` descriptors ({}) in a single stage than the \
`max_per_stage_descriptor_set_uniform_buffers` limit ({})",
provided, max_supported,
),
Self::MaxPerStageDescriptorStorageBuffersExceeded {
provided,
max_supported,
} => write!(
f,
"the `set_layouts` contain more `DescriptorType::StorageBuffer` and \
`DescriptorType::StorageBufferDynamic` descriptors ({}) in a single stage than the \
`max_per_stage_descriptor_set_storage_buffers` limit ({})",
provided, max_supported,
),
Self::MaxPerStageDescriptorSampledImagesExceeded {
provided,
max_supported,
} => write!(
f,
"the `set_layouts` contain more `DescriptorType::SampledImage`, \
`DescriptorType::CombinedImageSampler` and `DescriptorType::UniformTexelBuffer` \
descriptors ({}) in a single stage than the \
`max_per_stage_descriptor_set_sampled_images` limit ({})",
provided, max_supported,
),
Self::MaxPerStageDescriptorStorageImagesExceeded {
provided,
max_supported,
} => write!(
f,
"the `set_layouts` contain more `DescriptorType::StorageImage` and \
`DescriptorType::StorageTexelBuffer` descriptors ({}) in a single stage than the \
`max_per_stage_descriptor_set_storage_images` limit ({})",
provided, max_supported,
),
Self::MaxPerStageDescriptorInputAttachmentsExceeded {
provided,
max_supported,
} => write!(
f,
"the `set_layouts` contain more `DescriptorType::InputAttachment` descriptors ({}) \
in a single stage than the `max_per_stage_descriptor_set_input_attachments` limit \
({})",
provided, max_supported,
),
Self::MaxPushConstantsSizeExceeded {
provided,
max_supported,
} => write!(
f,
"an element in `push_constant_ranges` has an `offset + size` ({}) greater than the \
`max_push_constants_size` limit ({})",
provided, max_supported,
),
Self::PushConstantRangesStageMultiple => write!(
f,
"a shader stage appears in multiple elements of `push_constant_ranges`",
),
Self::SetLayoutsPushDescriptorMultiple => write!(
f,
"multiple elements of `set_layouts` have `push_descriptor` enabled",
),
}
}
}
impl From<OomError> for PipelineLayoutCreationError {
fn from(err: OomError) -> PipelineLayoutCreationError {
PipelineLayoutCreationError::OomError(err)
}
}
impl From<VulkanError> for PipelineLayoutCreationError {
fn from(err: VulkanError) -> PipelineLayoutCreationError {
match err {
err @ VulkanError::OutOfHostMemory => {
PipelineLayoutCreationError::OomError(OomError::from(err))
}
err @ VulkanError::OutOfDeviceMemory => {
PipelineLayoutCreationError::OomError(OomError::from(err))
}
_ => panic!("unexpected error: {:?}", err),
}
}
}
impl From<RequirementNotMet> for PipelineLayoutCreationError {
fn from(err: RequirementNotMet) -> Self {
Self::RequirementNotMet {
required_for: err.required_for,
requires_one_of: err.requires_one_of,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PipelineLayoutSupersetError {
DescriptorMissing {
set_num: u32,
binding_num: u32,
},
DescriptorRequirementsNotMet {
set_num: u32,
binding_num: u32,
error: DescriptorRequirementsNotMet,
},
PushConstantRange {
first_range: PushConstantRange,
second_range: PushConstantRange,
},
}
impl Error for PipelineLayoutSupersetError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
PipelineLayoutSupersetError::DescriptorRequirementsNotMet { error, .. } => Some(error),
_ => None,
}
}
}
impl Display for PipelineLayoutSupersetError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
PipelineLayoutSupersetError::DescriptorRequirementsNotMet {
set_num,
binding_num,
..
} => write!(
f,
"the descriptor at set {} binding {} does not meet the requirements",
set_num, binding_num,
),
PipelineLayoutSupersetError::DescriptorMissing {
set_num,
binding_num,
} => write!(
f,
"a descriptor at set {} binding {} is required by the shaders, but is missing from \
the pipeline layout",
set_num, binding_num,
),
PipelineLayoutSupersetError::PushConstantRange {
first_range,
second_range,
} => {
writeln!(f, "our range did not completely encompass the other range")?;
writeln!(f, " our stages: {:?}", first_range.stages)?;
writeln!(
f,
" our range: {} - {}",
first_range.offset,
first_range.offset + first_range.size,
)?;
writeln!(f, " other stages: {:?}", second_range.stages)?;
write!(
f,
" other range: {} - {}",
second_range.offset,
second_range.offset + second_range.size,
)
}
}
}
}
#[derive(Clone, Debug)]
pub struct PipelineLayoutCreateInfo {
pub set_layouts: Vec<Arc<DescriptorSetLayout>>,
pub push_constant_ranges: Vec<PushConstantRange>,
pub _ne: crate::NonExhaustive,
}
impl Default for PipelineLayoutCreateInfo {
#[inline]
fn default() -> Self {
Self {
set_layouts: Vec::new(),
push_constant_ranges: Vec::new(),
_ne: crate::NonExhaustive(()),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PushConstantRange {
pub stages: ShaderStages,
pub offset: u32,
pub size: u32,
}
impl Default for PushConstantRange {
#[inline]
fn default() -> Self {
Self {
stages: ShaderStages::empty(),
offset: 0,
size: 0,
}
}
}
#[derive(Default)]
struct Counter {
total: u32,
compute: u32,
vertex: u32,
geometry: u32,
tess_ctl: u32,
tess_eval: u32,
frag: u32,
}
impl Counter {
fn increment(&mut self, num: u32, stages: &ShaderStages) {
self.total += num;
if stages.compute {
self.compute += num;
}
if stages.vertex {
self.vertex += num;
}
if stages.tessellation_control {
self.tess_ctl += num;
}
if stages.tessellation_evaluation {
self.tess_eval += num;
}
if stages.geometry {
self.geometry += num;
}
if stages.fragment {
self.frag += num;
}
}
fn max_per_stage(&self) -> u32 {
[
self.compute,
self.vertex,
self.tess_ctl,
self.tess_eval,
self.geometry,
self.frag,
]
.into_iter()
.max()
.unwrap_or(0)
}
}
#[cfg(test)]
mod tests {
use super::PipelineLayout;
use crate::{
pipeline::layout::{PipelineLayoutCreateInfo, PushConstantRange},
shader::ShaderStages,
};
#[test]
fn push_constant_ranges_disjoint() {
let test_cases = [
(
&[
PushConstantRange {
stages: ShaderStages {
fragment: true,
..Default::default()
},
offset: 0,
size: 12,
},
PushConstantRange {
stages: ShaderStages {
vertex: true,
..Default::default()
},
offset: 0,
size: 40,
},
][..],
&[
PushConstantRange {
stages: ShaderStages {
vertex: true,
fragment: true,
..Default::default()
},
offset: 0,
size: 12,
},
PushConstantRange {
stages: ShaderStages {
vertex: true,
..Default::default()
},
offset: 12,
size: 28,
},
][..],
),
(
&[
PushConstantRange {
stages: ShaderStages {
fragment: true,
..Default::default()
},
offset: 0,
size: 12,
},
PushConstantRange {
stages: ShaderStages {
vertex: true,
..Default::default()
},
offset: 4,
size: 36,
},
][..],
&[
PushConstantRange {
stages: ShaderStages {
fragment: true,
..Default::default()
},
offset: 0,
size: 4,
},
PushConstantRange {
stages: ShaderStages {
fragment: true,
vertex: true,
..Default::default()
},
offset: 4,
size: 8,
},
PushConstantRange {
stages: ShaderStages {
vertex: true,
..Default::default()
},
offset: 12,
size: 28,
},
][..],
),
(
&[
PushConstantRange {
stages: ShaderStages {
fragment: true,
..Default::default()
},
offset: 0,
size: 12,
},
PushConstantRange {
stages: ShaderStages {
compute: true,
..Default::default()
},
offset: 8,
size: 12,
},
PushConstantRange {
stages: ShaderStages {
vertex: true,
..Default::default()
},
offset: 4,
size: 12,
},
PushConstantRange {
stages: ShaderStages {
tessellation_control: true,
..Default::default()
},
offset: 8,
size: 24,
},
][..],
&[
PushConstantRange {
stages: ShaderStages {
fragment: true,
..Default::default()
},
offset: 0,
size: 4,
},
PushConstantRange {
stages: ShaderStages {
fragment: true,
vertex: true,
..Default::default()
},
offset: 4,
size: 4,
},
PushConstantRange {
stages: ShaderStages {
vertex: true,
fragment: true,
compute: true,
tessellation_control: true,
..Default::default()
},
offset: 8,
size: 4,
},
PushConstantRange {
stages: ShaderStages {
vertex: true,
compute: true,
tessellation_control: true,
..Default::default()
},
offset: 12,
size: 4,
},
PushConstantRange {
stages: ShaderStages {
compute: true,
tessellation_control: true,
..Default::default()
},
offset: 16,
size: 4,
},
PushConstantRange {
stages: ShaderStages {
tessellation_control: true,
..Default::default()
},
offset: 20,
size: 12,
},
][..],
),
];
let (device, _) = gfx_dev_and_queue!();
for (input, expected) in test_cases {
let layout = PipelineLayout::new(
device.clone(),
PipelineLayoutCreateInfo {
push_constant_ranges: input.into(),
..Default::default()
},
)
.unwrap();
assert_eq!(layout.push_constant_ranges_disjoint.as_slice(), expected);
}
}
}