109 lines
3.9 KiB
TypeScript
109 lines
3.9 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import {
|
|
GEOMETRY_NODE_FIELD_BUDGET,
|
|
parseGeometryNodeFieldMaterializationBatch,
|
|
type GeometryNodeDomainCardinalityIR,
|
|
} from "../../protocol/geometry-nodes";
|
|
import { parseDepsgraphEvaluation, type DepsgraphEvaluationIR } from "../../protocol/depsgraph";
|
|
|
|
const root = path.resolve(import.meta.dirname, "../../..");
|
|
const fixture = fs.readFileSync(path.join(root, "tests/files/web/geometry_node_allowlist_evaluator.blend"));
|
|
|
|
test("M10-04 binds field conversion budgets to native mesh domain cardinality", async ({ page }) => {
|
|
test.setTimeout(120_000);
|
|
await page.goto("/");
|
|
const result = await page.evaluate(async ({ bytes }) => {
|
|
const { WebEngineClient } = await import("/src/engine-client/WebEngineClient.ts");
|
|
const source = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer;
|
|
const client = new WebEngineClient({ timeoutMs: 90_000 });
|
|
await client.init();
|
|
await client.openBlend(source);
|
|
const evaluated = await client.evaluateDepsgraph();
|
|
client.terminate();
|
|
const mesh = evaluated.depsgraph.meshes.find((candidate) =>
|
|
candidate.objectId === "object:M10GN_StoreAttribute");
|
|
if (!mesh?.domainCardinality) throw new Error("Store Named Attribute domain cardinality is missing");
|
|
return {
|
|
depsgraph: evaluated.depsgraph,
|
|
domainCardinality: mesh.domainCardinality,
|
|
fieldMaterializations: mesh.fieldMaterializations ?? [],
|
|
attributes: mesh.attributes ?? {},
|
|
};
|
|
}, { bytes: new Uint8Array(fixture) });
|
|
|
|
expect(result.domainCardinality).toEqual({
|
|
POINT: 8,
|
|
EDGE: 12,
|
|
FACE: 6,
|
|
CORNER: 24,
|
|
CURVE: 0,
|
|
INSTANCE: 0,
|
|
LAYER: 0,
|
|
});
|
|
expect(result.fieldMaterializations).toEqual([{
|
|
schemaVersion: 1,
|
|
fieldId: "attribute:m10_value",
|
|
domain: "POINT",
|
|
dataType: "FLOAT",
|
|
elementCount: 8,
|
|
scalarValueCount: 8,
|
|
materializedByteLength: 32,
|
|
transport: "JSON",
|
|
}]);
|
|
expect(result.attributes.m10_value.values).toHaveLength(8);
|
|
expect(result.attributes.m10_value.values.every((value: number) => value === 0.375)).toBe(true);
|
|
|
|
const rejects = (mutate: (candidate: DepsgraphEvaluationIR) => void): void => {
|
|
const candidate = structuredClone(result.depsgraph);
|
|
mutate(candidate);
|
|
expect(() => parseDepsgraphEvaluation(candidate)).toThrow();
|
|
};
|
|
rejects((candidate) => {
|
|
const target = candidate.meshes.find((entry) => entry.objectId === "object:M10GN_StoreAttribute")!;
|
|
target.domainCardinality!.POINT += 1;
|
|
});
|
|
rejects((candidate) => {
|
|
const target = candidate.meshes.find((entry) => entry.objectId === "object:M10GN_StoreAttribute")!;
|
|
target.fieldMaterializations![0].materializedByteLength += 4;
|
|
});
|
|
rejects((candidate) => {
|
|
const target = candidate.meshes.find((entry) => entry.objectId === "object:M10GN_StoreAttribute")!;
|
|
(target.fieldMaterializations![0] as unknown as Record<string, unknown>).values = [0];
|
|
});
|
|
|
|
const common = {
|
|
schemaVersion: 1 as const,
|
|
graphId: "node-group:M10GN_StoreAttributeGraph",
|
|
graphHash: "a".repeat(64),
|
|
revision: 1,
|
|
transport: "JSON" as const,
|
|
domainCardinality: result.domainCardinality as GeometryNodeDomainCardinalityIR,
|
|
};
|
|
const batch = parseGeometryNodeFieldMaterializationBatch([
|
|
{
|
|
...common,
|
|
fieldId: "field:point-to-corner",
|
|
sourceDomain: "POINT",
|
|
targetDomain: "CORNER",
|
|
dataType: "FLOAT",
|
|
},
|
|
{
|
|
...common,
|
|
fieldId: "field:constant-offset",
|
|
sourceDomain: "CONSTANT",
|
|
targetDomain: "POINT",
|
|
dataType: "VECTOR",
|
|
},
|
|
]);
|
|
expect(batch).toMatchObject({
|
|
fieldCount: 2,
|
|
domainConversionCount: 1,
|
|
materializedElementCount: 32,
|
|
materializedByteLength: 192,
|
|
});
|
|
expect(batch.fields.every((field) => field.scalarValueCount <=
|
|
GEOMETRY_NODE_FIELD_BUDGET.maxJsonScalarValuesPerField)).toBe(true);
|
|
});
|