Bevy 渲染图结构介绍
RenderGraph 是bevy用来处理渲染工作负载的任务调度器,是由Nodes和Edges(节点和边)连接成一个有向无环计算图。 每个节点代表一个执行渲染工作的独立单元(一个任务),边表示节点间的依赖关系(执行顺序)。
Node
- 节点由
RenderGraph调用,具体是由RenderGraphRunner调用run_graph运行节点,同时也会运行节点的SubGraph。查看源码 RenderGraph会由render_system调用。查看源码
节点的trait定义:
Rust
pub trait Node: Downcast + Send + Sync + 'static {
/// Specifies the required input slots for this node.
/// They will then be available during the run method inside the [`RenderGraphContext`].
fn input(&self) -> Vec<SlotInfo> {
Vec::new()
}
/// Specifies the produced output slots for this node.
/// They can then be passed one inside [`RenderGraphContext`] during the run method.
fn output(&self) -> Vec<SlotInfo> {
Vec::new()
}
/// Updates internal node state using the current render [`World`] prior to the run method.
fn update(&mut self, _world: &mut World) {}
/// 运行图节点逻辑,发出draw调用,更新输出槽
/// 并可选地将子图排队执行。图形数据、输入和输出值通过RenderGraphContext传递。
fn run<'w>(
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext<'w>,
world: &'w World,
) -> Result<(), NodeRunError>;
}Node::run 提供了节点不可变引用,RenderGraphContext和RenderContext可变引用,以及World不可变引用。
RenderGraphContext主要用于运行SubGraph(run_sub_graph())。其具有的插槽功能,不必理会,源代码中也只有test用例。- 参考
CameraDriverNode,查看源码
- 参考
RenderContext能够获取用于渲染的GPU实例,如CommandEncoder,RenderDevice,TrackedRenderPass等实现调用GPU进行绘制图形。Rustrender_context.add_command_buffer_generation_task(move |render_device|{ // Command encoder setup let mut command_encoder = render_device.create_command_encoder(&CommandEncoderDescriptor { label: Some("main_opaque_pass_2d_command_encoder"), }); // Render pass setup let render_pass = command_encoder.begin_render_pass(&RenderPassDescriptor { label: Some("main_opaque_pass_2d"), color_attachments: &color_attachments, depth_stencil_attachment, timestamp_writes: None, occlusion_query_set: None, }); let mut render_pass = TrackedRenderPass::new(&render_device, render_pass); // ...该位置会由RenderPhase 调用render函数,这里两种节点处理方式不一样 pass_span.end(&mut render_pass); drop(render_pass); command_encoder.finish() });World能够获取所有注册在RenderWorld中的Resource。作为渲染所需要的资源。比如PipelineCache等资源,- 示例:
Rustlet pipeline_cache = world.resource::<PipelineCache>(); // Or let Some(pipeline_cache) = world.get_resource::<PipelineCache>() else { return Ok(()); }
节点也能够查询Component(经过Extract阶段提取到渲染世界中的实体)
- 示例:依旧参考
CameraDriverNodeRust此时需要特化pub struct CameraDriverNode { cameras: QueryState<&'static ExtractedCamera>, }Node的update方法。Rustimpl Node for CameraDriverNode { fn update(&mut self, world: &mut World) { self.cameras.update_archetypes(world); } }
ViewNode
实现该trait的节点由ViewNodeRunner执行,ViewNodeRunner是一个泛型结构体,因此它可以被不同的实现特化。 ViewNodeRunner实现了Nodetrait。查看源码
Rust
impl<T> Node for ViewNodeRunner<T>
where
T: ViewNode + Send + Sync + 'static,
{
fn update(&mut self, world: &mut World) {
self.view_query.update_archetypes(world);
self.node.update(world);
}
fn run<'w>(
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext<'w>,
world: &'w World,
) -> Result<(), NodeRunError> {
let Ok(view) = self.view_query.get_manual(world, graph.view_entity()) else {
return Ok(());
};
ViewNode::run(&self.node, graph, render_context, view, world)?;
Ok(())
}
}减少了Node获取Component和更新QueryState的模板代码,同时又能实现运行属于不同的Camera的ViewEntities。 在CameraDriverNode中中ViewNode节点只负责渲染当前Camera中内容。 多个Camera由SortCamera(已经排好序)在CameraDriverNode的run函数中一次执行,运行每一个Camera下的SubGraph。


