vulkano/pipeline/
layout.rs

1//! The layout of descriptor sets and push constants used by a pipeline.
2//!
3//! # Overview
4//!
5//! The layout itself only *describes* the descriptors and push constants, and does not contain
6//! their content itself. Instead, you can think of it as a `struct` definition that states which
7//! members there are, what types they have, and in what order.
8//! One could imagine a Rust definition somewhat like this:
9//!
10//! ```text
11//! #[repr(C)]
12//! struct MyPipelineLayout {
13//!     push_constants: Pc,
14//!     descriptor_set0: Ds0,
15//!     descriptor_set1: Ds1,
16//!     descriptor_set2: Ds2,
17//!     descriptor_set3: Ds3,
18//! }
19//! ```
20//!
21//! Of course, a pipeline layout is created at runtime, unlike a Rust type.
22//!
23//! # Layout compatibility
24//!
25//! When binding descriptor sets or setting push constants, you must provide a pipeline layout.
26//! This layout is used to decide where in memory Vulkan should write the new data. The
27//! descriptor sets and push constants can later be read by dispatch or draw calls, but only if
28//! the bound pipeline being used for the command has a layout that is *compatible* with the layout
29//! that was used to bind the resources.
30//!
31//! *Compatible* means that the pipeline layout must be the same object, or a different layout in
32//! which the push constant ranges and descriptor set layouts were be identically defined.
33//! However, Vulkan allows for partial compatibility as well. In the `struct` analogy used above,
34//! one could imagine that using a different definition would leave some members with the same
35//! offset and size within the struct as in the old definition, while others are no longer
36//! positioned correctly. For example, if a new, incompatible type were used for `Ds1`, then the
37//! `descriptor_set1`, `descriptor_set2` and `descriptor_set3` members would no longer be correct,
38//! but `descriptor_set0` and `push_constants` would remain accessible in the new layout.
39//! Because of this behaviour, the following rules apply to compatibility between the layouts used
40//! in subsequent descriptor set binding calls:
41//!
42//! - An incompatible definition of `Pc` invalidates all bound descriptor sets.
43//! - An incompatible definition of `DsN` invalidates all bound descriptor sets *N* and higher.
44//! - If *N* is the highest set being assigned in a bind command, and it and all lower sets have
45//!   compatible definitions, including the push constants, then descriptor sets above *N* remain
46//!   valid.
47//!
48//! [`AutoCommandBufferBuilder`] keeps track of this state and will automatically remove descriptor
49//! sets that have been invalidated by incompatible layouts in subsequent binding commands.
50//!
51//! # Creating pipeline layouts
52//!
53//! A pipeline layout is a Vulkan object type, represented in Vulkano with the `PipelineLayout`
54//! type. Each pipeline that you create holds a pipeline layout object.
55//!
56//! [`AutoCommandBufferBuilder`]: crate::command_buffer::AutoCommandBufferBuilder
57
58use super::PipelineShaderStageCreateInfo;
59use crate::{
60    descriptor_set::layout::{
61        DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateFlags,
62        DescriptorSetLayoutCreateInfo, DescriptorType,
63    },
64    device::{Device, DeviceOwned, DeviceOwnedDebugWrapper, DeviceProperties},
65    instance::InstanceOwnedDebugWrapper,
66    macros::{impl_id_counter, vulkan_bitflags},
67    shader::{DescriptorBindingRequirements, ShaderStage, ShaderStages},
68    Validated, ValidationError, VulkanError, VulkanObject,
69};
70use foldhash::HashMap;
71use smallvec::SmallVec;
72use std::{
73    array,
74    cmp::max,
75    collections::hash_map::Entry,
76    error::Error,
77    fmt::{Display, Formatter, Write},
78    mem::MaybeUninit,
79    num::NonZeroU64,
80    ptr,
81    sync::Arc,
82};
83
84/// Describes the layout of descriptor sets and push constants that are made available to shaders.
85#[derive(Debug)]
86pub struct PipelineLayout {
87    handle: ash::vk::PipelineLayout,
88    device: InstanceOwnedDebugWrapper<Arc<Device>>,
89    id: NonZeroU64,
90
91    flags: PipelineLayoutCreateFlags,
92    set_layouts: Vec<DeviceOwnedDebugWrapper<Arc<DescriptorSetLayout>>>,
93    push_constant_ranges: Vec<PushConstantRange>,
94
95    push_constant_ranges_disjoint: Vec<PushConstantRange>,
96}
97
98impl PipelineLayout {
99    /// Creates a new `PipelineLayout`.
100    pub fn new(
101        device: Arc<Device>,
102        create_info: PipelineLayoutCreateInfo,
103    ) -> Result<Arc<PipelineLayout>, Validated<VulkanError>> {
104        Self::validate_new(&device, &create_info)?;
105
106        Ok(unsafe { Self::new_unchecked(device, create_info) }?)
107    }
108
109    fn validate_new(
110        device: &Device,
111        create_info: &PipelineLayoutCreateInfo,
112    ) -> Result<(), Box<ValidationError>> {
113        // VUID-vkCreatePipelineLayout-pCreateInfo-parameter
114        create_info
115            .validate(device)
116            .map_err(|err| err.add_context("create_info"))?;
117
118        Ok(())
119    }
120
121    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
122    pub unsafe fn new_unchecked(
123        device: Arc<Device>,
124        create_info: PipelineLayoutCreateInfo,
125    ) -> Result<Arc<PipelineLayout>, VulkanError> {
126        let create_info_fields1_vk = create_info.to_vk_fields1();
127        let create_info_vk = create_info.to_vk(&create_info_fields1_vk);
128
129        let handle = {
130            let fns = device.fns();
131            let mut output = MaybeUninit::uninit();
132            unsafe {
133                (fns.v1_0.create_pipeline_layout)(
134                    device.handle(),
135                    &create_info_vk,
136                    ptr::null(),
137                    output.as_mut_ptr(),
138                )
139            }
140            .result()
141            .map_err(VulkanError::from)?;
142            unsafe { output.assume_init() }
143        };
144
145        Ok(unsafe { Self::from_handle(device, handle, create_info) })
146    }
147
148    /// Creates a new `PipelineLayout` from a raw object handle.
149    ///
150    /// # Safety
151    ///
152    /// - `handle` must be a valid Vulkan object handle created from `device`.
153    /// - `create_info` must match the info used to create the object.
154    #[inline]
155    pub unsafe fn from_handle(
156        device: Arc<Device>,
157        handle: ash::vk::PipelineLayout,
158        create_info: PipelineLayoutCreateInfo,
159    ) -> Arc<PipelineLayout> {
160        let PipelineLayoutCreateInfo {
161            flags,
162            set_layouts,
163            mut push_constant_ranges,
164            _ne: _,
165        } = create_info;
166
167        // Sort the ranges for the purpose of comparing for equality.
168        // The stage mask is guaranteed to be unique, so it's a suitable sorting key.
169        push_constant_ranges.sort_unstable_by_key(|range| {
170            (
171                range.offset,
172                range.size,
173                ash::vk::ShaderStageFlags::from(range.stages),
174            )
175        });
176
177        let mut push_constant_ranges_disjoint: Vec<PushConstantRange> =
178            Vec::with_capacity(push_constant_ranges.len());
179
180        if !push_constant_ranges.is_empty() {
181            let mut min_offset = push_constant_ranges[0].offset;
182            loop {
183                let mut max_offset = u32::MAX;
184                let mut stages = ShaderStages::empty();
185
186                for range in push_constant_ranges.iter() {
187                    // new start (begin next time from it)
188                    if range.offset > min_offset {
189                        max_offset = max_offset.min(range.offset);
190                        break;
191                    } else if range.offset + range.size > min_offset {
192                        // inside the range, include the stage
193                        // use the minimum of the end of all ranges that are overlapping
194                        max_offset = max_offset.min(range.offset + range.size);
195                        stages |= range.stages;
196                    }
197                }
198                // finished all stages
199                if stages.is_empty() {
200                    break;
201                }
202
203                push_constant_ranges_disjoint.push(PushConstantRange {
204                    stages,
205                    offset: min_offset,
206                    size: max_offset - min_offset,
207                });
208                // prepare for next range
209                min_offset = max_offset;
210            }
211        }
212
213        Arc::new(PipelineLayout {
214            handle,
215            device: InstanceOwnedDebugWrapper(device),
216            id: Self::next_id(),
217            flags,
218            set_layouts: set_layouts
219                .into_iter()
220                .map(DeviceOwnedDebugWrapper)
221                .collect(),
222            push_constant_ranges,
223            push_constant_ranges_disjoint,
224        })
225    }
226
227    /// Returns the flags that the pipeline layout was created with.
228    #[inline]
229    pub fn flags(&self) -> PipelineLayoutCreateFlags {
230        self.flags
231    }
232
233    /// Returns the descriptor set layouts this pipeline layout was created from.
234    #[inline]
235    pub fn set_layouts(&self) -> &[Arc<DescriptorSetLayout>] {
236        DeviceOwnedDebugWrapper::cast_slice_inner(&self.set_layouts)
237    }
238
239    /// Returns a slice containing the push constant ranges this pipeline layout was created from.
240    ///
241    /// The ranges are guaranteed to be sorted deterministically by offset, size, then stages.
242    /// This means that two slices containing the same elements will always have the same order.
243    #[inline]
244    pub fn push_constant_ranges(&self) -> &[PushConstantRange] {
245        &self.push_constant_ranges
246    }
247
248    /// Returns a slice containing the push constant ranges in with all disjoint stages.
249    ///
250    /// For example, if we have these `push_constant_ranges`:
251    /// - `offset=0, size=4, stages=vertex`
252    /// - `offset=0, size=12, stages=fragment`
253    ///
254    /// The returned value will be:
255    /// - `offset=0, size=4, stages=vertex|fragment`
256    /// - `offset=4, size=8, stages=fragment`
257    ///
258    /// The ranges are guaranteed to be sorted deterministically by offset, and
259    /// guaranteed to be disjoint, meaning that there is no overlap between the ranges.
260    #[doc(hidden)]
261    #[inline]
262    pub fn push_constant_ranges_disjoint(&self) -> &[PushConstantRange] {
263        &self.push_constant_ranges_disjoint
264    }
265
266    /// Returns whether `self` is compatible with `other` for the given number of sets.
267    #[inline]
268    pub fn is_compatible_with(&self, other: &PipelineLayout, num_sets: u32) -> bool {
269        let num_sets = num_sets as usize;
270        assert!(num_sets <= self.set_layouts.len());
271
272        if self == other {
273            return true;
274        }
275
276        if self.push_constant_ranges != other.push_constant_ranges {
277            return false;
278        }
279
280        let other_sets = match other.set_layouts.get(0..num_sets) {
281            Some(x) => x,
282            None => return false,
283        };
284
285        self.set_layouts
286            .iter()
287            .zip(other_sets)
288            .all(|(self_set_layout, other_set_layout)| {
289                self_set_layout.is_compatible_with(other_set_layout)
290            })
291    }
292
293    /// Makes sure that `self` is a superset of the provided descriptor set layouts and push
294    /// constant ranges. Returns an `Err` if this is not the case.
295    pub(crate) fn ensure_compatible_with_shader<'a>(
296        &self,
297        descriptor_requirements: impl IntoIterator<
298            Item = ((u32, u32), &'a DescriptorBindingRequirements),
299        >,
300        push_constant_range: Option<&PushConstantRange>,
301    ) -> Result<(), Box<ValidationError>> {
302        for ((set_num, binding_num), reqs) in descriptor_requirements.into_iter() {
303            let layout_binding = self
304                .set_layouts
305                .get(set_num as usize)
306                .and_then(|set_layout| set_layout.bindings().get(&binding_num));
307
308            let layout_binding = match layout_binding {
309                Some(x) => x,
310                None => {
311                    return Err(Box::new(ValidationError {
312                        problem: format!(
313                            "the requirements for descriptor set {} binding {} were not met: \
314                            no such binding exists in the pipeline layout",
315                            set_num, binding_num,
316                        )
317                        .into(),
318                        ..Default::default()
319                    }));
320                }
321            };
322
323            if let Err(error) = layout_binding.ensure_compatible_with_shader(reqs) {
324                return Err(Box::new(ValidationError {
325                    problem: format!(
326                        "the requirements for descriptor set {} binding {} were not met: {}",
327                        set_num, binding_num, error,
328                    )
329                    .into(),
330                    ..Default::default()
331                }));
332            }
333        }
334
335        if let Some(range) = push_constant_range {
336            for own_range in self.push_constant_ranges.iter() {
337                if range.stages.intersects(own_range.stages) &&       // check if it shares any stages
338                    (range.offset < own_range.offset || // our range must start before and end after the given range
339                        own_range.offset + own_range.size < range.offset + range.size)
340                {
341                    return Err(Box::new(ValidationError {
342                        problem: "the required push constant range is larger than the \
343                            push constant range in the pipeline layout"
344                            .into(),
345                        ..Default::default()
346                    }));
347                }
348            }
349        }
350
351        Ok(())
352    }
353}
354
355impl Drop for PipelineLayout {
356    #[inline]
357    fn drop(&mut self) {
358        let fns = self.device.fns();
359        unsafe {
360            (fns.v1_0.destroy_pipeline_layout)(self.device.handle(), self.handle, ptr::null())
361        };
362    }
363}
364
365unsafe impl VulkanObject for PipelineLayout {
366    type Handle = ash::vk::PipelineLayout;
367
368    #[inline]
369    fn handle(&self) -> Self::Handle {
370        self.handle
371    }
372}
373
374unsafe impl DeviceOwned for PipelineLayout {
375    #[inline]
376    fn device(&self) -> &Arc<Device> {
377        &self.device
378    }
379}
380
381impl_id_counter!(PipelineLayout);
382
383/// Parameters to create a new `PipelineLayout`.
384#[derive(Clone, Debug)]
385pub struct PipelineLayoutCreateInfo {
386    /// Additional properties of the pipeline layout.
387    ///
388    /// The default value is empty.
389    pub flags: PipelineLayoutCreateFlags,
390
391    /// The descriptor set layouts that should be part of the pipeline layout.
392    ///
393    /// They are provided in order of set number.
394    ///
395    /// The default value is empty.
396    pub set_layouts: Vec<Arc<DescriptorSetLayout>>,
397
398    /// The ranges of push constants that the pipeline will access.
399    ///
400    /// A shader stage can only appear in one element of the list, but it is possible to combine
401    /// ranges for multiple shader stages if they are the same.
402    ///
403    /// The default value is empty.
404    pub push_constant_ranges: Vec<PushConstantRange>,
405
406    pub _ne: crate::NonExhaustive,
407}
408
409impl Default for PipelineLayoutCreateInfo {
410    #[inline]
411    fn default() -> Self {
412        Self {
413            flags: PipelineLayoutCreateFlags::empty(),
414            set_layouts: Vec::new(),
415            push_constant_ranges: Vec::new(),
416            _ne: crate::NonExhaustive(()),
417        }
418    }
419}
420
421impl PipelineLayoutCreateInfo {
422    pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
423        let properties = device.physical_device().properties();
424
425        let &Self {
426            flags,
427            ref set_layouts,
428            ref push_constant_ranges,
429            _ne: _,
430        } = self;
431
432        flags.validate_device(device).map_err(|err| {
433            err.add_context("flags")
434                .set_vuids(&["VUID-VkPipelineLayoutCreateInfo-flags-parameter"])
435        })?;
436
437        if set_layouts.len() > properties.max_bound_descriptor_sets as usize {
438            return Err(Box::new(ValidationError {
439                context: "set_layouts".into(),
440                problem: "the length exceeds the `max_bound_descriptor_sets` limit".into(),
441                vuids: &["VUID-VkPipelineLayoutCreateInfo-setLayoutCount-00286"],
442                ..Default::default()
443            }));
444        }
445
446        struct DescriptorLimit {
447            descriptor_types: &'static [DescriptorType],
448            get_limit_all: fn(&DeviceProperties) -> Option<u32>,
449            limit_name_all: &'static str,
450            vuids_all: &'static [&'static str],
451            get_limit_not_uab: fn(&DeviceProperties) -> u32,
452            limit_name_not_uab: &'static str,
453            vuids_not_uab: &'static [&'static str],
454        }
455
456        const PER_STAGE_DESCRIPTOR_LIMITS: [DescriptorLimit; 8] = [
457            DescriptorLimit {
458                descriptor_types: &[
459                    DescriptorType::Sampler,
460                    DescriptorType::CombinedImageSampler,
461                ],
462                get_limit_all: |p| p.max_per_stage_descriptor_update_after_bind_samplers,
463                limit_name_all: "max_per_stage_descriptor_update_after_bind_samplers",
464                vuids_all: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03022"],
465                get_limit_not_uab: |p| p.max_per_stage_descriptor_samplers,
466                limit_name_not_uab: "max_per_stage_descriptor_samplers",
467                vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03016"],
468            },
469            DescriptorLimit {
470                descriptor_types: &[
471                    DescriptorType::UniformBuffer,
472                    DescriptorType::UniformBufferDynamic,
473                ],
474                get_limit_all: |p| p.max_per_stage_descriptor_update_after_bind_uniform_buffers,
475                limit_name_all: "max_per_stage_descriptor_update_after_bind_uniform_buffers",
476                vuids_all: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03023"],
477                get_limit_not_uab: |p| p.max_per_stage_descriptor_uniform_buffers,
478                limit_name_not_uab: "max_per_stage_descriptor_uniform_buffers",
479                vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03017"],
480            },
481            DescriptorLimit {
482                descriptor_types: &[
483                    DescriptorType::StorageBuffer,
484                    DescriptorType::StorageBufferDynamic,
485                ],
486                get_limit_all: |p| p.max_per_stage_descriptor_update_after_bind_storage_buffers,
487                limit_name_all: "max_per_stage_descriptor_update_after_bind_storage_buffers",
488                vuids_all: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03024"],
489                get_limit_not_uab: |p| p.max_per_stage_descriptor_storage_buffers,
490                limit_name_not_uab: "max_per_stage_descriptor_storage_buffers",
491                vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03018"],
492            },
493            DescriptorLimit {
494                descriptor_types: &[
495                    DescriptorType::CombinedImageSampler,
496                    DescriptorType::SampledImage,
497                    DescriptorType::UniformTexelBuffer,
498                ],
499                get_limit_all: |p| p.max_per_stage_descriptor_update_after_bind_sampled_images,
500                limit_name_all: "max_per_stage_descriptor_update_after_bind_sampled_images",
501                vuids_all: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03025"],
502                get_limit_not_uab: |p| p.max_per_stage_descriptor_sampled_images,
503                limit_name_not_uab: "max_per_stage_descriptor_sampled_images",
504                vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-06939"],
505            },
506            DescriptorLimit {
507                descriptor_types: &[
508                    DescriptorType::StorageImage,
509                    DescriptorType::StorageTexelBuffer,
510                ],
511                get_limit_all: |p| p.max_per_stage_descriptor_update_after_bind_storage_images,
512                limit_name_all: "max_per_stage_descriptor_update_after_bind_storage_images",
513                vuids_all: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03026"],
514                get_limit_not_uab: |p| p.max_per_stage_descriptor_storage_images,
515                limit_name_not_uab: "max_per_stage_descriptor_storage_images",
516                vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03020"],
517            },
518            DescriptorLimit {
519                descriptor_types: &[DescriptorType::InputAttachment],
520                get_limit_all: |p| p.max_per_stage_descriptor_update_after_bind_input_attachments,
521                limit_name_all: "max_per_stage_descriptor_update_after_bind_input_attachments",
522                vuids_all: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03027"],
523                get_limit_not_uab: |p| p.max_per_stage_descriptor_input_attachments,
524                limit_name_not_uab: "max_per_stage_descriptor_input_attachments",
525                vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03021"],
526            },
527            DescriptorLimit {
528                descriptor_types: &[DescriptorType::InlineUniformBlock],
529                get_limit_all: |p| {
530                    p.max_per_stage_descriptor_update_after_bind_inline_uniform_blocks
531                },
532                limit_name_all: "max_per_stage_descriptor_update_after_bind_inline_uniform_blocks",
533                vuids_all: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-02215"],
534                get_limit_not_uab: |p| {
535                    p.max_per_stage_descriptor_inline_uniform_blocks
536                        .unwrap_or(0)
537                },
538                limit_name_not_uab: "max_per_stage_descriptor_inline_uniform_blocks",
539                vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-02214"],
540            },
541            DescriptorLimit {
542                descriptor_types: &[DescriptorType::AccelerationStructure],
543                get_limit_all: |p| {
544                    p.max_per_stage_descriptor_update_after_bind_acceleration_structures
545                },
546                limit_name_all:
547                    "max_per_stage_descriptor_update_after_bind_acceleration_structures",
548                vuids_all: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03572"],
549                get_limit_not_uab: |p| {
550                    p.max_per_stage_descriptor_acceleration_structures
551                        .unwrap_or(0)
552                },
553                limit_name_not_uab: "max_per_stage_descriptor_acceleration_structures",
554                vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03571"],
555            },
556        ];
557
558        const TOTAL_DESCRIPTOR_LIMITS: [DescriptorLimit; 10] = [
559            DescriptorLimit {
560                descriptor_types: &[
561                    DescriptorType::Sampler,
562                    DescriptorType::CombinedImageSampler,
563                ],
564                get_limit_all: |p| p.max_descriptor_set_update_after_bind_samplers,
565                limit_name_all: "max_descriptor_set_update_after_bind_samplers",
566                vuids_all: &["VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03036"],
567                get_limit_not_uab: |p| p.max_descriptor_set_samplers,
568                limit_name_not_uab: "max_descriptor_set_samplers",
569                vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03028"],
570            },
571            DescriptorLimit {
572                descriptor_types: &[DescriptorType::UniformBuffer],
573                get_limit_all: |p| p.max_descriptor_set_update_after_bind_uniform_buffers,
574                limit_name_all: "max_descriptor_set_update_after_bind_uniform_buffers",
575                vuids_all: &["VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03037"],
576                get_limit_not_uab: |p| p.max_descriptor_set_uniform_buffers,
577                limit_name_not_uab: "max_descriptor_set_uniform_buffers",
578                vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03029"],
579            },
580            DescriptorLimit {
581                descriptor_types: &[DescriptorType::UniformBufferDynamic],
582                get_limit_all: |p| p.max_descriptor_set_update_after_bind_uniform_buffers_dynamic,
583                limit_name_all: "max_descriptor_set_update_after_bind_uniform_buffers_dynamic",
584                vuids_all: &["VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03038"],
585                get_limit_not_uab: |p| p.max_descriptor_set_uniform_buffers_dynamic,
586                limit_name_not_uab: "max_descriptor_set_uniform_buffers_dynamic",
587                vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03030"],
588            },
589            DescriptorLimit {
590                descriptor_types: &[DescriptorType::StorageBuffer],
591                get_limit_all: |p| p.max_descriptor_set_update_after_bind_storage_buffers,
592                limit_name_all: "max_descriptor_set_update_after_bind_storage_buffers",
593                vuids_all: &["VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03039"],
594                get_limit_not_uab: |p| p.max_descriptor_set_storage_buffers,
595                limit_name_not_uab: "max_descriptor_set_storage_buffers",
596                vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03031"],
597            },
598            DescriptorLimit {
599                descriptor_types: &[DescriptorType::StorageBufferDynamic],
600                get_limit_all: |p| p.max_descriptor_set_update_after_bind_storage_buffers_dynamic,
601                limit_name_all: "max_descriptor_set_update_after_bind_storage_buffers_dynamic",
602                vuids_all: &["VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03040"],
603                get_limit_not_uab: |p| p.max_descriptor_set_storage_buffers_dynamic,
604                limit_name_not_uab: "max_descriptor_set_storage_buffers_dynamic",
605                vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03032"],
606            },
607            DescriptorLimit {
608                descriptor_types: &[
609                    DescriptorType::CombinedImageSampler,
610                    DescriptorType::SampledImage,
611                    DescriptorType::UniformTexelBuffer,
612                ],
613                get_limit_all: |p| p.max_descriptor_set_update_after_bind_sampled_images,
614                limit_name_all: "max_descriptor_set_update_after_bind_sampled_images",
615                vuids_all: &["VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03041"],
616                get_limit_not_uab: |p| p.max_descriptor_set_sampled_images,
617                limit_name_not_uab: "max_descriptor_set_sampled_images",
618                vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03033"],
619            },
620            DescriptorLimit {
621                descriptor_types: &[
622                    DescriptorType::StorageImage,
623                    DescriptorType::StorageTexelBuffer,
624                ],
625                get_limit_all: |p| p.max_descriptor_set_update_after_bind_storage_images,
626                limit_name_all: "max_descriptor_set_update_after_bind_storage_images",
627                vuids_all: &["VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03042"],
628                get_limit_not_uab: |p| p.max_descriptor_set_storage_images,
629                limit_name_not_uab: "max_descriptor_set_storage_images",
630                vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03034"],
631            },
632            DescriptorLimit {
633                descriptor_types: &[DescriptorType::InputAttachment],
634                get_limit_all: |p| p.max_descriptor_set_update_after_bind_input_attachments,
635                limit_name_all: "max_descriptor_set_update_after_bind_input_attachments",
636                vuids_all: &["VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03043"],
637                get_limit_not_uab: |p| p.max_descriptor_set_input_attachments,
638                limit_name_not_uab: "max_descriptor_set_input_attachments",
639                vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03035"],
640            },
641            DescriptorLimit {
642                descriptor_types: &[DescriptorType::InlineUniformBlock],
643                get_limit_all: |p| p.max_descriptor_set_update_after_bind_inline_uniform_blocks,
644                limit_name_all: "max_descriptor_set_update_after_bind_inline_uniform_blocks",
645                vuids_all: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-02217"],
646                get_limit_not_uab: |p| p.max_descriptor_set_inline_uniform_blocks.unwrap_or(0),
647                limit_name_not_uab: "max_descriptor_set_inline_uniform_blocks",
648                vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-02216"],
649            },
650            DescriptorLimit {
651                descriptor_types: &[DescriptorType::AccelerationStructure],
652                get_limit_all: |p| p.max_descriptor_set_update_after_bind_acceleration_structures,
653                limit_name_all: "max_descriptor_set_update_after_bind_acceleration_structures",
654                vuids_all: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03574"],
655                get_limit_not_uab: |p| p.max_descriptor_set_acceleration_structures.unwrap_or(0),
656                limit_name_not_uab: "max_descriptor_set_acceleration_structures",
657                vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03573"],
658            },
659        ];
660
661        let mut per_stage_descriptors_all: [HashMap<ShaderStage, u32>;
662            PER_STAGE_DESCRIPTOR_LIMITS.len()] = array::from_fn(|_| HashMap::default());
663        let mut per_stage_descriptors_not_uab: [HashMap<ShaderStage, u32>;
664            PER_STAGE_DESCRIPTOR_LIMITS.len()] = array::from_fn(|_| HashMap::default());
665        let mut total_descriptors_all = [0; TOTAL_DESCRIPTOR_LIMITS.len()];
666        let mut total_descriptors_not_uab = [0; TOTAL_DESCRIPTOR_LIMITS.len()];
667        let mut has_push_descriptor_set = false;
668
669        for set_layout in set_layouts {
670            assert_eq!(device, set_layout.device().as_ref());
671
672            if set_layout
673                .flags()
674                .intersects(DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR)
675            {
676                if has_push_descriptor_set {
677                    return Err(Box::new(ValidationError {
678                        context: "set_layouts".into(),
679                        problem: "contains more than one descriptor set layout whose flags \
680                            include `DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR`"
681                            .into(),
682                        vuids: &["VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00293"],
683                        ..Default::default()
684                    }));
685                }
686
687                has_push_descriptor_set = true;
688            }
689
690            let is_not_uab = !set_layout
691                .flags()
692                .intersects(DescriptorSetLayoutCreateFlags::UPDATE_AFTER_BIND_POOL);
693
694            for layout_binding in set_layout.bindings().values() {
695                let &DescriptorSetLayoutBinding {
696                    binding_flags: _,
697                    descriptor_type,
698                    descriptor_count,
699                    stages,
700                    immutable_samplers: _,
701                    _ne: _,
702                } = layout_binding;
703
704                for ((limit, count_all), count_not_uab) in PER_STAGE_DESCRIPTOR_LIMITS
705                    .iter()
706                    .zip(&mut per_stage_descriptors_all)
707                    .zip(&mut per_stage_descriptors_not_uab)
708                {
709                    if limit.descriptor_types.contains(&descriptor_type) {
710                        for stage in stages {
711                            *count_all.entry(stage).or_default() += descriptor_count;
712
713                            if is_not_uab {
714                                *count_not_uab.entry(stage).or_default() += descriptor_count;
715                            }
716                        }
717                    }
718                }
719
720                for ((limit, count_all), count_not_uab) in TOTAL_DESCRIPTOR_LIMITS
721                    .iter()
722                    .zip(&mut total_descriptors_all)
723                    .zip(&mut total_descriptors_not_uab)
724                {
725                    if limit.descriptor_types.contains(&descriptor_type) {
726                        *count_all += descriptor_count;
727
728                        if is_not_uab {
729                            *count_not_uab += descriptor_count;
730                        }
731                    }
732                }
733            }
734        }
735
736        for ((limit, count_all), count_not_uab) in PER_STAGE_DESCRIPTOR_LIMITS
737            .iter()
738            .zip(per_stage_descriptors_all)
739            .zip(per_stage_descriptors_not_uab)
740        {
741            let limit_not_uab = (limit.get_limit_not_uab)(properties);
742
743            if let Some((max_stage, max_count)) = count_not_uab.into_iter().max_by_key(|(_, c)| *c)
744            {
745                if max_count > limit_not_uab {
746                    return Err(Box::new(ValidationError {
747                        context: "set_layouts".into(),
748                        problem: format!(
749                            "the combined number of {} descriptors, \
750                            belonging to descriptor set layouts without the \
751                            `DescriptorSetLayoutCreateFlags::UPDATE_AFTER_BIND_POOL` flag, \
752                            accessible to the `ShaderStage::{:?}` stage, \
753                            exceeds the `{}` limit",
754                            limit.descriptor_types[1..].iter().fold(
755                                format!("`DescriptorType::{:?}`", limit.descriptor_types[0]),
756                                |mut s, dt| {
757                                    write!(s, " + `DescriptorType::{:?}`", dt).unwrap();
758                                    s
759                                }
760                            ),
761                            max_stage,
762                            limit.limit_name_not_uab,
763                        )
764                        .into(),
765                        vuids: limit.vuids_not_uab,
766                        ..Default::default()
767                    }));
768                }
769            }
770
771            let limit_all = match (limit.get_limit_all)(properties) {
772                Some(x) => x,
773                None => continue,
774            };
775
776            if let Some((max_stage, max_count)) = count_all.into_iter().max_by_key(|(_, c)| *c) {
777                if max_count > limit_all {
778                    return Err(Box::new(ValidationError {
779                        context: "set_layouts".into(),
780                        problem: format!(
781                            "the combined number of {} descriptors, \
782                            accessible to the `ShaderStage::{:?}` stage, \
783                            exceeds the `{}` limit",
784                            limit.descriptor_types[1..].iter().fold(
785                                format!("`DescriptorType::{:?}`", limit.descriptor_types[0]),
786                                |mut s, dt| {
787                                    write!(s, " + `DescriptorType::{:?}`", dt).unwrap();
788                                    s
789                                }
790                            ),
791                            max_stage,
792                            limit.limit_name_all,
793                        )
794                        .into(),
795                        vuids: limit.vuids_all,
796                        ..Default::default()
797                    }));
798                }
799            }
800        }
801
802        for ((limit, count_all), count_not_uab) in TOTAL_DESCRIPTOR_LIMITS
803            .iter()
804            .zip(total_descriptors_all)
805            .zip(total_descriptors_not_uab)
806        {
807            let limit_not_uab = (limit.get_limit_not_uab)(properties);
808
809            if count_not_uab > limit_not_uab {
810                return Err(Box::new(ValidationError {
811                    context: "set_layouts".into(),
812                    problem: format!(
813                        "the combined number of {} descriptors, \
814                        belonging to descriptor set layouts without the \
815                        `DescriptorSetLayoutCreateFlags::UPDATE_AFTER_BIND_POOL` flag, \
816                        accessible across all shader stages, \
817                        exceeds the `{}` limit",
818                        limit.descriptor_types[1..].iter().fold(
819                            format!("`DescriptorType::{:?}`", limit.descriptor_types[0]),
820                            |mut s, dt| {
821                                write!(s, " + `DescriptorType::{:?}`", dt).unwrap();
822                                s
823                            }
824                        ),
825                        limit.limit_name_not_uab,
826                    )
827                    .into(),
828                    vuids: limit.vuids_not_uab,
829                    ..Default::default()
830                }));
831            }
832
833            let limit_all = match (limit.get_limit_all)(properties) {
834                Some(x) => x,
835                None => continue,
836            };
837
838            if count_all > limit_all {
839                return Err(Box::new(ValidationError {
840                    context: "set_layouts".into(),
841                    problem: format!(
842                        "the combined number of {} descriptors, \
843                        accessible across all shader stages, \
844                        exceeds the `{}` limit",
845                        limit.descriptor_types[1..].iter().fold(
846                            format!("`DescriptorType::{:?}`", limit.descriptor_types[0]),
847                            |mut s, dt| {
848                                write!(s, " + `DescriptorType::{:?}`", dt).unwrap();
849                                s
850                            }
851                        ),
852                        limit.limit_name_all,
853                    )
854                    .into(),
855                    vuids: limit.vuids_all,
856                    ..Default::default()
857                }));
858            }
859        }
860
861        let mut seen_stages = ShaderStages::empty();
862
863        for (range_index, range) in push_constant_ranges.iter().enumerate() {
864            range
865                .validate(device)
866                .map_err(|err| err.add_context(format!("push_constant_ranges[{}]", range_index)))?;
867
868            let &PushConstantRange {
869                stages,
870                offset: _,
871                size: _,
872            } = range;
873
874            if seen_stages.intersects(stages) {
875                return Err(Box::new(ValidationError {
876                    context: "push_constant_ranges".into(),
877                    problem: "contains more than one range with the same stage".into(),
878                    vuids: &["VUID-VkPipelineLayoutCreateInfo-pPushConstantRanges-00292"],
879                    ..Default::default()
880                }));
881            }
882
883            seen_stages |= stages;
884        }
885
886        Ok(())
887    }
888
889    pub(crate) fn to_vk<'a>(
890        &self,
891        fields1_vk: &'a PipelineLayoutCreateInfoFields1Vk,
892    ) -> ash::vk::PipelineLayoutCreateInfo<'a> {
893        let &Self {
894            flags,
895            set_layouts: _,
896            push_constant_ranges: _,
897            _ne: _,
898        } = self;
899        let PipelineLayoutCreateInfoFields1Vk {
900            set_layouts_vk,
901            push_constant_ranges_vk,
902        } = fields1_vk;
903
904        ash::vk::PipelineLayoutCreateInfo::default()
905            .flags(flags.into())
906            .set_layouts(set_layouts_vk)
907            .push_constant_ranges(push_constant_ranges_vk)
908    }
909
910    pub(crate) fn to_vk_fields1(&self) -> PipelineLayoutCreateInfoFields1Vk {
911        let &Self {
912            ref set_layouts,
913            ref push_constant_ranges,
914            ..
915        } = self;
916
917        let set_layouts_vk = set_layouts.iter().map(|l| l.handle()).collect();
918        let push_constant_ranges_vk = push_constant_ranges
919            .iter()
920            .map(|range| range.to_vk())
921            .collect();
922
923        PipelineLayoutCreateInfoFields1Vk {
924            set_layouts_vk,
925            push_constant_ranges_vk,
926        }
927    }
928}
929
930pub(crate) struct PipelineLayoutCreateInfoFields1Vk {
931    pub(crate) set_layouts_vk: SmallVec<[ash::vk::DescriptorSetLayout; 4]>,
932    pub(crate) push_constant_ranges_vk: SmallVec<[ash::vk::PushConstantRange; 4]>,
933}
934
935vulkan_bitflags! {
936    #[non_exhaustive]
937
938    /// Flags specifying additional properties of a pipeline layout.
939    PipelineLayoutCreateFlags = PipelineLayoutCreateFlags(u32);
940
941    /* TODO: enable
942    // TODO: document
943    INDEPENDENT_SETS = INDEPENDENT_SETS_EXT
944    RequiresOneOf([
945        RequiresAllOf([DeviceExtension(ext_graphics_pipeline_library)]),
946    ]), */
947}
948
949/// Description of a range of the push constants of a pipeline layout.
950#[derive(Clone, Copy, Debug, PartialEq, Eq)]
951pub struct PushConstantRange {
952    /// The stages which can access this range. A stage can access at most one push constant range.
953    ///
954    /// The default value is [`ShaderStages::empty()`], which must be overridden.
955    pub stages: ShaderStages,
956
957    /// Offset in bytes from the start of the push constants to this range.
958    ///
959    /// The value must be a multiple of 4.
960    ///
961    /// The default value is `0`.
962    pub offset: u32,
963
964    /// Size in bytes of the range.
965    ///
966    /// The value must be a multiple of 4, and not 0.
967    ///
968    /// The default value is `0`, which must be overridden.
969    pub size: u32,
970}
971
972impl Default for PushConstantRange {
973    #[inline]
974    fn default() -> Self {
975        Self {
976            stages: ShaderStages::empty(),
977            offset: 0,
978            size: 0,
979        }
980    }
981}
982
983impl PushConstantRange {
984    pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
985        let &Self {
986            stages,
987            offset,
988            size,
989        } = self;
990
991        stages.validate_device(device).map_err(|err| {
992            err.add_context("stages")
993                .set_vuids(&["VUID-VkPushConstantRange-stageFlags-parameter"])
994        })?;
995
996        if stages.is_empty() {
997            return Err(Box::new(ValidationError {
998                context: "stages".into(),
999                problem: "is empty".into(),
1000                vuids: &["VUID-VkPushConstantRange-stageFlags-requiredbitmask"],
1001                ..Default::default()
1002            }));
1003        }
1004
1005        let max_push_constants_size = device
1006            .physical_device()
1007            .properties()
1008            .max_push_constants_size;
1009
1010        if offset >= max_push_constants_size {
1011            return Err(Box::new(ValidationError {
1012                context: "offset".into(),
1013                problem: "is not less than the `max_push_constants_size` limit".into(),
1014                vuids: &["VUID-VkPushConstantRange-offset-00294"],
1015                ..Default::default()
1016            }));
1017        }
1018
1019        if offset % 4 != 0 {
1020            return Err(Box::new(ValidationError {
1021                context: "offset".into(),
1022                problem: "is not a multiple of 4".into(),
1023                vuids: &["VUID-VkPushConstantRange-offset-00295"],
1024                ..Default::default()
1025            }));
1026        }
1027
1028        if size == 0 {
1029            return Err(Box::new(ValidationError {
1030                context: "size".into(),
1031                problem: "is zero".into(),
1032                vuids: &["VUID-VkPushConstantRange-size-00296"],
1033                ..Default::default()
1034            }));
1035        }
1036
1037        if size % 4 != 0 {
1038            return Err(Box::new(ValidationError {
1039                context: "size".into(),
1040                problem: "is not a multiple of 4".into(),
1041                vuids: &["VUID-VkPushConstantRange-size-00297"],
1042                ..Default::default()
1043            }));
1044        }
1045
1046        if size > max_push_constants_size - offset {
1047            return Err(Box::new(ValidationError {
1048                problem: "`size` is greater than `max_push_constants_size` limit minus `offset`"
1049                    .into(),
1050                vuids: &["VUID-VkPushConstantRange-size-00298"],
1051                ..Default::default()
1052            }));
1053        }
1054
1055        Ok(())
1056    }
1057
1058    #[allow(clippy::wrong_self_convention)]
1059    pub(crate) fn to_vk(&self) -> ash::vk::PushConstantRange {
1060        let &Self {
1061            stages,
1062            offset,
1063            size,
1064        } = self;
1065
1066        ash::vk::PushConstantRange {
1067            stage_flags: stages.into(),
1068            offset,
1069            size,
1070        }
1071    }
1072}
1073
1074/// Parameters to create a new `PipelineLayout` as well as its accompanying `DescriptorSetLayout`
1075/// objects.
1076#[derive(Clone, Debug)]
1077pub struct PipelineDescriptorSetLayoutCreateInfo {
1078    pub flags: PipelineLayoutCreateFlags,
1079    pub set_layouts: Vec<DescriptorSetLayoutCreateInfo>,
1080    pub push_constant_ranges: Vec<PushConstantRange>,
1081}
1082
1083impl PipelineDescriptorSetLayoutCreateInfo {
1084    /// Creates a new `PipelineDescriptorSetLayoutCreateInfo` from the union of the requirements of
1085    /// each shader stage in `stages`.
1086    ///
1087    /// This is intended for quick prototyping or for single use layouts that do not have any
1088    /// bindings in common with other shaders. For the general case, it is strongly recommended
1089    /// to create pipeline layouts manually:
1090    /// - When multiple pipelines share the same layout object, it is faster than if they have
1091    ///   different objects, even if the objects both contain identical bindings. It is also faster
1092    ///   (though a little bit less), if multiple pipeline layout objects share common descriptor
1093    ///   set objects.
1094    /// - Pipeline layouts only need to be a superset of what the shaders use, they don't have to
1095    ///   match exactly. Creating a manual pipeline layout therefore allows you to specify layouts
1096    ///   that are applicable for many shaders, as long as each one uses a subset. This allows
1097    ///   further sharing.
1098    /// - Creating a manual pipeline layout makes your code more robust against changes in the
1099    ///   shader, in particular regarding whether a particular binding in the shader is used or not
1100    ///   (see also the limitations below).
1101    ///
1102    /// # Limitations:
1103    ///
1104    /// Only bindings that are [statically used] are included in the descriptor binding
1105    /// requirements, and therefore are included in the descriptor set layout.
1106    /// If the use of a binding depends on input variables to the shader (buffers, images,
1107    /// push constants etc.) then the shader reflection is unable to know that the binding is in
1108    /// use, and it will not be included in the pipeline layout.
1109    ///
1110    /// Note that this corresponds to the `shader_*_array_dynamic_indexing` device features.
1111    ///
1112    /// [statically used]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#shaders-staticuse
1113    pub fn from_stages<'a>(
1114        stages: impl IntoIterator<Item = &'a PipelineShaderStageCreateInfo>,
1115    ) -> Self {
1116        // Produce `DescriptorBindingRequirements` for each binding, by iterating over all
1117        // shaders and adding the requirements of each.
1118        let mut descriptor_binding_requirements: HashMap<
1119            (u32, u32),
1120            DescriptorBindingRequirements,
1121        > = HashMap::default();
1122        let mut max_set_num = 0;
1123        let mut push_constant_ranges: Vec<PushConstantRange> = Vec::new();
1124
1125        for stage in stages {
1126            let entry_point_info = stage.entry_point.info();
1127
1128            for (&(set_num, binding_num), reqs) in &entry_point_info.descriptor_binding_requirements
1129            {
1130                max_set_num = max(max_set_num, set_num);
1131
1132                match descriptor_binding_requirements.entry((set_num, binding_num)) {
1133                    Entry::Occupied(entry) => {
1134                        // Previous shaders already added requirements, so we merge
1135                        // requirements of the current shader into the requirements of the
1136                        // previous ones.
1137                        // TODO: return an error here instead of panicking?
1138                        entry.into_mut().merge(reqs).expect("Could not produce an intersection of the shader descriptor requirements");
1139                    }
1140                    Entry::Vacant(entry) => {
1141                        // No previous shader had this descriptor yet, so we just insert the
1142                        // requirements.
1143                        entry.insert(reqs.clone());
1144                    }
1145                }
1146            }
1147
1148            if let Some(range) = &entry_point_info.push_constant_requirements {
1149                if let Some(existing_range) =
1150                    push_constant_ranges.iter_mut().find(|existing_range| {
1151                        existing_range.offset == range.offset && existing_range.size == range.size
1152                    })
1153                {
1154                    // If this range was already used before, add our stage to it.
1155                    existing_range.stages |= range.stages;
1156                } else {
1157                    // If this range is new, insert it.
1158                    push_constant_ranges.push(*range)
1159                }
1160            }
1161        }
1162
1163        // Convert the descriptor binding requirements.
1164        let mut set_layouts =
1165            vec![DescriptorSetLayoutCreateInfo::default(); max_set_num as usize + 1];
1166
1167        for ((set_num, binding_num), reqs) in descriptor_binding_requirements {
1168            set_layouts[set_num as usize]
1169                .bindings
1170                .insert(binding_num, DescriptorSetLayoutBinding::from(&reqs));
1171        }
1172
1173        Self {
1174            flags: PipelineLayoutCreateFlags::empty(),
1175            set_layouts,
1176            push_constant_ranges,
1177        }
1178    }
1179
1180    /// Converts the `PipelineDescriptorSetLayoutCreateInfo` into a `PipelineLayoutCreateInfo` by
1181    /// creating the descriptor set layout objects.
1182    pub fn into_pipeline_layout_create_info(
1183        self,
1184        device: Arc<Device>,
1185    ) -> Result<PipelineLayoutCreateInfo, IntoPipelineLayoutCreateInfoError> {
1186        let PipelineDescriptorSetLayoutCreateInfo {
1187            flags,
1188            set_layouts,
1189            push_constant_ranges,
1190        } = self;
1191
1192        let set_layouts = set_layouts
1193            .into_iter()
1194            .enumerate()
1195            .map(|(set_num, create_info)| {
1196                DescriptorSetLayout::new(device.clone(), create_info).map_err(|error| {
1197                    IntoPipelineLayoutCreateInfoError {
1198                        set_num: set_num as u32,
1199                        error,
1200                    }
1201                })
1202            })
1203            .collect::<Result<Vec<_>, _>>()?;
1204
1205        Ok(PipelineLayoutCreateInfo {
1206            flags,
1207            set_layouts,
1208            push_constant_ranges,
1209            _ne: crate::NonExhaustive(()),
1210        })
1211    }
1212}
1213
1214#[derive(Clone, Debug)]
1215pub struct IntoPipelineLayoutCreateInfoError {
1216    pub set_num: u32,
1217    pub error: Validated<VulkanError>,
1218}
1219
1220impl Display for IntoPipelineLayoutCreateInfoError {
1221    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1222        write!(
1223            f,
1224            "an error occurred while creating a descriptor set layout for set number {}",
1225            self.set_num
1226        )
1227    }
1228}
1229
1230impl Error for IntoPipelineLayoutCreateInfoError {
1231    fn source(&self) -> Option<&(dyn Error + 'static)> {
1232        Some(&self.error)
1233    }
1234}
1235
1236#[cfg(test)]
1237mod tests {
1238
1239    use super::PipelineLayout;
1240    use crate::{
1241        pipeline::layout::{PipelineLayoutCreateInfo, PushConstantRange},
1242        shader::ShaderStages,
1243    };
1244
1245    #[test]
1246    fn push_constant_ranges_disjoint() {
1247        let test_cases = [
1248            // input:
1249            // - `0..12`, stage=fragment
1250            // - `0..40`, stage=vertex
1251            //
1252            // output:
1253            // - `0..12`, stage=fragment|vertex
1254            // - `12..40`, stage=vertex
1255            (
1256                &[
1257                    PushConstantRange {
1258                        stages: ShaderStages::FRAGMENT,
1259                        offset: 0,
1260                        size: 12,
1261                    },
1262                    PushConstantRange {
1263                        stages: ShaderStages::VERTEX,
1264                        offset: 0,
1265                        size: 40,
1266                    },
1267                ][..],
1268                &[
1269                    PushConstantRange {
1270                        stages: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
1271                        offset: 0,
1272                        size: 12,
1273                    },
1274                    PushConstantRange {
1275                        stages: ShaderStages::VERTEX,
1276                        offset: 12,
1277                        size: 28,
1278                    },
1279                ][..],
1280            ),
1281            // input:
1282            // - `0..12`, stage=fragment
1283            // - `4..40`, stage=vertex
1284            //
1285            // output:
1286            // - `0..4`, stage=fragment
1287            // - `4..12`, stage=fragment|vertex
1288            // - `12..40`, stage=vertex
1289            (
1290                &[
1291                    PushConstantRange {
1292                        stages: ShaderStages::FRAGMENT,
1293                        offset: 0,
1294                        size: 12,
1295                    },
1296                    PushConstantRange {
1297                        stages: ShaderStages::VERTEX,
1298                        offset: 4,
1299                        size: 36,
1300                    },
1301                ][..],
1302                &[
1303                    PushConstantRange {
1304                        stages: ShaderStages::FRAGMENT,
1305                        offset: 0,
1306                        size: 4,
1307                    },
1308                    PushConstantRange {
1309                        stages: ShaderStages::FRAGMENT | ShaderStages::VERTEX,
1310                        offset: 4,
1311                        size: 8,
1312                    },
1313                    PushConstantRange {
1314                        stages: ShaderStages::VERTEX,
1315                        offset: 12,
1316                        size: 28,
1317                    },
1318                ][..],
1319            ),
1320            // input:
1321            // - `0..12`, stage=fragment
1322            // - `8..20`, stage=compute
1323            // - `4..16`, stage=vertex
1324            // - `8..32`, stage=tess_ctl
1325            //
1326            // output:
1327            // - `0..4`, stage=fragment
1328            // - `4..8`, stage=fragment|vertex
1329            // - `8..16`, stage=fragment|vertex|compute|tess_ctl
1330            // - `16..20`, stage=compute|tess_ctl
1331            // - `20..32` stage=tess_ctl
1332            (
1333                &[
1334                    PushConstantRange {
1335                        stages: ShaderStages::FRAGMENT,
1336                        offset: 0,
1337                        size: 12,
1338                    },
1339                    PushConstantRange {
1340                        stages: ShaderStages::COMPUTE,
1341                        offset: 8,
1342                        size: 12,
1343                    },
1344                    PushConstantRange {
1345                        stages: ShaderStages::VERTEX,
1346                        offset: 4,
1347                        size: 12,
1348                    },
1349                    PushConstantRange {
1350                        stages: ShaderStages::TESSELLATION_CONTROL,
1351                        offset: 8,
1352                        size: 24,
1353                    },
1354                ][..],
1355                &[
1356                    PushConstantRange {
1357                        stages: ShaderStages::FRAGMENT,
1358                        offset: 0,
1359                        size: 4,
1360                    },
1361                    PushConstantRange {
1362                        stages: ShaderStages::FRAGMENT | ShaderStages::VERTEX,
1363                        offset: 4,
1364                        size: 4,
1365                    },
1366                    PushConstantRange {
1367                        stages: ShaderStages::VERTEX
1368                            | ShaderStages::FRAGMENT
1369                            | ShaderStages::COMPUTE
1370                            | ShaderStages::TESSELLATION_CONTROL,
1371                        offset: 8,
1372                        size: 4,
1373                    },
1374                    PushConstantRange {
1375                        stages: ShaderStages::VERTEX
1376                            | ShaderStages::COMPUTE
1377                            | ShaderStages::TESSELLATION_CONTROL,
1378                        offset: 12,
1379                        size: 4,
1380                    },
1381                    PushConstantRange {
1382                        stages: ShaderStages::COMPUTE | ShaderStages::TESSELLATION_CONTROL,
1383                        offset: 16,
1384                        size: 4,
1385                    },
1386                    PushConstantRange {
1387                        stages: ShaderStages::TESSELLATION_CONTROL,
1388                        offset: 20,
1389                        size: 12,
1390                    },
1391                ][..],
1392            ),
1393        ];
1394
1395        let (device, _) = gfx_dev_and_queue!();
1396
1397        for (input, expected) in test_cases {
1398            let layout = PipelineLayout::new(
1399                device.clone(),
1400                PipelineLayoutCreateInfo {
1401                    push_constant_ranges: input.into(),
1402                    ..Default::default()
1403                },
1404            )
1405            .unwrap();
1406
1407            assert_eq!(layout.push_constant_ranges_disjoint.as_slice(), expected);
1408        }
1409    }
1410}
1411
1412/* TODO: restore
1413#[cfg(test)]
1414mod tests {
1415    use std::iter;
1416    use std::sync::Arc;
1417    use descriptor::descriptor::ShaderStages;
1418    use descriptor::descriptor_set::DescriptorSetLayout;
1419    use descriptor::pipeline_layout::sys::PipelineLayout;
1420    use descriptor::pipeline_layout::sys::PipelineLayoutCreationError;
1421
1422    #[test]
1423    fn empty() {
1424        let (device, _) = gfx_dev_and_queue!();
1425        let _layout = PipelineLayout::new(&device, iter::empty(), iter::empty()).unwrap();
1426    }
1427
1428    #[test]
1429    fn wrong_device_panic() {
1430        let (device1, _) = gfx_dev_and_queue!();
1431        let (device2, _) = gfx_dev_and_queue!();
1432
1433        let set = match DescriptorSetLayout::raw(device1, iter::empty()) {
1434            Ok(s) => Arc::new(s),
1435            Err(_) => return
1436        };
1437
1438        assert_should_panic!({
1439            let _ = PipelineLayout::new(&device2, Some(&set), iter::empty());
1440        });
1441    }
1442
1443    #[test]
1444    fn invalid_push_constant_stages() {
1445        let (device, _) = gfx_dev_and_queue!();
1446
1447        let push_constant = (0, 8, ShaderStages::empty());
1448
1449        match PipelineLayout::new(&device, iter::empty(), Some(push_constant)) {
1450            Err(PipelineLayoutCreationError::InvalidPushConstant) => (),
1451            _ => panic!()
1452        }
1453    }
1454
1455    #[test]
1456    fn invalid_push_constant_size1() {
1457        let (device, _) = gfx_dev_and_queue!();
1458
1459        let push_constant = (0, 0, ShaderStages::all_graphics());
1460
1461        match PipelineLayout::new(&device, iter::empty(), Some(push_constant)) {
1462            Err(PipelineLayoutCreationError::InvalidPushConstant) => (),
1463            _ => panic!()
1464        }
1465    }
1466
1467    #[test]
1468    fn invalid_push_constant_size2() {
1469        let (device, _) = gfx_dev_and_queue!();
1470
1471        let push_constant = (0, 11, ShaderStages::all_graphics());
1472
1473        match PipelineLayout::new(&device, iter::empty(), Some(push_constant)) {
1474            Err(PipelineLayoutCreationError::InvalidPushConstant) => (),
1475            _ => panic!()
1476        }
1477    }
1478}
1479*/