perf(app): hand a map collection to MapLibre only when it changed - #740
Conversation
The 1 Hz tick called setData on every source, changed or not, and MapLibre serialises, tiles and uploads whatever it is handed. On a Nijmegen-to-Arnhem export (4559 receptions, 4478 rays) a tick with nothing new took 96.6 ms of main thread on a laptop, and 11.8 ms with this change. put() remembers what each source was last handed (lastSent) and skips a collection that is the same object. The dots, the pillars, the hex and the noise cells already come out of a cache that returns the same object on a hit. The rays are built in about a millisecond from five inputs, so their answer is compared instead (sameFeatures) and an equal one reuses last draw's object. The 3D rays read the terrain height when their buffers are filled, so with the mesh on they are still filled on every draw. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
c278234 to
1fae10d
Compare
|
Rebased onto master (1fae10d). One conflict, in |
|
Merged as c6fc9a9. Fix-check of 1fae10d: every write to the cached sources goes through Three gaps in the contract test, left for a follow-up if you want them. They can cost speed, not correctness:
The 96.6 → 11.8 ms figure is from before #734. If a release note quotes it, it needs a new measurement. |
|
Post-merge review of c6fc9a9 with the full template: no point to change. It confirms the three test gaps from the comment above and adds two:
|
Resolves the conflict with efiten#740 in app/src/huntmap.js: drawCells writes the noise source through put(), as efiten#740 does, and keeps passing vis.noise to drawHexLabels for the noise labels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🤖 I have created a release *beep* *boop* --- <details><summary>app: 1.31.0</summary> ## [1.31.0](app-v1.30.0...app-v1.31.0) (2026-10-02) ### Features * **app:** show the measured noise floor in the cells and the HUD ([#711](#711)) ([d97cec0](d97cec0)) ### Performance Improvements * **app:** hand a map collection to MapLibre only when it changed ([#740](#740)) ([c6fc9a9](c6fc9a9)) </details> <details><summary>web: 1.27.0</summary> ## [1.27.0](web-v1.26.0...web-v1.27.0) (2026-10-02) ### Features * **app:** show the measured noise floor in the cells and the HUD ([#711](#711)) ([d97cec0](d97cec0)) </details> --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Closes #739
The 1 Hz tick called
setDataon every source, changed or not, and MapLibre serialises, tiles and uploads whatever it is handed. On a Nijmegen-to-Arnhem export (4559 receptions, 4478 rays, reach on) a tick with nothing new took 96.6 ms of main thread on a MacBook M1.In this PR
put()inhuntmap.jswrites a source only when the collection is not the object it was last handed (lastSent,rendercache.js). The dots, the pillars, the hex and the noise cells already come out of a cache that returns the same object on a hit, andEMPTYis one constant.sameFeaturescompares the answer with last draw's and reuses that object when equal. It is generic over the properties, so a field added to the rays later is compared without anyone adding it.addOverlaysclears what counted as sent.Measured
The real
createHuntMapin a browser, same export, five ticks with the same receptions as fresh objects:setDataon dots, hex, rays, noise, pillarsbufferDatacallsAlso checked there: one new hearing sends dots, hex and rays once; a selection sends them once and holding it sends nothing; going to 3D and back sends what the view shows; with the mesh on the ray buffers are filled on every draw (2
bufferDataper draw); afterapplyBasemapevery source is sent once and holds its 4477 rays and 4559 dots.Verification
rendercache.test.js: 13 new tests forsameFeaturesandlastSent.huntmap-contract.test.js: 7 new, readinghuntmap.jsas text, as that file does: the five collections are never written aroundput(), and the mount clears the memory.Not in this PR
web/) draws per fetch and per pan, not per second.🤖 Generated with Claude Code