feat: make all nodes work with new runtime

This commit is contained in:
Max Richter
2026-01-23 01:14:09 +01:00
parent 47882a832d
commit a497a46674
23 changed files with 494 additions and 244 deletions

View File

@@ -39,5 +39,6 @@ server {
EOF EOF
COPY --from=builder /app/app/build /app COPY --from=builder /app/app/build /app
COPY --from=builder /app/packages/ui/build /app/ui
EXPOSE 80 EXPOSE 80

View File

@@ -606,11 +606,14 @@ export class GraphManager extends EventEmitter<{
return; 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 // check if socket types match
const fromSocketType = from.state?.type?.outputs?.[fromSocket]; const fromSocketType = fromType?.outputs?.[fromSocket];
const toSocketType = [to.state?.type?.inputs?.[toSocket]?.type]; const toSocketType = [toType?.inputs?.[toSocket]?.type];
if (to.state?.type?.inputs?.[toSocket]?.accepts) { if (toType?.inputs?.[toSocket]?.accepts) {
toSocketType.push(...(to?.state?.type?.inputs?.[toSocket]?.accepts || [])); toSocketType.push(...(toType?.inputs?.[toSocket]?.accepts || []));
} }
if (!areSocketsCompatible(fromSocketType, toSocketType)) { if (!areSocketsCompatible(fromSocketType, toSocketType)) {
@@ -733,9 +736,9 @@ export class GraphManager extends EventEmitter<{
} }
getPossibleSockets({ node, index }: Socket): [NodeInstance, string | number][] { getPossibleSockets({ node, index }: Socket): [NodeInstance, string | number][] {
const nodeType = node?.state?.type; const nodeType = this.registry.getNode(node.type);
console.log({ node: $state.snapshot(node), index, nodeType });
if (!nodeType) return []; if (!nodeType) return [];
console.log({ index });
const sockets: [NodeInstance, string | number][] = []; const sockets: [NodeInstance, string | number][] = [];
@@ -750,7 +753,7 @@ export class GraphManager extends EventEmitter<{
const ownType = nodeType?.inputs?.[index].type; const ownType = nodeType?.inputs?.[index].type;
for (const node of nodes) { for (const node of nodes) {
const nodeType = node?.state?.type; const nodeType = this.registry.getNode(node.type);
const inputs = nodeType?.outputs; const inputs = nodeType?.outputs;
if (!inputs) continue; if (!inputs) continue;
for (let index = 0; index < inputs.length; index++) { for (let index = 0; index < inputs.length; index++) {
@@ -778,7 +781,7 @@ export class GraphManager extends EventEmitter<{
const ownType = nodeType.outputs?.[index]; const ownType = nodeType.outputs?.[index];
for (const node of nodes) { for (const node of nodes) {
const inputs = node?.state?.type?.inputs; const inputs = this.registry.getNode(node.type)?.inputs;
if (!inputs) continue; if (!inputs) continue;
for (const key in inputs) { for (const key in inputs) {
const otherType = [inputs[key].type]; const otherType = [inputs[key].type];

View File

@@ -43,7 +43,7 @@
<div class="wrapper" data-node-id={node.id} data-node-type={node.type}> <div class="wrapper" data-node-id={node.id} data-node-type={node.type}>
<div class="content"> <div class="content">
{node.type.split("/").pop()} {node.type.split("/").pop()} ({node.id})
</div> </div>
<div <div
class="click-target" class="click-target"

View File

@@ -57,9 +57,18 @@ function getValue(input: NodeInput, value?: unknown) {
throw new Error(`Unknown input type ${input.type}`); throw new Error(`Unknown input type ${input.type}`);
} }
type Pointer = { function compareInt32(a: Int32Array, b: Int32Array) {
if (a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) return false;
}
return true;
}
export type Pointer = {
start: number; start: number;
end: number; end: number;
_title?: string;
}; };
export class MemoryRuntimeExecutor implements RuntimeExecutor { export class MemoryRuntimeExecutor implements RuntimeExecutor {
@@ -69,15 +78,24 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
> = new Map(); > = new Map();
private offset = 0; private offset = 0;
private isRunning = false;
private memory = new WebAssembly.Memory({ private memory = new WebAssembly.Memory({
initial: 1024, initial: 1024,
maximum: 8192 maximum: 8192
}); });
private memoryView = new Int32Array();
results: Record<number, Pointer> = {};
inputPtrs: Record<number, Pointer[]> = {};
seed = 123123; seed = 123123;
perf?: PerformanceStore; perf?: PerformanceStore;
public getMemory() {
return new Int32Array(this.memory.buffer);
}
constructor( constructor(
private registry: NodeRegistry, private registry: NodeRegistry,
public cache?: SyncCache<Int32Array> public cache?: SyncCache<Int32Array>
@@ -129,7 +147,8 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
const outputNode = graphNodes.find((node) => node.type.endsWith('/output')); const outputNode = graphNodes.find((node) => node.type.endsWith('/output'));
if (!outputNode) { 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( const nodeMap = new Map(
@@ -151,7 +170,7 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
const nodes = []; const nodes = [];
// loop through all the nodes and assign each nodes its depth // loop through all the nodes and assign each nodes its depth
const stack = [outputNode]; const stack = [outputNode || graphNodes[0]];
while (stack.length) { while (stack.length) {
const node = stack.pop(); const node = stack.pop();
if (!node) continue; if (!node) continue;
@@ -166,14 +185,15 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
return [outputNode, nodes] as const; return [outputNode, nodes] as const;
} }
private writeToMemory(v: number | number[] | Int32Array) { private writeToMemory(v: number | number[] | Int32Array, title?: string) {
let length = 1; let length = 1;
const view = new Int32Array(this.memory.buffer);
if (typeof v === 'number') { if (typeof v === 'number') {
view[this.offset] = v; this.memoryView[this.offset] = v;
console.log('MEM: writing number', v, ' to', this.offset);
length = 1; length = 1;
} else { } else {
view.set(v, this.offset); this.memoryView.set(v, this.offset);
length = v.length; length = v.length;
} }
@@ -184,15 +204,25 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
return { return {
start, 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<string, unknown>) { async execute(graph: Graph, _settings: Record<string, unknown>) {
this.offset = 0; 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 // 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 * 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) (a, b) => (b.state?.depth || 0) - (a.state?.depth || 0)
); );
// here we store the intermediate results of the nodes
const results: Record<number, Pointer> = {};
for (const node of sortedNodes) { for (const node of sortedNodes) {
const node_type = this.nodes.get(node.type)!; const node_type = this.nodes.get(node.type)!;
console.log('EXECUTING NODE', node_type.definition.id); console.log('---------------');
console.log(node_type.definition.inputs); 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( const inputs = Object.entries(node_type.definition.inputs || {}).map(
([key, input]) => { ([key, input]) => {
// We should probably initially write this to memory // We should probably initially write this to memory
@@ -225,6 +255,8 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
return this.writeToMemory(this.seed); return this.writeToMemory(this.seed);
} }
const title = `${node.id}.${key}`;
// We should probably initially write this to memory // We should probably initially write this to memory
// If the input is linked to a setting, we use that value // If the input is linked to a setting, we use that value
// if (input.setting) { // if (input.setting) {
@@ -234,56 +266,85 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
// check if the input is connected to another node // check if the input is connected to another node
const inputNode = node.state.inputNodes[key]; const inputNode = node.state.inputNodes[key];
if (inputNode) { if (inputNode) {
if (results[inputNode.id] === undefined) { if (this.results[inputNode.id] === undefined) {
throw new Error( throw new Error(
`Node ${node.type} is missing input from node ${inputNode.type}` `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 the value is stored in the node itself, we use that value
if (node.props?.[key] !== undefined) { 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) { if (!node_type || !node.state || !node_type.execute) {
log.warn(`Node ${node.id} has no definition`); log.warn(`Node ${node.id} has no definition`);
continue; continue;
} }
this.inputPtrs[node.id] = inputs;
const args = inputs.map(s => [s.start, s.end]).flat(); const args = inputs.map(s => [s.start, s.end]).flat();
console.log('ARGS', args); console.log('ARGS', inputs);
this.printMemory();
try { try {
const bytesWritten = node_type.execute(this.offset, args); console.log('EXECUTING NODE, writing output of node to ->', this.offset);
results[node.id] = { const bytesWritten = node_type.execute(this.offset * 4, args.map(a => a * 4));
start: this.offset, const view = new Int32Array(this.memory.buffer);
end: this.offset + bytesWritten
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} ->`
}; };
this.offset += bytesWritten; } else {
this.results[node.id] = {
start: this.offset,
end: this.offset + bytesWritten / 4,
_title: `${node.id} ->`
};
this.offset += bytesWritten / 4;
}
console.log('FINISHED EXECUTION', { console.log('FINISHED EXECUTION', {
bytesWritten, bytesWritten,
offset: this.offset offset: this.offset
}); });
} catch (e) { } 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); // const mem = new Int32Array(this.memory.buffer);
console.log('OUT', mem.slice(0, 10)); // console.log('OUT', mem.slice(0, 10));
// return the result of the parent of the output node // 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'); this.perf?.endPoint('runtime');
return res as unknown as Int32Array; this.isRunning = false;
return undefined as unknown as Int32Array;
} }
getPerformanceData() { getPerformanceData() {

View File

@@ -1,16 +1,14 @@
<script lang="ts"> <script lang="ts">
import * as templates from "$lib/graph-templates";
import Panel from "$lib/sidebar/Panel.svelte"; import Panel from "$lib/sidebar/Panel.svelte";
import Sidebar from "$lib/sidebar/Sidebar.svelte"; import Sidebar from "$lib/sidebar/Sidebar.svelte";
import GraphInterface from "$lib/graph-interface"; import GraphInterface from "$lib/graph-interface";
import { RemoteNodeRegistry } from "@nodarium/registry"; import { RemoteNodeRegistry } from "@nodarium/registry";
import { type Graph } from "@nodarium/types"; import { type Graph, type NodeInstance } from "@nodarium/types";
import Grid from "$lib/grid"; import Grid from "$lib/grid";
import { MemoryRuntimeExecutor } from "$lib/runtime"; import { MemoryRuntimeExecutor, type Pointer } from "$lib/runtime";
import devPlant from "./dev-graph.json"; import devPlant from "./dev-graph.json";
import { decodeNestedArray } from "@nodarium/utils"; import { decodeFloat } from "@nodarium/utils";
import { localState } from "$lib/helpers/localState.svelte";
let result = $state<Int32Array>();
const nodeRegistry = new RemoteNodeRegistry(""); const nodeRegistry = new RemoteNodeRegistry("");
nodeRegistry.overwriteNode("max/plantarium/output", { nodeRegistry.overwriteNode("max/plantarium/output", {
@@ -24,21 +22,63 @@
type: "*", type: "*",
}, },
}, },
execute(input: Int32Array) { execute(outputPos: number, args: number[]) {
result = input; return 0;
return input;
}, },
}); });
const runtimeExecutor = new MemoryRuntimeExecutor(nodeRegistry); const runtimeExecutor = new MemoryRuntimeExecutor(nodeRegistry);
let inputPtrs: Record<number, Pointer[]>;
let activeNode = $state<NodeInstance>();
let isCalculating = $state<boolean>(false);
let windowHeight = $state(500);
let start = $state(0);
const rowHeight = 40;
const numRows = $derived(Math.floor(windowHeight / rowHeight));
let memory = $state<Int32Array>();
const visibleRows = $derived(memory?.slice(start, start + numRows));
const ptrs = $derived.by(() => {
if (!inputPtrs) return [];
const seen = new Set();
const ptrs = [...Object.values(inputPtrs)]
.flat()
.sort((a, b) => (a.start > b.start ? 1 : -1))
.filter((ptr) => {
const id = `${ptr.start}-${ptr.end}`;
if (seen.has(id)) return false;
seen.add(id);
return true;
});
if (!ptrs) return [];
let out = [];
for (let i = 0; i < numRows; i++) {
let rowIndex = start + i;
const activePtr = ptrs.find(
(ptr) => ptr.start < rowIndex && ptr.end >= rowIndex,
);
if (activePtr) {
out.push({
start: rowIndex,
end: rowIndex + 1,
_title: activePtr._title,
});
}
}
return out;
});
let graph = $state( let graph = $state(
localStorage.getItem("nodes.dev.graph") localStorage.getItem("nodes.dev.graph")
? JSON.parse(localStorage.getItem("nodes.dev.graph")!) ? JSON.parse(localStorage.getItem("nodes.dev.graph")!)
: devPlant, : devPlant,
); );
function handleSave(graph: Graph) { function handleSave(g: Graph) {
localStorage.setItem("nodes.dev.graph", JSON.stringify(graph)); localStorage.setItem("nodes.dev.graph", JSON.stringify(g));
} }
let graphSettings = $state<Record<string, any>>({}); let graphSettings = $state<Record<string, any>>({});
@@ -46,27 +86,116 @@
randomSeed: { type: "boolean", value: false }, randomSeed: { type: "boolean", value: false },
}); });
async function handleResult(res: unknown) { let calcTimeout: ReturnType<typeof setTimeout>;
const result = await runtimeExecutor.execute(graph, graphSettings); async function handleResult(res?: Graph) {
console.log({ res, result }); console.clear();
isCalculating = true;
if (res) handleSave(res);
try {
await runtimeExecutor.execute(res || graph, graphSettings);
} catch (e) {
console.log(e);
}
memory = runtimeExecutor.getMemory();
inputPtrs = runtimeExecutor.inputPtrs;
clearTimeout(calcTimeout);
calcTimeout = setTimeout(() => {
isCalculating = false;
}, 500);
}
const rowIsFloat = localState<boolean[]>("node.dev.isFloat", []);
function decodeValue(value: number, isFloat?: boolean) {
return isFloat ? decodeFloat(value) : value;
} }
</script> </script>
<svelte:window
bind:innerHeight={windowHeight}
onkeydown={(ev) => ev.key === "r" && handleResult()}
/>
<Grid.Row> <Grid.Row>
<Grid.Cell> <Grid.Cell>
{#if result} {#if visibleRows?.length}
<pre><code>{JSON.stringify(decodeNestedArray(result))}</code></pre> <table
class="min-w-full select-none overflow-hidden text-left text-sm flex-1"
>
<thead class="">
<tr>
<th class="px-4 py-2 border-b border-[var(--outline)]">i</th>
<th
class="px-4 py-2 border-b border-[var(--outline)] w-[50px]"
style:width="50px">Ptrs</th
>
<th class="px-4 py-2 border-b border-[var(--outline)]">Value</th>
<th class="px-4 py-2 border-b border-[var(--outline)]">Float</th>
</tr>
</thead>
<tbody>
{#each visibleRows as r, i}
{@const index = i + start}
{@const ptr = ptrs[i]}
<tr class="h-[40px] odd:bg-[var(--layer-1)]">
<td class="px-4 border-b border-[var(--outline)] w-8">{index}</td>
<td
class="w-[50px] border-b border-[var(--outline)]
{ptr?._title?.includes('->')
? 'bg-red-500'
: 'bg-blue-500'}"
>
<span>{ptr?._title}</span>
</td>
<td
class="px-4 border-b border-[var(--outline)] cursor-pointer text-blue-600 hover:text-blue-800"
onclick={() =>
(rowIsFloat.value[index] = !rowIsFloat.value[index])}
>
{decodeValue(r, rowIsFloat.value[index])}
</td>
<td class="px-4 border-b border-[var(--outline)] italic w-5">
<input
type="checkbox"
checked={rowIsFloat.value[index]}
onclick={() =>
(rowIsFloat.value[index] = !rowIsFloat.value[index])}
/>
</td>
</tr>
{/each}
</tbody>
</table>
<input
class="absolute bottom-4 left-4 bg-white"
bind:value={start}
min="0"
type="number"
step="1"
/>
{/if} {/if}
</Grid.Cell> </Grid.Cell>
<Grid.Cell> <Grid.Cell>
{#if isCalculating}
<span
class="opacity-50 top-4 left-4 i-[tabler--loader-2] w-10 h-10 absolute animate-spin z-100"
></span>
{/if}
<button
onclick={() => handleResult()}
class="flex items-center cursor-pointer absolute bottom-4 left-4 z-100"
>
Execute Graph (R)
</button>
<GraphInterface <GraphInterface
{graph} {graph}
bind:activeNode
registry={nodeRegistry} registry={nodeRegistry}
bind:settings={graphSettings} bind:settings={graphSettings}
bind:settingTypes={graphSettingTypes} bind:settingTypes={graphSettingTypes}
onsave={(g) => handleSave(g)} onsave={(g) => handleSave(g)}
onresult={(result) => handleResult(result)} onresult={(res) => handleResult(res)}
/> />
</Grid.Cell> </Grid.Cell>
</Grid.Row> </Grid.Row>

View File

@@ -3,18 +3,17 @@ use nodarium_macros::nodarium_execute;
use nodarium_utils::{ use nodarium_utils::{
encode_float, evaluate_float, geometry::calculate_normals,log, encode_float, evaluate_float, geometry::calculate_normals,log,
split_args, wrap_arg, split_args, wrap_arg,
read_i32_slice
}; };
nodarium_definition_file!("src/input.json"); nodarium_definition_file!("src/input.json");
#[nodarium_execute] #[nodarium_execute]
pub fn execute(input: &[i32]) -> Vec<i32> { pub fn execute(size: (i32, i32)) -> Vec<i32> {
let args = split_args(input); let args = read_i32_slice(size);
log!("WASM(cube): input: {:?} -> {:?}", input, args); let size = evaluate_float(&args);
let size = evaluate_float(args[0]);
let p = encode_float(size); let p = encode_float(size);
let n = encode_float(-size); let n = encode_float(-size);
@@ -77,8 +76,6 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
let res = wrap_arg(&cube_geometry); let res = wrap_arg(&cube_geometry);
log!("WASM(box): output: {:?}", res);
res res
} }

View File

@@ -1,5 +1,6 @@
use nodarium_macros::nodarium_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_macros::nodarium_execute; use nodarium_macros::nodarium_execute;
use nodarium_utils::read_i32_slice;
use nodarium_utils::{ use nodarium_utils::{
concat_arg_vecs, evaluate_float, evaluate_int, concat_arg_vecs, evaluate_float, evaluate_int,
geometry::{ geometry::{
@@ -13,15 +14,25 @@ use std::f32::consts::PI;
nodarium_definition_file!("src/input.json"); nodarium_definition_file!("src/input.json");
#[nodarium_execute] #[nodarium_execute]
pub fn execute(input: &[i32]) -> Vec<i32> { pub fn execute(
let args = split_args(input); path: (i32, i32),
length: (i32, i32),
let paths = split_args(args[0]); 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<i32> {
let arg = read_i32_slice(path);
let paths = split_args(arg.as_slice());
let mut output: Vec<Vec<i32>> = Vec::new(); let mut output: Vec<Vec<i32>> = Vec::new();
let resolution = evaluate_int(args[8]).max(4) as usize; let resolution = evaluate_int(read_i32_slice(resolution_curve).as_slice()).max(4) as usize;
let depth = evaluate_int(args[6]); let depth = evaluate_int(read_i32_slice(depth).as_slice());
let mut max_depth = 0; let mut max_depth = 0;
for path_data in paths.iter() { for path_data in paths.iter() {
@@ -40,18 +51,18 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
let path = wrap_path(path_data); 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 lowest_branch = evaluate_float(read_i32_slice(lowest_branch).as_slice());
let highest_branch = evaluate_float(args[5]); let highest_branch = evaluate_float(read_i32_slice(highest_branch).as_slice());
for i in 0..branch_amount { for i in 0..branch_amount {
let a = i as f32 / (branch_amount - 1).max(1) as f32; let a = i as f32 / (branch_amount - 1).max(1) as f32;
let length = evaluate_float(args[1]); let length = evaluate_float(read_i32_slice(length).as_slice());
let thickness = evaluate_float(args[2]); let thickness = evaluate_float(read_i32_slice(thickness).as_slice());
let offset_single = if i % 2 == 0 { let offset_single = if i % 2 == 0 {
evaluate_float(args[3]) evaluate_float(read_i32_slice(offset_single).as_slice())
} else { } else {
0.0 0.0
}; };
@@ -65,7 +76,8 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
root_alpha + (offset_single - 0.5) * 6.0 / resolution as f32, 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 // check if diration contains NaN
if orthogonal[0].is_nan() || orthogonal[1].is_nan() || orthogonal[2].is_nan() { if orthogonal[0].is_nan() || orthogonal[1].is_nan() || orthogonal[2].is_nan() {

View File

@@ -1,11 +1,11 @@
use nodarium_macros::nodarium_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_macros::nodarium_execute; use nodarium_macros::nodarium_execute;
use nodarium_utils::log; use nodarium_utils::{ log, read_f32, encode_float };
nodarium_definition_file!("src/input.json"); nodarium_definition_file!("src/input.json");
#[nodarium_execute] #[nodarium_execute]
pub fn execute(_value: *const i32) -> Vec<i32> { pub fn execute(a: (i32, i32)) -> Vec<i32> {
log!("Duuuude"); let a_val = read_f32(a.0);
vec![32] vec![encode_float(a_val)]
} }

View File

@@ -1,6 +1,7 @@
use glam::Vec3; use glam::Vec3;
use nodarium_macros::nodarium_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_macros::nodarium_execute; use nodarium_macros::nodarium_execute;
use nodarium_utils::read_i32_slice;
use nodarium_utils::{ use nodarium_utils::{
concat_args, evaluate_float, evaluate_int, concat_args, evaluate_float, evaluate_int,
geometry::{wrap_path, wrap_path_mut}, geometry::{wrap_path, wrap_path_mut},
@@ -14,13 +15,17 @@ fn lerp_vec3(a: Vec3, b: Vec3, t: f32) -> Vec3 {
} }
#[nodarium_execute] #[nodarium_execute]
pub fn execute(input: &[i32]) -> Vec<i32> { pub fn execute(
plant: (i32, i32),
strength: (i32, i32),
curviness: (i32, i32),
depth: (i32, i32),
) -> Vec<i32> {
reset_call_count(); reset_call_count();
let args = split_args(input); let arg = read_i32_slice(plant);
let plants = split_args(arg.as_slice());
let plants = split_args(args[0]); let depth = evaluate_int(read_i32_slice(depth).as_slice());
let depth = evaluate_int(args[3]);
let mut max_depth = 0; let mut max_depth = 0;
for path_data in plants.iter() { for path_data in plants.iter() {
@@ -55,9 +60,9 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
let length = direction.length(); let length = direction.length();
let curviness = evaluate_float(args[2]); let str = evaluate_float(read_i32_slice(strength).as_slice());
let strength = let curviness = evaluate_float(read_i32_slice(curviness).as_slice());
evaluate_float(args[1]) / curviness.max(0.0001) * evaluate_float(args[1]); let strength = str / curviness.max(0.0001) * str;
log!( log!(
"length: {}, curviness: {}, strength: {}", "length: {}, curviness: {}, strength: {}",

View File

@@ -1,23 +1,29 @@
use glam::{Mat4, Quat, Vec3}; use glam::{Mat4, Quat, Vec3};
use nodarium_macros::nodarium_execute;
use nodarium_macros::nodarium_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_macros::nodarium_execute;
use nodarium_utils::read_i32_slice;
use nodarium_utils::{ use nodarium_utils::{
concat_args, evaluate_float, evaluate_int, concat_args, evaluate_float, evaluate_int,
geometry::{ geometry::{create_instance_data, wrap_geometry_data, wrap_instance_data, wrap_path},
create_instance_data, wrap_geometry_data, wrap_instance_data, wrap_path,
},
log, split_args, log, split_args,
}; };
nodarium_definition_file!("src/input.json"); nodarium_definition_file!("src/input.json");
#[nodarium_execute] #[nodarium_execute]
pub fn execute(input: &[i32]) -> Vec<i32> { pub fn execute(
let args = split_args(input); plant: (i32, i32),
let mut inputs = split_args(args[0]); geometry: (i32, i32),
amount: (i32, i32),
lowest_instance: (i32, i32),
highest_instance: (i32, i32),
depth: (i32, i32),
) -> Vec<i32> {
let arg = read_i32_slice(plant);
let mut inputs = split_args(arg.as_slice());
log!("WASM(instance): inputs: {:?}", inputs); 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 geo = wrap_geometry_data(&mut geo_data);
let mut transforms: Vec<Mat4> = Vec::new(); let mut transforms: Vec<Mat4> = Vec::new();
@@ -30,17 +36,17 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
max_depth = max_depth.max(path_data[3]); 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() { for path_data in inputs.iter() {
if path_data[3] < (max_depth - depth + 1) { if path_data[3] < (max_depth - depth + 1) {
continue; 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 lowest_instance = evaluate_float(read_i32_slice(lowest_instance).as_slice());
let highest_instance = evaluate_float(args[4]); let highest_instance = evaluate_float(read_i32_slice(highest_instance).as_slice());
let path = wrap_path(path_data); let path = wrap_path(path_data);

View File

@@ -1,24 +1,13 @@
use nodarium_macros::nodarium_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_macros::nodarium_execute; 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_definition_file!("src/input.json");
#[nodarium_execute] #[nodarium_execute]
pub fn execute(op_type: *const i32, a: *const i32, b: *const i32) -> Vec<i32> { pub fn execute(op_type: (i32, i32), a: (i32, i32), b: (i32, i32)) -> Vec<i32> {
let op = unsafe { read_i32(op_type) }; let op = read_i32_slice(op_type);
let a_val = unsafe { read_f32(a) }; let a_val = read_i32_slice(a);
let b_val = unsafe { read_f32(b) }; let b_val = read_i32_slice(b);
concat_arg_vecs(vec![vec![0], op, a_val, b_val])
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]
} }

View File

@@ -1,7 +1,8 @@
use nodarium_macros::nodarium_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_macros::nodarium_execute; use nodarium_macros::nodarium_execute;
use nodarium_utils::read_i32_slice;
use nodarium_utils::{ 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, reset_call_count, split_args,
}; };
use noise::{HybridMulti, MultiFractal, NoiseFn, OpenSimplex}; use noise::{HybridMulti, MultiFractal, NoiseFn, OpenSimplex};
@@ -13,23 +14,31 @@ fn lerp(a: f32, b: f32, t: f32) -> f32 {
} }
#[nodarium_execute] #[nodarium_execute]
pub fn execute(input: &[i32]) -> Vec<i32> { 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<i32> {
reset_call_count(); 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 seed = read_i32(seed.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 = 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(read_i32_slice(octaves).as_slice());
let octaves = evaluate_int(args[7]);
let noise_x: HybridMulti<OpenSimplex> = let noise_x: HybridMulti<OpenSimplex> =
HybridMulti::new(seed as u32 + 1).set_octaves(octaves as usize); HybridMulti::new(seed as u32 + 1).set_octaves(octaves as usize);

View File

@@ -1,9 +1,14 @@
use nodarium_macros::nodarium_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_macros::nodarium_execute; use nodarium_macros::nodarium_execute;
use nodarium_utils::log;
use nodarium_utils::read_i32_slice;
nodarium_definition_file!("src/input.json"); nodarium_definition_file!("src/input.json");
#[nodarium_execute] #[nodarium_execute]
pub fn execute(_input: *const i32, _res: *const i32) -> Vec<i32> { pub fn execute(input: (i32, i32), _res: (i32, i32)) -> Vec<i32> {
return vec![0]; log!("HERE");
let mut vecs = read_i32_slice(input);
vecs.push(42);
vecs
} }

View File

@@ -1,11 +1,16 @@
use nodarium_macros::nodarium_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_macros::nodarium_execute; 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] #[nodarium_execute]
pub fn execute(args: &[i32]) -> Vec<i32> { pub fn execute(min: (i32, i32), max: (i32, i32), seed: (i32, i32)) -> Vec<i32> {
let args = split_args(args); concat_arg_vecs(vec![
concat_args(vec![&[1], args[0], args[1], args[2]]) vec![1],
read_i32_slice(min),
read_i32_slice(max),
read_i32_slice(seed),
])
} }

View File

@@ -1,23 +1,26 @@
use glam::{Mat4, Vec3}; use glam::{Mat4, Vec3};
use nodarium_macros::nodarium_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_macros::nodarium_execute; use nodarium_macros::nodarium_execute;
use nodarium_utils::read_i32_slice;
use nodarium_utils::{ use nodarium_utils::{
concat_args, evaluate_float, evaluate_int, geometry::wrap_path_mut, log, concat_args, evaluate_float, evaluate_int, geometry::wrap_path_mut, log, split_args,
split_args,
}; };
nodarium_definition_file!("src/input.json"); nodarium_definition_file!("src/input.json");
#[nodarium_execute] #[nodarium_execute]
pub fn execute(input: &[i32]) -> Vec<i32> { pub fn execute(
plant: (i32, i32),
axis: (i32, i32),
angle: (i32, i32),
spread: (i32, i32),
) -> Vec<i32> {
log!("DEBUG args: {:?}", plant);
log!("DEBUG args: {:?}", input); let arg = read_i32_slice(plant);
let plants = split_args(arg.as_slice());
let args = split_args(input); 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 plants = split_args(args[0]);
let axis = evaluate_int(args[1]); // 0 =x, 1 = y, 2 = z
let spread = evaluate_int(args[3]);
let output: Vec<Vec<i32>> = plants let output: Vec<Vec<i32>> = plants
.iter() .iter()
@@ -32,7 +35,7 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
let path = wrap_path_mut(&mut path_data); 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]]; let origin = [path.points[0], path.points[1], path.points[2]];

View File

@@ -4,29 +4,28 @@ use nodarium_utils::{
evaluate_float, evaluate_int, evaluate_vec3, evaluate_float, evaluate_int, evaluate_vec3,
geometry::{create_multiple_paths, wrap_multiple_paths}, geometry::{create_multiple_paths, wrap_multiple_paths},
log, reset_call_count, split_args, log, reset_call_count, split_args,
read_i32_slice, read_i32,
}; };
nodarium_definition_file!("src/input.json"); nodarium_definition_file!("src/input.json");
#[nodarium_execute] #[nodarium_execute]
pub fn execute(input: &[i32]) -> Vec<i32> { pub fn execute(origin: (i32, i32), _amount: (i32,i32), length: (i32, i32), thickness: (i32, i32), resolution_curve: (i32, i32)) -> Vec<i32> {
reset_call_count(); 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; log!("stem args: amount={:?}", amount);
let path_resolution = evaluate_int(args[4]) as usize;
log!("stem args: {:?}", args);
let mut stem_data = create_multiple_paths(amount, path_resolution, 1); let mut stem_data = create_multiple_paths(amount, path_resolution, 1);
let mut stems = wrap_multiple_paths(&mut stem_data); let mut stems = wrap_multiple_paths(&mut stem_data);
for stem in stems.iter_mut() { for stem in stems.iter_mut() {
let origin = evaluate_vec3(args[0]); let origin = evaluate_vec3(read_i32_slice(origin).as_slice());
let length = evaluate_float(args[2]); let length = evaluate_float(read_i32_slice(length).as_slice());
let thickness = evaluate_float(args[3]); let thickness = evaluate_float(read_i32_slice(thickness).as_slice());
let amount_points = stem.points.len() / 4; let amount_points = stem.points.len() / 4;
for i in 0..amount_points { for i in 0..amount_points {

View File

@@ -1,21 +1,17 @@
use nodarium_macros::nodarium_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_macros::nodarium_execute; use nodarium_macros::nodarium_execute;
use nodarium_utils::{ use nodarium_utils::read_i32_slice;
decode_float, encode_float, evaluate_int, split_args, wrap_arg, log use nodarium_utils::{decode_float, encode_float, evaluate_int, log, split_args, wrap_arg};
};
nodarium_definition_file!("src/input.json"); nodarium_definition_file!("src/input.json");
#[nodarium_execute] #[nodarium_execute]
pub fn execute(input: &[i32]) -> Vec<i32> { pub fn execute(size: (i32, i32)) -> Vec<i32> {
let size = evaluate_int(read_i32_slice(size).as_slice());
let args = split_args(input);
let size = evaluate_int(args[0]);
let decoded = decode_float(size); let decoded = decode_float(size);
let negative_size = encode_float(-decoded); 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]]; // [[1,3, x, y, z, x, y,z,x,y,z]];
wrap_arg(&[ wrap_arg(&[
@@ -23,7 +19,9 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
3, // 3 vertices 3, // 3 vertices
1, // 1 face 1, // 1 face
// this are the indeces for the face // this are the indeces for the face
0, 2, 1, 0,
2,
1,
// //
negative_size, // x -> point 1 negative_size, // x -> point 1
0, // y 0, // y
@@ -37,9 +35,14 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
0, // y 0, // y
size, // z size, // z
// this is the normal for the single face 1065353216 == 1.0f encoded is i32 // this is the normal for the single face 1065353216 == 1.0f encoded is i32
0, 1065353216, 0, 0,
0, 1065353216, 0, 1065353216,
0, 1065353216, 0, 0,
0,
1065353216,
0,
0,
1065353216,
0,
]) ])
} }

View File

@@ -1,13 +1,16 @@
use nodarium_macros::nodarium_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_macros::nodarium_execute; 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}; use nodarium_utils::{concat_args, log, split_args};
nodarium_definition_file!("src/input.json"); nodarium_definition_file!("src/input.json");
#[nodarium_execute] #[nodarium_execute]
pub fn execute(input: &[i32]) -> Vec<i32> { pub fn execute(x: (i32, i32), y: (i32, i32), z: (i32, i32)) -> Vec<i32> {
let args = split_args(input); concat_arg_vecs(vec![
log!("vec3 input: {:?}", input); read_i32_slice(x),
log!("vec3 args: {:?}", args); read_i32_slice(y),
concat_args(args) read_i32_slice(z),
])
} }

View File

@@ -68,40 +68,48 @@ pub fn nodarium_execute(_attr: TokenStream, item: TokenStream) -> TokenStream {
}) })
.collect(); .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())) .map(|i| syn::Ident::new(&format!("arg{}", i), input_fn.sig.span()))
.collect(); .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! { let expanded = quote! {
extern "C" { extern "C" {
fn __nodarium_log(ptr: *const u8, len: usize); fn __nodarium_log(ptr: *const u8, len: usize);
fn __nodarium_log_panic(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<i32> { #fn_vis fn #inner_fn_name(#( #input_param_names: (i32, i32) ),*) -> Vec<i32> {
#fn_body #fn_body
} }
#[no_mangle] #[no_mangle]
#fn_vis extern "C" fn execute(output_pos: i32, #( #arg_names: i32 ),*) -> i32 { #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( let result = #inner_fn_name(
#( #arg_names as *const i32 ),* #( #tuple_args ),*
); );
// log!("after_fn");
let len_bytes = result.len() * 4; let len_bytes = result.len() * 4;
unsafe { unsafe {
let src = result.as_ptr() as *const u8; let src = result.as_ptr() as *const u8;
let dst = output_pos as *mut 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); 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) { fn validate_signature(fn_sig: &syn::Signature, expected_inputs: usize, def: &NodeDefinition) {
let param_count = fn_sig.inputs.len(); let param_count = fn_sig.inputs.len();
if param_count != expected_inputs { let expected_params = expected_inputs;
if param_count != expected_params {
panic!( panic!(
"Execute function has {} parameters but definition has {} inputs\n\ "Execute function has {} parameters but definition has {} inputs\n\
Definition 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::<Vec<_>>()) .map(|i| i.keys().collect::<Vec<_>>())
.unwrap_or_default(), .unwrap_or_default(),
(0..expected_inputs) (0..expected_inputs)
.map(|i| format!("arg{}: *const i32", i)) .map(|i| format!("arg{}: (i32, i32)", i))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", ") .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 { match &fn_sig.output {
syn::ReturnType::Type(_, ty) => { syn::ReturnType::Type(_, ty) => {
let is_vec = match &**ty { let is_vec = match &**ty {

View File

@@ -21,7 +21,7 @@ export type NodeRuntimeState = {
parents?: NodeInstance[]; parents?: NodeInstance[];
children?: NodeInstance[]; children?: NodeInstance[];
inputNodes?: Record<string, NodeInstance>; inputNodes?: Record<string, NodeInstance>;
type?: NodeDefinition; type?: NodeDefinition; // we should probably remove this and rely on registry.getNode(nodeType)
downX?: number; downX?: number;
downY?: number; downY?: number;
x?: number; x?: number;

View File

@@ -11,51 +11,38 @@ pub fn decode_float(bits: i32) -> f32 {
} }
#[inline] #[inline]
pub unsafe fn read_i32(ptr: *const i32) -> i32 { pub fn read_i32(ptr: i32) -> i32 {
*ptr unsafe {
} let _ptr = ptr as *const 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<i32> {
std::slice::from_raw_parts(ptr, len).to_vec()
}
#[inline]
pub unsafe fn read_f32_slice(ptr: *const i32, len: usize) -> Vec<f32> {
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)
} }
} }
#[inline] #[inline]
pub unsafe fn read_i32_default(ptr: *const i32, default: i32) -> i32 { pub fn read_f32(ptr: i32) -> f32 {
if ptr.is_null() { unsafe {
default let _ptr = ptr as *const i32;
} else { f32::from_bits(*_ptr as u32)
read_i32(ptr) }
}
#[inline]
pub fn read_i32_slice(tuple: (i32, i32)) -> Vec<i32> {
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<f32> {
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()
} }
} }

View File

@@ -27,7 +27,6 @@ export function createWasmWrapper(buffer: ArrayBuffer, memory: WebAssembly.Memor
exports = instance.exports as NodariumExports; exports = instance.exports as NodariumExports;
function execute(outputPos: number, args: number[]): number { function execute(outputPos: number, args: number[]): number {
console.log('WASM_WRAPPER', { outputPos, args });
return exports.execute(outputPos, ...args); return exports.execute(outputPos, ...args);
} }