rendy_chain/
collect.rs

1use std::cmp::max;
2use std::collections::hash_map::RandomState;
3use std::collections::HashMap;
4use std::hash::Hash;
5use std::ops::Range;
6
7use crate::{
8    chain::{BufferChains, Chain, ImageChains, Link, LinkNode},
9    node::{Node, State},
10    resource::{Buffer, Image, Resource},
11    schedule::{Queue, QueueId, Schedule, Submission, SubmissionId},
12    Id,
13};
14
15/// Placeholder for synchronization type.
16#[derive(Clone, Copy, Debug)]
17pub struct Unsynchronized;
18
19/// Result of node scheduler.
20#[derive(Debug)]
21pub struct Chains {
22    /// Contains submissions for nodes spread among queue schedule.
23    pub schedule: Schedule<Unsynchronized>,
24
25    /// Contains all buffer chains.
26    pub buffers: BufferChains,
27
28    /// Contains all image chains.
29    pub images: ImageChains,
30}
31
32#[derive(PartialEq, PartialOrd, Eq, Ord)]
33struct Fitness {
34    transfers: usize,
35    wait_factor: usize,
36}
37
38struct ResolvedNode {
39    id: usize,
40    family: rendy_core::hal::queue::QueueFamilyId,
41    queues: Range<usize>,
42    rev_deps: Vec<usize>,
43    buffers: Vec<(usize, State<Buffer>)>,
44    images: Vec<(usize, State<Image>)>,
45}
46
47impl Default for ResolvedNode {
48    fn default() -> Self {
49        ResolvedNode {
50            id: 0,
51            family: rendy_core::hal::queue::QueueFamilyId(0),
52            queues: 0..0,
53            rev_deps: Vec::new(),
54            buffers: Vec::new(),
55            images: Vec::new(),
56        }
57    }
58}
59
60struct ResolvedNodeSet {
61    nodes: Vec<ResolvedNode>,
62    queues: Vec<QueueId>,
63    buffers: Vec<Id>,
64    images: Vec<Id>,
65}
66
67struct ChainData<R: Resource> {
68    chain: Chain<R>,
69    last_link_wait_factor: usize,
70    current_link_wait_factor: usize,
71    current_family: Option<rendy_core::hal::queue::QueueFamilyId>,
72}
73impl<R: Resource> Default for ChainData<R> {
74    fn default() -> Self {
75        ChainData {
76            chain: Chain::new(),
77            last_link_wait_factor: 0,
78            current_link_wait_factor: 0,
79            current_family: None,
80        }
81    }
82}
83
84struct QueueData {
85    queue: Queue<Unsynchronized>,
86    wait_factor: usize,
87}
88
89/// Calculate automatic `Chains` for nodes.
90/// This function tries to find the most appropriate schedule for nodes execution.
91pub fn collect<Q>(nodes: Vec<Node>, max_queues: Q) -> Chains
92where
93    Q: Fn(rendy_core::hal::queue::QueueFamilyId) -> usize,
94{
95    // Resolve nodes into a form faster to work with.
96    let (nodes, mut unscheduled_nodes) = resolve_nodes(nodes, max_queues);
97    let mut ready_nodes = Vec::new();
98
99    // Chains.
100    let mut images: Vec<ChainData<Image>> = fill(nodes.images.len());
101    let mut buffers: Vec<ChainData<Buffer>> = fill(nodes.buffers.len());
102
103    // Schedule
104    let mut schedule = Vec::with_capacity(nodes.queues.len());
105    for i in 0..nodes.queues.len() {
106        schedule.push(QueueData {
107            queue: Queue::new(nodes.queues[i]),
108            wait_factor: 0,
109        });
110    }
111
112    for node in &nodes.nodes {
113        if unscheduled_nodes[node.id] == 0 {
114            ready_nodes.push(node);
115        }
116    }
117
118    let mut scheduled = 0;
119    if nodes.queues.len() == 1 {
120        // With a single queue, wait_factor is always the number of scheduled nodes, and
121        // transfers is always zero. Thus, we only need dependency resolution.
122        while let Some(node) = ready_nodes.pop() {
123            schedule_node(
124                &mut ready_nodes,
125                &mut unscheduled_nodes,
126                &nodes,
127                node,
128                0,
129                scheduled,
130                scheduled,
131                &mut schedule,
132                &mut images,
133                &mut buffers,
134            );
135            scheduled += 1;
136        }
137    } else {
138        while !ready_nodes.is_empty() {
139            // Among ready nodes find best fit.
140            let (fitness, qid, index) = ready_nodes
141                .iter()
142                .enumerate()
143                .map(|(index, &node)| {
144                    let (fitness, qid) = fitness(node, &mut images, &mut buffers, &mut schedule);
145                    (fitness, qid, index)
146                })
147                .min()
148                .unwrap();
149
150            let node = ready_nodes.swap_remove(index);
151            schedule_node(
152                &mut ready_nodes,
153                &mut unscheduled_nodes,
154                &nodes,
155                node,
156                qid,
157                fitness.wait_factor,
158                scheduled,
159                &mut schedule,
160                &mut images,
161                &mut buffers,
162            );
163            scheduled += 1;
164        }
165    }
166    assert_eq!(scheduled, nodes.nodes.len(), "Dependency loop found!");
167
168    Chains {
169        schedule: reify_schedule(schedule),
170        buffers: reify_chain(&nodes.buffers, buffers),
171        images: reify_chain(&nodes.images, images),
172    }
173}
174
175fn fill<T: Default>(num: usize) -> Vec<T> {
176    let mut vec = Vec::with_capacity(num);
177    for _ in 0..num {
178        vec.push(T::default());
179    }
180    vec
181}
182
183struct LookupBuilder<I: Hash + Eq + Copy> {
184    forward: HashMap<I, usize>,
185    backward: Vec<I>,
186}
187impl<I: Hash + Eq + Copy> LookupBuilder<I> {
188    fn new() -> LookupBuilder<I> {
189        LookupBuilder {
190            forward: HashMap::default(),
191            backward: Vec::new(),
192        }
193    }
194
195    fn forward(&mut self, id: I) -> usize {
196        if let Some(&id_num) = self.forward.get(&id) {
197            id_num
198        } else {
199            let id_num = self.backward.len();
200            self.backward.push(id);
201            self.forward.insert(id, id_num);
202            id_num
203        }
204    }
205}
206
207fn resolve_nodes<Q>(nodes: Vec<Node>, max_queues: Q) -> (ResolvedNodeSet, Vec<usize>)
208where
209    Q: Fn(rendy_core::hal::queue::QueueFamilyId) -> usize,
210{
211    let node_count = nodes.len();
212
213    let mut unscheduled_nodes = fill(nodes.len());
214    let mut reified_nodes: Vec<ResolvedNode> = fill(nodes.len());
215    let mut node_ids = LookupBuilder::new();
216    let mut queues = LookupBuilder::new();
217    let mut buffers = LookupBuilder::new();
218    let mut images = LookupBuilder::new();
219
220    let s = RandomState::new();
221    let mut family_full = HashMap::with_hasher(s);
222
223    for node in nodes {
224        let family = node.family;
225        if !family_full.contains_key(&family) {
226            let count = max_queues(family);
227            assert!(count > 0, "Cannot create a family with 0 max queues.");
228            for i in 0..count {
229                queues.forward(QueueId::new(family, i));
230            }
231
232            let full_range = queues.forward(QueueId::new(family, 0))
233                ..queues.forward(QueueId::new(family, count - 1)) + 1;
234            family_full.insert(family, full_range);
235        }
236
237        let id = node_ids.forward(node.id);
238        assert!(id < node_count, "Dependency not found."); // This implies a dep is not there.
239        let unscheduled_count = node.dependencies.len();
240
241        for dep in node.dependencies {
242            // Duplicated dependencies work fine, since they push two rev_deps entries and add two
243            // to unscheduled_nodes.
244            reified_nodes[node_ids.forward(dep)].rev_deps.push(id);
245        }
246        unscheduled_nodes[id] = unscheduled_count;
247
248        // We set these manually, and notably, do *not* touch rev_deps.
249        reified_nodes[id].id = id;
250        reified_nodes[id].family = node.family;
251        reified_nodes[id].queues = family_full[&family].clone();
252        reified_nodes[id].buffers = node
253            .buffers
254            .into_iter()
255            .map(|(k, v)| (buffers.forward(k), v))
256            .collect();
257        reified_nodes[id].images = node
258            .images
259            .into_iter()
260            .map(|(k, v)| (images.forward(k), v))
261            .collect();
262    }
263
264    (
265        ResolvedNodeSet {
266            nodes: reified_nodes,
267            queues: queues.backward,
268            buffers: buffers.backward,
269            images: images.backward,
270        },
271        unscheduled_nodes,
272    )
273}
274
275fn reify_chain<R: Resource>(ids: &[Id], vec: Vec<ChainData<R>>) -> HashMap<Id, Chain<R>> {
276    let mut map = HashMap::with_capacity_and_hasher(vec.len(), Default::default());
277    for (chain, &i) in vec.into_iter().zip(ids) {
278        map.insert(i, chain.chain);
279    }
280    map
281}
282
283fn reify_schedule(vec: Vec<QueueData>) -> Schedule<Unsynchronized> {
284    let mut schedule = Schedule::new();
285    for queue_data in vec.into_iter() {
286        schedule.set_queue(queue_data.queue);
287    }
288    schedule
289}
290
291fn fitness(
292    node: &ResolvedNode,
293    images: &mut Vec<ChainData<Image>>,
294    buffers: &mut Vec<ChainData<Buffer>>,
295    schedule: &mut Vec<QueueData>,
296) -> (Fitness, usize) {
297    let mut transfers = 0;
298    let mut wait_factor_from_chains = 0;
299
300    // Collect minimal waits required and resource transfers count.
301    for &(id, _) in &node.buffers {
302        let chain = &buffers[id];
303        if chain
304            .current_family
305            .map_or(false, |family| family != node.family)
306        {
307            transfers += 1;
308        }
309        wait_factor_from_chains = max(wait_factor_from_chains, chain.last_link_wait_factor);
310    }
311    for &(id, _) in &node.images {
312        let chain = &images[id];
313        if chain
314            .current_family
315            .map_or(false, |family| family != node.family)
316        {
317            transfers += 1;
318        }
319        wait_factor_from_chains = max(wait_factor_from_chains, chain.last_link_wait_factor);
320    }
321
322    // Find best queue for node.
323    let (wait_factor_from_queue, queue) = node
324        .queues
325        .clone()
326        .map(|index| (schedule[index].wait_factor, index))
327        .min()
328        .unwrap();
329    (
330        Fitness {
331            transfers,
332            wait_factor: max(wait_factor_from_chains, wait_factor_from_queue),
333        },
334        queue,
335    )
336}
337
338fn schedule_node<'a>(
339    ready_nodes: &mut Vec<&'a ResolvedNode>,
340    unscheduled_nodes: &mut Vec<usize>,
341    nodes: &'a ResolvedNodeSet,
342    node: &ResolvedNode,
343    queue: usize,
344    wait_factor: usize,
345    submitted: usize,
346    schedule: &mut Vec<QueueData>,
347    images: &mut Vec<ChainData<Image>>,
348    buffers: &mut Vec<ChainData<Buffer>>,
349) {
350    let ref mut queue_data = schedule[queue];
351    queue_data.wait_factor = max(queue_data.wait_factor, wait_factor + 1);
352    let sid = queue_data
353        .queue
354        .add_submission(node.id, wait_factor, submitted, Unsynchronized);
355    let submission = queue_data.queue.submission_mut(sid).unwrap();
356
357    for &(id, state) in &node.buffers {
358        add_to_chain(
359            nodes.buffers[id],
360            node.family,
361            &mut buffers[id],
362            sid,
363            submission,
364            state,
365            |s, i, l| s.set_buffer_link(i, l),
366        );
367    }
368    for &(id, state) in &node.images {
369        add_to_chain(
370            nodes.images[id],
371            node.family,
372            &mut images[id],
373            sid,
374            submission,
375            state,
376            |s, i, l| s.set_image_link(i, l),
377        );
378    }
379
380    for &rev_dep in &node.rev_deps {
381        unscheduled_nodes[rev_dep] -= 1;
382        if unscheduled_nodes[rev_dep] == 0 {
383            ready_nodes.push(&nodes.nodes[rev_dep]);
384        }
385    }
386}
387
388fn add_to_chain<R, S>(
389    id: Id,
390    family: rendy_core::hal::queue::QueueFamilyId,
391    chain_data: &mut ChainData<R>,
392    sid: SubmissionId,
393    submission: &mut Submission<S>,
394    state: State<R>,
395    set_link: impl FnOnce(&mut Submission<S>, Id, usize),
396) where
397    R: Resource,
398{
399    let node = LinkNode { sid, state };
400
401    chain_data.current_family = Some(family);
402    chain_data.current_link_wait_factor = max(
403        submission.wait_factor() + 1,
404        chain_data.current_link_wait_factor,
405    );
406
407    let ref mut chain = chain_data.chain;
408    let chain_len = chain.links().len();
409    let append = match chain.last_link_mut() {
410        Some(ref mut link) if link.compatible(&node) => {
411            set_link(submission, id, chain_len - 1);
412            link.add_node(node);
413            None
414        }
415        Some(_) | None => {
416            set_link(submission, id, chain_len);
417            chain_data.last_link_wait_factor = chain_data.current_link_wait_factor;
418            Some(Link::new(node))
419        }
420    };
421
422    if let Some(link) = append {
423        chain.add_link(link);
424    }
425}