Check pipeline layout limits (#757)

* Check pipeline layout limits

* Fix copyright year

* Fix unimplemented!() for PipelineLayoutLimitsError

* Fix style

* Fix minor mistake
This commit is contained in:
tomaka 2017-08-22 14:29:28 +02:00 committed by GitHub
parent 06a0b3715f
commit 94c376c268
4 changed files with 467 additions and 21 deletions

View File

@ -0,0 +1,440 @@
// Copyright (c) 2017 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://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.
//! Contains the `check_desc_against_limits` function and the `PipelineLayoutLimitsError` error.
use std::error;
use std::fmt;
use device::Device;
use descriptor::descriptor::DescriptorType;
use descriptor::descriptor::ShaderStages;
use descriptor::pipeline_layout::PipelineLayoutDesc;
use descriptor::pipeline_layout::PipelineLayoutDescPcRange;
/// Checks whether the pipeline layout description fulfills the device limits requirements.
pub fn check_desc_against_limits<D>(device: &Device, desc: &D)
-> Result<(), PipelineLayoutLimitsError>
where D: ?Sized + PipelineLayoutDesc
{
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();
for set in 0 .. desc.num_sets() {
for binding in 0 .. desc.num_bindings_in_set(set).unwrap() {
let descriptor = desc.descriptor(set, binding).unwrap();
num_resources.increment(descriptor.array_count, &descriptor.stages);
match descriptor.ty.ty().expect("Not implemented yet") { // TODO:
DescriptorType::Sampler => {
num_samplers.increment(descriptor.array_count, &descriptor.stages);
},
DescriptorType::CombinedImageSampler => {
num_samplers.increment(descriptor.array_count, &descriptor.stages);
num_sampled_images.increment(descriptor.array_count, &descriptor.stages);
},
DescriptorType::SampledImage | DescriptorType::UniformTexelBuffer => {
num_sampled_images.increment(descriptor.array_count, &descriptor.stages);
},
DescriptorType::StorageImage | DescriptorType::StorageTexelBuffer => {
num_storage_images.increment(descriptor.array_count, &descriptor.stages);
},
DescriptorType::UniformBuffer => {
num_uniform_buffers.increment(descriptor.array_count, &descriptor.stages);
},
DescriptorType::UniformBufferDynamic => {
num_uniform_buffers.increment(descriptor.array_count, &descriptor.stages);
num_uniform_buffers_dynamic += 1;
},
DescriptorType::StorageBuffer => {
num_storage_buffers.increment(descriptor.array_count, &descriptor.stages);
},
DescriptorType::StorageBufferDynamic => {
num_storage_buffers.increment(descriptor.array_count, &descriptor.stages);
num_storage_buffers_dynamic += 1;
},
DescriptorType::InputAttachment => {
num_input_attachments.increment(descriptor.array_count, &descriptor.stages);
},
}
}
}
let limits = device.physical_device().limits();
if desc.num_sets() > limits.max_bound_descriptor_sets() as usize {
return Err(PipelineLayoutLimitsError::MaxDescriptorSetsLimitExceeded {
limit: limits.max_bound_descriptor_sets() as usize,
requested: desc.num_sets(),
});
}
if num_resources.max_per_stage() > limits.max_per_stage_resources() {
return Err(PipelineLayoutLimitsError::MaxPerStageResourcesLimitExceeded {
limit: limits.max_per_stage_resources(),
requested: num_resources.max_per_stage(),
});
}
if num_samplers.max_per_stage() > limits.max_per_stage_descriptor_samplers() {
return Err(PipelineLayoutLimitsError::MaxPerStageDescriptorSamplersLimitExceeded {
limit: limits.max_per_stage_descriptor_samplers(),
requested: num_samplers.max_per_stage(),
});
}
if num_uniform_buffers.max_per_stage() > limits.max_per_stage_descriptor_uniform_buffers() {
return Err(PipelineLayoutLimitsError::MaxPerStageDescriptorUniformBuffersLimitExceeded {
limit: limits.max_per_stage_descriptor_uniform_buffers(),
requested: num_uniform_buffers.max_per_stage(),
});
}
if num_storage_buffers.max_per_stage() > limits.max_per_stage_descriptor_storage_buffers() {
return Err(PipelineLayoutLimitsError::MaxPerStageDescriptorStorageBuffersLimitExceeded {
limit: limits.max_per_stage_descriptor_storage_buffers(),
requested: num_storage_buffers.max_per_stage(),
});
}
if num_sampled_images.max_per_stage() > limits.max_per_stage_descriptor_sampled_images() {
return Err(PipelineLayoutLimitsError::MaxPerStageDescriptorSampledImagesLimitExceeded {
limit: limits.max_per_stage_descriptor_sampled_images(),
requested: num_sampled_images.max_per_stage(),
});
}
if num_storage_images.max_per_stage() > limits.max_per_stage_descriptor_storage_images() {
return Err(PipelineLayoutLimitsError::MaxPerStageDescriptorStorageImagesLimitExceeded {
limit: limits.max_per_stage_descriptor_storage_images(),
requested: num_storage_images.max_per_stage(),
});
}
if num_input_attachments.max_per_stage() > limits.max_per_stage_descriptor_input_attachments() {
return Err(PipelineLayoutLimitsError::MaxPerStageDescriptorInputAttachmentsLimitExceeded {
limit: limits.max_per_stage_descriptor_input_attachments(),
requested: num_input_attachments.max_per_stage(),
});
}
if num_samplers.total > limits.max_descriptor_set_samplers() {
return Err(PipelineLayoutLimitsError::MaxDescriptorSetSamplersLimitExceeded {
limit: limits.max_descriptor_set_samplers(),
requested: num_samplers.total,
});
}
if num_uniform_buffers.total > limits.max_descriptor_set_uniform_buffers() {
return Err(PipelineLayoutLimitsError::MaxDescriptorSetUniformBuffersLimitExceeded {
limit: limits.max_descriptor_set_uniform_buffers(),
requested: num_uniform_buffers.total,
});
}
if num_uniform_buffers_dynamic > limits.max_descriptor_set_uniform_buffers_dynamic() {
return Err(PipelineLayoutLimitsError::MaxDescriptorSetUniformBuffersDynamicLimitExceeded {
limit: limits.max_descriptor_set_uniform_buffers_dynamic(),
requested: num_uniform_buffers_dynamic,
});
}
if num_storage_buffers.total > limits.max_descriptor_set_storage_buffers() {
return Err(PipelineLayoutLimitsError::MaxDescriptorSetStorageBuffersLimitExceeded {
limit: limits.max_descriptor_set_storage_buffers(),
requested: num_storage_buffers.total,
});
}
if num_storage_buffers_dynamic > limits.max_descriptor_set_storage_buffers_dynamic() {
return Err(PipelineLayoutLimitsError::MaxDescriptorSetStorageBuffersDynamicLimitExceeded {
limit: limits.max_descriptor_set_storage_buffers_dynamic(),
requested: num_storage_buffers_dynamic,
});
}
if num_sampled_images.total > limits.max_descriptor_set_sampled_images() {
return Err(PipelineLayoutLimitsError::MaxDescriptorSetSampledImagesLimitExceeded {
limit: limits.max_descriptor_set_sampled_images(),
requested: num_sampled_images.total,
});
}
if num_storage_images.total > limits.max_descriptor_set_storage_images() {
return Err(PipelineLayoutLimitsError::MaxDescriptorSetStorageImagesLimitExceeded {
limit: limits.max_descriptor_set_storage_images(),
requested: num_storage_images.total,
});
}
if num_input_attachments.total > limits.max_descriptor_set_input_attachments() {
return Err(PipelineLayoutLimitsError::MaxDescriptorSetInputAttachmentsLimitExceeded {
limit: limits.max_descriptor_set_input_attachments(),
requested: num_input_attachments.total,
});
}
for pc_id in 0 .. desc.num_push_constants_ranges() {
let PipelineLayoutDescPcRange {
offset,
size,
stages,
} = {
match desc.push_constants_range(pc_id) {
Some(o) => o,
None => continue,
}
};
if offset + size > limits.max_push_constants_size() as usize {
return Err(PipelineLayoutLimitsError::MaxPushConstantsSizeExceeded {
limit: limits.max_push_constants_size() as usize,
requested: offset + size,
});
}
}
Ok(())
}
/// The pipeline layout description isn't compatible with the hardware limits.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PipelineLayoutLimitsError {
/// The maximum number of descriptor sets has been exceeded.
MaxDescriptorSetsLimitExceeded {
/// The limit that must be fulfilled.
limit: usize,
/// What was requested.
requested: usize,
},
/// The maximum size of push constants has been exceeded.
MaxPushConstantsSizeExceeded {
/// The limit that must be fulfilled.
limit: usize,
/// What was requested.
requested: usize,
},
/// The `max_per_stage_resources()` limit has been exceeded.
MaxPerStageResourcesLimitExceeded {
/// The limit that must be fulfilled.
limit: u32,
/// What was requested.
requested: u32,
},
/// The `max_per_stage_descriptor_samplers()` limit has been exceeded.
MaxPerStageDescriptorSamplersLimitExceeded {
/// The limit that must be fulfilled.
limit: u32,
/// What was requested.
requested: u32,
},
/// The `max_per_stage_descriptor_uniform_buffers()` limit has been exceeded.
MaxPerStageDescriptorUniformBuffersLimitExceeded {
/// The limit that must be fulfilled.
limit: u32,
/// What was requested.
requested: u32,
},
/// The `max_per_stage_descriptor_storage_buffers()` limit has been exceeded.
MaxPerStageDescriptorStorageBuffersLimitExceeded {
/// The limit that must be fulfilled.
limit: u32,
/// What was requested.
requested: u32,
},
/// The `max_per_stage_descriptor_sampled_images()` limit has been exceeded.
MaxPerStageDescriptorSampledImagesLimitExceeded {
/// The limit that must be fulfilled.
limit: u32,
/// What was requested.
requested: u32,
},
/// The `max_per_stage_descriptor_storage_images()` limit has been exceeded.
MaxPerStageDescriptorStorageImagesLimitExceeded {
/// The limit that must be fulfilled.
limit: u32,
/// What was requested.
requested: u32,
},
/// The `max_per_stage_descriptor_input_attachments()` limit has been exceeded.
MaxPerStageDescriptorInputAttachmentsLimitExceeded {
/// The limit that must be fulfilled.
limit: u32,
/// What was requested.
requested: u32,
},
/// The `max_descriptor_set_samplers()` limit has been exceeded.
MaxDescriptorSetSamplersLimitExceeded {
/// The limit that must be fulfilled.
limit: u32,
/// What was requested.
requested: u32,
},
/// The `max_descriptor_set_uniform_buffers()` limit has been exceeded.
MaxDescriptorSetUniformBuffersLimitExceeded {
/// The limit that must be fulfilled.
limit: u32,
/// What was requested.
requested: u32,
},
/// The `max_descriptor_set_uniform_buffers_dynamic()` limit has been exceeded.
MaxDescriptorSetUniformBuffersDynamicLimitExceeded {
/// The limit that must be fulfilled.
limit: u32,
/// What was requested.
requested: u32,
},
/// The `max_descriptor_set_storage_buffers()` limit has been exceeded.
MaxDescriptorSetStorageBuffersLimitExceeded {
/// The limit that must be fulfilled.
limit: u32,
/// What was requested.
requested: u32,
},
/// The `max_descriptor_set_storage_buffers_dynamic()` limit has been exceeded.
MaxDescriptorSetStorageBuffersDynamicLimitExceeded {
/// The limit that must be fulfilled.
limit: u32,
/// What was requested.
requested: u32,
},
/// The `max_descriptor_set_sampled_images()` limit has been exceeded.
MaxDescriptorSetSampledImagesLimitExceeded {
/// The limit that must be fulfilled.
limit: u32,
/// What was requested.
requested: u32,
},
/// The `max_descriptor_set_storage_images()` limit has been exceeded.
MaxDescriptorSetStorageImagesLimitExceeded {
/// The limit that must be fulfilled.
limit: u32,
/// What was requested.
requested: u32,
},
/// The `max_descriptor_set_input_attachments()` limit has been exceeded.
MaxDescriptorSetInputAttachmentsLimitExceeded {
/// The limit that must be fulfilled.
limit: u32,
/// What was requested.
requested: u32,
},
}
impl error::Error for PipelineLayoutLimitsError {
#[inline]
fn description(&self) -> &str {
match *self {
PipelineLayoutLimitsError::MaxDescriptorSetsLimitExceeded { .. } => {
"the maximum number of descriptor sets has been exceeded"
},
PipelineLayoutLimitsError::MaxPushConstantsSizeExceeded { .. } => {
"the maximum size of push constants has been exceeded"
},
PipelineLayoutLimitsError::MaxPerStageResourcesLimitExceeded { .. } => {
"the `max_per_stage_resources()` limit has been exceeded"
},
PipelineLayoutLimitsError::MaxPerStageDescriptorSamplersLimitExceeded { .. } => {
"the `max_per_stage_descriptor_samplers()` limit has been exceeded"
},
PipelineLayoutLimitsError::MaxPerStageDescriptorUniformBuffersLimitExceeded { .. } => {
"the `max_per_stage_descriptor_uniform_buffers()` limit has been exceeded"
},
PipelineLayoutLimitsError::MaxPerStageDescriptorStorageBuffersLimitExceeded { .. } => {
"the `max_per_stage_descriptor_storage_buffers()` limit has been exceeded"
},
PipelineLayoutLimitsError::MaxPerStageDescriptorSampledImagesLimitExceeded { .. } => {
"the `max_per_stage_descriptor_sampled_images()` limit has been exceeded"
},
PipelineLayoutLimitsError::MaxPerStageDescriptorStorageImagesLimitExceeded { .. } => {
"the `max_per_stage_descriptor_storage_images()` limit has been exceeded"
},
PipelineLayoutLimitsError::MaxPerStageDescriptorInputAttachmentsLimitExceeded { .. } => {
"the `max_per_stage_descriptor_input_attachments()` limit has been exceeded"
},
PipelineLayoutLimitsError::MaxDescriptorSetSamplersLimitExceeded { .. } => {
"the `max_descriptor_set_samplers()` limit has been exceeded"
},
PipelineLayoutLimitsError::MaxDescriptorSetUniformBuffersLimitExceeded { .. } => {
"the `max_descriptor_set_uniform_buffers()` limit has been exceeded"
},
PipelineLayoutLimitsError::MaxDescriptorSetUniformBuffersDynamicLimitExceeded { .. } => {
"the `max_descriptor_set_uniform_buffers_dynamic()` limit has been exceeded"
},
PipelineLayoutLimitsError::MaxDescriptorSetStorageBuffersLimitExceeded { .. } => {
"the `max_descriptor_set_storage_buffers()` limit has been exceeded"
},
PipelineLayoutLimitsError::MaxDescriptorSetStorageBuffersDynamicLimitExceeded { .. } => {
"the `max_descriptor_set_storage_buffers_dynamic()` limit has been exceeded"
},
PipelineLayoutLimitsError::MaxDescriptorSetSampledImagesLimitExceeded { .. } => {
"the `max_descriptor_set_sampled_images()` limit has been exceeded"
},
PipelineLayoutLimitsError::MaxDescriptorSetStorageImagesLimitExceeded { .. } => {
"the `max_descriptor_set_storage_images()` limit has been exceeded"
},
PipelineLayoutLimitsError::MaxDescriptorSetInputAttachmentsLimitExceeded { .. } => {
"the `max_descriptor_set_input_attachments()` limit has been exceeded"
},
}
}
}
impl fmt::Display for PipelineLayoutLimitsError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{}", error::Error::description(self))
}
}
// Helper struct for the main function.
#[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 {
let mut max = 0;
if self.compute > max { max = self.compute; }
if self.vertex > max { max = self.vertex; }
if self.geometry > max { max = self.geometry; }
if self.tess_ctl > max { max = self.tess_ctl; }
if self.tess_eval > max { max = self.tess_eval; }
if self.frag > max { max = self.frag; }
max
}
}

