use super::{Node, NodeId, Values, labelled};
use crate::{Program, Stage};
use std::collections::BTreeMap;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct VertexDemand {
pub vertex: BTreeMap<usize, u8>,
pub pixel: BTreeMap<usize, u8>,
}
fn inputs(values: &Values, roots: impl IntoIterator<Item = NodeId>) -> BTreeMap<usize, u8> {
let mut masks = BTreeMap::new();
for id in values.reached_from(roots) {
if let Node::Input { input, lane } = values.node(id) {
*masks.entry(*input).or_default() |= 1 << lane;
}
}
masks
}
impl VertexDemand {
pub fn of_programs(vertex: &Program, pixel: &Program) -> Result<Self, String> {
if vertex.interface.stage != Stage::Vertex || pixel.interface.stage != Stage::Pixel {
return Err("vertex demand requires a vertex and a pixel program".into());
}
let vg = Values::of_program(vertex)?;
let pg = Values::of_program(pixel)?;
let pixel_inputs = inputs(&pg, pg.roots());
let mut roots: Vec<_> = vg
.outputs()
.iter()
.filter(|((output, _), _)| {
vertex
.interface
.outputs
.get(*output)
.is_some_and(|o| o.system.is_some())
})
.map(|(_, &node)| node)
.chain(vg.discards().iter().flatten().copied())
.chain(vg.writes().iter().flatten().copied())
.collect();
for (&input, &mask) in &pixel_inputs {
let varying = pixel.interface.inputs.get(input).ok_or_else(|| {
format!("the pixel program reads input {input}, which its interface does not have")
})?;
if varying.system.is_some() {
continue;
}
let output = labelled(&vertex.interface.outputs, &varying.label).ok_or_else(|| {
format!(
"pixel input {}{} has no vertex output",
varying.label.name, varying.label.index
)
})?;
for lane in 0..varying.value.width {
if mask & (1 << lane) != 0 {
roots.push(vg.output(output, lane).ok_or_else(|| {
format!(
"pixel input {}{} lane {lane} has no vertex write",
varying.label.name, varying.label.index
)
})?);
}
}
}
Ok(Self {
vertex: inputs(&vg, roots),
pixel: pixel_inputs,
})
}
}