1use {
4 super::{
5 DriverError,
6 device::Device,
7 image::SampleCount,
8 merge_push_constant_ranges,
9 shader::{
10 DescriptorBindingMap, PipelineDescriptorInfo, Shader, SpecializationInfo, align_spriv,
11 },
12 },
13 ash::vk,
14 derive_builder::{Builder, UninitializedFieldError},
15 log::{Level::Trace, log_enabled, trace, warn},
16 ordered_float::OrderedFloat,
17 std::{collections::HashSet, ffi::CString, sync::Arc, thread::panicking},
18};
19
20const RGBA_COLOR_COMPONENTS: vk::ColorComponentFlags = vk::ColorComponentFlags::from_raw(
21 vk::ColorComponentFlags::R.as_raw()
22 | vk::ColorComponentFlags::G.as_raw()
23 | vk::ColorComponentFlags::B.as_raw()
24 | vk::ColorComponentFlags::A.as_raw(),
25);
26
27#[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)]
33#[builder(
34 build_fn(private, name = "fallible_build", error = "BlendModeBuilderError"),
35 derive(Clone, Copy, Debug),
36 pattern = "owned"
37)]
38pub struct BlendMode {
39 #[builder(default = "false")]
44 pub blend_enable: bool,
45
46 #[builder(default = "vk::BlendFactor::SRC_COLOR")]
48 pub src_color_blend_factor: vk::BlendFactor,
49
50 #[builder(default = "vk::BlendFactor::ONE_MINUS_DST_COLOR")]
52 pub dst_color_blend_factor: vk::BlendFactor,
53
54 #[builder(default = "vk::BlendOp::ADD")]
57 pub color_blend_op: vk::BlendOp,
58
59 #[builder(default = "vk::BlendFactor::ZERO")]
61 pub src_alpha_blend_factor: vk::BlendFactor,
62
63 #[builder(default = "vk::BlendFactor::ZERO")]
65 pub dst_alpha_blend_factor: vk::BlendFactor,
66
67 #[builder(default = "vk::BlendOp::ADD")]
70 pub alpha_blend_op: vk::BlendOp,
71
72 #[builder(default = "RGBA_COLOR_COMPONENTS")]
76 pub color_write_mask: vk::ColorComponentFlags,
77}
78
79impl BlendMode {
80 pub const REPLACE: Self = Self {
82 blend_enable: false,
83 src_color_blend_factor: vk::BlendFactor::SRC_COLOR,
84 dst_color_blend_factor: vk::BlendFactor::ONE_MINUS_DST_COLOR,
85 color_blend_op: vk::BlendOp::ADD,
86 src_alpha_blend_factor: vk::BlendFactor::ZERO,
87 dst_alpha_blend_factor: vk::BlendFactor::ZERO,
88 alpha_blend_op: vk::BlendOp::ADD,
89 color_write_mask: RGBA_COLOR_COMPONENTS,
90 };
91
92 pub const ALPHA: Self = Self {
94 blend_enable: true,
95 src_color_blend_factor: vk::BlendFactor::SRC_ALPHA,
96 dst_color_blend_factor: vk::BlendFactor::ONE_MINUS_SRC_ALPHA,
97 color_blend_op: vk::BlendOp::ADD,
98 src_alpha_blend_factor: vk::BlendFactor::SRC_ALPHA,
99 dst_alpha_blend_factor: vk::BlendFactor::ONE_MINUS_SRC_ALPHA,
100 alpha_blend_op: vk::BlendOp::ADD,
101 color_write_mask: RGBA_COLOR_COMPONENTS,
102 };
103
104 pub const PRE_MULTIPLIED_ALPHA: Self = Self {
107 blend_enable: true,
108 src_color_blend_factor: vk::BlendFactor::SRC_ALPHA,
109 dst_color_blend_factor: vk::BlendFactor::ONE_MINUS_SRC_ALPHA,
110 color_blend_op: vk::BlendOp::ADD,
111 src_alpha_blend_factor: vk::BlendFactor::ONE,
112 dst_alpha_blend_factor: vk::BlendFactor::ONE,
113 alpha_blend_op: vk::BlendOp::ADD,
114 color_write_mask: RGBA_COLOR_COMPONENTS,
115 };
116
117 #[allow(clippy::new_ret_no_self)]
119 pub fn new() -> BlendModeBuilder {
120 BlendModeBuilder::default()
121 }
122
123 pub(super) fn into_vk(self) -> vk::PipelineColorBlendAttachmentState {
124 vk::PipelineColorBlendAttachmentState {
125 blend_enable: if self.blend_enable {
126 vk::TRUE
127 } else {
128 vk::FALSE
129 },
130 src_color_blend_factor: self.src_color_blend_factor,
131 dst_color_blend_factor: self.dst_color_blend_factor,
132 color_blend_op: self.color_blend_op,
133 src_alpha_blend_factor: self.src_alpha_blend_factor,
134 dst_alpha_blend_factor: self.dst_alpha_blend_factor,
135 alpha_blend_op: self.alpha_blend_op,
136 color_write_mask: self.color_write_mask,
137 }
138 }
139}
140
141impl Default for BlendMode {
143 fn default() -> Self {
144 Self::REPLACE
145 }
146}
147
148impl BlendModeBuilder {
150 pub fn build(self) -> BlendMode {
152 self.fallible_build().unwrap()
153 }
154}
155
156#[derive(Debug)]
157struct BlendModeBuilderError;
158
159impl From<UninitializedFieldError> for BlendModeBuilderError {
160 fn from(_: UninitializedFieldError) -> Self {
161 Self
162 }
163}
164
165#[derive(Builder, Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
171#[builder(
172 build_fn(
173 private,
174 name = "fallible_build",
175 error = "DepthStencilModeBuilderError"
176 ),
177 derive(Clone, Copy, Debug),
178 pattern = "owned"
179)]
180pub struct DepthStencilMode {
181 pub back: StencilMode,
183
184 pub bounds_test: bool,
188
189 pub compare_op: vk::CompareOp,
195
196 pub depth_test: bool,
200
201 pub depth_write: bool,
207
208 pub front: StencilMode,
210
211 #[builder(setter(into))]
216 pub min: OrderedFloat<f32>,
217
218 #[builder(setter(into))]
223 pub max: OrderedFloat<f32>,
224
225 pub stencil_test: bool,
229}
230
231impl DepthStencilMode {
232 pub const DEPTH_READ: Self = Self {
234 back: StencilMode::IGNORE,
235 bounds_test: true,
236 compare_op: vk::CompareOp::LESS,
237 depth_test: true,
238 depth_write: false,
239 front: StencilMode::IGNORE,
240 min: OrderedFloat(0.0),
241 max: OrderedFloat(1.0),
242 stencil_test: false,
243 };
244
245 pub const DEPTH_WRITE: Self = Self {
247 back: StencilMode::IGNORE,
248 bounds_test: true,
249 compare_op: vk::CompareOp::LESS,
250 depth_test: true,
251 depth_write: true,
252 front: StencilMode::IGNORE,
253 min: OrderedFloat(0.0),
254 max: OrderedFloat(1.0),
255 stencil_test: false,
256 };
257
258 pub const IGNORE: Self = Self {
260 back: StencilMode::IGNORE,
261 bounds_test: false,
262 compare_op: vk::CompareOp::NEVER,
263 depth_test: false,
264 depth_write: false,
265 front: StencilMode::IGNORE,
266 min: OrderedFloat(0.0),
267 max: OrderedFloat(0.0),
268 stencil_test: false,
269 };
270
271 #[allow(clippy::new_ret_no_self)]
273 pub fn new() -> DepthStencilModeBuilder {
274 DepthStencilModeBuilder::default()
275 }
276
277 pub(super) fn into_vk(self) -> vk::PipelineDepthStencilStateCreateInfo<'static> {
278 vk::PipelineDepthStencilStateCreateInfo {
279 back: self.back.into_vk(),
280 depth_bounds_test_enable: self.bounds_test as _,
281 depth_compare_op: self.compare_op,
282 depth_test_enable: self.depth_test as _,
283 depth_write_enable: self.depth_write as _,
284 front: self.front.into_vk(),
285 max_depth_bounds: *self.max,
286 min_depth_bounds: *self.min,
287 stencil_test_enable: self.stencil_test as _,
288 ..Default::default()
289 }
290 }
291}
292
293impl DepthStencilModeBuilder {
295 pub fn build(mut self) -> DepthStencilMode {
297 if self.back.is_none() {
298 self.back = Some(Default::default());
299 }
300
301 if self.bounds_test.is_none() {
302 self.bounds_test = Some(Default::default());
303 }
304
305 if self.compare_op.is_none() {
306 self.compare_op = Some(Default::default());
307 }
308
309 if self.depth_test.is_none() {
310 self.depth_test = Some(Default::default());
311 }
312
313 if self.depth_write.is_none() {
314 self.depth_write = Some(Default::default());
315 }
316
317 if self.front.is_none() {
318 self.front = Some(Default::default());
319 }
320
321 if self.min.is_none() {
322 self.min = Some(Default::default());
323 }
324
325 if self.max.is_none() {
326 self.max = Some(Default::default());
327 }
328
329 if self.stencil_test.is_none() {
330 self.stencil_test = Some(Default::default());
331 }
332
333 self.fallible_build()
334 .expect("All required fields set at initialization")
335 }
336}
337
338#[derive(Debug)]
339struct DepthStencilModeBuilderError;
340
341impl From<UninitializedFieldError> for DepthStencilModeBuilderError {
342 fn from(_: UninitializedFieldError) -> Self {
343 Self
344 }
345}
346
347#[derive(Debug)]
353pub struct GraphicPipeline {
354 pub(crate) descriptor_bindings: DescriptorBindingMap,
355 pub(crate) descriptor_info: PipelineDescriptorInfo,
356 device: Arc<Device>,
357
358 pub info: GraphicPipelineInfo,
360
361 pub(crate) input_attachments: Box<[u32]>,
362 pub(crate) layout: vk::PipelineLayout,
363
364 pub name: Option<String>,
366
367 pub(crate) push_constants: Vec<vk::PushConstantRange>,
368 pub(crate) shader_modules: Vec<vk::ShaderModule>,
369 pub(super) state: GraphicPipelineState,
370}
371
372impl GraphicPipeline {
373 #[profiling::function]
407 pub fn create<S>(
408 device: &Arc<Device>,
409 info: impl Into<GraphicPipelineInfo>,
410 shaders: impl IntoIterator<Item = S>,
411 ) -> Result<Self, DriverError>
412 where
413 S: Into<Shader>,
414 {
415 trace!("create");
416
417 let device = Arc::clone(device);
418 let info = info.into();
419 let shaders = shaders
420 .into_iter()
421 .map(|shader| shader.into())
422 .collect::<Vec<Shader>>();
423
424 let vertex_input = shaders
425 .iter()
426 .find(|shader| shader.stage == vk::ShaderStageFlags::VERTEX)
427 .expect("vertex shader not found")
428 .vertex_input();
429
430 let has_fragment_stage = shaders
432 .iter()
433 .any(|shader| shader.stage.contains(vk::ShaderStageFlags::FRAGMENT));
434 let has_tesselation_stage = shaders.iter().any(|shader| {
435 shader
436 .stage
437 .contains(vk::ShaderStageFlags::TESSELLATION_CONTROL)
438 }) && shaders.iter().any(|shader| {
439 shader
440 .stage
441 .contains(vk::ShaderStageFlags::TESSELLATION_EVALUATION)
442 });
443 let has_geometry_stage = shaders
444 .iter()
445 .any(|shader| shader.stage.contains(vk::ShaderStageFlags::GEOMETRY));
446
447 debug_assert!(
448 has_fragment_stage || has_tesselation_stage || has_geometry_stage,
449 "invalid shader stage combination"
450 );
451
452 let mut descriptor_bindings = Shader::merge_descriptor_bindings(
453 shaders.iter().map(|shader| shader.descriptor_bindings()),
454 );
455 for (descriptor_info, _) in descriptor_bindings.values_mut() {
456 if descriptor_info.binding_count() == 0 {
457 descriptor_info.set_binding_count(info.bindless_descriptor_count);
458 }
459 }
460
461 let descriptor_info = PipelineDescriptorInfo::create(&device, &descriptor_bindings)?;
462 let descriptor_sets_layouts = descriptor_info
463 .layouts
464 .values()
465 .map(|descriptor_set_layout| **descriptor_set_layout)
466 .collect::<Box<[_]>>();
467
468 let push_constants = shaders
469 .iter()
470 .map(|shader| shader.push_constant_range())
471 .filter_map(|mut push_const| push_const.take())
472 .collect::<Vec<_>>();
473
474 let input_attachments = {
475 let (input, write) = shaders
476 .iter()
477 .find(|shader| shader.stage == vk::ShaderStageFlags::FRAGMENT)
478 .expect("fragment shader not found")
479 .attachments();
480 let (input, write) = (
481 input
482 .collect::<HashSet<_>>()
483 .into_iter()
484 .collect::<Box<_>>(),
485 write.collect::<HashSet<_>>(),
486 );
487
488 if log_enabled!(Trace) {
489 for input in input.iter() {
490 trace!("detected input attachment {input}");
491 }
492
493 for write in &write {
494 trace!("detected write attachment {write}");
495 }
496 }
497
498 input
499 };
500
501 unsafe {
502 let layout = device
503 .create_pipeline_layout(
504 &vk::PipelineLayoutCreateInfo::default()
505 .set_layouts(&descriptor_sets_layouts)
506 .push_constant_ranges(&push_constants),
507 None,
508 )
509 .map_err(|err| {
510 warn!("{err}");
511
512 DriverError::Unsupported
513 })?;
514 let shader_info = shaders
515 .into_iter()
516 .map(|shader| {
517 let shader_module = device
518 .create_shader_module(
519 &vk::ShaderModuleCreateInfo::default()
520 .code(align_spriv(&shader.spirv)?),
521 None,
522 )
523 .map_err(|err| {
524 warn!("{err}");
525
526 DriverError::Unsupported
527 })?;
528 let shader_stage = Stage {
529 flags: shader.stage,
530 module: shader_module,
531 name: CString::new(shader.entry_name.as_str()).unwrap(),
532 specialization_info: shader.specialization_info,
533 };
534
535 Result::<_, DriverError>::Ok((shader_module, shader_stage))
536 })
537 .collect::<Result<Vec<_>, _>>()?;
538 let mut shader_modules = vec![];
539 let mut stages = vec![];
540 shader_info
541 .into_iter()
542 .for_each(|(shader_module, shader_stage)| {
543 shader_modules.push(shader_module);
544 stages.push(shader_stage);
545 });
546
547 let mut multisample = MultisampleState {
548 alpha_to_coverage_enable: info.alpha_to_coverage,
549 alpha_to_one_enable: info.alpha_to_one,
550 rasterization_samples: info.samples,
551 ..Default::default()
552 };
553
554 if let Some(OrderedFloat(min_sample_shading)) = info.min_sample_shading {
555 #[cfg(debug_assertions)]
556 if info.samples.is_single() {
557 warn!("unsupported sample rate shading of single-sample pipeline");
561 }
562
563 debug_assert!(
565 device.physical_device.features_v1_0.sample_rate_shading,
566 "unsupported sample rate shading feature"
567 );
568
569 multisample.sample_shading_enable = true;
570 multisample.min_sample_shading = min_sample_shading;
571 }
572
573 let push_constants = merge_push_constant_ranges(&push_constants);
574
575 Ok(Self {
576 descriptor_bindings,
577 descriptor_info,
578 device,
579 info,
580 input_attachments,
581 layout,
582 name: None,
583 push_constants,
584 shader_modules,
585 state: GraphicPipelineState {
586 layout,
587 multisample,
588 stages,
589 vertex_input,
590 },
591 })
592 }
593 }
594
595 pub fn with_name(mut this: Self, name: impl Into<String>) -> Self {
597 this.name = Some(name.into());
598 this
599 }
600}
601
602impl Drop for GraphicPipeline {
603 #[profiling::function]
604 fn drop(&mut self) {
605 if panicking() {
606 return;
607 }
608
609 unsafe {
610 self.device.destroy_pipeline_layout(self.layout, None);
611 }
612
613 for shader_module in self.shader_modules.drain(..) {
614 unsafe {
615 self.device.destroy_shader_module(shader_module, None);
616 }
617 }
618 }
619}
620
621#[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)]
623#[builder(
624 build_fn(
625 private,
626 name = "fallible_build",
627 error = "GraphicPipelineInfoBuilderError"
628 ),
629 derive(Clone, Copy, Debug),
630 pattern = "owned"
631)]
632#[non_exhaustive]
633pub struct GraphicPipelineInfo {
634 #[builder(default)]
637 pub alpha_to_coverage: bool,
638
639 #[builder(default)]
642 pub alpha_to_one: bool,
643
644 #[builder(default = "8192")]
667 pub bindless_descriptor_count: u32,
668
669 #[builder(default)]
674 pub blend: BlendMode,
675
676 #[builder(default = "vk::CullModeFlags::BACK")]
680 pub cull_mode: vk::CullModeFlags,
681
682 #[builder(default = "vk::FrontFace::COUNTER_CLOCKWISE")]
686 pub front_face: vk::FrontFace,
687
688 #[builder(default, setter(into, strip_option))]
690 pub min_sample_shading: Option<OrderedFloat<f32>>,
691
692 #[builder(default = "vk::PolygonMode::FILL")]
696 pub polygon_mode: vk::PolygonMode,
697
698 #[builder(default = "vk::PrimitiveTopology::TRIANGLE_LIST")]
702 pub topology: vk::PrimitiveTopology,
703
704 #[builder(default = "SampleCount::Type1")]
710 pub samples: SampleCount,
711}
712
713impl GraphicPipelineInfo {
714 #[allow(clippy::new_ret_no_self)]
716 pub fn builder() -> GraphicPipelineInfoBuilder {
717 Default::default()
718 }
719
720 #[inline(always)]
722 pub fn to_builder(self) -> GraphicPipelineInfoBuilder {
723 GraphicPipelineInfoBuilder {
724 alpha_to_coverage: Some(self.alpha_to_coverage),
725 alpha_to_one: Some(self.alpha_to_one),
726 bindless_descriptor_count: Some(self.bindless_descriptor_count),
727 blend: Some(self.blend),
728 cull_mode: Some(self.cull_mode),
729 front_face: Some(self.front_face),
730 min_sample_shading: Some(self.min_sample_shading),
731 polygon_mode: Some(self.polygon_mode),
732 topology: Some(self.topology),
733 samples: Some(self.samples),
734 }
735 }
736}
737
738impl Default for GraphicPipelineInfo {
739 fn default() -> Self {
740 Self {
741 alpha_to_coverage: false,
742 alpha_to_one: false,
743 bindless_descriptor_count: 8192,
744 blend: BlendMode::REPLACE,
745 cull_mode: vk::CullModeFlags::BACK,
746 front_face: vk::FrontFace::COUNTER_CLOCKWISE,
747 min_sample_shading: None,
748 polygon_mode: vk::PolygonMode::FILL,
749 topology: vk::PrimitiveTopology::TRIANGLE_LIST,
750 samples: SampleCount::Type1,
751 }
752 }
753}
754
755impl From<GraphicPipelineInfoBuilder> for GraphicPipelineInfo {
756 fn from(info: GraphicPipelineInfoBuilder) -> Self {
757 info.build()
758 }
759}
760
761impl GraphicPipelineInfoBuilder {
762 #[inline(always)]
764 pub fn build(self) -> GraphicPipelineInfo {
765 let res = self.fallible_build();
766
767 #[cfg(test)]
768 let res = res.unwrap();
769
770 #[cfg(not(test))]
771 let res = unsafe { res.unwrap_unchecked() };
772
773 res
774 }
775}
776
777#[derive(Debug)]
778struct GraphicPipelineInfoBuilderError;
779
780impl From<UninitializedFieldError> for GraphicPipelineInfoBuilderError {
781 fn from(_: UninitializedFieldError) -> Self {
782 Self
783 }
784}
785
786#[derive(Debug)]
787pub(super) struct GraphicPipelineState {
788 pub layout: vk::PipelineLayout,
789 pub multisample: MultisampleState,
790 pub stages: Vec<Stage>,
791 pub vertex_input: VertexInputState,
792}
793
794#[derive(Debug, Default)]
795pub(super) struct MultisampleState {
796 pub alpha_to_coverage_enable: bool,
797 pub alpha_to_one_enable: bool,
798 pub flags: vk::PipelineMultisampleStateCreateFlags,
799 pub min_sample_shading: f32,
800 pub rasterization_samples: SampleCount,
801 pub sample_mask: Vec<u32>,
802 pub sample_shading_enable: bool,
803}
804
805#[derive(Debug)]
806pub(super) struct Stage {
807 pub flags: vk::ShaderStageFlags,
808 pub module: vk::ShaderModule,
809 pub name: CString,
810 pub specialization_info: Option<SpecializationInfo>,
811}
812
813#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
818pub struct StencilMode {
819 pub fail_op: vk::StencilOp,
821
822 pub pass_op: vk::StencilOp,
824
825 pub depth_fail_op: vk::StencilOp,
827
828 pub compare_op: vk::CompareOp,
830
831 pub compare_mask: u32,
833
834 pub write_mask: u32,
837
838 pub reference: u32,
840}
841
842impl StencilMode {
843 pub const IGNORE: Self = Self {
845 fail_op: vk::StencilOp::KEEP,
846 pass_op: vk::StencilOp::KEEP,
847 depth_fail_op: vk::StencilOp::KEEP,
848 compare_op: vk::CompareOp::NEVER,
849 compare_mask: 0,
850 write_mask: 0,
851 reference: 0,
852 };
853
854 fn into_vk(self) -> vk::StencilOpState {
855 vk::StencilOpState {
856 fail_op: self.fail_op,
857 pass_op: self.pass_op,
858 depth_fail_op: self.depth_fail_op,
859 compare_op: self.compare_op,
860 compare_mask: self.compare_mask,
861 write_mask: self.write_mask,
862 reference: self.reference,
863 }
864 }
865}
866
867impl Default for StencilMode {
868 fn default() -> Self {
869 Self::IGNORE
870 }
871}
872
873#[derive(Clone, Debug, Default)]
874pub(super) struct VertexInputState {
875 pub vertex_binding_descriptions: Vec<vk::VertexInputBindingDescription>,
876 pub vertex_attribute_descriptions: Vec<vk::VertexInputAttributeDescription>,
877}
878
879#[cfg(test)]
880mod tests {
881 use super::*;
882
883 type Info = GraphicPipelineInfo;
884 type Builder = GraphicPipelineInfoBuilder;
885
886 #[test]
887 pub fn graphic_pipeline_info() {
888 let info = Info::default();
889 let builder = info.to_builder().build();
890
891 assert_eq!(info, builder);
892 }
893
894 #[test]
895 pub fn graphic_pipeline_info_builder() {
896 let info = Info::default();
897 let builder = Builder::default().build();
898
899 assert_eq!(info, builder);
900 }
901}