maplibre/render/graph/
node.rs

1use std::{borrow::Cow, fmt::Debug};
2
3use downcast_rs::{impl_downcast, Downcast};
4use thiserror::Error;
5
6use super::{
7    Edge, InputSlotError, OutputSlotError, RenderGraphContext, RenderGraphError, RunSubGraphError,
8    SlotInfo, SlotInfos,
9};
10use crate::{render::RenderResources, tcs::world::World};
11
12/// The context with all information required to interact with the GPU.
13///
14/// The [`Device`] is used to create render resources and the
15/// the [`CommandEncoder`] is used to record a series of GPU operations.
16pub struct RenderContext<'d> {
17    pub device: &'d wgpu::Device,
18    pub command_encoder: wgpu::CommandEncoder,
19}
20
21/// A [`Node`] identifier.
22/// It automatically generates its own random uuid.
23///
24/// This id is used to reference the node internally (edges, etc).
25#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
26pub struct NodeId(usize);
27
28impl NodeId {
29    #[allow(clippy::new_without_default)]
30    pub fn new(id: usize) -> Self {
31        NodeId(id)
32    }
33}
34
35/// A render node that can be added to a [`RenderGraph`](super::RenderGraph).
36///
37/// Nodes are the fundamental part of the graph and used to extend its functionality, by
38/// generating draw calls and/or running subgraphs.
39/// They are added via the `render_graph::add_node(my_node)` method.
40///
41/// To determine their position in the graph and ensure that all required dependencies (inputs)
42/// are already executed, [`Edges`](Edge) are used.
43///
44/// A node can produce outputs used as dependencies by other nodes.
45/// Those inputs and outputs are called slots and are the default way of passing render data
46/// inside the graph. For more information see [`SlotType`](super::SlotType).
47pub trait Node: Downcast + Send + Sync + 'static {
48    /// Specifies the required input slots for this node.
49    /// They will then be available during the run method inside the [`RenderGraphContext`].
50    fn input(&self) -> Vec<SlotInfo> {
51        Vec::new()
52    }
53
54    /// Specifies the produced output slots for this node.
55    /// They can then be passed one inside [`RenderGraphContext`] during the run method.
56    fn output(&self) -> Vec<SlotInfo> {
57        Vec::new()
58    }
59
60    /// Updates internal node state using the current [`RenderResources`] prior to the run method.
61    fn update(&mut self, _state: &mut RenderResources) {}
62
63    /// Runs the graph node logic, issues draw calls, updates the output slots and
64    /// optionally queues up subgraphs for execution. The graph data, input and output values are
65    /// passed via the [`RenderGraphContext`].
66    fn run(
67        &self,
68        graph: &mut RenderGraphContext,
69        render_context: &mut RenderContext,
70        resources: &RenderResources,
71        world: &World,
72    ) -> Result<(), NodeRunError>;
73}
74
75impl_downcast!(Node);
76
77#[derive(Error, Debug, Eq, PartialEq)]
78pub enum NodeRunError {
79    #[error("encountered an input slot error")]
80    InputSlotError(#[from] InputSlotError),
81    #[error("encountered an output slot error")]
82    OutputSlotError(#[from] OutputSlotError),
83    #[error("encountered an error when running a sub-graph")]
84    RunSubGraphError(#[from] RunSubGraphError),
85}
86
87/// A collection of input and output [`Edges`](Edge) for a [`Node`].
88#[derive(Debug)]
89pub struct Edges {
90    id: NodeId,
91    input_edges: Vec<Edge>,
92    output_edges: Vec<Edge>,
93}
94
95impl Edges {
96    /// Returns all "input edges" (edges going "in") for this node .
97    #[inline]
98    pub fn input_edges(&self) -> &[Edge] {
99        &self.input_edges
100    }
101
102    /// Returns all "output edges" (edges going "out") for this node .
103    #[inline]
104    pub fn output_edges(&self) -> &[Edge] {
105        &self.output_edges
106    }
107
108    /// Returns this node's id.
109    #[inline]
110    pub fn id(&self) -> NodeId {
111        self.id
112    }
113
114    /// Adds an edge to the `input_edges` if it does not already exist.
115    pub(crate) fn add_input_edge(&mut self, edge: Edge) -> Result<(), RenderGraphError> {
116        if self.has_input_edge(&edge) {
117            return Err(RenderGraphError::EdgeAlreadyExists(edge));
118        }
119        self.input_edges.push(edge);
120        Ok(())
121    }
122
123    /// Removes an edge from the `input_edges` if it exists.
124    pub(crate) fn remove_input_edge(&mut self, edge: Edge) -> Result<(), RenderGraphError> {
125        if let Some((index, _)) = self
126            .input_edges
127            .iter()
128            .enumerate()
129            .find(|(_i, e)| **e == edge)
130        {
131            self.input_edges.swap_remove(index);
132            Ok(())
133        } else {
134            Err(RenderGraphError::EdgeDoesNotExist(edge))
135        }
136    }
137
138    /// Adds an edge to the `output_edges` if it does not already exist.
139    pub(crate) fn add_output_edge(&mut self, edge: Edge) -> Result<(), RenderGraphError> {
140        if self.has_output_edge(&edge) {
141            return Err(RenderGraphError::EdgeAlreadyExists(edge));
142        }
143        self.output_edges.push(edge);
144        Ok(())
145    }
146
147    /// Removes an edge from the `output_edges` if it exists.
148    pub(crate) fn remove_output_edge(&mut self, edge: Edge) -> Result<(), RenderGraphError> {
149        if let Some((index, _)) = self
150            .output_edges
151            .iter()
152            .enumerate()
153            .find(|(_i, e)| **e == edge)
154        {
155            self.output_edges.swap_remove(index);
156            Ok(())
157        } else {
158            Err(RenderGraphError::EdgeDoesNotExist(edge))
159        }
160    }
161
162    /// Checks whether the input edge already exists.
163    pub fn has_input_edge(&self, edge: &Edge) -> bool {
164        self.input_edges.contains(edge)
165    }
166
167    /// Checks whether the output edge already exists.
168    pub fn has_output_edge(&self, edge: &Edge) -> bool {
169        self.output_edges.contains(edge)
170    }
171
172    /// Searches the `input_edges` for a [`Edge::SlotEdge`],
173    /// which `input_index` matches the `index`;
174    pub fn get_input_slot_edge(&self, index: usize) -> Result<&Edge, RenderGraphError> {
175        self.input_edges
176            .iter()
177            .find(|e| {
178                if let Edge::SlotEdge { input_index, .. } = e {
179                    *input_index == index
180                } else {
181                    false
182                }
183            })
184            .ok_or(RenderGraphError::UnconnectedNodeInputSlot {
185                input_slot: index,
186                node: self.id,
187            })
188    }
189
190    /// Searches the `output_edges` for a [`Edge::SlotEdge`],
191    /// which `output_index` matches the `index`;
192    pub fn get_output_slot_edge(&self, index: usize) -> Result<&Edge, RenderGraphError> {
193        self.output_edges
194            .iter()
195            .find(|e| {
196                if let Edge::SlotEdge { output_index, .. } = e {
197                    *output_index == index
198                } else {
199                    false
200                }
201            })
202            .ok_or(RenderGraphError::UnconnectedNodeOutputSlot {
203                output_slot: index,
204                node: self.id,
205            })
206    }
207}
208
209/// The internal representation of a [`Node`], with all data required
210/// by the [`RenderGraph`](super::RenderGraph).
211///
212/// The `input_slots` and `output_slots` are provided by the `node`.
213pub struct NodeState {
214    pub id: NodeId,
215    pub name: Option<Cow<'static, str>>,
216    /// The name of the type that implements [`Node`].
217    pub type_name: &'static str,
218    pub node: Box<dyn Node>,
219    pub input_slots: SlotInfos,
220    pub output_slots: SlotInfos,
221    pub edges: Edges,
222}
223
224impl Debug for NodeState {
225    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226        writeln!(f, "{:?} ({:?})", self.id, self.name)
227    }
228}
229
230impl NodeState {
231    /// Creates an [`NodeState`] without edges, but the `input_slots` and `output_slots`
232    /// are provided by the `node`.
233    pub fn new<T>(id: NodeId, node: T) -> Self
234    where
235        T: Node,
236    {
237        NodeState {
238            id,
239            name: None,
240            input_slots: node.input().into(),
241            output_slots: node.output().into(),
242            node: Box::new(node),
243            type_name: std::any::type_name::<T>(),
244            edges: Edges {
245                id,
246                input_edges: Vec::new(),
247                output_edges: Vec::new(),
248            },
249        }
250    }
251
252    /// Retrieves the [`Node`].
253    pub fn node<T>(&self) -> Result<&T, RenderGraphError>
254    where
255        T: Node,
256    {
257        self.node
258            .downcast_ref::<T>()
259            .ok_or(RenderGraphError::WrongNodeType)
260    }
261
262    /// Retrieves the [`Node`] mutably.
263    pub fn node_mut<T>(&mut self) -> Result<&mut T, RenderGraphError>
264    where
265        T: Node,
266    {
267        self.node
268            .downcast_mut::<T>()
269            .ok_or(RenderGraphError::WrongNodeType)
270    }
271
272    /// Validates that each input slot corresponds to an input edge.
273    pub fn validate_input_slots(&self) -> Result<(), RenderGraphError> {
274        for i in 0..self.input_slots.len() {
275            self.edges.get_input_slot_edge(i)?;
276        }
277
278        Ok(())
279    }
280
281    /// Validates that each output slot corresponds to an output edge.
282    pub fn validate_output_slots(&self) -> Result<(), RenderGraphError> {
283        for i in 0..self.output_slots.len() {
284            self.edges.get_output_slot_edge(i)?;
285        }
286
287        Ok(())
288    }
289}
290
291/// A [`NodeLabel`] is used to reference a [`NodeState`] by either its name or [`NodeId`]
292/// inside the [`RenderGraph`](super::RenderGraph).
293#[derive(Debug, Clone, Eq, PartialEq)]
294pub enum NodeLabel {
295    Id(NodeId),
296    Name(Cow<'static, str>),
297}
298
299impl From<&NodeLabel> for NodeLabel {
300    fn from(value: &NodeLabel) -> Self {
301        value.clone()
302    }
303}
304
305impl From<String> for NodeLabel {
306    fn from(value: String) -> Self {
307        NodeLabel::Name(value.into())
308    }
309}
310
311impl From<&'static str> for NodeLabel {
312    fn from(value: &'static str) -> Self {
313        NodeLabel::Name(value.into())
314    }
315}
316
317impl From<NodeId> for NodeLabel {
318    fn from(value: NodeId) -> Self {
319        NodeLabel::Id(value)
320    }
321}
322
323/// A [`Node`] without any inputs, outputs and subgraphs, which does nothing when run.
324/// Used (as a label) to bundle multiple dependencies into one inside
325/// the [`RenderGraph`](super::RenderGraph).
326pub struct EmptyNode;
327
328impl Node for EmptyNode {
329    fn run(
330        &self,
331        _graph: &mut RenderGraphContext,
332        _render_context: &mut RenderContext,
333        _state: &RenderResources,
334        _world: &World,
335    ) -> Result<(), NodeRunError> {
336        Ok(())
337    }
338}