Skip to main content

pebble/graphics/types/
pipeline_state.rs

1use crate::graphics::types::{
2    BlendFactor, BlendOperation, CompareFunction, StencilOperation, TextureFormat, VertexFormat, VertexStepMode,
3    flags::ColorWrites,
4};
5
6/// One field within a vertex — its format, byte offset, and `@location` in
7/// the shader.
8#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
9pub struct VertexAttribute {
10    pub format: VertexFormat,
11    pub offset: u64,
12    pub shader_location: u32,
13}
14
15impl From<VertexAttribute> for wgpu::VertexAttribute {
16    fn from(attr: VertexAttribute) -> Self {
17        Self {
18            format: attr.format.into(),
19            offset: attr.offset,
20            shader_location: attr.shader_location,
21        }
22    }
23}
24
25/// One vertex buffer's layout — stride, step mode, and its [`VertexAttribute`]s.
26/// Usually built by a vertex type's own `layout()` method (e.g. [`Vertex::layout`](crate::graphics::pipeline::mesh::Vertex::layout))
27/// rather than by hand.
28#[derive(Clone, Debug, PartialEq)]
29pub struct VertexBufferLayout {
30    pub array_stride: u64,
31    pub step_mode: VertexStepMode,
32    pub attributes: Vec<VertexAttribute>,
33}
34
35/// How one channel (color or alpha) blends — src/dst factors plus the
36/// combining operation. [`REPLACE`](Self::REPLACE)/[`OVER`](Self::OVER) cover the common cases.
37#[derive(Copy, Clone, PartialEq, Eq, Hash)]
38pub struct BlendComponent {
39    pub src_factor: BlendFactor,
40    pub dst_factor: BlendFactor,
41    pub operation: BlendOperation,
42}
43
44impl BlendComponent {
45    pub const REPLACE: Self = Self {
46        src_factor: BlendFactor::One,
47        dst_factor: BlendFactor::Zero,
48        operation: BlendOperation::Add,
49    };
50
51    pub const OVER: Self = Self {
52        src_factor: BlendFactor::One,
53        dst_factor: BlendFactor::OneMinusSrcAlpha,
54        operation: BlendOperation::Add,
55    };
56}
57
58impl Default for BlendComponent {
59    /// [`REPLACE`](Self::REPLACE) — no blending.
60    fn default() -> Self {
61        Self::REPLACE
62    }
63}
64
65impl From<BlendComponent> for wgpu::BlendComponent {
66    fn from(value: BlendComponent) -> Self {
67        Self {
68            src_factor: value.src_factor.into(),
69            dst_factor: value.dst_factor.into(),
70            operation: value.operation.into(),
71        }
72    }
73}
74
75/// Color + alpha blending for one [`ColorTargetState`]. Presets:
76/// [`REPLACE`](Self::REPLACE) (no blending), [`ALPHA_BLENDING`](Self::ALPHA_BLENDING),
77/// [`PREMULTIPLIED_ALPHA_BLENDING`](Self::PREMULTIPLIED_ALPHA_BLENDING).
78#[derive(Copy, Clone, PartialEq, Eq, Hash)]
79pub struct BlendState {
80    pub color: BlendComponent,
81    pub alpha: BlendComponent,
82}
83
84impl BlendState {
85    pub const REPLACE: Self = Self { color: BlendComponent::REPLACE, alpha: BlendComponent::REPLACE };
86
87    pub const ALPHA_BLENDING: Self = Self {
88        color: BlendComponent {
89            src_factor: BlendFactor::SrcAlpha,
90            dst_factor: BlendFactor::OneMinusSrcAlpha,
91            operation: BlendOperation::Add,
92        },
93        alpha: BlendComponent::OVER,
94    };
95
96    pub const PREMULTIPLIED_ALPHA_BLENDING: Self =
97        Self { color: BlendComponent::OVER, alpha: BlendComponent::OVER };
98}
99
100impl Default for BlendState {
101    /// [`REPLACE`](Self::REPLACE) — no blending.
102    fn default() -> Self {
103        Self::REPLACE
104    }
105}
106
107impl From<BlendState> for wgpu::BlendState {
108    fn from(value: BlendState) -> Self {
109        Self { color: value.color.into(), alpha: value.alpha.into() }
110    }
111}
112
113/// One color attachment's output format, blend mode, and write mask —
114/// passed to [`Material::with_targets`](crate::graphics::pipeline::material::Material::with_targets).
115#[derive(Clone, PartialEq, Eq, Hash)]
116pub struct ColorTargetState {
117    pub format: TextureFormat,
118    pub blend: Option<BlendState>,
119    pub write_mask: ColorWrites,
120}
121
122impl Default for ColorTargetState {
123    /// Opaque `Rgba8Unorm`, no blending, writes all channels — kept in sync
124    /// with [`DEFAULT_TARGET`], which just wraps this in a single-element `Vec`
125    /// for [`Material::with_targets`](crate::graphics::pipeline::material::Material::with_targets).
126    fn default() -> Self {
127        Self { format: TextureFormat::Rgba8Unorm, blend: None, write_mask: ColorWrites::ALL }
128    }
129}
130
131impl From<ColorTargetState> for wgpu::ColorTargetState {
132    fn from(value: ColorTargetState) -> Self {
133        Self {
134            format: value.format.into(),
135            blend: value.blend.map(Into::into),
136            write_mask: value.write_mask.into(),
137        }
138    }
139}
140
141/// A single opaque `Rgba8Unorm` target with no blending — a reasonable
142/// default for [`Material::with_targets`](crate::graphics::pipeline::material::Material::with_targets).
143pub const DEFAULT_TARGET: [ColorTargetState; 1] = [ColorTargetState {
144    format: TextureFormat::Rgba8Unorm,
145    blend: None,
146    write_mask: ColorWrites::ALL,
147}];
148
149/// Stencil test/write behavior for one face — see [`StencilState`]. Defaults
150/// to [`IGNORE`](Self::IGNORE) (stencil test always passes, no writes).
151#[derive(Copy, Clone, PartialEq, Eq, Hash)]
152pub struct StencilFaceState {
153    pub compare: CompareFunction,
154    pub fail_op: StencilOperation,
155    pub depth_fail_op: StencilOperation,
156    pub pass_op: StencilOperation,
157}
158
159impl StencilFaceState {
160    pub const IGNORE: Self = Self {
161        compare: CompareFunction::Always,
162        fail_op: StencilOperation::Keep,
163        depth_fail_op: StencilOperation::Keep,
164        pass_op: StencilOperation::Keep,
165    };
166}
167
168impl Default for StencilFaceState {
169    fn default() -> Self {
170        Self::IGNORE
171    }
172}
173
174impl From<StencilFaceState> for wgpu::StencilFaceState {
175    fn from(value: StencilFaceState) -> Self {
176        Self {
177            compare: value.compare.into(),
178            fail_op: value.fail_op.into(),
179            depth_fail_op: value.depth_fail_op.into(),
180            pass_op: value.pass_op.into(),
181        }
182    }
183}
184
185/// Front/back stencil face state plus read/write masks — part of [`DepthStencilState`].
186#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)]
187pub struct StencilState {
188    pub front: StencilFaceState,
189    pub back: StencilFaceState,
190    pub read_mask: u32,
191    pub write_mask: u32,
192}
193
194impl From<StencilState> for wgpu::StencilState {
195    fn from(value: StencilState) -> Self {
196        Self {
197            front: value.front.into(),
198            back: value.back.into(),
199            read_mask: value.read_mask,
200            write_mask: value.write_mask,
201        }
202    }
203}
204
205/// Depth bias ("polygon offset") — nudges depth values to avoid z-fighting,
206/// e.g. for shadow-acne mitigation. Part of [`DepthStencilState`].
207#[derive(Copy, Clone, PartialEq, Default)]
208pub struct DepthBiasState {
209    pub constant: i32,
210    pub slope_scale: f32,
211    pub clamp: f32,
212}
213
214impl From<DepthBiasState> for wgpu::DepthBiasState {
215    fn from(value: DepthBiasState) -> Self {
216        Self { constant: value.constant, slope_scale: value.slope_scale, clamp: value.clamp }
217    }
218}
219
220/// Depth/stencil buffer state for a pipeline — passed to
221/// [`Material::with_depth`](crate::graphics::pipeline::material::Material::with_depth).
222#[derive(Clone, PartialEq)]
223pub struct DepthStencilState {
224    pub format: TextureFormat,
225    pub depth_write_enabled: Option<bool>,
226    pub depth_compare: Option<CompareFunction>,
227    pub stencil: StencilState,
228    pub bias: DepthBiasState,
229}
230
231impl DepthStencilState {
232    /// A standard depth-tested/depth-written `Depth32Float` buffer with a
233    /// `Less` compare and no stencil — the common case for opaque 3D
234    /// geometry. A reasonable default for [`Material::with_depth`](crate::graphics::pipeline::material::Material::with_depth);
235    /// override `format` if your depth attachment uses a different one
236    /// (e.g. `Depth32FloatStencil8`).
237    pub const DEFAULT: Self = Self {
238        format: TextureFormat::Depth32Float,
239        depth_write_enabled: Some(true),
240        depth_compare: Some(CompareFunction::Less),
241        stencil: StencilState {
242            front: StencilFaceState::IGNORE,
243            back: StencilFaceState::IGNORE,
244            read_mask: 0,
245            write_mask: 0,
246        },
247        bias: DepthBiasState { constant: 0, slope_scale: 0.0, clamp: 0.0 },
248    };
249}
250
251impl Default for DepthStencilState {
252    /// [`DEFAULT`](Self::DEFAULT).
253    fn default() -> Self {
254        Self::DEFAULT
255    }
256}
257
258impl From<DepthStencilState> for wgpu::DepthStencilState {
259    fn from(value: DepthStencilState) -> Self {
260        Self {
261            format: value.format.into(),
262            depth_write_enabled: value.depth_write_enabled,
263            depth_compare: value.depth_compare.map(Into::into),
264            stencil: value.stencil.into(),
265            bias: value.bias.into(),
266        }
267    }
268}