Advance WebGPU volume and bounded workflows
This commit is contained in:
108
tools/web/check-vdb-native-pipeline.mjs
Normal file
108
tools/web/check-vdb-native-pipeline.mjs
Normal file
@@ -0,0 +1,108 @@
|
||||
import assert from "node:assert/strict";
|
||||
import crypto from "node:crypto";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { createRequire } from "node:module";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import ts from "../../web/node_modules/typescript/lib/typescript.js";
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
||||
const resourceRoot = path.resolve(process.env.VDB_RESOURCE_ROOT ?? "/home/mes123456/resource-library/blender-web-vdb");
|
||||
const converter = path.join(root, "build_vdb_tools", "vdb_to_nanovdb");
|
||||
const generator = path.join(root, "build_vdb_tools", "vdb_fixture_generator");
|
||||
const source = path.join(resourceRoot, "generated", "generated-smoke.vdb");
|
||||
const storedBundle = path.join(resourceRoot, "nanovdb", "generated-smoke.nvdb");
|
||||
const officialSource = path.join(resourceRoot, "official", "sphere.vdb");
|
||||
const officialBundle = path.join(resourceRoot, "nanovdb", "official-sphere.nvdb");
|
||||
const browserManifest = path.join(resourceRoot, "manifests", "generated-smoke.nanovdb.json");
|
||||
const catalog = JSON.parse(fs.readFileSync(path.join(resourceRoot, "manifest.json"), "utf8"));
|
||||
const evidence = JSON.parse(fs.readFileSync(path.join(root, "docs", "status", "vdb-native-evidence.json"), "utf8"));
|
||||
const sha256 = (file) => crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex");
|
||||
|
||||
assert.match(fs.readFileSync(path.join(root, "build_web_blender6", "CMakeCache.txt"), "utf8"), /^WITH_OPENVDB:BOOL=OFF$/m);
|
||||
assert.match(fs.readFileSync(path.join(root, "build_blender_5.2.0", "CMakeCache.txt"), "utf8"), /^WITH_OPENVDB:BOOL=ON$/m);
|
||||
assert.equal(catalog.schemaVersion, 1);
|
||||
assert.equal(catalog.toolchain.openVDBVersion, "13.0.0");
|
||||
assert.equal(catalog.toolchain.converterSha256, sha256(converter));
|
||||
assert.equal(evidence.schemaVersion, 1);
|
||||
assert.equal(evidence.browserOpenVDB, false);
|
||||
assert.equal(evidence.desktopOpenVDB, true);
|
||||
assert.equal(evidence.serverJobConfigured, true);
|
||||
assert.equal(evidence.opfsStreamingConfigured, true);
|
||||
assert.equal(evidence.webgpuRendererConfigured, true);
|
||||
assert.equal(evidence.mainVolumeRoundtripConfigured, true);
|
||||
assert.equal(evidence.primaryViewportVolumeIntegrated, false);
|
||||
assert.equal(evidence.releaseStatus, "BLOCKED");
|
||||
assert.deepEqual(evidence.toolchain, catalog.toolchain);
|
||||
assert.deepEqual(evidence.resources, catalog.entries);
|
||||
for (const entry of catalog.entries) {
|
||||
const file = path.join(resourceRoot, entry.path);
|
||||
assert.equal(fs.statSync(file).size, entry.byteLength, `${entry.id} byte length changed`);
|
||||
assert.equal(sha256(file), entry.sha256, `${entry.id} SHA-256 changed`);
|
||||
}
|
||||
assert.equal(catalog.entries.find((entry) => entry.id === "official-sphere")?.sha256, "bb884a9a38354e47191c4ece4a11d1e051594a910fde56501cfc51ff52b171ab");
|
||||
|
||||
const ldd = spawnSync("ldd", [converter], { encoding: "utf8" });
|
||||
assert.equal(ldd.status, 0);
|
||||
assert.match(ldd.stdout, /libopenvdb\.so\.13/);
|
||||
const temporary = fs.mkdtempSync(path.join(os.tmpdir(), "vdb-native-"));
|
||||
try {
|
||||
const output = path.join(temporary, "smoke.nvdb");
|
||||
const report = path.join(temporary, "smoke.json");
|
||||
const conversion = spawnSync(converter, ["--input", source, "--output", output, "--report", report, "--grid", "density", "--grid", "temperature", "--grid", "color", "--grid", "velocity", "--quantization", "LOSSLESS"], { encoding: "utf8" });
|
||||
assert.equal(conversion.status, 0, conversion.stderr);
|
||||
assert.equal(sha256(output), sha256(storedBundle), "OpenVDB to NanoVDB conversion is not deterministic");
|
||||
const parsedReport = JSON.parse(fs.readFileSync(report, "utf8"));
|
||||
assert.equal(parsedReport.openVDBVersion, "13.0.0");
|
||||
assert.equal(parsedReport.nanoVDBVersion, "32.9.0");
|
||||
assert.deepEqual(parsedReport.grids.map((grid) => grid.name), ["color", "density", "temperature", "velocity"]);
|
||||
assert.ok(parsedReport.grids.every((grid) => grid.byteOffset >= grid.segmentByteOffset && grid.byteOffset + grid.byteLength <= grid.segmentByteOffset + grid.segmentByteLength));
|
||||
|
||||
const generatedA = path.join(temporary, "generated-a");
|
||||
const generatedB = path.join(temporary, "generated-b");
|
||||
fs.mkdirSync(generatedA);
|
||||
fs.mkdirSync(generatedB);
|
||||
assert.equal(spawnSync(generator, [generatedA], { encoding: "utf8" }).status, 0);
|
||||
assert.equal(spawnSync(generator, [generatedB], { encoding: "utf8" }).status, 0);
|
||||
const semanticOutputs = [];
|
||||
for (const [index, generated] of [generatedA, generatedB].entries()) {
|
||||
const semanticOutput = path.join(temporary, `semantic-${index}.nvdb`);
|
||||
const semanticReport = path.join(temporary, `semantic-${index}.json`);
|
||||
const result = spawnSync(converter, ["--input", path.join(generated, "generated-smoke.vdb"), "--output", semanticOutput, "--report", semanticReport, "--grid", "density", "--grid", "temperature", "--grid", "color", "--grid", "velocity", "--quantization", "LOSSLESS"], { encoding: "utf8" });
|
||||
assert.equal(result.status, 0, result.stderr);
|
||||
semanticOutputs.push(semanticOutput);
|
||||
}
|
||||
assert.equal(sha256(semanticOutputs[0]), sha256(semanticOutputs[1]), "Semantically identical generated VDB grids produce different NanoVDB bundles");
|
||||
assert.equal(sha256(semanticOutputs[0]), sha256(storedBundle));
|
||||
|
||||
const officialOutput = path.join(temporary, "official-sphere.nvdb");
|
||||
const officialReport = path.join(temporary, "official-sphere.json");
|
||||
const officialConversion = spawnSync(converter, ["--input", officialSource, "--output", officialOutput, "--report", officialReport, "--quantization", "LOSSLESS"], { encoding: "utf8" });
|
||||
assert.equal(officialConversion.status, 0, officialConversion.stderr);
|
||||
assert.equal(sha256(officialOutput), sha256(officialBundle), "Official sphere conversion is not deterministic");
|
||||
const parsedOfficial = JSON.parse(fs.readFileSync(officialReport, "utf8"));
|
||||
assert.deepEqual(parsedOfficial.grids.map((grid) => [grid.name, grid.gridClass]), [["ls_sphere", "LEVEL_SET"]]);
|
||||
|
||||
const malformed = spawnSync(converter, ["--input", path.join(resourceRoot, "generated", "generated-smoke-truncated.vdb"), "--output", path.join(temporary, "bad.nvdb"), "--report", path.join(temporary, "bad.json")], { encoding: "utf8" });
|
||||
assert.notEqual(malformed.status, 0);
|
||||
assert.match(malformed.stderr, /VDB_CONVERSION_FAILED/);
|
||||
|
||||
for (const name of ["asset-path", "capability-gates", "volume-vdb"]) {
|
||||
const sourceText = fs.readFileSync(path.join(root, `web/protocol/${name}.ts`), "utf8");
|
||||
let code = ts.transpileModule(sourceText, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 }, fileName: `${name}.ts` }).outputText;
|
||||
code = code.replaceAll('require("./asset-path")', 'require("./asset-path.cjs")').replaceAll('require("./capability-gates")', 'require("./capability-gates.cjs")');
|
||||
fs.writeFileSync(path.join(temporary, `${name}.cjs`), code);
|
||||
}
|
||||
const require = createRequire(import.meta.url);
|
||||
const protocol = require(path.join(temporary, "volume-vdb.cjs"));
|
||||
const validated = protocol.validateNanoVDBBundleManifest(JSON.parse(fs.readFileSync(browserManifest, "utf8")));
|
||||
assert.equal(validated.bundleSha256, sha256(storedBundle));
|
||||
assert.deepEqual(validated.grids.map((grid) => grid.semantic), ["COLOR", "DENSITY", "TEMPERATURE", "VELOCITY"]);
|
||||
}
|
||||
finally {
|
||||
fs.rmSync(temporary, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
process.stdout.write(`vdb-native-pipeline-ok resources=${catalog.entries.length} converter=openvdb13-nanovdb32 conversion-deterministic=1 semantic-deterministic=1 official-deterministic=1 malformed=blocked browser-openvdb=off\n`);
|
||||
Reference in New Issue
Block a user