Complete V1 performance and OOM release gates
This commit is contained in:
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user