Files
goodpuppies--petplay/classes/uiTextSnapshotCache.test.ts
mommysgoodpuppy 927136050d Speed up the decoupled renderer and skip unchanged scenes
Render thread (panorama, nice 0, load ~27 on 16 cores):
- Per-run UI panel slots reused across eyes, one per-frame panel data texture, and cached
  struct bytes for shader uniforms and draw matrices: renderRaythreeFrame 4.16 -> 2.27 ms.
- The worker skips publishing scenes whose fingerprint (overlaySceneFingerprint) matches the
  last one sent, with a 1 s keepalive. Anchored items now keep bit-identical local matrices
  while nothing moves: built from the local transform chain, or held through rounding noise
  where uikit derives matrixWorld from layout. Decode on the render thread 1.78 -> 0.04
  ms/frame when idle; render thread busy 6.4 -> 4.2 ms/frame.

Diagnostics: a frame timeline probe with schedstat in overlayRenderOwner, CPU profiling with an
interval, dropLateFrames, renderThreadNice, split presenter finish/wait/publish, a native
WaitFrameSync phase probe and a compositor frame-timing probe. asyncFrameWait,
projectionPacing and deferDecode are projection experiments that did not help.

Also includes the other agent's pending overlay scene buffer, wire and ovrdriver lab work.

Agent: Claude Code 2.1.296; model claude-opus-5-5; thread 941be8c7-aca1-4664-b284-7cc0eed69e21
2026-10-16 20:12:06 +03:00

77 lines
2.6 KiB
TypeScript

import {
captureTextBatchContent,
textBatchContentMatches,
UiTextGeometryInterner,
} from "./uiTextSnapshotCache.ts";
import { captureTextBatchTransforms, textBatchTransformsMatch } from "./uiTextBatchTransforms.ts";
function text() {
return {
text: "A",
color: [1, 1, 1, 1],
worldMatrix: new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]),
geometry: {
positions: new Float32Array([0, 0, 1, 0, 0, 1]),
uvs: new Float32Array([0, 0, 1, 0, 0, 1]),
indices: new Uint16Array([0, 1, 2]),
version: 0,
},
};
}
function assert(ok: boolean, message: string) {
if (!ok) throw new Error(message);
}
Deno.test("decoded equivalent labels retain geometry and reuse batch content", () => {
const cache = new UiTextGeometryInterner();
const a = [text()];
cache.intern(a);
const saved = captureTextBatchContent(a);
const matrices = captureTextBatchTransforms(a);
const b = structuredClone(a);
cache.intern(b);
assert(b[0] !== a[0] && b[0].geometry === a[0].geometry, "must survive fresh packet identity");
assert(
textBatchContentMatches(b, saved) && textBatchTransformsMatch(b, matrices),
"equivalent scene must reuse",
);
b[0].worldMatrix[12] = .25;
assert(
!textBatchTransformsMatch(b, matrices),
"fresh hand movement must rebuild world-baked vertices",
);
b[0].color[3] = .5;
assert(
!textBatchContentMatches(b, saved),
"in-place opacity change must invalidate owned content",
);
});
Deno.test("same revision and lengths cannot hide changed glyph data", () => {
for (const part of ["positions", "uvs", "indices"] as const) {
const cache = new UiTextGeometryInterner();
const a = [text()];
cache.intern(a);
const saved = captureTextBatchContent(a);
const b = structuredClone(a);
b[0].geometry[part][1] = 2;
cache.intern(b);
assert(
b[0].geometry !== a[0].geometry && !textBatchContentMatches(b, saved),
`${part} change reused stale data`,
);
}
});
Deno.test("version, label removal, font geometry and slot reorder invalidate safely", () => {
const cache = new UiTextGeometryInterner();
const a = [text(), text()];
a[1].geometry.uvs[0] = .25;
cache.intern(a);
const saved = captureTextBatchContent(a);
const swapped = structuredClone([a[1], a[0]]);
cache.intern(swapped);
assert(!textBatchContentMatches(swapped, saved), "reordered geometry must invalidate");
const b = structuredClone(a);
b[0].geometry.version++;
cache.intern(b);
assert(!textBatchContentMatches(b, saved), "new revision must invalidate");
assert(!textBatchContentMatches([], saved), "removal must invalidate");
});