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, } 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 = 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); } } }