Begins work on #3207
## Description:
This PR is the first optimization slice for #3207: it removes O(n)
render lookups in `StructureIconsLayer` by replacing array-first render
state with a unit-id keyed map, and tightens hot-path execution to
reduce per-tick allocations.
### What changed
- Refactored render state from array-first to `rendersByUnitId:
Map<number, StructureRenderInfo>`.
- Replaced O(n) lookup/delete paths with O(1) `Map#get` / `Map#delete`.
- Replaced `seenUnits` object-identity tracking with `seenUnitIds:
Set<number>`.
- Removed `tick()` array/closure chain (`map(...).forEach(...)`) and
switched to index-based loop.
- Reduced ghost-path allocation pressure by reusing a layer-level `Set`
for connected ally IDs instead of allocating `filter` + `map` + `new
Set` per ghost query.
- Added dirty-flag caching for structure visibility focus
(`visibilityStateDirty`) so expensive visibility-state scans recompute
only when toggles change.
### Performance validation (before/after)
Benchmark added: `tests/perf/StructureIconsLayerLookupPerf.ts`
Command:
`npm run perf`
Observed result:
- `StructureIconsLayer BEFORE (array O(n) lookup/delete) x 0.33 ops/sec
±13.28%`
- `StructureIconsLayer AFTER (unit-id map O(1) lookup/delete) x 95.65
ops/sec ±2.46%`
- Fastest implementation: AFTER (unit-id map)
#### Profiler screenshots are too noisy to be useful for such a focused
change
### Verification
- `npx tsc --noEmit` ✅
- `npx eslint src/client/graphics/layers/StructureIconsLayer.ts
tests/perf/StructureIconsLayerLookupPerf.ts` ✅
- `npm run perf` ✅
## Please complete the following:
~~- [ ] I have added screenshots for all UI updates~~
~~- [ ] I process any text displayed to the user through translateText()
and I've added it to the en.json file~~
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
skigim