Skip to main content

vk_graph/driver/
shader.rs

1//! Shader reflection and pipeline-stage descriptions.
2//!
3//! This module contains the data used by compute, graphics, and ray tracing pipeline creation to
4//! describe shader stages.
5//!
6//! # Construction
7//!
8//! The stage-specific constructors are the usual entry points:
9//!
10//! - [`Shader::new_compute`] for compute pipelines.
11//! - [`Shader::new_vertex`] and [`Shader::new_fragment`] for graphics pipelines.
12//! - [`Shader::new_ray_gen`], [`Shader::new_closest_hit`], [`Shader::new_miss`], and the other
13//!   ray tracing constructors for ray tracing pipelines.
14//!
15//! These constructors return a [`ShaderBuilder`]. Use [`ShaderBuilder::build`] when invalid SPIR-V
16//! should panic during setup, or [`ShaderBuilder::try_build`] when invalid SPIR-V should become a
17//! [`DriverError`]. [`Shader::try_new_compute`] and the other `try_new_*` constructors are shortcuts
18//! for the fallible path.
19//!
20//! [`Shader::from_spirv`] is stage-neutral and is useful when the stage is selected separately. It
21//! still needs a stage before the shader can be built. SPIR-V can be supplied as bytes, words, or a
22//! value accepted by `spirq::parse::SpirvBinary`.
23//!
24//! # Entry Points
25//!
26//! The default entry point is `main`. Override it with [`ShaderBuilder::entry_name`] when a SPIR-V
27//! module contains multiple entry points or when the exported function uses another name.
28//! Reflection is performed for the selected entry point only, so descriptors, push constants,
29//! attachments, and vertex inputs from other entry points are ignored.
30//!
31//! # Reflection
32//!
33//! Shader creation reflects the selected entry point immediately. Invalid SPIR-V, a missing entry
34//! point, or unsupported reflection data makes the fallible builders return [`DriverError::InvalidData`]
35//! or [`DriverError::Unsupported`], depending on where the problem is discovered.
36//!
37//! Reflection is used internally to collect:
38//!
39//! - descriptor set and binding numbers, descriptor types, array counts, and stage flags;
40//! - push constant ranges for the stage;
41//! - input attachments and color output locations for graphics render-pass setup;
42//! - vertex input bindings, attributes, formats, offsets, strides, and input rates for vertex
43//!   shaders unless a manual vertex layout is supplied.
44//!
45//! When several shaders are combined into one pipeline, descriptor bindings with the same `(set,
46//! binding)` must describe compatible descriptor types. Compatible bindings are merged and their
47//! stage flags are ORed together. Conflicting descriptor declarations make pipeline creation fail.
48//!
49//! # Descriptor Bindings And Samplers
50//!
51//! [`Descriptor`] identifies a shader descriptor binding by set and binding number. It is a binding
52//! location, not a live descriptor-set allocation.
53//!
54//! Combined image samplers and sampler descriptors carry immutable sampler information into the
55//! generated descriptor set layout. You can provide explicit sampler state with
56//! [`ShaderBuilder::image_sampler`]. If no sampler is specified, the shader binding name is inspected
57//! for a compact sampler suffix of the form `_sampler_xyz`, where:
58//!
59//! - `x` selects texel filtering: `n` for nearest or `l` for linear;
60//! - `y` selects mip filtering: `n` for nearest or `l` for linear;
61//! - `z` selects address mode: `b` clamp-to-border, `e` clamp-to-edge, `m` mirrored-repeat, or `r`
62//!   repeat.
63//!
64//! For example, a binding whose reflected name ends with `_sampler_lle` uses linear texel filtering,
65//! linear mip filtering, and clamp-to-edge addressing. Bindings without this suffix use a linear
66//! repeat sampler by default. [`SamplerInfo`] and [`SamplerInfoBuilder`] expose the full Vulkan
67//! sampler state when the inferred defaults are not appropriate.
68//!
69//! # Specialization Constants
70//!
71//! Specialization constants are supplied with [`ShaderBuilder::specialization`] using a
72//! [`SpecializationMap`]. Reflection applies those values before descriptor information is collected,
73//! so specialization constants can affect reflected array lengths and other specialization-dependent
74//! layout data. The map stores raw bytes plus Vulkan specialization entries; use native endian byte
75//! conversion such as `to_ne_bytes()` for scalar values.
76//!
77//! # Vertex Input Reflection
78//!
79//! Vertex shaders can provide graphics vertex input state automatically. Scalar and vector input
80//! variables are mapped to Vulkan formats based on their reflected scalar type and component count.
81//! Offsets and strides are inferred from the reflected byte sizes, grouped by binding.
82//!
83//! Binding numbers and input rates can be encoded in reflected input names. Names containing
84//! `_vbindN` are assigned to vertex binding `N`; names containing `_ibindN` are assigned to instance
85//! binding `N`. Inputs without either marker use binding `0` with vertex input rate. If reflection is
86//! not expressive enough for a shader's vertex layout, provide a manual layout with
87//! [`ShaderBuilder::vertex_input`].
88//!
89//! # Pipeline Use
90//!
91//! Shaders are plain descriptions until a pipeline is created. Pipeline creation turns the SPIR-V
92//! into Vulkan shader modules, uses the reflected layout to create compatible descriptor set layouts,
93//! and then destroys the temporary shader modules after the pipeline has been created. Keep the
94//! [`Shader`] values around only as long as they are useful to construct or rebuild pipelines.
95
96use {
97    super::{
98        DescriptorSetLayout, DriverError, VertexInputState,
99        descriptor_set_layout::{DescriptorSetLayoutBindingInfo, DescriptorSetLayoutInfo},
100        device::Device,
101    },
102    ash::vk,
103    derive_builder::{Builder, UninitializedFieldError},
104    log::{debug, error, trace, warn},
105    ordered_float::OrderedFloat,
106    spirq::{
107        ReflectConfig,
108        entry_point::EntryPoint,
109        parse::SpirvBinary,
110        spirv::ExecutionModel,
111        ty::{DescriptorType, ScalarType, Type, VectorType},
112        var::Variable,
113    },
114    std::{
115        collections::{BTreeMap, HashMap, HashSet},
116        fmt::{Debug, Formatter},
117        ops::Deref,
118        panic::{AssertUnwindSafe, catch_unwind},
119        thread::panicking,
120    },
121};
122
123pub(crate) type DescriptorBindingMap = HashMap<Descriptor, (DescriptorInfo, vk::ShaderStageFlags)>;
124
125#[profiling::function]
126fn guess_immutable_sampler(binding_name: &str) -> SamplerInfo {
127    const INVALID_ERR: &str = "Invalid sampler specification";
128
129    let (texel_filter, mipmap_mode, address_modes) = if binding_name.contains("_sampler_") {
130        let spec = &binding_name[binding_name.len() - 3..];
131        let texel_filter = match &spec[0..1] {
132            "n" => vk::Filter::NEAREST,
133            "l" => vk::Filter::LINEAR,
134            _ => panic!("{INVALID_ERR}: {}", &spec[0..1]),
135        };
136
137        let mipmap_mode = match &spec[1..2] {
138            "n" => vk::SamplerMipmapMode::NEAREST,
139            "l" => vk::SamplerMipmapMode::LINEAR,
140            _ => panic!("{INVALID_ERR}: {}", &spec[1..2]),
141        };
142
143        let address_modes = match &spec[2..3] {
144            "b" => vk::SamplerAddressMode::CLAMP_TO_BORDER,
145            "e" => vk::SamplerAddressMode::CLAMP_TO_EDGE,
146            "m" => vk::SamplerAddressMode::MIRRORED_REPEAT,
147            "r" => vk::SamplerAddressMode::REPEAT,
148            _ => panic!("{INVALID_ERR}: {}", &spec[2..3]),
149        };
150
151        (texel_filter, mipmap_mode, address_modes)
152    } else {
153        debug!("image binding {binding_name} using default sampler");
154
155        (
156            vk::Filter::LINEAR,
157            vk::SamplerMipmapMode::LINEAR,
158            vk::SamplerAddressMode::REPEAT,
159        )
160    };
161    let anisotropy_enable = texel_filter == vk::Filter::LINEAR;
162    let mut info = SamplerInfoBuilder::default()
163        .mag_filter(texel_filter)
164        .min_filter(texel_filter)
165        .mipmap_mode(mipmap_mode)
166        .address_mode_u(address_modes)
167        .address_mode_v(address_modes)
168        .address_mode_w(address_modes)
169        .max_lod(vk::LOD_CLAMP_NONE)
170        .anisotropy_enable(anisotropy_enable);
171
172    if anisotropy_enable {
173        info = info.max_anisotropy(16.0);
174    }
175
176    info.build()
177}
178
179/// Tuple of descriptor set index and binding index.
180///
181/// This is a generic representation of the descriptor binding point within the shader and not a
182/// bound descriptor reference.
183#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
184pub struct Descriptor {
185    /// Descriptor set index.
186    pub set: u32,
187
188    /// Descriptor binding index.
189    pub binding: u32,
190}
191
192impl From<u32> for Descriptor {
193    fn from(binding: u32) -> Self {
194        Self { set: 0, binding }
195    }
196}
197
198impl From<(u32, u32)> for Descriptor {
199    fn from((set, binding): (u32, u32)) -> Self {
200        Self { set, binding }
201    }
202}
203
204#[derive(Clone, Copy, Debug)]
205pub(crate) enum DescriptorInfo {
206    AccelerationStructure(u32),
207    CombinedImageSampler(u32, SamplerInfo, bool), //count, sampler, is-manually-defined?
208    InputAttachment(u32, u32),                    //count, input index,
209    SampledImage(u32),
210    Sampler(u32, SamplerInfo, bool), //count, sampler, is-manually-defined?
211    StorageBuffer(u32),
212    StorageImage(u32),
213    StorageTexelBuffer(u32),
214    UniformBuffer(u32),
215    UniformTexelBuffer(u32),
216}
217
218impl DescriptorInfo {
219    pub fn binding_count(self) -> u32 {
220        match self {
221            Self::AccelerationStructure(binding_count) => binding_count,
222            Self::CombinedImageSampler(binding_count, ..) => binding_count,
223            Self::InputAttachment(binding_count, _) => binding_count,
224            Self::SampledImage(binding_count) => binding_count,
225            Self::Sampler(binding_count, ..) => binding_count,
226            Self::StorageBuffer(binding_count) => binding_count,
227            Self::StorageImage(binding_count) => binding_count,
228            Self::StorageTexelBuffer(binding_count) => binding_count,
229            Self::UniformBuffer(binding_count) => binding_count,
230            Self::UniformTexelBuffer(binding_count) => binding_count,
231        }
232    }
233
234    pub fn descriptor_type(self) -> vk::DescriptorType {
235        match self {
236            Self::AccelerationStructure(_) => vk::DescriptorType::ACCELERATION_STRUCTURE_KHR,
237            Self::CombinedImageSampler(..) => vk::DescriptorType::COMBINED_IMAGE_SAMPLER,
238            Self::InputAttachment(..) => vk::DescriptorType::INPUT_ATTACHMENT,
239            Self::SampledImage(_) => vk::DescriptorType::SAMPLED_IMAGE,
240            Self::Sampler(..) => vk::DescriptorType::SAMPLER,
241            Self::StorageBuffer(_) => vk::DescriptorType::STORAGE_BUFFER,
242            Self::StorageImage(_) => vk::DescriptorType::STORAGE_IMAGE,
243            Self::StorageTexelBuffer(_) => vk::DescriptorType::STORAGE_TEXEL_BUFFER,
244            Self::UniformBuffer(_) => vk::DescriptorType::UNIFORM_BUFFER,
245            Self::UniformTexelBuffer(_) => vk::DescriptorType::UNIFORM_TEXEL_BUFFER,
246        }
247    }
248
249    fn sampler_info(self) -> Option<SamplerInfo> {
250        match self {
251            Self::CombinedImageSampler(_, sampler_info, _) | Self::Sampler(_, sampler_info, _) => {
252                Some(sampler_info)
253            }
254            _ => None,
255        }
256    }
257
258    pub fn set_binding_count(&mut self, binding_count: u32) {
259        *match self {
260            Self::AccelerationStructure(binding_count) => binding_count,
261            Self::CombinedImageSampler(binding_count, ..) => binding_count,
262            Self::InputAttachment(binding_count, _) => binding_count,
263            Self::SampledImage(binding_count) => binding_count,
264            Self::Sampler(binding_count, ..) => binding_count,
265            Self::StorageBuffer(binding_count) => binding_count,
266            Self::StorageImage(binding_count) => binding_count,
267            Self::StorageTexelBuffer(binding_count) => binding_count,
268            Self::UniformBuffer(binding_count) => binding_count,
269            Self::UniformTexelBuffer(binding_count) => binding_count,
270        } = binding_count;
271    }
272}
273
274#[derive(Debug)]
275pub(crate) struct PipelineDescriptorInfo {
276    pub layouts: BTreeMap<u32, DescriptorSetLayout>,
277    pub pool_sizes: HashMap<u32, HashMap<vk::DescriptorType, u32>>,
278}
279
280impl PipelineDescriptorInfo {
281    #[profiling::function]
282    pub fn create(
283        device: &Device,
284        descriptor_bindings: &DescriptorBindingMap,
285        bindless_descriptors: &HashSet<Descriptor>,
286    ) -> Result<Self, DriverError> {
287        let descriptor_set_count = descriptor_bindings
288            .keys()
289            .map(|descriptor| descriptor.set)
290            .max()
291            .map(|set| set + 1)
292            .unwrap_or_default();
293        let mut layouts = BTreeMap::new();
294        let mut pool_sizes = HashMap::new();
295
296        //trace!("descriptor_bindings: {:#?}", &descriptor_bindings);
297
298        for descriptor_set_idx in 0..descriptor_set_count {
299            let mut binding_counts = HashMap::<vk::DescriptorType, u32>::new();
300            let mut bindings = vec![];
301
302            for (descriptor, (descriptor_info, stage_flags)) in descriptor_bindings
303                .iter()
304                .filter(|(descriptor, _)| descriptor.set == descriptor_set_idx)
305            {
306                let descriptor_ty = descriptor_info.descriptor_type();
307                *binding_counts.entry(descriptor_ty).or_default() +=
308                    descriptor_info.binding_count();
309                bindings.push(DescriptorSetLayoutBindingInfo {
310                    binding: descriptor.binding,
311                    binding_flags: if bindless_descriptors.contains(descriptor)
312                        && device
313                            .physical
314                            .features_v1_2
315                            .descriptor_binding_partially_bound
316                    {
317                        vk::DescriptorBindingFlags::PARTIALLY_BOUND
318                    } else {
319                        vk::DescriptorBindingFlags::empty()
320                    },
321                    descriptor_count: descriptor_info.binding_count(),
322                    descriptor_type: descriptor_ty,
323                    immutable_sampler: descriptor_info.sampler_info(),
324                    stage_flags: *stage_flags,
325                });
326            }
327
328            bindings.sort_unstable_by_key(|binding| binding.binding);
329
330            let pool_size = pool_sizes
331                .entry(descriptor_set_idx)
332                .or_insert_with(HashMap::new);
333
334            for (descriptor_ty, binding_count) in binding_counts.into_iter() {
335                *pool_size.entry(descriptor_ty).or_default() += binding_count;
336            }
337
338            layouts.insert(
339                descriptor_set_idx,
340                DescriptorSetLayout::get_or_create(
341                    device,
342                    DescriptorSetLayoutInfo {
343                        bindings: bindings.into_boxed_slice(),
344                    },
345                )?,
346            );
347        }
348
349        //trace!("layouts {:#?}", &layouts);
350        // trace!("pool_sizes {:#?}", &pool_sizes);
351
352        Ok(Self {
353            layouts,
354            pool_sizes,
355        })
356    }
357}
358
359pub(crate) struct Sampler {
360    device: Device,
361    sampler: vk::Sampler,
362}
363
364impl Sampler {
365    #[profiling::function]
366    pub fn create(device: &Device, info: impl Into<SamplerInfo>) -> Result<Self, DriverError> {
367        let device = device.clone();
368        let info = info.into();
369
370        let sampler = unsafe {
371            device
372                .create_sampler(
373                    &vk::SamplerCreateInfo::default()
374                        .flags(info.flags)
375                        .mag_filter(info.mag_filter)
376                        .min_filter(info.min_filter)
377                        .mipmap_mode(info.mipmap_mode)
378                        .address_mode_u(info.address_mode_u)
379                        .address_mode_v(info.address_mode_v)
380                        .address_mode_w(info.address_mode_w)
381                        .mip_lod_bias(info.mip_lod_bias.0)
382                        .anisotropy_enable(info.anisotropy_enable)
383                        .max_anisotropy(info.max_anisotropy.0)
384                        .compare_enable(info.compare_enable)
385                        .compare_op(info.compare_op)
386                        .min_lod(info.min_lod.0)
387                        .max_lod(info.max_lod.0)
388                        .border_color(info.border_color)
389                        .unnormalized_coordinates(info.unnormalized_coordinates)
390                        .push_next(
391                            &mut vk::SamplerReductionModeCreateInfo::default()
392                                .reduction_mode(info.reduction_mode),
393                        ),
394                    None,
395                )
396                .map_err(|err| match err {
397                    vk::Result::ERROR_OUT_OF_HOST_MEMORY
398                    | vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => {
399                        warn!("unable to create sampler: {err}");
400                        DriverError::OutOfMemory
401                    }
402                    _ => {
403                        warn!("unsupported sampler creation: {err}");
404                        DriverError::Unsupported
405                    }
406                })?
407        };
408
409        Ok(Self { device, sampler })
410    }
411}
412
413impl Debug for Sampler {
414    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
415        f.debug_struct(stringify!(Sampler))
416            .field("handle", &self.sampler)
417            .finish_non_exhaustive()
418    }
419}
420
421impl Deref for Sampler {
422    type Target = vk::Sampler;
423
424    fn deref(&self) -> &Self::Target {
425        &self.sampler
426    }
427}
428
429impl Drop for Sampler {
430    #[profiling::function]
431    fn drop(&mut self) {
432        if panicking() {
433            return;
434        }
435
436        unsafe {
437            self.device.destroy_sampler(self.sampler, None);
438        }
439    }
440}
441
442/// Information used to create a [`vk::Sampler`] instance.
443///
444/// See [`VkSamplerCreateInfo`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkSamplerCreateInfo.html).
445#[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)]
446#[builder(
447    build_fn(private, name = "fallible_build", error = "SamplerInfoBuilderError"),
448    derive(Clone, Copy, Debug),
449    pattern = "owned"
450)]
451pub struct SamplerInfo {
452    /// Bitmask specifying additional parameters of a sampler.
453    #[builder(default)]
454    pub flags: vk::SamplerCreateFlags,
455
456    /// Specifies the magnification filter to apply to texture lookups.
457    ///
458    /// The default value is [`vk::Filter::NEAREST`].
459    #[builder(default)]
460    pub mag_filter: vk::Filter,
461
462    /// Specifies the minification filter to apply to texture lookups.
463    ///
464    /// The default value is [`vk::Filter::NEAREST`].
465    #[builder(default)]
466    pub min_filter: vk::Filter,
467
468    /// A value specifying the mipmap filter to apply to lookups.
469    ///
470    /// The default value is [`vk::SamplerMipmapMode::NEAREST`].
471    #[builder(default)]
472    pub mipmap_mode: vk::SamplerMipmapMode,
473
474    /// A value specifying the addressing mode for U coordinates outside `[0, 1)`.
475    ///
476    /// The default value is [`vk::SamplerAddressMode::REPEAT`].
477    #[builder(default)]
478    pub address_mode_u: vk::SamplerAddressMode,
479
480    /// A value specifying the addressing mode for V coordinates outside `[0, 1)`.
481    ///
482    /// The default value is [`vk::SamplerAddressMode::REPEAT`].
483    #[builder(default)]
484    pub address_mode_v: vk::SamplerAddressMode,
485
486    /// A value specifying the addressing mode for W coordinates outside `[0, 1)`.
487    ///
488    /// The default value is [`vk::SamplerAddressMode::REPEAT`].
489    #[builder(default)]
490    pub address_mode_w: vk::SamplerAddressMode,
491
492    /// Bias added to the mipmap LOD calculation and to bias provided by SPIR-V image sampling
493    /// instructions.
494    #[builder(default, setter(into))]
495    pub mip_lod_bias: OrderedFloat<f32>,
496
497    /// Enables anisotropic filtering.
498    #[builder(default)]
499    pub anisotropy_enable: bool,
500
501    /// The anisotropy value clamp used by the sampler when `anisotropy_enable` is `true`.
502    ///
503    /// If [`anisotropy_enable`](Self::anisotropy_enable) is `false`, `max_anisotropy` is ignored.
504    #[builder(default, setter(into))]
505    pub max_anisotropy: OrderedFloat<f32>,
506
507    /// Enables comparison against a reference value during lookups.
508    #[builder(default)]
509    pub compare_enable: bool,
510
511    /// Specifies the comparison operator to apply to fetched data before filtering.
512    #[builder(default)]
513    pub compare_op: vk::CompareOp,
514
515    /// Minimum LOD value used to clamp the computed level of detail.
516    #[builder(default, setter(into))]
517    pub min_lod: OrderedFloat<f32>,
518
519    /// Maximum LOD value used to clamp the computed level of detail.
520    ///
521    /// To avoid clamping the maximum value, set `max_lod` to [`vk::LOD_CLAMP_NONE`].
522    #[builder(default, setter(into))]
523    pub max_lod: OrderedFloat<f32>,
524
525    /// Specifies the predefined border color to use.
526    ///
527    /// The default value is [`vk::BorderColor::FLOAT_TRANSPARENT_BLACK`].
528    #[builder(default)]
529    pub border_color: vk::BorderColor,
530
531    /// Controls whether to use unnormalized or normalized texel coordinates to address texels of
532    /// the image.
533    ///
534    /// When set to `true`, the range of the image coordinates used to lookup the texel is in the
535    /// range of zero to the image size in each dimension.
536    ///
537    /// When set to `false` the range of image coordinates is zero to one.
538    ///
539    /// See [`VkSamplerCreateInfo`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkSamplerCreateInfo.html).
540    #[builder(default)]
541    pub unnormalized_coordinates: bool,
542
543    /// Specifies sampler reduction mode.
544    ///
545    /// Setting magnification filter ([`mag_filter`](Self::mag_filter)) to [`vk::Filter::NEAREST`]
546    /// disables sampler reduction mode.
547    ///
548    /// The default value is [`vk::SamplerReductionMode::WEIGHTED_AVERAGE`].
549    ///
550    /// See [`VkSamplerReductionModeCreateInfo`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkSamplerReductionModeCreateInfo.html).
551    #[builder(default)]
552    pub reduction_mode: vk::SamplerReductionMode,
553}
554
555impl SamplerInfo {
556    /// Default sampler information with `mag_filter`, `min_filter` and `mipmap_mode` set to linear.
557    pub const LINEAR: SamplerInfoBuilder = SamplerInfoBuilder {
558        flags: None,
559        mag_filter: Some(vk::Filter::LINEAR),
560        min_filter: Some(vk::Filter::LINEAR),
561        mipmap_mode: Some(vk::SamplerMipmapMode::LINEAR),
562        address_mode_u: None,
563        address_mode_v: None,
564        address_mode_w: None,
565        mip_lod_bias: None,
566        anisotropy_enable: None,
567        max_anisotropy: None,
568        compare_enable: None,
569        compare_op: None,
570        min_lod: None,
571        max_lod: None,
572        border_color: None,
573        unnormalized_coordinates: None,
574        reduction_mode: None,
575    };
576
577    /// Default sampler information with `mag_filter`, `min_filter` and `mipmap_mode` set to
578    /// nearest.
579    pub const NEAREST: SamplerInfoBuilder = SamplerInfoBuilder {
580        flags: None,
581        mag_filter: Some(vk::Filter::NEAREST),
582        min_filter: Some(vk::Filter::NEAREST),
583        mipmap_mode: Some(vk::SamplerMipmapMode::NEAREST),
584        address_mode_u: None,
585        address_mode_v: None,
586        address_mode_w: None,
587        mip_lod_bias: None,
588        anisotropy_enable: None,
589        max_anisotropy: None,
590        compare_enable: None,
591        compare_op: None,
592        min_lod: None,
593        max_lod: None,
594        border_color: None,
595        unnormalized_coordinates: None,
596        reduction_mode: None,
597    };
598
599    /// Creates a default `SamplerInfoBuilder`.
600    pub fn builder() -> SamplerInfoBuilder {
601        Default::default()
602    }
603
604    /// Converts a `SamplerInfo` into a `SamplerInfoBuilder`.
605    pub fn into_builder(self) -> SamplerInfoBuilder {
606        SamplerInfoBuilder {
607            flags: Some(self.flags),
608            mag_filter: Some(self.mag_filter),
609            min_filter: Some(self.min_filter),
610            mipmap_mode: Some(self.mipmap_mode),
611            address_mode_u: Some(self.address_mode_u),
612            address_mode_v: Some(self.address_mode_v),
613            address_mode_w: Some(self.address_mode_w),
614            mip_lod_bias: Some(self.mip_lod_bias),
615            anisotropy_enable: Some(self.anisotropy_enable),
616            max_anisotropy: Some(self.max_anisotropy),
617            compare_enable: Some(self.compare_enable),
618            compare_op: Some(self.compare_op),
619            min_lod: Some(self.min_lod),
620            max_lod: Some(self.max_lod),
621            border_color: Some(self.border_color),
622            unnormalized_coordinates: Some(self.unnormalized_coordinates),
623            reduction_mode: Some(self.reduction_mode),
624        }
625    }
626}
627
628impl Default for SamplerInfo {
629    fn default() -> Self {
630        Self {
631            flags: vk::SamplerCreateFlags::empty(),
632            mag_filter: vk::Filter::NEAREST,
633            min_filter: vk::Filter::NEAREST,
634            mipmap_mode: vk::SamplerMipmapMode::NEAREST,
635            address_mode_u: vk::SamplerAddressMode::REPEAT,
636            address_mode_v: vk::SamplerAddressMode::REPEAT,
637            address_mode_w: vk::SamplerAddressMode::REPEAT,
638            mip_lod_bias: OrderedFloat(0.0),
639            anisotropy_enable: false,
640            max_anisotropy: OrderedFloat(0.0),
641            compare_enable: false,
642            compare_op: vk::CompareOp::NEVER,
643            min_lod: OrderedFloat(0.0),
644            max_lod: OrderedFloat(0.0),
645            border_color: vk::BorderColor::FLOAT_TRANSPARENT_BLACK,
646            unnormalized_coordinates: false,
647            reduction_mode: vk::SamplerReductionMode::WEIGHTED_AVERAGE,
648        }
649    }
650}
651
652impl SamplerInfoBuilder {
653    /// Builds a new `SamplerInfo`.
654    #[inline(always)]
655    pub fn build(self) -> SamplerInfo {
656        self.fallible_build().expect("invalid sampler info")
657    }
658}
659
660impl From<SamplerInfoBuilder> for SamplerInfo {
661    fn from(info: SamplerInfoBuilder) -> Self {
662        info.build()
663    }
664}
665
666#[derive(Debug)]
667struct SamplerInfoBuilderError;
668
669impl From<UninitializedFieldError> for SamplerInfoBuilderError {
670    fn from(_: UninitializedFieldError) -> Self {
671        Self
672    }
673}
674
675/// Describes a shader program which runs on a pipeline stage.
676///
677/// See [`VkShaderModuleCreateInfo`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkShaderModuleCreateInfo.html)
678/// and [`VkPipelineShaderStageCreateInfo`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkPipelineShaderStageCreateInfo.html).
679#[allow(missing_docs)]
680#[derive(Builder, Clone)]
681#[builder(
682    build_fn(private, name = "fallible_build", error = "ShaderBuilderError"),
683    derive(Clone, Debug),
684    pattern = "owned"
685)]
686pub struct Shader {
687    /// The name of the entry point which will be executed by this shader.
688    ///
689    /// The default value is `main`.
690    #[builder(default = "\"main\".to_owned()", setter(into))]
691    pub entry_name: String,
692
693    /// Data about Vulkan specialization constants.
694    ///
695    /// See [`VkSpecializationInfo`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkSpecializationInfo.html).
696    ///
697    /// # Examples
698    ///
699    /// Basic usage (GLSL):
700    ///
701    /// ```
702    /// # vk_shader_macros::glsl!(r#"
703    /// #version 460 core
704    /// #pragma shader_stage(compute)
705    ///
706    /// // Defaults to 6 if not set using Shader specialization!
707    /// layout(constant_id = 0) const uint MY_COUNT = 6;
708    ///
709    /// layout(set = 0, binding = 0) uniform sampler2D my_samplers[MY_COUNT];
710    ///
711    /// void main()
712    /// {
713    ///     // Code uses MY_COUNT number of my_samplers here
714    /// }
715    /// # "#);
716    /// ```
717    ///
718    /// ```no_run
719    /// # use std::sync::Arc;
720    /// # use ash::vk;
721    /// # use vk_graph::driver::DriverError;
722    /// # use vk_graph::driver::device::{Device, DeviceInfo};
723    /// # use vk_graph::driver::shader::{Shader, SpecializationMap};
724    /// # fn main() -> Result<(), DriverError> {
725    /// # let device = Device::create(DeviceInfo::default())?;
726    /// # let my_shader_code = [0u8; 1];
727    /// // We instead specify 42 for MY_COUNT:
728    /// let shader = Shader::new_fragment(my_shader_code.as_slice())
729    ///     .specialization(
730    ///         SpecializationMap::new(42u32.to_ne_bytes())
731    ///             .constant(0, 0, 4)
732    ///     );
733    /// # Ok(()) }
734    /// ```
735    #[builder(default, setter(strip_option))]
736    pub specialization: Option<SpecializationMap>,
737
738    /// Shader code.
739    ///
740    /// Although SPIR-V code is specified as `u32` values, this field uses `u8` in order to make
741    /// loading from file simpler. You should always have a SPIR-V code length which is a multiple
742    /// of four bytes, or an error will be returned during pipeline creation.
743    ///
744    /// See [`VkShaderModuleCreateInfo`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkShaderModuleCreateInfo.html).
745    #[builder(setter(into))]
746    pub spirv: SpirvBinary,
747
748    /// The shader stage this structure applies to.
749    ///
750    /// See [`VkShaderStageFlagBits`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkShaderStageFlagBits.html).
751    pub stage: vk::ShaderStageFlags,
752
753    #[builder(private)]
754    entry_point: EntryPoint,
755
756    #[builder(default, private)]
757    image_samplers: HashMap<Descriptor, SamplerInfo>,
758
759    #[builder(default, private, setter(strip_option))]
760    vertex_input_state: Option<VertexInputState>,
761}
762
763macro_rules! shader_ctors {
764    ($(($name:ident, $flag:ident, $desc:literal),)*) => {
765        paste::paste! {
766            $(
767                #[doc = $desc]
768                ///
769                /// # Panics
770                ///
771                /// Panics if the shader code is invalid. Use [`try_build`](ShaderBuilder::try_build)
772                /// on the returned builder for a fallible path.
773                pub fn [<new_ $name>](spirv: impl Into<SpirvBinary>) -> ShaderBuilder {
774                    ShaderBuilder::default().spirv(spirv).stage(vk::ShaderStageFlags::$flag)
775                }
776
777                #[doc = "Creates a "]
778                #[doc = $desc]
779                #[doc = ", returning an error if the SPIR-V is invalid."]
780                pub fn [<try_new_ $name>](spirv: impl Into<SpirvBinary>) -> Result<Shader, DriverError> {
781                    ShaderBuilder::default()
782                        .spirv(spirv)
783                        .stage(vk::ShaderStageFlags::$flag)
784                        .try_build()
785                }
786            )*
787        }
788    }
789}
790
791impl Shader {
792    /// Specifies a shader with the given `stage` and shader code.
793    ///
794    /// See [`VkShaderModuleCreateInfo`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkShaderModuleCreateInfo.html).
795    #[allow(clippy::new_ret_no_self)]
796    pub fn new(stage: vk::ShaderStageFlags, spirv: impl Into<SpirvBinary>) -> ShaderBuilder {
797        ShaderBuilder::default().spirv(spirv).stage(stage)
798    }
799
800    shader_ctors! {
801        (any_hit, ANY_HIT_KHR, "Creates a new ray tracing any-hit shader."),
802        (callable, CALLABLE_KHR, "Creates a new ray tracing callable shader."),
803        (closest_hit, CLOSEST_HIT_KHR, "Creates a new ray tracing closest-hit shader."),
804        (compute, COMPUTE, "Creates a new compute shader."),
805        (fragment, FRAGMENT, "Creates a new fragment shader."),
806        (geometry, GEOMETRY, "Creates a new geometry shader."),
807        (intersection, INTERSECTION_KHR, "Creates a new ray tracing intersection shader."),
808        (mesh, MESH_EXT, "Creates a new mesh shader."),
809        (miss, MISS_KHR, "Creates a new ray tracing miss shader."),
810        (ray_gen, RAYGEN_KHR, "Creates a new ray tracing ray-generation shader."),
811        (task, TASK_EXT, "Creates a new task shader."),
812        (tessellation_ctrl, TESSELLATION_CONTROL, "Creates a new tessellation control shader."),
813        (tessellation_eval, TESSELLATION_EVALUATION, "Creates a new tessellation evaluation shader."),
814        (vertex, VERTEX, "Creates a new vertex shader."),
815    }
816
817    /// Returns the input and write attachments of a shader.
818    #[profiling::function]
819    pub(super) fn attachments(
820        &self,
821    ) -> (
822        impl Iterator<Item = u32> + '_,
823        impl Iterator<Item = u32> + '_,
824    ) {
825        (
826            self.entry_point.vars.iter().filter_map(|var| match var {
827                Variable::Descriptor {
828                    desc_ty: DescriptorType::InputAttachment(attachment),
829                    ..
830                } => Some(*attachment),
831                _ => None,
832            }),
833            self.entry_point.vars.iter().filter_map(|var| match var {
834                Variable::Output { location, .. } => Some(location.loc()),
835                _ => None,
836            }),
837        )
838    }
839
840    /// Creates a default `ShaderBuilder`.
841    pub fn builder() -> ShaderBuilder {
842        Default::default()
843    }
844
845    #[profiling::function]
846    pub(super) fn descriptor_bindings(&self) -> DescriptorBindingMap {
847        let mut res = DescriptorBindingMap::default();
848
849        for (name, descriptor, desc_ty, binding_count) in
850            self.entry_point.vars.iter().filter_map(|var| match var {
851                Variable::Descriptor {
852                    name,
853                    desc_bind,
854                    desc_ty,
855                    nbind,
856                    ..
857                } => Some((
858                    name,
859                    Descriptor {
860                        set: desc_bind.set(),
861                        binding: desc_bind.bind(),
862                    },
863                    desc_ty,
864                    *nbind,
865                )),
866                _ => None,
867            })
868        {
869            trace!(
870                "descriptor {}: {}.{} = {:?}[{}]",
871                name.as_deref().unwrap_or_default(),
872                descriptor.set,
873                descriptor.binding,
874                *desc_ty,
875                binding_count
876            );
877
878            let descriptor_info = match desc_ty {
879                DescriptorType::AccelStruct() => {
880                    DescriptorInfo::AccelerationStructure(binding_count)
881                }
882                DescriptorType::CombinedImageSampler() => {
883                    let (sampler_info, is_manually_defined) =
884                        self.image_sampler(descriptor, name.as_deref().unwrap_or_default());
885
886                    DescriptorInfo::CombinedImageSampler(
887                        binding_count,
888                        sampler_info,
889                        is_manually_defined,
890                    )
891                }
892                DescriptorType::InputAttachment(attachment) => {
893                    DescriptorInfo::InputAttachment(binding_count, *attachment)
894                }
895                DescriptorType::SampledImage() => DescriptorInfo::SampledImage(binding_count),
896                DescriptorType::Sampler() => {
897                    let (sampler_info, is_manually_defined) =
898                        self.image_sampler(descriptor, name.as_deref().unwrap_or_default());
899
900                    DescriptorInfo::Sampler(binding_count, sampler_info, is_manually_defined)
901                }
902                DescriptorType::StorageBuffer(_access_ty) => {
903                    DescriptorInfo::StorageBuffer(binding_count)
904                }
905                DescriptorType::StorageImage(_access_ty) => {
906                    DescriptorInfo::StorageImage(binding_count)
907                }
908                DescriptorType::StorageTexelBuffer(_access_ty) => {
909                    DescriptorInfo::StorageTexelBuffer(binding_count)
910                }
911                DescriptorType::UniformBuffer() => DescriptorInfo::UniformBuffer(binding_count),
912                DescriptorType::UniformTexelBuffer() => {
913                    DescriptorInfo::UniformTexelBuffer(binding_count)
914                }
915            };
916            res.insert(descriptor, (descriptor_info, self.stage));
917        }
918
919        res
920    }
921
922    /// Specifies a shader with the given shader code.
923    ///
924    /// See [`VkShaderModuleCreateInfo`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkShaderModuleCreateInfo.html).
925    pub fn from_spirv(spirv: impl Into<SpirvBinary>) -> ShaderBuilder {
926        ShaderBuilder::default().spirv(spirv)
927    }
928
929    fn image_sampler(&self, descriptor: Descriptor, name: &str) -> (SamplerInfo, bool) {
930        self.image_samplers
931            .get(&descriptor)
932            .copied()
933            .map(|sampler_info| (sampler_info, true))
934            .unwrap_or_else(|| (guess_immutable_sampler(name), false))
935    }
936
937    #[profiling::function]
938    pub(super) fn merge_descriptor_bindings(
939        descriptor_bindings: impl IntoIterator<Item = DescriptorBindingMap>,
940    ) -> Result<DescriptorBindingMap, DriverError> {
941        fn merge_info(lhs: &mut DescriptorInfo, rhs: DescriptorInfo) -> bool {
942            let (lhs_count, rhs_count) = match lhs {
943                DescriptorInfo::AccelerationStructure(lhs) => {
944                    if let DescriptorInfo::AccelerationStructure(rhs) = rhs {
945                        (lhs, rhs)
946                    } else {
947                        return false;
948                    }
949                }
950                DescriptorInfo::CombinedImageSampler(lhs, lhs_sampler, lhs_is_manually_defined) => {
951                    if let DescriptorInfo::CombinedImageSampler(
952                        rhs,
953                        rhs_sampler,
954                        rhs_is_manually_defined,
955                    ) = rhs
956                    {
957                        // Allow one of the samplers to be manually defined (only one!)
958                        if *lhs_is_manually_defined && rhs_is_manually_defined {
959                            return false;
960                        } else if rhs_is_manually_defined {
961                            *lhs_sampler = rhs_sampler;
962                        }
963
964                        (lhs, rhs)
965                    } else {
966                        return false;
967                    }
968                }
969                DescriptorInfo::InputAttachment(lhs, lhs_idx) => {
970                    if let DescriptorInfo::InputAttachment(rhs, rhs_idx) = rhs {
971                        if *lhs_idx != rhs_idx {
972                            return false;
973                        }
974
975                        (lhs, rhs)
976                    } else {
977                        return false;
978                    }
979                }
980                DescriptorInfo::SampledImage(lhs) => {
981                    if let DescriptorInfo::SampledImage(rhs) = rhs {
982                        (lhs, rhs)
983                    } else {
984                        return false;
985                    }
986                }
987                DescriptorInfo::Sampler(lhs, lhs_sampler, lhs_is_manually_defined) => {
988                    if let DescriptorInfo::Sampler(rhs, rhs_sampler, rhs_is_manually_defined) = rhs
989                    {
990                        // Allow one of the samplers to be manually defined (only one!)
991                        if *lhs_is_manually_defined && rhs_is_manually_defined {
992                            return false;
993                        } else if rhs_is_manually_defined {
994                            *lhs_sampler = rhs_sampler;
995                        }
996
997                        (lhs, rhs)
998                    } else {
999                        return false;
1000                    }
1001                }
1002                DescriptorInfo::StorageBuffer(lhs) => {
1003                    if let DescriptorInfo::StorageBuffer(rhs) = rhs {
1004                        (lhs, rhs)
1005                    } else {
1006                        return false;
1007                    }
1008                }
1009                DescriptorInfo::StorageImage(lhs) => {
1010                    if let DescriptorInfo::StorageImage(rhs) = rhs {
1011                        (lhs, rhs)
1012                    } else {
1013                        return false;
1014                    }
1015                }
1016                DescriptorInfo::StorageTexelBuffer(lhs) => {
1017                    if let DescriptorInfo::StorageTexelBuffer(rhs) = rhs {
1018                        (lhs, rhs)
1019                    } else {
1020                        return false;
1021                    }
1022                }
1023                DescriptorInfo::UniformBuffer(lhs) => {
1024                    if let DescriptorInfo::UniformBuffer(rhs) = rhs {
1025                        (lhs, rhs)
1026                    } else {
1027                        return false;
1028                    }
1029                }
1030                DescriptorInfo::UniformTexelBuffer(lhs) => {
1031                    if let DescriptorInfo::UniformTexelBuffer(rhs) = rhs {
1032                        (lhs, rhs)
1033                    } else {
1034                        return false;
1035                    }
1036                }
1037            };
1038
1039            *lhs_count = rhs_count.max(*lhs_count);
1040
1041            true
1042        }
1043
1044        #[profiling::function]
1045        fn merge_pair(
1046            src: DescriptorBindingMap,
1047            dst: &mut DescriptorBindingMap,
1048        ) -> Result<(), DriverError> {
1049            for (descriptor_binding, (descriptor_info, descriptor_flags)) in src.into_iter() {
1050                if let Some((existing_info, existing_flags)) = dst.get_mut(&descriptor_binding) {
1051                    if !merge_info(existing_info, descriptor_info) {
1052                        warn!("inconsistent shader descriptors ({descriptor_binding:?})");
1053
1054                        return Err(DriverError::InvalidData);
1055                    }
1056
1057                    *existing_flags |= descriptor_flags;
1058                } else {
1059                    dst.insert(descriptor_binding, (descriptor_info, descriptor_flags));
1060                }
1061            }
1062
1063            Ok(())
1064        }
1065
1066        let mut descriptor_bindings = descriptor_bindings.into_iter();
1067        let mut res = descriptor_bindings.next().unwrap_or_default();
1068        for descriptor_binding in descriptor_bindings {
1069            merge_pair(descriptor_binding, &mut res)?;
1070        }
1071
1072        Ok(res)
1073    }
1074
1075    #[profiling::function]
1076    pub(super) fn push_constant_range(&self) -> Option<vk::PushConstantRange> {
1077        self.entry_point
1078            .vars
1079            .iter()
1080            .filter_map(|var| match var {
1081                Variable::PushConstant {
1082                    ty: Type::Struct(ty),
1083                    ..
1084                } => Some(ty.members.clone()),
1085                _ => None,
1086            })
1087            .flatten()
1088            .map(|push_const| {
1089                let offset = push_const.offset.unwrap_or_default();
1090                let size = push_const
1091                    .ty
1092                    .nbyte()
1093                    .unwrap_or_default()
1094                    .next_multiple_of(4);
1095                offset..offset + size
1096            })
1097            .reduce(|a, b| a.start.min(b.start)..a.end.max(b.end))
1098            .map(|push_const| vk::PushConstantRange {
1099                stage_flags: self.stage,
1100                size: (push_const.end - push_const.start) as _,
1101                offset: push_const.start as _,
1102            })
1103    }
1104
1105    #[profiling::function]
1106    fn reflect_entry_point(
1107        entry_name: &str,
1108        spirv: impl Into<SpirvBinary>,
1109        specialization: Option<&SpecializationMap>,
1110    ) -> Result<EntryPoint, DriverError> {
1111        /*
1112        spirq can panic on malformed SPIR-V instead of returning Err, for example:
1113        `range start index 5 out of range for slice of length 0` from spirq/src/parse/bin.rs
1114        */
1115        catch_unwind(AssertUnwindSafe(|| {
1116            Self::reflect_entry_point_unchecked(entry_name, spirv, specialization)
1117        }))
1118        .map_err(|_| {
1119            warn!("invalid shader reflection entry point: panic");
1120
1121            DriverError::InvalidData
1122        })?
1123        .map_err(|err| {
1124            warn!("invalid shader reflection entry point: {err}");
1125
1126            DriverError::InvalidData
1127        })
1128    }
1129
1130    #[profiling::function]
1131    fn reflect_entry_point_unchecked(
1132        entry_name: &str,
1133        spirv: impl Into<SpirvBinary>,
1134        specialization: Option<&SpecializationMap>,
1135    ) -> Result<EntryPoint, DriverError> {
1136        let mut config = ReflectConfig::new();
1137        config.ref_all_rscs(true).spv(spirv);
1138
1139        if let Some(specialization) = specialization {
1140            for &vk::SpecializationMapEntry {
1141                constant_id,
1142                offset,
1143                size,
1144            } in &specialization.entries
1145            {
1146                config.specialize(
1147                    constant_id,
1148                    specialization.data[offset as usize..offset as usize + size].into(),
1149                );
1150            }
1151        }
1152
1153        let entry_points = config.reflect().map_err(|err| {
1154            error!("invalid SPIR-V reflection data: {err}");
1155
1156            DriverError::InvalidData
1157        })?;
1158        let entry_point = entry_points
1159            .into_iter()
1160            .find(|entry_point| entry_point.name == entry_name)
1161            .ok_or_else(|| {
1162                error!("invalid shader entry point: not found");
1163
1164                DriverError::InvalidData
1165            })?;
1166
1167        Ok(entry_point)
1168    }
1169
1170    #[profiling::function]
1171    pub(super) fn try_vertex_input(&self) -> Result<VertexInputState, DriverError> {
1172        // Check for manually-specified vertex layout descriptions
1173        if let Some(vertex_input) = &self.vertex_input_state {
1174            return Ok(vertex_input.clone());
1175        }
1176
1177        fn scalar_format(ty: &ScalarType) -> Option<vk::Format> {
1178            match *ty {
1179                ScalarType::Float { bits } => match bits {
1180                    u8::BITS => Some(vk::Format::R8_SNORM),
1181                    u16::BITS => Some(vk::Format::R16_SFLOAT),
1182                    u32::BITS => Some(vk::Format::R32_SFLOAT),
1183                    u64::BITS => Some(vk::Format::R64_SFLOAT),
1184                    _ => None,
1185                },
1186                ScalarType::Integer {
1187                    bits,
1188                    is_signed: false,
1189                } => match bits {
1190                    u8::BITS => Some(vk::Format::R8_UINT),
1191                    u16::BITS => Some(vk::Format::R16_UINT),
1192                    u32::BITS => Some(vk::Format::R32_UINT),
1193                    u64::BITS => Some(vk::Format::R64_UINT),
1194                    _ => None,
1195                },
1196                ScalarType::Integer {
1197                    bits,
1198                    is_signed: true,
1199                } => match bits {
1200                    u8::BITS => Some(vk::Format::R8_SINT),
1201                    u16::BITS => Some(vk::Format::R16_SINT),
1202                    u32::BITS => Some(vk::Format::R32_SINT),
1203                    u64::BITS => Some(vk::Format::R64_SINT),
1204                    _ => None,
1205                },
1206                _ => None,
1207            }
1208        }
1209
1210        fn vector_format(ty: &VectorType) -> Option<vk::Format> {
1211            match *ty {
1212                VectorType {
1213                    scalar_ty: ScalarType::Float { bits },
1214                    nscalar,
1215                } => match (bits, nscalar) {
1216                    (u8::BITS, 2) => Some(vk::Format::R8G8_SNORM),
1217                    (u8::BITS, 3) => Some(vk::Format::R8G8B8_SNORM),
1218                    (u8::BITS, 4) => Some(vk::Format::R8G8B8A8_SNORM),
1219                    (u16::BITS, 2) => Some(vk::Format::R16G16_SFLOAT),
1220                    (u16::BITS, 3) => Some(vk::Format::R16G16B16_SFLOAT),
1221                    (u16::BITS, 4) => Some(vk::Format::R16G16B16A16_SFLOAT),
1222                    (u32::BITS, 2) => Some(vk::Format::R32G32_SFLOAT),
1223                    (u32::BITS, 3) => Some(vk::Format::R32G32B32_SFLOAT),
1224                    (u32::BITS, 4) => Some(vk::Format::R32G32B32A32_SFLOAT),
1225                    (u64::BITS, 2) => Some(vk::Format::R64G64_SFLOAT),
1226                    (u64::BITS, 3) => Some(vk::Format::R64G64B64_SFLOAT),
1227                    (u64::BITS, 4) => Some(vk::Format::R64G64B64A64_SFLOAT),
1228                    _ => None,
1229                },
1230                VectorType {
1231                    scalar_ty:
1232                        ScalarType::Integer {
1233                            bits,
1234                            is_signed: false,
1235                        },
1236                    nscalar,
1237                } => match (bits, nscalar) {
1238                    (u8::BITS, 2) => Some(vk::Format::R8G8_UINT),
1239                    (u8::BITS, 3) => Some(vk::Format::R8G8B8_UINT),
1240                    (u8::BITS, 4) => Some(vk::Format::R8G8B8A8_UINT),
1241                    (u16::BITS, 2) => Some(vk::Format::R16G16_UINT),
1242                    (u16::BITS, 3) => Some(vk::Format::R16G16B16_UINT),
1243                    (u16::BITS, 4) => Some(vk::Format::R16G16B16A16_UINT),
1244                    (u32::BITS, 2) => Some(vk::Format::R32G32_UINT),
1245                    (u32::BITS, 3) => Some(vk::Format::R32G32B32_UINT),
1246                    (u32::BITS, 4) => Some(vk::Format::R32G32B32A32_UINT),
1247                    (u64::BITS, 2) => Some(vk::Format::R64G64_UINT),
1248                    (u64::BITS, 3) => Some(vk::Format::R64G64B64_UINT),
1249                    (u64::BITS, 4) => Some(vk::Format::R64G64B64A64_UINT),
1250                    _ => None,
1251                },
1252                VectorType {
1253                    scalar_ty:
1254                        ScalarType::Integer {
1255                            bits,
1256                            is_signed: true,
1257                        },
1258                    nscalar,
1259                } => match (bits, nscalar) {
1260                    (u8::BITS, 2) => Some(vk::Format::R8G8_SINT),
1261                    (u8::BITS, 3) => Some(vk::Format::R8G8B8_SINT),
1262                    (u8::BITS, 4) => Some(vk::Format::R8G8B8A8_SINT),
1263                    (u16::BITS, 2) => Some(vk::Format::R16G16_SINT),
1264                    (u16::BITS, 3) => Some(vk::Format::R16G16B16_SINT),
1265                    (u16::BITS, 4) => Some(vk::Format::R16G16B16A16_SINT),
1266                    (u32::BITS, 2) => Some(vk::Format::R32G32_SINT),
1267                    (u32::BITS, 3) => Some(vk::Format::R32G32B32_SINT),
1268                    (u32::BITS, 4) => Some(vk::Format::R32G32B32A32_SINT),
1269                    (u64::BITS, 2) => Some(vk::Format::R64G64_SINT),
1270                    (u64::BITS, 3) => Some(vk::Format::R64G64B64_SINT),
1271                    (u64::BITS, 4) => Some(vk::Format::R64G64B64A64_SINT),
1272                    _ => None,
1273                },
1274                _ => None,
1275            }
1276        }
1277
1278        let mut input_rates_strides = HashMap::new();
1279        let mut vertex_attribute_descriptions = vec![];
1280
1281        for (name, location, ty) in self.entry_point.vars.iter().filter_map(|var| match var {
1282            Variable::Input { name, location, ty } => Some((name, location, ty)),
1283            _ => None,
1284        }) {
1285            let (binding, guessed_rate) = name
1286                .as_ref()
1287                .filter(|name| name.contains("_ibind") || name.contains("_vbind"))
1288                .map(|name| {
1289                    let binding = name[name.rfind("bind").expect("missing bind suffix")..]
1290                        .parse()
1291                        .unwrap_or_default();
1292                    let rate = if name.contains("_ibind") {
1293                        vk::VertexInputRate::INSTANCE
1294                    } else {
1295                        vk::VertexInputRate::VERTEX
1296                    };
1297
1298                    (binding, rate)
1299                })
1300                .unwrap_or_default();
1301            let (location, _) = location.into_inner();
1302            if let Some((input_rate, _)) = input_rates_strides.get(&binding) {
1303                assert_eq!(*input_rate, guessed_rate);
1304            }
1305
1306            let byte_stride = ty.nbyte().unwrap_or_default() as u32;
1307            let (input_rate, stride) = input_rates_strides.entry(binding).or_default();
1308            *input_rate = guessed_rate;
1309            *stride += byte_stride;
1310
1311            //trace!("{location} {:?} is {byte_stride} bytes", name);
1312
1313            let format = match ty {
1314                Type::Scalar(ty) => scalar_format(ty),
1315                Type::Vector(ty) => vector_format(ty),
1316                _ => None,
1317            }
1318            .ok_or_else(|| {
1319                warn!("unsupported reflected vertex input type: {ty:?}");
1320
1321                DriverError::Unsupported
1322            })?;
1323
1324            vertex_attribute_descriptions.push(vk::VertexInputAttributeDescription {
1325                location,
1326                binding,
1327                format,
1328                offset: byte_stride,
1329            });
1330        }
1331
1332        vertex_attribute_descriptions.sort_unstable_by(|lhs, rhs| {
1333            let binding = lhs.binding.cmp(&rhs.binding);
1334            if binding.is_lt() {
1335                return binding;
1336            }
1337
1338            lhs.location.cmp(&rhs.location)
1339        });
1340
1341        let mut offset = 0;
1342        let mut offset_binding = 0;
1343
1344        for vertex_attribute_description in &mut vertex_attribute_descriptions {
1345            if vertex_attribute_description.binding != offset_binding {
1346                offset_binding = vertex_attribute_description.binding;
1347                offset = 0;
1348            }
1349
1350            let stride = vertex_attribute_description.offset;
1351            vertex_attribute_description.offset = offset;
1352            offset += stride;
1353
1354            debug!(
1355                "vertex attribute {}.{}: {:?} (offset={})",
1356                vertex_attribute_description.binding,
1357                vertex_attribute_description.location,
1358                vertex_attribute_description.format,
1359                vertex_attribute_description.offset,
1360            );
1361        }
1362
1363        let mut vertex_binding_descriptions = vec![];
1364        for (binding, (input_rate, stride)) in input_rates_strides.into_iter() {
1365            vertex_binding_descriptions.push(vk::VertexInputBindingDescription {
1366                binding,
1367                input_rate,
1368                stride,
1369            });
1370        }
1371
1372        Ok(VertexInputState {
1373            vertex_attribute_descriptions,
1374            vertex_binding_descriptions,
1375        })
1376    }
1377
1378    #[profiling::function]
1379    pub(super) fn vertex_input(&self) -> VertexInputState {
1380        self.try_vertex_input()
1381            .expect("unsupported reflected vertex input layout")
1382    }
1383}
1384
1385impl Debug for Shader {
1386    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1387        // Avoid dumping the embedded SPIR-V bytes in debug output.
1388        f.debug_struct(stringify!(Shader))
1389            .field("entry_name", &self.entry_name)
1390            .field("stage", &self.stage)
1391            .field("specialization", &self.specialization)
1392            .field("reflected_vars", &self.entry_point.vars.len())
1393            .finish_non_exhaustive()
1394    }
1395}
1396
1397impl TryFrom<ShaderBuilder> for Shader {
1398    type Error = DriverError;
1399
1400    fn try_from(shader: ShaderBuilder) -> Result<Self, Self::Error> {
1401        shader.try_build()
1402    }
1403}
1404
1405impl TryFrom<&[u8]> for Shader {
1406    type Error = DriverError;
1407
1408    fn try_from(spirv: &[u8]) -> Result<Self, Self::Error> {
1409        Shader::from_spirv(spirv).try_build()
1410    }
1411}
1412
1413impl TryFrom<&[u32]> for Shader {
1414    type Error = DriverError;
1415
1416    fn try_from(spirv: &[u32]) -> Result<Self, Self::Error> {
1417        Shader::from_spirv(spirv).try_build()
1418    }
1419}
1420
1421impl TryFrom<Vec<u8>> for Shader {
1422    type Error = DriverError;
1423
1424    fn try_from(spirv: Vec<u8>) -> Result<Self, Self::Error> {
1425        Shader::from_spirv(spirv).try_build()
1426    }
1427}
1428
1429impl TryFrom<Vec<u32>> for Shader {
1430    type Error = DriverError;
1431
1432    fn try_from(spirv: Vec<u32>) -> Result<Self, Self::Error> {
1433        Shader::from_spirv(spirv).try_build()
1434    }
1435}
1436
1437// HACK: https://github.com/colin-kiegel/rust-derive-builder/issues/56
1438impl ShaderBuilder {
1439    /// Specifies a shader with the given `stage` and shader code values.
1440    pub fn new(stage: vk::ShaderStageFlags, spirv: Vec<u8>) -> Self {
1441        Self::default().stage(stage).spirv(spirv)
1442    }
1443
1444    /// Builds a new `Shader`.
1445    ///
1446    /// # Panics
1447    ///
1448    /// Panics if the SPIR-V is invalid. Prefer [`try_build`](Self::try_build) for a fallible path.
1449    pub fn build(self) -> Shader {
1450        self.try_build()
1451            .unwrap_or_else(|err| panic!("invalid or unsupported shader code: {err}"))
1452    }
1453
1454    /// Specifies a manually-defined image sampler.
1455    ///
1456    /// Sampled images, by default, use reflection to automatically assign image samplers. Each
1457    /// sampled image may use a suffix such as `_llr` or `_nne` for common linear/linear repeat or
1458    /// nearest/nearest clamp-to-edge samplers, respectively.
1459    ///
1460    /// See the [main documentation] for more information about automatic image samplers.
1461    ///
1462    /// Descriptor bindings may be specified as `(1, 2)` for descriptor set index `1` and binding
1463    /// index `2`, or if the descriptor set index is `0` simply specify `2` for the same case.
1464    ///
1465    /// _NOTE:_ When defining image samplers which are used in multiple stages of a single pipeline
1466    /// you must only call this function on one of the shader stages; it does not matter which one.
1467    ///
1468    /// # Panics
1469    ///
1470    /// Panics if two shader stages of the same pipeline define individual calls to `image_sampler`.
1471    ///
1472    /// [main documentation]: crate
1473    #[profiling::function]
1474    pub fn image_sampler(
1475        mut self,
1476        descriptor: impl Into<Descriptor>,
1477        info: impl Into<SamplerInfo>,
1478    ) -> Self {
1479        let descriptor = descriptor.into();
1480        let info = info.into();
1481
1482        if self.image_samplers.is_none() {
1483            self.image_samplers = Some(Default::default());
1484        }
1485
1486        self.image_samplers
1487            .as_mut()
1488            .expect("missing image samplers")
1489            .insert(descriptor, info);
1490
1491        self
1492    }
1493
1494    /// Attempts to build a new `Shader`.
1495    pub fn try_build(mut self) -> Result<Shader, DriverError> {
1496        let entry_name = self.entry_name.as_deref().unwrap_or("main");
1497        let spirv = self
1498            .spirv
1499            .as_ref()
1500            .map(|spirv| spirv.words())
1501            .ok_or(DriverError::InvalidData)?;
1502        let specialization = self
1503            .specialization
1504            .as_ref()
1505            .map(|opt| opt.as_ref())
1506            .unwrap_or_default();
1507        let entry_point = Shader::reflect_entry_point(entry_name, spirv, specialization)?;
1508
1509        if self.stage.unwrap_or_default().is_empty() {
1510            self.stage = Some(match entry_point.exec_model {
1511                ExecutionModel::Vertex => vk::ShaderStageFlags::VERTEX,
1512                ExecutionModel::TessellationControl => vk::ShaderStageFlags::TESSELLATION_CONTROL,
1513                ExecutionModel::TessellationEvaluation => {
1514                    vk::ShaderStageFlags::TESSELLATION_EVALUATION
1515                }
1516                ExecutionModel::Geometry => vk::ShaderStageFlags::GEOMETRY,
1517                ExecutionModel::Fragment => vk::ShaderStageFlags::FRAGMENT,
1518                ExecutionModel::GLCompute => vk::ShaderStageFlags::COMPUTE,
1519                ExecutionModel::Kernel => {
1520                    warn!("unsupported shader execution model: kernel");
1521
1522                    return Err(DriverError::Unsupported);
1523                }
1524                ExecutionModel::TaskNV => vk::ShaderStageFlags::TASK_EXT,
1525                ExecutionModel::MeshNV => vk::ShaderStageFlags::MESH_EXT,
1526                ExecutionModel::RayGenerationNV => vk::ShaderStageFlags::RAYGEN_KHR,
1527                ExecutionModel::IntersectionNV => vk::ShaderStageFlags::INTERSECTION_KHR,
1528                ExecutionModel::AnyHitNV => vk::ShaderStageFlags::ANY_HIT_KHR,
1529                ExecutionModel::ClosestHitNV => vk::ShaderStageFlags::CLOSEST_HIT_KHR,
1530                ExecutionModel::MissNV => vk::ShaderStageFlags::MISS_KHR,
1531                ExecutionModel::CallableNV => vk::ShaderStageFlags::CALLABLE_KHR,
1532                ExecutionModel::TaskEXT => vk::ShaderStageFlags::TASK_EXT,
1533                ExecutionModel::MeshEXT => vk::ShaderStageFlags::MESH_EXT,
1534            })
1535        }
1536
1537        self.entry_point = Some(entry_point);
1538
1539        self.fallible_build().map_err(|err| {
1540            warn!("invalid shader builder state: {err:?}");
1541
1542            DriverError::InvalidData
1543        })
1544    }
1545
1546    /// Specifies a manually-defined vertex input layout.
1547    ///
1548    /// The vertex input layout, by default, uses reflection to automatically define vertex binding
1549    /// and attribute descriptions. Each vertex location is inferred to have 32-bit channels and be
1550    /// tightly packed in the vertex buffer. In this mode, a location with `_ibind_0` or `_vbind3`
1551    /// suffixes is inferred to use instance-rate on vertex buffer binding `0` or vertex rate on
1552    /// binding `3`, respectively.
1553    ///
1554    /// See the [main documentation] for more information about automatic vertex input layout.
1555    ///
1556    /// [main documentation]: crate
1557    #[profiling::function]
1558    pub fn vertex_input(
1559        mut self,
1560        bindings: impl Into<Vec<vk::VertexInputBindingDescription>>,
1561        attributes: impl Into<Vec<vk::VertexInputAttributeDescription>>,
1562    ) -> Self {
1563        self.vertex_input_state = Some(Some(VertexInputState {
1564            vertex_binding_descriptions: bindings.into(),
1565            vertex_attribute_descriptions: attributes.into(),
1566        }));
1567        self
1568    }
1569}
1570
1571#[derive(Debug)]
1572struct ShaderBuilderError;
1573
1574impl From<UninitializedFieldError> for ShaderBuilderError {
1575    fn from(_: UninitializedFieldError) -> Self {
1576        Self
1577    }
1578}
1579
1580/// Describes specialized constant values.
1581#[derive(Clone, Debug, Default)]
1582pub struct SpecializationMap {
1583    /// A buffer of data which holds the constant values.
1584    pub data: Vec<u8>,
1585
1586    /// Mapping of locations within the constant value data which describe each individual
1587    /// constant.
1588    pub entries: Vec<vk::SpecializationMapEntry>,
1589}
1590
1591impl SpecializationMap {
1592    /// Constructs a new `SpecializationMap`.
1593    pub fn new(data: impl Into<Vec<u8>>) -> Self {
1594        Self {
1595            data: data.into(),
1596            entries: Default::default(),
1597        }
1598    }
1599
1600    /// Adds a single constant offset and size to the map and returns the map for further building.
1601    pub fn constant(mut self, constant_id: u32, offset: u32, size: usize) -> Self {
1602        self.set_constant(constant_id, offset, size);
1603        self
1604    }
1605
1606    /// Adds a single constant offset and size to the map and returns the map for further building.
1607    pub fn set_constant(&mut self, constant_id: u32, offset: u32, size: usize) {
1608        self.entries.push(vk::SpecializationMapEntry {
1609            constant_id,
1610            offset,
1611            size,
1612        });
1613    }
1614}
1615
1616impl<'a> From<&'a SpecializationMap> for vk::SpecializationInfo<'a> {
1617    fn from(value: &'a SpecializationMap) -> Self {
1618        vk::SpecializationInfo::default()
1619            .map_entries(&value.entries)
1620            .data(&value.data)
1621    }
1622}
1623
1624#[cfg(test)]
1625mod test {
1626    use super::*;
1627
1628    type Info = SamplerInfo;
1629    type Builder = SamplerInfoBuilder;
1630
1631    #[test]
1632    pub fn sampler_info() {
1633        let info = Info::default();
1634        let builder = info.into_builder().build();
1635
1636        assert_eq!(info, builder);
1637    }
1638
1639    #[test]
1640    pub fn sampler_info_builder() {
1641        let info = Info::default();
1642        let builder = Builder::default().build();
1643
1644        assert_eq!(info, builder);
1645    }
1646
1647    #[test]
1648    pub fn invalid_spirv_try_into_driver_value() {
1649        assert!(Shader::try_from(vec![0u32]).is_err());
1650    }
1651
1652    #[test]
1653    pub fn merge_descriptor_bindings_rejects_mismatched_descriptors() {
1654        let mut lhs = DescriptorBindingMap::default();
1655        lhs.insert(
1656            Descriptor::from(0),
1657            (
1658                DescriptorInfo::UniformBuffer(1),
1659                vk::ShaderStageFlags::VERTEX,
1660            ),
1661        );
1662
1663        let mut rhs = DescriptorBindingMap::default();
1664        rhs.insert(
1665            Descriptor::from(0),
1666            (
1667                DescriptorInfo::StorageBuffer(1),
1668                vk::ShaderStageFlags::FRAGMENT,
1669            ),
1670        );
1671
1672        assert!(matches!(
1673            Shader::merge_descriptor_bindings([lhs, rhs]),
1674            Err(DriverError::InvalidData)
1675        ));
1676    }
1677}