View File

@ -50,6 +50,7 @@
//! TODO: write this section
pub use self::empty::EmptyPipelineDesc;
pub use self::limits_check::PipelineLayoutLimitsError;
pub use self::sys::PipelineLayout;
pub use self::sys::PipelineLayoutCreationError;
pub use self::sys::PipelineLayoutSys;
@ -63,6 +64,7 @@ pub use self::traits::PipelineLayoutSuperset;
pub use self::union::PipelineLayoutDescUnion;
mod empty;
mod limits_check;
mod sys;
mod traits;
mod union;

View File

@ -26,6 +26,7 @@ use descriptor::descriptor_set::UnsafeDescriptorSetLayout;
use descriptor::pipeline_layout::PipelineLayoutAbstract;
use descriptor::pipeline_layout::PipelineLayoutDesc;
use descriptor::pipeline_layout::PipelineLayoutDescPcRange;
use descriptor::pipeline_layout::PipelineLayoutLimitsError;
use device::Device;
use device::DeviceOwned;
@ -51,7 +52,8 @@ impl<L> PipelineLayout<L>
pub fn new(device: Arc<Device>, desc: L)
-> Result<PipelineLayout<L>, PipelineLayoutCreationError> {
let vk = device.pointers();
let limits = device.physical_device().limits();
desc.check_against_limits(&device)?;
// Building the list of `UnsafeDescriptorSetLayout` objects.
let layouts = {
@ -81,12 +83,6 @@ impl<L> PipelineLayout<L>
.map(|l| l.internal_object())
.collect::<SmallVec<[_; 16]>>();
// FIXME: must also check per-descriptor-type limits (eg. max uniform buffer descriptors)
if layouts_ids.len() > limits.max_bound_descriptor_sets() as usize {
return Err(PipelineLayoutCreationError::MaxDescriptorSetsLimitExceeded);
}
// Builds a list of `vkPushConstantRange` that describe the push constants.
let push_constants = {
let mut out: SmallVec<[_; 8]> = SmallVec::new();
@ -107,10 +103,6 @@ impl<L> PipelineLayout<L>
return Err(PipelineLayoutCreationError::InvalidPushConstant);
}
if offset + size > limits.max_push_constants_size() as usize {
return Err(PipelineLayoutCreationError::MaxPushConstantsSizeExceeded);
}
out.push(vk::PushConstantRange {
stageFlags: stages.into_vulkan_bits(),
offset: offset as u32,
@ -267,15 +259,13 @@ unsafe impl<'a> VulkanObject for PipelineLayoutSys<'a> {
}
}
/// Error that can happen when creating an instance.
/// Error that can happen when creating a pipeline layout.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PipelineLayoutCreationError {
/// Not enough memory.
OomError(OomError),
/// The maximum number of descriptor sets has been exceeded.
MaxDescriptorSetsLimitExceeded,
/// The maximum size of push constants has been exceeded.
MaxPushConstantsSizeExceeded,
/// The pipeline layout description doesn't fulfill the limit requirements.
LimitsError(PipelineLayoutLimitsError),
/// One of the push constants range didn't obey the rules. The list of stages must not be
/// empty, the size must not be 0, and the size must be a multiple or 4.
InvalidPushConstant,
@ -288,11 +278,8 @@ impl error::Error for PipelineLayoutCreationError {
PipelineLayoutCreationError::OomError(_) => {
"not enough memory available"
},
PipelineLayoutCreationError::MaxDescriptorSetsLimitExceeded => {
"the maximum number of descriptor sets has been exceeded"
},
PipelineLayoutCreationError::MaxPushConstantsSizeExceeded => {
"the maximum size of push constants has been exceeded"
PipelineLayoutCreationError::LimitsError(_) => {
"the pipeline layout description doesn't fulfill the limit requirements"
},
PipelineLayoutCreationError::InvalidPushConstant => {
"one of the push constants range didn't obey the rules"
@ -304,6 +291,7 @@ impl error::Error for PipelineLayoutCreationError {
fn cause(&self) -> Option<&error::Error> {
match *self {
PipelineLayoutCreationError::OomError(ref err) => Some(err),
PipelineLayoutCreationError::LimitsError(ref err) => Some(err),
_ => None,
}
}
@ -323,6 +311,13 @@ impl From<OomError> for PipelineLayoutCreationError {
}
}
impl From<PipelineLayoutLimitsError> for PipelineLayoutCreationError {
#[inline]
fn from(err: PipelineLayoutLimitsError) -> PipelineLayoutCreationError {
PipelineLayoutCreationError::LimitsError(err)
}
}
impl From<Error> for PipelineLayoutCreationError {
#[inline]
fn from(err: Error) -> PipelineLayoutCreationError {

View File

@ -17,6 +17,7 @@ use descriptor::descriptor::DescriptorDesc;
use descriptor::descriptor::ShaderStages;
use descriptor::descriptor_set::DescriptorSetsCollection;
use descriptor::descriptor_set::UnsafeDescriptorSetLayout;
use descriptor::pipeline_layout::limits_check;
use descriptor::pipeline_layout::PipelineLayout;
use descriptor::pipeline_layout::PipelineLayoutCreationError;
use descriptor::pipeline_layout::PipelineLayoutDescUnion;
@ -99,6 +100,14 @@ pub unsafe trait PipelineLayoutDesc {
PipelineLayoutDescUnion::new(self, other)
}
/// Checks whether this description fulfills the device limits requirements.
#[inline]
fn check_against_limits(&self, device: &Device)
-> Result<(), limits_check::PipelineLayoutLimitsError>
{
limits_check::check_desc_against_limits(device, self)
}
/// Turns the layout description into a `PipelineLayout` object that can be used by Vulkan.
///
/// > **Note**: This is just a shortcut for `PipelineLayout::new`.