use crate::check_errors;
use crate::descriptor_set::layout::DescriptorRequirementsNotMet;
use crate::descriptor_set::layout::DescriptorSetLayout;
use crate::descriptor_set::layout::DescriptorSetLayoutError;
use crate::descriptor_set::layout::DescriptorType;
use crate::device::Device;
use crate::device::DeviceOwned;
use crate::device::Properties;
use crate::shader::DescriptorRequirements;
use crate::shader::ShaderStages;
use crate::Error;
use crate::OomError;
use crate::VulkanObject;
use smallvec::SmallVec;
use std::error;
use std::fmt;
use std::mem::MaybeUninit;
use std::ptr;
use std::sync::Arc;
pub struct PipelineLayout {
handle: ash::vk::PipelineLayout,
device: Arc<Device>,
descriptor_set_layouts: SmallVec<[Arc<DescriptorSetLayout>; 4]>,
push_constant_ranges: SmallVec<[PipelineLayoutPcRange; 4]>,
}
impl PipelineLayout {
#[inline]
pub fn new<D, P>(
device: Arc<Device>,
descriptor_set_layouts: D,
push_constant_ranges: P,
) -> Result<Arc<PipelineLayout>, PipelineLayoutCreationError>
where
D: IntoIterator<Item = Arc<DescriptorSetLayout>>,
P: IntoIterator<Item = PipelineLayoutPcRange>,
{
let fns = device.fns();
let descriptor_set_layouts: SmallVec<[Arc<DescriptorSetLayout>; 4]> =
descriptor_set_layouts.into_iter().collect();
if descriptor_set_layouts
.iter()
.filter(|layout| layout.desc().is_push_descriptor())
.count()
> 1
{
return Err(PipelineLayoutCreationError::MultiplePushDescriptor);
}
let mut push_constant_ranges: SmallVec<[PipelineLayoutPcRange; 4]> =
push_constant_ranges.into_iter().collect();
for (a_id, a) in push_constant_ranges.iter().enumerate() {
for b in push_constant_ranges.iter().skip(a_id + 1) {
if a.stages.intersects(&b.stages) {
return Err(PipelineLayoutCreationError::PushConstantsConflict {
first_range: *a,
second_range: *b,
});
}
}
}
push_constant_ranges.sort_unstable_by_key(|range| {
(
range.offset,
range.size,
ash::vk::ShaderStageFlags::from(range.stages),
)
});
check_desc_against_limits(
device.physical_device().properties(),
&descriptor_set_layouts,
&push_constant_ranges,
)?;
let layouts_ids = descriptor_set_layouts
.iter()
.map(|l| l.internal_object())
.collect::<SmallVec<[_; 4]>>();
let push_constants = {
let mut out: SmallVec<[_; 4]> = SmallVec::new();
for &PipelineLayoutPcRange {
offset,
size,
stages,
} in &push_constant_ranges
{
if stages == ShaderStages::none() || size == 0 || (size % 4) != 0 {
return Err(PipelineLayoutCreationError::InvalidPushConstant);
}
out.push(ash::vk::PushConstantRange {
stage_flags: stages.into(),
offset,
size,
});
}
out
};
debug_assert!({
let mut stages = ash::vk::ShaderStageFlags::empty();
let mut outcome = true;
for pc in push_constants.iter() {
if !(stages & pc.stage_flags).is_empty() {
outcome = false;
break;
}
stages &= pc.stage_flags;
}
outcome
});
let handle = unsafe {
let infos = ash::vk::PipelineLayoutCreateInfo {
flags: ash::vk::PipelineLayoutCreateFlags::empty(),
set_layout_count: layouts_ids.len() as u32,
p_set_layouts: layouts_ids.as_ptr(),
push_constant_range_count: push_constants.len() as u32,
p_push_constant_ranges: push_constants.as_ptr(),
..Default::default()
};
let mut output = MaybeUninit::uninit();
check_errors(fns.v1_0.create_pipeline_layout(
device.internal_object(),
&infos,
ptr::null(),
output.as_mut_ptr(),
))?;
output.assume_init()
};
Ok(Arc::new(PipelineLayout {
handle,
device: device.clone(),
descriptor_set_layouts,
push_constant_ranges,
}))
}
#[inline]
pub fn descriptor_set_layouts(&self) -> &[Arc<DescriptorSetLayout>] {
&self.descriptor_set_layouts
}
#[inline]
pub fn push_constant_ranges(&self) -> &[PipelineLayoutPcRange] {
&self.push_constant_ranges
}
pub fn is_compatible_with(&self, other: &PipelineLayout, num_sets: u32) -> bool {
let num_sets = num_sets as usize;
assert!(num_sets >= self.descriptor_set_layouts.len());
if self.handle == other.handle {
return true;
}
if self.push_constant_ranges != other.push_constant_ranges {
return false;
}
let other_sets = match other.descriptor_set_layouts.get(0..num_sets) {
Some(x) => x,
None => return false,
};
self.descriptor_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<&PipelineLayoutPcRange>,
) -> Result<(), PipelineLayoutSupersetError> {
for ((set_num, binding_num), reqs) in descriptor_requirements.into_iter() {
let descriptor_desc = self
.descriptor_set_layouts
.get(set_num as usize)
.and_then(|set_desc| set_desc.descriptor(binding_num));
let descriptor_desc = match descriptor_desc {
Some(x) => x,
None => {
return Err(PipelineLayoutSupersetError::DescriptorMissing {
set_num,
binding_num,
})
}
};
if let Err(error) = descriptor_desc.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.as_ref().into_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(())
}
}
unsafe impl DeviceOwned for PipelineLayout {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
unsafe impl VulkanObject for PipelineLayout {
type Object = ash::vk::PipelineLayout;
fn internal_object(&self) -> Self::Object {
self.handle
}
}
impl fmt::Debug for PipelineLayout {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fmt.debug_struct("PipelineLayout")
.field("raw", &self.handle)
.field("device", &self.device)
.field("descriptor_set_layouts", &self.descriptor_set_layouts)
.field("push_constant_ranges", &self.push_constant_ranges)
.finish()
}
}
impl Drop for PipelineLayout {
#[inline]
fn drop(&mut self) {
unsafe {
let fns = self.device.fns();
fns.v1_0.destroy_pipeline_layout(
self.device.internal_object(),
self.handle,
ptr::null(),
);
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PipelineLayoutCreationError {
OomError(OomError),
LimitsError(PipelineLayoutLimitsError),
InvalidPushConstant,
MultiplePushDescriptor,
PushConstantsConflict {
first_range: PipelineLayoutPcRange,
second_range: PipelineLayoutPcRange,
},
SetLayoutError(DescriptorSetLayoutError),
}
impl error::Error for PipelineLayoutCreationError {
#[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
Self::OomError(ref err) => Some(err),
Self::LimitsError(ref err) => Some(err),
Self::SetLayoutError(ref err) => Some(err),
_ => None,
}
}
}
impl fmt::Display for PipelineLayoutCreationError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
Self::OomError(_) => write!(fmt, "not enough memory available"),
Self::LimitsError(_) => {
write!(
fmt,
"the pipeline layout description doesn't fulfill the limit requirements"
)
}
Self::InvalidPushConstant => {
write!(fmt, "one of the push constants range didn't obey the rules")
}
Self::MultiplePushDescriptor => {
write!(
fmt,
"more than one descriptor set layout was set for push descriptors"
)
}
Self::PushConstantsConflict { .. } => {
write!(fmt, "conflict between different push constants ranges")
}
Self::SetLayoutError(_) => write!(fmt, "one of the sets has an error"),
}
}
}
impl From<OomError> for PipelineLayoutCreationError {
#[inline]
fn from(err: OomError) -> PipelineLayoutCreationError {
PipelineLayoutCreationError::OomError(err)
}
}
impl From<PipelineLayoutLimitsError> for PipelineLayoutCreationError {
#[inline]
fn from(err: PipelineLayoutLimitsError) -> PipelineLayoutCreationError {
PipelineLayoutCreationError::LimitsError(err)
}
}
impl From<DescriptorSetLayoutError> for PipelineLayoutCreationError {
#[inline]
fn from(err: DescriptorSetLayoutError) -> PipelineLayoutCreationError {
PipelineLayoutCreationError::SetLayoutError(err)
}
}
impl From<Error> for PipelineLayoutCreationError {
#[inline]
fn from(err: Error) -> PipelineLayoutCreationError {
match err {
err @ Error::OutOfHostMemory => {
PipelineLayoutCreationError::OomError(OomError::from(err))
}
err @ Error::OutOfDeviceMemory => {
PipelineLayoutCreationError::OomError(OomError::from(err))
}
_ => panic!("unexpected error: {:?}", err),
}
}
}
#[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: PipelineLayoutPcRange,
second_range: PipelineLayoutPcRange,
},
}
impl error::Error for PipelineLayoutSupersetError {
#[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
PipelineLayoutSupersetError::DescriptorRequirementsNotMet { ref error, .. } => {
Some(error)
}
_ => None,
}
}
}
impl fmt::Display for PipelineLayoutSupersetError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
PipelineLayoutSupersetError::DescriptorRequirementsNotMet { set_num, binding_num, .. } => write!(
fmt,
"the descriptor at set {} binding {} does not meet the requirements",
set_num, binding_num
),
PipelineLayoutSupersetError::DescriptorMissing {
set_num,
binding_num,
} => write!(
fmt,
"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!(
fmt,
"our range did not completely encompass the other range"
)?;
writeln!(fmt, " our stages: {:?}", first_range.stages)?;
writeln!(
fmt,
" our range: {} - {}",
first_range.offset,
first_range.offset + first_range.size
)?;
writeln!(fmt, " other stages: {:?}", second_range.stages)?;
write!(
fmt,
" other range: {} - {}",
second_range.offset,
second_range.offset + second_range.size
)
}
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct PipelineLayoutPcRange {
pub offset: u32,
pub size: u32,
pub stages: ShaderStages,
}
fn check_desc_against_limits(
properties: &Properties,
descriptor_set_layouts: &[Arc<DescriptorSetLayout>],
push_constants_ranges: &[PipelineLayoutPcRange],
) -> Result<(), PipelineLayoutLimitsError> {
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 descriptor_set_layouts {
for descriptor in (0..set.num_bindings()).filter_map(|i| set.descriptor(i).map(|d| d)) {
num_resources.increment(descriptor.descriptor_count, &descriptor.stages);
match descriptor.ty {
DescriptorType::Sampler => {
num_samplers.increment(descriptor.descriptor_count, &descriptor.stages);
}
DescriptorType::CombinedImageSampler => {
num_samplers.increment(descriptor.descriptor_count, &descriptor.stages);
num_sampled_images.increment(descriptor.descriptor_count, &descriptor.stages);
}
DescriptorType::SampledImage | DescriptorType::UniformTexelBuffer => {
num_sampled_images.increment(descriptor.descriptor_count, &descriptor.stages);
}
DescriptorType::StorageImage | DescriptorType::StorageTexelBuffer => {
num_storage_images.increment(descriptor.descriptor_count, &descriptor.stages);
}
DescriptorType::UniformBuffer => {
num_uniform_buffers.increment(descriptor.descriptor_count, &descriptor.stages);
}
DescriptorType::UniformBufferDynamic => {
num_uniform_buffers.increment(descriptor.descriptor_count, &descriptor.stages);
num_uniform_buffers_dynamic += 1;
}
DescriptorType::StorageBuffer => {
num_storage_buffers.increment(descriptor.descriptor_count, &descriptor.stages);
}
DescriptorType::StorageBufferDynamic => {
num_storage_buffers.increment(descriptor.descriptor_count, &descriptor.stages);
num_storage_buffers_dynamic += 1;
}
DescriptorType::InputAttachment => {
num_input_attachments
.increment(descriptor.descriptor_count, &descriptor.stages);
}
}
}
}
if descriptor_set_layouts.len() > properties.max_bound_descriptor_sets as usize {
return Err(PipelineLayoutLimitsError::MaxDescriptorSetsLimitExceeded {
limit: properties.max_bound_descriptor_sets as usize,
requested: descriptor_set_layouts.len(),
});
}
if num_resources.max_per_stage() > properties.max_per_stage_resources {
return Err(
PipelineLayoutLimitsError::MaxPerStageResourcesLimitExceeded {
limit: properties.max_per_stage_resources,
requested: num_resources.max_per_stage(),
},
);
}
if num_samplers.max_per_stage() > properties.max_per_stage_descriptor_samplers {
return Err(
PipelineLayoutLimitsError::MaxPerStageDescriptorSamplersLimitExceeded {
limit: properties.max_per_stage_descriptor_samplers,
requested: num_samplers.max_per_stage(),
},
);
}
if num_uniform_buffers.max_per_stage() > properties.max_per_stage_descriptor_uniform_buffers {
return Err(
PipelineLayoutLimitsError::MaxPerStageDescriptorUniformBuffersLimitExceeded {
limit: properties.max_per_stage_descriptor_uniform_buffers,
requested: num_uniform_buffers.max_per_stage(),
},
);
}
if num_storage_buffers.max_per_stage() > properties.max_per_stage_descriptor_storage_buffers {
return Err(
PipelineLayoutLimitsError::MaxPerStageDescriptorStorageBuffersLimitExceeded {
limit: properties.max_per_stage_descriptor_storage_buffers,
requested: num_storage_buffers.max_per_stage(),
},
);
}
if num_sampled_images.max_per_stage() > properties.max_per_stage_descriptor_sampled_images {
return Err(
PipelineLayoutLimitsError::MaxPerStageDescriptorSampledImagesLimitExceeded {
limit: properties.max_per_stage_descriptor_sampled_images,
requested: num_sampled_images.max_per_stage(),
},
);
}
if num_storage_images.max_per_stage() > properties.max_per_stage_descriptor_storage_images {
return Err(
PipelineLayoutLimitsError::MaxPerStageDescriptorStorageImagesLimitExceeded {
limit: properties.max_per_stage_descriptor_storage_images,
requested: num_storage_images.max_per_stage(),
},
);
}
if num_input_attachments.max_per_stage() > properties.max_per_stage_descriptor_input_attachments
{
return Err(
PipelineLayoutLimitsError::MaxPerStageDescriptorInputAttachmentsLimitExceeded {
limit: properties.max_per_stage_descriptor_input_attachments,
requested: num_input_attachments.max_per_stage(),
},
);
}
if num_samplers.total > properties.max_descriptor_set_samplers {
return Err(
PipelineLayoutLimitsError::MaxDescriptorSetSamplersLimitExceeded {
limit: properties.max_descriptor_set_samplers,
requested: num_samplers.total,
},
);
}
if num_uniform_buffers.total > properties.max_descriptor_set_uniform_buffers {
return Err(
PipelineLayoutLimitsError::MaxDescriptorSetUniformBuffersLimitExceeded {
limit: properties.max_descriptor_set_uniform_buffers,
requested: num_uniform_buffers.total,
},
);
}
if num_uniform_buffers_dynamic > properties.max_descriptor_set_uniform_buffers_dynamic {
return Err(
PipelineLayoutLimitsError::MaxDescriptorSetUniformBuffersDynamicLimitExceeded {
limit: properties.max_descriptor_set_uniform_buffers_dynamic,
requested: num_uniform_buffers_dynamic,
},
);
}
if num_storage_buffers.total > properties.max_descriptor_set_storage_buffers {
return Err(
PipelineLayoutLimitsError::MaxDescriptorSetStorageBuffersLimitExceeded {
limit: properties.max_descriptor_set_storage_buffers,
requested: num_storage_buffers.total,
},
);
}
if num_storage_buffers_dynamic > properties.max_descriptor_set_storage_buffers_dynamic {
return Err(
PipelineLayoutLimitsError::MaxDescriptorSetStorageBuffersDynamicLimitExceeded {
limit: properties.max_descriptor_set_storage_buffers_dynamic,
requested: num_storage_buffers_dynamic,
},
);
}
if num_sampled_images.total > properties.max_descriptor_set_sampled_images {
return Err(
PipelineLayoutLimitsError::MaxDescriptorSetSampledImagesLimitExceeded {
limit: properties.max_descriptor_set_sampled_images,
requested: num_sampled_images.total,
},
);
}
if num_storage_images.total > properties.max_descriptor_set_storage_images {
return Err(
PipelineLayoutLimitsError::MaxDescriptorSetStorageImagesLimitExceeded {
limit: properties.max_descriptor_set_storage_images,
requested: num_storage_images.total,
},
);
}
if num_input_attachments.total > properties.max_descriptor_set_input_attachments {
return Err(
PipelineLayoutLimitsError::MaxDescriptorSetInputAttachmentsLimitExceeded {
limit: properties.max_descriptor_set_input_attachments,
requested: num_input_attachments.total,
},
);
}
for &PipelineLayoutPcRange { offset, size, .. } in push_constants_ranges {
if offset + size > properties.max_push_constants_size {
return Err(PipelineLayoutLimitsError::MaxPushConstantsSizeExceeded {
limit: properties.max_push_constants_size,
requested: offset + size,
});
}
}
Ok(())
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PipelineLayoutLimitsError {
MaxDescriptorSetsLimitExceeded {
limit: usize,
requested: usize,
},
MaxPushConstantsSizeExceeded {
limit: u32,
requested: u32,
},
MaxPerStageResourcesLimitExceeded {
limit: u32,
requested: u32,
},
MaxPerStageDescriptorSamplersLimitExceeded {
limit: u32,
requested: u32,
},
MaxPerStageDescriptorUniformBuffersLimitExceeded {
limit: u32,
requested: u32,
},
MaxPerStageDescriptorStorageBuffersLimitExceeded {
limit: u32,
requested: u32,
},
MaxPerStageDescriptorSampledImagesLimitExceeded {
limit: u32,
requested: u32,
},
MaxPerStageDescriptorStorageImagesLimitExceeded {
limit: u32,
requested: u32,
},
MaxPerStageDescriptorInputAttachmentsLimitExceeded {
limit: u32,
requested: u32,
},
MaxDescriptorSetSamplersLimitExceeded {
limit: u32,
requested: u32,
},
MaxDescriptorSetUniformBuffersLimitExceeded {
limit: u32,
requested: u32,
},
MaxDescriptorSetUniformBuffersDynamicLimitExceeded {
limit: u32,
requested: u32,
},
MaxDescriptorSetStorageBuffersLimitExceeded {
limit: u32,
requested: u32,
},
MaxDescriptorSetStorageBuffersDynamicLimitExceeded {
limit: u32,
requested: u32,
},
MaxDescriptorSetSampledImagesLimitExceeded {
limit: u32,
requested: u32,
},
MaxDescriptorSetStorageImagesLimitExceeded {
limit: u32,
requested: u32,
},
MaxDescriptorSetInputAttachmentsLimitExceeded {
limit: u32,
requested: u32,
},
}
impl error::Error for PipelineLayoutLimitsError {}
impl fmt::Display for PipelineLayoutLimitsError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"{}",
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"
}
}
)
}
}
#[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
}
}