aboutsummaryrefslogtreecommitdiff
path: root/src/pipeline/graphics/color_blend.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/pipeline/graphics/color_blend.rs')
-rw-r--r--src/pipeline/graphics/color_blend.rs717
1 files changed, 717 insertions, 0 deletions
diff --git a/src/pipeline/graphics/color_blend.rs b/src/pipeline/graphics/color_blend.rs
new file mode 100644
index 0000000..8acc342
--- /dev/null
+++ b/src/pipeline/graphics/color_blend.rs
@@ -0,0 +1,717 @@
+// Copyright (c) 2016 The vulkano developers
+// Licensed under the Apache License, Version 2.0
+// <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
+// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
+// at your option. All files in the project carrying such
+// notice may not be copied, modified, or distributed except
+// according to those terms.
+
+//! Configures how the color output of the fragment shader is written to the attachment.
+//!
+//! # Blending in details
+//!
+//! There are three kinds of color attachments for the purpose of blending:
+//!
+//! - Attachments with a floating-point or fixed point format.
+//! - Attachments with a (non-normalized) integer format.
+//! - Attachments with a normalized integer format.
+//!
+//! For floating-point and fixed-point formats, the blending operation is applied. For integer
+//! formats, the logic operation is applied. For normalized integer formats, the logic operation
+//! will take precedence if it is activated, otherwise the blending operation is applied.
+
+use crate::{
+ macros::{vulkan_bitflags, vulkan_enum},
+ pipeline::StateMode,
+};
+
+/// Describes how the color output of the fragment shader is written to the attachment. See the
+/// documentation of the `blend` module for more info.
+#[derive(Clone, Debug)]
+pub struct ColorBlendState {
+ /// Sets the logical operation to perform between the incoming fragment color and the existing
+ /// fragment in the framebuffer attachment.
+ ///
+ /// If set to `Some`, the [`logic_op`](crate::device::Features::logic_op) feature must be
+ /// enabled on the device. If set to `Some(Dynamic)`, then the
+ /// [`extended_dynamic_state2_logic_op`](crate::device::Features::extended_dynamic_state2_logic_op)
+ /// feature must also be enabled on the device.
+ pub logic_op: Option<StateMode<LogicOp>>,
+
+ /// Sets the blend and output state for each color attachment. The number of elements must match
+ /// the number of color attachments in the framebuffer.
+ ///
+ /// If there are multiple elements, and the `blend` and `color_write_mask` members of each
+ /// element differ, then the [`independent_blend`](crate::device::Features::independent_blend)
+ /// feature must be enabled on the device.
+ pub attachments: Vec<ColorBlendAttachmentState>,
+
+ /// The constant color to use for some of the `BlendFactor` variants.
+ pub blend_constants: StateMode<[f32; 4]>,
+}
+
+impl ColorBlendState {
+ /// Creates a `ColorBlendState` with logical operations disabled, blend constants set to zero,
+ /// and `num` attachment entries that have blending disabled, and color write and all color
+ /// components enabled.
+ #[inline]
+ pub fn new(num: u32) -> Self {
+ Self {
+ logic_op: None,
+ attachments: (0..num)
+ .map(|_| ColorBlendAttachmentState {
+ blend: None,
+ color_write_mask: ColorComponents::all(),
+ color_write_enable: StateMode::Fixed(true),
+ })
+ .collect(),
+ blend_constants: StateMode::Fixed([0.0, 0.0, 0.0, 0.0]),
+ }
+ }
+
+ /// Enables logical operations with the given logical operation.
+ #[inline]
+ pub fn logic_op(mut self, logic_op: LogicOp) -> Self {
+ self.logic_op = Some(StateMode::Fixed(logic_op));
+ self
+ }
+
+ /// Enables logical operations with a dynamic logical operation.
+ #[inline]
+ pub fn logic_op_dynamic(mut self) -> Self {
+ self.logic_op = Some(StateMode::Dynamic);
+ self
+ }
+
+ /// Enables blending for all attachments, with the given parameters.
+ #[inline]
+ pub fn blend(mut self, blend: AttachmentBlend) -> Self {
+ self.attachments
+ .iter_mut()
+ .for_each(|attachment_state| attachment_state.blend = Some(blend));
+ self
+ }
+
+ /// Enables blending for all attachments, with alpha blending.
+ #[inline]
+ pub fn blend_alpha(mut self) -> Self {
+ self.attachments
+ .iter_mut()
+ .for_each(|attachment_state| attachment_state.blend = Some(AttachmentBlend::alpha()));
+ self
+ }
+
+ /// Enables blending for all attachments, with additive blending.
+ #[inline]
+ pub fn blend_additive(mut self) -> Self {
+ self.attachments.iter_mut().for_each(|attachment_state| {
+ attachment_state.blend = Some(AttachmentBlend::additive())
+ });
+ self
+ }
+
+ /// Sets the color write mask for all attachments.
+ #[inline]
+ pub fn color_write_mask(mut self, color_write_mask: ColorComponents) -> Self {
+ self.attachments
+ .iter_mut()
+ .for_each(|attachment_state| attachment_state.color_write_mask = color_write_mask);
+ self
+ }
+
+ /// Sets the blend constants.
+ #[inline]
+ pub fn blend_constants(mut self, constants: [f32; 4]) -> Self {
+ self.blend_constants = StateMode::Fixed(constants);
+ self
+ }
+
+ /// Sets the blend constants as dynamic.
+ #[inline]
+ pub fn blend_constants_dynamic(mut self) -> Self {
+ self.blend_constants = StateMode::Dynamic;
+ self
+ }
+}
+
+impl Default for ColorBlendState {
+ /// Returns [`ColorBlendState::new(1)`].
+ #[inline]
+ fn default() -> Self {
+ Self::new(1)
+ }
+}
+
+vulkan_enum! {
+ #[non_exhaustive]
+ /// Which logical operation to apply to the output values.
+ ///
+ /// The operation is applied individually for each channel (red, green, blue and alpha).
+ ///
+ /// Only relevant for integer or unsigned attachments.
+ ///
+ /// Also note that some implementations don't support logic operations.
+ LogicOp = LogicOp(i32);
+
+ /// Returns `0`.
+ Clear = CLEAR,
+
+ /// Returns `source & destination`.
+ And = AND,
+
+ /// Returns `source & !destination`.
+ AndReverse = AND_REVERSE,
+
+ /// Returns `source`.
+ Copy = COPY,
+
+ /// Returns `!source & destination`.
+ AndInverted = AND_INVERTED,
+
+ /// Returns `destination`.
+ Noop = NO_OP,
+
+ /// Returns `source ^ destination`.
+ Xor = XOR,
+
+ /// Returns `source | destination`.
+ Or = OR,
+
+ /// Returns `!(source | destination)`.
+ Nor = NOR,
+
+ /// Returns `!(source ^ destination)`.
+ Equivalent = EQUIVALENT,
+
+ /// Returns `!destination`.
+ Invert = INVERT,
+
+ /// Returns `source | !destination.
+ OrReverse = OR_REVERSE,
+
+ /// Returns `!source`.
+ CopyInverted = COPY_INVERTED,
+
+ /// Returns `!source | destination`.
+ OrInverted = OR_INVERTED,
+
+ /// Returns `!(source & destination)`.
+ Nand = NAND,
+
+ /// Returns `!0` (all bits set to 1).
+ Set = SET,
+
+}
+
+impl Default for LogicOp {
+ #[inline]
+ fn default() -> LogicOp {
+ LogicOp::Noop
+ }
+}
+
+/// Describes how a framebuffer color attachment is handled in the pipeline during the color
+/// blend stage.
+#[derive(Clone, Debug)]
+pub struct ColorBlendAttachmentState {
+ /// The blend parameters for the attachment.
+ ///
+ /// If set to `None`, blending is disabled, and all incoming pixels will be used directly.
+ pub blend: Option<AttachmentBlend>,
+
+ /// Sets which components of the final pixel value are written to the attachment.
+ pub color_write_mask: ColorComponents,
+
+ /// Sets whether anything at all is written to the attachment. If enabled, the pixel data
+ /// that is written is determined by the `color_write_mask`. If disabled, the mask is ignored
+ /// and nothing is written.
+ ///
+ /// If set to anything other than `Fixed(true)`, the
+ /// [`color_write_enable`](crate::device::Features::color_write_enable) feature must be enabled
+ /// on the device.
+ pub color_write_enable: StateMode<bool>,
+}
+
+/// Describes how the blending system should behave for an attachment.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct AttachmentBlend {
+ /// The operation to apply between the color components of the source and destination pixels,
+ /// to produce the final pixel value.
+ pub color_op: BlendOp,
+
+ /// The operation to apply to the source color component before applying `color_op`.
+ pub color_source: BlendFactor,
+
+ /// The operation to apply to the destination color component before applying `color_op`.
+ pub color_destination: BlendFactor,
+
+ /// The operation to apply between the alpha component of the source and destination pixels,
+ /// to produce the final pixel value.
+ pub alpha_op: BlendOp,
+
+ /// The operation to apply to the source alpha component before applying `alpha_op`.
+ pub alpha_source: BlendFactor,
+
+ /// The operation to apply to the destination alpha component before applying `alpha_op`.
+ pub alpha_destination: BlendFactor,
+}
+
+impl AttachmentBlend {
+ /// Builds an `AttachmentBlend` where the output of the fragment shader is ignored and the
+ /// destination is untouched.
+ #[inline]
+ pub fn ignore_source() -> Self {
+ Self {
+ color_op: BlendOp::Add,
+ color_source: BlendFactor::Zero,
+ color_destination: BlendFactor::DstColor,
+ alpha_op: BlendOp::Add,
+ alpha_source: BlendFactor::Zero,
+ alpha_destination: BlendFactor::DstColor,
+ }
+ }
+
+ /// Builds an `AttachmentBlend` where the output will be merged with the existing value
+ /// based on the alpha of the source.
+ #[inline]
+ pub fn alpha() -> Self {
+ Self {
+ color_op: BlendOp::Add,
+ color_source: BlendFactor::SrcAlpha,
+ color_destination: BlendFactor::OneMinusSrcAlpha,
+ alpha_op: BlendOp::Add,
+ alpha_source: BlendFactor::SrcAlpha,
+ alpha_destination: BlendFactor::OneMinusSrcAlpha,
+ }
+ }
+
+ /// Builds an `AttachmentBlend` where the colors are added, and alpha is set to the maximum of
+ /// the two.
+ #[inline]
+ pub fn additive() -> Self {
+ Self {
+ color_op: BlendOp::Add,
+ color_source: BlendFactor::One,
+ color_destination: BlendFactor::One,
+ alpha_op: BlendOp::Max,
+ alpha_source: BlendFactor::One,
+ alpha_destination: BlendFactor::One,
+ }
+ }
+}
+
+impl From<AttachmentBlend> for ash::vk::PipelineColorBlendAttachmentState {
+ #[inline]
+ fn from(val: AttachmentBlend) -> Self {
+ ash::vk::PipelineColorBlendAttachmentState {
+ blend_enable: ash::vk::TRUE,
+ src_color_blend_factor: val.color_source.into(),
+ dst_color_blend_factor: val.color_destination.into(),
+ color_blend_op: val.color_op.into(),
+ src_alpha_blend_factor: val.alpha_source.into(),
+ dst_alpha_blend_factor: val.alpha_destination.into(),
+ alpha_blend_op: val.alpha_op.into(),
+ color_write_mask: ash::vk::ColorComponentFlags::empty(), // Overwritten by GraphicsPipelineBuilder
+ }
+ }
+}
+
+vulkan_enum! {
+ #[non_exhaustive]
+
+ /// The operation that takes `source` (output from the fragment shader), `destination` (value
+ /// currently in the framebuffer attachment) and `blend_constant` input values,
+ /// and produces new inputs to be fed to `BlendOp`.
+ ///
+ /// Some operations take `source1` as an input, representing the second source value. The
+ /// [`dual_src_blend`](crate::device::Features::dual_src_blend) feature must be enabled on the
+ /// device when these are used.
+ BlendFactor = BlendFactor(i32);
+
+ /// Always `0`.
+ Zero = ZERO,
+
+ /// Always `1`.
+ One = ONE,
+
+ /// `source` component-wise.
+ SrcColor = SRC_COLOR,
+
+ /// `1 - source` component-wise.
+ OneMinusSrcColor = ONE_MINUS_SRC_COLOR,
+
+ /// `destination` component-wise.
+ DstColor = DST_COLOR,
+
+ /// `1 - destination` component-wise.
+ OneMinusDstColor = ONE_MINUS_DST_COLOR,
+
+ /// `source.a` for all components.
+ SrcAlpha = SRC_ALPHA,
+
+ /// `1 - source.a` for all components.
+ OneMinusSrcAlpha = ONE_MINUS_SRC_ALPHA,
+
+ /// `destination.a` for all components.
+ DstAlpha = DST_ALPHA,
+
+ /// `1 - destination.a` for all components.
+ OneMinusDstAlpha = ONE_MINUS_DST_ALPHA,
+
+ /// `blend_constants` component-wise.
+ ConstantColor = CONSTANT_COLOR,
+
+ /// `1 - blend_constants` component-wise.
+ OneMinusConstantColor = ONE_MINUS_CONSTANT_COLOR,
+
+ /// `blend_constants.a` for all components.
+ ///
+ /// On [portability subset](crate::instance#portability-subset-devices-and-the-enumerate_portability-flag)
+ /// devices, if this value is used for the `color_source` or `color_destination` blend factors,
+ /// then the
+ /// [`constant_alpha_color_blend_factors`](crate::device::Features::constant_alpha_color_blend_factors)
+ /// feature must be enabled on the device.
+ ConstantAlpha = CONSTANT_ALPHA,
+
+ /// `1 - blend_constants.a` for all components.
+ ///
+ /// On [portability subset](crate::instance#portability-subset-devices-and-the-enumerate_portability-flag)
+ /// devices, if this value is used for the `color_source` or `color_destination` blend factors,
+ /// then the
+ /// [`constant_alpha_color_blend_factors`](crate::device::Features::constant_alpha_color_blend_factors)
+ /// feature must be enabled on the device.
+ OneMinusConstantAlpha = ONE_MINUS_CONSTANT_ALPHA,
+
+ /// For the alpha component, always `1`. For the color components,
+ /// `min(source.a, 1 - destination.a)` for all components.
+ SrcAlphaSaturate = SRC_ALPHA_SATURATE,
+
+ /// `source1` component-wise.
+ Src1Color = SRC1_COLOR,
+
+ /// `1 - source1` component-wise.
+ OneMinusSrc1Color = ONE_MINUS_SRC1_COLOR,
+
+ /// `source1.a` for all components.
+ Src1Alpha = SRC1_ALPHA,
+
+ /// `1 - source1.a` for all components.
+ OneMinusSrc1Alpha = ONE_MINUS_SRC1_ALPHA,
+}
+
+vulkan_enum! {
+ #[non_exhaustive]
+
+ /// The arithmetic operation that is applied between the `source` and `destination` component
+ /// values, after the appropriate `BlendFactor` is applied to both.
+ BlendOp = BlendOp(i32);
+
+ /// `source + destination`.
+ Add = ADD,
+
+ /// `source - destination`.
+ Subtract = SUBTRACT,
+
+ /// `destination - source`.
+ ReverseSubtract = REVERSE_SUBTRACT,
+
+ /// `min(source, destination)`.
+ Min = MIN,
+
+ /// `max(source, destination)`.
+ Max = MAX,
+
+ /* TODO: enable
+ // TODO: document
+ Zero = ZERO_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Src = SRC_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Dst = DST_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ SrcOver = SRC_OVER_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ DstOver = DST_OVER_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ SrcIn = SRC_IN_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ DstIn = DST_IN_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ SrcOut = SRC_OUT_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ DstOut = DST_OUT_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ SrcAtop = SRC_ATOP_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ DstAtop = DST_ATOP_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Xor = XOR_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Multiply = MULTIPLY_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Screen = SCREEN_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Overlay = OVERLAY_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Darken = DARKEN_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Lighten = LIGHTEN_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Colordodge = COLORDODGE_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Colorburn = COLORBURN_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Hardlight = HARDLIGHT_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Softlight = SOFTLIGHT_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Difference = DIFFERENCE_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Exclusion = EXCLUSION_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Invert = INVERT_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ InvertRgb = INVERT_RGB_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Lineardodge = LINEARDODGE_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Linearburn = LINEARBURN_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Vividlight = VIVIDLIGHT_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Linearlight = LINEARLIGHT_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Pinlight = PINLIGHT_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Hardmix = HARDMIX_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ HslHue = HSL_HUE_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ HslSaturation = HSL_SATURATION_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ HslColor = HSL_COLOR_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ HslLuminosity = HSL_LUMINOSITY_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Plus = PLUS_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ PlusClamped = PLUS_CLAMPED_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ PlusClampedAlpha = PLUS_CLAMPED_ALPHA_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ PlusDarker = PLUS_DARKER_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Minus = MINUS_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ MinusClamped = MINUS_CLAMPED_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Contrast = CONTRAST_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ InvertOvg = INVERT_OVG_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Red = RED_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Green = GREEN_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+
+ /* TODO: enable
+ // TODO: document
+ Blue = BLUE_EXT {
+ device_extensions: [ext_blend_operation_advanced],
+ },*/
+}
+
+vulkan_bitflags! {
+ /// A mask specifying color components that can be written to a framebuffer attachment.
+ ColorComponents = ColorComponentFlags(u32);
+
+ /// The red component.
+ R = R,
+
+ /// The green component.
+ G = G,
+
+ /// The blue component.
+ B = B,
+
+ /// The alpha component.
+ A = A,
+}