Path: blob/main/crates/bevy_render/src/render_graph/context.rs
6596 views
use crate::{1render_graph::{NodeState, RenderGraph, SlotInfos, SlotLabel, SlotType, SlotValue},2render_resource::{Buffer, Sampler, TextureView},3};4use alloc::borrow::Cow;5use bevy_ecs::{entity::Entity, intern::Interned};6use thiserror::Error;78use super::{InternedRenderSubGraph, RenderLabel, RenderSubGraph};910/// A command that signals the graph runner to run the sub graph corresponding to the `sub_graph`11/// with the specified `inputs` next.12pub struct RunSubGraph {13pub sub_graph: InternedRenderSubGraph,14pub inputs: Vec<SlotValue>,15pub view_entity: Option<Entity>,16}1718/// The context with all graph information required to run a [`Node`](super::Node).19/// This context is created for each node by the render graph runner.20///21/// The slot input can be read from here and the outputs must be written back to the context for22/// passing them onto the next node.23///24/// Sub graphs can be queued for running by adding a [`RunSubGraph`] command to the context.25/// After the node has finished running the graph runner is responsible for executing the sub graphs.26pub struct RenderGraphContext<'a> {27graph: &'a RenderGraph,28node: &'a NodeState,29inputs: &'a [SlotValue],30outputs: &'a mut [Option<SlotValue>],31run_sub_graphs: Vec<RunSubGraph>,32/// The `view_entity` associated with the render graph being executed33/// This is optional because you aren't required to have a `view_entity` for a node.34/// For example, compute shader nodes don't have one.35/// It should always be set when the [`RenderGraph`] is running on a View.36view_entity: Option<Entity>,37}3839impl<'a> RenderGraphContext<'a> {40/// Creates a new render graph context for the `node`.41pub fn new(42graph: &'a RenderGraph,43node: &'a NodeState,44inputs: &'a [SlotValue],45outputs: &'a mut [Option<SlotValue>],46) -> Self {47Self {48graph,49node,50inputs,51outputs,52run_sub_graphs: Vec::new(),53view_entity: None,54}55}5657/// Returns the input slot values for the node.58#[inline]59pub fn inputs(&self) -> &[SlotValue] {60self.inputs61}6263/// Returns the [`SlotInfos`] of the inputs.64pub fn input_info(&self) -> &SlotInfos {65&self.node.input_slots66}6768/// Returns the [`SlotInfos`] of the outputs.69pub fn output_info(&self) -> &SlotInfos {70&self.node.output_slots71}7273/// Retrieves the input slot value referenced by the `label`.74pub fn get_input(&self, label: impl Into<SlotLabel>) -> Result<&SlotValue, InputSlotError> {75let label = label.into();76let index = self77.input_info()78.get_slot_index(label.clone())79.ok_or(InputSlotError::InvalidSlot(label))?;80Ok(&self.inputs[index])81}8283// TODO: should this return an Arc or a reference?84/// Retrieves the input slot value referenced by the `label` as a [`TextureView`].85pub fn get_input_texture(86&self,87label: impl Into<SlotLabel>,88) -> Result<&TextureView, InputSlotError> {89let label = label.into();90match self.get_input(label.clone())? {91SlotValue::TextureView(value) => Ok(value),92value => Err(InputSlotError::MismatchedSlotType {93label,94actual: value.slot_type(),95expected: SlotType::TextureView,96}),97}98}99100/// Retrieves the input slot value referenced by the `label` as a [`Sampler`].101pub fn get_input_sampler(102&self,103label: impl Into<SlotLabel>,104) -> Result<&Sampler, InputSlotError> {105let label = label.into();106match self.get_input(label.clone())? {107SlotValue::Sampler(value) => Ok(value),108value => Err(InputSlotError::MismatchedSlotType {109label,110actual: value.slot_type(),111expected: SlotType::Sampler,112}),113}114}115116/// Retrieves the input slot value referenced by the `label` as a [`Buffer`].117pub fn get_input_buffer(&self, label: impl Into<SlotLabel>) -> Result<&Buffer, InputSlotError> {118let label = label.into();119match self.get_input(label.clone())? {120SlotValue::Buffer(value) => Ok(value),121value => Err(InputSlotError::MismatchedSlotType {122label,123actual: value.slot_type(),124expected: SlotType::Buffer,125}),126}127}128129/// Retrieves the input slot value referenced by the `label` as an [`Entity`].130pub fn get_input_entity(&self, label: impl Into<SlotLabel>) -> Result<Entity, InputSlotError> {131let label = label.into();132match self.get_input(label.clone())? {133SlotValue::Entity(value) => Ok(*value),134value => Err(InputSlotError::MismatchedSlotType {135label,136actual: value.slot_type(),137expected: SlotType::Entity,138}),139}140}141142/// Sets the output slot value referenced by the `label`.143pub fn set_output(144&mut self,145label: impl Into<SlotLabel>,146value: impl Into<SlotValue>,147) -> Result<(), OutputSlotError> {148let label = label.into();149let value = value.into();150let slot_index = self151.output_info()152.get_slot_index(label.clone())153.ok_or_else(|| OutputSlotError::InvalidSlot(label.clone()))?;154let slot = self155.output_info()156.get_slot(slot_index)157.expect("slot is valid");158if value.slot_type() != slot.slot_type {159return Err(OutputSlotError::MismatchedSlotType {160label,161actual: slot.slot_type,162expected: value.slot_type(),163});164}165self.outputs[slot_index] = Some(value);166Ok(())167}168169pub fn view_entity(&self) -> Entity {170self.view_entity.unwrap()171}172173pub fn get_view_entity(&self) -> Option<Entity> {174self.view_entity175}176177pub fn set_view_entity(&mut self, view_entity: Entity) {178self.view_entity = Some(view_entity);179}180181/// Queues up a sub graph for execution after the node has finished running.182pub fn run_sub_graph(183&mut self,184name: impl RenderSubGraph,185inputs: Vec<SlotValue>,186view_entity: Option<Entity>,187) -> Result<(), RunSubGraphError> {188let name = name.intern();189let sub_graph = self190.graph191.get_sub_graph(name)192.ok_or(RunSubGraphError::MissingSubGraph(name))?;193if let Some(input_node) = sub_graph.get_input_node() {194for (i, input_slot) in input_node.input_slots.iter().enumerate() {195if let Some(input_value) = inputs.get(i) {196if input_slot.slot_type != input_value.slot_type() {197return Err(RunSubGraphError::MismatchedInputSlotType {198graph_name: name,199slot_index: i,200actual: input_value.slot_type(),201expected: input_slot.slot_type,202label: input_slot.name.clone().into(),203});204}205} else {206return Err(RunSubGraphError::MissingInput {207slot_index: i,208slot_name: input_slot.name.clone(),209graph_name: name,210});211}212}213} else if !inputs.is_empty() {214return Err(RunSubGraphError::SubGraphHasNoInputs(name));215}216217self.run_sub_graphs.push(RunSubGraph {218sub_graph: name,219inputs,220view_entity,221});222223Ok(())224}225226/// Returns a human-readable label for this node, for debugging purposes.227pub fn label(&self) -> Interned<dyn RenderLabel> {228self.node.label229}230231/// Finishes the context for this [`Node`](super::Node) by232/// returning the sub graphs to run next.233pub fn finish(self) -> Vec<RunSubGraph> {234self.run_sub_graphs235}236}237238#[derive(Error, Debug, Eq, PartialEq)]239pub enum RunSubGraphError {240#[error("attempted to run sub-graph `{0:?}`, but it does not exist")]241MissingSubGraph(InternedRenderSubGraph),242#[error("attempted to pass inputs to sub-graph `{0:?}`, which has no input slots")]243SubGraphHasNoInputs(InternedRenderSubGraph),244#[error("sub graph (name: `{graph_name:?}`) could not be run because slot `{slot_name}` at index {slot_index} has no value")]245MissingInput {246slot_index: usize,247slot_name: Cow<'static, str>,248graph_name: InternedRenderSubGraph,249},250#[error("attempted to use the wrong type for input slot")]251MismatchedInputSlotType {252graph_name: InternedRenderSubGraph,253slot_index: usize,254label: SlotLabel,255expected: SlotType,256actual: SlotType,257},258}259260#[derive(Error, Debug, Eq, PartialEq)]261pub enum OutputSlotError {262#[error("output slot `{0:?}` does not exist")]263InvalidSlot(SlotLabel),264#[error("attempted to output a value of type `{actual}` to output slot `{label:?}`, which has type `{expected}`")]265MismatchedSlotType {266label: SlotLabel,267expected: SlotType,268actual: SlotType,269},270}271272#[derive(Error, Debug, Eq, PartialEq)]273pub enum InputSlotError {274#[error("input slot `{0:?}` does not exist")]275InvalidSlot(SlotLabel),276#[error("attempted to retrieve a value of type `{actual}` from input slot `{label:?}`, which has type `{expected}`")]277MismatchedSlotType {278label: SlotLabel,279expected: SlotType,280actual: SlotType,281},282}283284285