Rewrite core around shared rows and render graphs

Co-authored-by: Heaust Azure <heaust.azure@gmail.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a01380-b478-77d0-84a0-102880a5c5ae
This commit is contained in:
Amp
2026-08-19 15:30:32 +00:00
co-authored by heaust
parent d34722d66d
commit f12c7c9603
118 changed files with 687 additions and 34500 deletions
-40
View File
@@ -1,40 +0,0 @@
# How Yawn fits together
Yawn has two public boundaries: **worker communication** for small, infrequent operations and **shared render data** for values that change often. Everything else is an authoring or convenience layer outside core.
```text
JSO / fluent builder ─┐
├──▶ canonical DAG AST ─▶ S-expression ─▶ render worker
FXNode snapshot ─────┘ │
├─ graph compiler
glTF import worker ───── shared upload array ────────────────────┤
├─ transient allocator
any browser thread ─── lifecycle messages ──────────────────────┤
any browser thread ─── atomic SOA writes ────────────────────────┘
```
## What core owns
`@yawn/core` owns only the protocol client for render data and render graphs. The worker behind it owns graph validation, loadout preparation, transient lifetime analysis, GPU allocation, and rendering.
Core does **not** own a camera module, scene object model, glTF parser, shader library, editor, or picking system. Camera and material values are ordinary render-data columns. Higher-level objects are optional addon views over those columns.
## The graph is the program
Every frontend must produce `@yawn/render-graph-ast` data. A node is named, while an input contains one or more `{ node, socket }` references. Reusing the same reference creates fan-out, so the format describes a DAG instead of duplicating a tree.
Render and compute pipeline declarations are part of that AST. Their WGSL and state are compiled into a prepared loadout, not linked into core.
## The SOA is the mutable scene
Shared arrays are 64-byte aligned and each row stride is a multiple of 16 bytes. The standard columns cover mesh, instance, camera, and material data. Applications can request additional mesh-, instance-, or fixed-domain arrays; domain arrays grow with the corresponding render-data capacity.
Lifecycle operations such as allocating a column, importing an asset, compiling a graph, or creating an instance cross the command boundary. A frame-rate transform, camera, classification, or material update writes the existing SAB row directly.
## Thread placement is a choice
The JS client only requires a Worker-like endpoint. A main thread can own it, or another worker can connect through a `MessagePort`. Shared descriptors can be passed to additional workers, which can then read or write the same SOA data without proxying every update through the main thread.
::: warning Cross-origin isolation is required
Serve the application with `Cross-Origin-Opener-Policy: same-origin` and `Cross-Origin-Embedder-Policy: require-corp`. `npm run examples` supplies both headers.
:::
-84
View File
@@ -1,84 +0,0 @@
# Your first scene
This tutorial composes Yawn the same way an application does: create the worker transport, wait for shared render data, compile a graph, import an asset, and activate the prepared loadout.
## 1. Start core
Create one renderer worker and transfer an `OffscreenCanvas` to it. `YawnCore` is deliberately transport-oriented; your bootstrap owns canvas sizing and worker construction.
```js
import { YawnCore } from "@yawn/core";
const canvas = document.querySelector("canvas");
canvas.width = Math.round(canvas.clientWidth * devicePixelRatio);
canvas.height = Math.round(canvas.clientHeight * devicePixelRatio);
const offscreen = canvas.transferControlToOffscreen();
const worker = new Worker(new URL("./render-worker.js", import.meta.url), {
type: "module",
});
const core = new YawnCore({ worker });
worker.postMessage({ type: "init", canvas: offscreen }, [offscreen]);
await core.ready;
```
`ready` resolves after core receives the standard SOA descriptors. From that point, `core.array("camera.state")` and the other built-in columns are safe to access.
## 2. Compile a graph
The optional default-pipelines addon supplies scene WGSL. A JSO graph places those declarations beside graph nodes, then the graph addon serializes the canonical AST for core.
```js
import { loadGraph } from "@yawn/render-graph-js";
import { defaultPipelines } from "@yawn/default-pipelines";
const graph = {
id: "main",
revision: 1,
pipelines: defaultPipelines,
nodes: completeSceneNodes,
};
const compiled = await loadGraph(core, graph);
```
Compilation validates the DAG, removes dead work, computes transient resource lifetimes, aliases compatible resources, and allocates the resulting loadout before returning its ID.
## 3. Import render data
The glTF addon fetches and parses in its own worker. It asks core for a fixed shared upload array, writes the packet into that SAB, then sends only the array ID and byte count for the commit.
```js
import { GltfImporter } from "@yawn/gltf-import";
import { MeshHandles } from "@yawn/mesh-handles";
const importer = new GltfImporter(core);
const result = await importer.load("/assets/scene.glb");
const handles = new MeshHandles(core);
const meshes = handles.fromImportedScene(result);
importer.dispose();
```
## 4. Activate the loadout
Switching is transactional from the application's perspective: the previously active loadout keeps rendering until the prepared graph becomes active.
```js
await core.switchCompiledGraph(compiled.compiledId);
meshes[0].defaultInstance.setTransform(nextTransform); // direct SAB write
```
Use messages for setup and teardown. Use shared writes for values that are already present and can change every frame.
<Playground
id="first-scene"
title="Complete first scene"
description="Open the editor to change the procedural loadout or inspect live telemetry."
/>
## Next steps
- Learn why these boundaries exist in [How Yawn fits together](./architecture).
- Author graphs with [plain objects, a fluent builder, or FXNode](../packages/render-graph).
- Add custom shared columns in [Core and render data](../packages/core).
- Use familiar objects in [Conventional handles](../packages/mesh-handles).