Skip to main content

vk_graph/driver/
graphics.rs

1//! Graphics pipeline types
2
3use {
4    super::{
5        DriverError,
6        device::Device,
7        image::SampleCount,
8        merge_push_constant_ranges,
9        shader::{DescriptorBindingMap, PipelineDescriptorInfo, Shader, SpecializationMap},
10    },
11    crate::{driver::DescriptorSetLayout, lazy_str},
12    ash::vk,
13    derive_builder::Builder,
14    log::{Level::Trace, log_enabled, trace, warn},
15    ordered_float::OrderedFloat,
16    std::{
17        collections::HashSet,
18        ffi::CString,
19        fmt::{Debug, Formatter},
20        hash::{Hash, Hasher},
21        sync::Arc,
22        thread::panicking,
23    },
24};
25
26const RGBA_COLOR_COMPONENTS: vk::ColorComponentFlags = vk::ColorComponentFlags::from_raw(
27    vk::ColorComponentFlags::R.as_raw()
28        | vk::ColorComponentFlags::G.as_raw()
29        | vk::ColorComponentFlags::B.as_raw()
30        | vk::ColorComponentFlags::A.as_raw(),
31);
32
33/// Specifies color blend state used when rasterization is enabled for any color attachments
34/// accessed during rendering.
35///
36/// See [`VkPipelineColorBlendAttachmentState`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkPipelineColorBlendAttachmentState.html).
37#[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)]
38#[builder(
39    build_fn(private, name = "fallible_build"),
40    derive(Clone, Copy, Debug),
41    pattern = "owned"
42)]
43pub struct BlendInfo {
44    /// Controls whether blending is enabled for the corresponding color attachment.
45    ///
46    /// If blending is not enabled, the source fragment’s color for that attachment is passed
47    /// through unmodified.
48    #[builder(default = "false")]
49    pub blend_enable: bool,
50
51    /// Selects which blend factor is used to determine the source factors.
52    #[builder(default = "vk::BlendFactor::SRC_COLOR")]
53    pub src_color_blend_factor: vk::BlendFactor,
54
55    /// Selects which blend factor is used to determine the destination factors.
56    #[builder(default = "vk::BlendFactor::ONE_MINUS_DST_COLOR")]
57    pub dst_color_blend_factor: vk::BlendFactor,
58
59    /// Selects which blend operation is used to calculate the RGB values to write to the color
60    /// attachment.
61    #[builder(default = "vk::BlendOp::ADD")]
62    pub color_blend_op: vk::BlendOp,
63
64    /// Selects which blend factor is used to determine the source factor.
65    #[builder(default = "vk::BlendFactor::ZERO")]
66    pub src_alpha_blend_factor: vk::BlendFactor,
67
68    /// Selects which blend factor is used to determine the destination factor.
69    #[builder(default = "vk::BlendFactor::ZERO")]
70    pub dst_alpha_blend_factor: vk::BlendFactor,
71
72    /// Selects which blend operation is used to calculate the alpha values to write to the color
73    /// attachment.
74    #[builder(default = "vk::BlendOp::ADD")]
75    pub alpha_blend_op: vk::BlendOp,
76
77    /// A bitmask specifying which of the R, G, B, and/or A components are enabled for writing,
78    /// as described for [`VkPipelineColorBlendAttachmentState`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkPipelineColorBlendAttachmentState.html).
79    #[builder(default = "RGBA_COLOR_COMPONENTS")]
80    pub color_write_mask: vk::ColorComponentFlags,
81}
82
83impl BlendInfo {
84    /// A commonly used blend mode for additive blending.
85    pub const ADDITIVE: Self = Self {
86        blend_enable: true,
87        src_color_blend_factor: vk::BlendFactor::ONE,
88        dst_color_blend_factor: vk::BlendFactor::ONE,
89        color_blend_op: vk::BlendOp::ADD,
90        src_alpha_blend_factor: vk::BlendFactor::ONE,
91        dst_alpha_blend_factor: vk::BlendFactor::ONE,
92        alpha_blend_op: vk::BlendOp::ADD,
93        color_write_mask: RGBA_COLOR_COMPONENTS,
94    };
95
96    /// A commonly used blend mode for replacing color attachment values with new ones.
97    pub const REPLACE: Self = Self {
98        blend_enable: false,
99        src_color_blend_factor: vk::BlendFactor::SRC_COLOR,
100        dst_color_blend_factor: vk::BlendFactor::ONE_MINUS_DST_COLOR,
101        color_blend_op: vk::BlendOp::ADD,
102        src_alpha_blend_factor: vk::BlendFactor::ZERO,
103        dst_alpha_blend_factor: vk::BlendFactor::ZERO,
104        alpha_blend_op: vk::BlendOp::ADD,
105        color_write_mask: RGBA_COLOR_COMPONENTS,
106    };
107
108    /// A commonly used blend mode for blending color attachment values based on the alpha channel.
109    pub const ALPHA: Self = Self {
110        blend_enable: true,
111        src_color_blend_factor: vk::BlendFactor::SRC_ALPHA,
112        dst_color_blend_factor: vk::BlendFactor::ONE_MINUS_SRC_ALPHA,
113        color_blend_op: vk::BlendOp::ADD,
114        src_alpha_blend_factor: vk::BlendFactor::SRC_ALPHA,
115        dst_alpha_blend_factor: vk::BlendFactor::ONE_MINUS_SRC_ALPHA,
116        alpha_blend_op: vk::BlendOp::ADD,
117        color_write_mask: RGBA_COLOR_COMPONENTS,
118    };
119
120    /// A color attachment state that disables all color component writes.
121    ///
122    /// This is useful for passes that bind a color attachment only to satisfy pipeline or render
123    /// target layout requirements, while writing depth or stencil data without modifying color.
124    pub const COLOR_WRITE_DISABLED: Self = Self {
125        blend_enable: false,
126        src_color_blend_factor: vk::BlendFactor::SRC_COLOR,
127        dst_color_blend_factor: vk::BlendFactor::ONE_MINUS_DST_COLOR,
128        color_blend_op: vk::BlendOp::ADD,
129        src_alpha_blend_factor: vk::BlendFactor::ZERO,
130        dst_alpha_blend_factor: vk::BlendFactor::ZERO,
131        alpha_blend_op: vk::BlendOp::ADD,
132        color_write_mask: vk::ColorComponentFlags::empty(),
133    };
134
135    /// A commonly used blend mode for blending color attachment values based on the alpha channel,
136    /// where the color components have been pre-multiplied with the alpha component value.
137    pub const PRE_MULTIPLIED_ALPHA: Self = Self {
138        blend_enable: true,
139        src_color_blend_factor: vk::BlendFactor::SRC_ALPHA,
140        dst_color_blend_factor: vk::BlendFactor::ONE_MINUS_SRC_ALPHA,
141        color_blend_op: vk::BlendOp::ADD,
142        src_alpha_blend_factor: vk::BlendFactor::ONE,
143        dst_alpha_blend_factor: vk::BlendFactor::ONE,
144        alpha_blend_op: vk::BlendOp::ADD,
145        color_write_mask: RGBA_COLOR_COMPONENTS,
146    };
147
148    /// Specifies a default blend mode which is not enabled.
149    pub fn builder() -> BlendInfoBuilder {
150        BlendInfoBuilder::default()
151    }
152
153    /// Converts a `BlendInfo` into a `BlendInfoBuilder`.
154    pub fn into_builder(self) -> BlendInfoBuilder {
155        BlendInfoBuilder {
156            blend_enable: Some(self.blend_enable),
157            src_color_blend_factor: Some(self.src_color_blend_factor),
158            dst_color_blend_factor: Some(self.dst_color_blend_factor),
159            color_blend_op: Some(self.color_blend_op),
160            src_alpha_blend_factor: Some(self.src_alpha_blend_factor),
161            dst_alpha_blend_factor: Some(self.dst_alpha_blend_factor),
162            alpha_blend_op: Some(self.alpha_blend_op),
163            color_write_mask: Some(self.color_write_mask),
164        }
165    }
166}
167
168// the Builder derive Macro wants Default to be implemented for BlendMode
169impl Default for BlendInfo {
170    fn default() -> Self {
171        Self::REPLACE
172    }
173}
174
175impl From<BlendInfo> for vk::PipelineColorBlendAttachmentState {
176    fn from(mode: BlendInfo) -> Self {
177        Self {
178            blend_enable: mode.blend_enable as _,
179            src_color_blend_factor: mode.src_color_blend_factor,
180            dst_color_blend_factor: mode.dst_color_blend_factor,
181            color_blend_op: mode.color_blend_op,
182            src_alpha_blend_factor: mode.src_alpha_blend_factor,
183            dst_alpha_blend_factor: mode.dst_alpha_blend_factor,
184            alpha_blend_op: mode.alpha_blend_op,
185            color_write_mask: mode.color_write_mask,
186        }
187    }
188}
189
190impl BlendInfoBuilder {
191    /// Builds a new `BlendInfo`.
192    pub fn build(self) -> BlendInfo {
193        self.fallible_build().expect("invalid blend info")
194    }
195}
196
197/// Specifies the [depth bounds tests], [stencil test], and [depth test] pipeline state.
198///
199/// See [`VkPipelineDepthStencilStateCreateInfo`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkPipelineDepthStencilStateCreateInfo.html).
200#[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)]
201#[builder(
202    build_fn(private, name = "fallible_build"),
203    derive(Clone, Copy, Debug),
204    pattern = "owned"
205)]
206pub struct DepthStencilInfo {
207    /// Control parameters of the stencil test.
208    ///
209    /// Defaults to [`StencilMode::IGNORE`].
210    #[builder(default)]
211    pub back: StencilMode,
212
213    /// Controls whether [depth bounds testing] is enabled.
214    ///
215    /// See [`VkPipelineDepthStencilStateCreateInfo`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkPipelineDepthStencilStateCreateInfo.html).
216    ///
217    #[builder(default)]
218    pub bounds_test: bool,
219
220    /// A value specifying the comparison operator to use in the [depth comparison] step of the
221    /// [depth test].
222    ///
223    /// See [`VkPipelineDepthStencilStateCreateInfo`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkPipelineDepthStencilStateCreateInfo.html).
224    ///
225    /// Defaults to [`vk::CompareOp::NEVER`].
226    #[builder(default)]
227    pub compare_op: vk::CompareOp,
228
229    /// Controls whether [depth testing] is enabled.
230    ///
231    /// See [`VkPipelineDepthStencilStateCreateInfo`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkPipelineDepthStencilStateCreateInfo.html).
232    ///
233    #[builder(default)]
234    pub depth_test: bool,
235
236    /// Controls whether [depth writes] are enabled when `depth_test` is `true`.
237    ///
238    /// Depth writes are always disabled when `depth_test` is `false`.
239    ///
240    /// See [`VkPipelineDepthStencilStateCreateInfo`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkPipelineDepthStencilStateCreateInfo.html).
241    ///
242    #[builder(default)]
243    pub depth_write: bool,
244
245    /// Control parameters of the stencil test.
246    ///
247    /// Defaults to [`StencilMode::IGNORE`].
248    #[builder(default)]
249    pub front: StencilMode,
250
251    // Note: Using setter(into) so caller does not need our version of OrderedFloat
252    /// Minimum depth bound used in the [depth bounds test].
253    ///
254    /// See [`VkPipelineDepthStencilStateCreateInfo`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkPipelineDepthStencilStateCreateInfo.html).
255    ///
256    #[builder(default, setter(into))]
257    pub min: OrderedFloat<f32>,
258
259    // Note: Using setter(into) so caller does not need our version of OrderedFloat
260    /// Maximum depth bound used in the [depth bounds test].
261    ///
262    /// See [`VkPipelineDepthStencilStateCreateInfo`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkPipelineDepthStencilStateCreateInfo.html).
263    ///
264    #[builder(default, setter(into))]
265    pub max: OrderedFloat<f32>,
266
267    /// Controls whether [stencil testing] is enabled.
268    ///
269    /// See [`VkPipelineDepthStencilStateCreateInfo`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkPipelineDepthStencilStateCreateInfo.html).
270    ///
271    #[builder(default)]
272    pub stencil_test: bool,
273}
274
275impl DepthStencilInfo {
276    /// Specifies a no-depth/no-stencil mode.
277    ///
278    /// This is the default state.
279    pub const IGNORE: Self = Self {
280        back: StencilMode::IGNORE,
281        bounds_test: false,
282        compare_op: vk::CompareOp::NEVER,
283        depth_test: false,
284        depth_write: false,
285        front: StencilMode::IGNORE,
286        min: OrderedFloat(0.0),
287        max: OrderedFloat(0.0),
288        stencil_test: false,
289    };
290
291    /// Creates a depth-read mode with stencil ignored.
292    pub const fn depth_read(compare_op: vk::CompareOp) -> Self {
293        Self {
294            depth_test: true,
295            compare_op,
296            min: OrderedFloat(0.0),
297            max: OrderedFloat(1.0),
298            ..Self::IGNORE
299        }
300    }
301
302    /// Creates a depth-write mode with stencil ignored.
303    pub const fn depth_write(compare_op: vk::CompareOp) -> Self {
304        Self {
305            depth_test: true,
306            depth_write: true,
307            compare_op,
308            min: OrderedFloat(0.0),
309            max: OrderedFloat(1.0),
310            ..Self::IGNORE
311        }
312    }
313
314    /// Creates a depth-read/write mode with stencil ignored.
315    pub const fn depth_read_write(compare_op: vk::CompareOp) -> Self {
316        Self::depth_write(compare_op)
317    }
318
319    /// Common depth-write mode for normal-Z depth buffers.
320    pub const DEPTH_WRITE_LESS: Self = Self::depth_write(vk::CompareOp::LESS);
321
322    /// Common depth-write mode for normal-Z depth buffers when equal depth passes are accepted.
323    pub const DEPTH_WRITE_LESS_OR_EQUAL: Self = Self::depth_write(vk::CompareOp::LESS_OR_EQUAL);
324
325    /// Common depth-write mode for reversed-Z depth buffers.
326    pub const DEPTH_WRITE_GREATER: Self = Self::depth_write(vk::CompareOp::GREATER);
327
328    /// Common depth-write mode for reversed-Z depth buffers when equal depth passes are accepted.
329    pub const DEPTH_WRITE_GREATER_OR_EQUAL: Self =
330        Self::depth_write(vk::CompareOp::GREATER_OR_EQUAL);
331
332    /// A commonly used normal-Z depth-write mode with stencil ignored.
333    pub const DEPTH_WRITE_LESS_IGNORE_STENCIL: Self = Self::DEPTH_WRITE_LESS;
334
335    /// Creates a default `DepthStencilInfoBuilder`.
336    pub fn builder() -> DepthStencilInfoBuilder {
337        Default::default()
338    }
339
340    /// Converts a `DepthStencilInfo` into a `DepthStencilInfoBuilder`.
341    pub fn into_builder(self) -> DepthStencilInfoBuilder {
342        DepthStencilInfoBuilder {
343            back: Some(self.back),
344            bounds_test: Some(self.bounds_test),
345            compare_op: Some(self.compare_op),
346            depth_test: Some(self.depth_test),
347            depth_write: Some(self.depth_write),
348            front: Some(self.front),
349            max: Some(self.max),
350            min: Some(self.min),
351            stencil_test: Some(self.stencil_test),
352        }
353    }
354}
355
356impl Default for DepthStencilInfo {
357    fn default() -> Self {
358        Self::IGNORE
359    }
360}
361
362impl From<DepthStencilInfo> for vk::PipelineDepthStencilStateCreateInfo<'_> {
363    fn from(info: DepthStencilInfo) -> Self {
364        Self::default()
365            .back(info.back.into())
366            .depth_bounds_test_enable(info.bounds_test as _)
367            .depth_compare_op(info.compare_op)
368            .depth_test_enable(info.depth_test as _)
369            .depth_write_enable(info.depth_write as _)
370            .front(info.front.into())
371            .max_depth_bounds(info.max.into_inner())
372            .min_depth_bounds(info.min.into_inner())
373            .stencil_test_enable(info.stencil_test as _)
374    }
375}
376
377impl DepthStencilInfoBuilder {
378    /// Builds a new `DepthStencilInfo`.
379    pub fn build(self) -> DepthStencilInfo {
380        self.fallible_build().expect("invalid depth stencil info")
381    }
382}
383
384/// Opaque representation of a pipeline object.
385///
386/// Also contains information about the object.
387///
388/// See [`VkPipeline`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkPipeline.html).
389#[derive(Clone)]
390#[read_only::cast]
391pub struct GraphicsPipeline {
392    pub(crate) inner: Arc<GraphicsPipelineInner>,
393}
394
395impl GraphicsPipeline {
396    /// Creates a new graphics pipeline on the given device.
397    ///
398    /// The correct pipeline stages will be enabled based on the provided shaders. See [`Shader`]
399    /// for details on all available stages.
400    ///
401    /// See [`VkGraphicsPipelineCreateInfo`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkGraphicsPipelineCreateInfo.html).
402    ///
403    /// `shaders` may contain pre-built [`Shader`] values or any inputs that can be converted into
404    /// them. Invalid shader data is returned as [`DriverError::InvalidData`] through the `Result`
405    /// instead of panicking.
406    ///
407    /// # Examples
408    ///
409    /// Basic usage:
410    ///
411    /// ```no_run
412    /// # use std::sync::Arc;
413    /// # use ash::vk;
414    /// # use vk_graph::driver::DriverError;
415    /// # use vk_graph::driver::device::{Device, DeviceInfo};
416    /// # use vk_graph::driver::graphics::{GraphicsPipeline, GraphicsPipelineInfo};
417    /// # use vk_graph::driver::shader::Shader;
418    /// # fn main() -> Result<(), DriverError> {
419    /// # let device = Device::create(DeviceInfo::default())?;
420    /// # let my_frag_code = [0u8; 1];
421    /// # let my_vert_code = [0u8; 1];
422    /// // shader code is raw SPIR-V code as bytes
423    /// let vert = Shader::new_vertex(my_vert_code.as_slice());
424    /// let frag = Shader::new_fragment(my_frag_code.as_slice());
425    /// let info = GraphicsPipelineInfo::default();
426    /// let pipeline = GraphicsPipeline::create(&device, info, [vert, frag])?;
427    ///
428    /// assert_eq!(pipeline.info().front_face, vk::FrontFace::COUNTER_CLOCKWISE);
429    /// # Ok(()) }
430    /// ```
431    #[profiling::function]
432    pub fn create<S>(
433        device: &Device,
434        info: impl Into<GraphicsPipelineInfo>,
435        shaders: impl IntoIterator<Item = S>,
436    ) -> Result<Self, DriverError>
437    where
438        S: TryInto<Shader>,
439        S::Error: Into<DriverError>,
440    {
441        trace!("create");
442
443        let device = device.clone();
444        let info = info.into();
445        let shaders = shaders
446            .into_iter()
447            .map(|shader| shader.try_into().map_err(Into::into))
448            .collect::<Result<Vec<_>, _>>()?;
449
450        let vertex_input = shaders
451            .iter()
452            .find(|shader| shader.stage == vk::ShaderStageFlags::VERTEX)
453            .ok_or(DriverError::InvalidData)?
454            .try_vertex_input()?;
455
456        // Check for proper stages because Vulkan may not complain but this is invalid.
457        let has_fragment_stage = shaders
458            .iter()
459            .any(|shader| shader.stage.contains(vk::ShaderStageFlags::FRAGMENT));
460        let has_tessellation_stage = shaders.iter().any(|shader| {
461            shader
462                .stage
463                .contains(vk::ShaderStageFlags::TESSELLATION_CONTROL)
464        }) && shaders.iter().any(|shader| {
465            shader
466                .stage
467                .contains(vk::ShaderStageFlags::TESSELLATION_EVALUATION)
468        });
469        let has_geometry_stage = shaders
470            .iter()
471            .any(|shader| shader.stage.contains(vk::ShaderStageFlags::GEOMETRY));
472
473        debug_assert!(
474            has_fragment_stage || has_tessellation_stage || has_geometry_stage,
475            "invalid shader stage combination"
476        );
477
478        let mut descriptor_bindings = Shader::merge_descriptor_bindings(
479            shaders.iter().map(|shader| shader.descriptor_bindings()),
480        )?;
481        let mut bindless_descriptors = HashSet::new();
482        for (descriptor, (descriptor_info, _)) in descriptor_bindings.iter_mut() {
483            if descriptor_info.binding_count() == 0 {
484                bindless_descriptors.insert(*descriptor);
485                descriptor_info.set_binding_count(info.bindless_descriptor_count);
486            }
487        }
488
489        let descriptor_info =
490            PipelineDescriptorInfo::create(&device, &descriptor_bindings, &bindless_descriptors)?;
491        let descriptor_sets_layouts = descriptor_info
492            .layouts
493            .values()
494            .map(DescriptorSetLayout::handle)
495            .collect::<Box<_>>();
496
497        let push_constants = shaders
498            .iter()
499            .map(|shader| shader.push_constant_range())
500            .filter_map(|mut push_const| push_const.take())
501            .collect::<Vec<_>>();
502
503        let input_attachments = shaders
504            .iter()
505            .find(|shader| shader.stage == vk::ShaderStageFlags::FRAGMENT)
506            .map(|shader| {
507                let (input, write) = shader.attachments();
508                let (input, write) = (
509                    input
510                        .collect::<HashSet<_>>()
511                        .into_iter()
512                        .collect::<Box<_>>(),
513                    write.collect::<HashSet<_>>(),
514                );
515
516                if log_enabled!(Trace) {
517                    for input in input.iter() {
518                        trace!("detected input attachment {input}");
519                    }
520
521                    for write in &write {
522                        trace!("detected write attachment {write}");
523                    }
524                }
525
526                input
527            })
528            .unwrap_or_default();
529
530        unsafe {
531            let layout = device
532                .create_pipeline_layout(
533                    &vk::PipelineLayoutCreateInfo::default()
534                        .set_layouts(&descriptor_sets_layouts)
535                        .push_constant_ranges(&push_constants),
536                    None,
537                )
538                .map_err(|err| {
539                    warn!("unable to create graphics pipeline layout: {err}");
540
541                    DriverError::Unsupported
542                })?;
543            let shader_stages = shaders
544                .into_iter()
545                .map(|shader| {
546                    let shader_module = device
547                        .create_shader_module(
548                            &vk::ShaderModuleCreateInfo::default().code(shader.spirv.words()),
549                            None,
550                        )
551                        .map_err(|err| {
552                            warn!("unable to create graphic shader module: {err}");
553
554                            DriverError::Unsupported
555                        })?;
556                    let shader_stage = ShaderStage {
557                        flags: shader.stage,
558                        module: shader_module,
559                        name: CString::new(shader.entry_name.as_str()).map_err(|err| {
560                            warn!("invalid graphics shader entry name: {err}");
561
562                            DriverError::InvalidData
563                        })?,
564                        specialization: shader.specialization,
565                    };
566
567                    Result::<_, DriverError>::Ok(shader_stage)
568                })
569                .collect::<Result<Box<_>, _>>()?;
570
571            let mut multisample = MultisampleState {
572                alpha_to_coverage_enable: info.alpha_to_coverage,
573                alpha_to_one_enable: info.alpha_to_one,
574                rasterization_samples: info.samples,
575                ..Default::default()
576            };
577
578            if let Some(OrderedFloat(min_sample_shading)) = info.min_sample_shading {
579                #[cfg(debug_assertions)]
580                if info.samples.is_single() {
581                    /*
582                    This combination of a single-sampled pipeline and minimum sample shading does
583                    not make sense and should not be requested. In the future maybe this is part of
584                    the MSAA value so it can't be specified.
585                    */
586                    warn!("unsupported sample rate shading of single-sample pipeline");
587                }
588
589                // Callers should check this before attempting to use the feature
590                debug_assert!(
591                    device.physical.features_v1_0.sample_rate_shading,
592                    "unsupported sample rate shading feature"
593                );
594
595                multisample.sample_shading_enable = true;
596                multisample.min_sample_shading = min_sample_shading;
597            }
598
599            let push_constants = merge_push_constant_ranges(&push_constants).into_boxed_slice();
600
601            Ok(Self {
602                inner: Arc::new(GraphicsPipelineInner {
603                    descriptor_bindings,
604                    descriptor_info,
605                    device,
606                    info,
607                    input_attachments,
608                    layout,
609                    multisample,
610                    push_constants,
611                    shader_stages,
612                    vertex_input,
613                }),
614            })
615        }
616    }
617
618    /// The device which owns this graphics pipeline.
619    pub fn device(&self) -> &Device {
620        &self.inner.device
621    }
622
623    /// Gets the information used to create this object.
624    pub fn info(&self) -> GraphicsPipelineInfo {
625        self.inner.info
626    }
627
628    /// Sets the debugging name assigned to this pipeline.
629    ///
630    /// _Note:_ The name of the underlying Vulkan pipeline is lazily updated as submissions are
631    /// recorded.
632    pub fn set_debug_name(&self, name: impl AsRef<str>) {
633        Device::try_set_debug_utils_object_name(
634            &self.inner.device,
635            self.inner.layout,
636            lazy_str!("{} (layout)", name.as_ref()),
637        );
638        Device::try_set_private_data_object_name(
639            &self.inner.device,
640            vk::ObjectType::PIPELINE_LAYOUT,
641            self.inner.layout,
642            lazy_str!("{}", name.as_ref()),
643        );
644
645        for (set_idx, layout) in &self.inner.descriptor_info.layouts {
646            layout.set_debug_name(lazy_str!("{} (DS{set_idx})", name.as_ref()));
647        }
648    }
649
650    pub(crate) fn set_variant_debug_name(
651        &self,
652        pipeline_handle: vk::Pipeline,
653        render_pass: vk::RenderPass,
654        subpass_idx: u32,
655        name: impl AsRef<str>,
656    ) {
657        Device::try_set_debug_utils_object_name(
658            &self.inner.device,
659            pipeline_handle,
660            lazy_str!(
661                "{} (render pass {:?}, subpass {subpass_idx})",
662                name.as_ref(),
663                render_pass
664            ),
665        );
666        Device::try_set_private_data_object_name(
667            &self.inner.device,
668            vk::ObjectType::PIPELINE,
669            pipeline_handle,
670            name,
671        );
672    }
673
674    /// Sets the debugging name assigned to this pipeline.
675    pub fn with_debug_name(self, name: impl AsRef<str>) -> Self {
676        self.set_debug_name(name);
677
678        self
679    }
680}
681
682impl Debug for GraphicsPipeline {
683    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
684        let mut res = f.debug_struct(stringify!(GraphicsPipeline));
685        let shader_stages = self
686            .inner
687            .shader_stages
688            .iter()
689            .map(|stage| stage.flags)
690            .collect::<Box<_>>();
691
692        if let Some(debug_name) = &Device::private_data_object_name(
693            &self.inner.device,
694            vk::ObjectType::PIPELINE_LAYOUT,
695            self.inner.layout,
696        ) {
697            res.field("debug_name", debug_name);
698        }
699
700        res.field("layout", &self.inner.layout)
701            .field("shader_stages", &shader_stages)
702            .field("input_attachments", &self.inner.input_attachments)
703            .finish_non_exhaustive()
704    }
705}
706
707impl Eq for GraphicsPipeline {}
708
709impl Hash for GraphicsPipeline {
710    fn hash<H: Hasher>(&self, state: &mut H) {
711        Arc::as_ptr(&self.inner).hash(state);
712    }
713}
714
715impl PartialEq for GraphicsPipeline {
716    fn eq(&self, other: &Self) -> bool {
717        Arc::ptr_eq(&self.inner, &other.inner)
718    }
719}
720
721/// Information used to create a [`GraphicsPipeline`] instance.
722///
723/// See [`VkGraphicsPipelineCreateInfo`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkGraphicsPipelineCreateInfo.html).
724#[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)]
725#[builder(
726    build_fn(private, name = "fallible_build"),
727    derive(Clone, Copy, Debug),
728    pattern = "owned"
729)]
730pub struct GraphicsPipelineInfo {
731    /// Controls whether a temporary coverage value is generated based on the alpha component of
732    /// the fragment’s first color output.
733    #[builder(default)]
734    pub alpha_to_coverage: bool,
735
736    /// Controls whether the alpha component of the fragment’s first color output is replaced with
737    /// one.
738    #[builder(default)]
739    pub alpha_to_one: bool,
740
741    /// The number of descriptors to allocate for a given binding when using bindless (unbounded)
742    /// syntax.
743    ///
744    /// The default is `8192`.
745    ///
746    /// # Examples
747    ///
748    /// Basic usage (GLSL):
749    ///
750    /// ```
751    /// # vk_shader_macros::glsl!(r#"
752    /// #version 460 core
753    /// #extension GL_EXT_nonuniform_qualifier : require
754    /// #pragma shader_stage(fragment)
755    ///
756    /// layout(set = 0, binding = 0) uniform sampler2D my_binding[];
757    ///
758    /// void main() {
759    ///     // my_binding will have space for 8,192 images by default
760    /// }
761    /// # "#);
762    /// ```
763    #[builder(default = "8192")]
764    pub bindless_descriptor_count: u32,
765
766    /// Specifies color blend state used when rasterization is enabled for any color attachments
767    /// accessed during rendering.
768    ///
769    /// The default value is [`BlendInfo::REPLACE`].
770    #[builder(default)]
771    pub blend: BlendInfo,
772
773    /// Bitmask controlling triangle culling.
774    ///
775    /// The default value is `vk::CullModeFlags::BACK`.
776    #[builder(default = "vk::CullModeFlags::BACK")]
777    pub cull_mode: vk::CullModeFlags,
778
779    /// Interprets polygon front-facing orientation.
780    ///
781    /// The default value is `vk::FrontFace::COUNTER_CLOCKWISE`.
782    #[builder(default = "vk::FrontFace::COUNTER_CLOCKWISE")]
783    pub front_face: vk::FrontFace,
784
785    /// Specifies a fraction of the minimum number of unique samples to process for each fragment.
786    #[builder(default, setter(into, strip_option))]
787    pub min_sample_shading: Option<OrderedFloat<f32>>,
788
789    /// Controls polygon rasterization mode.
790    ///
791    /// The default value is `vk::PolygonMode::FILL`.
792    #[builder(default = "vk::PolygonMode::FILL")]
793    pub polygon_mode: vk::PolygonMode,
794
795    /// Input primitive topology.
796    ///
797    /// The default value is `vk::PrimitiveTopology::TRIANGLE_LIST`.
798    #[builder(default = "vk::PrimitiveTopology::TRIANGLE_LIST")]
799    pub topology: vk::PrimitiveTopology,
800
801    /// Multisampling antialias mode.
802    ///
803    /// The default value is `SampleCount::Type1`.
804    ///
805    /// See [`VkPipelineMultisampleStateCreateInfo`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkPipelineMultisampleStateCreateInfo.html).
806    #[builder(default = "SampleCount::Type1")]
807    pub samples: SampleCount,
808}
809
810impl GraphicsPipelineInfo {
811    /// Creates a default `GraphicsPipelineInfoBuilder`.
812    pub fn builder() -> GraphicsPipelineInfoBuilder {
813        Default::default()
814    }
815
816    /// Converts a `GraphicsPipelineInfo` into a `GraphicsPipelineInfoBuilder`.
817    pub fn into_builder(self) -> GraphicsPipelineInfoBuilder {
818        GraphicsPipelineInfoBuilder {
819            alpha_to_coverage: Some(self.alpha_to_coverage),
820            alpha_to_one: Some(self.alpha_to_one),
821            bindless_descriptor_count: Some(self.bindless_descriptor_count),
822            blend: Some(self.blend),
823            cull_mode: Some(self.cull_mode),
824            front_face: Some(self.front_face),
825            min_sample_shading: Some(self.min_sample_shading),
826            polygon_mode: Some(self.polygon_mode),
827            topology: Some(self.topology),
828            samples: Some(self.samples),
829        }
830    }
831}
832
833impl Default for GraphicsPipelineInfo {
834    fn default() -> Self {
835        Self {
836            alpha_to_coverage: false,
837            alpha_to_one: false,
838            bindless_descriptor_count: 8192,
839            blend: BlendInfo::REPLACE,
840            cull_mode: vk::CullModeFlags::BACK,
841            front_face: vk::FrontFace::COUNTER_CLOCKWISE,
842            min_sample_shading: None,
843            polygon_mode: vk::PolygonMode::FILL,
844            topology: vk::PrimitiveTopology::TRIANGLE_LIST,
845            samples: SampleCount::Type1,
846        }
847    }
848}
849
850impl From<GraphicsPipelineInfoBuilder> for GraphicsPipelineInfo {
851    fn from(info: GraphicsPipelineInfoBuilder) -> Self {
852        info.build()
853    }
854}
855
856impl GraphicsPipelineInfoBuilder {
857    /// Builds a new `GraphicsPipelineInfo`.
858    #[inline(always)]
859    pub fn build(self) -> GraphicsPipelineInfo {
860        self.fallible_build()
861            .expect("invalid graphics pipeline info")
862    }
863}
864
865#[derive(Debug)]
866pub(crate) struct GraphicsPipelineInner {
867    pub descriptor_bindings: DescriptorBindingMap,
868    pub descriptor_info: PipelineDescriptorInfo,
869    pub device: Device,
870    pub info: GraphicsPipelineInfo,
871    pub input_attachments: Box<[u32]>,
872    pub layout: vk::PipelineLayout,
873    pub multisample: MultisampleState,
874    pub push_constants: Box<[vk::PushConstantRange]>,
875    pub shader_stages: Box<[ShaderStage]>,
876    pub vertex_input: VertexInputState,
877}
878
879impl Drop for GraphicsPipelineInner {
880    #[profiling::function]
881    fn drop(&mut self) {
882        if panicking() {
883            return;
884        }
885
886        Device::try_clear_private_data_object_name(
887            &self.device,
888            vk::ObjectType::PIPELINE_LAYOUT,
889            self.layout,
890        );
891
892        unsafe {
893            self.device.destroy_pipeline_layout(self.layout, None);
894        }
895
896        for shader_stage in &mut self.shader_stages {
897            unsafe {
898                self.device.destroy_shader_module(shader_stage.module, None);
899            }
900        }
901    }
902}
903
904#[derive(Debug, Default)]
905pub(crate) struct MultisampleState {
906    pub alpha_to_coverage_enable: bool,
907    pub alpha_to_one_enable: bool,
908    pub flags: vk::PipelineMultisampleStateCreateFlags,
909    pub min_sample_shading: f32,
910    pub rasterization_samples: SampleCount,
911    pub sample_mask: Vec<u32>,
912    pub sample_shading_enable: bool,
913}
914
915#[derive(Debug)]
916pub(crate) struct ShaderStage {
917    pub flags: vk::ShaderStageFlags,
918    pub module: vk::ShaderModule,
919    pub name: CString, // TODO
920    pub specialization: Option<SpecializationMap>,
921}
922
923/// Specifies stencil mode during rasterization.
924///
925/// See [`VkStencilOpState`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkStencilOpState.html).
926#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
927pub struct StencilMode {
928    /// The action performed on samples that fail the stencil test.
929    pub fail_op: vk::StencilOp,
930
931    /// The action performed on samples that pass both the depth and stencil tests.
932    pub pass_op: vk::StencilOp,
933
934    /// The action performed on samples that pass the stencil test and fail the depth test.
935    pub depth_fail_op: vk::StencilOp,
936
937    /// The comparison operator used in the stencil test.
938    pub compare_op: vk::CompareOp,
939
940    /// The bits of the unsigned integer stencil values participating in the stencil test.
941    pub compare_mask: u32,
942
943    /// The bits of the unsigned integer stencil values updated by the stencil test in the stencil
944    /// framebuffer attachment.
945    pub write_mask: u32,
946
947    /// An unsigned integer stencil reference value that is used in the unsigned stencil
948    /// comparison.
949    pub reference: u32,
950}
951
952impl StencilMode {
953    /// Specifies a stencil mode which has no effect.
954    pub const IGNORE: Self = Self {
955        fail_op: vk::StencilOp::KEEP,
956        pass_op: vk::StencilOp::KEEP,
957        depth_fail_op: vk::StencilOp::KEEP,
958        compare_op: vk::CompareOp::NEVER,
959        compare_mask: 0,
960        write_mask: 0,
961        reference: 0,
962    };
963}
964
965impl Default for StencilMode {
966    fn default() -> Self {
967        Self::IGNORE
968    }
969}
970
971impl From<StencilMode> for vk::StencilOpState {
972    fn from(mode: StencilMode) -> Self {
973        Self {
974            fail_op: mode.fail_op,
975            pass_op: mode.pass_op,
976            depth_fail_op: mode.depth_fail_op,
977            compare_op: mode.compare_op,
978            compare_mask: mode.compare_mask,
979            write_mask: mode.write_mask,
980            reference: mode.reference,
981        }
982    }
983}
984
985#[derive(Clone, Debug, Default)]
986pub(crate) struct VertexInputState {
987    pub vertex_binding_descriptions: Vec<vk::VertexInputBindingDescription>,
988    pub vertex_attribute_descriptions: Vec<vk::VertexInputAttributeDescription>,
989}
990
991#[cfg(test)]
992mod test {
993    use super::*;
994
995    #[test]
996    pub fn blend_info() {
997        let info = BlendInfo::default();
998        let builder = info.into_builder().build();
999
1000        assert_eq!(info, builder);
1001    }
1002
1003    #[test]
1004    pub fn blend_info_builder() {
1005        let info = BlendInfo::default();
1006        let builder = BlendInfoBuilder::default().build();
1007
1008        assert_eq!(info, builder);
1009    }
1010
1011    #[test]
1012    pub fn depth_stencil_info() {
1013        let info = DepthStencilInfo::default();
1014        let builder = info.into_builder().build();
1015
1016        assert_eq!(info, builder);
1017    }
1018
1019    #[test]
1020    pub fn depth_stencil_info_builder() {
1021        let info = DepthStencilInfo::default();
1022        let builder = DepthStencilInfoBuilder::default().build();
1023
1024        assert_eq!(info, builder);
1025    }
1026
1027    #[test]
1028    pub fn graphics_pipeline_info() {
1029        let info = GraphicsPipelineInfo::default();
1030        let builder = info.into_builder().build();
1031
1032        assert_eq!(info, builder);
1033    }
1034
1035    #[test]
1036    pub fn graphics_pipeline_info_builder() {
1037        let info = GraphicsPipelineInfo::default();
1038        let builder = GraphicsPipelineInfoBuilder::default().build();
1039
1040        assert_eq!(info, builder);
1041    }
1042}