alpha2_render/
graph_plan.rs1use crate::RenderQualityControls;
2use crate::graph::{GraphError, RenderCacheKey, RenderGraph, RenderNode, RenderNodeId};
3
4#[derive(Clone, Debug)]
6pub struct RenderPlan {
7 pub(crate) graph: u32,
8 pub(crate) order: Vec<RenderNodeId>,
9 pub(crate) last_use: Vec<Option<usize>>,
10 pub(crate) output: RenderNodeId,
11 pub(crate) cache_key: RenderCacheKey,
12 pub(crate) filter_sample_limits: Vec<Option<u32>>,
13 pub(crate) temporal_offsets: Vec<f32>,
14}
15
16impl RenderPlan {
17 #[must_use]
19 pub fn execution_order(&self) -> &[RenderNodeId] {
20 &self.order
21 }
22
23 #[must_use]
25 pub fn last_use(&self, node: RenderNodeId) -> Option<usize> {
26 (node.graph == self.graph)
27 .then(|| self.last_use.get(node.index).copied().flatten())
28 .flatten()
29 }
30
31 #[must_use]
33 pub const fn cache_key(&self) -> RenderCacheKey {
34 self.cache_key
35 }
36
37 #[must_use]
39 pub fn filter_sample_limit(&self, node: RenderNodeId) -> Option<u32> {
40 (node.graph == self.graph)
41 .then(|| self.filter_sample_limits.get(node.index).copied().flatten())
42 .flatten()
43 }
44
45 #[must_use]
47 pub fn temporal_offsets(&self) -> &[f32] {
48 &self.temporal_offsets
49 }
50}
51
52impl RenderGraph<'_> {
53 pub fn compile(&self) -> Result<RenderPlan, GraphError> {
58 self.compile_with_quality(RenderQualityControls::default())
59 }
60
61 pub fn compile_with_quality(
66 &self,
67 quality: RenderQualityControls,
68 ) -> Result<RenderPlan, GraphError> {
69 let output = self.output.ok_or(GraphError::MissingOutput)?;
70 let mut reachable = vec![false; self.nodes.len()];
71 mark_reachable(output, &self.nodes, &mut reachable);
72 let order = reachable
73 .iter()
74 .enumerate()
75 .filter(|(_, is_reachable)| **is_reachable)
76 .map(|(index, _)| RenderNodeId {
77 graph: self.id,
78 index,
79 })
80 .collect::<Vec<_>>();
81 let mut last_use = vec![None; self.nodes.len()];
82 for (position, node) in order.iter().enumerate() {
83 for dependency in self.nodes[node.index].dependencies() {
84 last_use[dependency.index] = Some(position);
85 }
86 }
87 last_use[output.index] = Some(order.len());
88 let filter_sample_limits = self
89 .nodes
90 .iter()
91 .map(|node| match node {
92 RenderNode::Effect { .. }
93 | RenderNode::Transition { .. }
94 | RenderNode::Generator { .. } => Some(quality.max_filter_samples.max(1)),
95 RenderNode::Source(_)
96 | RenderNode::Composite { .. }
97 | RenderNode::Vector { .. } => None,
98 })
99 .collect();
100 let count = quality.motion_blur_subframes.max(1);
101 #[allow(clippy::cast_precision_loss)]
102 let temporal_offsets = (0..count)
103 .map(|index| (index as f32 + 0.5) / count as f32 - 0.5)
104 .collect();
105 Ok(RenderPlan {
106 graph: self.id,
107 order,
108 last_use,
109 output,
110 cache_key: self.cache_keys[output.index],
111 filter_sample_limits,
112 temporal_offsets,
113 })
114 }
115}
116
117fn mark_reachable(node: RenderNodeId, nodes: &[RenderNode<'_>], reachable: &mut [bool]) {
118 let mut pending = vec![node];
119 while let Some(node) = pending.pop() {
120 if reachable[node.index] {
121 continue;
122 }
123 reachable[node.index] = true;
124 pending.extend(nodes[node.index].dependencies());
125 }
126}