Path: blob/main/crates/bevy_render/src/renderer/graph_runner.rs
6596 views
use bevy_ecs::{prelude::Entity, world::World};1use bevy_platform::collections::HashMap;2#[cfg(feature = "trace")]3use tracing::info_span;45use alloc::{borrow::Cow, collections::VecDeque};6use smallvec::{smallvec, SmallVec};7use thiserror::Error;89use crate::{10diagnostic::internal::{DiagnosticsRecorder, RenderDiagnosticsMutex},11render_graph::{12Edge, InternedRenderLabel, InternedRenderSubGraph, NodeRunError, NodeState, RenderGraph,13RenderGraphContext, SlotLabel, SlotType, SlotValue,14},15renderer::{RenderContext, RenderDevice},16};1718/// The [`RenderGraphRunner`] is responsible for executing a [`RenderGraph`].19///20/// It will run all nodes in the graph sequentially in the correct order (defined by the edges).21/// Each [`Node`](crate::render_graph::Node) can run any arbitrary code, but will generally22/// either send directly a [`CommandBuffer`] or a task that will asynchronously generate a [`CommandBuffer`]23///24/// After running the graph, the [`RenderGraphRunner`] will execute in parallel all the tasks to get25/// an ordered list of [`CommandBuffer`]s to execute. These [`CommandBuffer`] will be submitted to the GPU26/// sequentially in the order that the tasks were submitted. (which is the order of the [`RenderGraph`])27///28/// [`CommandBuffer`]: wgpu::CommandBuffer29pub(crate) struct RenderGraphRunner;3031#[derive(Error, Debug)]32pub enum RenderGraphRunnerError {33#[error(transparent)]34NodeRunError(#[from] NodeRunError),35#[error("node output slot not set (index {slot_index}, name {slot_name})")]36EmptyNodeOutputSlot {37type_name: &'static str,38slot_index: usize,39slot_name: Cow<'static, str>,40},41#[error("graph '{sub_graph:?}' could not be run because slot '{slot_name}' at index {slot_index} has no value")]42MissingInput {43slot_index: usize,44slot_name: Cow<'static, str>,45sub_graph: Option<InternedRenderSubGraph>,46},47#[error("attempted to use the wrong type for input slot")]48MismatchedInputSlotType {49slot_index: usize,50label: SlotLabel,51expected: SlotType,52actual: SlotType,53},54#[error(55"node (name: '{node_name:?}') has {slot_count} input slots, but was provided {value_count} values"56)]57MismatchedInputCount {58node_name: InternedRenderLabel,59slot_count: usize,60value_count: usize,61},62}6364impl RenderGraphRunner {65pub fn run(66graph: &RenderGraph,67render_device: RenderDevice,68mut diagnostics_recorder: Option<DiagnosticsRecorder>,69queue: &wgpu::Queue,70world: &World,71finalizer: impl FnOnce(&mut wgpu::CommandEncoder),72) -> Result<Option<DiagnosticsRecorder>, RenderGraphRunnerError> {73if let Some(recorder) = &mut diagnostics_recorder {74recorder.begin_frame();75}7677let mut render_context = RenderContext::new(render_device, diagnostics_recorder);78Self::run_graph(graph, None, &mut render_context, world, &[], None)?;79finalizer(render_context.command_encoder());8081let (render_device, mut diagnostics_recorder) = {82let (commands, render_device, diagnostics_recorder) = render_context.finish();8384#[cfg(feature = "trace")]85let _span = info_span!("submit_graph_commands").entered();86queue.submit(commands);8788(render_device, diagnostics_recorder)89};9091if let Some(recorder) = &mut diagnostics_recorder {92let render_diagnostics_mutex = world.resource::<RenderDiagnosticsMutex>().0.clone();93recorder.finish_frame(&render_device, move |diagnostics| {94*render_diagnostics_mutex.lock().expect("lock poisoned") = Some(diagnostics);95});96}9798Ok(diagnostics_recorder)99}100101/// Runs the [`RenderGraph`] and all its sub-graphs sequentially, making sure that all nodes are102/// run in the correct order. (a node only runs when all its dependencies have finished running)103fn run_graph<'w>(104graph: &RenderGraph,105sub_graph: Option<InternedRenderSubGraph>,106render_context: &mut RenderContext<'w>,107world: &'w World,108inputs: &[SlotValue],109view_entity: Option<Entity>,110) -> Result<(), RenderGraphRunnerError> {111let mut node_outputs: HashMap<InternedRenderLabel, SmallVec<[SlotValue; 4]>> =112HashMap::default();113#[cfg(feature = "trace")]114let span = if let Some(label) = &sub_graph {115info_span!("run_graph", name = format!("{label:?}"))116} else {117info_span!("run_graph", name = "main_graph")118};119#[cfg(feature = "trace")]120let _guard = span.enter();121122// Queue up nodes without inputs, which can be run immediately123let mut node_queue: VecDeque<&NodeState> = graph124.iter_nodes()125.filter(|node| node.input_slots.is_empty())126.collect();127128// pass inputs into the graph129if let Some(input_node) = graph.get_input_node() {130let mut input_values: SmallVec<[SlotValue; 4]> = SmallVec::new();131for (i, input_slot) in input_node.input_slots.iter().enumerate() {132if let Some(input_value) = inputs.get(i) {133if input_slot.slot_type != input_value.slot_type() {134return Err(RenderGraphRunnerError::MismatchedInputSlotType {135slot_index: i,136actual: input_value.slot_type(),137expected: input_slot.slot_type,138label: input_slot.name.clone().into(),139});140}141input_values.push(input_value.clone());142} else {143return Err(RenderGraphRunnerError::MissingInput {144slot_index: i,145slot_name: input_slot.name.clone(),146sub_graph,147});148}149}150151node_outputs.insert(input_node.label, input_values);152153for (_, node_state) in graph154.iter_node_outputs(input_node.label)155.expect("node exists")156{157node_queue.push_front(node_state);158}159}160161'handle_node: while let Some(node_state) = node_queue.pop_back() {162// skip nodes that are already processed163if node_outputs.contains_key(&node_state.label) {164continue;165}166167let mut slot_indices_and_inputs: SmallVec<[(usize, SlotValue); 4]> = SmallVec::new();168// check if all dependencies have finished running169for (edge, input_node) in graph170.iter_node_inputs(node_state.label)171.expect("node is in graph")172{173match edge {174Edge::SlotEdge {175output_index,176input_index,177..178} => {179if let Some(outputs) = node_outputs.get(&input_node.label) {180slot_indices_and_inputs181.push((*input_index, outputs[*output_index].clone()));182} else {183node_queue.push_front(node_state);184continue 'handle_node;185}186}187Edge::NodeEdge { .. } => {188if !node_outputs.contains_key(&input_node.label) {189node_queue.push_front(node_state);190continue 'handle_node;191}192}193}194}195196// construct final sorted input list197slot_indices_and_inputs.sort_by_key(|(index, _)| *index);198let inputs: SmallVec<[SlotValue; 4]> = slot_indices_and_inputs199.into_iter()200.map(|(_, value)| value)201.collect();202203if inputs.len() != node_state.input_slots.len() {204return Err(RenderGraphRunnerError::MismatchedInputCount {205node_name: node_state.label,206slot_count: node_state.input_slots.len(),207value_count: inputs.len(),208});209}210211let mut outputs: SmallVec<[Option<SlotValue>; 4]> =212smallvec![None; node_state.output_slots.len()];213{214let mut context = RenderGraphContext::new(graph, node_state, &inputs, &mut outputs);215if let Some(view_entity) = view_entity {216context.set_view_entity(view_entity);217}218219{220#[cfg(feature = "trace")]221let _span = info_span!("node", name = node_state.type_name).entered();222223node_state.node.run(&mut context, render_context, world)?;224}225226for run_sub_graph in context.finish() {227let sub_graph = graph228.get_sub_graph(run_sub_graph.sub_graph)229.expect("sub graph exists because it was validated when queued.");230Self::run_graph(231sub_graph,232Some(run_sub_graph.sub_graph),233render_context,234world,235&run_sub_graph.inputs,236run_sub_graph.view_entity,237)?;238}239}240241let mut values: SmallVec<[SlotValue; 4]> = SmallVec::new();242for (i, output) in outputs.into_iter().enumerate() {243if let Some(value) = output {244values.push(value);245} else {246let empty_slot = node_state.output_slots.get_slot(i).unwrap();247return Err(RenderGraphRunnerError::EmptyNodeOutputSlot {248type_name: node_state.type_name,249slot_index: i,250slot_name: empty_slot.name.clone(),251});252}253}254node_outputs.insert(node_state.label, values);255256for (_, node_state) in graph257.iter_node_outputs(node_state.label)258.expect("node exists")259{260node_queue.push_front(node_state);261}262}263264Ok(())265}266}267268269