Bevy 0.11 was released the other day, adding some changes related to the render graph I covered in my previous post so I thought I would just go through them. In particular there were some changes regarding render graphs that requires some updates to be compatible with Bevy 0.11.

Read more about the update on their blog.

Render graph construction

First off, the construction of the render graph has changed quite a lot. Previously it involved constructing the nodes, adding them, setting input slots, and setting all the edges. The new process is a lot simpler and more condensed.

Updating the render graph setup from the previous post will look something like this:

render_app
    .add_render_sub_graph(GRAPH_NAME)
    .add_render_graph_node::<ViewNodeRunner<RaycastNode>>(
        GRAPH_NAME,
        RAYCAST_NODE,
    )
    .add_render_graph_node::<ViewNodeRunner<LightNode>>(
        GRAPH_NAME,
        LIGHT_NODE,
    )
    .add_render_graph_node::<ViewNodeRunner<BlitNode>>(
        GRAPH_NAME,
        BLIT_NODE,
    )
    .add_render_graph_node::<ViewNodeRunner<TonemappingNode>>(
        GRAPH_NAME,
        TONEMAPPING,
    )
    .add_render_graph_node::<ViewNodeRunner<UpscalingNode>>(
        GRAPH_NAME, UPSCALING,
    )
    .add_render_graph_edges(
        GRAPH_NAME,
        &[
            RAYCAST_NODE,
            LIGHT_NODE,
            BLIT_NODE,
            TONEMAPPING,
            UPSCALING,
        ],
    );

Note the lack of a VIEW_ENTITY slot and the ViewNodeRunner nodes. This is covered in the next section.

ViewNode

ViewNode is a new trait for render graph nodes. As covered in the previous post there was a bit of a struggle to get access to the view entity when running. You had to define a input slot for the node, setup a query, update the query, etc. This can now be done automatically by Bevy using ViewNode and ViewNodeRunner as described here.

So for Bevy 0.11 we can update our RaycastNode:

[derive(Default)]
pub(crate) struct RaycastNode;

/// Implement methods required by the Node trait
impl ViewNode for RaycastNode {
    type ViewQuery = (
        &'static ExtractedCamera,
        &'static GBuffer,
        &'static ViewUniformOffset,
    );

    fn run(
        &self,
        _graph: &mut RenderGraphContext,
        render_context: &mut RenderContext,
        (camera, gbuffer, view_uniform_offset): QueryItem<Self::ViewQuery>,
        world: &World,
    ) -> Result<(), NodeRunError> {
        // Now we can proceed by setting bind groups and dispatching the compute kernel
        ...
    }
}

This significantly reduces the amount of boilerplate needed to setup a render node.

The last step needed to get this running is to use ViewNodeRunner when adding the node in a render graph:

render_app
    // To run a ViewNode you need to create a ViewNodeRunner
    .add_render_graph_node::<ViewNodeRunner<RaycastNode>>(
        GRAPH_NAME,
        RAYCAST_NODE,
    );

Plugin::finish

A new hook has been added to the Plugin trait called finish. This hook should be used for setting up resources requiring the RenderDevice, such as pipelines. For instance, say you have a render resource setup like this:

#[derive(Resource)]
struct RenderResource {
    ...
}

impl FromWorld for RenderResource {
    fn from_world(world: &mut World) -> Self {
        let render_device = world.resource::<RenderDevice>();
        ...
        Self { ... }
    }
}

Now you’ll have to init this resource in finish since the render device will not be available if you add init in build:

struct MyPlugin;

impl Plugin for MyPlugin {
    fn build(&self, app: &mut App) {
        ...
    }

    fn finish(&self, app: &mut App) {
        let render_app = match app.get_sub_app_mut(RenderApp) {
            Ok(render_app) => render_app,
            Err(_) => return,
        };
    
        render_app
            .init_resource::<RenderResource>();
    }
}