use crate::error::RenderError;
use crate::graph::node::PassNode;
use molgfx_gpu::Device;
#[cfg(test)]
#[path = "schedule_tests.rs"]
mod tests;
pub(crate) fn schedule<D: Device>(passes: &[PassNode<D>]) -> Result<Vec<usize>, RenderError> {
let n = passes.len();
let mut after: Vec<Vec<usize>> = vec![Vec::new(); n];
let mut in_degree = vec![0usize; n];
let add_edge = |after: &mut Vec<Vec<usize>>, in_degree: &mut Vec<usize>, i: usize, j: usize| {
if !after[i].contains(&j) {
after[i].push(j);
in_degree[j] += 1;
}
};
for (i, writer) in passes.iter().enumerate() {
for (j, other) in passes.iter().enumerate() {
if i == j {
continue;
}
if writer.writes.iter().any(|w| other.reads.contains(w)) {
add_edge(&mut after, &mut in_degree, i, j);
}
if i < j && writer.writes.iter().any(|w| other.writes.contains(w)) {
add_edge(&mut after, &mut in_degree, i, j);
}
}
}
let mut order = Vec::with_capacity(n);
let mut ready: Vec<bool> = in_degree.iter().map(|&d| d == 0).collect();
while order.len() < n {
let Some(next) = ready.iter().position(|&r| r) else {
let stuck = in_degree
.iter()
.position(|&d| d > 0 && d != usize::MAX)
.and_then(|i| passes.get(i))
.map_or("<unknown>", |p| p.name);
return Err(RenderError::GraphCycle { pass: stuck });
};
ready[next] = false;
in_degree[next] = usize::MAX; order.push(next);
for &j in &after[next] {
if in_degree[j] != usize::MAX {
in_degree[j] -= 1;
if in_degree[j] == 0 {
ready[j] = true;
}
}
}
}
Ok(order)
}