Skip to main content

sva_engine/render/
run.rs

1// Concern: renders a target over memory, from the root down to what it answers | Non-concern: what memory keeps or writes, computing a value | IO: (&Graph, target, Tier) -> Render
2
3use std::collections::BTreeMap;
4use std::pin::Pin;
5use std::sync::Arc;
6
7use sva_ast::Graph;
8use sva_formula::NodeId;
9
10use super::value_graph;
11use super::world::{Reach, Reached, Walking, World};
12use super::{Render, RenderConfig, closed, driving, dropped, drove, ended, planned_over};
13use crate::cache::{Backend, Recording, Stored, Tier};
14use crate::error::EngineError;
15use crate::schedule;
16
17/// Whether a render's caller let it go, asked between blocks.
18pub trait Abandon {
19    fn abandoned(&self) -> Pin<Box<dyn Future<Output = bool> + '_>>;
20}
21
22pub struct Never;
23
24/// Renders made in turn, each advancing the version the last held.
25#[derive(Default)]
26pub struct Session {
27    own: Option<World>,
28    /// The composition with every volatile parameter at its stand-in.
29    pub(super) stand_in: Option<World>,
30}
31
32impl Abandon for Never {
33    fn abandoned(&self) -> Pin<Box<dyn Future<Output = bool> + '_>> {
34        Box::pin(std::future::ready(false))
35    }
36}
37
38/// `target` over `tier`, from the root down, in a session of its own.
39pub async fn render_over<B: Backend>(
40    graph: &Graph,
41    target: &str,
42    config: RenderConfig,
43    tier: &Tier<B>,
44) -> Result<Render, EngineError> {
45    render_in(&mut Session::default(), graph, target, config, tier, &Never).await
46}
47
48/// `target` over `tier`, from the root down, typed anew only where `session` typed it
49/// otherwise; a node memory answers stands as its samples. With `out` dropped and no reading,
50/// a root it answers ends the render unread. Abandoned, it stops before its next block.
51pub async fn render_in<B: Backend>(
52    session: &mut Session,
53    graph: &Graph,
54    target: &str,
55    config: RenderConfig,
56    tier: &Tier<B>,
57    abandon: &dyn Abandon,
58) -> Result<Render, EngineError> {
59    let memory = tier.memory();
60    let mut recording = Recording::over(memory);
61    let Session { own, stand_in } = session;
62    let (world, root) = World::rendered(own, graph, target, config.rate)?;
63    let (instances, typed) = (&world.instances, &world.typing);
64    let id = typed
65        .id(&root)
66        .ok_or_else(|| EngineError::UnknownNode(root.clone()))?;
67    let (config, decided) = ended(typed, id, config);
68    let lowered = typed.lowered().to_vec();
69    let round = tier.begin();
70    let found = walked(world, (&root, &config), (tier, round, &mut recording)).await;
71    let tys = typed.clone();
72    if dropped(&config) && found.held.contains_key(&root) {
73        let schedule = schedule::plan(&tys, id, &config.asks);
74        let mut held = Render::shell((tys, id), (config, schedule), memory.clone());
75        let mut stats = recording.stats(memory);
76        stats.typed = lowered;
77        held.cache_stats = Some(stats);
78        return Ok(held);
79    }
80    let hits: BTreeMap<NodeId, Arc<Stored>> = found
81        .held
82        .iter()
83        .filter_map(|(path, stored)| Some((tys.id(path)?, Arc::clone(stored))))
84        .collect();
85    let mut held = planned_over(
86        (graph, target, instances),
87        (tys, id),
88        (config, &mut *stand_in, memory.clone()),
89        (decided, &hits),
90    )?;
91    let mut retyped = lowered;
92    retyped.append(&mut held.stand_in_typed);
93    if let (Some(value_graph), Some(range)) = (&mut held.value_graph, held.range) {
94        loop {
95            let mut needs = value_graph.needs(range);
96            while !needs.is_empty() {
97                let fetched = tier.fetch(&needs).await;
98                for (key, parts) in fetched.handed {
99                    value_graph.took(key, &parts);
100                }
101                needs = fetched.left;
102            }
103            let short = value_graph.short((value_graph.root, range, value_graph::Past::Held));
104            if short.is_empty() {
105                break;
106            }
107            for at in short {
108                value_graph.read_on(&held.tys, at)?;
109            }
110            value_graph.settled(value_graph.root, &[], (range.start, false));
111            value_graph.refuse_endless(range)?;
112        }
113        value_graph.offers(&held.tys, range);
114    }
115    let walked = recording.stats(memory);
116    match driving(&mut held, (memory, recording))? {
117        Some(mut driver) => {
118            loop {
119                if abandon.abandoned().await {
120                    return Err(EngineError::Abandoned);
121                }
122                if !driver.pull()? {
123                    break;
124                }
125                let needs = driver.value_graph.needs(driver.next());
126                for (key, parts) in tier.fetch(&needs).await.handed {
127                    driver.value_graph.took(key, &parts);
128                }
129            }
130            drove(&mut held, driver);
131        }
132        None => held.cache_stats = Some(walked),
133    }
134    let computed = held.value_graph.as_ref().map_or(Vec::new(), |value_graph| {
135        let computed = value_graph.values.iter();
136        let computed =
137            computed.filter(|(_, v)| !matches!(v.kind, value_graph::Kind::Resident { .. }));
138        computed.map(|(_, v)| v.name.clone()).collect()
139    });
140    if let Some(stats) = &mut held.cache_stats {
141        stats.typed = retyped;
142        stats.planned = computed;
143        stats.unslotted = held.unslotted.clone();
144    }
145    closed(&mut held)?;
146    Ok(held)
147}
148
149/// What memory answers from `root` down, each key it cannot answer looked up this round.
150async fn walked<B: Backend>(
151    world: &World,
152    (root, config): (&str, &RenderConfig),
153    (tier, round, seen): (&Tier<B>, u64, &mut Recording),
154) -> Reached {
155    let walking = Walking {
156        root,
157        config,
158        whole: true,
159    };
160    loop {
161        let memory = tier.memory();
162        match world.walk(&walking, &mut |node, key| {
163            memory.answered((key, round), (node, seen))
164        }) {
165            Reach::Reached(reached) => return reached,
166            Reach::Asks(keys) => {
167                for key in keys {
168                    tier.lookup(key, round).await;
169                }
170            }
171        }
172    }
173}