Skip to main content

ff_render/nodes/
mod.rs

1pub mod blur;
2pub mod color_grade;
3pub mod color_wheels;
4pub mod composite;
5pub mod crossfade;
6pub mod curves;
7pub mod film_grain;
8pub mod glow;
9pub mod hsl;
10pub mod lut;
11pub mod overlay;
12pub mod scale;
13pub mod transition;
14pub mod upload;
15pub mod vignette;
16
17pub use blur::{GaussianBlurNode, MotionBlurNode, SharpenNode};
18pub use color_grade::ColorGradeNode;
19pub use color_wheels::ColorWheelsNode;
20pub use composite::{
21    AlphaMatteNode, BlendMode, BlendModeNode, ChromaKeyNode, CompositeOp, LumaMaskNode,
22    ShapeMaskNode, TransformNode,
23};
24pub use crossfade::CrossfadeNode;
25pub use curves::CurvesNode;
26pub use film_grain::FilmGrainNode;
27pub use glow::GlowNode;
28pub use hsl::HslNode;
29pub use lut::LutNode;
30pub use overlay::OverlayNode;
31pub use scale::{ScaleAlgorithm, ScaleNode};
32pub use transition::{
33    DipToColorNode, DissolveTransitionNode, FadeTransitionNode, WipeTransitionNode,
34};
35pub use upload::{YuvFormat, YuvUploadNode};
36pub use vignette::VignetteNode;
37
38// RenderNodeCpu
39
40/// CPU fallback processing for a render node.
41///
42/// Implemented by all built-in nodes. Nodes that do not change frame
43/// dimensions modify `rgba` in-place. Multi-input nodes (e.g. [`CrossfadeNode`])
44/// store their secondary inputs as fields and access them during `process_cpu`.
45pub trait RenderNodeCpu: Send {
46    /// Process `rgba` in-place.
47    ///
48    /// `rgba` is a row-major RGBA buffer of size `w × h × 4` bytes.
49    /// Nodes that cannot implement a CPU path leave `rgba` unchanged.
50    fn process_cpu(&self, rgba: &mut [u8], w: u32, h: u32);
51}
52
53/// A parameter a node accepts per frame without being rebuilt.
54///
55/// This exists for **stateful** nodes. A node whose output depends only on its
56/// parameters can simply be rebuilt when one changes; [`MotionBlurNode`] cannot,
57/// because rebuilding discards the exposure trail that is the whole point of it.
58/// So the parameter travels to the live node instead.
59///
60/// Non-exhaustive: more animatable parameters join it as they are needed.
61#[derive(Debug, Clone, Copy, PartialEq)]
62#[non_exhaustive]
63pub enum NodeParam {
64    /// [`MotionBlurNode`]'s shutter angle in degrees.
65    MotionBlurShutter(f32),
66    /// [`ShapeMaskNode`]'s rectangle, in source-frame pixels, and whether it is
67    /// inverted. The shader evaluates the rectangle, so moving it is a parameter
68    /// change rather than a rebuild.
69    ShapeMaskRect {
70        /// Left edge.
71        x: u32,
72        /// Top edge.
73        y: u32,
74        /// Width in pixels.
75        width: u32,
76        /// Height in pixels.
77        height: u32,
78        /// Keep outside the rectangle instead of inside.
79        invert: bool,
80    },
81}
82
83// RenderNode
84
85/// GPU render node. Extends [`RenderNodeCpu`] so both paths are available.
86///
87/// Each node is responsible for creating and caching its own wgpu pipeline
88/// on first use. The pipeline is stored in a [`std::sync::OnceLock`] field
89/// so it is created exactly once per node instance.
90///
91/// `process` may submit one or more `wgpu::CommandEncoder` buffers. The
92/// [`RenderGraph`](crate::graph::RenderGraph) guarantees that the queue
93/// processes them in submission order.
94#[cfg(feature = "wgpu")]
95pub trait RenderNode: RenderNodeCpu {
96    /// Number of input textures required by this node (default: 1).
97    fn input_count(&self) -> usize {
98        1
99    }
100
101    /// Number of render passes (default: 1). Multi-pass nodes (e.g. gaussian
102    /// blur) return 2 or more.
103    fn pass_count(&self) -> usize {
104        1
105    }
106
107    /// Output dimensions this node produces given its input dimensions.
108    ///
109    /// Default: unchanged (`(in_w, in_h)`). A resampling node (e.g.
110    /// [`ScaleNode`]) overrides this so the executor allocates its output target
111    /// — and every following node's input — at the new size. All of a node's
112    /// `pass_count()` targets are allocated at this size.
113    fn output_dimensions(&self, in_w: u32, in_h: u32) -> (u32, u32) {
114        (in_w, in_h)
115    }
116
117    /// Run the GPU render pass.
118    ///
119    /// `inputs` has `input_count()` textures: `inputs[0]` is the previous node's
120    /// final-pass output (or the source frame for the first node), and
121    /// `inputs[1..]` are the original source frame. `outputs` has `pass_count()`
122    /// pre-allocated `Rgba8Unorm` targets; write the final result into
123    /// `outputs[pass_count()-1]`, which the executor feeds to the next node.
124    fn process(
125        &self,
126        inputs: &[&wgpu::Texture],
127        outputs: &[&wgpu::Texture],
128        ctx: &crate::context::RenderContext,
129    );
130
131    /// Applies `param` to this node in place, returning whether it was taken.
132    ///
133    /// Takes `&self` because the caller only ever holds the graph, and because a
134    /// node that accepts a parameter is by nature one that already carries interior
135    /// mutability for its state.
136    ///
137    /// Defaults to `false`: a node that does not name a parameter is unaffected by
138    /// one, and a caller can tell nothing was applied.
139    fn set_param(&self, _param: NodeParam) -> bool {
140        false
141    }
142}