87 lines
4.1 KiB
JavaScript
87 lines
4.1 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import { performance } from "node:perf_hooks";
|
|
import factory from "../../web/app/src/vendor/blender/web_engine.js";
|
|
|
|
const wasmBinary = fs.readFileSync(new URL("../../web/app/src/vendor/blender/web_engine.wasm", import.meta.url));
|
|
const limits = {
|
|
100000: Number(process.env.WEB_PERF_100K_MS ?? 120_000),
|
|
1000000: Number(process.env.WEB_PERF_1M_MS ?? 180_000),
|
|
heapBytes: Number(process.env.WEB_PERF_MAX_HEAP_BYTES ?? 1_610_612_736),
|
|
};
|
|
|
|
function grid(targetTriangles) {
|
|
const columns = Math.ceil(Math.sqrt(targetTriangles / 2));
|
|
const rows = Math.ceil(targetTriangles / (columns * 2));
|
|
const positions = new Float32Array((columns + 1) * (rows + 1) * 3);
|
|
for (let y = 0; y <= rows; y++) {
|
|
for (let x = 0; x <= columns; x++) {
|
|
const offset = (y * (columns + 1) + x) * 3;
|
|
positions[offset] = x / columns;
|
|
positions[offset + 1] = y / rows;
|
|
positions[offset + 2] = Math.sin(x * 0.03) * Math.cos(y * 0.03) * 0.01;
|
|
}
|
|
}
|
|
const triangleCount = Math.min(targetTriangles, columns * rows * 2);
|
|
const indices = new Uint32Array(triangleCount * 3);
|
|
for (let triangle = 0; triangle < triangleCount; triangle++) {
|
|
const cell = Math.floor(triangle / 2);
|
|
const x = cell % columns;
|
|
const y = Math.floor(cell / columns);
|
|
const a = y * (columns + 1) + x;
|
|
const offset = triangle * 3;
|
|
if (triangle % 2 === 0) indices.set([a, a + 1, a + columns + 2], offset);
|
|
else indices.set([a, a + columns + 2, a + columns + 1], offset);
|
|
}
|
|
return { positions, indices, triangleCount };
|
|
}
|
|
|
|
function alloc(engine, typed) {
|
|
const pointer = engine._malloc(typed.byteLength);
|
|
assert.ok(pointer > 0, `WASM allocation failed for ${typed.byteLength} bytes`);
|
|
engine.HEAPU8.set(new Uint8Array(typed.buffer, typed.byteOffset, typed.byteLength), pointer);
|
|
return pointer;
|
|
}
|
|
|
|
const results = [];
|
|
for (const target of [100_000, 1_000_000]) {
|
|
const engine = await factory({ wasmBinary });
|
|
const source = grid(target);
|
|
const pointers = [];
|
|
let report;
|
|
try {
|
|
const positions = alloc(engine, source.positions); pointers.push(positions);
|
|
const indices = alloc(engine, source.indices); pointers.push(indices);
|
|
const positionsOut = engine._malloc(source.positions.byteLength); pointers.push(positionsOut);
|
|
const indicesOut = engine._malloc(source.indices.byteLength); pointers.push(indicesOut);
|
|
const counts = Array.from({ length: 6 }, () => engine._malloc(4)); pointers.push(...counts);
|
|
const started = performance.now();
|
|
const ratio = target === 100_000 ? 0.9 : 1;
|
|
const result = engine._web_engine_decimate_apply(
|
|
positions, source.positions.length / 3, indices, source.triangleCount,
|
|
0, ratio, 1, 0, 0, 0, 0, -1, 1e-4,
|
|
0, 1, 0, 0, 0, 0,
|
|
positionsOut, source.positions.length, counts[0],
|
|
indicesOut, source.indices.length, counts[1],
|
|
0, 0, counts[2], 0, 0, counts[3], 0, 0, counts[4], counts[5],
|
|
);
|
|
const elapsedMs = performance.now() - started;
|
|
assert.equal(result, 0, `native decimate failed for ${target} triangles with code ${result}`);
|
|
const outputTriangles = engine.HEAPU32[counts[1] >>> 2] / 3;
|
|
assert.ok(outputTriangles > 0 && outputTriangles <= source.triangleCount, "invalid performance-gate output topology");
|
|
const heapBytes = engine.HEAPU8.byteLength;
|
|
assert.ok(elapsedMs <= limits[target], `${target} triangle gate exceeded ${limits[target]}ms: ${elapsedMs.toFixed(1)}ms`);
|
|
assert.ok(heapBytes <= limits.heapBytes, `${target} triangle gate exceeded WASM heap limit: ${heapBytes}`);
|
|
report = { target, ratio, inputTriangles: source.triangleCount, outputTriangles, elapsedMs: Math.round(elapsedMs), peakHeapBytes: heapBytes };
|
|
}
|
|
finally {
|
|
for (const pointer of pointers.reverse()) if (pointer) engine._free(pointer);
|
|
}
|
|
const recoveryPointer = engine._malloc(16);
|
|
assert.ok(recoveryPointer > 0, `${target} triangle gate did not recover a small allocation`);
|
|
engine._free(recoveryPointer);
|
|
results.push({ ...report, releasedPointerCount: pointers.length, recoveryAllocation: true });
|
|
}
|
|
|
|
process.stdout.write(`release-performance-ok ${JSON.stringify(results)}\n`);
|