Skip to main content

wisp/render/
blend_pipeline.rs

1//! `BlendPipelineMap` — pre-built `RenderPipeline` per standard blend mode.
2//!
3//! Each of wisp's 6 pipelines (sprite, quad, triangle, mesh, text,
4//! graphics) takes the same shader/vertex/bind-group setup and varies
5//! *only* in the `wgpu::BlendState`. Rather than building one pipeline
6//! per mode by hand (× 6 pipelines × 8 native modes = 48 pipeline
7//! construction sites), this helper accepts a builder closure that
8//! takes a `BlendState` and returns a `RenderPipeline`. We pre-build
9//! one pipeline per native [`BlendMode`] at construction time.
10//!
11//! Advanced modes (Tier C — Overlay, `ColorBurn`, …) aren't represented
12//! in the map — they require the offscreen filter pipeline. When a
13//! caller asks for an advanced mode via [`BlendPipelineMap::get`], we
14//! fall back to `Normal`. This fallback is INTENTIONAL when
15//! [`Renderer::render_stage`](crate::render::Renderer::render_stage)
16//! renders an advanced-blend subtree into a foreground RT — the leaf's
17//! pure colors land in the foreground, then the parent's advanced blend
18//! is applied via [`apply_advanced_blend`](crate::render::Renderer::apply_advanced_blend).
19//! No warning is emitted because auto-dispatch makes it correct by
20//! default.
21
22use std::collections::HashMap;
23
24use crate::blend::BlendMode;
25
26/// Map from a [`BlendMode`] to its pre-built `RenderPipeline`.
27pub(crate) struct BlendPipelineMap {
28    inner: HashMap<BlendMode, wgpu::RenderPipeline>,
29}
30
31impl BlendPipelineMap {
32    /// Build one pipeline per native [`BlendMode`] by invoking `build`
33    /// with that mode's [`wgpu::BlendState`].
34    pub(crate) fn new<F>(mut build: F) -> Self
35    where
36        F: FnMut(wgpu::BlendState) -> wgpu::RenderPipeline,
37    {
38        let mut inner = HashMap::new();
39        for mode in BlendMode::all() {
40            if let Some(blend) = mode.native_blend_state() {
41                inner.insert(mode, build(blend));
42            }
43        }
44        Self { inner }
45    }
46
47    /// Look up the pipeline for `mode`. Advanced modes silently fall
48    /// back to `Normal` — by design, since `render_stage`'s
49    /// auto-dispatch path renders advanced-blend subtrees into a
50    /// foreground RT with Normal blending, then composites via
51    /// `apply_advanced_blend`.
52    pub(crate) fn get(&self, mode: BlendMode) -> &wgpu::RenderPipeline {
53        if let Some(p) = self.inner.get(&mode) {
54            return p;
55        }
56        self.inner
57            .get(&BlendMode::Normal)
58            .expect("BlendPipelineMap always builds Normal")
59    }
60}