Advance M8-M11 parity workflows
This commit is contained in:
182
web/tests/unit/nanovdb-page-feedback.test.mjs
Normal file
182
web/tests/unit/nanovdb-page-feedback.test.mjs
Normal file
@@ -0,0 +1,182 @@
|
||||
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 repoRoot = path.resolve(import.meta.dirname, "../../..");
|
||||
const sourcePath = path.join(repoRoot, "web/protocol/nanovdb-page-feedback.ts");
|
||||
const transpiled = ts.transpileModule(fs.readFileSync(sourcePath, "utf8"), {
|
||||
compilerOptions: { module: ts.ModuleKind.ES2022, target: ts.ScriptTarget.ES2022 },
|
||||
fileName: sourcePath,
|
||||
reportDiagnostics: true,
|
||||
});
|
||||
assert.deepEqual(transpiled.diagnostics, []);
|
||||
const moduleUrl = "data:text/javascript;base64," + Buffer.from(transpiled.outputText).toString("base64");
|
||||
const feedback = await import(moduleUrl);
|
||||
|
||||
test("M8-01 freezes the bounded GPU feedback word layout", () => {
|
||||
const buffer = feedback.createNanoVDBPageFeedbackBuffer();
|
||||
const words = new Uint32Array(buffer);
|
||||
assert.equal(buffer.byteLength, (feedback.NANOVDB_PAGE_FEEDBACK_HEADER_WORDS + feedback.NANOVDB_PAGE_FEEDBACK_DEFAULT_CAPACITY) * 4);
|
||||
assert.deepEqual([...words.slice(0, 4)], [1, 1024, 0, 0]);
|
||||
assert.ok([...words.slice(4)].every((word) => word === 0xffffffff));
|
||||
assert.deepEqual(feedback.NANOVDB_PAGE_FEEDBACK_WORD, { schemaVersion: 0, capacity: 1, count: 2, overflow: 3, pageIds: 4 });
|
||||
assert.match(feedback.NANOVDB_PAGE_FEEDBACK_WGSL, /count: atomic<u32>/);
|
||||
assert.match(feedback.NANOVDB_PAGE_FEEDBACK_WGSL, /overflow: atomic<u32>/);
|
||||
assert.match(feedback.NANOVDB_PAGE_FEEDBACK_WGSL, /page_ids: array<atomic<u32>>/);
|
||||
});
|
||||
|
||||
test("M8-02 records unique page IDs with a physical-capacity bound", () => {
|
||||
assert.match(feedback.NANOVDB_PAGE_FEEDBACK_RECORD_WGSL, /arrayLength\(&nanovdb_page_feedback\.page_ids\)/);
|
||||
assert.match(feedback.NANOVDB_PAGE_FEEDBACK_RECORD_WGSL, /min\(nanovdb_page_feedback\.capacity, physical_capacity\)/);
|
||||
assert.match(feedback.NANOVDB_PAGE_FEEDBACK_RECORD_WGSL, /slot < capacity/);
|
||||
assert.match(feedback.NANOVDB_PAGE_FEEDBACK_RECORD_WGSL, /atomicCompareExchangeWeak/);
|
||||
assert.match(feedback.NANOVDB_PAGE_FEEDBACK_RECORD_WGSL, /claim\.old_value == page_id/);
|
||||
assert.match(feedback.NANOVDB_PAGE_FEEDBACK_RECORD_WGSL, /atomicStore\(&nanovdb_page_feedback\.overflow, 1u\)/);
|
||||
});
|
||||
|
||||
test("M8-03 sorts, deduplicates and binds CPU feedback to a render revision", () => {
|
||||
const buffer = feedback.createNanoVDBPageFeedbackBuffer(4);
|
||||
const words = new Uint32Array(buffer);
|
||||
words[feedback.NANOVDB_PAGE_FEEDBACK_WORD.count] = 4;
|
||||
words.set([7, 2, 7, 5], feedback.NANOVDB_PAGE_FEEDBACK_WORD.pageIds);
|
||||
assert.deepEqual(feedback.parseNanoVDBPageFeedbackBatch(buffer, 8, 42), {
|
||||
schemaVersion: 1,
|
||||
renderRevision: 42,
|
||||
attemptedCount: 4,
|
||||
gpuStoredCount: 4,
|
||||
uniqueCount: 3,
|
||||
pageIds: [2, 5, 7],
|
||||
status: "READY",
|
||||
errorCode: null,
|
||||
});
|
||||
assert.throws(
|
||||
() => feedback.parseNanoVDBPageFeedbackBatch(buffer, 8, -1),
|
||||
(error) => error.code === "INVALID_ARGUMENT",
|
||||
);
|
||||
assert.throws(
|
||||
() => feedback.parseNanoVDBPageFeedbackBatch(buffer, 8, 1.5),
|
||||
(error) => error.code === "INVALID_ARGUMENT",
|
||||
);
|
||||
});
|
||||
|
||||
test("M8-04 rejects stale frame feedback before page I/O", async () => {
|
||||
const buffer = feedback.createNanoVDBPageFeedbackBuffer(4);
|
||||
const words = new Uint32Array(buffer);
|
||||
words[feedback.NANOVDB_PAGE_FEEDBACK_WORD.count] = 3;
|
||||
words.set([7, 2, 5], feedback.NANOVDB_PAGE_FEEDBACK_WORD.pageIds);
|
||||
const batch = feedback.parseNanoVDBPageFeedbackBatch(buffer, 8, 42);
|
||||
const requests = [];
|
||||
const requestPage = async (pageId, renderRevision) => {
|
||||
requests.push({ pageId, renderRevision });
|
||||
};
|
||||
|
||||
assert.deepEqual(await feedback.dispatchNanoVDBPageFeedbackBatch(batch, 43, requestPage), {
|
||||
schemaVersion: 1,
|
||||
renderRevision: 42,
|
||||
currentRenderRevision: 43,
|
||||
status: "STALE",
|
||||
requestedPageIds: [],
|
||||
requestedCount: 0,
|
||||
errorCode: "REVISION_CONFLICT",
|
||||
});
|
||||
assert.deepEqual(await feedback.dispatchNanoVDBPageFeedbackBatch(batch, 41, requestPage), {
|
||||
schemaVersion: 1,
|
||||
renderRevision: 42,
|
||||
currentRenderRevision: 41,
|
||||
status: "STALE",
|
||||
requestedPageIds: [],
|
||||
requestedCount: 0,
|
||||
errorCode: "REVISION_CONFLICT",
|
||||
});
|
||||
assert.deepEqual(requests, []);
|
||||
|
||||
assert.deepEqual(await feedback.dispatchNanoVDBPageFeedbackBatch(batch, 42, requestPage), {
|
||||
schemaVersion: 1,
|
||||
renderRevision: 42,
|
||||
currentRenderRevision: 42,
|
||||
status: "ACCEPTED",
|
||||
requestedPageIds: [2, 5, 7],
|
||||
requestedCount: 3,
|
||||
errorCode: null,
|
||||
});
|
||||
assert.deepEqual(requests, [
|
||||
{ pageId: 2, renderRevision: 42 },
|
||||
{ pageId: 5, renderRevision: 42 },
|
||||
{ pageId: 7, renderRevision: 42 },
|
||||
]);
|
||||
await assert.rejects(
|
||||
feedback.dispatchNanoVDBPageFeedbackBatch(batch, -1, requestPage),
|
||||
(error) => error.code === "INVALID_ARGUMENT",
|
||||
);
|
||||
});
|
||||
|
||||
test("M8-01 parses ready and overflow feedback with a stable overflow code", () => {
|
||||
const readyBuffer = feedback.createNanoVDBPageFeedbackBuffer(4);
|
||||
const readyWords = new Uint32Array(readyBuffer);
|
||||
readyWords[feedback.NANOVDB_PAGE_FEEDBACK_WORD.count] = 3;
|
||||
readyWords.set([7, 2, 5], feedback.NANOVDB_PAGE_FEEDBACK_WORD.pageIds);
|
||||
assert.deepEqual(feedback.parseNanoVDBPageFeedbackBuffer(readyBuffer, 8), {
|
||||
schemaVersion: 1,
|
||||
capacity: 4,
|
||||
attemptedCount: 3,
|
||||
storedCount: 3,
|
||||
pageIds: [7, 2, 5],
|
||||
status: "READY",
|
||||
errorCode: null,
|
||||
});
|
||||
|
||||
const overflowBuffer = feedback.createNanoVDBPageFeedbackBuffer(2);
|
||||
const overflowWords = new Uint32Array(overflowBuffer);
|
||||
overflowWords[feedback.NANOVDB_PAGE_FEEDBACK_WORD.count] = 4;
|
||||
overflowWords[feedback.NANOVDB_PAGE_FEEDBACK_WORD.overflow] = 1;
|
||||
overflowWords.set([3, 4], feedback.NANOVDB_PAGE_FEEDBACK_WORD.pageIds);
|
||||
assert.deepEqual(feedback.parseNanoVDBPageFeedbackBuffer(overflowBuffer, 8), {
|
||||
schemaVersion: 1,
|
||||
capacity: 2,
|
||||
attemptedCount: 4,
|
||||
storedCount: 2,
|
||||
pageIds: [3, 4],
|
||||
status: "OVERFLOW",
|
||||
errorCode: "NANOVDB_PAGE_FEEDBACK_OVERFLOW",
|
||||
});
|
||||
});
|
||||
|
||||
test("M8-01 rejects layout drift, invalid pages and inconsistent overflow", () => {
|
||||
assert.throws(() => feedback.createNanoVDBPageFeedbackBuffer(0), (error) => error.code === "INVALID_ARGUMENT");
|
||||
assert.throws(() => feedback.createNanoVDBPageFeedbackBuffer(8193), (error) => error.code === "INVALID_ARGUMENT");
|
||||
|
||||
const wrongSchema = feedback.createNanoVDBPageFeedbackBuffer(2);
|
||||
new Uint32Array(wrongSchema)[0] = 2;
|
||||
assert.throws(() => feedback.parseNanoVDBPageFeedbackBuffer(wrongSchema, 8), (error) => error.code === "PROTOCOL_MISMATCH");
|
||||
|
||||
const wrongCapacity = feedback.createNanoVDBPageFeedbackBuffer(2);
|
||||
new Uint32Array(wrongCapacity)[1] = 8193;
|
||||
assert.throws(() => feedback.parseNanoVDBPageFeedbackBuffer(wrongCapacity, 8), (error) => error.code === "PROTOCOL_MISMATCH");
|
||||
|
||||
const wrongSize = feedback.createNanoVDBPageFeedbackBuffer(2).slice(0, 20);
|
||||
assert.throws(() => feedback.parseNanoVDBPageFeedbackBuffer(wrongSize, 8), (error) => error.code === "PROTOCOL_MISMATCH");
|
||||
|
||||
const inconsistent = feedback.createNanoVDBPageFeedbackBuffer(2);
|
||||
const inconsistentWords = new Uint32Array(inconsistent);
|
||||
inconsistentWords[2] = 3;
|
||||
inconsistentWords.set([1, 2], 4);
|
||||
assert.throws(() => feedback.parseNanoVDBPageFeedbackBuffer(inconsistent, 8), (error) => error.code === "PROTOCOL_MISMATCH");
|
||||
|
||||
const invalidPage = feedback.createNanoVDBPageFeedbackBuffer(2);
|
||||
const invalidPageWords = new Uint32Array(invalidPage);
|
||||
invalidPageWords[2] = 1;
|
||||
invalidPageWords[4] = 8;
|
||||
assert.throws(() => feedback.parseNanoVDBPageFeedbackBuffer(invalidPage, 8), (error) => error.code === "PROTOCOL_MISMATCH");
|
||||
});
|
||||
|
||||
test("M8-01 reset clears atomic words and stale page IDs", () => {
|
||||
const buffer = feedback.createNanoVDBPageFeedbackBuffer(3);
|
||||
const words = new Uint32Array(buffer);
|
||||
words[2] = 5;
|
||||
words[3] = 1;
|
||||
words.set([1, 2, 3], 4);
|
||||
feedback.resetNanoVDBPageFeedbackBuffer(buffer);
|
||||
assert.deepEqual([...words], [1, 3, 0, 0, 0xffffffff, 0xffffffff, 0xffffffff]);
|
||||
});
|
||||
Reference in New Issue
Block a user