1 // Copyright (c) 2016 The vulkano developers
2 // Licensed under the Apache License, Version 2.0
3 // <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT
5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
6 // at your option. All files in the project carrying such
7 // notice may not be copied, modified, or distributed except
8 // according to those terms.
9 
10 //! Configures how the color output of the fragment shader is written to the attachment.
11 //!
12 //! # Blending in details
13 //!
14 //! There are three kinds of color attachments for the purpose of blending:
15 //!
16 //! - Attachments with a floating-point or fixed point format.
17 //! - Attachments with a (non-normalized) integer format.
18 //! - Attachments with a normalized integer format.
19 //!
20 //! For floating-point and fixed-point formats, the blending operation is applied. For integer
21 //! formats, the logic operation is applied. For normalized integer formats, the logic operation
22 //! will take precedence if it is activated, otherwise the blending operation is applied.
23 
24 use crate::{
25     macros::{vulkan_bitflags, vulkan_enum},
26     pipeline::StateMode,
27 };
28 
29 /// Describes how the color output of the fragment shader is written to the attachment. See the
30 /// documentation of the `blend` module for more info.
31 #[derive(Clone, Debug)]
32 pub struct ColorBlendState {
33     /// Sets the logical operation to perform between the incoming fragment color and the existing
34     /// fragment in the framebuffer attachment.
35     ///
36     /// If set to `Some`, the [`logic_op`](crate::device::Features::logic_op) feature must be
37     /// enabled on the device. If set to `Some(Dynamic)`, then the
38     /// [`extended_dynamic_state2_logic_op`](crate::device::Features::extended_dynamic_state2_logic_op)
39     /// feature must also be enabled on the device.
40     pub logic_op: Option<StateMode<LogicOp>>,
41 
42     /// Sets the blend and output state for each color attachment. The number of elements must match
43     /// the number of color attachments in the framebuffer.
44     ///
45     /// If there are multiple elements, and the `blend` and `color_write_mask` members of each
46     /// element differ, then the [`independent_blend`](crate::device::Features::independent_blend)
47     /// feature must be enabled on the device.
48     pub attachments: Vec<ColorBlendAttachmentState>,
49 
50     /// The constant color to use for some of the `BlendFactor` variants.
51     pub blend_constants: StateMode<[f32; 4]>,
52 }
53 
54 impl ColorBlendState {
55     /// Creates a `ColorBlendState` with logical operations disabled, blend constants set to zero,
56     /// and `num` attachment entries that have blending disabled, and color write and all color
57     /// components enabled.
58     #[inline]
new(num: u32) -> Self59     pub fn new(num: u32) -> Self {
60         Self {
61             logic_op: None,
62             attachments: (0..num)
63                 .map(|_| ColorBlendAttachmentState {
64                     blend: None,
65                     color_write_mask: ColorComponents::all(),
66                     color_write_enable: StateMode::Fixed(true),
67                 })
68                 .collect(),
69             blend_constants: StateMode::Fixed([0.0, 0.0, 0.0, 0.0]),
70         }
71     }
72 
73     /// Enables logical operations with the given logical operation.
74     #[inline]
logic_op(mut self, logic_op: LogicOp) -> Self75     pub fn logic_op(mut self, logic_op: LogicOp) -> Self {
76         self.logic_op = Some(StateMode::Fixed(logic_op));
77         self
78     }
79 
80     /// Enables logical operations with a dynamic logical operation.
81     #[inline]
logic_op_dynamic(mut self) -> Self82     pub fn logic_op_dynamic(mut self) -> Self {
83         self.logic_op = Some(StateMode::Dynamic);
84         self
85     }
86 
87     /// Enables blending for all attachments, with the given parameters.
88     #[inline]
blend(mut self, blend: AttachmentBlend) -> Self89     pub fn blend(mut self, blend: AttachmentBlend) -> Self {
90         self.attachments
91             .iter_mut()
92             .for_each(|attachment_state| attachment_state.blend = Some(blend));
93         self
94     }
95 
96     /// Enables blending for all attachments, with alpha blending.
97     #[inline]
blend_alpha(mut self) -> Self98     pub fn blend_alpha(mut self) -> Self {
99         self.attachments
100             .iter_mut()
101             .for_each(|attachment_state| attachment_state.blend = Some(AttachmentBlend::alpha()));
102         self
103     }
104 
105     /// Enables blending for all attachments, with additive blending.
106     #[inline]
blend_additive(mut self) -> Self107     pub fn blend_additive(mut self) -> Self {
108         self.attachments.iter_mut().for_each(|attachment_state| {
109             attachment_state.blend = Some(AttachmentBlend::additive())
110         });
111         self
112     }
113 
114     /// Sets the color write mask for all attachments.
115     #[inline]
color_write_mask(mut self, color_write_mask: ColorComponents) -> Self116     pub fn color_write_mask(mut self, color_write_mask: ColorComponents) -> Self {
117         self.attachments
118             .iter_mut()
119             .for_each(|attachment_state| attachment_state.color_write_mask = color_write_mask);
120         self
121     }
122 
123     /// Sets the blend constants.
124     #[inline]
blend_constants(mut self, constants: [f32; 4]) -> Self125     pub fn blend_constants(mut self, constants: [f32; 4]) -> Self {
126         self.blend_constants = StateMode::Fixed(constants);
127         self
128     }
129 
130     /// Sets the blend constants as dynamic.
131     #[inline]
blend_constants_dynamic(mut self) -> Self132     pub fn blend_constants_dynamic(mut self) -> Self {
133         self.blend_constants = StateMode::Dynamic;
134         self
135     }
136 }
137 
138 impl Default for ColorBlendState {
139     /// Returns [`ColorBlendState::new(1)`].
140     #[inline]
default() -> Self141     fn default() -> Self {
142         Self::new(1)
143     }
144 }
145 
146 vulkan_enum! {
147     #[non_exhaustive]
148     /// Which logical operation to apply to the output values.
149     ///
150     /// The operation is applied individually for each channel (red, green, blue and alpha).
151     ///
152     /// Only relevant for integer or unsigned attachments.
153     ///
154     /// Also note that some implementations don't support logic operations.
155     LogicOp = LogicOp(i32);
156 
157     /// Returns `0`.
158     Clear = CLEAR,
159 
160     /// Returns `source & destination`.
161     And = AND,
162 
163     /// Returns `source & !destination`.
164     AndReverse = AND_REVERSE,
165 
166     /// Returns `source`.
167     Copy = COPY,
168 
169     /// Returns `!source & destination`.
170     AndInverted = AND_INVERTED,
171 
172     /// Returns `destination`.
173     Noop = NO_OP,
174 
175     /// Returns `source ^ destination`.
176     Xor = XOR,
177 
178     /// Returns `source | destination`.
179     Or = OR,
180 
181     /// Returns `!(source | destination)`.
182     Nor = NOR,
183 
184     /// Returns `!(source ^ destination)`.
185     Equivalent = EQUIVALENT,
186 
187     /// Returns `!destination`.
188     Invert = INVERT,
189 
190     /// Returns `source | !destination.
191     OrReverse = OR_REVERSE,
192 
193     /// Returns `!source`.
194     CopyInverted = COPY_INVERTED,
195 
196     /// Returns `!source | destination`.
197     OrInverted = OR_INVERTED,
198 
199     /// Returns `!(source & destination)`.
200     Nand = NAND,
201 
202     /// Returns `!0` (all bits set to 1).
203     Set = SET,
204 
205 }
206 
207 impl Default for LogicOp {
208     #[inline]
default() -> LogicOp209     fn default() -> LogicOp {
210         LogicOp::Noop
211     }
212 }
213 
214 /// Describes how a framebuffer color attachment is handled in the pipeline during the color
215 /// blend stage.
216 #[derive(Clone, Debug)]
217 pub struct ColorBlendAttachmentState {
218     /// The blend parameters for the attachment.
219     ///
220     /// If set to `None`, blending is disabled, and all incoming pixels will be used directly.
221     pub blend: Option<AttachmentBlend>,
222 
223     /// Sets which components of the final pixel value are written to the attachment.
224     pub color_write_mask: ColorComponents,
225 
226     /// Sets whether anything at all is written to the attachment. If enabled, the pixel data
227     /// that is written is determined by the `color_write_mask`. If disabled, the mask is ignored
228     /// and nothing is written.
229     ///
230     /// If set to anything other than `Fixed(true)`, the
231     /// [`color_write_enable`](crate::device::Features::color_write_enable) feature must be enabled
232     /// on the device.
233     pub color_write_enable: StateMode<bool>,
234 }
235 
236 /// Describes how the blending system should behave for an attachment.
237 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
238 pub struct AttachmentBlend {
239     /// The operation to apply between the color components of the source and destination pixels,
240     /// to produce the final pixel value.
241     pub color_op: BlendOp,
242 
243     /// The operation to apply to the source color component before applying `color_op`.
244     pub color_source: BlendFactor,
245 
246     /// The operation to apply to the destination color component before applying `color_op`.
247     pub color_destination: BlendFactor,
248 
249     /// The operation to apply between the alpha component of the source and destination pixels,
250     /// to produce the final pixel value.
251     pub alpha_op: BlendOp,
252 
253     /// The operation to apply to the source alpha component before applying `alpha_op`.
254     pub alpha_source: BlendFactor,
255 
256     /// The operation to apply to the destination alpha component before applying `alpha_op`.
257     pub alpha_destination: BlendFactor,
258 }
259 
260 impl AttachmentBlend {
261     /// Builds an `AttachmentBlend` where the output of the fragment shader is ignored and the
262     /// destination is untouched.
263     #[inline]
ignore_source() -> Self264     pub fn ignore_source() -> Self {
265         Self {
266             color_op: BlendOp::Add,
267             color_source: BlendFactor::Zero,
268             color_destination: BlendFactor::DstColor,
269             alpha_op: BlendOp::Add,
270             alpha_source: BlendFactor::Zero,
271             alpha_destination: BlendFactor::DstColor,
272         }
273     }
274 
275     /// Builds an `AttachmentBlend` where the output will be merged with the existing value
276     /// based on the alpha of the source.
277     #[inline]
alpha() -> Self278     pub fn alpha() -> Self {
279         Self {
280             color_op: BlendOp::Add,
281             color_source: BlendFactor::SrcAlpha,
282             color_destination: BlendFactor::OneMinusSrcAlpha,
283             alpha_op: BlendOp::Add,
284             alpha_source: BlendFactor::SrcAlpha,
285             alpha_destination: BlendFactor::OneMinusSrcAlpha,
286         }
287     }
288 
289     /// Builds an `AttachmentBlend` where the colors are added, and alpha is set to the maximum of
290     /// the two.
291     #[inline]
additive() -> Self292     pub fn additive() -> Self {
293         Self {
294             color_op: BlendOp::Add,
295             color_source: BlendFactor::One,
296             color_destination: BlendFactor::One,
297             alpha_op: BlendOp::Max,
298             alpha_source: BlendFactor::One,
299             alpha_destination: BlendFactor::One,
300         }
301     }
302 }
303 
304 impl From<AttachmentBlend> for ash::vk::PipelineColorBlendAttachmentState {
305     #[inline]
from(val: AttachmentBlend) -> Self306     fn from(val: AttachmentBlend) -> Self {
307         ash::vk::PipelineColorBlendAttachmentState {
308             blend_enable: ash::vk::TRUE,
309             src_color_blend_factor: val.color_source.into(),
310             dst_color_blend_factor: val.color_destination.into(),
311             color_blend_op: val.color_op.into(),
312             src_alpha_blend_factor: val.alpha_source.into(),
313             dst_alpha_blend_factor: val.alpha_destination.into(),
314             alpha_blend_op: val.alpha_op.into(),
315             color_write_mask: ash::vk::ColorComponentFlags::empty(), // Overwritten by GraphicsPipelineBuilder
316         }
317     }
318 }
319 
320 vulkan_enum! {
321     #[non_exhaustive]
322 
323     /// The operation that takes `source` (output from the fragment shader), `destination` (value
324     /// currently in the framebuffer attachment) and `blend_constant` input values,
325     /// and produces new inputs to be fed to `BlendOp`.
326     ///
327     /// Some operations take `source1` as an input, representing the second source value. The
328     /// [`dual_src_blend`](crate::device::Features::dual_src_blend) feature must be enabled on the
329     /// device when these are used.
330     BlendFactor = BlendFactor(i32);
331 
332     /// Always `0`.
333     Zero = ZERO,
334 
335     /// Always `1`.
336     One = ONE,
337 
338     /// `source` component-wise.
339     SrcColor = SRC_COLOR,
340 
341     /// `1 - source` component-wise.
342     OneMinusSrcColor = ONE_MINUS_SRC_COLOR,
343 
344     /// `destination` component-wise.
345     DstColor = DST_COLOR,
346 
347     /// `1 - destination` component-wise.
348     OneMinusDstColor = ONE_MINUS_DST_COLOR,
349 
350     /// `source.a` for all components.
351     SrcAlpha = SRC_ALPHA,
352 
353     /// `1 - source.a` for all components.
354     OneMinusSrcAlpha = ONE_MINUS_SRC_ALPHA,
355 
356     /// `destination.a` for all components.
357     DstAlpha = DST_ALPHA,
358 
359     /// `1 - destination.a` for all components.
360     OneMinusDstAlpha = ONE_MINUS_DST_ALPHA,
361 
362     /// `blend_constants` component-wise.
363     ConstantColor = CONSTANT_COLOR,
364 
365     /// `1 - blend_constants` component-wise.
366     OneMinusConstantColor = ONE_MINUS_CONSTANT_COLOR,
367 
368     /// `blend_constants.a` for all components.
369     ///
370     /// On [portability subset](crate::instance#portability-subset-devices-and-the-enumerate_portability-flag)
371     /// devices, if this value is used for the `color_source` or `color_destination` blend factors,
372     /// then the
373     /// [`constant_alpha_color_blend_factors`](crate::device::Features::constant_alpha_color_blend_factors)
374     /// feature must be enabled on the device.
375     ConstantAlpha = CONSTANT_ALPHA,
376 
377     /// `1 - blend_constants.a` for all components.
378     ///
379     /// On [portability subset](crate::instance#portability-subset-devices-and-the-enumerate_portability-flag)
380     /// devices, if this value is used for the `color_source` or `color_destination` blend factors,
381     /// then the
382     /// [`constant_alpha_color_blend_factors`](crate::device::Features::constant_alpha_color_blend_factors)
383     /// feature must be enabled on the device.
384     OneMinusConstantAlpha = ONE_MINUS_CONSTANT_ALPHA,
385 
386     /// For the alpha component, always `1`. For the color components,
387     /// `min(source.a, 1 - destination.a)` for all components.
388     SrcAlphaSaturate = SRC_ALPHA_SATURATE,
389 
390     /// `source1` component-wise.
391     Src1Color = SRC1_COLOR,
392 
393     /// `1 - source1` component-wise.
394     OneMinusSrc1Color = ONE_MINUS_SRC1_COLOR,
395 
396     /// `source1.a` for all components.
397     Src1Alpha = SRC1_ALPHA,
398 
399     /// `1 - source1.a` for all components.
400     OneMinusSrc1Alpha = ONE_MINUS_SRC1_ALPHA,
401 }
402 
403 vulkan_enum! {
404     #[non_exhaustive]
405 
406     /// The arithmetic operation that is applied between the `source` and `destination` component
407     /// values, after the appropriate `BlendFactor` is applied to both.
408     BlendOp = BlendOp(i32);
409 
410     /// `source + destination`.
411     Add = ADD,
412 
413     /// `source - destination`.
414     Subtract = SUBTRACT,
415 
416     /// `destination - source`.
417     ReverseSubtract = REVERSE_SUBTRACT,
418 
419     /// `min(source, destination)`.
420     Min = MIN,
421 
422     /// `max(source, destination)`.
423     Max = MAX,
424 
425     /* TODO: enable
426     // TODO: document
427     Zero = ZERO_EXT {
428         device_extensions: [ext_blend_operation_advanced],
429     },*/
430 
431     /* TODO: enable
432     // TODO: document
433     Src = SRC_EXT {
434         device_extensions: [ext_blend_operation_advanced],
435     },*/
436 
437     /* TODO: enable
438     // TODO: document
439     Dst = DST_EXT {
440         device_extensions: [ext_blend_operation_advanced],
441     },*/
442 
443     /* TODO: enable
444     // TODO: document
445     SrcOver = SRC_OVER_EXT {
446         device_extensions: [ext_blend_operation_advanced],
447     },*/
448 
449     /* TODO: enable
450     // TODO: document
451     DstOver = DST_OVER_EXT {
452         device_extensions: [ext_blend_operation_advanced],
453     },*/
454 
455     /* TODO: enable
456     // TODO: document
457     SrcIn = SRC_IN_EXT {
458         device_extensions: [ext_blend_operation_advanced],
459     },*/
460 
461     /* TODO: enable
462     // TODO: document
463     DstIn = DST_IN_EXT {
464         device_extensions: [ext_blend_operation_advanced],
465     },*/
466 
467     /* TODO: enable
468     // TODO: document
469     SrcOut = SRC_OUT_EXT {
470         device_extensions: [ext_blend_operation_advanced],
471     },*/
472 
473     /* TODO: enable
474     // TODO: document
475     DstOut = DST_OUT_EXT {
476         device_extensions: [ext_blend_operation_advanced],
477     },*/
478 
479     /* TODO: enable
480     // TODO: document
481     SrcAtop = SRC_ATOP_EXT {
482         device_extensions: [ext_blend_operation_advanced],
483     },*/
484 
485     /* TODO: enable
486     // TODO: document
487     DstAtop = DST_ATOP_EXT {
488         device_extensions: [ext_blend_operation_advanced],
489     },*/
490 
491     /* TODO: enable
492     // TODO: document
493     Xor = XOR_EXT {
494         device_extensions: [ext_blend_operation_advanced],
495     },*/
496 
497     /* TODO: enable
498     // TODO: document
499     Multiply = MULTIPLY_EXT {
500         device_extensions: [ext_blend_operation_advanced],
501     },*/
502 
503     /* TODO: enable
504     // TODO: document
505     Screen = SCREEN_EXT {
506         device_extensions: [ext_blend_operation_advanced],
507     },*/
508 
509     /* TODO: enable
510     // TODO: document
511     Overlay = OVERLAY_EXT {
512         device_extensions: [ext_blend_operation_advanced],
513     },*/
514 
515     /* TODO: enable
516     // TODO: document
517     Darken = DARKEN_EXT {
518         device_extensions: [ext_blend_operation_advanced],
519     },*/
520 
521     /* TODO: enable
522     // TODO: document
523     Lighten = LIGHTEN_EXT {
524         device_extensions: [ext_blend_operation_advanced],
525     },*/
526 
527     /* TODO: enable
528     // TODO: document
529     Colordodge = COLORDODGE_EXT {
530         device_extensions: [ext_blend_operation_advanced],
531     },*/
532 
533     /* TODO: enable
534     // TODO: document
535     Colorburn = COLORBURN_EXT {
536         device_extensions: [ext_blend_operation_advanced],
537     },*/
538 
539     /* TODO: enable
540     // TODO: document
541     Hardlight = HARDLIGHT_EXT {
542         device_extensions: [ext_blend_operation_advanced],
543     },*/
544 
545     /* TODO: enable
546     // TODO: document
547     Softlight = SOFTLIGHT_EXT {
548         device_extensions: [ext_blend_operation_advanced],
549     },*/
550 
551     /* TODO: enable
552     // TODO: document
553     Difference = DIFFERENCE_EXT {
554         device_extensions: [ext_blend_operation_advanced],
555     },*/
556 
557     /* TODO: enable
558     // TODO: document
559     Exclusion = EXCLUSION_EXT {
560         device_extensions: [ext_blend_operation_advanced],
561     },*/
562 
563     /* TODO: enable
564     // TODO: document
565     Invert = INVERT_EXT {
566         device_extensions: [ext_blend_operation_advanced],
567     },*/
568 
569     /* TODO: enable
570     // TODO: document
571     InvertRgb = INVERT_RGB_EXT {
572         device_extensions: [ext_blend_operation_advanced],
573     },*/
574 
575     /* TODO: enable
576     // TODO: document
577     Lineardodge = LINEARDODGE_EXT {
578         device_extensions: [ext_blend_operation_advanced],
579     },*/
580 
581     /* TODO: enable
582     // TODO: document
583     Linearburn = LINEARBURN_EXT {
584         device_extensions: [ext_blend_operation_advanced],
585     },*/
586 
587     /* TODO: enable
588     // TODO: document
589     Vividlight = VIVIDLIGHT_EXT {
590         device_extensions: [ext_blend_operation_advanced],
591     },*/
592 
593     /* TODO: enable
594     // TODO: document
595     Linearlight = LINEARLIGHT_EXT {
596         device_extensions: [ext_blend_operation_advanced],
597     },*/
598 
599     /* TODO: enable
600     // TODO: document
601     Pinlight = PINLIGHT_EXT {
602         device_extensions: [ext_blend_operation_advanced],
603     },*/
604 
605     /* TODO: enable
606     // TODO: document
607     Hardmix = HARDMIX_EXT {
608         device_extensions: [ext_blend_operation_advanced],
609     },*/
610 
611     /* TODO: enable
612     // TODO: document
613     HslHue = HSL_HUE_EXT {
614         device_extensions: [ext_blend_operation_advanced],
615     },*/
616 
617     /* TODO: enable
618     // TODO: document
619     HslSaturation = HSL_SATURATION_EXT {
620         device_extensions: [ext_blend_operation_advanced],
621     },*/
622 
623     /* TODO: enable
624     // TODO: document
625     HslColor = HSL_COLOR_EXT {
626         device_extensions: [ext_blend_operation_advanced],
627     },*/
628 
629     /* TODO: enable
630     // TODO: document
631     HslLuminosity = HSL_LUMINOSITY_EXT {
632         device_extensions: [ext_blend_operation_advanced],
633     },*/
634 
635     /* TODO: enable
636     // TODO: document
637     Plus = PLUS_EXT {
638         device_extensions: [ext_blend_operation_advanced],
639     },*/
640 
641     /* TODO: enable
642     // TODO: document
643     PlusClamped = PLUS_CLAMPED_EXT {
644         device_extensions: [ext_blend_operation_advanced],
645     },*/
646 
647     /* TODO: enable
648     // TODO: document
649     PlusClampedAlpha = PLUS_CLAMPED_ALPHA_EXT {
650         device_extensions: [ext_blend_operation_advanced],
651     },*/
652 
653     /* TODO: enable
654     // TODO: document
655     PlusDarker = PLUS_DARKER_EXT {
656         device_extensions: [ext_blend_operation_advanced],
657     },*/
658 
659     /* TODO: enable
660     // TODO: document
661     Minus = MINUS_EXT {
662         device_extensions: [ext_blend_operation_advanced],
663     },*/
664 
665     /* TODO: enable
666     // TODO: document
667     MinusClamped = MINUS_CLAMPED_EXT {
668         device_extensions: [ext_blend_operation_advanced],
669     },*/
670 
671     /* TODO: enable
672     // TODO: document
673     Contrast = CONTRAST_EXT {
674         device_extensions: [ext_blend_operation_advanced],
675     },*/
676 
677     /* TODO: enable
678     // TODO: document
679     InvertOvg = INVERT_OVG_EXT {
680         device_extensions: [ext_blend_operation_advanced],
681     },*/
682 
683     /* TODO: enable
684     // TODO: document
685     Red = RED_EXT {
686         device_extensions: [ext_blend_operation_advanced],
687     },*/
688 
689     /* TODO: enable
690     // TODO: document
691     Green = GREEN_EXT {
692         device_extensions: [ext_blend_operation_advanced],
693     },*/
694 
695     /* TODO: enable
696     // TODO: document
697     Blue = BLUE_EXT {
698         device_extensions: [ext_blend_operation_advanced],
699     },*/
700 }
701 
702 vulkan_bitflags! {
703     /// A mask specifying color components that can be written to a framebuffer attachment.
704     ColorComponents = ColorComponentFlags(u32);
705 
706     /// The red component.
707     R = R,
708 
709     /// The green component.
710     G = G,
711 
712     /// The blue component.
713     B = B,
714 
715     /// The alpha component.
716     A = A,
717 }
718