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}