forked from katzen-cafe/iowo
Compare commits
No commits in common. "1772b70172010af94158df4a6e146357582bf234" and "c4207af8da25aad70529f14caec5bc0af64e2e6c" have entirely different histories.
1772b70172
...
c4207af8da
28 changed files with 149 additions and 407 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -4,5 +4,3 @@
|
||||||
.pre-commit-config.yaml
|
.pre-commit-config.yaml
|
||||||
*.pdf
|
*.pdf
|
||||||
/docs/*.png
|
/docs/*.png
|
||||||
/testfiles/gen/*
|
|
||||||
!/testfiles/gen/.gitkeep
|
|
||||||
|
|
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -63,8 +63,6 @@ dependencies = [
|
||||||
"ariadne",
|
"ariadne",
|
||||||
"clap",
|
"clap",
|
||||||
"dirs",
|
"dirs",
|
||||||
"eval",
|
|
||||||
"ir",
|
|
||||||
"owo-colors",
|
"owo-colors",
|
||||||
"ron",
|
"ron",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -274,7 +272,6 @@ dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"image",
|
"image",
|
||||||
"ir",
|
"ir",
|
||||||
"serde",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -6,16 +6,14 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ariadne = "0.4"
|
|
||||||
clap = { workspace = true, features = [ "derive", "env" ] }
|
clap = { workspace = true, features = [ "derive", "env" ] }
|
||||||
dirs = "5"
|
|
||||||
eval = { path = "../eval" }
|
|
||||||
ir = { path = "../ir" }
|
|
||||||
owo-colors = "4"
|
|
||||||
ron = "0.8"
|
|
||||||
serde = { workspace = true, features = [ "derive" ] }
|
serde = { workspace = true, features = [ "derive" ] }
|
||||||
|
ron = "0.8"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
ariadne = "0.4"
|
||||||
time = { version = "0.3", features = [ "local-offset" ] }
|
time = { version = "0.3", features = [ "local-offset" ] }
|
||||||
|
dirs = "5"
|
||||||
|
owo-colors = "4"
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
|
@ -12,9 +10,6 @@ mod config_file;
|
||||||
|
|
||||||
/// this struct may hold all configuration
|
/// this struct may hold all configuration
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub source: PathBuf,
|
|
||||||
pub evaluator: eval::Available,
|
|
||||||
|
|
||||||
pub startup_msg: bool,
|
pub startup_msg: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,37 +17,37 @@ impl Config {
|
||||||
/// Get the configs from all possible places (args, file, env...)
|
/// Get the configs from all possible places (args, file, env...)
|
||||||
pub fn read() -> Self {
|
pub fn read() -> Self {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
let config = if let Some(config) = args.config_path {
|
let config_path = if let Some(config_path) = args.config_path {
|
||||||
Ok(config)
|
Ok(config_path)
|
||||||
} else {
|
} else {
|
||||||
find_config_file()
|
find_config_file()
|
||||||
};
|
};
|
||||||
|
|
||||||
// try to read a maybe existing config file
|
// try to read a maybe existing config file
|
||||||
let config = config.ok().and_then(|path| {
|
let file_config = if let Ok(config_path) = config_path {
|
||||||
Configs::read(path).map_or_else(
|
let file_config = Configs::read(config_path);
|
||||||
|e| {
|
|
||||||
|
match file_config {
|
||||||
|
Ok(c) => Some(c),
|
||||||
|
Err(e) => {
|
||||||
eprintln!("Config error: {e:?}");
|
eprintln!("Config error: {e:?}");
|
||||||
eprintln!("Proceeding with defaults or cli args...");
|
eprintln!("Proceeding with defaults or cli args...");
|
||||||
None
|
None
|
||||||
},
|
}
|
||||||
Some,
|
}
|
||||||
)
|
} else {
|
||||||
});
|
None
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(file) = config {
|
if let Some(file_config) = file_config {
|
||||||
Self {
|
Self {
|
||||||
source: args.source,
|
|
||||||
evaluator: args.evaluator.and(file.evaluator).unwrap_or_default(),
|
|
||||||
// this is negated because to an outward api, the negative is more intuitive,
|
// this is negated because to an outward api, the negative is more intuitive,
|
||||||
// while in the source the other way around is more intuitive
|
// while in the source the other way around is more intuitive
|
||||||
startup_msg: !(args.no_startup_message || file.no_startup_message),
|
startup_msg: !(args.no_startup_message || file_config.no_startup_message),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Self {
|
Self {
|
||||||
source: args.source,
|
|
||||||
startup_msg: !args.no_startup_message,
|
startup_msg: !args.no_startup_message,
|
||||||
evaluator: args.evaluator.unwrap_or_default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +56,7 @@ impl Config {
|
||||||
pub mod error {
|
pub mod error {
|
||||||
/// Errors that can occur when reading configs
|
/// Errors that can occur when reading configs
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Config {
|
pub enum ConfigError {
|
||||||
/// The config dir doesn't exist
|
/// The config dir doesn't exist
|
||||||
NoConfigDir,
|
NoConfigDir,
|
||||||
/// We didn't find a config file in the config dir
|
/// We didn't find a config file in the config dir
|
||||||
|
@ -78,19 +73,19 @@ pub mod error {
|
||||||
SerdeRonError(ron::error::SpannedError),
|
SerdeRonError(ron::error::SpannedError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<std::io::Error> for Config {
|
impl From<std::io::Error> for ConfigError {
|
||||||
fn from(value: std::io::Error) -> Self {
|
fn from(value: std::io::Error) -> Self {
|
||||||
Self::IoError(value)
|
Self::IoError(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<serde_json::Error> for Config {
|
impl From<serde_json::Error> for ConfigError {
|
||||||
fn from(value: serde_json::Error) -> Self {
|
fn from(value: serde_json::Error) -> Self {
|
||||||
Self::SerdeJsonError(value)
|
Self::SerdeJsonError(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ron::error::SpannedError> for Config {
|
impl From<ron::error::SpannedError> for ConfigError {
|
||||||
fn from(value: ron::error::SpannedError) -> Self {
|
fn from(value: ron::error::SpannedError) -> Self {
|
||||||
Self::SerdeRonError(value)
|
Self::SerdeRonError(value)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,6 @@ use clap::{builder::BoolishValueParser, ArgAction, Parser};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
pub(crate) struct Args {
|
pub(crate) struct Args {
|
||||||
/// What file contains the pipeline to evaluate.
|
|
||||||
pub source: PathBuf,
|
|
||||||
|
|
||||||
/// How to actually run the pipeline.
|
|
||||||
/// Overrides the config file. Defaults to the debug evaluator.
|
|
||||||
#[arg(short, long)]
|
|
||||||
pub evaluator: Option<eval::Available>,
|
|
||||||
|
|
||||||
/// Read this config file.
|
/// Read this config file.
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
pub config_path: Option<PathBuf>,
|
pub config_path: Option<PathBuf>,
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::{
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::error::Config;
|
use super::error::ConfigError;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct Configs {
|
pub struct Configs {
|
||||||
|
@ -13,7 +13,6 @@ pub struct Configs {
|
||||||
pub example_value: i32,
|
pub example_value: i32,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub no_startup_message: bool,
|
pub no_startup_message: bool,
|
||||||
pub evaluator: Option<eval::Available>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// what the fuck serde why do i need this
|
/// what the fuck serde why do i need this
|
||||||
|
@ -22,9 +21,9 @@ fn default_example_value() -> i32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the location of a config file and check if there is, in fact, a file
|
/// Find the location of a config file and check if there is, in fact, a file
|
||||||
pub(super) fn find_config_file() -> Result<PathBuf, Config> {
|
pub(super) fn find_config_file() -> Result<PathBuf, ConfigError> {
|
||||||
let Some(config_path) = dirs::config_dir() else {
|
let Some(config_path) = dirs::config_dir() else {
|
||||||
return Err(Config::NoConfigDir);
|
return Err(ConfigError::NoConfigDir);
|
||||||
};
|
};
|
||||||
|
|
||||||
let ron_path = config_path.with_file_name("config.ron");
|
let ron_path = config_path.with_file_name("config.ron");
|
||||||
|
@ -35,19 +34,16 @@ pub(super) fn find_config_file() -> Result<PathBuf, Config> {
|
||||||
} else if Path::new(&json_path).exists() {
|
} else if Path::new(&json_path).exists() {
|
||||||
Ok(json_path)
|
Ok(json_path)
|
||||||
} else {
|
} else {
|
||||||
Err(Config::NoConfigFileFound)
|
Err(ConfigError::NoConfigFileFound)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Configs {
|
impl Configs {
|
||||||
pub fn read(p: PathBuf) -> Result<Self, Config> {
|
pub fn read(p: PathBuf) -> Result<Self, ConfigError> {
|
||||||
match p
|
match p.extension().map(|v| v.to_str().unwrap()) {
|
||||||
.extension()
|
|
||||||
.map(|v| v.to_str().expect("config path to be UTF-8"))
|
|
||||||
{
|
|
||||||
Some("ron") => Ok(serde_json::from_str(&fs::read_to_string(p)?)?),
|
Some("ron") => Ok(serde_json::from_str(&fs::read_to_string(p)?)?),
|
||||||
Some("json") => Ok(ron::from_str(&fs::read_to_string(p)?)?),
|
Some("json") => Ok(ron::from_str(&fs::read_to_string(p)?)?),
|
||||||
e => Err(Config::UnknownExtension(e.map(str::to_owned))),
|
e => Err(ConfigError::UnknownExtension(e.map(|v| v.to_owned()))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,12 @@ use std::process;
|
||||||
use ron::error::Position;
|
use ron::error::Position;
|
||||||
|
|
||||||
/// Report an `Error` from the `serde_json` crate
|
/// Report an `Error` from the `serde_json` crate
|
||||||
pub fn report_serde_json_err(src: &str, err: &serde_json::Error) -> ! {
|
pub fn report_serde_json_err(src: &str, err: serde_json::Error) -> ! {
|
||||||
report_serde_err(src, err.line(), err.column(), err.to_string())
|
report_serde_err(src, err.line(), err.column(), err.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Report a `SpannedError` from the `ron` crate
|
/// Report a `SpannedError` from the `ron` crate
|
||||||
pub fn report_serde_ron_err(src: &str, err: &ron::error::SpannedError) -> ! {
|
pub fn report_serde_ron_err(src: &str, err: ron::error::SpannedError) -> ! {
|
||||||
let Position { line, col } = err.position;
|
let Position { line, col } = err.position;
|
||||||
report_serde_err(src, line, col, err.to_string())
|
report_serde_err(src, line, col, err.to_string())
|
||||||
}
|
}
|
||||||
|
@ -23,8 +23,8 @@ fn report_serde_err(src: &str, line: usize, col: usize, msg: String) -> ! {
|
||||||
.with_message(msg)
|
.with_message(msg)
|
||||||
.with_note("We'd like to give better errors, but serde errors are horrible to work with...")
|
.with_note("We'd like to give better errors, but serde errors are horrible to work with...")
|
||||||
.finish()
|
.finish()
|
||||||
.eprint(("test", Source::from(src)))
|
.print(("test", Source::from(src)))
|
||||||
.expect("writing error to stderr failed");
|
.unwrap();
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use std::fs;
|
|
||||||
|
|
||||||
use config::Config;
|
use config::Config;
|
||||||
use welcome_msg::print_startup_msg;
|
use welcome_msg::print_startup_msg;
|
||||||
|
|
||||||
|
@ -10,18 +8,10 @@ mod error_reporting;
|
||||||
mod welcome_msg;
|
mod welcome_msg;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// TODO: proper error handling across the whole function
|
// TODO: proper error handling
|
||||||
// don't forget to also look inside `Config`
|
|
||||||
let cfg = Config::read();
|
let cfg = Config::read();
|
||||||
|
|
||||||
if cfg.startup_msg {
|
if cfg.startup_msg {
|
||||||
print_startup_msg();
|
print_startup_msg();
|
||||||
}
|
}
|
||||||
|
|
||||||
let source = fs::read_to_string(cfg.source).expect("can't find source file");
|
|
||||||
let ir = ir::from_ron(&source).expect("failed to parse source to graph ir");
|
|
||||||
|
|
||||||
let mut machine = cfg.evaluator.pick();
|
|
||||||
machine.feed(ir);
|
|
||||||
machine.eval_full();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ edition = "2021"
|
||||||
clap = { workspace = true, features = [ "derive" ] }
|
clap = { workspace = true, features = [ "derive" ] }
|
||||||
image = "0.24"
|
image = "0.24"
|
||||||
ir = { path = "../ir" }
|
ir = { path = "../ir" }
|
||||||
serde = { workspace = true }
|
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
1
crates/eval/src/cpu/mod.rs
Normal file
1
crates/eval/src/cpu/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub(crate) struct CpuExecutor;
|
|
@ -2,7 +2,7 @@ pub mod read {
|
||||||
use image::{io::Reader as ImageReader, DynamicImage};
|
use image::{io::Reader as ImageReader, DynamicImage};
|
||||||
use ir::instruction::read::{Read, SourceType};
|
use ir::instruction::read::{Read, SourceType};
|
||||||
|
|
||||||
pub fn read(Read { source }: Read) -> DynamicImage {
|
pub fn read(Read { source, .. }: Read) -> DynamicImage {
|
||||||
// TODO: actual error handling
|
// TODO: actual error handling
|
||||||
let img = ImageReader::open(match source {
|
let img = ImageReader::open(match source {
|
||||||
SourceType::File(path) => path,
|
SourceType::File(path) => path,
|
||||||
|
@ -18,7 +18,6 @@ pub mod write {
|
||||||
use ir::instruction::write::{TargetFormat, TargetType, Write};
|
use ir::instruction::write::{TargetFormat, TargetType, Write};
|
||||||
|
|
||||||
pub fn write(Write { target, format }: Write, input_data: &DynamicImage) {
|
pub fn write(Write { target, format }: Write, input_data: &DynamicImage) {
|
||||||
// TODO: actual error handling
|
|
||||||
input_data
|
input_data
|
||||||
.save_with_format(
|
.save_with_format(
|
||||||
match target {
|
match target {
|
||||||
|
@ -29,7 +28,7 @@ pub mod write {
|
||||||
TargetFormat::Png => ImageFormat::Png,
|
TargetFormat::Png => ImageFormat::Png,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.expect("couldn't save file");
|
.expect("couldn't save file — come back later and handle me properly please uwu");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
37
crates/eval/src/debug/mod.rs
Normal file
37
crates/eval/src/debug/mod.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
use ir::instruction::{Filter, Kind};
|
||||||
|
|
||||||
|
use crate::value::Dynamic;
|
||||||
|
mod instructions;
|
||||||
|
|
||||||
|
pub struct Executor;
|
||||||
|
|
||||||
|
impl crate::Executor for Executor {
|
||||||
|
fn execute(instruction: Kind, input: Option<Dynamic>) -> Option<Dynamic> {
|
||||||
|
match instruction {
|
||||||
|
Kind::Read(read_instruction) => {
|
||||||
|
Some(Dynamic::Image(instructions::read::read(read_instruction)))
|
||||||
|
}
|
||||||
|
Kind::Write(write_instruction) => {
|
||||||
|
instructions::write::write(
|
||||||
|
write_instruction,
|
||||||
|
match input {
|
||||||
|
Some(Dynamic::Image(ref img)) => img,
|
||||||
|
_ => panic!("awawwawwa"),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Kind::Math(_) => todo!(),
|
||||||
|
Kind::Blend(_) => todo!(),
|
||||||
|
Kind::Noise(_) => todo!(),
|
||||||
|
Kind::Filter(filter_instruction) => match filter_instruction {
|
||||||
|
Filter::Invert => Some(Dynamic::Image(instructions::filters::invert::invert(
|
||||||
|
match input {
|
||||||
|
Some(Dynamic::Image(img)) => img,
|
||||||
|
_ => panic!("invalid value type for invert"),
|
||||||
|
},
|
||||||
|
))),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,105 +0,0 @@
|
||||||
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) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
pub mod debug;
|
|
|
@ -1,43 +1,34 @@
|
||||||
use ir::GraphIr;
|
use ir::instruction::Kind;
|
||||||
|
use value::Dynamic;
|
||||||
|
|
||||||
mod kind;
|
mod debug;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
/// Can collapse a [`GraphIr`] in meaningful ways and do interesting work on it.
|
/// The available executors
|
||||||
///
|
/// unused in early dev.
|
||||||
/// It's surprisingly difficult to find a fitting description for this.
|
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
|
||||||
pub trait Evaluator {
|
pub enum RegisteredExecutor {
|
||||||
/// Take some [`GraphIr`] which will then be processed later.
|
/// the debug executor is single threaded and really, *really* slow. And unstable. Don't use. Unless you're a dev working on this.
|
||||||
/// May be called multiple times, in which the [`GraphIr`]s should add up.
|
|
||||||
// TODO: atm they definitely don't add up -- add some functionality to GraphIr to
|
|
||||||
// make it combine two graphs into one
|
|
||||||
fn feed(&mut self, ir: GraphIr);
|
|
||||||
|
|
||||||
/// Walk through the _whole_ [`GraphIr`] and run through each instruction.
|
|
||||||
fn eval_full(&mut self);
|
|
||||||
|
|
||||||
// TODO: for an LSP or the like, eval_single which starts at a given instr
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The available [`Evaluator`]s.
|
|
||||||
///
|
|
||||||
/// Checklist for adding new ones:
|
|
||||||
///
|
|
||||||
/// 1. Create a new module under the [`kind`] module.
|
|
||||||
/// 2. Add a struct and implement [`Evaluator`] for it.
|
|
||||||
#[derive(Clone, Copy, Debug, Default, clap::ValueEnum, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub enum Available {
|
|
||||||
/// Runs fully on the CPU. Single-threaded, debug-friendly and quick to implement.
|
|
||||||
#[default]
|
|
||||||
Debug,
|
Debug,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Available {
|
trait Executor {
|
||||||
/// Selects the [`Evaluator`] corresponding to this label.
|
fn execute(instruction: Kind, input: Option<Dynamic>) -> Option<Dynamic>;
|
||||||
#[must_use]
|
}
|
||||||
pub fn pick(&self) -> Box<dyn Evaluator> {
|
|
||||||
match self {
|
pub fn execute_all(instructions: Vec<Kind>) {
|
||||||
Self::Debug => Box::new(kind::debug::Evaluator::default()),
|
let mut tmp = None;
|
||||||
}
|
|
||||||
|
for instruction in instructions {
|
||||||
|
tmp = debug::Executor::execute(instruction, tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// scratchpad lol:
|
||||||
|
// execution structure:
|
||||||
|
// 1. take in rpl
|
||||||
|
// 2. analyse/validate structure against allowed executors
|
||||||
|
// 3. assign executors to instructions
|
||||||
|
// 4. optimize
|
||||||
|
// 5. prepare memory management patterns
|
||||||
|
// 6. run
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
use image::DynamicImage;
|
use image::DynamicImage;
|
||||||
|
|
||||||
/// Any runtime value that an instruction can input or output.
|
pub enum Dynamic {
|
||||||
///
|
|
||||||
/// The name is taken from [Godot's `Variant` type],
|
|
||||||
/// which is very similar to this one.
|
|
||||||
///
|
|
||||||
/// [Godot's `Variant` type]: https://docs.godotengine.org/en/stable/classes/class_variant.html
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum Variant {
|
|
||||||
Image(DynamicImage),
|
Image(DynamicImage),
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ impl Input {
|
||||||
///
|
///
|
||||||
/// In contrast to [`Input`]s, [`Output`]s may be used or unused.
|
/// In contrast to [`Input`]s, [`Output`]s may be used or unused.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||||
pub struct Output(pub Socket); // TODO: Restrict publicness to super
|
pub struct Output(pub(super) Socket);
|
||||||
|
|
||||||
impl Output {
|
impl Output {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
@ -75,7 +75,7 @@ pub struct Socket {
|
||||||
/// This really only serves for denoting where a socket is,
|
/// This really only serves for denoting where a socket is,
|
||||||
/// when it's already clear which instruction is referred to.
|
/// when it's already clear which instruction is referred to.
|
||||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||||
pub struct SocketIdx(pub u16); // TODO: Restrict publicness to super
|
pub struct SocketIdx(pub u16);
|
||||||
|
|
||||||
impl fmt::Debug for SocketIdx {
|
impl fmt::Debug for SocketIdx {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
|
|
@ -4,9 +4,16 @@ use std::path::PathBuf;
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||||
pub struct Read {
|
pub struct Read {
|
||||||
pub source: SourceType,
|
pub source: SourceType,
|
||||||
|
pub format: SourceFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||||
pub enum SourceType {
|
pub enum SourceType {
|
||||||
File(PathBuf),
|
File(PathBuf),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||||
|
pub enum SourceFormat {
|
||||||
|
Jpeg,
|
||||||
|
Png,
|
||||||
|
}
|
||||||
|
|
|
@ -27,8 +27,6 @@ pub fn from_ron(source: &str) -> ron::error::SpannedResult<GraphIr> {
|
||||||
|
|
||||||
/// The toplevel representation of a whole pipeline.
|
/// The toplevel representation of a whole pipeline.
|
||||||
///
|
///
|
||||||
/// # DAGs
|
|
||||||
///
|
|
||||||
/// Pipelines may not be fully linear. They may branch out and recombine later on.
|
/// Pipelines may not be fully linear. They may branch out and recombine later on.
|
||||||
/// As such, the representation for them which is currently used is a
|
/// As such, the representation for them which is currently used is a
|
||||||
/// [**D**irected **A**cyclic **G**raph](https://en.wikipedia.org/wiki/Directed_acyclic_graph).
|
/// [**D**irected **A**cyclic **G**raph](https://en.wikipedia.org/wiki/Directed_acyclic_graph).
|
||||||
|
@ -58,28 +56,7 @@ pub fn from_ron(source: &str) -> ron::error::SpannedResult<GraphIr> {
|
||||||
/// So the vertices of the DAG are the **sockets**
|
/// So the vertices of the DAG are the **sockets**
|
||||||
/// (which are either [`id::Input`] or [`id::Output`] depending on the direction),
|
/// (which are either [`id::Input`] or [`id::Output`] depending on the direction),
|
||||||
/// and each **socket** in turn belongs to an **instruction**.
|
/// and each **socket** in turn belongs to an **instruction**.
|
||||||
///
|
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||||
/// # Usage
|
|
||||||
///
|
|
||||||
/// - If you want to build one from scratch,
|
|
||||||
/// add a few helper methods like
|
|
||||||
/// constructing an empty one,
|
|
||||||
/// adding instructions and
|
|
||||||
/// adding edges
|
|
||||||
/// - If you want to construct one from an existing repr,
|
|
||||||
/// maybe you want to use [`semi_human::GraphIr`].
|
|
||||||
///
|
|
||||||
/// # Storing additional data
|
|
||||||
///
|
|
||||||
/// Chances are the graph IR seems somewhat fit to put metadata in it.
|
|
||||||
/// However, most likely you're interacting in context of some other system,
|
|
||||||
/// and also want to manage and index that data on your own.
|
|
||||||
///
|
|
||||||
/// As such, consider using _secondary_ maps instead.
|
|
||||||
/// That is, store in a data structure _you_ own a mapping
|
|
||||||
/// from [`id`]s
|
|
||||||
/// to whatever data you need.
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
|
|
||||||
pub struct GraphIr {
|
pub struct GraphIr {
|
||||||
/// "Backbone" storage of all **instruction** IDs to
|
/// "Backbone" storage of all **instruction** IDs to
|
||||||
/// what **kind of instruction** they are.
|
/// what **kind of instruction** they are.
|
||||||
|
@ -146,13 +123,13 @@ impl GraphIr {
|
||||||
/// to actually have multiple [`GraphIr`]s at one point in time.
|
/// to actually have multiple [`GraphIr`]s at one point in time.
|
||||||
/// Open an issue if that poses a problem for you.
|
/// Open an issue if that poses a problem for you.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn resolve<'ir>(&'ir self, subject: &id::Instruction) -> Option<InstructionRef<'ir>> {
|
pub fn resolve<'ir>(&'ir self, subject: &id::Instruction) -> Option<Instruction<'ir>> {
|
||||||
let (id, kind) = self.instructions.get_key_value(subject)?;
|
let (id, kind) = self.instructions.get_key_value(subject)?;
|
||||||
|
|
||||||
let input_sources = self.input_sources(subject)?.collect();
|
let input_sources = self.input_sources(subject)?.collect();
|
||||||
let output_targets = self.output_targets(subject)?.collect();
|
let output_targets = self.output_targets(subject)?.collect();
|
||||||
|
|
||||||
Some(InstructionRef {
|
Some(Instruction {
|
||||||
id,
|
id,
|
||||||
kind,
|
kind,
|
||||||
input_sources,
|
input_sources,
|
||||||
|
@ -164,7 +141,7 @@ impl GraphIr {
|
||||||
///
|
///
|
||||||
/// The same caveats as for [`GraphIr::resolve`] apply.
|
/// The same caveats as for [`GraphIr::resolve`] apply.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn owner_of_input<'ir>(&'ir self, input: &id::Input) -> Option<InstructionRef<'ir>> {
|
pub fn owner_of_input<'ir>(&'ir self, input: &id::Input) -> Option<Instruction<'ir>> {
|
||||||
self.resolve(&input.socket().belongs_to)
|
self.resolve(&input.socket().belongs_to)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +149,7 @@ impl GraphIr {
|
||||||
///
|
///
|
||||||
/// The same caveats as for [`GraphIr::resolve`] apply.
|
/// The same caveats as for [`GraphIr::resolve`] apply.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn owner_of_output<'ir>(&'ir self, output: &id::Output) -> Option<InstructionRef<'ir>> {
|
pub fn owner_of_output<'ir>(&'ir self, output: &id::Output) -> Option<Instruction<'ir>> {
|
||||||
self.resolve(&output.socket().belongs_to)
|
self.resolve(&output.socket().belongs_to)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +163,7 @@ impl GraphIr {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
// yes, this function could probably return an iterator and be lazy
|
// yes, this function could probably return an iterator and be lazy
|
||||||
// no, not today
|
// no, not today
|
||||||
pub fn topological_sort(&self) -> Vec<InstructionRef> {
|
pub fn topological_sort(&self) -> Vec<Instruction> {
|
||||||
// count how many incoming edges each vertex has
|
// count how many incoming edges each vertex has
|
||||||
let mut nonzero_input_counts: Map<_, NonZeroUsize> =
|
let mut nonzero_input_counts: Map<_, NonZeroUsize> =
|
||||||
self.rev_edges
|
self.rev_edges
|
||||||
|
@ -263,58 +240,18 @@ impl GraphIr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs an [`id::Socket`] a bit more tersely.
|
/// A full instruction in context, with its inputs and outputs.
|
||||||
fn socket(id: &id::Instruction, idx: u16) -> id::Socket {
|
|
||||||
id::Socket {
|
|
||||||
belongs_to: id.clone(),
|
|
||||||
idx: id::SocketIdx(idx),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A full instruction bundeled in context, with its inputs and outputs.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Instruction {
|
pub struct Instruction<'ir> {
|
||||||
pub id: id::Instruction,
|
|
||||||
pub kind: instruction::Kind,
|
|
||||||
|
|
||||||
// can't have these two public since then a user might corrupt their length
|
|
||||||
input_sources: Vec<Option<id::Output>>,
|
|
||||||
output_targets: Vec<Set<id::Input>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Instruction {
|
|
||||||
/// Where this instruction gets its inputs from.
|
|
||||||
///
|
|
||||||
/// [`None`] means that this input is unfilled,
|
|
||||||
/// and must be filled before the instruction can be ran.
|
|
||||||
#[must_use]
|
|
||||||
pub fn input_sources(&self) -> &[Option<id::Output>] {
|
|
||||||
&self.input_sources
|
|
||||||
}
|
|
||||||
|
|
||||||
/// To whom outputs are sent.
|
|
||||||
#[must_use]
|
|
||||||
pub fn output_targets(&self) -> &[Set<id::Input>] {
|
|
||||||
&self.output_targets
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [`Instruction`], but every single field is borrowed instead.
|
|
||||||
/// See its docs.
|
|
||||||
///
|
|
||||||
/// Use the [`From`] impl to handily convert into an [`Instruction`].
|
|
||||||
/// The other way around is unlikely to be wanted — since you already have an [`Instruction`],
|
|
||||||
/// chances are you just want to take a reference (`&`) of it.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
pub struct InstructionRef<'ir> {
|
|
||||||
pub id: &'ir id::Instruction,
|
pub id: &'ir id::Instruction,
|
||||||
pub kind: &'ir instruction::Kind,
|
pub kind: &'ir instruction::Kind,
|
||||||
|
|
||||||
|
// can't have these two public since then a user might corrupt their length
|
||||||
input_sources: Vec<Option<&'ir id::Output>>,
|
input_sources: Vec<Option<&'ir id::Output>>,
|
||||||
output_targets: Vec<Option<&'ir Set<id::Input>>>,
|
output_targets: Vec<Option<&'ir Set<id::Input>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ir> InstructionRef<'ir> {
|
impl<'ir> Instruction<'ir> {
|
||||||
/// Where this instruction gets its inputs from.
|
/// Where this instruction gets its inputs from.
|
||||||
///
|
///
|
||||||
/// [`None`] means that this input is unfilled,
|
/// [`None`] means that this input is unfilled,
|
||||||
|
@ -331,23 +268,10 @@ impl<'ir> InstructionRef<'ir> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// would love to use ToOwned but Rust has no specialization yet
|
/// Constructs an [`id::Socket`] a bit more tersely.
|
||||||
// and it'd hurt a blanket impl of ToOwned otherwise
|
fn socket(id: &id::Instruction, idx: u16) -> id::Socket {
|
||||||
impl From<InstructionRef<'_>> for Instruction {
|
id::Socket {
|
||||||
fn from(source: InstructionRef<'_>) -> Self {
|
belongs_to: id.clone(),
|
||||||
Self {
|
idx: id::SocketIdx(idx),
|
||||||
id: source.id.clone(),
|
|
||||||
kind: source.kind.clone(),
|
|
||||||
input_sources: source
|
|
||||||
.input_sources
|
|
||||||
.into_iter()
|
|
||||||
.map(Option::<&_>::cloned)
|
|
||||||
.collect(),
|
|
||||||
output_targets: source
|
|
||||||
.output_targets
|
|
||||||
.into_iter()
|
|
||||||
.map(|outputs| outputs.map(Clone::clone).unwrap_or_default())
|
|
||||||
.collect(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,8 +77,7 @@ fn reverse_and_type_edges(edges: Map<Socket, Set<Socket>>) -> Map<id::Input, id:
|
||||||
let input = id::Input(input.into());
|
let input = id::Input(input.into());
|
||||||
let previous = rev_edges.insert(input, output.clone());
|
let previous = rev_edges.insert(input, output.clone());
|
||||||
if let Some(previous) = previous {
|
if let Some(previous) = previous {
|
||||||
// TODO: handle this with a TryFrom impl
|
panic!("two or more outputs referred to the same input {previous:#?} — handle me better later with a TryFrom impl");
|
||||||
panic!("two or more outputs referred to the same input {previous:#?}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
3
crates/ir/src/value/mod.rs
Normal file
3
crates/ir/src/value/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
pub enum DynamicValue {
|
||||||
|
Image(DynamicImage),
|
||||||
|
}
|
|
@ -7,75 +7,7 @@
|
||||||
subtitle: [don't worry, we're just dreaming],
|
subtitle: [don't worry, we're just dreaming],
|
||||||
)
|
)
|
||||||
|
|
||||||
= Term overview
|
= Evaluation stages
|
||||||
|
|
||||||
/ Processing stages:
|
|
||||||
Whole workflow of iOwO,
|
|
||||||
from the source to evaluation.
|
|
||||||
|
|
||||||
/ Source:
|
|
||||||
Nice textual representation of what iOwO is supposed to do.
|
|
||||||
Consists of instructions and pipelines.
|
|
||||||
|
|
||||||
/ Graph IR:
|
|
||||||
Intermediate machine-readable representation of the source.
|
|
||||||
Can be modified by the optimizer
|
|
||||||
and is evaluated or "ran" by the runtime.
|
|
||||||
|
|
||||||
/ Optimizer:
|
|
||||||
Simplifies the graph IR and makes it faster to run.
|
|
||||||
|
|
||||||
/ Runtime:
|
|
||||||
All-encompassing term for what's done
|
|
||||||
after a graph IR is optimized and fully ready to go.
|
|
||||||
|
|
||||||
/ Scheduler:
|
|
||||||
Looks at the graph IR
|
|
||||||
and decides which evaluator gets to run which part of it.
|
|
||||||
|
|
||||||
/ Evaluator:
|
|
||||||
One specific implementation of how to run
|
|
||||||
through the whole graph IR to get its results.
|
|
||||||
|
|
||||||
/ Function:
|
|
||||||
On the source level and before the graph IR,
|
|
||||||
anything that can be run with inputs,
|
|
||||||
possibly receiving outputs.
|
|
||||||
|
|
||||||
/ Instruction:
|
|
||||||
Function, but in the graph IR and at runtime.
|
|
||||||
Ask schrottkatze on why the differentiation is important.
|
|
||||||
|
|
||||||
/ Input:
|
|
||||||
Received by a function or instruction.
|
|
||||||
Different inputs may result in different behavior
|
|
||||||
and/or in different outputs.
|
|
||||||
|
|
||||||
/ Argument:
|
|
||||||
On the source level,
|
|
||||||
an input which is given ad-hoc
|
|
||||||
instead of provided through a pipeline.
|
|
||||||
|
|
||||||
/ Output:
|
|
||||||
Returned by a function or instruction,
|
|
||||||
and can be fed into other functions or instructions.
|
|
||||||
|
|
||||||
/ Consumer:
|
|
||||||
Function or instruction that takes at least 1 input.
|
|
||||||
|
|
||||||
/ Streamer:
|
|
||||||
Function or instruction that returns at least 1 output.
|
|
||||||
|
|
||||||
/ Modifier:
|
|
||||||
Function or instruction that is _both_ a consumer and a streamer.
|
|
||||||
|
|
||||||
/ Pipeline:
|
|
||||||
Any chain of streamers and consumers,
|
|
||||||
possibly with modifiers in-between,
|
|
||||||
that may branch out
|
|
||||||
and recombine arbitrarily.
|
|
||||||
|
|
||||||
= Processing stages
|
|
||||||
|
|
||||||
#graphics.stages-overview
|
#graphics.stages-overview
|
||||||
|
|
||||||
|
@ -86,7 +18,7 @@ This has a number of benefits and implications:
|
||||||
- Bugs are easier to trace down to one stage.
|
- Bugs are easier to trace down to one stage.
|
||||||
- Stages are also replacable, pluggable and usable somewhere else.
|
- Stages are also replacable, pluggable and usable somewhere else.
|
||||||
- For example,
|
- For example,
|
||||||
one could write a Just-In-Time compiler as a new evaluator to replace the runtime stage,
|
one could write a Just-In-Time compiler as a new executor to replace the runtime stage,
|
||||||
while preserving the source #arrow() graph IR step.
|
while preserving the source #arrow() graph IR step.
|
||||||
|
|
||||||
However, this also makes the architecture somewhat more complicated. So here we try our best to describe how each stage looks like. If you have any feedback, feel free to drop it on #link("https://forge.katzen.cafe/katzen-cafe/iowo/issues")[the issues in the repository]!
|
However, this also makes the architecture somewhat more complicated. So here we try our best to describe how each stage looks like. If you have any feedback, feel free to drop it on #link("https://forge.katzen.cafe/katzen-cafe/iowo/issues")[the issues in the repository]!
|
||||||
|
@ -220,17 +152,17 @@ Merges and simplifies functions in the graph IR.
|
||||||
== Runtime <runtime>
|
== Runtime <runtime>
|
||||||
|
|
||||||
Runs through all functions in the graph IR.
|
Runs through all functions in the graph IR.
|
||||||
It does not have any significantly different representation,
|
It does not have any significantly other representation,
|
||||||
and despite its name there's _no_ bytecode involved.
|
and despite its name there's _no_ bytecode involved.
|
||||||
|
|
||||||
|
Different runtimes are called executors.
|
||||||
|
Executors operate on instructions instead of functions.
|
||||||
|
|
||||||
=== Scheduler
|
=== Scheduler
|
||||||
|
|
||||||
Looks at the graph IR and decides when which evaluator gets to evaluate what.
|
Looks at the graph IR and decides when the VM should execute what.
|
||||||
|
|
||||||
=== Evaluator
|
=== VM <vm>
|
||||||
|
|
||||||
Runs instructions given to it in a specific way,
|
|
||||||
such as for example on the GPU using OpenCL.
|
|
||||||
|
|
||||||
= Open questions
|
= Open questions
|
||||||
|
|
||||||
|
|
|
@ -33,23 +33,22 @@
|
||||||
// this'd require wrapping the whole document in a show rule
|
// this'd require wrapping the whole document in a show rule
|
||||||
// at which point `query` doesn't find anything anymore
|
// at which point `query` doesn't find anything anymore
|
||||||
#let terms = (
|
#let terms = (
|
||||||
"processing stage",
|
|
||||||
"source",
|
"source",
|
||||||
"AST",
|
"AST",
|
||||||
"graph IR",
|
"graph IR",
|
||||||
"runtime",
|
"runtime",
|
||||||
|
"executor",
|
||||||
|
|
||||||
"optimizer",
|
"optimizer",
|
||||||
"scheduler",
|
"scheduler",
|
||||||
"evaluator",
|
"VM",
|
||||||
"evaluation",
|
|
||||||
|
|
||||||
"function",
|
"function",
|
||||||
"instruction",
|
"instruction",
|
||||||
|
"pipeline",
|
||||||
"input", "argument", "consumer",
|
"input", "argument", "consumer",
|
||||||
"output", "streamer",
|
"output", "streamer",
|
||||||
"modifier",
|
"modifier",
|
||||||
"pipeline",
|
|
||||||
)
|
)
|
||||||
// yes, the shadowing is intentional to avoid accidentally using the list
|
// yes, the shadowing is intentional to avoid accidentally using the list
|
||||||
#let terms = regex("\\b(" + terms.map(expand).join("|") + ")\\b")
|
#let terms = regex("\\b(" + terms.map(expand).join("|") + ")\\b")
|
||||||
|
|
3
justfile
3
justfile
|
@ -4,9 +4,6 @@ test:
|
||||||
#!/usr/bin/env nu
|
#!/usr/bin/env nu
|
||||||
cargo nextest run
|
cargo nextest run
|
||||||
|
|
||||||
|
|
||||||
alias doc := docs
|
|
||||||
|
|
||||||
# Compile all documentation as in proposals and design documents, placing them under `docs/compiled`.
|
# Compile all documentation as in proposals and design documents, placing them under `docs/compiled`.
|
||||||
docs:
|
docs:
|
||||||
#!/usr/bin/env nu
|
#!/usr/bin/env nu
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
(
|
(
|
||||||
instructions: {
|
instructions: {
|
||||||
0: Read((
|
0: Read((
|
||||||
source: File("testfiles/rails.png"),
|
source: File("testfiles/juan.jpg"),
|
||||||
|
format: Jpeg,
|
||||||
)),
|
)),
|
||||||
1: Write((
|
1: Write((
|
||||||
target: File("testfiles/gen/out.jpg"),
|
target: File("testfiles/out.png"),
|
||||||
format: Jpeg,
|
format: Png,
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
edges: {
|
edges: {
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
the testfile scripts will place generated images and media here. thank you for your understanding.
|
|
|
@ -1,11 +1,12 @@
|
||||||
(
|
(
|
||||||
instructions: {
|
instructions: {
|
||||||
0: Read((
|
0: Read((
|
||||||
source: File("testfiles/rails.png"),
|
source: File("testfiles/juan.jpg"),
|
||||||
|
format: Jpeg,
|
||||||
)),
|
)),
|
||||||
1: Filter(Invert),
|
1: Filter(Invert),
|
||||||
2: Write((
|
2: Write((
|
||||||
target: File("testfiles/gen/inverted.png"),
|
target: File("testfiles/inverted.png"),
|
||||||
format: Png,
|
format: Png,
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue