feat: add render graph driven renderer architecture
Amp-Thread-ID: https://ampcode.com/threads/T-019f9d91-77c1-7206-a60f-ed6554ce92ab Co-authored-by: Heaust Azure <heaust.azure@gmail.com>
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
const expectedNodeTypes = [
|
||||
"fxnode.common.frame",
|
||||
"fxnode.common.reroute",
|
||||
"fxnode.common.group-input",
|
||||
"fxnode.common.group-output",
|
||||
"fxnode.shader.value",
|
||||
"fxnode.shader.color",
|
||||
"fxnode.shader.math",
|
||||
"fxnode.shader.vector-math",
|
||||
"fxnode.shader.mix",
|
||||
"fxnode.shader.color-ramp",
|
||||
"fxnode.shader.texture-coordinate",
|
||||
"fxnode.shader.noise-texture",
|
||||
"fxnode.shader.image-texture",
|
||||
"fxnode.shader.principled-bsdf",
|
||||
"fxnode.shader.material-output",
|
||||
"fxnode.geometry.position",
|
||||
"fxnode.geometry.mesh-cube",
|
||||
"fxnode.geometry.set-position",
|
||||
"fxnode.geometry.transform-geometry",
|
||||
"fxnode.geometry.join-geometry",
|
||||
"fxnode.compositor.image",
|
||||
"fxnode.compositor.color-balance",
|
||||
] as const;
|
||||
|
||||
test("example installs every startup node through the live worker API", async ({ page }) => {
|
||||
await page.addInitScript(() => {
|
||||
const sent: unknown[] = [],
|
||||
received: unknown[] = [];
|
||||
(
|
||||
window as unknown as { applicationStartupMessages: { sent: unknown[]; received: unknown[] } }
|
||||
).applicationStartupMessages = { sent, received };
|
||||
const NativeWorker = Worker;
|
||||
class StartupWorker extends NativeWorker {
|
||||
constructor(url: string | URL, options?: WorkerOptions) {
|
||||
super(url, options);
|
||||
super.addEventListener("message", (event) => received.push(structuredClone(event.data)));
|
||||
}
|
||||
override postMessage(message: unknown, options?: StructuredSerializeOptions | Transferable[]) {
|
||||
sent.push(structuredClone(message));
|
||||
super.postMessage(message, options as StructuredSerializeOptions);
|
||||
}
|
||||
}
|
||||
Object.defineProperty(window, "Worker", { configurable: true, writable: true, value: StartupWorker });
|
||||
});
|
||||
await page.goto("/examples/blender/");
|
||||
await page.evaluate(() => window.fxnodeExample.ready);
|
||||
const result = await page.evaluate(async () => {
|
||||
const messages = (window as unknown as { applicationStartupMessages: { sent: any[]; received: any[] } })
|
||||
.applicationStartupMessages,
|
||||
init = messages.sent.find((message) => message.type === "init"),
|
||||
updates = messages.sent.filter((message) => message.type === "composition.update"),
|
||||
stateSet = messages.sent.find((message) => message.type === "state.set"),
|
||||
api = window.fxnodeExample.root!;
|
||||
const updateIds = new Set(updates.map((message) => message.id)),
|
||||
compositionReceipts = messages.received
|
||||
.filter((message) => message.type === "response" && updateIds.has(message.id))
|
||||
.map((message) => message.value),
|
||||
stateSetIndex = messages.sent.indexOf(stateSet),
|
||||
lastUpdateIndex = Math.max(...updates.map((message) => messages.sent.indexOf(message))),
|
||||
snapshot = await api.getState(),
|
||||
undo = await api.undo({ expectedVersion: 1 }),
|
||||
empty = await api.getState(),
|
||||
redo = await api.redo({ expectedVersion: 2 }),
|
||||
restored = await api.getState();
|
||||
return {
|
||||
initKeys: Object.keys(init).sort(),
|
||||
updates: updates.map((message) => ({
|
||||
kind: message.update.kind,
|
||||
id: message.update.id,
|
||||
expected: message.expected,
|
||||
})),
|
||||
compositionReceipts,
|
||||
stateSet,
|
||||
stateSetIndex,
|
||||
lastUpdateIndex,
|
||||
snapshot,
|
||||
undo,
|
||||
empty,
|
||||
redo,
|
||||
restored,
|
||||
saved: await api.save(),
|
||||
initialLayout: await (await fetch("/examples/blender/initialLayout.json")).json(),
|
||||
};
|
||||
});
|
||||
expect(result.initKeys).toEqual([
|
||||
"applicationId",
|
||||
"applicationVersion",
|
||||
"historyLimit",
|
||||
"id",
|
||||
"protocol",
|
||||
"resources",
|
||||
"type",
|
||||
]);
|
||||
expect(result.updates).toHaveLength(31);
|
||||
expect(result.updates.slice(0, 9).map((update) => update.kind)).toEqual([
|
||||
"theme.set",
|
||||
"header-styles.set",
|
||||
...Array(6).fill("socket.compose"),
|
||||
"compatibility.set",
|
||||
]);
|
||||
expect(result.updates.slice(9).map((update) => update.kind)).toEqual(Array(22).fill("node.compose"));
|
||||
expect(result.updates.slice(9).map((update) => update.id)).toEqual(expectedNodeTypes);
|
||||
expect(result.updates.map((update) => update.expected)).toEqual(Array(31).fill({ kind: "current" }));
|
||||
expect(result.compositionReceipts).toHaveLength(31);
|
||||
expect(
|
||||
result.compositionReceipts.every((receipt) => receipt.graphVersion === 0 && receipt.graphChanged === false),
|
||||
).toBe(true);
|
||||
expect(result.stateSetIndex).toBeGreaterThan(result.lastUpdateIndex);
|
||||
expect(result.stateSet.expected).toEqual({ kind: "current" });
|
||||
expect(result.stateSet.state.schemaVersion).toBeUndefined();
|
||||
expect(result.stateSet.state.version).toBeUndefined();
|
||||
expect(result.stateSet.state.nodes.every((node: any) => node.known === true)).toBe(true);
|
||||
expect(result.snapshot.version).toBe(1);
|
||||
expect(result.snapshot.nodes.length).toBe(result.initialLayout.nodes.length);
|
||||
expect(result.snapshot.nodes.every((node: any) => node.known)).toBe(true);
|
||||
expect(result.undo).toEqual({ status: "committed", version: 2 });
|
||||
expect(result.empty).toMatchObject({ version: 2, nodes: [], links: [] });
|
||||
expect(result.redo).toEqual({ status: "committed", version: 3 });
|
||||
expect(result.restored.nodes).toEqual(result.snapshot.nodes);
|
||||
expect(result.restored.links).toEqual(result.snapshot.links);
|
||||
expect(result.saved).toEqual({
|
||||
schemaVersion: 2,
|
||||
...result.initialLayout,
|
||||
nodes: result.initialLayout.nodes.map(({ known: _known, ...node }: any) => node),
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user