use super::{Edge, RenderGraphError, ResourceSlotInfo, ResourceSlots}; use crate::renderer::RenderContext; use downcast_rs::{impl_downcast, Downcast}; use legion::prelude::{Resources, Schedulable, World}; use std::{borrow::Cow, fmt::Debug}; use uuid::Uuid; #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct NodeId(Uuid); impl NodeId { pub fn new() -> Self { NodeId(Uuid::new_v4()) } } pub trait Node: Downcast + Send + Sync + 'static { fn input(&self) -> &[ResourceSlotInfo] { &[] } fn output(&self) -> &[ResourceSlotInfo] { &[] } fn update( &mut self, world: &World, resources: &Resources, render_context: &mut dyn RenderContext, input: &ResourceSlots, output: &mut ResourceSlots, ); } impl_downcast!(Node); pub trait SystemNode: Node { fn get_system(&self) -> Box; } pub struct Edges { pub id: NodeId, pub input_edges: Vec, pub output_edges: Vec, } impl Edges { pub(crate) fn add_input_edge(&mut self, edge: Edge) -> Result<(), RenderGraphError> { if self.has_input_edge(&edge) { return Err(RenderGraphError::EdgeAlreadyExists(edge.clone())); } self.input_edges.push(edge); Ok(()) } pub(crate) fn add_output_edge(&mut self, edge: Edge) -> Result<(), RenderGraphError> { if self.has_output_edge(&edge) { return Err(RenderGraphError::EdgeAlreadyExists(edge.clone())); } self.output_edges.push(edge); Ok(()) } pub fn has_input_edge(&self, edge: &Edge) -> bool { self.input_edges.contains(edge) } pub fn has_output_edge(&self, edge: &Edge) -> bool { self.output_edges.contains(edge) } pub fn get_input_slot_edge(&self, index: usize) -> Result<&Edge, RenderGraphError> { self.input_edges .iter() .find(|e| { if let Edge::SlotEdge { input_index, .. } = e { *input_index == index } else { false } }) .ok_or_else(|| RenderGraphError::UnconnectedNodeInputSlot { input_slot: index, node: self.id, }) } pub fn get_output_slot_edge(&self, index: usize) -> Result<&Edge, RenderGraphError> { self.output_edges .iter() .find(|e| { if let Edge::SlotEdge { output_index, .. } = e { *output_index == index } else { false } }) .ok_or_else(|| RenderGraphError::UnconnectedNodeOutputSlot { output_slot: index, node: self.id, }) } } pub struct NodeState { pub id: NodeId, pub name: Option>, pub node: Box, pub input_slots: ResourceSlots, pub output_slots: ResourceSlots, pub edges: Edges, } impl Debug for NodeState { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "{:?} ({:?})", self.id, self.name) } } impl NodeState { pub fn new(id: NodeId, node: T) -> Self where T: Node, { NodeState { id, name: None, input_slots: ResourceSlots::from(node.input()), output_slots: ResourceSlots::from(node.output()), node: Box::new(node), edges: Edges { id, input_edges: Vec::new(), output_edges: Vec::new(), }, } } pub fn node(&self) -> Result<&T, RenderGraphError> where T: Node, { self.node .downcast_ref::() .ok_or_else(|| RenderGraphError::WrongNodeType) } pub fn node_mut(&mut self) -> Result<&mut T, RenderGraphError> where T: Node, { self.node .downcast_mut::() .ok_or_else(|| RenderGraphError::WrongNodeType) } pub fn validate_output_slots(&self) -> Result<(), RenderGraphError> { for i in 0..self.output_slots.len() { self.edges.get_output_slot_edge(i)?; } Ok(()) } pub fn validate_input_slots(&self) -> Result<(), RenderGraphError> { for i in 0..self.input_slots.len() { self.edges.get_input_slot_edge(i)?; } Ok(()) } } #[derive(Debug, Clone, Eq, PartialEq)] pub enum NodeLabel { Id(NodeId), Name(Cow<'static, str>), } impl From<&NodeLabel> for NodeLabel { fn from(value: &NodeLabel) -> Self { value.clone() } } impl From for NodeLabel { fn from(value: String) -> Self { NodeLabel::Name(value.into()) } } impl From<&'static str> for NodeLabel { fn from(value: &'static str) -> Self { NodeLabel::Name(value.into()) } } impl From for NodeLabel { fn from(value: NodeId) -> Self { NodeLabel::Id(value) } }