diff --git a/app/Dockerfile b/app/Dockerfile index a059a66..3b43fbd 100644 --- a/app/Dockerfile +++ b/app/Dockerfile @@ -39,5 +39,6 @@ server { EOF COPY --from=builder /app/app/build /app +COPY --from=builder /app/packages/ui/build /app/ui EXPOSE 80 diff --git a/app/src/lib/graph-interface/graph-manager.svelte.ts b/app/src/lib/graph-interface/graph-manager.svelte.ts index 65d7012..78bd296 100644 --- a/app/src/lib/graph-interface/graph-manager.svelte.ts +++ b/app/src/lib/graph-interface/graph-manager.svelte.ts @@ -606,11 +606,14 @@ export class GraphManager extends EventEmitter<{ return; } + const fromType = from.state.type || this.registry.getNode(from.type); + const toType = to.state.type || this.registry.getNode(to.type); + // check if socket types match - const fromSocketType = from.state?.type?.outputs?.[fromSocket]; - const toSocketType = [to.state?.type?.inputs?.[toSocket]?.type]; - if (to.state?.type?.inputs?.[toSocket]?.accepts) { - toSocketType.push(...(to?.state?.type?.inputs?.[toSocket]?.accepts || [])); + const fromSocketType = fromType?.outputs?.[fromSocket]; + const toSocketType = [toType?.inputs?.[toSocket]?.type]; + if (toType?.inputs?.[toSocket]?.accepts) { + toSocketType.push(...(toType?.inputs?.[toSocket]?.accepts || [])); } if (!areSocketsCompatible(fromSocketType, toSocketType)) { @@ -733,9 +736,9 @@ export class GraphManager extends EventEmitter<{ } getPossibleSockets({ node, index }: Socket): [NodeInstance, string | number][] { - const nodeType = node?.state?.type; - console.log({ node: $state.snapshot(node), index, nodeType }); + const nodeType = this.registry.getNode(node.type); if (!nodeType) return []; + console.log({ index }); const sockets: [NodeInstance, string | number][] = []; @@ -750,7 +753,7 @@ export class GraphManager extends EventEmitter<{ const ownType = nodeType?.inputs?.[index].type; for (const node of nodes) { - const nodeType = node?.state?.type; + const nodeType = this.registry.getNode(node.type); const inputs = nodeType?.outputs; if (!inputs) continue; for (let index = 0; index < inputs.length; index++) { @@ -778,7 +781,7 @@ export class GraphManager extends EventEmitter<{ const ownType = nodeType.outputs?.[index]; for (const node of nodes) { - const inputs = node?.state?.type?.inputs; + const inputs = this.registry.getNode(node.type)?.inputs; if (!inputs) continue; for (const key in inputs) { const otherType = [inputs[key].type]; diff --git a/app/src/lib/graph-interface/node/NodeHeader.svelte b/app/src/lib/graph-interface/node/NodeHeader.svelte index a17769a..83478e5 100644 --- a/app/src/lib/graph-interface/node/NodeHeader.svelte +++ b/app/src/lib/graph-interface/node/NodeHeader.svelte @@ -43,7 +43,7 @@
- {node.type.split("/").pop()} + {node.type.split("/").pop()} ({node.id})
= new Map(); private offset = 0; + private isRunning = false; private memory = new WebAssembly.Memory({ initial: 1024, maximum: 8192 }); + private memoryView = new Int32Array(); + + results: Record = {}; + inputPtrs: Record = {}; seed = 123123; perf?: PerformanceStore; + public getMemory() { + return new Int32Array(this.memory.buffer); + } + constructor( private registry: NodeRegistry, public cache?: SyncCache @@ -129,7 +147,8 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor { const outputNode = graphNodes.find((node) => node.type.endsWith('/output')); if (!outputNode) { - throw new Error('No output node found'); + // throw new Error('No output node found'); + console.log('No output node found'); } const nodeMap = new Map( @@ -151,7 +170,7 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor { const nodes = []; // loop through all the nodes and assign each nodes its depth - const stack = [outputNode]; + const stack = [outputNode || graphNodes[0]]; while (stack.length) { const node = stack.pop(); if (!node) continue; @@ -166,14 +185,15 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor { return [outputNode, nodes] as const; } - private writeToMemory(v: number | number[] | Int32Array) { + private writeToMemory(v: number | number[] | Int32Array, title?: string) { let length = 1; - const view = new Int32Array(this.memory.buffer); + if (typeof v === 'number') { - view[this.offset] = v; + this.memoryView[this.offset] = v; + console.log('MEM: writing number', v, ' to', this.offset); length = 1; } else { - view.set(v, this.offset); + this.memoryView.set(v, this.offset); length = v.length; } @@ -184,15 +204,25 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor { return { start, - end + end, + _title: title }; } + private printMemory() { + this.memoryView = new Int32Array(this.memory.buffer); + console.log('MEMORY', this.memoryView.slice(0, 10)); + } + async execute(graph: Graph, _settings: Record) { this.offset = 0; + this.inputPtrs = {}; + this.results = {}; + if (this.isRunning) return undefined as unknown as Int32Array; + this.isRunning = true; // Then we add some metadata to the graph - const [outputNode, nodes] = await this.addMetaData(graph); + const [_outputNode, nodes] = await this.addMetaData(graph); /* * Here we sort the nodes into buckets, which we then execute one by one @@ -210,14 +240,14 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor { (a, b) => (b.state?.depth || 0) - (a.state?.depth || 0) ); - // here we store the intermediate results of the nodes - const results: Record = {}; - for (const node of sortedNodes) { const node_type = this.nodes.get(node.type)!; - console.log('EXECUTING NODE', node_type.definition.id); - console.log(node_type.definition.inputs); + console.log('---------------'); + console.log('STARTING NODE EXECUTION', node_type.definition.id); + this.printMemory(); + + // console.log(node_type.definition.inputs); const inputs = Object.entries(node_type.definition.inputs || {}).map( ([key, input]) => { // We should probably initially write this to memory @@ -225,6 +255,8 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor { return this.writeToMemory(this.seed); } + const title = `${node.id}.${key}`; + // We should probably initially write this to memory // If the input is linked to a setting, we use that value // if (input.setting) { @@ -234,56 +266,85 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor { // check if the input is connected to another node const inputNode = node.state.inputNodes[key]; if (inputNode) { - if (results[inputNode.id] === undefined) { + if (this.results[inputNode.id] === undefined) { throw new Error( `Node ${node.type} is missing input from node ${inputNode.type}` ); } - return results[inputNode.id]; + return this.results[inputNode.id]; } // If the value is stored in the node itself, we use that value if (node.props?.[key] !== undefined) { - return this.writeToMemory(getValue(input, node.props[key])); + const value = getValue(input, node.props[key]); + console.log(`Writing prop for ${node.id} -> ${key} to memory`, node.props[key], value); + return this.writeToMemory(value, title); } - return this.writeToMemory(getValue(input)); + return this.writeToMemory(getValue(input), title); } ); + this.printMemory(); + if (!node_type || !node.state || !node_type.execute) { log.warn(`Node ${node.id} has no definition`); continue; } + this.inputPtrs[node.id] = inputs; const args = inputs.map(s => [s.start, s.end]).flat(); - console.log('ARGS', args); + console.log('ARGS', inputs); + this.printMemory(); try { - const bytesWritten = node_type.execute(this.offset, args); - results[node.id] = { - start: this.offset, - end: this.offset + bytesWritten - }; - this.offset += bytesWritten; + console.log('EXECUTING NODE, writing output of node to ->', this.offset); + const bytesWritten = node_type.execute(this.offset * 4, args.map(a => a * 4)); + const view = new Int32Array(this.memory.buffer); + + const input = view.slice(args[0], args[1]); + const output = view.slice(this.offset, this.offset + bytesWritten / 4); + console.log('RESULT', { args, input, output }); + + // Optimization + // If the input arg is the same length as the output arg + if ( + args.length === 2 && args[1] - args[0] == bytesWritten / 4 && compareInt32(input, output) + ) { + console.log('INPUT === OUTPUT'); + this.results[node.id] = { + start: args[0], + end: args[1], + _title: `${node.id} ->` + }; + } else { + this.results[node.id] = { + start: this.offset, + end: this.offset + bytesWritten / 4, + _title: `${node.id} ->` + }; + this.offset += bytesWritten / 4; + } console.log('FINISHED EXECUTION', { bytesWritten, offset: this.offset }); } catch (e) { - console.error(e); + console.error(`Failed to execute node ${node.type}/${node.id}`, e); + this.isRunning = false; } } - const mem = new Int32Array(this.memory.buffer); - console.log('OUT', mem.slice(0, 10)); + // const mem = new Int32Array(this.memory.buffer); + // console.log('OUT', mem.slice(0, 10)); // return the result of the parent of the output node - const res = results[outputNode.id]; + // const res = this.results[outputNode.id]; this.perf?.endPoint('runtime'); - return res as unknown as Int32Array; + this.isRunning = false; + return undefined as unknown as Int32Array; } getPerformanceData() { diff --git a/app/src/routes/dev/+page.svelte b/app/src/routes/dev/+page.svelte index 24e01b5..0e8ae01 100644 --- a/app/src/routes/dev/+page.svelte +++ b/app/src/routes/dev/+page.svelte @@ -1,16 +1,14 @@ + ev.key === "r" && handleResult()} +/> + - {#if result} -
{JSON.stringify(decodeNestedArray(result))}
+ {#if visibleRows?.length} + + + + + + + + + + + {#each visibleRows as r, i} + {@const index = i + start} + {@const ptr = ptrs[i]} + + + + + + + {/each} + +
iPtrsValueFloat
{index} + {ptr?._title} + + (rowIsFloat.value[index] = !rowIsFloat.value[index])} + > + {decodeValue(r, rowIsFloat.value[index])} + + + (rowIsFloat.value[index] = !rowIsFloat.value[index])} + /> +
+ {/if}
+ {#if isCalculating} + + {/if} + handleSave(g)} - onresult={(result) => handleResult(result)} + onresult={(res) => handleResult(res)} />
diff --git a/nodes/max/plantarium/box/src/lib.rs b/nodes/max/plantarium/box/src/lib.rs index e4dc8f4..5802fd1 100644 --- a/nodes/max/plantarium/box/src/lib.rs +++ b/nodes/max/plantarium/box/src/lib.rs @@ -3,18 +3,17 @@ use nodarium_macros::nodarium_execute; use nodarium_utils::{ encode_float, evaluate_float, geometry::calculate_normals,log, split_args, wrap_arg, + read_i32_slice }; nodarium_definition_file!("src/input.json"); #[nodarium_execute] -pub fn execute(input: &[i32]) -> Vec { +pub fn execute(size: (i32, i32)) -> Vec { - let args = split_args(input); + let args = read_i32_slice(size); - log!("WASM(cube): input: {:?} -> {:?}", input, args); - - let size = evaluate_float(args[0]); + let size = evaluate_float(&args); let p = encode_float(size); let n = encode_float(-size); @@ -77,8 +76,6 @@ pub fn execute(input: &[i32]) -> Vec { let res = wrap_arg(&cube_geometry); - log!("WASM(box): output: {:?}", res); - res } diff --git a/nodes/max/plantarium/branch/src/lib.rs b/nodes/max/plantarium/branch/src/lib.rs index 6f9b60d..b5d296a 100644 --- a/nodes/max/plantarium/branch/src/lib.rs +++ b/nodes/max/plantarium/branch/src/lib.rs @@ -1,5 +1,6 @@ use nodarium_macros::nodarium_definition_file; use nodarium_macros::nodarium_execute; +use nodarium_utils::read_i32_slice; use nodarium_utils::{ concat_arg_vecs, evaluate_float, evaluate_int, geometry::{ @@ -13,15 +14,25 @@ use std::f32::consts::PI; nodarium_definition_file!("src/input.json"); #[nodarium_execute] -pub fn execute(input: &[i32]) -> Vec { - let args = split_args(input); - - let paths = split_args(args[0]); +pub fn execute( + path: (i32, i32), + length: (i32, i32), + thickness: (i32, i32), + offset_single: (i32, i32), + lowest_branch: (i32, i32), + highest_branch: (i32, i32), + depth: (i32, i32), + amount: (i32, i32), + resolution_curve: (i32, i32), + rotation: (i32, i32), +) -> Vec { + let arg = read_i32_slice(path); + let paths = split_args(arg.as_slice()); let mut output: Vec> = Vec::new(); - let resolution = evaluate_int(args[8]).max(4) as usize; - let depth = evaluate_int(args[6]); + let resolution = evaluate_int(read_i32_slice(resolution_curve).as_slice()).max(4) as usize; + let depth = evaluate_int(read_i32_slice(depth).as_slice()); let mut max_depth = 0; for path_data in paths.iter() { @@ -40,18 +51,18 @@ pub fn execute(input: &[i32]) -> Vec { let path = wrap_path(path_data); - let branch_amount = evaluate_int(args[7]).max(1); + let branch_amount = evaluate_int(read_i32_slice(amount).as_slice()).max(1); - let lowest_branch = evaluate_float(args[4]); - let highest_branch = evaluate_float(args[5]); + let lowest_branch = evaluate_float(read_i32_slice(lowest_branch).as_slice()); + let highest_branch = evaluate_float(read_i32_slice(highest_branch).as_slice()); for i in 0..branch_amount { let a = i as f32 / (branch_amount - 1).max(1) as f32; - let length = evaluate_float(args[1]); - let thickness = evaluate_float(args[2]); + let length = evaluate_float(read_i32_slice(length).as_slice()); + let thickness = evaluate_float(read_i32_slice(thickness).as_slice()); let offset_single = if i % 2 == 0 { - evaluate_float(args[3]) + evaluate_float(read_i32_slice(offset_single).as_slice()) } else { 0.0 }; @@ -65,7 +76,8 @@ pub fn execute(input: &[i32]) -> Vec { root_alpha + (offset_single - 0.5) * 6.0 / resolution as f32, ); - let rotation_angle = (evaluate_float(args[9]) * PI / 180.0) * i as f32; + let rotation_angle = + (evaluate_float(read_i32_slice(rotation).as_slice()) * PI / 180.0) * i as f32; // check if diration contains NaN if orthogonal[0].is_nan() || orthogonal[1].is_nan() || orthogonal[2].is_nan() { diff --git a/nodes/max/plantarium/float/src/lib.rs b/nodes/max/plantarium/float/src/lib.rs index 6872713..77da6ca 100644 --- a/nodes/max/plantarium/float/src/lib.rs +++ b/nodes/max/plantarium/float/src/lib.rs @@ -1,11 +1,11 @@ use nodarium_macros::nodarium_definition_file; use nodarium_macros::nodarium_execute; -use nodarium_utils::log; +use nodarium_utils::{ log, read_f32, encode_float }; nodarium_definition_file!("src/input.json"); #[nodarium_execute] -pub fn execute(_value: *const i32) -> Vec { - log!("Duuuude"); - vec![32] +pub fn execute(a: (i32, i32)) -> Vec { + let a_val = read_f32(a.0); + vec![encode_float(a_val)] } diff --git a/nodes/max/plantarium/gravity/src/lib.rs b/nodes/max/plantarium/gravity/src/lib.rs index 4453934..e656153 100644 --- a/nodes/max/plantarium/gravity/src/lib.rs +++ b/nodes/max/plantarium/gravity/src/lib.rs @@ -1,6 +1,7 @@ use glam::Vec3; use nodarium_macros::nodarium_definition_file; use nodarium_macros::nodarium_execute; +use nodarium_utils::read_i32_slice; use nodarium_utils::{ concat_args, evaluate_float, evaluate_int, geometry::{wrap_path, wrap_path_mut}, @@ -14,13 +15,17 @@ fn lerp_vec3(a: Vec3, b: Vec3, t: f32) -> Vec3 { } #[nodarium_execute] -pub fn execute(input: &[i32]) -> Vec { +pub fn execute( + plant: (i32, i32), + strength: (i32, i32), + curviness: (i32, i32), + depth: (i32, i32), +) -> Vec { reset_call_count(); - let args = split_args(input); - - let plants = split_args(args[0]); - let depth = evaluate_int(args[3]); + let arg = read_i32_slice(plant); + let plants = split_args(arg.as_slice()); + let depth = evaluate_int(read_i32_slice(depth).as_slice()); let mut max_depth = 0; for path_data in plants.iter() { @@ -55,9 +60,9 @@ pub fn execute(input: &[i32]) -> Vec { let length = direction.length(); - let curviness = evaluate_float(args[2]); - let strength = - evaluate_float(args[1]) / curviness.max(0.0001) * evaluate_float(args[1]); + let str = evaluate_float(read_i32_slice(strength).as_slice()); + let curviness = evaluate_float(read_i32_slice(curviness).as_slice()); + let strength = str / curviness.max(0.0001) * str; log!( "length: {}, curviness: {}, strength: {}", diff --git a/nodes/max/plantarium/instance/src/lib.rs b/nodes/max/plantarium/instance/src/lib.rs index ca98ac9..59e103f 100644 --- a/nodes/max/plantarium/instance/src/lib.rs +++ b/nodes/max/plantarium/instance/src/lib.rs @@ -1,23 +1,29 @@ use glam::{Mat4, Quat, Vec3}; -use nodarium_macros::nodarium_execute; use nodarium_macros::nodarium_definition_file; +use nodarium_macros::nodarium_execute; +use nodarium_utils::read_i32_slice; use nodarium_utils::{ concat_args, evaluate_float, evaluate_int, - geometry::{ - create_instance_data, wrap_geometry_data, wrap_instance_data, wrap_path, - }, + geometry::{create_instance_data, wrap_geometry_data, wrap_instance_data, wrap_path}, log, split_args, }; nodarium_definition_file!("src/input.json"); #[nodarium_execute] -pub fn execute(input: &[i32]) -> Vec { - let args = split_args(input); - let mut inputs = split_args(args[0]); +pub fn execute( + plant: (i32, i32), + geometry: (i32, i32), + amount: (i32, i32), + lowest_instance: (i32, i32), + highest_instance: (i32, i32), + depth: (i32, i32), +) -> Vec { + let arg = read_i32_slice(plant); + let mut inputs = split_args(arg.as_slice()); log!("WASM(instance): inputs: {:?}", inputs); - let mut geo_data = args[1].to_vec(); + let mut geo_data = read_i32_slice(geometry); let geo = wrap_geometry_data(&mut geo_data); let mut transforms: Vec = Vec::new(); @@ -30,17 +36,17 @@ pub fn execute(input: &[i32]) -> Vec { max_depth = max_depth.max(path_data[3]); } - let depth = evaluate_int(args[5]); + let depth = evaluate_int(read_i32_slice(depth).as_slice()); for path_data in inputs.iter() { if path_data[3] < (max_depth - depth + 1) { continue; } - let amount = evaluate_int(args[2]); + let amount = evaluate_int(read_i32_slice(amount).as_slice()); - let lowest_instance = evaluate_float(args[3]); - let highest_instance = evaluate_float(args[4]); + let lowest_instance = evaluate_float(read_i32_slice(lowest_instance).as_slice()); + let highest_instance = evaluate_float(read_i32_slice(highest_instance).as_slice()); let path = wrap_path(path_data); diff --git a/nodes/max/plantarium/math/src/lib.rs b/nodes/max/plantarium/math/src/lib.rs index 27bd4ce..9ebc3a3 100644 --- a/nodes/max/plantarium/math/src/lib.rs +++ b/nodes/max/plantarium/math/src/lib.rs @@ -1,24 +1,13 @@ use nodarium_macros::nodarium_definition_file; use nodarium_macros::nodarium_execute; -use nodarium_utils::{read_f32, read_i32, log}; +use nodarium_utils::{concat_arg_vecs, encode_float, log, read_i32_slice}; nodarium_definition_file!("src/input.json"); #[nodarium_execute] -pub fn execute(op_type: *const i32, a: *const i32, b: *const i32) -> Vec { - let op = unsafe { read_i32(op_type) }; - let a_val = unsafe { read_f32(a) }; - let b_val = unsafe { read_f32(b) }; - - log!("op_type: {:?}", op); - - let result = match op { - 0 => a_val + b_val, - 1 => a_val - b_val, - 2 => a_val * b_val, - 3 => a_val / b_val, - _ => 0.0, - }; - - vec![result.to_bits() as i32] +pub fn execute(op_type: (i32, i32), a: (i32, i32), b: (i32, i32)) -> Vec { + let op = read_i32_slice(op_type); + let a_val = read_i32_slice(a); + let b_val = read_i32_slice(b); + concat_arg_vecs(vec![vec![0], op, a_val, b_val]) } diff --git a/nodes/max/plantarium/noise/src/lib.rs b/nodes/max/plantarium/noise/src/lib.rs index b54f684..7beb625 100644 --- a/nodes/max/plantarium/noise/src/lib.rs +++ b/nodes/max/plantarium/noise/src/lib.rs @@ -1,7 +1,8 @@ use nodarium_macros::nodarium_definition_file; use nodarium_macros::nodarium_execute; +use nodarium_utils::read_i32_slice; use nodarium_utils::{ - concat_args, evaluate_float, evaluate_int, evaluate_vec3, geometry::wrap_path_mut, + concat_args, evaluate_float, evaluate_int, evaluate_vec3, geometry::wrap_path_mut, read_i32, reset_call_count, split_args, }; use noise::{HybridMulti, MultiFractal, NoiseFn, OpenSimplex}; @@ -13,23 +14,31 @@ fn lerp(a: f32, b: f32, t: f32) -> f32 { } #[nodarium_execute] -pub fn execute(input: &[i32]) -> Vec { +pub fn execute( + plant: (i32, i32), + scale: (i32, i32), + strength: (i32, i32), + fix_bottom: (i32, i32), + seed: (i32, i32), + directional_strength: (i32, i32), + depth: (i32, i32), + octaves: (i32, i32), +) -> Vec { reset_call_count(); - let args = split_args(input); + let arg = read_i32_slice(plant); + let plants = split_args(arg.as_slice()); + let scale = (evaluate_float(read_i32_slice(scale).as_slice()) * 0.1) as f64; + let strength = evaluate_float(read_i32_slice(strength).as_slice()); + let fix_bottom = evaluate_float(read_i32_slice(fix_bottom).as_slice()); - let plants = split_args(args[0]); - let scale = (evaluate_float(args[1]) * 0.1) as f64; - let strength = evaluate_float(args[2]); - let fix_bottom = evaluate_float(args[3]); + let seed = read_i32(seed.0); - let seed = args[4][0]; + let directional_strength = evaluate_vec3(read_i32_slice(directional_strength).as_slice()); - let directional_strength = evaluate_vec3(args[5]); + let depth = evaluate_int(read_i32_slice(depth).as_slice()); - let depth = evaluate_int(args[6]); - - let octaves = evaluate_int(args[7]); + let octaves = evaluate_int(read_i32_slice(octaves).as_slice()); let noise_x: HybridMulti = HybridMulti::new(seed as u32 + 1).set_octaves(octaves as usize); diff --git a/nodes/max/plantarium/output/src/lib.rs b/nodes/max/plantarium/output/src/lib.rs index 90d1cd6..9b2f4b3 100644 --- a/nodes/max/plantarium/output/src/lib.rs +++ b/nodes/max/plantarium/output/src/lib.rs @@ -1,9 +1,14 @@ use nodarium_macros::nodarium_definition_file; use nodarium_macros::nodarium_execute; +use nodarium_utils::log; +use nodarium_utils::read_i32_slice; nodarium_definition_file!("src/input.json"); #[nodarium_execute] -pub fn execute(_input: *const i32, _res: *const i32) -> Vec { - return vec![0]; +pub fn execute(input: (i32, i32), _res: (i32, i32)) -> Vec { + log!("HERE"); + let mut vecs = read_i32_slice(input); + vecs.push(42); + vecs } diff --git a/nodes/max/plantarium/random/src/definition.json b/nodes/max/plantarium/random/src/input.json similarity index 100% rename from nodes/max/plantarium/random/src/definition.json rename to nodes/max/plantarium/random/src/input.json diff --git a/nodes/max/plantarium/random/src/lib.rs b/nodes/max/plantarium/random/src/lib.rs index 3937c50..3ef724d 100644 --- a/nodes/max/plantarium/random/src/lib.rs +++ b/nodes/max/plantarium/random/src/lib.rs @@ -1,11 +1,16 @@ use nodarium_macros::nodarium_definition_file; use nodarium_macros::nodarium_execute; -use nodarium_utils::{concat_args, split_args}; +use nodarium_utils::concat_arg_vecs; +use nodarium_utils::read_i32_slice; -nodarium_definition_file!("src/definition.json"); +nodarium_definition_file!("src/input.json"); #[nodarium_execute] -pub fn execute(args: &[i32]) -> Vec { - let args = split_args(args); - concat_args(vec![&[1], args[0], args[1], args[2]]) +pub fn execute(min: (i32, i32), max: (i32, i32), seed: (i32, i32)) -> Vec { + concat_arg_vecs(vec![ + vec![1], + read_i32_slice(min), + read_i32_slice(max), + read_i32_slice(seed), + ]) } diff --git a/nodes/max/plantarium/rotate/src/lib.rs b/nodes/max/plantarium/rotate/src/lib.rs index ad2bd70..366c891 100644 --- a/nodes/max/plantarium/rotate/src/lib.rs +++ b/nodes/max/plantarium/rotate/src/lib.rs @@ -1,23 +1,26 @@ use glam::{Mat4, Vec3}; use nodarium_macros::nodarium_definition_file; use nodarium_macros::nodarium_execute; +use nodarium_utils::read_i32_slice; use nodarium_utils::{ - concat_args, evaluate_float, evaluate_int, geometry::wrap_path_mut, log, - split_args, + concat_args, evaluate_float, evaluate_int, geometry::wrap_path_mut, log, split_args, }; nodarium_definition_file!("src/input.json"); #[nodarium_execute] -pub fn execute(input: &[i32]) -> Vec { +pub fn execute( + plant: (i32, i32), + axis: (i32, i32), + angle: (i32, i32), + spread: (i32, i32), +) -> Vec { + log!("DEBUG args: {:?}", plant); - log!("DEBUG args: {:?}", input); - - let args = split_args(input); - - let plants = split_args(args[0]); - let axis = evaluate_int(args[1]); // 0 =x, 1 = y, 2 = z - let spread = evaluate_int(args[3]); + let arg = read_i32_slice(plant); + let plants = split_args(arg.as_slice()); + let axis = evaluate_int(read_i32_slice(axis).as_slice()); // 0 =x, 1 = y, 2 = z + let spread = evaluate_int(read_i32_slice(spread).as_slice()); let output: Vec> = plants .iter() @@ -32,7 +35,7 @@ pub fn execute(input: &[i32]) -> Vec { let path = wrap_path_mut(&mut path_data); - let angle = evaluate_float(args[2]); + let angle = evaluate_float(read_i32_slice(angle).as_slice()); let origin = [path.points[0], path.points[1], path.points[2]]; diff --git a/nodes/max/plantarium/stem/src/lib.rs b/nodes/max/plantarium/stem/src/lib.rs index 32e57e6..d962126 100644 --- a/nodes/max/plantarium/stem/src/lib.rs +++ b/nodes/max/plantarium/stem/src/lib.rs @@ -4,29 +4,28 @@ use nodarium_utils::{ evaluate_float, evaluate_int, evaluate_vec3, geometry::{create_multiple_paths, wrap_multiple_paths}, log, reset_call_count, split_args, + read_i32_slice, read_i32, }; nodarium_definition_file!("src/input.json"); #[nodarium_execute] -pub fn execute(input: &[i32]) -> Vec { +pub fn execute(origin: (i32, i32), _amount: (i32,i32), length: (i32, i32), thickness: (i32, i32), resolution_curve: (i32, i32)) -> Vec { reset_call_count(); - let args = split_args(input); + let amount = evaluate_int(read_i32_slice(_amount).as_slice()) as usize; + let path_resolution = read_i32(resolution_curve.0) as usize; - let amount = evaluate_int(args[1]) as usize; - let path_resolution = evaluate_int(args[4]) as usize; - - log!("stem args: {:?}", args); + log!("stem args: amount={:?}", amount); let mut stem_data = create_multiple_paths(amount, path_resolution, 1); let mut stems = wrap_multiple_paths(&mut stem_data); for stem in stems.iter_mut() { - let origin = evaluate_vec3(args[0]); - let length = evaluate_float(args[2]); - let thickness = evaluate_float(args[3]); + let origin = evaluate_vec3(read_i32_slice(origin).as_slice()); + let length = evaluate_float(read_i32_slice(length).as_slice()); + let thickness = evaluate_float(read_i32_slice(thickness).as_slice()); let amount_points = stem.points.len() / 4; for i in 0..amount_points { diff --git a/nodes/max/plantarium/triangle/src/lib.rs b/nodes/max/plantarium/triangle/src/lib.rs index e14533c..065c37d 100644 --- a/nodes/max/plantarium/triangle/src/lib.rs +++ b/nodes/max/plantarium/triangle/src/lib.rs @@ -1,45 +1,48 @@ use nodarium_macros::nodarium_definition_file; use nodarium_macros::nodarium_execute; -use nodarium_utils::{ - decode_float, encode_float, evaluate_int, split_args, wrap_arg, log -}; +use nodarium_utils::read_i32_slice; +use nodarium_utils::{decode_float, encode_float, evaluate_int, log, split_args, wrap_arg}; nodarium_definition_file!("src/input.json"); #[nodarium_execute] -pub fn execute(input: &[i32]) -> Vec { - - let args = split_args(input); - - let size = evaluate_int(args[0]); +pub fn execute(size: (i32, i32)) -> Vec { + let size = evaluate_int(read_i32_slice(size).as_slice()); let decoded = decode_float(size); let negative_size = encode_float(-decoded); - log!("WASM(triangle): input: {:?} -> {}", args[0],decoded); + log!("WASM(triangle): input: {:?} -> {}", size, decoded); // [[1,3, x, y, z, x, y,z,x,y,z]]; wrap_arg(&[ - 1, // 1: geometry - 3, // 3 vertices - 1, // 1 face + 1, // 1: geometry + 3, // 3 vertices + 1, // 1 face // this are the indeces for the face - 0, 2, 1, + 0, + 2, + 1, // - negative_size, // x -> point 1 - 0, // y - 0, // z + negative_size, // x -> point 1 + 0, // y + 0, // z // - size, // x -> point 2 - 0, // y - 0, // z + size, // x -> point 2 + 0, // y + 0, // z // - 0, // x -> point 3 - 0, // y - size, // z + 0, // x -> point 3 + 0, // y + size, // z // this is the normal for the single face 1065353216 == 1.0f encoded is i32 - 0, 1065353216, 0, - 0, 1065353216, 0, - 0, 1065353216, 0, + 0, + 1065353216, + 0, + 0, + 1065353216, + 0, + 0, + 1065353216, + 0, ]) - } diff --git a/nodes/max/plantarium/vec3/src/lib.rs b/nodes/max/plantarium/vec3/src/lib.rs index 118fd29..a6d0dae 100644 --- a/nodes/max/plantarium/vec3/src/lib.rs +++ b/nodes/max/plantarium/vec3/src/lib.rs @@ -1,13 +1,16 @@ use nodarium_macros::nodarium_definition_file; use nodarium_macros::nodarium_execute; +use nodarium_utils::concat_arg_vecs; +use nodarium_utils::read_i32_slice; use nodarium_utils::{concat_args, log, split_args}; nodarium_definition_file!("src/input.json"); #[nodarium_execute] -pub fn execute(input: &[i32]) -> Vec { - let args = split_args(input); - log!("vec3 input: {:?}", input); - log!("vec3 args: {:?}", args); - concat_args(args) +pub fn execute(x: (i32, i32), y: (i32, i32), z: (i32, i32)) -> Vec { + concat_arg_vecs(vec![ + read_i32_slice(x), + read_i32_slice(y), + read_i32_slice(z), + ]) } diff --git a/packages/macros/src/lib.rs b/packages/macros/src/lib.rs index 385c63d..81b0d2f 100644 --- a/packages/macros/src/lib.rs +++ b/packages/macros/src/lib.rs @@ -68,40 +68,48 @@ pub fn nodarium_execute(_attr: TokenStream, item: TokenStream) -> TokenStream { }) .collect(); - let arg_names: Vec<_> = (0..input_count) + let param_count = input_fn.sig.inputs.len(); + let total_c_params = param_count * 2; + + let arg_names: Vec<_> = (0..total_c_params) .map(|i| syn::Ident::new(&format!("arg{}", i), input_fn.sig.span())) .collect(); + let mut tuple_args = Vec::new(); + for i in 0..param_count { + let start_name = &arg_names[i * 2]; + let end_name = &arg_names[i * 2 + 1]; + let tuple_arg = quote! { + (#start_name, #end_name) + }; + tuple_args.push(tuple_arg); + } + let expanded = quote! { + extern "C" { fn __nodarium_log(ptr: *const u8, len: usize); fn __nodarium_log_panic(ptr: *const u8, len: usize); } - #fn_vis fn #inner_fn_name(#( #input_param_names: *const i32 ),*) -> Vec { + #fn_vis fn #inner_fn_name(#( #input_param_names: (i32, i32) ),*) -> Vec { #fn_body } #[no_mangle] #fn_vis extern "C" fn execute(output_pos: i32, #( #arg_names: i32 ),*) -> i32 { - static PANIC_HOOK_SET: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); - - if !PANIC_HOOK_SET.load(std::sync::atomic::Ordering::SeqCst) { - std::panic::set_hook(Box::new(|info| { - let msg = info.to_string(); - unsafe { __nodarium_log_panic(msg.as_ptr(), msg.len()); } - })); - PANIC_HOOK_SET.store(true, std::sync::atomic::Ordering::SeqCst); - } + // log!("before_fn"); let result = #inner_fn_name( - #( #arg_names as *const i32 ),* + #( #tuple_args ),* ); + // log!("after_fn"); let len_bytes = result.len() * 4; unsafe { let src = result.as_ptr() as *const u8; let dst = output_pos as *mut u8; + // log!("writing output_pos={:?} src={:?} len_bytes={:?}", output_pos, src, len_bytes); dst.copy_from_nonoverlapping(src, len_bytes); } @@ -116,7 +124,9 @@ pub fn nodarium_execute(_attr: TokenStream, item: TokenStream) -> TokenStream { fn validate_signature(fn_sig: &syn::Signature, expected_inputs: usize, def: &NodeDefinition) { let param_count = fn_sig.inputs.len(); - if param_count != expected_inputs { + let expected_params = expected_inputs; + + if param_count != expected_params { panic!( "Execute function has {} parameters but definition has {} inputs\n\ Definition inputs: {:?}\n\ @@ -129,12 +139,36 @@ fn validate_signature(fn_sig: &syn::Signature, expected_inputs: usize, def: &Nod .map(|i| i.keys().collect::>()) .unwrap_or_default(), (0..expected_inputs) - .map(|i| format!("arg{}: *const i32", i)) + .map(|i| format!("arg{}: (i32, i32)", i)) .collect::>() .join(", ") ); } + for (i, arg) in fn_sig.inputs.iter().enumerate() { + match arg { + syn::FnArg::Typed(pat_type) => { + let type_str = quote! { #pat_type.ty }.to_string(); + let clean_type = type_str + .trim() + .trim_start_matches("_") + .trim_end_matches(".ty") + .trim() + .to_string(); + if !clean_type.contains("(") && !clean_type.contains(",") { + panic!( + "Parameter {} has type '{}' but should be a tuple (i32, i32) representing (start, end) positions in memory", + i, + clean_type + ); + } + } + syn::FnArg::Receiver(_) => { + panic!("Execute function cannot have 'self' parameter"); + } + } + } + match &fn_sig.output { syn::ReturnType::Type(_, ty) => { let is_vec = match &**ty { diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index 0203104..2586369 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -21,7 +21,7 @@ export type NodeRuntimeState = { parents?: NodeInstance[]; children?: NodeInstance[]; inputNodes?: Record; - type?: NodeDefinition; + type?: NodeDefinition; // we should probably remove this and rely on registry.getNode(nodeType) downX?: number; downY?: number; x?: number; diff --git a/packages/utils/src/encoding.rs b/packages/utils/src/encoding.rs index 4b7f7c9..6cc3bb0 100644 --- a/packages/utils/src/encoding.rs +++ b/packages/utils/src/encoding.rs @@ -11,51 +11,38 @@ pub fn decode_float(bits: i32) -> f32 { } #[inline] -pub unsafe fn read_i32(ptr: *const i32) -> i32 { - *ptr -} - -#[inline] -pub unsafe fn read_f32(ptr: *const i32) -> f32 { - f32::from_bits(*ptr as u32) -} - -#[inline] -pub unsafe fn read_bool(ptr: *const i32) -> bool { - *ptr != 0 -} - -#[inline] -pub unsafe fn read_vec3(ptr: *const i32) -> [f32; 3] { - let p = ptr as *const f32; - [p.read(), p.add(1).read(), p.add(2).read()] -} - -#[inline] -pub unsafe fn read_i32_slice(ptr: *const i32, len: usize) -> Vec { - std::slice::from_raw_parts(ptr, len).to_vec() -} - -#[inline] -pub unsafe fn read_f32_slice(ptr: *const i32, len: usize) -> Vec { - std::slice::from_raw_parts(ptr as *const f32, len).to_vec() -} - -#[inline] -pub unsafe fn read_f32_default(ptr: *const i32, default: f32) -> f32 { - if ptr.is_null() { - default - } else { - read_f32(ptr) +pub fn read_i32(ptr: i32) -> i32 { + unsafe { + let _ptr = ptr as *const i32; + *_ptr } } #[inline] -pub unsafe fn read_i32_default(ptr: *const i32, default: i32) -> i32 { - if ptr.is_null() { - default - } else { - read_i32(ptr) +pub fn read_f32(ptr: i32) -> f32 { + unsafe { + let _ptr = ptr as *const i32; + f32::from_bits(*_ptr as u32) + } +} + +#[inline] +pub fn read_i32_slice(tuple: (i32, i32)) -> Vec { + unsafe { + let start = tuple.0 as *const i32; + let end = tuple.1 as *const i32; + let len = (end as usize - start as usize) / 4; + std::slice::from_raw_parts(start, len).to_vec() + } +} + +#[inline] +pub fn read_f32_slice(tuple: (i32, i32)) -> Vec { + unsafe { + let start = tuple.0 as *const f32; + let end = tuple.1 as *const f32; + let len = (end as usize - start as usize) / 4; + std::slice::from_raw_parts(start, len).to_vec() } } diff --git a/packages/utils/src/wasm-wrapper.ts b/packages/utils/src/wasm-wrapper.ts index 0a56ea8..5d163d8 100644 --- a/packages/utils/src/wasm-wrapper.ts +++ b/packages/utils/src/wasm-wrapper.ts @@ -27,7 +27,6 @@ export function createWasmWrapper(buffer: ArrayBuffer, memory: WebAssembly.Memor exports = instance.exports as NodariumExports; function execute(outputPos: number, args: number[]): number { - console.log('WASM_WRAPPER', { outputPos, args }); return exports.execute(outputPos, ...args); }