use std::hash::{Hash, Hasher};
use crate::{
backend::Device,
dimensions::{Extent2, Extent3, Offset2},
format::Format,
render_pass::RenderPass,
sampler::CompareOp,
shader::{FragmentShader, VertexShader},
OutOfMemory,
};
pub use crate::backend::GraphicsPipeline;
use super::PipelineLayout;
#[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 {
matches!(self, Self::Dynamic)
}
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)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub struct Bounds {
pub offset: f32,
pub size: f32,
}
impl PartialEq for Bounds {
fn eq(&self, other: &Self) -> bool {
f32::to_bits(self.offset) == f32::to_bits(other.offset)
&& f32::to_bits(self.size) == f32::to_bits(other.size)
}
}
impl Eq for Bounds {}
impl Hash for Bounds {
fn hash<H>(&self, hasher: &mut H)
where
H: Hasher,
{
Hash::hash(&f32::to_bits(self.offset), hasher);
Hash::hash(&f32::to_bits(self.size), hasher);
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum GraphicsPipelineRenderingInfo {
RenderPass {
render_pass: RenderPass,
subpass: u32,
},
DynamicRendering {
colors: Vec<Format>,
depth_stencil: Option<Format>,
},
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct GraphicsPipelineDesc {
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,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct GraphicsPipelineInfo {
pub desc: GraphicsPipelineDesc,
pub rendering: GraphicsPipelineRenderingInfo,
}
#[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,
}
impl From<Rect> for Viewport {
fn from(rect: Rect) -> Self {
Viewport {
x: Bounds {
offset: rect.offset.x as f32,
size: rect.extent.width as f32,
},
y: Bounds {
offset: rect.offset.y as f32,
size: rect.extent.height as f32,
},
z: Bounds {
offset: 0.0,
size: 1.0,
},
}
}
}
impl From<Extent2> for Viewport {
fn from(extent: Extent2) -> Self {
Viewport {
x: Bounds {
offset: 0.0,
size: extent.width as f32,
},
y: Bounds {
offset: 0.0,
size: extent.height as f32,
},
z: Bounds {
offset: 0.0,
size: 1.0,
},
}
}
}
impl From<Extent3> for Viewport {
fn from(extent: Extent3) -> Self {
Viewport {
x: Bounds {
offset: 0.0,
size: extent.width as f32,
},
y: Bounds {
offset: 0.0,
size: extent.height as f32,
},
z: Bounds {
offset: 0.0,
size: extent.depth as f32,
},
}
}
}
impl From<Extent2> for State<Viewport> {
fn from(extent: Extent2) -> Self {
State::Static {
value: extent.into(),
}
}
}
impl From<Extent3> for State<Viewport> {
fn from(extent: Extent3) -> Self {
State::Static {
value: extent.into(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub struct Rect {
pub offset: Offset2,
pub extent: Extent2,
}
impl From<Extent2> for Rect {
fn from(extent: Extent2) -> Self {
Rect {
offset: Offset2::zeros(),
extent,
}
}
}
impl From<Extent2> for State<Rect> {
fn from(extent: Extent2) -> Self {
State::Static {
value: extent.into(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Rasterizer {
pub viewport: State<Viewport>,
pub scissor: State<Rect>,
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,
}
impl Default for Rasterizer {
fn default() -> Self {
Self::new()
}
}
impl Rasterizer {
pub const fn new() -> Self {
Rasterizer {
viewport: State::Dynamic,
scissor: State::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: State::Static {
value: [0.0, 0.0, 0.0, 0.0],
},
},
}
}
}
#[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,
}
impl DepthTest {
pub const LESS_WRITE: Self = DepthTest {
compare: CompareOp::Less,
write: true,
};
}
#[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)]
#[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<[f32; 4]>,
},
IndependentBlending {
blending: Vec<(Option<Blending>, ComponentMask)>,
constants: State<[f32; 4]>,
},
}
impl PartialEq for ColorBlend {
fn eq(&self, other: &Self) -> bool {
use ColorBlend::*;
match (self, other) {
(Logic { op: l_op }, Logic { op: r_op }) => *l_op == *r_op,
(
Blending {
blending: l_blending,
write_mask: l_write_mask,
constants: l_constants,
},
Blending {
blending: r_blending,
write_mask: r_write_mask,
constants: r_constants,
},
) => {
*l_blending == *r_blending
&& *l_write_mask == *r_write_mask
&& match (l_constants, r_constants) {
(State::Dynamic, State::Dynamic) => true,
(State::Static { value: l_value }, State::Static { value: r_value }) => {
(*l_value).map(f32::to_bits) == (*r_value).map(f32::to_bits)
}
_ => false,
}
}
(
IndependentBlending {
blending: l_blending,
constants: l_constants,
},
IndependentBlending {
blending: r_blending,
constants: r_constants,
},
) => {
*l_blending == *r_blending
&& match (l_constants, r_constants) {
(State::Dynamic, State::Dynamic) => true,
(State::Static { value: l_value }, State::Static { value: r_value }) => {
(*l_value).map(f32::to_bits) == (*r_value).map(f32::to_bits)
}
_ => false,
}
}
_ => false,
}
}
}
impl Eq for ColorBlend {}
impl Hash for ColorBlend {
fn hash<H>(&self, hasher: &mut H)
where
H: Hasher,
{
match self {
ColorBlend::Logic { op } => {
Hash::hash(op, hasher);
}
ColorBlend::Blending {
blending,
write_mask,
constants,
} => {
hasher.write_u8(1);
if let Some(blending) = blending {
Hash::hash(blending, hasher);
}
Hash::hash(write_mask, hasher);
if let State::Static { value } = constants {
Hash::hash(&(*value).map(f32::to_bits), hasher);
}
}
ColorBlend::IndependentBlending {
blending,
constants,
} => {
hasher.write_u8(2);
for (blending, mask) in blending {
if let Some(blending) = blending {
Hash::hash(blending, hasher);
}
Hash::hash(mask, hasher);
}
if let State::Static { value } = constants {
Hash::hash(&(*value).map(f32::to_bits), hasher);
}
}
}
}
}
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: State::Static { value: [0.0; 4] },
}
}
}
#[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;
}
}
#[derive(Debug)]
pub struct DynamicGraphicsPipeline {
graphics_pipeline: Option<GraphicsPipeline>,
pub desc: GraphicsPipelineDesc,
}
impl DynamicGraphicsPipeline {
pub fn new(desc: GraphicsPipelineDesc) -> Self {
DynamicGraphicsPipeline {
desc,
graphics_pipeline: None,
}
}
pub fn get_for_dynamic_rendering(
&mut self,
colors: &[Format],
depth_stencil: Option<Format>,
device: &Device,
) -> Result<&GraphicsPipeline, OutOfMemory> {
if let Some(graphics_pipeline) = &mut self.graphics_pipeline {
let info = graphics_pipeline.info();
let compatible = match info.rendering {
GraphicsPipelineRenderingInfo::DynamicRendering {
colors: ref current_colors,
depth_stencil: current_depth_stencil,
} => current_colors[..] != colors[..] || current_depth_stencil != depth_stencil,
_ => false,
};
if !compatible || info.desc != self.desc {
self.graphics_pipeline = None;
}
}
let graphics_pipeline = match &mut self.graphics_pipeline {
Some(graphics_pipeline) => graphics_pipeline,
graphics_pipeline => graphics_pipeline.get_or_insert(device.create_graphics_pipeline(
GraphicsPipelineInfo {
desc: self.desc.clone(),
rendering: GraphicsPipelineRenderingInfo::DynamicRendering {
colors: colors.to_vec(),
depth_stencil,
},
},
)?),
};
Ok(graphics_pipeline)
}
pub fn get_for_render_pass(
&mut self,
render_pass: &RenderPass,
subpass: u32,
device: &Device,
) -> Result<&GraphicsPipeline, OutOfMemory> {
if let Some(graphics_pipeline) = &mut self.graphics_pipeline {
let info = graphics_pipeline.info();
let compatible = match info.rendering {
GraphicsPipelineRenderingInfo::RenderPass {
render_pass: ref current_render_pass,
subpass: current_subpass,
} => *current_render_pass != *render_pass || current_subpass != subpass,
_ => false,
};
if !compatible || info.desc != self.desc {
self.graphics_pipeline = None;
}
}
let graphics_pipeline = match &mut self.graphics_pipeline {
Some(graphics_pipeline) => graphics_pipeline,
graphics_pipeline => graphics_pipeline.get_or_insert(device.create_graphics_pipeline(
GraphicsPipelineInfo {
desc: self.desc.clone(),
rendering: GraphicsPipelineRenderingInfo::RenderPass {
render_pass: render_pass.clone(),
subpass,
},
},
)?),
};
Ok(graphics_pipeline)
}
}