bevy_render/render_graph/
context.rs1use crate::{
2 render_graph::{NodeState, RenderGraph, SlotInfos, SlotLabel, SlotType, SlotValue},
3 render_resource::{Buffer, Sampler, TextureView},
4};
5use alloc::borrow::Cow;
6use bevy_ecs::{entity::Entity, intern::Interned};
7use thiserror::Error;
8
9use super::{InternedRenderSubGraph, RenderLabel, RenderSubGraph};
10
11pub struct RunSubGraph {
14 pub sub_graph: InternedRenderSubGraph,
15 pub inputs: Vec<SlotValue>,
16 pub view_entity: Option<Entity>,
17 pub debug_group: Option<String>,
18}
19
20pub struct RenderGraphContext<'a> {
29 graph: &'a RenderGraph,
30 node: &'a NodeState,
31 inputs: &'a [SlotValue],
32 outputs: &'a mut [Option<SlotValue>],
33 run_sub_graphs: Vec<RunSubGraph>,
34 view_entity: Option<Entity>,
39}
40
41impl<'a> RenderGraphContext<'a> {
42 pub fn new(
44 graph: &'a RenderGraph,
45 node: &'a NodeState,
46 inputs: &'a [SlotValue],
47 outputs: &'a mut [Option<SlotValue>],
48 ) -> Self {
49 Self {
50 graph,
51 node,
52 inputs,
53 outputs,
54 run_sub_graphs: Vec::new(),
55 view_entity: None,
56 }
57 }
58
59 #[inline]
61 pub fn inputs(&self) -> &[SlotValue] {
62 self.inputs
63 }
64
65 pub fn input_info(&self) -> &SlotInfos {
67 &self.node.input_slots
68 }
69
70 pub fn output_info(&self) -> &SlotInfos {
72 &self.node.output_slots
73 }
74
75 pub fn get_input(&self, label: impl Into<SlotLabel>) -> Result<&SlotValue, InputSlotError> {
77 let label = label.into();
78 let index = self
79 .input_info()
80 .get_slot_index(label.clone())
81 .ok_or(InputSlotError::InvalidSlot(label))?;
82 Ok(&self.inputs[index])
83 }
84
85 pub fn get_input_texture(
88 &self,
89 label: impl Into<SlotLabel>,
90 ) -> Result<&TextureView, InputSlotError> {
91 let label = label.into();
92 match self.get_input(label.clone())? {
93 SlotValue::TextureView(value) => Ok(value),
94 value => Err(InputSlotError::MismatchedSlotType {
95 label,
96 actual: value.slot_type(),
97 expected: SlotType::TextureView,
98 }),
99 }
100 }
101
102 pub fn get_input_sampler(
104 &self,
105 label: impl Into<SlotLabel>,
106 ) -> Result<&Sampler, InputSlotError> {
107 let label = label.into();
108 match self.get_input(label.clone())? {
109 SlotValue::Sampler(value) => Ok(value),
110 value => Err(InputSlotError::MismatchedSlotType {
111 label,
112 actual: value.slot_type(),
113 expected: SlotType::Sampler,
114 }),
115 }
116 }
117
118 pub fn get_input_buffer(&self, label: impl Into<SlotLabel>) -> Result<&Buffer, InputSlotError> {
120 let label = label.into();
121 match self.get_input(label.clone())? {
122 SlotValue::Buffer(value) => Ok(value),
123 value => Err(InputSlotError::MismatchedSlotType {
124 label,
125 actual: value.slot_type(),
126 expected: SlotType::Buffer,
127 }),
128 }
129 }
130
131 pub fn get_input_entity(&self, label: impl Into<SlotLabel>) -> Result<Entity, InputSlotError> {
133 let label = label.into();
134 match self.get_input(label.clone())? {
135 SlotValue::Entity(value) => Ok(*value),
136 value => Err(InputSlotError::MismatchedSlotType {
137 label,
138 actual: value.slot_type(),
139 expected: SlotType::Entity,
140 }),
141 }
142 }
143
144 pub fn set_output(
146 &mut self,
147 label: impl Into<SlotLabel>,
148 value: impl Into<SlotValue>,
149 ) -> Result<(), OutputSlotError> {
150 let label = label.into();
151 let value = value.into();
152 let slot_index = self
153 .output_info()
154 .get_slot_index(label.clone())
155 .ok_or_else(|| OutputSlotError::InvalidSlot(label.clone()))?;
156 let slot = self
157 .output_info()
158 .get_slot(slot_index)
159 .expect("slot is valid");
160 if value.slot_type() != slot.slot_type {
161 return Err(OutputSlotError::MismatchedSlotType {
162 label,
163 actual: slot.slot_type,
164 expected: value.slot_type(),
165 });
166 }
167 self.outputs[slot_index] = Some(value);
168 Ok(())
169 }
170
171 pub fn view_entity(&self) -> Entity {
172 self.view_entity.unwrap()
173 }
174
175 pub fn get_view_entity(&self) -> Option<Entity> {
176 self.view_entity
177 }
178
179 pub fn set_view_entity(&mut self, view_entity: Entity) {
180 self.view_entity = Some(view_entity);
181 }
182
183 pub fn run_sub_graph(
185 &mut self,
186 name: impl RenderSubGraph,
187 inputs: Vec<SlotValue>,
188 view_entity: Option<Entity>,
189 debug_group: Option<String>,
190 ) -> Result<(), RunSubGraphError> {
191 let name = name.intern();
192 let sub_graph = self
193 .graph
194 .get_sub_graph(name)
195 .ok_or(RunSubGraphError::MissingSubGraph(name))?;
196 if let Some(input_node) = sub_graph.get_input_node() {
197 for (i, input_slot) in input_node.input_slots.iter().enumerate() {
198 if let Some(input_value) = inputs.get(i) {
199 if input_slot.slot_type != input_value.slot_type() {
200 return Err(RunSubGraphError::MismatchedInputSlotType {
201 graph_name: name,
202 slot_index: i,
203 actual: input_value.slot_type(),
204 expected: input_slot.slot_type,
205 label: input_slot.name.clone().into(),
206 });
207 }
208 } else {
209 return Err(RunSubGraphError::MissingInput {
210 slot_index: i,
211 slot_name: input_slot.name.clone(),
212 graph_name: name,
213 });
214 }
215 }
216 } else if !inputs.is_empty() {
217 return Err(RunSubGraphError::SubGraphHasNoInputs(name));
218 }
219
220 self.run_sub_graphs.push(RunSubGraph {
221 sub_graph: name,
222 inputs,
223 view_entity,
224 debug_group,
225 });
226
227 Ok(())
228 }
229
230 pub fn label(&self) -> Interned<dyn RenderLabel> {
232 self.node.label
233 }
234
235 pub fn finish(self) -> Vec<RunSubGraph> {
238 self.run_sub_graphs
239 }
240}
241
242#[derive(Error, Debug, Eq, PartialEq)]
243pub enum RunSubGraphError {
244 #[error("attempted to run sub-graph `{0:?}`, but it does not exist")]
245 MissingSubGraph(InternedRenderSubGraph),
246 #[error("attempted to pass inputs to sub-graph `{0:?}`, which has no input slots")]
247 SubGraphHasNoInputs(InternedRenderSubGraph),
248 #[error("sub graph (name: `{graph_name:?}`) could not be run because slot `{slot_name}` at index {slot_index} has no value")]
249 MissingInput {
250 slot_index: usize,
251 slot_name: Cow<'static, str>,
252 graph_name: InternedRenderSubGraph,
253 },
254 #[error("attempted to use the wrong type for input slot")]
255 MismatchedInputSlotType {
256 graph_name: InternedRenderSubGraph,
257 slot_index: usize,
258 label: SlotLabel,
259 expected: SlotType,
260 actual: SlotType,
261 },
262}
263
264#[derive(Error, Debug, Eq, PartialEq)]
265pub enum OutputSlotError {
266 #[error("output slot `{0:?}` does not exist")]
267 InvalidSlot(SlotLabel),
268 #[error("attempted to output a value of type `{actual}` to output slot `{label:?}`, which has type `{expected}`")]
269 MismatchedSlotType {
270 label: SlotLabel,
271 expected: SlotType,
272 actual: SlotType,
273 },
274}
275
276#[derive(Error, Debug, Eq, PartialEq)]
277pub enum InputSlotError {
278 #[error("input slot `{0:?}` does not exist")]
279 InvalidSlot(SlotLabel),
280 #[error("attempted to retrieve a value of type `{actual}` from input slot `{label:?}`, which has type `{expected}`")]
281 MismatchedSlotType {
282 label: SlotLabel,
283 expected: SlotType,
284 actual: SlotType,
285 },
286}