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
124impl Default for BlendMode {
126 fn default() -> Self {
127 Self::REPLACE
128 }
129}
130
131impl From<BlendMode> for vk::PipelineColorBlendAttachmentState {
132 fn from(mode: BlendMode) -> Self {
133 Self {
134 blend_enable: mode.blend_enable as _,
135 src_color_blend_factor: mode.src_color_blend_factor,
136 dst_color_blend_factor: mode.dst_color_blend_factor,
137 color_blend_op: mode.color_blend_op,
138 src_alpha_blend_factor: mode.src_alpha_blend_factor,
139 dst_alpha_blend_factor: mode.dst_alpha_blend_factor,
140 alpha_blend_op: mode.alpha_blend_op,
141 color_write_mask: mode.color_write_mask,
142 }
143 }
144}
145
146impl BlendModeBuilder {
148 pub fn build(self) -> BlendMode {
150 self.fallible_build().unwrap()
151 }
152}
153
154#[derive(Debug)]
155struct BlendModeBuilderError;
156
157impl From<UninitializedFieldError> for BlendModeBuilderError {
158 fn from(_: UninitializedFieldError) -> Self {
159 Self
160 }
161}
162
163#[derive(Builder, Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
169#[builder(
170 build_fn(
171 private,
172 name = "fallible_build",
173 error = "DepthStencilModeBuilderError"
174 ),
175 derive(Clone, Copy, Debug),
176 pattern = "owned"
177)]
178pub struct DepthStencilMode {
179 pub back: StencilMode,
181
182 pub bounds_test: bool,
186
187 pub compare_op: vk::CompareOp,
193
194 pub depth_test: bool,
198
199 pub depth_write: bool,
205
206 pub front: StencilMode,
208
209 #[builder(setter(into))]
214 pub min: OrderedFloat<f32>,
215
216 #[builder(setter(into))]
221 pub max: OrderedFloat<f32>,
222
223 pub stencil_test: bool,
227}
228
229impl DepthStencilMode {
230 pub const DEPTH_READ: Self = Self {
232 back: StencilMode::IGNORE,
233 bounds_test: true,
234 compare_op: vk::CompareOp::LESS,
235 depth_test: true,
236 depth_write: false,
237 front: StencilMode::IGNORE,
238 min: OrderedFloat(0.0),
239 max: OrderedFloat(1.0),
240 stencil_test: false,
241 };
242
243 pub const DEPTH_WRITE: Self = Self {
245 back: StencilMode::IGNORE,
246 bounds_test: true,
247 compare_op: vk::CompareOp::LESS,
248 depth_test: true,
249 depth_write: true,
250 front: StencilMode::IGNORE,
251 min: OrderedFloat(0.0),
252 max: OrderedFloat(1.0),
253 stencil_test: false,
254 };
255
256 pub const IGNORE: Self = Self {
258 back: StencilMode::IGNORE,
259 bounds_test: false,
260 compare_op: vk::CompareOp::NEVER,
261 depth_test: false,
262 depth_write: false,
263 front: StencilMode::IGNORE,
264 min: OrderedFloat(0.0),
265 max: OrderedFloat(0.0),
266 stencil_test: false,
267 };
268
269 #[allow(clippy::new_ret_no_self)]
271 pub fn new() -> DepthStencilModeBuilder {
272 DepthStencilModeBuilder::default()
273 }
274}
275
276impl From<DepthStencilMode> for vk::PipelineDepthStencilStateCreateInfo<'_> {
277 fn from(mode: DepthStencilMode) -> Self {
278 Self::default()
279 .back(mode.back.into())
280 .depth_bounds_test_enable(mode.bounds_test as _)
281 .depth_compare_op(mode.compare_op)
282 .depth_test_enable(mode.depth_test as _)
283 .depth_write_enable(mode.depth_write as _)
284 .front(mode.front.into())
285 .max_depth_bounds(mode.max.into_inner())
286 .min_depth_bounds(mode.min.into_inner())
287 .stencil_test_enable(mode.stencil_test as _)
288 }
289}
290
291impl DepthStencilModeBuilder {
293 pub fn build(mut self) -> DepthStencilMode {
295 if self.back.is_none() {
296 self.back = Some(Default::default());
297 }
298
299 if self.bounds_test.is_none() {
300 self.bounds_test = Some(Default::default());
301 }
302
303 if self.compare_op.is_none() {
304 self.compare_op = Some(Default::default());
305 }
306
307 if self.depth_test.is_none() {
308 self.depth_test = Some(Default::default());
309 }
310
311 if self.depth_write.is_none() {
312 self.depth_write = Some(Default::default());
313 }
314
315 if self.front.is_none() {
316 self.front = Some(Default::default());
317 }
318
319 if self.min.is_none() {
320 self.min = Some(Default::default());
321 }
322
323 if self.max.is_none() {
324 self.max = Some(Default::default());
325 }
326
327 if self.stencil_test.is_none() {
328 self.stencil_test = Some(Default::default());
329 }
330
331 self.fallible_build()
332 .expect("All required fields set at initialization")
333 }
334}
335
336#[derive(Debug)]
337struct DepthStencilModeBuilderError;
338
339impl From<UninitializedFieldError> for DepthStencilModeBuilderError {
340 fn from(_: UninitializedFieldError) -> Self {
341 Self
342 }
343}
344
345#[derive(Debug)]
351pub struct GraphicPipeline {
352 pub(crate) descriptor_bindings: DescriptorBindingMap,
353 pub(crate) descriptor_info: PipelineDescriptorInfo,
354 device: Arc<Device>,
355
356 pub info: GraphicPipelineInfo,
358
359 pub(crate) input_attachments: Box<[u32]>,
360 pub(crate) layout: vk::PipelineLayout,
361
362 pub name: Option<String>,
364
365 pub(crate) push_constants: Vec<vk::PushConstantRange>,
366 pub(crate) shader_modules: Vec<vk::ShaderModule>,
367 pub(super) state: GraphicPipelineState,
368}
369
370impl GraphicPipeline {
371 #[profiling::function]
405 pub fn create<S>(
406 device: &Arc<Device>,
407 info: impl Into<GraphicPipelineInfo>,
408 shaders: impl IntoIterator<Item = S>,
409 ) -> Result<Self, DriverError>
410 where
411 S: Into<Shader>,
412 {
413 trace!("create");
414
415 let device = Arc::clone(device);
416 let info = info.into();
417 let shaders = shaders
418 .into_iter()
419 .map(|shader| shader.into())
420 .collect::<Vec<Shader>>();
421
422 let vertex_input = shaders
423 .iter()
424 .find(|shader| shader.stage == vk::ShaderStageFlags::VERTEX)
425 .expect("vertex shader not found")
426 .vertex_input();
427
428 let has_fragment_stage = shaders
430 .iter()
431 .any(|shader| shader.stage.contains(vk::ShaderStageFlags::FRAGMENT));
432 let has_tesselation_stage = shaders.iter().any(|shader| {
433 shader
434 .stage
435 .contains(vk::ShaderStageFlags::TESSELLATION_CONTROL)
436 }) && shaders.iter().any(|shader| {
437 shader
438 .stage
439 .contains(vk::ShaderStageFlags::TESSELLATION_EVALUATION)
440 });
441 let has_geometry_stage = shaders
442 .iter()
443 .any(|shader| shader.stage.contains(vk::ShaderStageFlags::GEOMETRY));
444
445 debug_assert!(
446 has_fragment_stage || has_tesselation_stage || has_geometry_stage,
447 "invalid shader stage combination"
448 );
449
450 let mut descriptor_bindings = Shader::merge_descriptor_bindings(
451 shaders.iter().map(|shader| shader.descriptor_bindings()),
452 );
453 for (descriptor_info, _) in descriptor_bindings.values_mut() {
454 if descriptor_info.binding_count() == 0 {
455 descriptor_info.set_binding_count(info.bindless_descriptor_count);
456 }
457 }
458
459 let descriptor_info = PipelineDescriptorInfo::create(&device, &descriptor_bindings)?;
460 let descriptor_sets_layouts = descriptor_info
461 .layouts
462 .values()
463 .map(|descriptor_set_layout| **descriptor_set_layout)
464 .collect::<Box<[_]>>();
465
466 let push_constants = shaders
467 .iter()
468 .map(|shader| shader.push_constant_range())
469 .filter_map(|mut push_const| push_const.take())
470 .collect::<Vec<_>>();
471
472 let input_attachments = {
473 let (input, write) = shaders
474 .iter()
475 .find(|shader| shader.stage == vk::ShaderStageFlags::FRAGMENT)
476 .expect("fragment shader not found")
477 .attachments();
478 let (input, write) = (
479 input
480 .collect::<HashSet<_>>()
481 .into_iter()
482 .collect::<Box<_>>(),
483 write.collect::<HashSet<_>>(),
484 );
485
486 if log_enabled!(Trace) {
487 for input in input.iter() {
488 trace!("detected input attachment {input}");
489 }
490
491 for write in &write {
492 trace!("detected write attachment {write}");
493 }
494 }
495
496 input
497 };
498
499 unsafe {
500 let layout = device
501 .create_pipeline_layout(
502 &vk::PipelineLayoutCreateInfo::default()
503 .set_layouts(&descriptor_sets_layouts)
504 .push_constant_ranges(&push_constants),
505 None,
506 )
507 .map_err(|err| {
508 warn!("{err}");
509
510 DriverError::Unsupported
511 })?;
512 let shader_info = shaders
513 .into_iter()
514 .map(|shader| {
515 let shader_module = device
516 .create_shader_module(
517 &vk::ShaderModuleCreateInfo::default()
518 .code(align_spriv(&shader.spirv)?),
519 None,
520 )
521 .map_err(|err| {
522 warn!("{err}");
523
524 DriverError::Unsupported
525 })?;
526 let shader_stage = Stage {
527 flags: shader.stage,
528 module: shader_module,
529 name: CString::new(shader.entry_name.as_str()).unwrap(),
530 specialization_info: shader.specialization_info,
531 };
532
533 Result::<_, DriverError>::Ok((shader_module, shader_stage))
534 })
535 .collect::<Result<Vec<_>, _>>()?;
536 let mut shader_modules = vec![];
537 let mut stages = vec![];
538 shader_info
539 .into_iter()
540 .for_each(|(shader_module, shader_stage)| {
541 shader_modules.push(shader_module);
542 stages.push(shader_stage);
543 });
544
545 let mut multisample = MultisampleState {
546 alpha_to_coverage_enable: info.alpha_to_coverage,
547 alpha_to_one_enable: info.alpha_to_one,
548 rasterization_samples: info.samples,
549 ..Default::default()
550 };
551
552 if let Some(OrderedFloat(min_sample_shading)) = info.min_sample_shading {
553 #[cfg(debug_assertions)]
554 if info.samples.is_single() {
555 warn!("unsupported sample rate shading of single-sample pipeline");
559 }
560
561 debug_assert!(
563 device.physical_device.features_v1_0.sample_rate_shading,
564 "unsupported sample rate shading feature"
565 );
566
567 multisample.sample_shading_enable = true;
568 multisample.min_sample_shading = min_sample_shading;
569 }
570
571 let push_constants = merge_push_constant_ranges(&push_constants);
572
573 Ok(Self {
574 descriptor_bindings,
575 descriptor_info,
576 device,
577 info,
578 input_attachments,
579 layout,
580 name: None,
581 push_constants,
582 shader_modules,
583 state: GraphicPipelineState {
584 layout,
585 multisample,
586 stages,
587 vertex_input,
588 },
589 })
590 }
591 }
592
593 pub fn with_name(mut this: Self, name: impl Into<String>) -> Self {
595 this.name = Some(name.into());
596 this
597 }
598}
599
600impl Drop for GraphicPipeline {
601 #[profiling::function]
602 fn drop(&mut self) {
603 if panicking() {
604 return;
605 }
606
607 unsafe {
608 self.device.destroy_pipeline_layout(self.layout, None);
609 }
610
611 for shader_module in self.shader_modules.drain(..) {
612 unsafe {
613 self.device.destroy_shader_module(shader_module, None);
614 }
615 }
616 }
617}
618
619#[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)]
621#[builder(
622 build_fn(
623 private,
624 name = "fallible_build",
625 error = "GraphicPipelineInfoBuilderError"
626 ),
627 derive(Clone, Copy, Debug),
628 pattern = "owned"
629)]
630#[non_exhaustive]
631pub struct GraphicPipelineInfo {
632 #[builder(default)]
635 pub alpha_to_coverage: bool,
636
637 #[builder(default)]
640 pub alpha_to_one: bool,
641
642 #[builder(default = "8192")]
665 pub bindless_descriptor_count: u32,
666
667 #[builder(default)]
672 pub blend: BlendMode,
673
674 #[builder(default = "vk::CullModeFlags::BACK")]
678 pub cull_mode: vk::CullModeFlags,
679
680 #[builder(default = "vk::FrontFace::COUNTER_CLOCKWISE")]
684 pub front_face: vk::FrontFace,
685
686 #[builder(default, setter(into, strip_option))]
688 pub min_sample_shading: Option<OrderedFloat<f32>>,
689
690 #[builder(default = "vk::PolygonMode::FILL")]
694 pub polygon_mode: vk::PolygonMode,
695
696 #[builder(default = "vk::PrimitiveTopology::TRIANGLE_LIST")]
700 pub topology: vk::PrimitiveTopology,
701
702 #[builder(default = "SampleCount::Type1")]
708 pub samples: SampleCount,
709}
710
711impl GraphicPipelineInfo {
712 #[allow(clippy::new_ret_no_self)]
714 pub fn builder() -> GraphicPipelineInfoBuilder {
715 Default::default()
716 }
717
718 #[inline(always)]
720 pub fn to_builder(self) -> GraphicPipelineInfoBuilder {
721 GraphicPipelineInfoBuilder {
722 alpha_to_coverage: Some(self.alpha_to_coverage),
723 alpha_to_one: Some(self.alpha_to_one),
724 bindless_descriptor_count: Some(self.bindless_descriptor_count),
725 blend: Some(self.blend),
726 cull_mode: Some(self.cull_mode),
727 front_face: Some(self.front_face),
728 min_sample_shading: Some(self.min_sample_shading),
729 polygon_mode: Some(self.polygon_mode),
730 topology: Some(self.topology),
731 samples: Some(self.samples),
732 }
733 }
734}
735
736impl Default for GraphicPipelineInfo {
737 fn default() -> Self {
738 Self {
739 alpha_to_coverage: false,
740 alpha_to_one: false,
741 bindless_descriptor_count: 8192,
742 blend: BlendMode::REPLACE,
743 cull_mode: vk::CullModeFlags::BACK,
744 front_face: vk::FrontFace::COUNTER_CLOCKWISE,
745 min_sample_shading: None,
746 polygon_mode: vk::PolygonMode::FILL,
747 topology: vk::PrimitiveTopology::TRIANGLE_LIST,
748 samples: SampleCount::Type1,
749 }
750 }
751}
752
753impl From<GraphicPipelineInfoBuilder> for GraphicPipelineInfo {
754 fn from(info: GraphicPipelineInfoBuilder) -> Self {
755 info.build()
756 }
757}
758
759impl GraphicPipelineInfoBuilder {
760 #[inline(always)]
762 pub fn build(self) -> GraphicPipelineInfo {
763 let res = self.fallible_build();
764
765 #[cfg(test)]
766 let res = res.unwrap();
767
768 #[cfg(not(test))]
769 let res = unsafe { res.unwrap_unchecked() };
770
771 res
772 }
773}
774
775#[derive(Debug)]
776struct GraphicPipelineInfoBuilderError;
777
778impl From<UninitializedFieldError> for GraphicPipelineInfoBuilderError {
779 fn from(_: UninitializedFieldError) -> Self {
780 Self
781 }
782}
783
784#[derive(Debug)]
785pub(super) struct GraphicPipelineState {
786 pub layout: vk::PipelineLayout,
787 pub multisample: MultisampleState,
788 pub stages: Vec<Stage>,
789 pub vertex_input: VertexInputState,
790}
791
792#[derive(Debug, Default)]
793pub(super) struct MultisampleState {
794 pub alpha_to_coverage_enable: bool,
795 pub alpha_to_one_enable: bool,
796 pub flags: vk::PipelineMultisampleStateCreateFlags,
797 pub min_sample_shading: f32,
798 pub rasterization_samples: SampleCount,
799 pub sample_mask: Vec<u32>,
800 pub sample_shading_enable: bool,
801}
802
803#[derive(Debug)]
804pub(super) struct Stage {
805 pub flags: vk::ShaderStageFlags,
806 pub module: vk::ShaderModule,
807 pub name: CString,
808 pub specialization_info: Option<SpecializationInfo>,
809}
810
811#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
816pub struct StencilMode {
817 pub fail_op: vk::StencilOp,
819
820 pub pass_op: vk::StencilOp,
822
823 pub depth_fail_op: vk::StencilOp,
825
826 pub compare_op: vk::CompareOp,
828
829 pub compare_mask: u32,
831
832 pub write_mask: u32,
835
836 pub reference: u32,
838}
839
840impl StencilMode {
841 pub const IGNORE: Self = Self {
843 fail_op: vk::StencilOp::KEEP,
844 pass_op: vk::StencilOp::KEEP,
845 depth_fail_op: vk::StencilOp::KEEP,
846 compare_op: vk::CompareOp::NEVER,
847 compare_mask: 0,
848 write_mask: 0,
849 reference: 0,
850 };
851}
852
853impl Default for StencilMode {
854 fn default() -> Self {
855 Self::IGNORE
856 }
857}
858
859impl From<StencilMode> for vk::StencilOpState {
860 fn from(mode: StencilMode) -> Self {
861 Self {
862 fail_op: mode.fail_op,
863 pass_op: mode.pass_op,
864 depth_fail_op: mode.depth_fail_op,
865 compare_op: mode.compare_op,
866 compare_mask: mode.compare_mask,
867 write_mask: mode.write_mask,
868 reference: mode.reference,
869 }
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}