1use std::collections::{BTreeMap, BTreeSet};
4
5use sva_ast::Graph;
6use sva_formula::{Hash, NodeId};
7
8use super::offer::{Offers, readable};
9use super::table::{self, Table};
10use super::{Render, RenderConfig, closed, driving, drove, frontier, planned_over};
11use crate::cache::{Backend, CacheStats, Recording, Tier};
12use crate::error::EngineError;
13use crate::instantiate;
14use crate::schedule;
15use crate::typing;
16
17pub async fn render_over<B: Backend>(
22 graph: &Graph,
23 target: &str,
24 config: RenderConfig,
25 tier: &Tier<B>,
26) -> Result<Render, EngineError> {
27 match run(graph, target, config, tier, Keep::Wanted).await? {
28 Reached::Render(held) => Ok(*held),
29 Reached::Held(_) => unreachable!("a render reads a held root's samples"),
30 }
31}
32
33pub async fn warm<B: Backend>(
35 graph: &Graph,
36 target: &str,
37 config: RenderConfig,
38 tier: &Tier<B>,
39) -> Result<CacheStats, EngineError> {
40 Ok(
41 match run(graph, target, config, tier, Keep::Nothing).await? {
42 Reached::Render(mut held) => held.cache_stats.take().expect("a render reports"),
43 Reached::Held(stats) => stats,
44 },
45 )
46}
47
48#[derive(Clone, Copy, PartialEq, Eq)]
49enum Keep {
50 Wanted,
51 Nothing,
52}
53
54enum Reached {
55 Render(Box<Render>),
56 Held(CacheStats),
57}
58
59async fn run<B: Backend>(
60 graph: &Graph,
61 target: &str,
62 config: RenderConfig,
63 tier: &Tier<B>,
64 keep: Keep,
65) -> Result<Reached, EngineError> {
66 let round = tier.begin();
67 let mut recording = Recording::over(tier.memory());
68 let instances = instantiate::instantiate(graph, target, config.rate)?;
69 let root = instances.instance_of(target)?;
70 let order = schedule::schedule_from(&instances, std::slice::from_ref(&root))?;
71 let keys = keys(graph, &instances, &order, &config);
72 let mut found = frontier::Frontier::from((&instances, &order), &keys, &root, &config);
73 found.walked(tier, round).await;
74 if keep == Keep::Nothing && found.stored.contains_key(&root) {
75 recording.found(found.lookups);
76 return Ok(Reached::Held(recording.stats()));
77 }
78 let (mut held, typed) = loop {
79 found.walked(tier, round).await;
80 let tys = typing::infer_over(&instances, &order.within(&found.visited), &found.stored)?;
81 let id = tys
82 .id(&root)
83 .ok_or_else(|| EngineError::UnknownNode(root.clone()))?;
84 let bounds: BTreeSet<NodeId> = keys
85 .keys()
86 .filter_map(|path| tys.id(path))
87 .filter(|id| readable(&tys, *id))
88 .collect();
89 let typed = tys.lowered().to_vec();
90 let mut held = planned_over(&instances, (tys, id), config.clone(), &bounds)?;
91 let short = match (&mut held.table, held.range) {
92 (Some(table), Some(range)) => {
93 let mut needs = table.needs(range);
94 while !needs.is_empty() {
95 let fetched = tier.fetch(&needs).await;
96 for (key, parts) in fetched.handed {
97 table.took(key, &parts);
98 }
99 needs = fetched.left;
100 }
101 short(table, range)
102 }
103 _ => Vec::new(),
104 };
105 if short.is_empty() {
106 break (held, typed);
107 }
108 for path in short {
109 found.reopen(&path);
110 }
111 };
112 let mut offers = Offers::of(&mut held, (&keys, &found), tier.memory());
113 recording.found(std::mem::take(&mut found.lookups));
114 let walked = recording.stats();
115 match driving(&mut held, recording)? {
116 Some(mut driver) => {
117 while driver.pull()? {
118 offers.whole(&driver.table, tier.memory());
119 let needs = driver.table.needs(driver.next());
120 for (key, parts) in tier.fetch(&needs).await.handed {
121 driver.table.took(key, &parts);
122 }
123 }
124 offers.rest(&driver.table, tier.memory());
125 drove(&mut held, driver, keep == Keep::Wanted);
126 }
127 None => held.cache_stats = Some(walked),
128 }
129 let computed = held.table.as_ref().map_or(Vec::new(), |table| {
130 let computed = table.values.iter();
131 let computed = computed.filter(|(_, v)| !matches!(v.kind, table::Kind::Resident { .. }));
132 computed.map(|(_, v)| v.name.clone()).collect()
133 });
134 if let Some(stats) = &mut held.cache_stats {
135 stats.typed = typed;
136 stats.planned = computed;
137 }
138 closed(&mut held)?;
139 Ok(Reached::Render(Box::new(held)))
140}
141
142pub(crate) fn keys(
144 graph: &Graph,
145 instances: &instantiate::Instances,
146 order: &schedule::Order,
147 config: &RenderConfig,
148) -> BTreeMap<String, Hash> {
149 crate::source::identities(graph, instances, order)
150 .into_iter()
151 .map(|(path, identity)| {
152 let key = crate::cache::node_key(identity, config.rate, &config.profile);
153 (path, key)
154 })
155 .collect()
156}
157
158fn short(table: &Table, range: sva_samples::Extent) -> Vec<String> {
160 let needs = table.demand(range);
161 table
162 .values
163 .iter()
164 .filter(|(at, value)| {
165 matches!(value.kind, table::Kind::Resident { .. }) && !needs[*at].compute.is_empty()
166 })
167 .map(|(_, value)| value.name.clone())
168 .collect()
169}