use super::{DynamicState, GraphicsPipelineCreationError};
use crate::{device::Device, pipeline::StateMode, render_pass::Subpass};
use smallvec::SmallVec;
use std::collections::HashMap;
#[derive(Clone, Debug)]
pub struct ColorBlendState {
pub logic_op: Option<StateMode<LogicOp>>,
pub attachments: Vec<ColorBlendAttachmentState>,
pub blend_constants: StateMode<[f32; 4]>,
}
impl ColorBlendState {
#[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]),
}
}
#[inline]
pub fn logic_op(mut self, logic_op: LogicOp) -> Self {
self.logic_op = Some(StateMode::Fixed(logic_op));
self
}
#[inline]
pub fn logic_op_dynamic(mut self) -> Self {
self.logic_op = Some(StateMode::Dynamic);
self
}
#[inline]
pub fn blend(mut self, blend: AttachmentBlend) -> Self {
self.attachments
.iter_mut()
.for_each(|attachment_state| attachment_state.blend = Some(blend));
self
}
#[inline]
pub fn blend_alpha(mut self) -> Self {
self.attachments
.iter_mut()
.for_each(|attachment_state| attachment_state.blend = Some(AttachmentBlend::alpha()));
self
}
#[inline]
pub fn blend_additive(mut self) -> Self {
self.attachments.iter_mut().for_each(|attachment_state| {
attachment_state.blend = Some(AttachmentBlend::additive())
});
self
}
#[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
}
#[inline]
pub fn blend_constants(mut self, constants: [f32; 4]) -> Self {
self.blend_constants = StateMode::Fixed(constants);
self
}
#[inline]
pub fn blend_constants_dynamic(mut self) -> Self {
self.blend_constants = StateMode::Dynamic;
self
}
pub(crate) fn to_vulkan_attachments(
&mut self, device: &Device,
dynamic_state_modes: &mut HashMap<DynamicState, bool>,
subpass: &Subpass,
) -> Result<
(
SmallVec<[ash::vk::PipelineColorBlendAttachmentState; 4]>,
SmallVec<[ash::vk::Bool32; 4]>,
),
GraphicsPipelineCreationError,
> {
let num_atch = subpass.num_color_attachments();
if self.attachments.len() == 1 {
let element = self.attachments.pop().unwrap();
self.attachments
.extend(std::iter::repeat(element).take(num_atch as usize));
} else {
if self.attachments.len() != num_atch as usize {
return Err(GraphicsPipelineCreationError::MismatchBlendingAttachmentsCount);
}
if self.attachments.len() > 1 && !device.enabled_features().independent_blend {
let mut iter = self
.attachments
.iter()
.map(|state| (&state.blend, &state.color_write_mask));
let first = iter.next().unwrap();
if !iter.all(|state| state == first) {
return Err(
GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "independent_blend",
reason: "The blend and color_write_mask members of all elements of ColorBlendState::attachments were not identical",
},
);
}
}
}
let mut color_blend_attachments = SmallVec::with_capacity(self.attachments.len());
let mut color_write_enables = SmallVec::with_capacity(self.attachments.len());
for state in self.attachments.iter() {
let blend = if let Some(blend) = &state.blend {
if !device.enabled_features().dual_src_blend
&& [
blend.color_source,
blend.color_destination,
blend.alpha_source,
blend.alpha_destination,
]
.into_iter()
.any(|blend_factor| {
matches!(
blend_factor,
BlendFactor::Src1Color
| BlendFactor::OneMinusSrc1Color
| BlendFactor::Src1Alpha
| BlendFactor::OneMinusSrc1Alpha
)
})
{
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "dual_src_blend",
reason: "One of the BlendFactor members of AttachmentBlend was set to Src1",
});
}
blend.clone().into()
} else {
Default::default()
};
let color_blend_attachment_state = ash::vk::PipelineColorBlendAttachmentState {
color_write_mask: state.color_write_mask.into(),
..blend
};
let color_write_enable = match state.color_write_enable {
StateMode::Fixed(enable) => {
if !enable && !device.enabled_features().color_write_enable {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "color_write_enable",
reason:
"ColorBlendAttachmentState::color_blend_enable was set to Fixed(false)",
});
}
dynamic_state_modes.insert(DynamicState::ColorWriteEnable, false);
enable as ash::vk::Bool32
}
StateMode::Dynamic => {
if !device.enabled_features().color_write_enable {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "color_write_enable",
reason:
"ColorBlendAttachmentState::color_blend_enable was set to Dynamic",
});
}
dynamic_state_modes.insert(DynamicState::ColorWriteEnable, true);
ash::vk::TRUE
}
};
color_blend_attachments.push(color_blend_attachment_state);
color_write_enables.push(color_write_enable);
}
debug_assert_eq!(color_blend_attachments.len(), color_write_enables.len());
Ok((color_blend_attachments, color_write_enables))
}
pub(crate) fn to_vulkan_color_write(
&self,
device: &Device,
dynamic_state_modes: &mut HashMap<DynamicState, bool>,
color_write_enables: &[ash::vk::Bool32],
) -> Result<Option<ash::vk::PipelineColorWriteCreateInfoEXT>, GraphicsPipelineCreationError>
{
Ok(if device.enabled_extensions().ext_color_write_enable {
Some(ash::vk::PipelineColorWriteCreateInfoEXT {
attachment_count: color_write_enables.len() as u32,
p_color_write_enables: color_write_enables.as_ptr(),
..Default::default()
})
} else {
None
})
}
pub(crate) fn to_vulkan(
&self,
device: &Device,
dynamic_state_modes: &mut HashMap<DynamicState, bool>,
color_blend_attachments: &[ash::vk::PipelineColorBlendAttachmentState],
color_write: Option<&mut ash::vk::PipelineColorWriteCreateInfoEXT>,
) -> Result<ash::vk::PipelineColorBlendStateCreateInfo, GraphicsPipelineCreationError> {
let (logic_op_enable, logic_op) = if let Some(logic_op) = self.logic_op {
if !device.enabled_features().logic_op {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "logic_op",
reason: "ColorBlendState::logic_op was set to Some",
});
}
let logic_op = match logic_op {
StateMode::Fixed(logic_op) => {
dynamic_state_modes.insert(DynamicState::LogicOp, false);
logic_op.into()
}
StateMode::Dynamic => {
if !device.enabled_features().extended_dynamic_state2_logic_op {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state2_logic_op",
reason: "ColorBlendState::logic_op was set to Some(Dynamic)",
});
}
dynamic_state_modes.insert(DynamicState::LogicOp, true);
Default::default()
}
};
(ash::vk::TRUE, logic_op)
} else {
(ash::vk::FALSE, Default::default())
};
let blend_constants = match self.blend_constants {
StateMode::Fixed(blend_constants) => {
dynamic_state_modes.insert(DynamicState::BlendConstants, false);
blend_constants
}
StateMode::Dynamic => {
dynamic_state_modes.insert(DynamicState::BlendConstants, true);
Default::default()
}
};
let mut color_blend_state = ash::vk::PipelineColorBlendStateCreateInfo {
flags: ash::vk::PipelineColorBlendStateCreateFlags::empty(),
logic_op_enable,
logic_op,
attachment_count: color_blend_attachments.len() as u32,
p_attachments: color_blend_attachments.as_ptr(),
blend_constants,
..Default::default()
};
if let Some(color_write) = color_write {
color_write.p_next = color_blend_state.p_next;
color_blend_state.p_next = color_write as *const _ as *const _;
}
Ok(color_blend_state)
}
}
impl Default for ColorBlendState {
#[inline]
fn default() -> Self {
Self::new(1)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(i32)]
pub enum LogicOp {
Clear = ash::vk::LogicOp::CLEAR.as_raw(),
And = ash::vk::LogicOp::AND.as_raw(),
AndReverse = ash::vk::LogicOp::AND_REVERSE.as_raw(),
Copy = ash::vk::LogicOp::COPY.as_raw(),
AndInverted = ash::vk::LogicOp::AND_INVERTED.as_raw(),
Noop = ash::vk::LogicOp::NO_OP.as_raw(),
Xor = ash::vk::LogicOp::XOR.as_raw(),
Or = ash::vk::LogicOp::OR.as_raw(),
Nor = ash::vk::LogicOp::NOR.as_raw(),
Equivalent = ash::vk::LogicOp::EQUIVALENT.as_raw(),
Invert = ash::vk::LogicOp::INVERT.as_raw(),
OrReverse = ash::vk::LogicOp::OR_REVERSE.as_raw(),
CopyInverted = ash::vk::LogicOp::COPY_INVERTED.as_raw(),
OrInverted = ash::vk::LogicOp::OR_INVERTED.as_raw(),
Nand = ash::vk::LogicOp::NAND.as_raw(),
Set = ash::vk::LogicOp::SET.as_raw(),
}
impl From<LogicOp> for ash::vk::LogicOp {
#[inline]
fn from(val: LogicOp) -> Self {
Self::from_raw(val as i32)
}
}
impl Default for LogicOp {
#[inline]
fn default() -> LogicOp {
LogicOp::Noop
}
}
#[derive(Clone, Debug)]
pub struct ColorBlendAttachmentState {
pub blend: Option<AttachmentBlend>,
pub color_write_mask: ColorComponents,
pub color_write_enable: StateMode<bool>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct AttachmentBlend {
pub color_op: BlendOp,
pub color_source: BlendFactor,
pub color_destination: BlendFactor,
pub alpha_op: BlendOp,
pub alpha_source: BlendFactor,
pub alpha_destination: BlendFactor,
}
impl AttachmentBlend {
#[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,
}
}
#[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,
}
}
#[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(), }
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(i32)]
pub enum BlendFactor {
Zero = ash::vk::BlendFactor::ZERO.as_raw(),
One = ash::vk::BlendFactor::ONE.as_raw(),
SrcColor = ash::vk::BlendFactor::SRC_COLOR.as_raw(),
OneMinusSrcColor = ash::vk::BlendFactor::ONE_MINUS_SRC_COLOR.as_raw(),
DstColor = ash::vk::BlendFactor::DST_COLOR.as_raw(),
OneMinusDstColor = ash::vk::BlendFactor::ONE_MINUS_DST_COLOR.as_raw(),
SrcAlpha = ash::vk::BlendFactor::SRC_ALPHA.as_raw(),
OneMinusSrcAlpha = ash::vk::BlendFactor::ONE_MINUS_SRC_ALPHA.as_raw(),
DstAlpha = ash::vk::BlendFactor::DST_ALPHA.as_raw(),
OneMinusDstAlpha = ash::vk::BlendFactor::ONE_MINUS_DST_ALPHA.as_raw(),
ConstantColor = ash::vk::BlendFactor::CONSTANT_COLOR.as_raw(),
OneMinusConstantColor = ash::vk::BlendFactor::ONE_MINUS_CONSTANT_COLOR.as_raw(),
ConstantAlpha = ash::vk::BlendFactor::CONSTANT_ALPHA.as_raw(),
OneMinusConstantAlpha = ash::vk::BlendFactor::ONE_MINUS_CONSTANT_ALPHA.as_raw(),
SrcAlphaSaturate = ash::vk::BlendFactor::SRC_ALPHA_SATURATE.as_raw(),
Src1Color = ash::vk::BlendFactor::SRC1_COLOR.as_raw(),
OneMinusSrc1Color = ash::vk::BlendFactor::ONE_MINUS_SRC1_COLOR.as_raw(),
Src1Alpha = ash::vk::BlendFactor::SRC1_ALPHA.as_raw(),
OneMinusSrc1Alpha = ash::vk::BlendFactor::ONE_MINUS_SRC1_ALPHA.as_raw(),
}
impl From<BlendFactor> for ash::vk::BlendFactor {
#[inline]
fn from(val: BlendFactor) -> Self {
Self::from_raw(val as i32)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(i32)]
pub enum BlendOp {
Add = ash::vk::BlendOp::ADD.as_raw(),
Subtract = ash::vk::BlendOp::SUBTRACT.as_raw(),
ReverseSubtract = ash::vk::BlendOp::REVERSE_SUBTRACT.as_raw(),
Min = ash::vk::BlendOp::MIN.as_raw(),
Max = ash::vk::BlendOp::MAX.as_raw(),
}
impl From<BlendOp> for ash::vk::BlendOp {
#[inline]
fn from(val: BlendOp) -> Self {
Self::from_raw(val as i32)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ColorComponents {
#[allow(missing_docs)]
pub r: bool,
#[allow(missing_docs)]
pub g: bool,
#[allow(missing_docs)]
pub b: bool,
#[allow(missing_docs)]
pub a: bool,
}
impl ColorComponents {
#[inline]
pub fn none() -> Self {
Self {
r: false,
g: false,
b: false,
a: false,
}
}
#[inline]
pub fn all() -> Self {
Self {
r: true,
g: true,
b: true,
a: true,
}
}
}
impl From<ColorComponents> for ash::vk::ColorComponentFlags {
fn from(val: ColorComponents) -> Self {
let mut result = Self::empty();
if val.r {
result |= ash::vk::ColorComponentFlags::R;
}
if val.g {
result |= ash::vk::ColorComponentFlags::G;
}
if val.b {
result |= ash::vk::ColorComponentFlags::B;
}
if val.a {
result |= ash::vk::ColorComponentFlags::A;
}
result
}
}