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::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        for (descriptor_info, _) in descriptor_bindings.values_mut() {
482            if descriptor_info.binding_count() == 0 {
483                descriptor_info.set_binding_count(info.bindless_descriptor_count);
484            }
485        }
486
487        let descriptor_info = PipelineDescriptorInfo::create(&device, &descriptor_bindings)?;
488        let descriptor_sets_layouts = descriptor_info
489            .layouts
490            .values()
491            .map(|descriptor_set_layout| descriptor_set_layout.handle)
492            .collect::<Box<_>>();
493
494        let push_constants = shaders
495            .iter()
496            .map(|shader| shader.push_constant_range())
497            .filter_map(|mut push_const| push_const.take())
498            .collect::<Vec<_>>();
499
500        let input_attachments = shaders
501            .iter()
502            .find(|shader| shader.stage == vk::ShaderStageFlags::FRAGMENT)
503            .map(|shader| {
504                let (input, write) = shader.attachments();
505                let (input, write) = (
506                    input
507                        .collect::<HashSet<_>>()
508                        .into_iter()
509                        .collect::<Box<_>>(),
510                    write.collect::<HashSet<_>>(),
511                );
512
513                if log_enabled!(Trace) {
514                    for input in input.iter() {
515                        trace!("detected input attachment {input}");
516                    }
517
518                    for write in &write {
519                        trace!("detected write attachment {write}");
520                    }
521                }
522
523                input
524            })
525            .unwrap_or_default();
526
527        unsafe {
528            let layout = device
529                .create_pipeline_layout(
530                    &vk::PipelineLayoutCreateInfo::default()
531                        .set_layouts(&descriptor_sets_layouts)
532                        .push_constant_ranges(&push_constants),
533                    None,
534                )
535                .map_err(|err| {
536                    warn!("unable to create graphics pipeline layout: {err}");
537
538                    DriverError::Unsupported
539                })?;
540            let shader_stages = shaders
541                .into_iter()
542                .map(|shader| {
543                    let shader_module = device
544                        .create_shader_module(
545                            &vk::ShaderModuleCreateInfo::default().code(shader.spirv.words()),
546                            None,
547                        )
548                        .map_err(|err| {
549                            warn!("unable to create graphic shader module: {err}");
550
551                            DriverError::Unsupported
552                        })?;
553                    let shader_stage = ShaderStage {
554                        flags: shader.stage,
555                        module: shader_module,
556                        name: CString::new(shader.entry_name.as_str()).map_err(|err| {
557                            warn!("invalid graphics shader entry name: {err}");
558
559                            DriverError::InvalidData
560                        })?,
561                        specialization: shader.specialization,
562                    };
563
564                    Result::<_, DriverError>::Ok(shader_stage)
565                })
566                .collect::<Result<Box<_>, _>>()?;
567
568            let mut multisample = MultisampleState {
569                alpha_to_coverage_enable: info.alpha_to_coverage,
570                alpha_to_one_enable: info.alpha_to_one,
571                rasterization_samples: info.samples,
572                ..Default::default()
573            };
574
575            if let Some(OrderedFloat(min_sample_shading)) = info.min_sample_shading {
576                #[cfg(debug_assertions)]
577                if info.samples.is_single() {
578                    /*
579                    This combination of a single-sampled pipeline and minimum sample shading does
580                    not make sense and should not be requested. In the future maybe this is part of
581                    the MSAA value so it can't be specified.
582                    */
583                    warn!("unsupported sample rate shading of single-sample pipeline");
584                }
585
586                // Callers should check this before attempting to use the feature
587                debug_assert!(
588                    device.physical.features_v1_0.sample_rate_shading,
589                    "unsupported sample rate shading feature"
590                );
591
592                multisample.sample_shading_enable = true;
593                multisample.min_sample_shading = min_sample_shading;
594            }
595
596            let push_constants = merge_push_constant_ranges(&push_constants).into_boxed_slice();
597
598            Ok(Self {
599                inner: Arc::new(GraphicsPipelineInner {
600                    descriptor_bindings,
601                    descriptor_info,
602                    device,
603                    info,
604                    input_attachments,
605                    layout,
606                    multisample,
607                    push_constants,
608                    shader_stages,
609                    vertex_input,
610                }),
611            })
612        }
613    }
614
615    /// The device which owns this graphics pipeline.
616    pub fn device(&self) -> &Device {
617        &self.inner.device
618    }
619
620    /// Gets the information used to create this object.
621    pub fn info(&self) -> GraphicsPipelineInfo {
622        self.inner.info
623    }
624
625    /// Sets the debugging name assigned to this pipeline.
626    ///
627    /// _Note:_ The name of the underlying Vulkan pipeline is lazily updated as submissions are
628    /// recorded.
629    pub fn set_debug_name(&self, name: impl AsRef<str>) {
630        Device::try_set_debug_utils_object_name(
631            &self.inner.device,
632            self.inner.layout,
633            lazy_str!("{} (layout)", name.as_ref()),
634        );
635        Device::try_set_private_data_object_name(
636            &self.inner.device,
637            vk::ObjectType::PIPELINE_LAYOUT,
638            self.inner.layout,
639            lazy_str!("{}", name.as_ref()),
640        );
641
642        for (set_idx, layout) in &self.inner.descriptor_info.layouts {
643            layout.set_debug_name(lazy_str!("{} (DS{set_idx})", name.as_ref()));
644        }
645    }
646
647    pub(crate) fn set_variant_debug_name(
648        &self,
649        pipeline_handle: vk::Pipeline,
650        render_pass: vk::RenderPass,
651        subpass_idx: u32,
652        name: impl AsRef<str>,
653    ) {
654        Device::try_set_debug_utils_object_name(
655            &self.inner.device,
656            pipeline_handle,
657            lazy_str!(
658                "{} (render pass {:?}, subpass {subpass_idx})",
659                name.as_ref(),
660                render_pass
661            ),
662        );
663        Device::try_set_private_data_object_name(
664            &self.inner.device,
665            vk::ObjectType::PIPELINE,
666            pipeline_handle,
667            name,
668        );
669    }
670
671    /// Sets the debugging name assigned to this pipeline.
672    pub fn with_debug_name(self, name: impl AsRef<str>) -> Self {
673        self.set_debug_name(name);
674
675        self
676    }
677}
678
679impl Debug for GraphicsPipeline {
680    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
681        let mut res = f.debug_struct(stringify!(GraphicsPipeline));
682        let shader_stages = self
683            .inner
684            .shader_stages
685            .iter()
686            .map(|stage| stage.flags)
687            .collect::<Box<_>>();
688
689        if let Some(debug_name) = &Device::private_data_object_name(
690            &self.inner.device,
691            vk::ObjectType::PIPELINE_LAYOUT,
692            self.inner.layout,
693        ) {
694            res.field("debug_name", debug_name);
695        }
696
697        res.field("layout", &self.inner.layout)
698            .field("shader_stages", &shader_stages)
699            .field("input_attachments", &self.inner.input_attachments)
700            .finish_non_exhaustive()
701    }
702}
703
704impl Eq for GraphicsPipeline {}
705
706impl Hash for GraphicsPipeline {
707    fn hash<H: Hasher>(&self, state: &mut H) {
708        Arc::as_ptr(&self.inner).hash(state);
709    }
710}
711
712impl PartialEq for GraphicsPipeline {
713    fn eq(&self, other: &Self) -> bool {
714        Arc::ptr_eq(&self.inner, &other.inner)
715    }
716}
717
718/// Information used to create a [`GraphicsPipeline`] instance.
719///
720/// See [`VkGraphicsPipelineCreateInfo`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkGraphicsPipelineCreateInfo.html).
721#[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)]
722#[builder(
723    build_fn(private, name = "fallible_build"),
724    derive(Clone, Copy, Debug),
725    pattern = "owned"
726)]
727pub struct GraphicsPipelineInfo {
728    /// Controls whether a temporary coverage value is generated based on the alpha component of
729    /// the fragment’s first color output.
730    #[builder(default)]
731    pub alpha_to_coverage: bool,
732
733    /// Controls whether the alpha component of the fragment’s first color output is replaced with
734    /// one.
735    #[builder(default)]
736    pub alpha_to_one: bool,
737
738    /// The number of descriptors to allocate for a given binding when using bindless (unbounded)
739    /// syntax.
740    ///
741    /// The default is `8192`.
742    ///
743    /// # Examples
744    ///
745    /// Basic usage (GLSL):
746    ///
747    /// ```
748    /// # vk_shader_macros::glsl!(r#"
749    /// #version 460 core
750    /// #extension GL_EXT_nonuniform_qualifier : require
751    /// #pragma shader_stage(fragment)
752    ///
753    /// layout(set = 0, binding = 0) uniform sampler2D my_binding[];
754    ///
755    /// void main() {
756    ///     // my_binding will have space for 8,192 images by default
757    /// }
758    /// # "#);
759    /// ```
760    #[builder(default = "8192")]
761    pub bindless_descriptor_count: u32,
762
763    /// Specifies color blend state used when rasterization is enabled for any color attachments
764    /// accessed during rendering.
765    ///
766    /// The default value is [`BlendInfo::REPLACE`].
767    #[builder(default)]
768    pub blend: BlendInfo,
769
770    /// Bitmask controlling triangle culling.
771    ///
772    /// The default value is `vk::CullModeFlags::BACK`.
773    #[builder(default = "vk::CullModeFlags::BACK")]
774    pub cull_mode: vk::CullModeFlags,
775
776    /// Interprets polygon front-facing orientation.
777    ///
778    /// The default value is `vk::FrontFace::COUNTER_CLOCKWISE`.
779    #[builder(default = "vk::FrontFace::COUNTER_CLOCKWISE")]
780    pub front_face: vk::FrontFace,
781
782    /// Specifies a fraction of the minimum number of unique samples to process for each fragment.
783    #[builder(default, setter(into, strip_option))]
784    pub min_sample_shading: Option<OrderedFloat<f32>>,
785
786    /// Controls polygon rasterization mode.
787    ///
788    /// The default value is `vk::PolygonMode::FILL`.
789    #[builder(default = "vk::PolygonMode::FILL")]
790    pub polygon_mode: vk::PolygonMode,
791
792    /// Input primitive topology.
793    ///
794    /// The default value is `vk::PrimitiveTopology::TRIANGLE_LIST`.
795    #[builder(default = "vk::PrimitiveTopology::TRIANGLE_LIST")]
796    pub topology: vk::PrimitiveTopology,
797
798    /// Multisampling antialias mode.
799    ///
800    /// The default value is `SampleCount::Type1`.
801    ///
802    /// See [`VkPipelineMultisampleStateCreateInfo`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkPipelineMultisampleStateCreateInfo.html).
803    #[builder(default = "SampleCount::Type1")]
804    pub samples: SampleCount,
805}
806
807impl GraphicsPipelineInfo {
808    /// Creates a default `GraphicsPipelineInfoBuilder`.
809    pub fn builder() -> GraphicsPipelineInfoBuilder {
810        Default::default()
811    }
812
813    /// Converts a `GraphicsPipelineInfo` into a `GraphicsPipelineInfoBuilder`.
814    pub fn into_builder(self) -> GraphicsPipelineInfoBuilder {
815        GraphicsPipelineInfoBuilder {
816            alpha_to_coverage: Some(self.alpha_to_coverage),
817            alpha_to_one: Some(self.alpha_to_one),
818            bindless_descriptor_count: Some(self.bindless_descriptor_count),
819            blend: Some(self.blend),
820            cull_mode: Some(self.cull_mode),
821            front_face: Some(self.front_face),
822            min_sample_shading: Some(self.min_sample_shading),
823            polygon_mode: Some(self.polygon_mode),
824            topology: Some(self.topology),
825            samples: Some(self.samples),
826        }
827    }
828}
829
830impl Default for GraphicsPipelineInfo {
831    fn default() -> Self {
832        Self {
833            alpha_to_coverage: false,
834            alpha_to_one: false,
835            bindless_descriptor_count: 8192,
836            blend: BlendInfo::REPLACE,
837            cull_mode: vk::CullModeFlags::BACK,
838            front_face: vk::FrontFace::COUNTER_CLOCKWISE,
839            min_sample_shading: None,
840            polygon_mode: vk::PolygonMode::FILL,
841            topology: vk::PrimitiveTopology::TRIANGLE_LIST,
842            samples: SampleCount::Type1,
843        }
844    }
845}
846
847impl From<GraphicsPipelineInfoBuilder> for GraphicsPipelineInfo {
848    fn from(info: GraphicsPipelineInfoBuilder) -> Self {
849        info.build()
850    }
851}
852
853impl GraphicsPipelineInfoBuilder {
854    /// Builds a new `GraphicsPipelineInfo`.
855    #[inline(always)]
856    pub fn build(self) -> GraphicsPipelineInfo {
857        self.fallible_build()
858            .expect("invalid graphics pipeline info")
859    }
860}
861
862#[derive(Debug)]
863pub(crate) struct GraphicsPipelineInner {
864    pub descriptor_bindings: DescriptorBindingMap,
865    pub descriptor_info: PipelineDescriptorInfo,
866    pub device: Device,
867    pub info: GraphicsPipelineInfo,
868    pub input_attachments: Box<[u32]>,
869    pub layout: vk::PipelineLayout,
870    pub multisample: MultisampleState,
871    pub push_constants: Box<[vk::PushConstantRange]>,
872    pub shader_stages: Box<[ShaderStage]>,
873    pub vertex_input: VertexInputState,
874}
875
876impl Drop for GraphicsPipelineInner {
877    #[profiling::function]
878    fn drop(&mut self) {
879        if panicking() {
880            return;
881        }
882
883        Device::try_clear_private_data_object_name(
884            &self.device,
885            vk::ObjectType::PIPELINE_LAYOUT,
886            self.layout,
887        );
888
889        unsafe {
890            self.device.destroy_pipeline_layout(self.layout, None);
891        }
892
893        for shader_stage in &mut self.shader_stages {
894            unsafe {
895                self.device.destroy_shader_module(shader_stage.module, None);
896            }
897        }
898    }
899}
900
901#[derive(Debug, Default)]
902pub(crate) struct MultisampleState {
903    pub alpha_to_coverage_enable: bool,
904    pub alpha_to_one_enable: bool,
905    pub flags: vk::PipelineMultisampleStateCreateFlags,
906    pub min_sample_shading: f32,
907    pub rasterization_samples: SampleCount,
908    pub sample_mask: Vec<u32>,
909    pub sample_shading_enable: bool,
910}
911
912#[derive(Debug)]
913pub(crate) struct ShaderStage {
914    pub flags: vk::ShaderStageFlags,
915    pub module: vk::ShaderModule,
916    pub name: CString, // TODO
917    pub specialization: Option<SpecializationMap>,
918}
919
920/// Specifies stencil mode during rasterization.
921///
922/// See [`VkStencilOpState`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkStencilOpState.html).
923#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
924pub struct StencilMode {
925    /// The action performed on samples that fail the stencil test.
926    pub fail_op: vk::StencilOp,
927
928    /// The action performed on samples that pass both the depth and stencil tests.
929    pub pass_op: vk::StencilOp,
930
931    /// The action performed on samples that pass the stencil test and fail the depth test.
932    pub depth_fail_op: vk::StencilOp,
933
934    /// The comparison operator used in the stencil test.
935    pub compare_op: vk::CompareOp,
936
937    /// The bits of the unsigned integer stencil values participating in the stencil test.
938    pub compare_mask: u32,
939
940    /// The bits of the unsigned integer stencil values updated by the stencil test in the stencil
941    /// framebuffer attachment.
942    pub write_mask: u32,
943
944    /// An unsigned integer stencil reference value that is used in the unsigned stencil
945    /// comparison.
946    pub reference: u32,
947}
948
949impl StencilMode {
950    /// Specifies a stencil mode which has no effect.
951    pub const IGNORE: Self = Self {
952        fail_op: vk::StencilOp::KEEP,
953        pass_op: vk::StencilOp::KEEP,
954        depth_fail_op: vk::StencilOp::KEEP,
955        compare_op: vk::CompareOp::NEVER,
956        compare_mask: 0,
957        write_mask: 0,
958        reference: 0,
959    };
960}
961
962impl Default for StencilMode {
963    fn default() -> Self {
964        Self::IGNORE
965    }
966}
967
968impl From<StencilMode> for vk::StencilOpState {
969    fn from(mode: StencilMode) -> Self {
970        Self {
971            fail_op: mode.fail_op,
972            pass_op: mode.pass_op,
973            depth_fail_op: mode.depth_fail_op,
974            compare_op: mode.compare_op,
975            compare_mask: mode.compare_mask,
976            write_mask: mode.write_mask,
977            reference: mode.reference,
978        }
979    }
980}
981
982#[derive(Clone, Debug, Default)]
983pub(crate) struct VertexInputState {
984    pub vertex_binding_descriptions: Vec<vk::VertexInputBindingDescription>,
985    pub vertex_attribute_descriptions: Vec<vk::VertexInputAttributeDescription>,
986}
987
988#[cfg(test)]
989mod test {
990    use super::*;
991
992    #[test]
993    pub fn blend_info() {
994        let info = BlendInfo::default();
995        let builder = info.into_builder().build();
996
997        assert_eq!(info, builder);
998    }
999
1000    #[test]
1001    pub fn blend_info_builder() {
1002        let info = BlendInfo::default();
1003        let builder = BlendInfoBuilder::default().build();
1004
1005        assert_eq!(info, builder);
1006    }
1007
1008    #[test]
1009    pub fn depth_stencil_info() {
1010        let info = DepthStencilInfo::default();
1011        let builder = info.into_builder().build();
1012
1013        assert_eq!(info, builder);
1014    }
1015
1016    #[test]
1017    pub fn depth_stencil_info_builder() {
1018        let info = DepthStencilInfo::default();
1019        let builder = DepthStencilInfoBuilder::default().build();
1020
1021        assert_eq!(info, builder);
1022    }
1023
1024    #[test]
1025    pub fn graphics_pipeline_info() {
1026        let info = GraphicsPipelineInfo::default();
1027        let builder = info.into_builder().build();
1028
1029        assert_eq!(info, builder);
1030    }
1031
1032    #[test]
1033    pub fn graphics_pipeline_info_builder() {
1034        let info = GraphicsPipelineInfo::default();
1035        let builder = GraphicsPipelineInfoBuilder::default().build();
1036
1037        assert_eq!(info, builder);
1038    }
1039}