Path: blob/main/crates/bevy_render/src/render_graph/graph.rs
6596 views
use crate::{1render_graph::{2Edge, Node, NodeRunError, NodeState, RenderGraphContext, RenderGraphError, RenderLabel,3SlotInfo, SlotLabel,4},5renderer::RenderContext,6};7use bevy_ecs::{define_label, intern::Interned, prelude::World, resource::Resource};8use bevy_platform::collections::HashMap;9use core::fmt::Debug;1011use super::{EdgeExistence, InternedRenderLabel, IntoRenderNodeArray};1213pub use bevy_render_macros::RenderSubGraph;1415define_label!(16#[diagnostic::on_unimplemented(17note = "consider annotating `{Self}` with `#[derive(RenderSubGraph)]`"18)]19/// A strongly-typed class of labels used to identify a [`SubGraph`] in a render graph.20RenderSubGraph,21RENDER_SUB_GRAPH_INTERNER22);2324/// A shorthand for `Interned<dyn RenderSubGraph>`.25pub type InternedRenderSubGraph = Interned<dyn RenderSubGraph>;2627/// The render graph configures the modular and re-usable render logic.28///29/// It is a retained and stateless (nodes themselves may have their own internal state) structure,30/// which can not be modified while it is executed by the graph runner.31///32/// The render graph runner is responsible for executing the entire graph each frame.33/// It will execute each node in the graph in the correct order, based on the edges between the nodes.34///35/// It consists of three main components: [`Nodes`](Node), [`Edges`](Edge)36/// and [`Slots`](super::SlotType).37///38/// Nodes are responsible for generating draw calls and operating on input and output slots.39/// Edges specify the order of execution for nodes and connect input and output slots together.40/// Slots describe the render resources created or used by the nodes.41///42/// Additionally a render graph can contain multiple sub graphs, which are run by the43/// corresponding nodes. Every render graph can have its own optional input node.44///45/// ## Example46/// Here is a simple render graph example with two nodes connected by a node edge.47/// ```ignore48/// # TODO: Remove when #10645 is fixed49/// # use bevy_app::prelude::*;50/// # use bevy_ecs::prelude::World;51/// # use bevy_render::render_graph::{RenderGraph, RenderLabel, Node, RenderGraphContext, NodeRunError};52/// # use bevy_render::renderer::RenderContext;53/// #54/// #[derive(RenderLabel)]55/// enum Labels {56/// A,57/// B,58/// }59///60/// # struct MyNode;61/// #62/// # impl Node for MyNode {63/// # fn run(&self, graph: &mut RenderGraphContext, render_context: &mut RenderContext, world: &World) -> Result<(), NodeRunError> {64/// # unimplemented!()65/// # }66/// # }67/// #68/// let mut graph = RenderGraph::default();69/// graph.add_node(Labels::A, MyNode);70/// graph.add_node(Labels::B, MyNode);71/// graph.add_node_edge(Labels::B, Labels::A);72/// ```73#[derive(Resource, Default)]74pub struct RenderGraph {75nodes: HashMap<InternedRenderLabel, NodeState>,76sub_graphs: HashMap<InternedRenderSubGraph, RenderGraph>,77}7879/// The label for the input node of a graph. Used to connect other nodes to it.80#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]81pub struct GraphInput;8283impl RenderGraph {84/// Updates all nodes and sub graphs of the render graph. Should be called before executing it.85pub fn update(&mut self, world: &mut World) {86for node in self.nodes.values_mut() {87node.node.update(world);88}8990for sub_graph in self.sub_graphs.values_mut() {91sub_graph.update(world);92}93}9495/// Creates an [`GraphInputNode`] with the specified slots if not already present.96pub fn set_input(&mut self, inputs: Vec<SlotInfo>) {97assert!(98matches!(99self.get_node_state(GraphInput),100Err(RenderGraphError::InvalidNode(_))101),102"Graph already has an input node"103);104105self.add_node(GraphInput, GraphInputNode { inputs });106}107108/// Returns the [`NodeState`] of the input node of this graph.109///110/// # See also111///112/// - [`input_node`](Self::input_node) for an unchecked version.113#[inline]114pub fn get_input_node(&self) -> Option<&NodeState> {115self.get_node_state(GraphInput).ok()116}117118/// Returns the [`NodeState`] of the input node of this graph.119///120/// # Panics121///122/// Panics if there is no input node set.123///124/// # See also125///126/// - [`get_input_node`](Self::get_input_node) for a version which returns an [`Option`] instead.127#[inline]128pub fn input_node(&self) -> &NodeState {129self.get_input_node().unwrap()130}131132/// Adds the `node` with the `label` to the graph.133/// If the label is already present replaces it instead.134pub fn add_node<T>(&mut self, label: impl RenderLabel, node: T)135where136T: Node,137{138let label = label.intern();139let node_state = NodeState::new(label, node);140self.nodes.insert(label, node_state);141}142143/// Add `node_edge`s based on the order of the given `edges` array.144///145/// Defining an edge that already exists is not considered an error with this api.146/// It simply won't create a new edge.147pub fn add_node_edges<const N: usize>(&mut self, edges: impl IntoRenderNodeArray<N>) {148for window in edges.into_array().windows(2) {149let [a, b] = window else {150break;151};152if let Err(err) = self.try_add_node_edge(*a, *b) {153match err {154// Already existing edges are very easy to produce with this api155// and shouldn't cause a panic156RenderGraphError::EdgeAlreadyExists(_) => {}157_ => panic!("{err:?}"),158}159}160}161}162163/// Removes the `node` with the `label` from the graph.164/// If the label does not exist, nothing happens.165pub fn remove_node(&mut self, label: impl RenderLabel) -> Result<(), RenderGraphError> {166let label = label.intern();167if let Some(node_state) = self.nodes.remove(&label) {168// Remove all edges from other nodes to this one. Note that as we're removing this169// node, we don't need to remove its input edges170for input_edge in node_state.edges.input_edges() {171match input_edge {172Edge::SlotEdge { output_node, .. }173| Edge::NodeEdge {174input_node: _,175output_node,176} => {177if let Ok(output_node) = self.get_node_state_mut(*output_node) {178output_node.edges.remove_output_edge(input_edge.clone())?;179}180}181}182}183// Remove all edges from this node to other nodes. Note that as we're removing this184// node, we don't need to remove its output edges185for output_edge in node_state.edges.output_edges() {186match output_edge {187Edge::SlotEdge {188output_node: _,189output_index: _,190input_node,191input_index: _,192}193| Edge::NodeEdge {194output_node: _,195input_node,196} => {197if let Ok(input_node) = self.get_node_state_mut(*input_node) {198input_node.edges.remove_input_edge(output_edge.clone())?;199}200}201}202}203}204205Ok(())206}207208/// Retrieves the [`NodeState`] referenced by the `label`.209pub fn get_node_state(&self, label: impl RenderLabel) -> Result<&NodeState, RenderGraphError> {210let label = label.intern();211self.nodes212.get(&label)213.ok_or(RenderGraphError::InvalidNode(label))214}215216/// Retrieves the [`NodeState`] referenced by the `label` mutably.217pub fn get_node_state_mut(218&mut self,219label: impl RenderLabel,220) -> Result<&mut NodeState, RenderGraphError> {221let label = label.intern();222self.nodes223.get_mut(&label)224.ok_or(RenderGraphError::InvalidNode(label))225}226227/// Retrieves the [`Node`] referenced by the `label`.228pub fn get_node<T>(&self, label: impl RenderLabel) -> Result<&T, RenderGraphError>229where230T: Node,231{232self.get_node_state(label).and_then(|n| n.node())233}234235/// Retrieves the [`Node`] referenced by the `label` mutably.236pub fn get_node_mut<T>(&mut self, label: impl RenderLabel) -> Result<&mut T, RenderGraphError>237where238T: Node,239{240self.get_node_state_mut(label).and_then(|n| n.node_mut())241}242243/// Adds the [`Edge::SlotEdge`] to the graph. This guarantees that the `output_node`244/// is run before the `input_node` and also connects the `output_slot` to the `input_slot`.245///246/// Fails if any invalid [`RenderLabel`]s or [`SlotLabel`]s are given.247///248/// # See also249///250/// - [`add_slot_edge`](Self::add_slot_edge) for an infallible version.251pub fn try_add_slot_edge(252&mut self,253output_node: impl RenderLabel,254output_slot: impl Into<SlotLabel>,255input_node: impl RenderLabel,256input_slot: impl Into<SlotLabel>,257) -> Result<(), RenderGraphError> {258let output_slot = output_slot.into();259let input_slot = input_slot.into();260261let output_node = output_node.intern();262let input_node = input_node.intern();263264let output_index = self265.get_node_state(output_node)?266.output_slots267.get_slot_index(output_slot.clone())268.ok_or(RenderGraphError::InvalidOutputNodeSlot(output_slot))?;269let input_index = self270.get_node_state(input_node)?271.input_slots272.get_slot_index(input_slot.clone())273.ok_or(RenderGraphError::InvalidInputNodeSlot(input_slot))?;274275let edge = Edge::SlotEdge {276output_node,277output_index,278input_node,279input_index,280};281282self.validate_edge(&edge, EdgeExistence::DoesNotExist)?;283284{285let output_node = self.get_node_state_mut(output_node)?;286output_node.edges.add_output_edge(edge.clone())?;287}288let input_node = self.get_node_state_mut(input_node)?;289input_node.edges.add_input_edge(edge)?;290291Ok(())292}293294/// Adds the [`Edge::SlotEdge`] to the graph. This guarantees that the `output_node`295/// is run before the `input_node` and also connects the `output_slot` to the `input_slot`.296///297/// # Panics298///299/// Any invalid [`RenderLabel`]s or [`SlotLabel`]s are given.300///301/// # See also302///303/// - [`try_add_slot_edge`](Self::try_add_slot_edge) for a fallible version.304pub fn add_slot_edge(305&mut self,306output_node: impl RenderLabel,307output_slot: impl Into<SlotLabel>,308input_node: impl RenderLabel,309input_slot: impl Into<SlotLabel>,310) {311self.try_add_slot_edge(output_node, output_slot, input_node, input_slot)312.unwrap();313}314315/// Removes the [`Edge::SlotEdge`] from the graph. If any nodes or slots do not exist then316/// nothing happens.317pub fn remove_slot_edge(318&mut self,319output_node: impl RenderLabel,320output_slot: impl Into<SlotLabel>,321input_node: impl RenderLabel,322input_slot: impl Into<SlotLabel>,323) -> Result<(), RenderGraphError> {324let output_slot = output_slot.into();325let input_slot = input_slot.into();326327let output_node = output_node.intern();328let input_node = input_node.intern();329330let output_index = self331.get_node_state(output_node)?332.output_slots333.get_slot_index(output_slot.clone())334.ok_or(RenderGraphError::InvalidOutputNodeSlot(output_slot))?;335let input_index = self336.get_node_state(input_node)?337.input_slots338.get_slot_index(input_slot.clone())339.ok_or(RenderGraphError::InvalidInputNodeSlot(input_slot))?;340341let edge = Edge::SlotEdge {342output_node,343output_index,344input_node,345input_index,346};347348self.validate_edge(&edge, EdgeExistence::Exists)?;349350{351let output_node = self.get_node_state_mut(output_node)?;352output_node.edges.remove_output_edge(edge.clone())?;353}354let input_node = self.get_node_state_mut(input_node)?;355input_node.edges.remove_input_edge(edge)?;356357Ok(())358}359360/// Adds the [`Edge::NodeEdge`] to the graph. This guarantees that the `output_node`361/// is run before the `input_node`.362///363/// Fails if any invalid [`RenderLabel`] is given.364///365/// # See also366///367/// - [`add_node_edge`](Self::add_node_edge) for an infallible version.368pub fn try_add_node_edge(369&mut self,370output_node: impl RenderLabel,371input_node: impl RenderLabel,372) -> Result<(), RenderGraphError> {373let output_node = output_node.intern();374let input_node = input_node.intern();375376let edge = Edge::NodeEdge {377output_node,378input_node,379};380381self.validate_edge(&edge, EdgeExistence::DoesNotExist)?;382383{384let output_node = self.get_node_state_mut(output_node)?;385output_node.edges.add_output_edge(edge.clone())?;386}387let input_node = self.get_node_state_mut(input_node)?;388input_node.edges.add_input_edge(edge)?;389390Ok(())391}392393/// Adds the [`Edge::NodeEdge`] to the graph. This guarantees that the `output_node`394/// is run before the `input_node`.395///396/// # Panics397///398/// Panics if any invalid [`RenderLabel`] is given.399///400/// # See also401///402/// - [`try_add_node_edge`](Self::try_add_node_edge) for a fallible version.403pub fn add_node_edge(&mut self, output_node: impl RenderLabel, input_node: impl RenderLabel) {404self.try_add_node_edge(output_node, input_node).unwrap();405}406407/// Removes the [`Edge::NodeEdge`] from the graph. If either node does not exist then nothing408/// happens.409pub fn remove_node_edge(410&mut self,411output_node: impl RenderLabel,412input_node: impl RenderLabel,413) -> Result<(), RenderGraphError> {414let output_node = output_node.intern();415let input_node = input_node.intern();416417let edge = Edge::NodeEdge {418output_node,419input_node,420};421422self.validate_edge(&edge, EdgeExistence::Exists)?;423424{425let output_node = self.get_node_state_mut(output_node)?;426output_node.edges.remove_output_edge(edge.clone())?;427}428let input_node = self.get_node_state_mut(input_node)?;429input_node.edges.remove_input_edge(edge)?;430431Ok(())432}433434/// Verifies that the edge existence is as expected and435/// checks that slot edges are connected correctly.436pub fn validate_edge(437&mut self,438edge: &Edge,439should_exist: EdgeExistence,440) -> Result<(), RenderGraphError> {441if should_exist == EdgeExistence::Exists && !self.has_edge(edge) {442return Err(RenderGraphError::EdgeDoesNotExist(edge.clone()));443} else if should_exist == EdgeExistence::DoesNotExist && self.has_edge(edge) {444return Err(RenderGraphError::EdgeAlreadyExists(edge.clone()));445}446447match *edge {448Edge::SlotEdge {449output_node,450output_index,451input_node,452input_index,453} => {454let output_node_state = self.get_node_state(output_node)?;455let input_node_state = self.get_node_state(input_node)?;456457let output_slot = output_node_state458.output_slots459.get_slot(output_index)460.ok_or(RenderGraphError::InvalidOutputNodeSlot(SlotLabel::Index(461output_index,462)))?;463let input_slot = input_node_state.input_slots.get_slot(input_index).ok_or(464RenderGraphError::InvalidInputNodeSlot(SlotLabel::Index(input_index)),465)?;466467if let Some(Edge::SlotEdge {468output_node: current_output_node,469..470}) = input_node_state.edges.input_edges().iter().find(|e| {471if let Edge::SlotEdge {472input_index: current_input_index,473..474} = e475{476input_index == *current_input_index477} else {478false479}480}) && should_exist == EdgeExistence::DoesNotExist481{482return Err(RenderGraphError::NodeInputSlotAlreadyOccupied {483node: input_node,484input_slot: input_index,485occupied_by_node: *current_output_node,486});487}488489if output_slot.slot_type != input_slot.slot_type {490return Err(RenderGraphError::MismatchedNodeSlots {491output_node,492output_slot: output_index,493input_node,494input_slot: input_index,495});496}497}498Edge::NodeEdge { .. } => { /* nothing to validate here */ }499}500501Ok(())502}503504/// Checks whether the `edge` already exists in the graph.505pub fn has_edge(&self, edge: &Edge) -> bool {506let output_node_state = self.get_node_state(edge.get_output_node());507let input_node_state = self.get_node_state(edge.get_input_node());508if let Ok(output_node_state) = output_node_state509&& output_node_state.edges.output_edges().contains(edge)510&& let Ok(input_node_state) = input_node_state511&& input_node_state.edges.input_edges().contains(edge)512{513return true;514}515516false517}518519/// Returns an iterator over the [`NodeStates`](NodeState).520pub fn iter_nodes(&self) -> impl Iterator<Item = &NodeState> {521self.nodes.values()522}523524/// Returns an iterator over the [`NodeStates`](NodeState), that allows modifying each value.525pub fn iter_nodes_mut(&mut self) -> impl Iterator<Item = &mut NodeState> {526self.nodes.values_mut()527}528529/// Returns an iterator over the sub graphs.530pub fn iter_sub_graphs(&self) -> impl Iterator<Item = (InternedRenderSubGraph, &RenderGraph)> {531self.sub_graphs.iter().map(|(name, graph)| (*name, graph))532}533534/// Returns an iterator over the sub graphs, that allows modifying each value.535pub fn iter_sub_graphs_mut(536&mut self,537) -> impl Iterator<Item = (InternedRenderSubGraph, &mut RenderGraph)> {538self.sub_graphs539.iter_mut()540.map(|(name, graph)| (*name, graph))541}542543/// Returns an iterator over a tuple of the input edges and the corresponding output nodes544/// for the node referenced by the label.545pub fn iter_node_inputs(546&self,547label: impl RenderLabel,548) -> Result<impl Iterator<Item = (&Edge, &NodeState)>, RenderGraphError> {549let node = self.get_node_state(label)?;550Ok(node551.edges552.input_edges()553.iter()554.map(|edge| (edge, edge.get_output_node()))555.map(move |(edge, output_node)| (edge, self.get_node_state(output_node).unwrap())))556}557558/// Returns an iterator over a tuple of the output edges and the corresponding input nodes559/// for the node referenced by the label.560pub fn iter_node_outputs(561&self,562label: impl RenderLabel,563) -> Result<impl Iterator<Item = (&Edge, &NodeState)>, RenderGraphError> {564let node = self.get_node_state(label)?;565Ok(node566.edges567.output_edges()568.iter()569.map(|edge| (edge, edge.get_input_node()))570.map(move |(edge, input_node)| (edge, self.get_node_state(input_node).unwrap())))571}572573/// Adds the `sub_graph` with the `label` to the graph.574/// If the label is already present replaces it instead.575pub fn add_sub_graph(&mut self, label: impl RenderSubGraph, sub_graph: RenderGraph) {576self.sub_graphs.insert(label.intern(), sub_graph);577}578579/// Removes the `sub_graph` with the `label` from the graph.580/// If the label does not exist then nothing happens.581pub fn remove_sub_graph(&mut self, label: impl RenderSubGraph) {582self.sub_graphs.remove(&label.intern());583}584585/// Retrieves the sub graph corresponding to the `label`.586pub fn get_sub_graph(&self, label: impl RenderSubGraph) -> Option<&RenderGraph> {587self.sub_graphs.get(&label.intern())588}589590/// Retrieves the sub graph corresponding to the `label` mutably.591pub fn get_sub_graph_mut(&mut self, label: impl RenderSubGraph) -> Option<&mut RenderGraph> {592self.sub_graphs.get_mut(&label.intern())593}594595/// Retrieves the sub graph corresponding to the `label`.596///597/// # Panics598///599/// Panics if any invalid subgraph label is given.600///601/// # See also602///603/// - [`get_sub_graph`](Self::get_sub_graph) for a fallible version.604pub fn sub_graph(&self, label: impl RenderSubGraph) -> &RenderGraph {605let label = label.intern();606self.sub_graphs607.get(&label)608.unwrap_or_else(|| panic!("Subgraph {label:?} not found"))609}610611/// Retrieves the sub graph corresponding to the `label` mutably.612///613/// # Panics614///615/// Panics if any invalid subgraph label is given.616///617/// # See also618///619/// - [`get_sub_graph_mut`](Self::get_sub_graph_mut) for a fallible version.620pub fn sub_graph_mut(&mut self, label: impl RenderSubGraph) -> &mut RenderGraph {621let label = label.intern();622self.sub_graphs623.get_mut(&label)624.unwrap_or_else(|| panic!("Subgraph {label:?} not found"))625}626}627628impl Debug for RenderGraph {629fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {630for node in self.iter_nodes() {631writeln!(f, "{:?}", node.label)?;632writeln!(f, " in: {:?}", node.input_slots)?;633writeln!(f, " out: {:?}", node.output_slots)?;634}635636Ok(())637}638}639640/// A [`Node`] which acts as an entry point for a [`RenderGraph`] with custom inputs.641/// It has the same input and output slots and simply copies them over when run.642pub struct GraphInputNode {643inputs: Vec<SlotInfo>,644}645646impl Node for GraphInputNode {647fn input(&self) -> Vec<SlotInfo> {648self.inputs.clone()649}650651fn output(&self) -> Vec<SlotInfo> {652self.inputs.clone()653}654655fn run(656&self,657graph: &mut RenderGraphContext,658_render_context: &mut RenderContext,659_world: &World,660) -> Result<(), NodeRunError> {661for i in 0..graph.inputs().len() {662let input = graph.inputs()[i].clone();663graph.set_output(i, input)?;664}665Ok(())666}667}668669#[cfg(test)]670mod tests {671use crate::{672render_graph::{673node::IntoRenderNodeArray, Edge, InternedRenderLabel, Node, NodeRunError, RenderGraph,674RenderGraphContext, RenderGraphError, RenderLabel, SlotInfo, SlotType,675},676renderer::RenderContext,677};678use bevy_ecs::world::{FromWorld, World};679use bevy_platform::collections::HashSet;680681#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]682enum TestLabel {683A,684B,685C,686D,687}688689#[derive(Debug)]690struct TestNode {691inputs: Vec<SlotInfo>,692outputs: Vec<SlotInfo>,693}694695impl TestNode {696pub fn new(inputs: usize, outputs: usize) -> Self {697TestNode {698inputs: (0..inputs)699.map(|i| SlotInfo::new(format!("in_{i}"), SlotType::TextureView))700.collect(),701outputs: (0..outputs)702.map(|i| SlotInfo::new(format!("out_{i}"), SlotType::TextureView))703.collect(),704}705}706}707708impl Node for TestNode {709fn input(&self) -> Vec<SlotInfo> {710self.inputs.clone()711}712713fn output(&self) -> Vec<SlotInfo> {714self.outputs.clone()715}716717fn run(718&self,719_: &mut RenderGraphContext,720_: &mut RenderContext,721_: &World,722) -> Result<(), NodeRunError> {723Ok(())724}725}726727fn input_nodes(label: impl RenderLabel, graph: &RenderGraph) -> HashSet<InternedRenderLabel> {728graph729.iter_node_inputs(label)730.unwrap()731.map(|(_edge, node)| node.label)732.collect::<HashSet<InternedRenderLabel>>()733}734735fn output_nodes(label: impl RenderLabel, graph: &RenderGraph) -> HashSet<InternedRenderLabel> {736graph737.iter_node_outputs(label)738.unwrap()739.map(|(_edge, node)| node.label)740.collect::<HashSet<InternedRenderLabel>>()741}742743#[test]744fn test_graph_edges() {745let mut graph = RenderGraph::default();746graph.add_node(TestLabel::A, TestNode::new(0, 1));747graph.add_node(TestLabel::B, TestNode::new(0, 1));748graph.add_node(TestLabel::C, TestNode::new(1, 1));749graph.add_node(TestLabel::D, TestNode::new(1, 0));750751graph.add_slot_edge(TestLabel::A, "out_0", TestLabel::C, "in_0");752graph.add_node_edge(TestLabel::B, TestLabel::C);753graph.add_slot_edge(TestLabel::C, 0, TestLabel::D, 0);754755assert!(756input_nodes(TestLabel::A, &graph).is_empty(),757"A has no inputs"758);759assert_eq!(760output_nodes(TestLabel::A, &graph),761HashSet::from_iter((TestLabel::C,).into_array()),762"A outputs to C"763);764765assert!(766input_nodes(TestLabel::B, &graph).is_empty(),767"B has no inputs"768);769assert_eq!(770output_nodes(TestLabel::B, &graph),771HashSet::from_iter((TestLabel::C,).into_array()),772"B outputs to C"773);774775assert_eq!(776input_nodes(TestLabel::C, &graph),777HashSet::from_iter((TestLabel::A, TestLabel::B).into_array()),778"A and B input to C"779);780assert_eq!(781output_nodes(TestLabel::C, &graph),782HashSet::from_iter((TestLabel::D,).into_array()),783"C outputs to D"784);785786assert_eq!(787input_nodes(TestLabel::D, &graph),788HashSet::from_iter((TestLabel::C,).into_array()),789"C inputs to D"790);791assert!(792output_nodes(TestLabel::D, &graph).is_empty(),793"D has no outputs"794);795}796797#[test]798fn test_get_node_typed() {799struct MyNode {800value: usize,801}802803impl Node for MyNode {804fn run(805&self,806_: &mut RenderGraphContext,807_: &mut RenderContext,808_: &World,809) -> Result<(), NodeRunError> {810Ok(())811}812}813814let mut graph = RenderGraph::default();815816graph.add_node(TestLabel::A, MyNode { value: 42 });817818let node: &MyNode = graph.get_node(TestLabel::A).unwrap();819assert_eq!(node.value, 42, "node value matches");820821let result: Result<&TestNode, RenderGraphError> = graph.get_node(TestLabel::A);822assert_eq!(823result.unwrap_err(),824RenderGraphError::WrongNodeType,825"expect a wrong node type error"826);827}828829#[test]830fn test_slot_already_occupied() {831let mut graph = RenderGraph::default();832833graph.add_node(TestLabel::A, TestNode::new(0, 1));834graph.add_node(TestLabel::B, TestNode::new(0, 1));835graph.add_node(TestLabel::C, TestNode::new(1, 1));836837graph.add_slot_edge(TestLabel::A, 0, TestLabel::C, 0);838assert_eq!(839graph.try_add_slot_edge(TestLabel::B, 0, TestLabel::C, 0),840Err(RenderGraphError::NodeInputSlotAlreadyOccupied {841node: TestLabel::C.intern(),842input_slot: 0,843occupied_by_node: TestLabel::A.intern(),844}),845"Adding to a slot that is already occupied should return an error"846);847}848849#[test]850fn test_edge_already_exists() {851let mut graph = RenderGraph::default();852853graph.add_node(TestLabel::A, TestNode::new(0, 1));854graph.add_node(TestLabel::B, TestNode::new(1, 0));855856graph.add_slot_edge(TestLabel::A, 0, TestLabel::B, 0);857assert_eq!(858graph.try_add_slot_edge(TestLabel::A, 0, TestLabel::B, 0),859Err(RenderGraphError::EdgeAlreadyExists(Edge::SlotEdge {860output_node: TestLabel::A.intern(),861output_index: 0,862input_node: TestLabel::B.intern(),863input_index: 0,864})),865"Adding to a duplicate edge should return an error"866);867}868869#[test]870fn test_add_node_edges() {871struct SimpleNode;872impl Node for SimpleNode {873fn run(874&self,875_graph: &mut RenderGraphContext,876_render_context: &mut RenderContext,877_world: &World,878) -> Result<(), NodeRunError> {879Ok(())880}881}882impl FromWorld for SimpleNode {883fn from_world(_world: &mut World) -> Self {884Self885}886}887888let mut graph = RenderGraph::default();889graph.add_node(TestLabel::A, SimpleNode);890graph.add_node(TestLabel::B, SimpleNode);891graph.add_node(TestLabel::C, SimpleNode);892893graph.add_node_edges((TestLabel::A, TestLabel::B, TestLabel::C));894895assert_eq!(896output_nodes(TestLabel::A, &graph),897HashSet::from_iter((TestLabel::B,).into_array()),898"A -> B"899);900assert_eq!(901input_nodes(TestLabel::B, &graph),902HashSet::from_iter((TestLabel::A,).into_array()),903"A -> B"904);905assert_eq!(906output_nodes(TestLabel::B, &graph),907HashSet::from_iter((TestLabel::C,).into_array()),908"B -> C"909);910assert_eq!(911input_nodes(TestLabel::C, &graph),912HashSet::from_iter((TestLabel::B,).into_array()),913"B -> C"914);915}916}917918919