1use std::collections::BTreeMap;
4use std::pin::Pin;
5use std::sync::Arc;
6
7use sva_ast::Graph;
8use sva_formula::NodeId;
9use sva_samples::Buffer;
10
11use super::value_graph;
12use super::world::{Reach, Reached, Walking, World};
13use super::{Render, RenderConfig, closed, driving, dropped, drove, ended, planned_over};
14use crate::cache::{Backend, Recording, Stored, Tier};
15use crate::error::EngineError;
16use crate::schedule;
17
18pub trait Abandon {
20 fn abandoned(&self) -> Pin<Box<dyn Future<Output = bool> + '_>>;
21}
22
23pub struct Never;
24
25pub trait Hear {
27 fn heard(&self, block: &Buffer) -> bool;
29}
30
31#[derive(Default)]
33pub struct Session {
34 own: Option<World>,
35 pub(super) stand_in: Option<World>,
37}
38
39impl Abandon for Never {
40 fn abandoned(&self) -> Pin<Box<dyn Future<Output = bool> + '_>> {
41 Box::pin(std::future::ready(false))
42 }
43}
44
45pub async fn render_over<B: Backend>(
47 graph: &Graph,
48 target: &str,
49 config: RenderConfig,
50 tier: &Tier<B>,
51) -> Result<Render, EngineError> {
52 render_in(
53 &mut Session::default(),
54 graph,
55 target,
56 config,
57 tier,
58 (&Never, None),
59 )
60 .await
61}
62
63pub async fn render_in<B: Backend>(
68 session: &mut Session,
69 graph: &Graph,
70 target: &str,
71 config: RenderConfig,
72 tier: &Tier<B>,
73 (abandon, hear): (&dyn Abandon, Option<&dyn Hear>),
74) -> Result<Render, EngineError> {
75 let memory = tier.memory();
76 let mut recording = Recording::over(memory);
77 let Session { own, stand_in } = session;
78 let (world, root) = World::rendered(own, graph, target, config.rate)?;
79 let (instances, typed) = (&world.instances, &world.typing);
80 let id = typed
81 .id(&root)
82 .ok_or_else(|| EngineError::UnknownNode(root.clone()))?;
83 let profile = config.profile;
84 let (config, decided) = ended(typed, id, config, world.bounds(id, &profile));
85 world.bound(id, &profile, &decided);
86 let lowered = typed.lowered().to_vec();
87 let round = tier.begin();
88 let found = walked(
89 world,
90 (&root, &config, false),
91 (tier, round, &mut recording),
92 )
93 .await;
94 let tys = typed.clone();
95 if dropped(&config) && found.held.contains_key(&root) {
96 let schedule = schedule::plan(&tys, id, &config.asks);
97 let mut held = Render::shell((tys, id), (config, schedule), memory.clone());
98 let mut stats = recording.stats(memory);
99 stats.typed = lowered;
100 held.cache_stats = Some(stats);
101 return Ok(held);
102 }
103 let hits: BTreeMap<NodeId, Arc<Stored>> = found
104 .held
105 .iter()
106 .filter_map(|(path, stored)| Some((tys.id(path)?, Arc::clone(stored))))
107 .collect();
108 let mut held = planned_over(
109 (graph, target, instances),
110 (tys, id),
111 (config, &mut *stand_in, memory.clone()),
112 (decided, &hits),
113 )?;
114 if let Some(value_graph) = &held.value_graph {
115 world.bound(id, &profile, &(value_graph.supports.clone(), None));
116 }
117 let mut retyped = lowered;
118 retyped.append(&mut held.stand_in_typed);
119 if let (Some(value_graph), Some(range)) = (&mut held.value_graph, held.range) {
120 loop {
121 let mut needs = value_graph.needs(range);
122 while !needs.is_empty() {
123 let fetched = tier.fetch(&needs).await;
124 for (key, parts) in fetched.handed {
125 value_graph.took(key, &parts);
126 }
127 needs = fetched.left;
128 }
129 let short = value_graph.short((value_graph.root, range, value_graph::Past::Held));
130 if short.is_empty() {
131 break;
132 }
133 for at in short {
134 let under = held
135 .tys
136 .name(value_graph.answered(&held.tys, at))
137 .to_string();
138 let walking = (under.as_str(), &held.config, true);
139 let found = walked(world, walking, (tier, round, &mut recording)).await;
140 let hits = found.held.iter();
141 let hits = hits
142 .filter_map(|(path, stored)| Some((held.tys.id(path)?, Arc::clone(stored))));
143 value_graph.read_on(&held.tys, at, &hits.collect())?;
144 }
145 value_graph.settled(value_graph.root, &[], (range.start, false));
146 value_graph.refuse_endless(range)?;
147 }
148 value_graph.offers(&held.tys, range);
149 }
150 let walked = recording.stats(memory);
151 match driving(&mut held, (memory, recording))? {
152 Some(mut driver) => {
153 loop {
154 if abandon.abandoned().await {
155 return Err(EngineError::Abandoned);
156 }
157 let from = driver.at;
158 if !driver.pull()? {
159 break;
160 }
161 if hear.is_some_and(|hear| !hear.heard(&driver.played(from))) {
162 return Err(EngineError::Abandoned);
163 }
164 let needs = driver.value_graph.needs(driver.next());
165 for (key, parts) in tier.fetch(&needs).await.handed {
166 driver.value_graph.took(key, &parts);
167 }
168 }
169 drove(&mut held, driver);
170 }
171 None => held.cache_stats = Some(walked),
172 }
173 let computed = held.value_graph.as_ref().map_or(Vec::new(), |value_graph| {
174 let computed = value_graph.values.iter();
175 let computed =
176 computed.filter(|(_, v)| !matches!(v.kind, value_graph::Kind::Resident { .. }));
177 computed.map(|(_, v)| v.name.clone()).collect()
178 });
179 if let Some(stats) = &mut held.cache_stats {
180 stats.typed = retyped;
181 stats.planned = computed;
182 stats.unslotted = held.unslotted.clone();
183 }
184 closed(&mut held)?;
185 Ok(held)
186}
187
188async fn walked<B: Backend>(
191 world: &World,
192 (root, config, under): (&str, &RenderConfig, bool),
193 (tier, round, seen): (&Tier<B>, u64, &mut Recording),
194) -> Reached {
195 let walking = Walking {
196 root,
197 config,
198 whole: true,
199 under,
200 };
201 loop {
202 let memory = tier.memory();
203 match world.walk(&walking, &mut |node, key| {
204 memory.answered((key, round), (node, seen))
205 }) {
206 Reach::Reached(reached) => return reached,
207 Reach::Asks(keys) => {
208 for key in keys {
209 tier.lookup(key, round).await;
210 }
211 }
212 }
213 }
214}