Files
workinf_Blender_Wasm/web/tests/e2e/shader-compile.spec.ts
mes123456 0fe8d2bb56
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled
Advance M8-M11 parity workflows
2026-08-17 04:37:07 -04:00

158 lines
7.6 KiB
TypeScript

import { expect, test } from "@playwright/test";
import { createHash } from "node:crypto";
import fs from "node:fs";
import path from "node:path";
const root = path.resolve(import.meta.dirname, "../../..");
const expected = JSON.parse(fs.readFileSync(path.join(root, "tests/golden/M10-07/shader-compile.json"), "utf8"));
const blendBytes = fs.readFileSync(path.join(root, expected.fixture));
function sockets() {
return {
rgb: [{ id: "color", name: "Color", direction: "OUTPUT", dataType: "COLOR", defaultValue: [0.15, 0.25, 0.35, 1] }],
value: [{ id: "value", name: "Value", direction: "OUTPUT", dataType: "VALUE", defaultValue: 0.2 }],
math: [
{ id: "a", name: "Value", direction: "INPUT", dataType: "VALUE", defaultValue: 0 },
{ id: "b", name: "Value_001", direction: "INPUT", dataType: "VALUE", defaultValue: 0 },
{ id: "value", name: "Value", direction: "OUTPUT", dataType: "VALUE" },
],
principled: [
{ id: "base", name: "Base Color", direction: "INPUT", dataType: "COLOR", defaultValue: [0.2, 0.3, 0.4, 1] },
{ id: "roughness", name: "Roughness", direction: "INPUT", dataType: "VALUE", defaultValue: 0.5 },
{ id: "bsdf", name: "BSDF", direction: "OUTPUT", dataType: "SHADER" },
],
output: [{ id: "surface", name: "Surface", direction: "INPUT", dataType: "SHADER" }],
};
}
test("M10-07 produces a bounded compile report from the real Main Shader graph", async ({ page }) => {
test.setTimeout(120_000);
expect(createHash("sha256").update(blendBytes).digest("hex")).toBe(expected.fixtureSha256);
await page.goto("/");
const result = await page.evaluate(async ({ bytes, socketSet }) => {
const { WebEngineClient } = await import("/src/engine-client/WebEngineClient.ts");
const client = new WebEngineClient({ timeoutMs: 60_000 });
await client.init();
const source = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer;
const opened = await client.openBlend(source);
const material = opened.snapshot.materials[0];
if (!material) throw new Error("basic fixture has no editable material");
const graph = {
schemaVersion: 1 as const,
id: "shader:m10-07",
materialId: material.id,
outputNodeId: "output",
nodes: [
{ id: "rgb", type: "RGB" as const, name: "RGB", sockets: socketSet.rgb },
{ id: "a", type: "VALUE" as const, name: "A", sockets: socketSet.value },
{ id: "b", type: "VALUE" as const, name: "B", sockets: [{ ...socketSet.value[0], defaultValue: 0.22 }] },
{ id: "math", type: "MATH" as const, name: "Add", properties: { operation: "ADD" as const }, sockets: socketSet.math },
{ id: "principled", type: "PRINCIPLED" as const, name: "Principled", sockets: socketSet.principled },
{ id: "output", type: "MATERIAL_OUTPUT" as const, name: "Output", sockets: socketSet.output },
],
links: [
{ fromNodeId: "rgb", fromSocketId: "color", toNodeId: "principled", toSocketId: "base" },
{ fromNodeId: "a", fromSocketId: "value", toNodeId: "math", toSocketId: "a" },
{ fromNodeId: "b", fromSocketId: "value", toNodeId: "math", toSocketId: "b" },
{ fromNodeId: "math", fromSocketId: "value", toNodeId: "principled", toSocketId: "roughness" },
{ fromNodeId: "principled", fromSocketId: "bsdf", toNodeId: "output", toSocketId: "surface" },
],
};
const applied = await client.applyCommand({ type: "setShaderGraph", materialId: material.id, graph });
const report = applied.shaderCompile;
const after = await client.snapshot();
client.terminate();
return {
openedHash: material.shaderGraphHash,
status: report?.status,
taskId: report?.taskId,
backend: report?.backend,
graphHash: report?.graphHash,
roughness: report?.material?.roughness,
nodeTypes: report?.compiledNodeTypes,
revisionDelta: after.snapshot.revision - opened.snapshot.revision,
};
}, { bytes: new Uint8Array(blendBytes), socketSet: sockets() });
expect(result.status).toBe("COMPILED");
expect(result.openedHash).toMatch(/^[0-9a-f]{64}$/);
expect(result.taskId).toBe("M10-07");
expect(result.backend).toBe(expected.backend);
expect(result.graphHash).toMatch(/^[0-9a-f]{64}$/);
expect(result.roughness).toBeCloseTo(expected.expectedMathResult, 5);
expect(result.nodeTypes).toEqual(expect.arrayContaining(expected.allowlist.filter((type: string) => !["IMAGE_TEXTURE", "NORMAL_MAP"].includes(type))));
expect(result.revisionDelta).toBe(1);
});
test("M10-07 shared viewport material path compiles Image/Normal/Math bindings", async ({ page }) => {
await page.goto("/");
const result = await page.evaluate(async () => {
const { createPBRMaterial } = await import("/src/three-adapter/pbr.ts");
const material = createPBRMaterial({
id: "material:viewport",
name: "Viewport",
baseColor: [0.8, 0.8, 0.8, 1],
roughness: 0.5,
metallic: 0,
emissionColor: [0, 0, 0, 1],
alpha: 1,
ior: 1.45,
nodes: [
{ id: "a", type: "VALUE", name: "A", defaultValue: [0.2] },
{ id: "b", type: "VALUE", name: "B", defaultValue: [0.22] },
{ id: "math", type: "MATH", name: "Add", properties: { operation: "ADD" } },
{ id: "image", type: "IMAGE_TEXTURE", name: "Image", imageId: "image:normal" },
{ id: "normal", type: "NORMAL_MAP", name: "Normal" },
{ id: "principled", type: "PRINCIPLED", name: "Principled" },
{ id: "output", type: "OUTPUT", name: "Output" },
],
links: [
{ fromNodeId: "a", fromSocket: "Value", toNodeId: "math", toSocket: "Value" },
{ fromNodeId: "b", fromSocket: "Value", toNodeId: "math", toSocket: "Value_001" },
{ fromNodeId: "math", fromSocket: "Value", toNodeId: "principled", toSocket: "Roughness" },
{ fromNodeId: "image", fromSocket: "Color", toNodeId: "normal", toSocket: "Color" },
{ fromNodeId: "normal", fromSocket: "Normal", toNodeId: "principled", toSocket: "Normal" },
{ fromNodeId: "principled", fromSocket: "BSDF", toNodeId: "output", toSocket: "Surface" },
],
});
const compile = material.userData.shaderCompile;
const report = {
status: compile.status,
graphHash: compile.graphHash,
roughness: material.roughness,
textures: compile.textureBindings,
};
material.dispose();
return report;
});
expect(result.status).toBe("COMPILED");
expect(result.graphHash).toMatch(/^[0-9a-f]{64}$/);
expect(result.roughness).toBeCloseTo(expected.expectedMathResult, 5);
expect(result.textures).toEqual([{ imageId: "image:normal", usage: "NORMAL" }]);
});
test("M10-07 compiler blocks a non-declared node without mutating the input graph", async ({ page }) => {
await page.goto("/");
const result = await page.evaluate(async () => {
const { createPBRMaterial } = await import("/src/three-adapter/pbr.ts");
const material = {
id: "material:blocked",
name: "Blocked",
baseColor: [1, 1, 1, 1],
roughness: 0.5,
metallic: 0,
emissionColor: [0, 0, 0, 1],
alpha: 1,
ior: 1.45,
nodes: [{ id: "mix", type: "UNSUPPORTED", name: "Mix" }, { id: "output", type: "OUTPUT", name: "Output" }],
links: [],
} as const;
const before = JSON.stringify(material);
const threeMaterial = createPBRMaterial(material);
const report = threeMaterial.userData.shaderCompile;
threeMaterial.dispose();
return { status: report.status, code: report.issues[0]?.code, preserved: JSON.stringify(material) === before };
});
expect(result).toEqual({ status: "BLOCKED", code: expected.unsupportedNodeCode, preserved: true });
});