Skip to content

Commit 82b73f2

Browse files
superdumpcart
authored andcommitted
bevy_render: Support removal of nodes, edges, subgraphs (bevyengine#3048)
Add support for removing nodes, edges, and subgraphs. This enables live re-wiring of the render graph. This was something I did to support the MSAA implementation, but it turned out to be unnecessary there. However, it is still useful so here it is in its own PR. Co-authored-by: Carter Anderson <[email protected]>
1 parent c222b34 commit 82b73f2

File tree

4 files changed

+224
-18
lines changed

4 files changed

+224
-18
lines changed

crates/bevy_render/src/render_graph/edge.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,9 @@ impl Edge {
4848
}
4949
}
5050
}
51+
52+
#[derive(PartialEq, Eq)]
53+
pub enum EdgeExistence {
54+
Exists,
55+
DoesNotExist,
56+
}

crates/bevy_render/src/render_graph/graph.rs

Lines changed: 165 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use bevy_ecs::prelude::World;
99
use bevy_utils::HashMap;
1010
use std::{borrow::Cow, fmt::Debug};
1111

12+
use super::EdgeExistence;
13+
1214
/// The render graph configures the modular, parallel and re-usable render logic.
1315
/// It is a retained and stateless (nodes itself my have their internal state) structure,
1416
/// which can not be modified while it is executed by the graph runner.
@@ -99,6 +101,69 @@ impl RenderGraph {
99101
id
100102
}
101103

