Complete V1 performance and OOM release gates

This commit is contained in:
mes123456
2026-08-14 22:32:09 -04:00
parent 3ea9974eee
commit a3f3071c03
45 changed files with 4206 additions and 276 deletions

View File

@@ -1230,6 +1230,7 @@ test("commits and reopens a hash-bound NanoVDB project through OPFS", async ({ p
});
test("samples and integrates a real NanoVDB Float32 tree with WebGPU", async ({ page }) => {
test.setTimeout(90_000);
await page.goto("/");
const result = await page.evaluate(() => new Promise<any>((resolve, reject) => {
const worker = new Worker("/src/workers/vdb-webgpu-test.worker.ts", { type: "module" });
@@ -1250,17 +1251,18 @@ test("samples and integrates a real NanoVDB Float32 tree with WebGPU", async ({
});
expect(result.visiblePixels).toBeGreaterThan(500);
expect(result.alphaSum).toBeGreaterThan(10_000);
expect(result.imageSha256).toBe("7aab6639d8d173a4b22d913d16b9c61eeea4cc00cb8ccec2202edf75a1b1f978");
expect(result.materialMapping.supportedSemantics).toEqual(["DENSITY_GRID", "CONSTANT_COLOR", "CONSTANT_EMISSION", "ANISOTROPY", "INTERPOLATION"]);
expect(result.imageSha256).toBe("718e824bda68197e5bf63e2261396f47afe35efbc3ec84d3b38e4afe5b07e33f");
expect(result.viewAxis).toBe("X");
expect(result.materialMapping.supportedSemantics).toEqual(["DENSITY_GRID", "COLOR_GRID", "TEMPERATURE_GRID_BLACKBODY", "EMISSION_GRID", "CONSTANT_COLOR", "CONSTANT_EMISSION", "ANISOTROPY", "INTERPOLATION"]);
expect(result.materialMapping.material).toMatchObject({ interpolation: "LINEAR", color: [0.7, 0.8, 0.95], emissionColor: [1, 0.35, 0.1] });
expect(result.materialMapping.losses.map((loss: any) => loss.code)).toEqual([
"VOLUME_COLOR_GRID_UNSUPPORTED",
"VOLUME_TEMPERATURE_BLACKBODY_UNSUPPORTED",
"VOLUME_VELOCITY_RENDER_UNSUPPORTED",
]);
expect(result.materialMapping.losses.map((loss: any) => loss.code)).toEqual(["VOLUME_VELOCITY_RENDER_UNSUPPORTED"]);
expect(result.emissionMapping.material.emissionGrid).toBe("temperature");
expect(result.emissionMapping.losses).toEqual([]);
expect(result.emissionVisiblePixels).toBeGreaterThan(100);
});
test("renders a real NanoVDB volume through both production viewport backends", async ({ page }) => {
test.setTimeout(180_000);
await page.goto("/");
const result = await page.evaluate(async () => {
const [{ loadNanoVDBViewportAsset }, { ViewportRenderer }, { OffscreenViewportRenderer }] = await Promise.all([
@@ -1269,6 +1271,11 @@ test("renders a real NanoVDB volume through both production viewport backends",
import("/src/three-adapter/offscreen-viewport.ts"),
]);
const asset = await loadNanoVDBViewportAsset("volume:ViewportSmoke", "/__vdb_fixture__/manifest", "/__vdb_fixture__/bundle", new AbortController().signal);
asset.material = {
...asset.manifest.material,
interpolation: "NEAREST",
emissionGrid: asset.manifest.material.temperatureGrid,
};
const snapshot: any = {
schemaVersion: 1, revision: 1, sceneId: "scene:Volume", source: { kind: "mock" },
coordinateSystem: { upAxis: "Z", forwardAxis: "-Y", handedness: "RIGHT", unitSystem: 0, unitScale: 1 },
@@ -1281,7 +1288,7 @@ test("renders a real NanoVDB volume through both production viewport backends",
nonMeshData: [{ id: asset.dataId, name: "Viewport Volume", type: "VOLUME", geometryStatus: "blocked", pointCount: 0, splineCount: 0, sourcePath: "//volumes/generated-smoke.vdb", resourceKind: "OPENVDB" }],
activeObjectId: "object:Volume", frame: { current: 1, start: 1, end: 250 },
};
const waitFor = async (condition: () => boolean, timeoutMs = 20_000): Promise<void> => {
const waitFor = async (condition: () => boolean, timeoutMs = 80_000): Promise<void> => {
const deadline = performance.now() + timeoutMs;
while (!condition()) {
if (performance.now() > deadline) throw new Error("viewport volume timed out");
@@ -1299,7 +1306,8 @@ test("renders a real NanoVDB volume through both production viewport backends",
const main = new ViewportRenderer(mainCanvas);
main.setSnapshot(snapshot);
main.setVolumeAssets([asset]);
await waitFor(() => mainCanvas.dataset.volumeStatus === "ready");
await waitFor(() => ["ready", "blocked"].includes(mainCanvas.dataset.volumeStatus ?? ""));
if (mainCanvas.dataset.volumeStatus !== "ready") throw new Error(`main viewport volume ${mainCanvas.dataset.volumeErrorCode ?? "blocked"}`);
await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));
let mainVolumeObjects = 0;
main.scene.traverse((object: any) => { if (object.userData.nanoVDBVolume) mainVolumeObjects++; });
@@ -1314,7 +1322,9 @@ test("renders a real NanoVDB volume through both production viewport backends",
const offscreen = new OffscreenViewportRenderer(offscreenCanvas);
offscreen.setSnapshot(snapshot);
offscreen.setVolumeAssets([asset]);
await waitFor(() => offscreenCanvas.dataset.volumeStatus === "ready" && Number(offscreenCanvas.dataset.rendererPixels ?? 0) > 0);
await waitFor(() => ["ready", "blocked"].includes(offscreenCanvas.dataset.volumeStatus ?? ""));
if (offscreenCanvas.dataset.volumeStatus !== "ready") throw new Error(`offscreen viewport volume ${offscreenCanvas.dataset.volumeErrorCode ?? "blocked"}`);
await waitFor(() => Number(offscreenCanvas.dataset.rendererPixels ?? 0) > 0);
const offscreenResult = { status: offscreenCanvas.dataset.volumeStatus, count: Number(offscreenCanvas.dataset.volumeCount), visible: Number(offscreenCanvas.dataset.rendererPixels) };
offscreen.dispose();
offscreenCanvas.remove();
@@ -1355,9 +1365,25 @@ test("recovers NanoVDB paging from network, Worker and WebGPU device faults", as
expect(gpu.network.outOfOrderResponse).toContain("NANOVDB_STREAM_INCOMPLETE");
expect(gpu.lru).toMatchObject({ residentPages: 2, residentBytes: 128 * 1024, evictions: 1, keys: ["page-a", "page-c"] });
expect(gpu.oom).toContain("NANOVDB_GPU_BUDGET_EXCEEDED");
expect(gpu.demandPaging.pageCount).toBeGreaterThan(gpu.demandPaging.residentPageCapacity);
expect(gpu.demandPaging.initialVirtualPages).toEqual([]);
expect(gpu.demandPaging.residentBytes).toBe(512 * 1024);
expect(gpu.demandPaging.maxResidentBytes).toBe(512 * 1024);
expect(gpu.demandPaging.evictions).toBe(1);
expect(gpu.demandPaging.requestedPageResident).toBe(true);
expect(gpu.demandPaging.touchedPageResident).toBe(true);
expect(gpu.demandPaging.evictedPageResident).toBe(false);
expect(gpu.demandPaging.residentVirtualPages).toContain(gpu.demandPaging.pageCount - 1);
expect(gpu.demandPaging.pageRanges.length).toBeGreaterThanOrEqual(3);
expect(gpu.demandPaging.pageRanges.every((range: string) => /^bytes=\d+-\d+$/.test(range))).toBe(true);
expect(gpu.demandPaging.demandWords).toEqual(gpu.demandPaging.expectedWords);
expect(gpu.demandPaging.incompletePage).toContain("NANOVDB_STREAM_INCOMPLETE");
expect(gpu.globalResidency.totalResidentBytes).toBeLessThanOrEqual(gpu.globalResidency.maxResidentBytes);
expect(Object.values(gpu.globalResidency.gridResidentBytes)).toEqual([256 * 1024, 256 * 1024, 256 * 1024]);
expect(gpu.paging.pageCount).toBeGreaterThan(1);
expect(gpu.paging.residentPageCount).toBe(gpu.paging.pageCount);
expect(gpu.deviceLoss.recoveredGeneration).toBe(gpu.deviceLoss.firstGeneration + 1);
expect(gpu.deviceLoss.recoveredDemandWords).toEqual(gpu.demandPaging.expectedWords.slice(0, 2));
expect(gpu.samplesStable).toBe(true);
const interrupted = await page.evaluate(() => new Promise<any>((resolve, reject) => {