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