Skip to main content

concinnity_core/build/environment_map/
schedule.rs

1//! How a caller runs the independent rows an environment-map convolution
2//! decomposes into.
3//!
4//! The rows share nothing: each reads only the immutable source and writes only
5//! its own texels, so they can be worked through in any order and on any thread.
6//! This crate owns no thread pool, so the schedule is the caller's to supply.
7//! `concinnity_host::thread` owns the engine's pool; a caller without one uses
8//! [`Serial`].
9
10/// Runs a set of independent work items to completion, in any order.
11///
12/// The method is generic rather than object-safe so one scheduler serves every
13/// item type; pass an implementor by reference.
14pub trait RowScheduler {
15    /// Apply `compute` to every item in `items`, then return.
16    fn run<T: Send>(&self, items: &mut [T], compute: &(dyn Fn(&mut T) + Send + Sync));
17}
18
19/// Runs every item on the calling thread, in order.
20pub struct Serial;
21
22impl RowScheduler for Serial {
23    fn run<T: Send>(&self, items: &mut [T], compute: &(dyn Fn(&mut T) + Send + Sync)) {
24        items.iter_mut().for_each(compute);
25    }
26}
27
28#[cfg(test)]
29mod tests {
30    use super::*;
31    use alloc::vec::Vec;
32
33    #[test]
34    fn serial_touches_every_item_once() {
35        let mut items: Vec<u32> = (0..8).collect();
36        Serial.run(&mut items, &|v| *v *= 2);
37        assert_eq!(items, (0..8).map(|v| v * 2).collect::<Vec<_>>());
38    }
39
40    #[test]
41    fn serial_over_an_empty_set_is_a_no_op() {
42        let mut items: Vec<u32> = Vec::new();
43        Serial.run(&mut items, &|v| *v += 1);
44        assert!(items.is_empty());
45    }
46}