104+
/// Removes the `node` with the `name` from the graph.
105+
/// If the name is does not exist, nothing happens.
106+
pub fn remove_node(
107+
&mut self,
108+
name: impl Into<Cow<'static, str>>,
109+
) -> Result<(), RenderGraphError> {
110+
let name = name.into();
111+
if let Some(id) = self.node_names.remove(&name) {
112+
if let Some(node_state) = self.nodes.remove(&id) {
113+
// Remove all edges from other nodes to this one. Note that as we're removing this
114+
// node, we don't need to remove its input edges
115+
for input_edge in node_state.edges.input_edges().iter() {
116+
match input_edge {
117+
Edge::SlotEdge {
118+
output_node,
119+
output_index: _,
120+
input_node: _,
121+
input_index: _,
122+
} => {
123+
if let Ok(output_node) = self.get_node_state_mut(*output_node) {
124+
output_node.edges.remove_output_edge(input_edge.clone())?;
125+
}
126+
}
127+
Edge::NodeEdge {
128+
input_node: _,
129+
output_node,
130+
} => {
131+
if let Ok(output_node) = self.get_node_state_mut(*output_node) {
132+
output_node.edges.remove_output_edge(input_edge.clone())?;
133+
}
134+
}
135+
}
136+
}
137+
// Remove all edges from this node to other nodes. Note that as we're removing this
138+
// node, we don't need to remove its output edges
139+
for output_edge in node_state.edges.output_edges().iter() {
140+
match output_edge {
141+
Edge::SlotEdge {
142+
output_node: _,
143+
output_index: _,
144+
input_node,
145+
input_index: _,
146+
} => {
147+
if let Ok(input_node) = self.get_node_state_mut(*input_node) {
148+
input_node.edges.remove_input_edge(output_edge.clone())?;
149+
}
150+
}
151+
Edge::NodeEdge {
152+
output_node: _,
153+
input_node,
154+
} => {
155+
if let Ok(input_node) = self.get_node_state_mut(*input_node) {
156+
input_node.edges.remove_input_edge(output_edge.clone())?;
157+
}
158+
}
159+
}
160+
}
161+
}
162+
}
163+
164+
Ok(())
165+
}
166+
102167
/// Retrieves the [`NodeState`] referenced by the `label`.
103168
pub fn get_node_state(
104169
&self,
@@ -187,7 +252,7 @@ impl RenderGraph {
187252
input_index,
188253
};
189254

190-
self.validate_edge(&edge)?;
255+
self.validate_edge(&edge, EdgeExistence::DoesNotExist)?;
191256

192257
{
193258
let output_node = self.get_node_state_mut(output_node_id)?;
@@ -199,6 +264,50 @@ impl RenderGraph {
199264
Ok(())
200265
}
201266

267+
/// Removes the [`Edge::SlotEdge`] from the graph. If any nodes or slots do not exist then
268+
/// nothing happens.
269+
pub fn remove_slot_edge(
270+
&mut self,
271+
output_node: impl Into<NodeLabel>,
272+
output_slot: impl Into<SlotLabel>,
273+
input_node: impl Into<NodeLabel>,
274+
input_slot: impl Into<SlotLabel>,
275+
) -> Result<(), RenderGraphError> {
276+
let output_slot = output_slot.into();
277+
let input_slot = input_slot.into();
278+
let output_node_id = self.get_node_id(output_node)?;
279+
let input_node_id = self.get_node_id(input_node)?;
280+
281+
let output_index = self
282+
.get_node_state(output_node_id)?
283+
.output_slots
284+
.get_slot_index(output_slot.clone())
285+
.ok_or(RenderGraphError::InvalidOutputNodeSlot(output_slot))?;
286+
let input_index = self
287+
.get_node_state(input_node_id)?
288+
.input_slots
289+
.get_slot_index(input_slot.clone())
290+
.ok_or(RenderGraphError::InvalidInputNodeSlot(input_slot))?;
291+
292+
let edge = Edge::SlotEdge {
293+
output_node: output_node_id,
294+
output_index,
295+
input_node: input_node_id,
296+
input_index,
297+
};
298+
299+
self.validate_edge(&edge, EdgeExistence::Exists)?;
300+
301+
{
302+
let output_node = self.get_node_state_mut(output_node_id)?;
303+
output_node.edges.remove_output_edge(edge.clone())?;
304+
}
305+
let input_node = self.get_node_state_mut(input_node_id)?;
306+
input_node.edges.remove_input_edge(edge)?;
307+
308+
Ok(())
309+
}
310+
202311
/// Adds the [`Edge::NodeEdge`] to the graph. This guarantees that the `output_node`
203312
/// is run before the `input_node`.
204313
pub fn add_node_edge(
@@ -214,7 +323,7 @@ impl RenderGraph {
214323
input_node: input_node_id,
215324
};
216325

217-
self.validate_edge(&edge)?;
326+
self.validate_edge(&edge, EdgeExistence::DoesNotExist)?;
218327

219328
{
220329
let output_node = self.get_node_state_mut(output_node_id)?;
@@ -226,10 +335,43 @@ impl RenderGraph {
226335
Ok(())
227336
}
228337

229-
/// Verifies that the edge is not already existing and
338+
/// Removes the [`Edge::NodeEdge`] from the graph. If either node does not exist then nothing
339+
/// happens.
340+
pub fn remove_node_edge(
341+
&mut self,
342+
output_node: impl Into<NodeLabel>,
343+
input_node: impl Into<NodeLabel>,
344+
) -> Result<(), RenderGraphError> {
345+
let output_node_id = self.get_node_id(output_node)?;
346+
let input_node_id = self.get_node_id(input_node)?;
347+
348+
let edge = Edge::NodeEdge {
349+
output_node: output_node_id,
350+
input_node: input_node_id,
351+
};
352+
353+
self.validate_edge(&edge, EdgeExistence::Exists)?;
354+
355+
{
356+
let output_node = self.get_node_state_mut(output_node_id)?;
357+
output_node.edges.remove_output_edge(edge.clone())?;
358+
}
359+
let input_node = self.get_node_state_mut(input_node_id)?;
360+
input_node.edges.remove_input_edge(edge)?;
361+
362+
Ok(())
363+
}
364+
365+
/// Verifies that the edge existence is as expected and
230366
/// checks that slot edges are connected correctly.
231-
pub fn validate_edge(&mut self, edge: &Edge) -> Result<(), RenderGraphError> {
232-
if self.has_edge(edge) {
367+
pub fn validate_edge(
368+
&mut self,
369+
edge: &Edge,
370+
should_exist: EdgeExistence,
371+
) -> Result<(), RenderGraphError> {
372+
if should_exist == EdgeExistence::Exists && !self.has_edge(edge) {
373+
return Err(RenderGraphError::EdgeDoesNotExist(edge.clone()));
374+
} else if should_exist == EdgeExistence::DoesNotExist && self.has_edge(edge) {
233375
return Err(RenderGraphError::EdgeAlreadyExists(edge.clone()));
234376
}
235377

@@ -256,7 +398,7 @@ impl RenderGraph {
256398
if let Some(Edge::SlotEdge {
257399
output_node: current_output_node,
258400
..
259-
}) = input_node_state.edges.input_edges.iter().find(|e| {
401+
}) = input_node_state.edges.input_edges().iter().find(|e| {
260402
if let Edge::SlotEdge {
261403
input_index: current_input_index,
262404
..
@@ -267,11 +409,13 @@ impl RenderGraph {
267409
false
268410
}
269411
}) {
270-
return Err(RenderGraphError::NodeInputSlotAlreadyOccupied {
271-
node: input_node,
272-
input_slot: input_index,
273-
occupied_by_node: *current_output_node,
274-
});
412+
if should_exist == EdgeExistence::DoesNotExist {
413+
return Err(RenderGraphError::NodeInputSlotAlreadyOccupied {
414+
node: input_node,
415+
input_slot: input_index,
416+
occupied_by_node: *current_output_node,
417+
});
418+
}
275419
}
276420

277421
if output_slot.slot_type != input_slot.slot_type {
@@ -294,9 +438,9 @@ impl RenderGraph {
294438
let output_node_state = self.get_node_state(edge.get_output_node());
295439
let input_node_state = self.get_node_state(edge.get_input_node());
296440
if let Ok(output_node_state) = output_node_state {
297-
if output_node_state.edges.output_edges.contains(edge) {
441+
if output_node_state.edges.output_edges().contains(edge) {
298442
if let Ok(input_node_state) = input_node_state {
299-
if input_node_state.edges.input_edges.contains(edge) {
443+
if input_node_state.edges.input_edges().contains(edge) {
300444
return true;
301445
}
302446
}
@@ -339,7 +483,7 @@ impl RenderGraph {
339483
let node = self.get_node_state(label)?;
340484
Ok(node
341485
.edges
342-
.input_edges
486+
.input_edges()
343487
.iter()
344488
.map(|edge| (edge, edge.get_output_node()))
345489
.map(move |(edge, output_node_id)| {
@@ -356,7 +500,7 @@ impl RenderGraph {
356500
let node = self.get_node_state(label)?;
357501
Ok(node
358502
.edges
359-
.output_edges
503+
.output_edges()
360504
.iter()
361505
.map(|edge| (edge, edge.get_input_node()))
362506
.map(move |(edge, input_node_id)| (edge, self.get_node_state(input_node_id).unwrap())))
@@ -368,6 +512,12 @@ impl RenderGraph {
368512
self.sub_graphs.insert(name.into(), sub_graph);
369513
}
370514

515+
/// Removes the `sub_graph` with the `name` from the graph.
516+
/// If the name does not exist then nothing happens.
517+
pub fn remove_sub_graph(&mut self, name: impl Into<Cow<'static, str>>) {
518+
self.sub_graphs.remove(&name.into());
519+
}
520+
371521
/// Retrieves the sub graph corresponding to the `name`.
372522
pub fn get_sub_graph(&self, name: impl AsRef<str>) -> Option<&RenderGraph> {
373523
self.sub_graphs.get(name.as_ref())

crates/bevy_render/src/render_graph/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ pub enum RenderGraphError {
3131
},
3232
#[error("attempted to add an edge that already exists")]
3333
EdgeAlreadyExists(Edge),
34+
#[error("attempted to remove an edge that does not exist")]
35+
EdgeDoesNotExist(Edge),
3436
#[error("node has an unconnected input slot")]
3537
UnconnectedNodeInputSlot { node: NodeId, input_slot: usize },
3638
#[error("node has an unconnected output slot")]

crates/bevy_render/src/render_graph/node.rs

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,30 @@ pub enum NodeRunError {
8383
/// A collection of input and output [`Edges`](Edge) for a [`Node`].
8484
#[derive(Debug)]
8585
pub struct Edges {
86-
pub id: NodeId,
87-
pub input_edges: Vec<Edge>,
88-
pub output_edges: Vec<Edge>,
86+
id: NodeId,
87+
input_edges: Vec<Edge>,
88+
output_edges: Vec<Edge>,
8989
}
9090

9191
impl Edges {
92+
/// Returns all "input edges" (edges going "in") for this node .
93+
#[inline]
94+
pub fn input_edges(&self) -> &[Edge] {
95+
&self.input_edges
96+
}
97+
98+
/// Returns all "output edges" (edges going "out") for this node .
99+
#[inline]
100+
pub fn output_edges(&self) -> &[Edge] {
101+
&self.output_edges
102+
}
103+
104+
/// Returns this node's id.
105+
#[inline]
106+
pub fn id(&self) -> NodeId {
107+
self.id
108+
}
109+
92110
/// Adds an edge to the `input_edges` if it does not already exist.
93111
pub(crate) fn add_input_edge(&mut self, edge: Edge) -> Result<(), RenderGraphError> {
94112
if self.has_input_edge(&edge) {
@@ -98,6 +116,21 @@ impl Edges {
98116
Ok(())
99117
}
100118

119+
/// Removes an edge from the `input_edges` if it exists.
120+
pub(crate) fn remove_input_edge(&mut self, edge: Edge) -> Result<(), RenderGraphError> {
121+
if let Some((index, _)) = self
122+
.input_edges
123+
.iter()
124+
.enumerate()
125+
.find(|(_i, e)| **e == edge)
126+
{
127+
self.input_edges.swap_remove(index);
128+
Ok(())
129+
} else {
130+
Err(RenderGraphError::EdgeDoesNotExist(edge))
131+
}
132+
}
133+
101134
/// Adds an edge to the `output_edges` if it does not already exist.
102135
pub(crate) fn add_output_edge(&mut self, edge: Edge) -> Result<(), RenderGraphError> {
103136
if self.has_output_edge(&edge) {
@@ -107,6 +140,21 @@ impl Edges {
107140
Ok(())
108141
}
109142

143+
/// Removes an edge from the `output_edges` if it exists.
144+
pub(crate) fn remove_output_edge(&mut self, edge: Edge) -> Result<(), RenderGraphError> {
145+
if let Some((index, _)) = self
146+
.output_edges
147+
.iter()
148+
.enumerate()
149+
.find(|(_i, e)| **e == edge)
150+
{
151+
self.output_edges.swap_remove(index);
152+
Ok(())
153+
} else {
154+
Err(RenderGraphError::EdgeDoesNotExist(edge))
155+
}
156+
}
157+
110158
/// Checks whether the input edge already exists.
111159
pub fn has_input_edge(&self, edge: &Edge) -> bool {
112160
self.input_edges.contains(edge)

0 commit comments

Comments
 (0)