use crate::{
device::Device,
macros::{vulkan_bitflags, vulkan_enum},
Requires, RequiresAllOf, RequiresOneOf, ValidationError,
};
use std::iter;
#[derive(Clone, Debug)]
pub struct ColorBlendState {
pub flags: ColorBlendStateFlags,
pub logic_op: Option<LogicOp>,
pub attachments: Vec<ColorBlendAttachmentState>,
pub blend_constants: [f32; 4],
pub _ne: crate::NonExhaustive,
}
impl Default for ColorBlendState {
#[inline]
fn default() -> Self {
Self {
flags: ColorBlendStateFlags::empty(),
logic_op: None,
attachments: Vec::new(),
blend_constants: [0.0; 4],
_ne: crate::NonExhaustive(()),
}
}
}
impl ColorBlendState {
#[inline]
pub fn with_attachment_states(count: u32, attachment_state: ColorBlendAttachmentState) -> Self {
Self {
attachments: iter::repeat(attachment_state)
.take(count as usize)
.collect(),
..Default::default()
}
}
#[inline]
#[deprecated(since = "0.34.0")]
pub fn new(num: u32) -> Self {
Self {
flags: ColorBlendStateFlags::empty(),
logic_op: None,
attachments: (0..num)
.map(|_| ColorBlendAttachmentState::default())
.collect(),
blend_constants: [0.0; 4],
_ne: crate::NonExhaustive(()),
}
}
#[inline]
#[deprecated(since = "0.34.0")]
pub fn logic_op(mut self, logic_op: LogicOp) -> Self {
self.logic_op = Some(logic_op);
self
}
#[inline]
#[deprecated(since = "0.34.0")]
pub fn blend(mut self, blend: AttachmentBlend) -> Self {
self.attachments
.iter_mut()
.for_each(|attachment_state| attachment_state.blend = Some(blend));
self
}
#[inline]
#[deprecated(since = "0.34.0")]
pub fn blend_alpha(mut self) -> Self {
self.attachments
.iter_mut()
.for_each(|attachment_state| attachment_state.blend = Some(AttachmentBlend::alpha()));
self
}
#[inline]
#[deprecated(since = "0.34.0")]
pub fn blend_additive(mut self) -> Self {
self.attachments.iter_mut().for_each(|attachment_state| {
attachment_state.blend = Some(AttachmentBlend::additive())
});
self
}
#[inline]
#[deprecated(since = "0.34.0")]
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]
#[deprecated(since = "0.34.0")]
pub fn blend_constants(mut self, constants: [f32; 4]) -> Self {
self.blend_constants = constants;
self
}
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
flags,
logic_op,
ref attachments,
blend_constants: _,
_ne: _,
} = self;
flags.validate_device(device).map_err(|err| {
err.add_context("flags")
.set_vuids(&["VUID-VkPipelineColorBlendStateCreateInfo-flags-parameter"])
})?;
if let Some(logic_op) = logic_op {
if !device.enabled_features().logic_op {
return Err(Box::new(ValidationError {
context: "logic_op".into(),
problem: "is `Some`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"logic_op",
)])]),
vuids: &["VUID-VkPipelineColorBlendStateCreateInfo-logicOpEnable-00606"],
}));
}
logic_op.validate_device(device).map_err(|err| {
err.add_context("logic_op")
.set_vuids(&["VUID-VkPipelineColorBlendStateCreateInfo-logicOpEnable-00607"])
})?;
}
if device.enabled_features().independent_blend {
for (index, state) in attachments.iter().enumerate() {
state
.validate(device)
.map_err(|err| err.add_context(format!("attachments[{}]", index)))?;
}
} else if let Some(first) = attachments.first() {
first
.validate(device)
.map_err(|err| err.add_context("attachments[0]"))?;
for (index, state) in attachments.iter().enumerate().skip(1) {
if state != first {
return Err(Box::new(ValidationError {
problem: format!(
"`attachments[{}]` does not equal `attachments[0]`",
index
)
.into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"independent_blend",
)])]),
vuids: &["VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605"],
..Default::default()
}));
}
}
}
Ok(())
}
}
vulkan_bitflags! {
#[non_exhaustive]
ColorBlendStateFlags = PipelineColorBlendStateCreateFlags(u32);
}
vulkan_enum! {
#[non_exhaustive]
LogicOp = LogicOp(i32);
Clear = CLEAR,
And = AND,
AndReverse = AND_REVERSE,
Copy = COPY,
AndInverted = AND_INVERTED,
Noop = NO_OP,
Xor = XOR,
Or = OR,
Nor = NOR,
Equivalent = EQUIVALENT,
Invert = INVERT,
OrReverse = OR_REVERSE,
CopyInverted = COPY_INVERTED,
OrInverted = OR_INVERTED,
Nand = NAND,
Set = SET,
}
impl Default for LogicOp {
#[inline]
fn default() -> LogicOp {
LogicOp::Noop
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ColorBlendAttachmentState {
pub blend: Option<AttachmentBlend>,
pub color_write_mask: ColorComponents,
pub color_write_enable: bool,
}
impl Default for ColorBlendAttachmentState {
#[inline]
fn default() -> Self {
Self {
blend: None,
color_write_mask: ColorComponents::all(),
color_write_enable: true,
}
}
}
impl ColorBlendAttachmentState {
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
ref blend,
color_write_mask: _,
color_write_enable,
} = self;
if let Some(blend) = blend {
blend
.validate(device)
.map_err(|err| err.add_context("blend"))?;
}
if !color_write_enable && !device.enabled_features().color_write_enable {
return Err(Box::new(ValidationError {
context: "color_write_enable".into(),
problem: "is `false`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"color_write_enable",
)])]),
vuids: &["VUID-VkPipelineColorWriteCreateInfoEXT-pAttachments-04801"],
}));
}
Ok(())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct AttachmentBlend {
pub src_color_blend_factor: BlendFactor,
pub dst_color_blend_factor: BlendFactor,
pub color_blend_op: BlendOp,
pub src_alpha_blend_factor: BlendFactor,
pub dst_alpha_blend_factor: BlendFactor,
pub alpha_blend_op: BlendOp,
}
impl Default for AttachmentBlend {
#[inline]
fn default() -> Self {
Self {
src_color_blend_factor: BlendFactor::SrcColor,
dst_color_blend_factor: BlendFactor::Zero,
color_blend_op: BlendOp::Add,
src_alpha_blend_factor: BlendFactor::SrcColor,
dst_alpha_blend_factor: BlendFactor::Zero,
alpha_blend_op: BlendOp::Add,
}
}
}
impl AttachmentBlend {
#[inline]
pub fn ignore_source() -> Self {
Self {
src_color_blend_factor: BlendFactor::Zero,
dst_color_blend_factor: BlendFactor::DstColor,
color_blend_op: BlendOp::Add,
src_alpha_blend_factor: BlendFactor::Zero,
dst_alpha_blend_factor: BlendFactor::DstColor,
alpha_blend_op: BlendOp::Add,
}
}
#[inline]
pub fn alpha() -> Self {
Self {
src_color_blend_factor: BlendFactor::SrcAlpha,
dst_color_blend_factor: BlendFactor::OneMinusSrcAlpha,
color_blend_op: BlendOp::Add,
src_alpha_blend_factor: BlendFactor::SrcAlpha,
dst_alpha_blend_factor: BlendFactor::OneMinusSrcAlpha,
alpha_blend_op: BlendOp::Add,
}
}
#[inline]
pub fn additive() -> Self {
Self {
src_color_blend_factor: BlendFactor::One,
dst_color_blend_factor: BlendFactor::One,
color_blend_op: BlendOp::Add,
src_alpha_blend_factor: BlendFactor::One,
dst_alpha_blend_factor: BlendFactor::One,
alpha_blend_op: BlendOp::Max,
}
}
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
src_color_blend_factor,
dst_color_blend_factor,
color_blend_op,
src_alpha_blend_factor,
dst_alpha_blend_factor,
alpha_blend_op,
} = self;
src_color_blend_factor
.validate_device(device)
.map_err(|err| {
err.add_context("src_color_blend_factor").set_vuids(&[
"VUID-VkPipelineColorBlendAttachmentState-srcColorBlendFactor-parameter",
])
})?;
dst_color_blend_factor
.validate_device(device)
.map_err(|err| {
err.add_context("dst_color_blend_factor").set_vuids(&[
"VUID-VkPipelineColorBlendAttachmentState-dstColorBlendFactor-parameter",
])
})?;
color_blend_op.validate_device(device).map_err(|err| {
err.add_context("color_blend_op")
.set_vuids(&["VUID-VkPipelineColorBlendAttachmentState-colorBlendOp-parameter"])
})?;
src_alpha_blend_factor
.validate_device(device)
.map_err(|err| {
err.add_context("src_alpha_blend_factor").set_vuids(&[
"VUID-VkPipelineColorBlendAttachmentState-srcAlphaBlendFactor-parameter",
])
})?;
dst_alpha_blend_factor
.validate_device(device)
.map_err(|err| {
err.add_context("dst_alpha_blend_factor").set_vuids(&[
"VUID-VkPipelineColorBlendAttachmentState-dstAlphaBlendFactor-parameter",
])
})?;
alpha_blend_op.validate_device(device).map_err(|err| {
err.add_context("alpha_blend_op")
.set_vuids(&["VUID-VkPipelineColorBlendAttachmentState-alphaBlendOp-parameter"])
})?;
if !device.enabled_features().dual_src_blend {
if matches!(
src_color_blend_factor,
BlendFactor::Src1Color
| BlendFactor::OneMinusSrc1Color
| BlendFactor::Src1Alpha
| BlendFactor::OneMinusSrc1Alpha
) {
return Err(Box::new(ValidationError {
context: "src_color_blend_factor".into(),
problem: "is `BlendFactor::Src1*`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"dual_src_blend",
)])]),
vuids: &["VUID-VkPipelineColorBlendAttachmentState-srcColorBlendFactor-00608"],
}));
}
if matches!(
dst_color_blend_factor,
BlendFactor::Src1Color
| BlendFactor::OneMinusSrc1Color
| BlendFactor::Src1Alpha
| BlendFactor::OneMinusSrc1Alpha
) {
return Err(Box::new(ValidationError {
context: "dst_color_blend_factor".into(),
problem: "is `BlendFactor::Src1*`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"dual_src_blend",
)])]),
vuids: &["VUID-VkPipelineColorBlendAttachmentState-dstColorBlendFactor-00609"],
}));
}
if matches!(
src_alpha_blend_factor,
BlendFactor::Src1Color
| BlendFactor::OneMinusSrc1Color
| BlendFactor::Src1Alpha
| BlendFactor::OneMinusSrc1Alpha
) {
return Err(Box::new(ValidationError {
context: "src_alpha_blend_factor".into(),
problem: "is `BlendFactor::Src1*`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"dual_src_blend",
)])]),
vuids: &["VUID-VkPipelineColorBlendAttachmentState-srcAlphaBlendFactor-00610"],
}));
}
if matches!(
dst_alpha_blend_factor,
BlendFactor::Src1Color
| BlendFactor::OneMinusSrc1Color
| BlendFactor::Src1Alpha
| BlendFactor::OneMinusSrc1Alpha
) {
return Err(Box::new(ValidationError {
context: "dst_alpha_blend_factor".into(),
problem: "is `BlendFactor::Src1*`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"dual_src_blend",
)])]),
vuids: &["VUID-VkPipelineColorBlendAttachmentState-dstAlphaBlendFactor-00611"],
}));
}
}
if device.enabled_extensions().khr_portability_subset
&& !device.enabled_features().constant_alpha_color_blend_factors
{
if matches!(
src_color_blend_factor,
BlendFactor::ConstantAlpha | BlendFactor::OneMinusConstantAlpha
) {
return Err(Box::new(ValidationError {
problem: "this device is a portability subset device, and \
`src_color_blend_factor` is `BlendFactor::ConstantAlpha` or \
`BlendFactor::OneMinusConstantAlpha`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"constant_alpha_color_blend_factors",
)])]),
vuids: &["VUID-VkPipelineColorBlendAttachmentState-constantAlphaColorBlendFactors-04454"],
..Default::default()
}));
}
if matches!(
dst_color_blend_factor,
BlendFactor::ConstantAlpha | BlendFactor::OneMinusConstantAlpha
) {
return Err(Box::new(ValidationError {
problem: "this device is a portability subset device, and \
`dst_color_blend_factor` is `BlendFactor::ConstantAlpha` or \
`BlendFactor::OneMinusConstantAlpha`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"constant_alpha_color_blend_factors",
)])]),
vuids: &["VUID-VkPipelineColorBlendAttachmentState-constantAlphaColorBlendFactors-04455"],
..Default::default()
}));
}
}
Ok(())
}
}
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.src_color_blend_factor.into(),
dst_color_blend_factor: val.dst_color_blend_factor.into(),
color_blend_op: val.color_blend_op.into(),
src_alpha_blend_factor: val.src_alpha_blend_factor.into(),
dst_alpha_blend_factor: val.dst_alpha_blend_factor.into(),
alpha_blend_op: val.alpha_blend_op.into(),
color_write_mask: ash::vk::ColorComponentFlags::empty(),
}
}
}
vulkan_enum! {
#[non_exhaustive]
BlendFactor = BlendFactor(i32);
Zero = ZERO,
One = ONE,
SrcColor = SRC_COLOR,
OneMinusSrcColor = ONE_MINUS_SRC_COLOR,
DstColor = DST_COLOR,
OneMinusDstColor = ONE_MINUS_DST_COLOR,
SrcAlpha = SRC_ALPHA,
OneMinusSrcAlpha = ONE_MINUS_SRC_ALPHA,
DstAlpha = DST_ALPHA,
OneMinusDstAlpha = ONE_MINUS_DST_ALPHA,
ConstantColor = CONSTANT_COLOR,
OneMinusConstantColor = ONE_MINUS_CONSTANT_COLOR,
ConstantAlpha = CONSTANT_ALPHA,
OneMinusConstantAlpha = ONE_MINUS_CONSTANT_ALPHA,
SrcAlphaSaturate = SRC_ALPHA_SATURATE,
Src1Color = SRC1_COLOR,
OneMinusSrc1Color = ONE_MINUS_SRC1_COLOR,
Src1Alpha = SRC1_ALPHA,
OneMinusSrc1Alpha = ONE_MINUS_SRC1_ALPHA,
}
vulkan_enum! {
#[non_exhaustive]
BlendOp = BlendOp(i32);
Add = ADD,
Subtract = SUBTRACT,
ReverseSubtract = REVERSE_SUBTRACT,
Min = MIN,
Max = MAX,
}
vulkan_bitflags! {
ColorComponents = ColorComponentFlags(u32);
R = R,
G = G,
B = B,
A = A,
}