Budget the jump-link layer in pixels of line, nearest the view's centre first

With the drawn set following the view, the layer at 8 pc cost the integrated Radeon 503 ms a frame
at 30 pc from the Sun. Measured, the cost follows the length of line on screen (about 10 ms per
million pixels near the Sun), not the number of links: 100 000 links were 12 ms at the opening
view, 25 000 were 61 ms at 30 pc. So the budget is a length: a million pixels, turned into parsecs
at the depth the view is centred on, spent on the links nearest that centre by their nearer end.
Orbiting with links at 8 pc on the iGPU: 12-18 ms p50 at every pose measured, no long tasks.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi
This commit is contained in:
2026-09-17 18:23:55 +02:00
co-authored by Claude Opus 5
parent 45d5209433
commit bd5e9d4b0d
7 changed files with 161 additions and 20 deletions
@@ -94,6 +94,16 @@ describe('RoutingClient without a worker', () => {
client.dispose();
});
it('keeps only the links its budget holds, nearest the centre first', async () => {
const client = new RoutingClient(STARS, POSITIONS, index);
// Stars at x = 0 to 4 a parsec apart: from a centre at 3.9, one and a half parsecs is the link 3-4 alone.
const segments = await client.links(1.5, ALL, { centre: { x: 3.9, y: 0, z: 0 }, lengthPc: 1.5 });
expect(Array.from(segments)).toEqual([3, 0, 0, 4, 0, 0]);
client.dispose();
});
it('links only the stars it is told are drawn', async () => {
const client = new RoutingClient(STARS, POSITIONS, index);
@@ -196,6 +206,12 @@ describe('RoutingClient with a worker', () => {
expect(worker.requests.map((request) => request.kind === 'links' && Array.from(request.drawn))).toEqual([[0, 1, 2], [3, 4, 5]]);
worker.answer({ kind: 'links', requestId: worker.requests[1].requestId, segments: new Float32Array(12) });
await expect(second).resolves.toHaveLength(12);
// The same list at a different budget is a different graph.
void client.links(3, far, { centre: { x: 1, y: 0, z: 0 }, lengthPc: 10 });
void client.links(3, far, { centre: { x: 1, y: 0, z: 0 }, lengthPc: 20 });
worker.answer({ kind: 'links', requestId: worker.requests[2].requestId, segments: new Float32Array(0) });
await flush();
expect(worker.requests.map((request) => request.kind === 'links' && request.budget?.lengthPc)).toEqual([undefined, undefined, 10, 20]);
// The star field goes on drawing and picking from these lists, so they are copied, not moved.
expect(worker.transferred).not.toContain(near.buffer);
expect(worker.transferred).not.toContain(far.buffer);