use std::collections::HashMap;
use indexmap::IndexMap;
use anyhow::{anyhow, Result};
use crate::graph::*;
use crate::node::*;
use crate::values::*;
#[derive(Clone, Debug)]
pub struct CompiledValue {
pub value: String,
pub dt: DataType,
}
impl core::fmt::Display for CompiledValue {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(&self.value)
}
}
impl CompiledValue {
pub fn convert(&mut self, to_dt: DataType) -> Result<()> {
self.value = match (self.dt, to_dt) {
(from, to) if from == to => {
return Ok(());
}
(DataType::Dynamic, _) => {
return Err(anyhow!("Dynamic compiled values are not supported"));
}
(DataType::DynamicVector, _) => {
return Err(anyhow!("Dynamic Vector compiled values are not supported"));
}
(DataType::DynamicMatrix, _) => {
return Err(anyhow!("Dynamic Matrix compiled values are not supported"));
}
(DataType::I32, DataType::U32) => format!("u32({})", self.value),
(DataType::I32, DataType::F32) => format!("f32({})", self.value),
(DataType::I32, DataType::Vec2) => format!("vec2<f32>({})", self.value),
(DataType::I32, DataType::Vec3) => format!("vec3<f32>({})", self.value),
(DataType::I32, DataType::Vec4) => format!("vec4<f32>({})", self.value),
(DataType::I32, DataType::Dynamic | DataType::DynamicVector) => {
return Ok(());
}
(DataType::U32, DataType::I32) => format!("i32({})", self.value),
(DataType::U32, DataType::F32) => format!("f32({})", self.value),
(DataType::U32, DataType::Vec2) => format!("vec2<f32>({})", self.value),
(DataType::U32, DataType::Vec3) => format!("vec3<f32>({})", self.value),
(DataType::U32, DataType::Vec4) => format!("vec4<f32>({})", self.value),
(DataType::U32, DataType::Dynamic | DataType::DynamicVector) => {
return Ok(());
}
(DataType::F32, DataType::I32) => format!("i32({})", self.value),
(DataType::F32, DataType::U32) => format!("u32({})", self.value),
(DataType::F32, DataType::Vec2) => format!("vec2<f32>({})", self.value),
(DataType::F32, DataType::Vec3) => format!("vec3<f32>({})", self.value),
(DataType::F32, DataType::Vec4) => format!("vec4<f32>({})", self.value),
(DataType::F32, DataType::Dynamic | DataType::DynamicVector) => {
return Ok(());
}
(DataType::Vec2, DataType::I32) => format!("i32({}.x)", self.value),
(DataType::Vec2, DataType::U32) => format!("u32({}.x)", self.value),
(DataType::Vec2, DataType::F32) => format!("f32({}.x)", self.value),
(DataType::Vec2, DataType::Vec3) => format!("vec3<f32>({}.xy, 0.)", self.value),
(DataType::Vec2, DataType::Vec4) => format!("vec4<f32>({}.xy, 0., 1.)", self.value),
(DataType::Vec2, DataType::Dynamic | DataType::DynamicVector) => {
return Ok(());
}
(DataType::Vec3, DataType::I32) => format!("i32({}.x)", self.value),
(DataType::Vec3, DataType::U32) => format!("u32({}.x)", self.value),
(DataType::Vec3, DataType::F32) => format!("f32({}.x)", self.value),
(DataType::Vec3, DataType::Vec2) => format!("vec2<f32>({}.xy)", self.value),
(DataType::Vec3, DataType::Vec4) => format!("vec4<f32>({}.xyz, 1.)", self.value),
(DataType::Vec3, DataType::Dynamic | DataType::DynamicVector) => {
return Ok(());
}
(DataType::Vec4, DataType::I32) => format!("i32({}.x)", self.value),
(DataType::Vec4, DataType::U32) => format!("u32({}.x)", self.value),
(DataType::Vec4, DataType::F32) => format!("f32({}.x)", self.value),
(DataType::Vec4, DataType::Vec2) => format!("vec2<f32>({}.xy)", self.value),
(DataType::Vec4, DataType::Vec3) => format!("vec4<f32>({}.xyz)", self.value),
(DataType::Vec4, DataType::Dynamic | DataType::DynamicVector) => {
return Ok(());
}
(DataType::Mat2, DataType::Mat3) => {
return Err(anyhow!("Promoting Mat2 to Mat3 not supported."));
}
(DataType::Mat2, DataType::Mat4) => {
return Err(anyhow!("Promoting Mat2 to Mat4 not supported."));
}
(DataType::Mat2, DataType::Dynamic | DataType::DynamicMatrix) => {
return Ok(());
}
(DataType::Mat3, DataType::Mat2) => {
format!("mat2x2<f32>({}[0].xy, {}[1].xy)", self.value, self.value)
}
(DataType::Mat3, DataType::Mat4) => {
return Err(anyhow!("Promoting Mat3 to Mat4 not supported."));
}
(DataType::Mat3, DataType::Dynamic | DataType::DynamicMatrix) => {
return Ok(());
}
(DataType::Mat4, DataType::Mat2) => {
format!("mat2x2<f32>({}[0].xy, {}[1].xy)", self.value, self.value)
}
(DataType::Mat4, DataType::Mat3) => format!(
"mat3x3<f32>({}[0].xyz, {}[1].xyz, {}[2].xyz)",
self.value, self.value, self.value
),
(DataType::Mat4, DataType::Dynamic | DataType::DynamicMatrix) => {
return Ok(());
}
(from_dt, to_dt) => {
return Err(anyhow!(
"Conversion from {from_dt:?} to {to_dt:?} not supported."
));
}
};
self.dt = to_dt;
Ok(())
}
}
#[derive(Clone, Debug)]
pub enum NodeOutput {
LazyCode(String, String, DataType),
Compiled(CompiledValue),
}
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Hash)]
pub struct CodeBlockId(u32);
#[derive(Clone, Default, Debug)]
pub struct CodeBlock {
code: Vec<String>,
variables: IndexMap<String, DataType>,
outputs: IndexMap<OutputId, NodeOutput>,
counter: usize,
}
impl CodeBlock {
pub fn add_local(&mut self, prefix: &str, code: String, dt: DataType) -> String {
self.counter += 1;
let idx = self.counter;
let name = format!("{prefix}_{idx}");
self.append(format!(
r#"
let {name} = {code};"#
));
self.variables.insert(name.clone(), dt);
name
}
pub fn add_output(&mut self, id: OutputId, prefix: &str, code: String, dt: DataType) {
self
.outputs
.insert(id, NodeOutput::LazyCode(prefix.to_string(), code, dt));
}
pub fn resolve_output(&mut self, id: OutputId) -> Result<CompiledValue> {
let output = self
.outputs
.get(&id)
.ok_or_else(|| anyhow!("Tried to resolve an unknown output: {id:?}"))?;
let value = match output.clone() {
NodeOutput::LazyCode(prefix, code, dt) => {
let name = self.add_local(&prefix, code, dt);
let value = CompiledValue { value: name, dt };
self.outputs.insert(id, NodeOutput::Compiled(value.clone()));
Ok(value)
}
NodeOutput::Compiled(value) => Ok(value),
};
value
}
pub fn append_output(&mut self, node: NodeId, code: String) {
let id = OutputId { node, idx: 0 };
let dt = DataType::Vec4;
let name = self.add_local("out", code, dt);
let value = CompiledValue { value: name, dt };
self.outputs.insert(id, NodeOutput::Compiled(value));
}
pub fn append(&mut self, code: String) {
self.code.push(code);
}
pub fn clear(&mut self) {
self.code.clear();
self.variables.clear();
self.outputs.clear();
self.counter = 0;
}
pub fn dump(&self) -> String {
self.code.join("")
}
}
#[derive(Default, Debug)]
pub struct NodeGraphCompile {
next_id: CodeBlockId,
blocks: IndexMap<CodeBlockId, CodeBlock>,
names: HashMap<String, CodeBlockId>,
block_order: Vec<CodeBlockId>,
stack: Vec<CodeBlockId>,
compiled: HashMap<NodeId, bool>,
}
impl NodeGraphCompile {
pub fn new() -> Self {
Self::default()
}
pub fn define_block(&mut self, name: &str) -> CodeBlockId {
if let Some(&id) = self.names.get(name) {
return id;
}
let id = CodeBlockId(self.next_id.0 + 1);
self.next_id = id;
self.blocks.insert(id, CodeBlock::default());
self.block_order.push(id);
self.names.insert(name.to_string(), id);
id
}
pub fn current_block(&mut self) -> Result<&mut CodeBlock> {
self
.stack
.last()
.and_then(|&id| self.blocks.get_mut(&id))
.ok_or_else(|| anyhow!("No current block on stack"))
}
pub fn push(&mut self, id: CodeBlockId) {
self.stack.push(id);
}
pub fn push_new_block(&mut self, name: &str) -> CodeBlockId {
let id = self.define_block(name);
self.push(id);
id
}
pub fn pop(&mut self, expect: Option<CodeBlockId>) -> Result<Option<CodeBlockId>> {
let id = self.stack.pop();
if id != expect {
Err(anyhow!("Stack corruption detected."))
} else {
Ok(id)
}
}
pub fn get_block(&self, name: &str) -> Option<&CodeBlock> {
self
.names
.get(name)
.and_then(|&id| self.get_block_by_id(id))
}
pub fn get_block_mut(&mut self, name: &str) -> Option<&mut CodeBlock> {
self
.names
.get(name)
.copied()
.and_then(|id| self.get_block_by_id_mut(id))
}
pub fn get_block_by_id(&self, id: CodeBlockId) -> Option<&CodeBlock> {
self.blocks.get(&id)
}
pub fn get_block_by_id_mut(&mut self, id: CodeBlockId) -> Option<&mut CodeBlock> {
self.blocks.get_mut(&id)
}
pub fn add_local(&mut self, prefix: &str, code: String, dt: DataType) -> Result<String> {
let block = self.current_block()?;
Ok(block.add_local(prefix, code, dt))
}
pub fn add_output(
&mut self,
id: OutputId,
prefix: &str,
code: String,
dt: DataType,
) -> Result<()> {
let block = self.current_block()?;
block.add_output(id, prefix, code, dt);
Ok(())
}
pub fn append_code(&mut self, name: &str, code: String) -> Result<()> {
match self.get_block_mut(name) {
Some(block) => {
block.append(code);
Ok(())
}
None => Err(anyhow!("Undefined block: {name:?}")),
}
}
pub fn dump(&self) -> String {
let mut output = Vec::new();
for block in self.blocks.values() {
output.push(block.dump());
}
output.join("")
}
pub fn clear(&mut self) {
for block in self.blocks.values_mut() {
block.clear();
}
}
pub fn resolve_output(&mut self, graph: &NodeGraph, id: OutputId) -> Result<CompiledValue> {
self.compile_node(graph, id.node)?;
let block = self.current_block()?;
block.resolve_output(id)
}
pub fn compile_graph(&mut self, graph: &NodeGraph) -> Result<()> {
let id = graph
.output()
.ok_or_else(|| anyhow!("Graph missing output node"))?;
self.compile_node(graph, id)
}
pub fn compile_node(&mut self, graph: &NodeGraph, id: NodeId) -> Result<()> {
let compiled = self.compiled.insert(id, true).unwrap_or_default();
if compiled {
return Ok(());
}
let node = graph.get(id)?;
node.compile(graph, self, id)
}
}