use {
super::PipelineLayout,
crate::{
format::Format,
render_pass::RenderPass,
sampler::CompareOp,
shader::{FragmentShader, VertexShader},
Rect2d,
},
ordered_float::OrderedFloat,
};
pub use {
self::State::{Dynamic, Static},
crate::backend::GraphicsPipeline,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub enum State<T> {
Static { value: T },
Dynamic,
}
impl<T> State<T> {
pub fn is_dynamic(&self) -> bool {
match self {
Self::Dynamic => true,
_ => false,
}
}
pub fn dynamic() -> Self {
Self::Dynamic
}
}
impl<T> From<T> for State<T> {
fn from(value: T) -> Self {
State::Static { value }
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub struct Bounds {
pub offset: OrderedFloat<f32>,
pub size: OrderedFloat<f32>,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct GraphicsPipelineInfo {
pub vertex_bindings: Vec<VertexInputBinding>,
pub vertex_attributes: Vec<VertexInputAttribute>,
pub primitive_topology: PrimitiveTopology,
pub primitive_restart_enable: bool,
pub vertex_shader: VertexShader,
pub rasterizer: Option<Rasterizer>,
pub layout: PipelineLayout,
pub render_pass: RenderPass,
pub subpass: u32,
}
#[doc(hidden)]
#[allow(missing_debug_implementations)]
pub struct GraphicsPipelineInfoBuilder {
pub vertex_bindings: Vec<VertexInputBinding>,
pub vertex_attributes: Vec<VertexInputAttribute>,
pub primitive_topology: PrimitiveTopology,
pub primitive_restart_enable: bool,
pub rasterizer: Option<Rasterizer>,
pub subpass: u32,
}
#[doc(hidden)]
impl GraphicsPipelineInfoBuilder {
pub fn new() -> Self {
GraphicsPipelineInfoBuilder {
vertex_bindings: Vec::new(),
vertex_attributes: Vec::new(),
primitive_topology: PrimitiveTopology::TriangleList,
primitive_restart_enable: false,
rasterizer: None,
subpass: 0,
}
}
}
#[macro_export]
macro_rules! graphics_pipeline_info {
($($field:ident : $value:expr),* $(,)?) => {
graphics_pipeline_info!(@UNFOLD builder { let mut builder = GraphicsPipelineInfoBuilder::new(); } { $($field: $value),* } {})
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } { vertex_bindings: $vertex_bindings:expr $(, $field:ident : $value:expr)* } { $($rfield:ident : $rvalue:expr),* }) => {
graphics_pipeline_info!(@UNFOLD $builder { $($stmts)* $builder.vertex_bindings = $vertex_bindings.into(); } { $($field: $value),* } {$($rfield:$rvalue),*})
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } { vertex_attributes: $vertex_attributes:expr $(, $field:ident : $value:expr)* } { $($rfield:ident : $rvalue:expr),* }) => {
graphics_pipeline_info!(@UNFOLD $builder { $($stmts)* $builder.vertex_attributes = $vertex_attributes.into(); } { $($field: $value),* } {$($rfield:$rvalue),*})
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } { primitive_topology: $primitive_topology:expr $(, $field:ident : $value:expr)* } { $($rfield:ident : $rvalue:expr),* }) => {
graphics_pipeline_info!(@UNFOLD $builder { $($stmts)* $builder.primitive_topology = $primitive_topology.into(); } { $($field: $value),* } {$($rfield:$rvalue),*})
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } { primitive_restart_enable: $primitive_restart_enable:expr $(, $field:ident : $value:expr)* } { $($rfield:ident : $rvalue:expr),* }) => {
graphics_pipeline_info!(@UNFOLD $builder { $($stmts)* $builder.primitive_restart_enable = $primitive_restart_enable.into(); } { $($field: $value),* } {$($rfield:$rvalue),*})
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } { rasterizer: $rasterizer:expr $(, $field:ident : $value:expr)* } { $($rfield:ident : $rvalue:expr),* }) => {
graphics_pipeline_info!(@UNFOLD $builder { $($stmts)* $builder.rasterizer = $rasterizer.into(); } { $($field: $value),* } {$($rfield:$rvalue),*})
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } { subpass: $subpass:expr $(, $field:ident : $value:expr)* } { $($rfield:ident : $rvalue:expr),* }) => {
graphics_pipeline_info!(@UNFOLD $builder { $($stmts)* $builder.subpass = $subpass.into(); } { $($field: $value),* } {$($rfield:$rvalue),*})
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } { layout: $layout:expr $(, $field:ident : $value:expr)* } { $($rfield:ident : $rvalue:expr),* }) => {
graphics_pipeline_info!(@UNFOLD $builder { $($stmts)* } { $($field: $value),* } { layout: $layout $(,$rfield:$rvalue)*})
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } { render_pass: $render_pass:expr $(, $field:ident : $value:expr)* } { $($rfield:ident : $rvalue:expr),* }) => {
graphics_pipeline_info!(@UNFOLD $builder { $($stmts)* } { $($field: $value),* } { render_pass: $render_pass $(, $rfield:$rvalue)*})
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } { vertex_shader: $vertex_shader:expr $(, $field:ident : $value:expr)* } { $($rfield:ident : $rvalue:expr),* }) => {
graphics_pipeline_info!(@UNFOLD $builder { $($stmts)* } { $($field: $value),* } { vertex_shader: $vertex_shader $(,$rfield:$rvalue)*})
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } {} { $($rfield:ident : $rvalue:expr),* }) => {
{
$($stmts)*
GraphicsPipelineInfo {
vertex_bindings: $builder.vertex_bindings,
vertex_attributes: $builder.vertex_attributes,
primitive_topology: $builder.primitive_topology,
primitive_restart_enable: $builder.primitive_restart_enable,
rasterizer: $builder.rasterizer,
subpass: $builder.subpass,
$($rfield: $rvalue,)*
}
}
};
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub struct VertexInputBinding {
pub rate: VertexInputRate,
pub stride: u32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub enum VertexInputRate {
Vertex,
Instance,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub struct VertexInputAttribute {
pub location: u32,
pub format: Format,
pub binding: u32,
pub offset: u32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub enum PrimitiveTopology {
PointList,
LineList,
LineStrip,
TriangleList,
TriangleStrip,
TriangleFan,
}
impl Default for PrimitiveTopology {
fn default() -> Self {
PrimitiveTopology::TriangleList
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub struct Viewport {
pub x: Bounds,
pub y: Bounds,
pub z: Bounds,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Rasterizer {
pub viewport: State<Viewport>,
pub scissor: State<Rect2d>,
pub depth_clamp: bool,
pub front_face: FrontFace,
pub culling: Option<Culling>,
pub polygon_mode: PolygonMode,
pub depth_test: Option<DepthTest>,
pub stencil_tests: Option<StencilTests>,
pub depth_bounds: Option<State<Bounds>>,
pub fragment_shader: Option<FragmentShader>,
pub color_blend: ColorBlend,
}
#[doc(hiddent)]
impl Rasterizer {
pub fn new() -> Self {
Rasterizer {
viewport: Dynamic,
scissor: Dynamic,
depth_clamp: false,
front_face: FrontFace::Clockwise,
culling: None,
polygon_mode: PolygonMode::Fill,
depth_test: None,
stencil_tests: None,
depth_bounds: None,
fragment_shader: None,
color_blend: ColorBlend::Blending {
blending: Some(Blending {
color_src_factor: BlendFactor::SrcAlpha,
color_dst_factor: BlendFactor::OneMinusSrcAlpha,
color_op: BlendOp::Add,
alpha_src_factor: BlendFactor::One,
alpha_dst_factor: BlendFactor::OneMinusSrcAlpha,
alpha_op: BlendOp::Add,
}),
write_mask: ComponentMask::RGBA,
constants: Static {
value: [0.0.into(), 0.0.into(), 0.0.into(), 0.0.into()],
},
},
}
}
}
#[macro_export]
macro_rules! rasterizer {
($($field:ident : $value:expr),* $(,)?) => {
rasterizer!(@UNFOLD builder { let mut builder = Rasterizer::new(); } { $($field: $value),* })
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } { viewport: $viewport:expr $(, $field:ident : $value:expr)* }) => {
rasterizer!(@UNFOLD $builder { $($stmts)* $builder.viewport = $viewport.into(); } { $($field: $value),* })
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } { scissor: $scissor:expr $(, $field:ident : $value:expr)* }) => {
rasterizer!(@UNFOLD $builder { $($stmts)* $builder.scissor = $scissor.into(); } { $($field: $value),* })
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } { depth_clamp: $depth_clamp:expr $(, $field:ident : $value:expr)* }) => {
rasterizer!(@UNFOLD $builder { $($stmts)* $builder.depth_clamp = $depth_clamp.into(); } { $($field: $value),* })
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } { front_face: $front_face:expr $(, $field:ident : $value:expr)* }) => {
rasterizer!(@UNFOLD $builder { $($stmts)* $builder.front_face = $front_face.into(); } { $($field: $value),* })
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } { culling: $culling:expr $(, $field:ident : $value:expr)* }) => {
rasterizer!(@UNFOLD $builder { $($stmts)* $builder.culling = $culling.into(); } { $($field: $value),* })
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } { polygon_mode: $polygon_mode:expr $(, $field:ident : $value:expr)* }) => {
rasterizer!(@UNFOLD $builder { $($stmts)* $builder.polygon_mode = $polygon_mode.into(); } { $($field: $value),* })
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } { depth_test: $depth_test:expr $(, $field:ident : $value:expr)* }) => {
rasterizer!(@UNFOLD $builder { $($stmts)* $builder.depth_test = $depth_test.into(); } { $($field: $value),* })
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } { depth: $depth:expr $(, $field:ident : $value:expr)* }) => {
rasterizer!(@UNFOLD $builder { $($stmts)* $builder.depth_test = if $depth { Some(DepthTest { compare: CompareOp::Less, write: true, }) } else { None }; } { $($field: $value),* })
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } { stencil_tests: $stencil_tests:expr $(, $field:ident : $value:expr)* }) => {
rasterizer!(@UNFOLD $builder { $($stmts)* $builder.stencil_tests = $stencil_tests.into(); } { $($field: $value),* })
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } { depth_bounds: $depth_bounds:expr $(, $field:ident : $value:expr)* }) => {
rasterizer!(@UNFOLD $builder { $($stmts)* $builder.depth_bounds = $depth_bounds.into(); } { $($field: $value),* })
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } { fragment_shader: $fragment_shader:expr $(, $field:ident : $value:expr)* }) => {
rasterizer!(@UNFOLD $builder { $($stmts)* $builder.fragment_shader = $fragment_shader.into(); } { $($field: $value),* })
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } { color_blend: $color_blend:expr $(, $field:ident : $value:expr)* }) => {
rasterizer!(@UNFOLD $builder { $($stmts)* $builder.color_blend = $color_blend.into(); } { $($field: $value),* })
};
(@UNFOLD $builder:ident { $($stmts:stmt)* } {}) => {
{
$($stmts)*
Rasterizer {
viewport: $builder.viewport,
scissor: $builder.scissor,
depth_clamp: $builder.depth_clamp,
front_face: $builder.front_face,
culling: $builder.culling,
polygon_mode: $builder.polygon_mode,
depth_test: $builder.depth_test,
stencil_tests: $builder.stencil_tests,
depth_bounds: $builder.depth_bounds,
fragment_shader: $builder.fragment_shader,
color_blend: $builder.color_blend,
}
}
};
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub enum FrontFace {
Clockwise,
CounterClockwise,
}
impl Default for FrontFace {
fn default() -> Self {
FrontFace::Clockwise
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub enum Culling {
Front,
Back,
FrontAndBack,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub enum PolygonMode {
Fill,
Line,
Point,
}
impl Default for PolygonMode {
fn default() -> Self {
PolygonMode::Fill
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub struct DepthTest {
pub compare: CompareOp,
pub write: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub struct StencilTests {
pub front: StencilTest,
pub back: StencilTest,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub struct StencilTest {
pub compare: CompareOp,
pub compare_mask: State<u32>,
pub write_mask: State<u32>,
pub reference: State<u32>,
pub fail: StencilOp,
pub pass: StencilOp,
pub depth_fail: StencilOp,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub enum StencilOp {
Keep,
Zero,
Replace,
IncrementAndClamp,
DecrementAndClamp,
Invert,
IncrementAndWrap,
DecrementAndWrap,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub enum ColorBlend {
Logic {
op: LogicOp,
},
Blending {
blending: Option<Blending>,
write_mask: ComponentMask,
constants: State<[OrderedFloat<f32>; 4]>,
},
IndependentBlending {
blending: Vec<(Option<Blending>, ComponentMask)>,
constants: State<[OrderedFloat<f32>; 4]>,
},
}
impl Default for ColorBlend {
fn default() -> Self {
ColorBlend::Blending {
blending: Some(Blending {
color_src_factor: BlendFactor::SrcAlpha,
color_dst_factor: BlendFactor::OneMinusSrcAlpha,
color_op: BlendOp::Add,
alpha_src_factor: BlendFactor::One,
alpha_dst_factor: BlendFactor::OneMinusSrcAlpha,
alpha_op: BlendOp::Add,
}),
write_mask: ComponentMask::RGBA,
constants: Static {
value: [0.0.into(), 0.0.into(), 0.0.into(), 0.0.into()],
},
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub struct Blending {
pub color_src_factor: BlendFactor,
pub color_dst_factor: BlendFactor,
pub color_op: BlendOp,
pub alpha_src_factor: BlendFactor,
pub alpha_dst_factor: BlendFactor,
pub alpha_op: BlendOp,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub enum LogicOp {
Clear,
And,
AndReverse,
Copy,
AndInverted,
NoOp,
Xor,
Or,
Nor,
Equivalent,
Invert,
OrReverse,
CopyInverted,
OrInverted,
Nand,
Set,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub enum BlendFactor {
Zero,
One,
SrcColor,
OneMinusSrcColor,
DstColor,
OneMinusDstColor,
SrcAlpha,
OneMinusSrcAlpha,
DstAlpha,
OneMinusDstAlpha,
ConstantColor,
OneMinusConstantColor,
ConstantAlpha,
OneMinusConstantAlpha,
SrcAlphaSaturate,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub enum BlendOp {
Add,
Subtract,
ReverseSubtract,
Min,
Max,
}
bitflags::bitflags! {
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub struct ComponentMask: u8 {
const R = 0b0001;
const G = 0b0010;
const B = 0b0100;
const A = 0b1000;
const RGB = 0b0111;
const RGBA = 0b1111;
}
}