Rebuild core around render graph AST and shared memory

Amp-Thread-ID: https://ampcode.com/threads/T-01a01380-b478-77d0-84a0-102880a5c5ae
Co-authored-by: Heaust Azure <heaust.azure@gmail.com>
This commit is contained in:
Amp
2026-08-18 11:58:31 +00:00
co-authored by heaust
parent a23ef37b4d
commit 0e44917f9e
101 changed files with 4409 additions and 2610 deletions
+27
View File
@@ -0,0 +1,27 @@
import {
createGraphAst,
reference,
serializeGraphAst,
} from "@yawn/render-graph-ast";
const expression = (id, inputs = {}) => ({
id,
state: "enabled",
executor: { key: "and", version: 2 },
parameters: {},
inputs,
});
/** Build one DAG whose shared output fans out to two consumers. */
export function canonicalAstExample() {
const ast = createGraphAst({
id: "shared_dag",
revision: 1,
nodes: [
expression("source"),
expression("left", { inputs: [reference("source", "value")] }),
expression("right", { inputs: [reference("source", "value")] }),
],
});
return { ast, source: serializeGraphAst(ast) };
}
+18
View File
@@ -0,0 +1,18 @@
import { graphFromObject } from "@yawn/render-graph-js";
/** Author a graph with an ordinary JavaScript object and receive canonical AST. */
export function jsoGraphExample() {
return graphFromObject({
id: "jso_graph",
revision: 1,
nodes: [
{
id: "mesh",
state: "enabled",
executor: { key: "mesh", version: 2 },
parameters: {},
inputs: {},
},
],
});
}
+12
View File
@@ -0,0 +1,12 @@
import { RenderGraph, ref } from "@yawn/render-graph-js";
/** Build the same sort of DAG with the small mutable authoring facade. */
export function fluentGraphExample() {
return new RenderGraph("fluent_graph", 1)
.node("source", "and", { version: 2 })
.node("consumer", "not", {
version: 1,
inputs: { operand: [ref("source", "value")] },
})
.ast();
}
+19
View File
@@ -0,0 +1,19 @@
import { defaultPipelines } from "@yawn/default-pipelines";
import { adaptFxNodeSnapshot } from "@yawn/render-graph-fxnode";
import { CATALOG_VERSION, GRAPH_ID } from "@yawn/render-graph-fxnode/catalog";
/** Export a minimal FXNode authoring document through the shared AST boundary. */
export function fxNodeExportExample() {
return adaptFxNodeSnapshot(
{
graphId: GRAPH_ID,
catalogVersion: CATALOG_VERSION,
nodes: [],
links: [],
metadata: {},
version: 1,
},
1,
{ pipelines: defaultPipelines },
);
}
+12
View File
@@ -0,0 +1,12 @@
import { defaultPipelines } from "@yawn/default-pipelines";
import { RenderGraph } from "@yawn/render-graph-js";
/** Copy the optional package's external programs into a graph AST. */
export function defaultPipelineExample() {
const graph = new RenderGraph("default_programs", 1);
for (const pipeline of defaultPipelines.render)
graph.renderPipeline(pipeline);
for (const pipeline of defaultPipelines.compute)
graph.computePipeline(pipeline);
return graph.ast();
}
@@ -0,0 +1,31 @@
import { RenderGraph } from "@yawn/render-graph-js";
export const simpleSceneShader = /* wgsl */ `
@group(1) @binding(0) var<uniform> view_projection: mat4x4<f32>;
struct Input {
@location(0) position: vec3<f32>,
@location(3) model_0: vec4<f32>,
@location(4) model_1: vec4<f32>,
@location(5) model_2: vec4<f32>,
@location(6) model_3: vec4<f32>,
}
@vertex fn vertex_main(input: Input) -> @builtin(position) vec4<f32> {
let model = mat4x4<f32>(input.model_0, input.model_1, input.model_2, input.model_3);
return view_projection * model * vec4(input.position, 1.0);
}
@fragment fn fragment_main() -> @location(0) vec4<f32> {
return vec4(0.2, 0.7, 1.0, 1.0);
}`;
/** Supply scene WGSL and entry points as graph data, never as core source. */
export function customRenderPipelineExample() {
return new RenderGraph("custom_render_program", 1)
.renderPipeline({
name: "ground_plane",
shader: simpleSceneShader,
vertexEntry: "vertex_main",
fragmentEntry: "fragment_main",
doubleSided: false,
})
.ast();
}
+18
View File
@@ -0,0 +1,18 @@
import { RenderGraph } from "@yawn/render-graph-js";
export const initializeShader = /* wgsl */ `
@compute @workgroup_size(8, 1, 1)
fn initialize() {}
`;
/** Declare a binding-free compute pass that runs before graph render passes. */
export function computePipelineExample() {
return new RenderGraph("compute_program", 1)
.computePipeline({
name: "initialize",
shader: initializeShader,
entry: "initialize",
dispatch: [4, 1, 1],
})
.ast();
}
@@ -0,0 +1,19 @@
import { loadGraph } from "@yawn/render-graph-js";
/** Compile a complete AST/JSO and make its prepared loadout active. */
export async function compileAndSwitch(core, graph) {
const compiled = await loadGraph(core, graph);
try {
await core.switchCompiledGraph(compiled.compiledId);
return compiled;
} catch (error) {
await core.dropCompiledGraph(compiled.compiledId).catch(() => {});
throw error;
}
}
/** Return to clear-only mode before releasing an active compiled loadout. */
export async function dropActiveGraph(core, compiled) {
await core.switchToImmediate();
await core.dropCompiledGraph(compiled.compiledId);
}
@@ -0,0 +1,13 @@
import { GltfImporter } from "@yawn/gltf-import";
import { MeshHandles } from "@yawn/mesh-handles";
/** Fetch a glTF URL in the import worker and wrap the resulting protocol handles. */
export async function importGltf(core, url, options) {
const importer = new GltfImporter(core);
try {
const imported = await importer.load(url, options);
return new MeshHandles(core).fromImportedScene(imported);
} finally {
importer.dispose();
}
}
+14
View File
@@ -0,0 +1,14 @@
export const identityTransform = Object.freeze([
1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
]);
/** Create a conventional instance object from an imported mesh handle. */
export function createInstance(mesh, transform = identityTransform) {
return mesh.createInstance(transform);
}
/** Frequent mutations stay on the shared-memory path exposed by the handle addon. */
export function updateInstance(instance, transform, typeWords) {
instance.setTransform(transform);
instance.setType(typeWords);
}
+14
View File
@@ -0,0 +1,14 @@
/** Allocate one SIMD-aligned velocity row for every live instance slot. */
export function createVelocityColumn(core) {
return core.allocateArray({
name: "instance.velocity",
domain: "instance",
scalar: "f32",
lanes: 4,
});
}
/** Mutate an existing row directly; no renderer command message is emitted. */
export function setVelocity(column, instance, xyz) {
column.write(instance.handle[0], [xyz[0], xyz[1], xyz[2], 0]);
}
@@ -0,0 +1,9 @@
/** Write a new model matrix through core's generation-guarded shared SOA column. */
export function animateInstance(core, instance, transform) {
core.setInstanceTransform(instance.handle, transform);
}
/** Write the opaque 512-bit instance classification used by graph predicates. */
export function classifyInstance(core, instance, words) {
core.setInstanceType(instance.handle, words);
}
+9
View File
@@ -0,0 +1,9 @@
import { MeshHandles } from "@yawn/mesh-handles";
/** Query the optional snapshot/BVH worker and receive wrapped instance handles. */
export function pickNearest(core, origin, direction) {
return new MeshHandles(core).pickRay(origin, direction, {
maxDistance: 10_000,
maxHits: 1,
});
}
+14
View File
@@ -0,0 +1,14 @@
import { YawnCore } from "@yawn/core";
/** Connect from any worker using a MessagePort with the Worker-like transport API. */
export async function connectFromWorker(port, options = {}) {
const core = new YawnCore({
worker: port,
memory: options.memory,
ringPtr: options.ringPtr,
pickingWorkerFactory: options.pickingWorkerFactory,
free: options.free,
});
await core.ready;
return core;
}
+19
View File
@@ -0,0 +1,19 @@
import { culling } from "../render-graph-studio/render-graph/presets.js";
import { importGltf } from "./09-gltf-import-worker.js";
import { compileAndSwitch, dropActiveGraph } from "./08-compile-and-switch.js";
/** Combine the complete JSO graph, shared glTF import, and mesh-handle facade. */
export async function loadCompleteScene(core, gltfUrl) {
const compiled = await compileAndSwitch(core, culling);
try {
const meshes = await importGltf(core, gltfUrl);
return {
compiled,
meshes,
dispose: () => dropActiveGraph(core, compiled),
};
} catch (error) {
await dropActiveGraph(core, compiled).catch(() => {});
throw error;
}
}
+26
View File
@@ -0,0 +1,26 @@
# Yawn addon cookbook
These recipes deliberately avoid another application framework or renderer wrapper.
Import the function you need and pass the same `YawnCore` instance to every addon.
| Recipe | Demonstrates |
| --- | --- |
| `01-canonical-ast.js` | A shared DAG output and canonical S-expression serialization |
| `02-jso-graph.js` | Plain JavaScript object authoring |
| `03-fluent-builder.js` | Fluent render-graph authoring |
| `04-fxnode-export.js` | Exporting an FXNode snapshot to the canonical AST |
| `05-default-pipelines.js` | Attaching optional external WGSL declarations |
| `06-custom-render-pipeline.js` | Supplying a custom scene render shader |
| `07-compute-pipeline.js` | Supplying a binding-free compute pass |
| `08-compile-and-switch.js` | Compiling, activating, and safely cleaning up a graph |
| `09-gltf-import-worker.js` | Fetching a glTF URL into shared memory from a worker |
| `10-mesh-instances.js` | Creating and mutating conventional instance handles |
| `11-custom-soa-column.js` | Allocating an instance-sized shared SOA column |
| `12-direct-sab-animation.js` | Updating transforms through generation-guarded SAB writes |
| `13-bvh-picking.js` | Ray picking through the mesh-handles addon |
| `14-worker-to-worker.js` | Using core from another worker through a `MessagePort` |
| `15-complete-scene.js` | Combining the graph, glTF, and mesh addons |
Recipes 17 isolate graph authoring concepts, so their ASTs are intentionally
fragments rather than complete renderable loadouts. Recipe 15 uses the complete
scene graph from `render-graph-studio` when an end-to-end example is needed.
+15
View File
@@ -0,0 +1,15 @@
export * from "./01-canonical-ast.js";
export * from "./02-jso-graph.js";
export * from "./03-fluent-builder.js";
export * from "./04-fxnode-export.js";
export * from "./05-default-pipelines.js";
export * from "./06-custom-render-pipeline.js";
export * from "./07-compute-pipeline.js";
export * from "./08-compile-and-switch.js";
export * from "./09-gltf-import-worker.js";
export * from "./10-mesh-instances.js";
export * from "./11-custom-soa-column.js";
export * from "./12-direct-sab-animation.js";
export * from "./13-bvh-picking.js";
export * from "./14-worker-to-worker.js";
export * from "./15-complete-scene.js";