Very hacky, but this is enough to be finished with the graph IR for now.
106 lines
3.7 KiB
Rust
106 lines
3.7 KiB
Rust
use ir::{
|
|
id,
|
|
instruction::{Filter, Kind},
|
|
GraphIr, Instruction, Map,
|
|
};
|
|
|
|
use crate::value::Variant;
|
|
mod instr;
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct Evaluator {
|
|
ir: GraphIr,
|
|
|
|
/// What the output of each individual streamer, and as result its output sockets, is.
|
|
/// Grows larger as evaluation continues,
|
|
/// as there's no mechanism for purging never-to-be-used-anymore instructions yet.
|
|
evaluated: Map<id::Output, Variant>,
|
|
}
|
|
|
|
impl crate::Evaluator for Evaluator {
|
|
fn feed(&mut self, ir: GraphIr) {
|
|
// TODO: should add instead of replace, see note in Evaluator trait above this method
|
|
self.ir = ir;
|
|
self.evaluated.clear();
|
|
}
|
|
|
|
fn eval_full(&mut self) {
|
|
// GraphIr::topological_sort returns InstructionRefs, which are mostly cool
|
|
// but we'd like to have them owned, so we can call Self::step without lifetime hassle
|
|
let queue: Vec<Instruction> = self
|
|
.ir
|
|
.topological_sort()
|
|
.into_iter()
|
|
.map(Into::into)
|
|
.collect();
|
|
|
|
for instr in queue {
|
|
self.step(instr);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Evaluator {
|
|
#[allow(clippy::needless_pass_by_value)]
|
|
fn step(&mut self, instr: Instruction) {
|
|
// what inputs does this instr need? fetch them
|
|
let inputs: Vec<_> = instr
|
|
.input_sources()
|
|
.iter()
|
|
.map(|source| {
|
|
let source_socket = source
|
|
.as_ref()
|
|
.expect("all inputs to be connected when an instruction is ran");
|
|
self.evaluated
|
|
.get(source_socket)
|
|
.expect("toposort to yield later instrs only after previous ones")
|
|
})
|
|
.collect();
|
|
|
|
// then actually do whatever the instruction should do
|
|
// NOTE: makes heavy use of index slicing,
|
|
// on the basis that ir::instruction::Kind::socket_count is correct
|
|
// TODO: make this a more flexible dispatch-ish arch
|
|
let output = match instr.kind {
|
|
Kind::Read(details) => Some(Variant::Image(instr::read::read(details))),
|
|
Kind::Write(details) => {
|
|
#[allow(irrefutable_let_patterns)] // will necessarily change
|
|
let Variant::Image(input) = inputs[0] else {
|
|
panic!("cannot only write images, but received: `{:?}`", inputs[0]);
|
|
};
|
|
instr::write::write(details, input);
|
|
None
|
|
}
|
|
Kind::Math(_) => todo!(),
|
|
Kind::Blend(_) => todo!(),
|
|
Kind::Noise(_) => todo!(),
|
|
Kind::Filter(filter_instruction) => match filter_instruction {
|
|
Filter::Invert => {
|
|
#[allow(irrefutable_let_patterns)]
|
|
let Variant::Image(input) = inputs[0] else {
|
|
panic!(
|
|
"cannot only filter invert images, but received: `{:?}`",
|
|
inputs[0]
|
|
);
|
|
};
|
|
|
|
Some(Variant::Image(instr::filters::invert::invert(
|
|
input.clone(),
|
|
)))
|
|
}
|
|
},
|
|
};
|
|
|
|
if let Some(output) = output {
|
|
// TODO: very inaccurate, actually respect individual instructions.
|
|
// should be implied by a different arch
|
|
// TODO: all of those should not be public, offer some methods to get this on
|
|
// `Instruction` instead (can infer short-term based on Kind::socket_count)
|
|
let socket = id::Output(id::Socket {
|
|
belongs_to: instr.id,
|
|
idx: id::SocketIdx(0),
|
|
});
|
|
self.evaluated.insert(socket, output);
|
|
}
|
|
}
|
|
}
|