-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add RenderGraphApp to simplify adding render nodes #8007
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A solid step towards more ergonomic render nodes :)
9bed368
to
362d417
Compare
94bcf89
to
ba346a6
Compare
@@ -676,6 +714,22 @@ mod tests { | |||
} | |||
} | |||
|
|||
fn input_nodes(name: &'static str, graph: &RenderGraph) -> HashSet<NodeId> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code was simply moved out of test_graph_edges because I used it in the new test.
# Objective - Currently, the render graph slots are only used to pass the view_entity around. This introduces significant boilerplate for very little value. Instead of using slots for this, make the view_entity part of the `RenderGraphContext`. This also means we won't need to have `IN_VIEW` on every node and and we'll be able to use the default impl of `Node::input()`. ## Solution - Add `view_entity: Option<Entity>` to the `RenderGraphContext` - Update all nodes to use this instead of entity slot input --- ## Changelog - Add optional `view_entity` to `RenderGraphContext` ## Migration Guide You can now get the view_entity directly from the `RenderGraphContext`. When implementing the Node: ```rust // 0.10 struct FooNode; impl FooNode { const IN_VIEW: &'static str = "view"; } impl Node for FooNode { fn input(&self) -> Vec<SlotInfo> { vec![SlotInfo::new(Self::IN_VIEW, SlotType::Entity)] } fn run( &self, graph: &mut RenderGraphContext, // ... ) -> Result<(), NodeRunError> { let view_entity = graph.get_input_entity(Self::IN_VIEW)?; // ... Ok(()) } } // 0.11 struct FooNode; impl Node for FooNode { fn run( &self, graph: &mut RenderGraphContext, // ... ) -> Result<(), NodeRunError> { let view_entity = graph.view_entity(); // ... Ok(()) } } ``` When adding the node to the graph, you don't need to specify a slot_edge for the view_entity. ```rust // 0.10 let mut graph = RenderGraph::default(); graph.add_node(FooNode::NAME, node); let input_node_id = draw_2d_graph.set_input(vec![SlotInfo::new( graph::input::VIEW_ENTITY, SlotType::Entity, )]); graph.add_slot_edge( input_node_id, graph::input::VIEW_ENTITY, FooNode::NAME, FooNode::IN_VIEW, ); // add_node_edge ... // 0.11 let mut graph = RenderGraph::default(); graph.add_node(FooNode::NAME, node); // add_node_edge ... ``` ## Notes This PR paired with #8007 will help reduce a lot of annoying boilerplate with the render nodes. Depending on which one gets merged first. It will require a bit of clean up work to make both compatible. I tagged this as a breaking change, because using the old system to get the view_entity will break things because it's not a node input slot anymore. ## Notes for reviewers A lot of the diffs are just removing the slots in every nodes and graph creation. The important part is mostly in the graph_runner/CameraDriverNode.
ba346a6
to
22d741f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A nice change.
Why don't we store the name of each node on the node itself?
struct MyNode: Node {}
impl Node for MyNode {
fn name() -> &'static str {
"MyNode"
}
}
I've been considering doing that because it would make a few of those patterns even simpler. The issue is that it wouldn't work with EmptyNode that is used in a few places like post processing, but having a way to automatically label a node would be nice a bit like how you can use systems as labels. So for now, I'd prefer keeping this PR simpler and consider better labels in a future PR |
18f9a3d
to
5849c15
Compare
While updating this for TAA, I noticed that adding the same edge multiple time would cause a panic so I updated the code to not panic in that case since it doesn't seem like something that is inherently wrong. You can just not add it again if it already exist |
Would something like this work? struct EmptyNode { name: &'static str }
impl Node for EmptyNode {
fn name(&self) -> &'static str {
self.name
}
} We could also maybe replace empty nodes with subgraphs? Idk, haven't looked at how subgraphs work. |
Yeah, that's exactly what I thought of after my original comment. I still think I'd prefer doing this in a separate PR just because big PRs tend to take a long time to merge. |
bed44df
to
00c857f
Compare
8e14d99
to
959e7f2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm very on board for this (love the improved legibility and ergonomics), but I have a few suggestions:
- I think this helper should be an "app builder" method. It is already effectively written as one (by taking a mutable app as the first param). It makes this more "standard" (we use this pattern for every other app operation), and it allows us to add it to other app ops.
- I think we should split out adding edges and nodes. They are technically orthogonal operations. I know we basically always call them back to back, but I think calling separate named methods helps make it clearer what is going on (rather than having a big list of variables as input that result in a variety of outputs). Combine that with (1) and this actually ends up taking up fewer lines anyway.
- Because (1) makes this an App extension method,
add_node
isn't descriptive enough in a global context. So these should beadd_render_graph_X
notadd_X
. (I probably would have pushed for this anyway for the original helper method).
I've pushed a commit that does all of this (as a proposal to make the conversation easier ... we can always revert or change things). I personally think it reads quite well. Check out the diff:
Also as a side note: not a fan of "helper" naming generally and I try to avoid it in Bevy. "helper" is a grab bag term and there is almost always a more descriptive and useful name.
I didn't even consider this, I was just trying to encapsulate this common pattern but that diff looks really good.
That's fair, I liked the idea of bundling everything so it's hard to forget to set a node edge, but I do agree that they are separate concepts. My approach also made it impossible to use add_node in the main_pass_2d/3d which is why I ended up adding the add_node_edges function to the render_graph.
Completely agree here, I just really didn't know how to name it/where to put this. In general, I'm on board with the app extension. My only concern here is that people might end up calling it on the App and not RenderApp. I'm not sure how to handle that case. To be fair, it was also an issue with the original approach. |
Since I'm fully on board with this, I updated the description to match this new api |
I feel that we should have a RenderAppExt trait implemented on App. Users won't be able to access the methods unless they import the trait, which should hopefully be less error prone. |
I reread your comment, the new feature is implemented like that. It's an extension trait implemented on App. It's just that if it's in scope people could still end up using it on the wrong app. |
I added a check in the function to warn the user if it doesn't find the RenderGraph and to remind them to run it on the RenderApp. |
Alternative: Make RenderApp a type wrapping the render App, and implement the methods there. Then, instead of get_sub_app(), add get_render_app() which wraps the app in RenderApp. Users could still call get_render_app() on the wrong app, if they had a custom subapp, but it's much more unlikely. |
I think for now, with the improved panic message and considering this api is most likely only used by more advanced user we'll be fine for a bit. But I'll add it to my list of potential improvements. |
a7681d2
to
9a36d58
Compare
Objective
Solution
Node
implFromWorld
, but every internal node is built using a new function taking a&mut World
so it was essentially alreadyFromWorld
Changelog
FromWorld
forBloomNode
,FxaaNode
andTaaNode
RenderGraph::add_node_edges()
RenderGraph::sub_graph()
RenderGraph::sub_graph_mut()
RenderGraphApp
,RenderGraphApp::add_render_graph_node
,RenderGraphApp::add_render_graph_edges
,RenderGraphApp::add_render_graph_edge
Notes
This was taken out of #7995 because it works on it's own. Once the linked PR is done, the newadd_node()
will be simplified a bit since the input/output params won't be necessary.This feature will be useful in most of the upcoming render nodes so it's impact will be more relevant at that point.
Partially fixes #7985
Future work