fix(street-level): KartaView round-2 follow-ups — context loss, size cap, slow lookups

From the second IC8 pass (launch ready; P3s closed here):
- A lost WebGL context is redrawn on a new canvas (the old one only gives
  back its lost context), through the core, and only if no newer photo is
  opening; verified live with WEBGL_lose_context, twice.
- The 16 MB answer cap counts bytes while reading: KartaView sends
  chunked answers with no Content-Length, so the header check never fired.
- A lookup gets 8 s, then 15 s once more: KartaView sometimes sits on a
  lookup for 11 s and answers the same one in 3 s.

Left as follow-ups: cancelling abandoned sequence pages; finding a page
by binary search on the index when thousands of photos are missing.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Said TurkseverandClaude Opus 5.5 committed 2026-10-13 21:03:13 +01:00
1 parent 0394a9b908
commit 97a1a667de
7 files changed
+158 -24

No files matched your search

@@ -69,6 +69,7 @@ const PHOTOS = {
function fakeRenderer(canvas) {
const view = { yaw: 0, pitch: 0, fov: 80 };
return {
canvas,
images: [],
hiddenAtSetImage: [],
setImage(image) {
@@ -435,16 +436,32 @@ test('two fingers pinch the panorama’s zoom', async () => {
});
test('a lost WebGL context drops the panorama and shows the photo again on a new one', async () => {
const { viewer, host, renderers, poses } = setup();
const { viewer, host, renderers, poses, stepped } = setup();
await viewer.mount(host);
await viewer.open('pano1');
parts(host).canvas.fire('webglcontextlost');
const lost = parts(host).canvas;
lost.fire('webglcontextlost');
assert.equal(renderers[0].destroyed, true);
// A lost canvas only ever gives back its lost context: a new element.
assert.notEqual(parts(host).canvas, lost);
assert.deepEqual(stepped, ['pano1'], 'reopened through the core');
assert.ok(await until(() => renderers.length === 2), 'a new renderer');
assert.equal(renderers[1].canvas, parts(host).canvas);
assert.equal(poses.at(-1).imageId, 'pano1');
assert.equal(parts(host).canvas.hidden, false);
});
test('a context lost while a newer photo opens leaves that open alone', async () => {
const { viewer, host, stepped } = setup();
await viewer.mount(host);
await viewer.open('pano1');
const opening = viewer.open('flat1');
parts(host).canvas.fire('webglcontextlost');
await opening;
assert.deepEqual(stepped, [], 'no reopen of the old panorama');
assert.equal(parts(host).picture.hidden, false);
});
test('a WebGL failure only lasts until the viewer is mounted again', async () => {
let fail = true;
const built = [];