use crate::command_buffer::synced::CommandBufferState;
use crate::pipeline::graphics::input_assembly::PrimitiveTopology;
use crate::pipeline::DynamicState;
use crate::pipeline::GraphicsPipeline;
use crate::pipeline::PartialStateMode;
use crate::shader::ShaderStage;
use std::error;
use std::fmt;
pub(in super::super) fn check_dynamic_state_validity(
current_state: CommandBufferState,
pipeline: &GraphicsPipeline,
) -> Result<(), CheckDynamicStateValidityError> {
let device = pipeline.device();
for dynamic_state in pipeline
.dynamic_states()
.filter(|(_, d)| *d)
.map(|(s, _)| s)
{
match dynamic_state {
DynamicState::BlendConstants => {
if current_state.blend_constants().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::ColorWriteEnable => {
let enables = if let Some(enables) = current_state.color_write_enable() {
enables
} else {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
};
if enables.len() < pipeline.color_blend_state().unwrap().attachments.len() {
return Err(CheckDynamicStateValidityError::NotEnoughColorWriteEnable {
color_write_enable_count: enables.len() as u32,
attachment_count: pipeline.color_blend_state().unwrap().attachments.len()
as u32,
});
}
}
DynamicState::CullMode => {
if current_state.cull_mode().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::DepthBias => {
if current_state.depth_bias().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::DepthBiasEnable => {
if current_state.depth_bias_enable().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::DepthBounds => {
if current_state.depth_bounds().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::DepthBoundsTestEnable => {
if current_state.depth_bounds_test_enable().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::DepthCompareOp => {
if current_state.depth_compare_op().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::DepthTestEnable => {
if current_state.depth_test_enable().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::DepthWriteEnable => {
if current_state.depth_write_enable().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::DiscardRectangle => {
let discard_rectangle_count =
match pipeline.discard_rectangle_state().unwrap().rectangles {
PartialStateMode::Dynamic(count) => count,
_ => unreachable!(),
};
for num in 0..discard_rectangle_count {
if current_state.discard_rectangle(num).is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
}
DynamicState::ExclusiveScissor => todo!(),
DynamicState::FragmentShadingRate => todo!(),
DynamicState::FrontFace => {
if current_state.front_face().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::LineStipple => {
if current_state.line_stipple().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::LineWidth => {
if current_state.line_width().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::LogicOp => {
if current_state.logic_op().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::PatchControlPoints => {
if current_state.patch_control_points().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::PrimitiveRestartEnable => {
let primitive_restart_enable =
if let Some(enable) = current_state.primitive_restart_enable() {
enable
} else {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
};
if primitive_restart_enable {
let topology = match pipeline.input_assembly_state().topology {
PartialStateMode::Fixed(topology) => topology,
PartialStateMode::Dynamic(_) => {
if let Some(topology) = current_state.primitive_topology() {
topology
} else {
return Err(CheckDynamicStateValidityError::NotSet {
dynamic_state: DynamicState::PrimitiveTopology,
});
}
}
};
match topology {
PrimitiveTopology::PointList
| PrimitiveTopology::LineList
| PrimitiveTopology::TriangleList
| PrimitiveTopology::LineListWithAdjacency
| PrimitiveTopology::TriangleListWithAdjacency => {
if !device.enabled_features().primitive_topology_list_restart {
return Err(CheckDynamicStateValidityError::FeatureNotEnabled {
feature: "primitive_topology_list_restart",
reason: "the PrimitiveRestartEnable dynamic state was true in combination with a List PrimitiveTopology",
});
}
}
PrimitiveTopology::PatchList => {
if !device
.enabled_features()
.primitive_topology_patch_list_restart
{
return Err(CheckDynamicStateValidityError::FeatureNotEnabled {
feature: "primitive_topology_patch_list_restart",
reason: "the PrimitiveRestartEnable dynamic state was true in combination with PrimitiveTopology::PatchList",
});
}
}
_ => (),
}
}
}
DynamicState::PrimitiveTopology => {
let topology = if let Some(topology) = current_state.primitive_topology() {
topology
} else {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
};
if pipeline.shader(ShaderStage::TessellationControl).is_some() {
if !matches!(topology, PrimitiveTopology::PatchList) {
return Err(CheckDynamicStateValidityError::InvalidPrimitiveTopology {
topology,
reason: "the graphics pipeline includes tessellation shaders, so the topology must be PatchList",
});
}
} else {
if matches!(topology, PrimitiveTopology::PatchList) {
return Err(CheckDynamicStateValidityError::InvalidPrimitiveTopology {
topology,
reason: "the graphics pipeline doesn't include tessellation shaders",
});
}
}
let topology_class = match pipeline.input_assembly_state().topology {
PartialStateMode::Dynamic(topology_class) => topology_class,
_ => unreachable!(),
};
if topology.class() != topology_class {
return Err(CheckDynamicStateValidityError::InvalidPrimitiveTopology {
topology,
reason: "the topology class does not match the class the pipeline was created for",
});
}
}
DynamicState::RasterizerDiscardEnable => {
if current_state.rasterizer_discard_enable().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::RayTracingPipelineStackSize => unreachable!(
"RayTracingPipelineStackSize dynamic state should not occur on a graphics pipeline"
),
DynamicState::SampleLocations => todo!(),
DynamicState::Scissor => {
for num in 0..pipeline.viewport_state().unwrap().count().unwrap() {
if current_state.scissor(num).is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
}
DynamicState::ScissorWithCount => {
let scissor_count = if let Some(scissors) = current_state.scissor_with_count() {
scissors.len() as u32
} else {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
};
if let Some(viewport_count) = pipeline.viewport_state().unwrap().count() {
if viewport_count != scissor_count {
return Err(
CheckDynamicStateValidityError::ViewportScissorCountMismatch {
viewport_count,
scissor_count,
},
);
}
}
}
DynamicState::StencilCompareMask => {
let state = current_state.stencil_compare_mask();
if state.front.is_none() || state.back.is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::StencilOp => {
let state = current_state.stencil_op();
if state.front.is_none() || state.back.is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::StencilReference => {
let state = current_state.stencil_reference();
if state.front.is_none() || state.back.is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::StencilTestEnable => {
if current_state.stencil_test_enable().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::StencilWriteMask => {
let state = current_state.stencil_write_mask();
if state.front.is_none() || state.back.is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::VertexInput => todo!(),
DynamicState::VertexInputBindingStride => todo!(),
DynamicState::Viewport => {
for num in 0..pipeline.viewport_state().unwrap().count().unwrap() {
if current_state.viewport(num).is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
}
DynamicState::ViewportCoarseSampleOrder => todo!(),
DynamicState::ViewportShadingRatePalette => todo!(),
DynamicState::ViewportWithCount => {
let viewport_count = if let Some(viewports) = current_state.viewport_with_count() {
viewports.len() as u32
} else {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
};
let scissor_count =
if let Some(scissor_count) = pipeline.viewport_state().unwrap().count() {
scissor_count
} else {
if let Some(scissors) = current_state.scissor_with_count() {
scissors.len() as u32
} else {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
};
if viewport_count != scissor_count {
return Err(
CheckDynamicStateValidityError::ViewportScissorCountMismatch {
viewport_count,
scissor_count,
},
);
}
}
DynamicState::ViewportWScaling => todo!(),
}
}
Ok(())
}
#[derive(Debug, Copy, Clone)]
pub enum CheckDynamicStateValidityError {
FeatureNotEnabled {
feature: &'static str,
reason: &'static str,
},
InvalidPrimitiveTopology {
topology: PrimitiveTopology,
reason: &'static str,
},
NotEnoughColorWriteEnable {
color_write_enable_count: u32,
attachment_count: u32,
},
NotSet { dynamic_state: DynamicState },
ViewportScissorCountMismatch {
viewport_count: u32,
scissor_count: u32,
},
}
impl error::Error for CheckDynamicStateValidityError {}
impl fmt::Display for CheckDynamicStateValidityError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
Self::FeatureNotEnabled { feature, reason } => {
write!(fmt, "the feature {} must be enabled: {}", feature, reason)
}
Self::InvalidPrimitiveTopology { topology, reason } => {
write!(
fmt,
"invalid dynamic PrimitiveTypology::{:?}: {}",
topology, reason
)
}
Self::NotEnoughColorWriteEnable {
color_write_enable_count,
attachment_count,
} => {
write!(fmt, "the number of ColorWriteEnable values ({}) was less than the number of attachments ({}) in the color blend state of the pipeline", color_write_enable_count, attachment_count)
}
Self::NotSet { dynamic_state } => {
write!(fmt, "the pipeline requires the dynamic state {:?} to be set, but the value was not or only partially set", dynamic_state)
}
Self::ViewportScissorCountMismatch {
viewport_count,
scissor_count,
} => {
write!(fmt, "the viewport count and scissor count do not match; viewport count is {}, scissor count is {}", viewport_count, scissor_count)
}
}
}
}