78 lines
4.2 KiB
JavaScript
78 lines
4.2 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import test from "node:test";
|
|
import ts from "typescript";
|
|
|
|
const root = path.resolve(import.meta.dirname, "../../..");
|
|
const sourcePath = path.join(root, "web/protocol/render-budget.ts");
|
|
const source = fs.readFileSync(sourcePath, "utf8")
|
|
.replace('import type { ErrorCode } from "./error";\n', "")
|
|
.replace('import { MAX_GPU_TEXTURE_ASSETS, MAX_GPU_TEXTURE_BYTES, MAX_GPU_TEXTURE_DIMENSION, type GPUTextureAsset } from "./render-assets";\n', "const MAX_GPU_TEXTURE_ASSETS = 256; const MAX_GPU_TEXTURE_BYTES = 64 * 1024 * 1024; const MAX_GPU_TEXTURE_DIMENSION = 16_384;\n")
|
|
.replace('import type { SceneSnapshotIR } from "./scene-ir";\n', "");
|
|
const transpiled = ts.transpileModule(source, {
|
|
compilerOptions: { module: ts.ModuleKind.ES2022, target: ts.ScriptTarget.ES2022 },
|
|
fileName: sourcePath,
|
|
reportDiagnostics: true,
|
|
});
|
|
assert.deepEqual(transpiled.diagnostics, []);
|
|
const budget = await import("data:text/javascript;base64," + Buffer.from(transpiled.outputText).toString("base64"));
|
|
const golden = JSON.parse(fs.readFileSync(path.join(root, "tests/golden/M11-03/render-resource-budget.json"), "utf8"));
|
|
|
|
function snapshot(lightCount) {
|
|
const lights = Array.from({ length: lightCount }, (_, index) => ({
|
|
id: `light:${index}`, lightType: 2, castsShadow: true,
|
|
}));
|
|
const nodes = lights.map((light, index) => ({
|
|
id: `object:light:${index}`, type: "LIGHT", visible: true, dataId: light.id,
|
|
}));
|
|
return { lights, nodes };
|
|
}
|
|
|
|
test("M11-03 freezes explicit Three WebGL2 and WebGPU product budgets", () => {
|
|
for (const [backend, expected] of [["THREE_WEBGL2", golden.webgl2], ["THREE_WEBGPU", golden.webgpu]]) {
|
|
const actual = budget.resolvePBRRenderBudget(backend);
|
|
for (const [field, value] of Object.entries(expected)) assert.equal(actual[field], value, `${backend}.${field}`);
|
|
}
|
|
const clamped = budget.resolvePBRRenderBudget("THREE_WEBGPU", {
|
|
maxLights: 32, maxShadowMaps: 4, maxShadowMapDimension: 1024, maxTextureDimension2D: 8192,
|
|
});
|
|
assert.deepEqual([clamped.maxLights, clamped.maxShadowMaps, clamped.shadowMapDimension, clamped.maxTextureDimension], [32, 4, 1024, 8192]);
|
|
assert.throws(() => budget.resolvePBRRenderBudget("THREE_WEBGPU", { maxLights: 0 }), /GPU_TEXTURE_BUDGET_EXCEEDED/);
|
|
});
|
|
|
|
test("M11-03 deterministically bounds lights and shadow maps", () => {
|
|
const report = budget.planPBRLightingBudget(snapshot(golden.overflow.requestedLights));
|
|
assert.equal(report.status, "BLOCKED");
|
|
assert.equal(report.requestedLights, golden.overflow.requestedLights);
|
|
assert.equal(report.renderedLightNodeIds.length, golden.overflow.renderedLights);
|
|
assert.equal(report.droppedLightNodeIds.length, golden.overflow.droppedLights);
|
|
assert.equal(report.requestedShadowMaps, golden.overflow.requestedShadowMaps);
|
|
assert.equal(report.shadowLightNodeIds.length, golden.overflow.renderedShadowMaps);
|
|
assert.equal(report.shadowBlockedLightNodeIds.length, golden.overflow.blockedShadowMaps);
|
|
assert.deepEqual(report.issues.map((issue) => issue.code), golden.overflow.codes);
|
|
assert.equal(report.renderedLightNodeIds[0], "object:light:0");
|
|
assert.equal(report.renderedLightNodeIds.at(-1), "object:light:13");
|
|
});
|
|
|
|
test("M11-03 rejects aggregate texture allocation before decode", () => {
|
|
const small = { assetId: "asset:small", imageId: "image:small", usage: "BASE_COLOR", width: 4, height: 4, byteLength: 16 };
|
|
assert.deepEqual(budget.planPBRTextureBudget([small]), {
|
|
schemaVersion: 1,
|
|
backend: "THREE_WEBGL2",
|
|
status: "READY",
|
|
budget: budget.resolvePBRRenderBudget("THREE_WEBGL2"),
|
|
requestedAssets: 1,
|
|
payloadBytes: 16,
|
|
decodedGPUBytes: 64,
|
|
maxRequestedDimension: 4,
|
|
issues: [],
|
|
});
|
|
const tooMany = Array.from({ length: 257 }, (_, index) => ({ ...small, assetId: `asset:${index}`, imageId: `image:${index}` }));
|
|
const report = budget.planPBRTextureBudget(tooMany);
|
|
assert.equal(report.status, "BLOCKED");
|
|
assert.equal(report.requestedAssets, 257);
|
|
assert.equal(report.issues[0].code, "GPU_TEXTURE_BUDGET_EXCEEDED");
|
|
assert.equal(budget.planPBRTextureBudget([{ ...small, width: 16_384, height: 16_384 }]).status, "BLOCKED");
|
|
});
|