acdream/src/AcDream.App
Erik 119a2326be fix(#193): clear InstanceGroup.Opacities each frame — stop the ~1 GB/min LOH leak
Root cause (measured, not guessed): the OOM was a MANAGED Large-Object-Heap leak
(~5 GB in 6 min of roaming; working set 1.6 GB -> 7.6 GB), dominated by ~3.8 GB of
live System.Single[]. A heap-retention analysis of a captured gcdump traced 241 of
242 giant float arrays to a single List<float>: WbDrawDispatcher.InstanceGroup.Opacities.

#188 (2026-07-09, fading doors) added Opacities as a 5th per-instance parallel list
alongside Matrices/Slots/LightSets/IndoorFlags, but left it out of the per-frame
clear loop (WbDrawDispatcher.cs:959). So Opacities.Add(1.0f) fired once per drawn
instance per frame and the list never reset — growing forever; List<float> capacity
doubling produced the observed ~128 MB / ~512 MB power-of-two arrays and OOM after
~50 min. Hit both UI builds because it's core render, and started only recently
because Opacities did not exist before #188.

Fix: extract the per-frame reset into InstanceGroup.ClearPerInstanceData() (promoted
InstanceGroup private->internal) that clears ALL FIVE parallel lists in one place, so
a future 6th list can't silently drift out of the frame lifecycle again. Line 959 now
calls it. TDD: InstanceGroupClearTests asserts every parallel list resets (RED with
the old 4-list clear, GREEN after adding Opacities.Clear()).

Static-audit note: this is exactly why we measured before fixing — the audit ranked
GlobalMeshBuffer (GPU) #1 and the entity-dict leak #3; both were wrong about the
dominant cause. The dotnet-counters (managed vs native) + gcdump retention graph
named the real one-line culprit.

Secondary (NOT this commit): _groups dictionary is never pruned (bounded by distinct
group keys, empty groups are cheap) — filed as a follow-up, not the multi-GB driver.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 14:15:10 +02:00
..
Audio feat(audio): Phase E.2 OpenAL engine + SoundTable cookbook + hook wiring 2026-04-18 16:38:26 +02:00
Diagnostics fix(pipeline): MP0 - profiler toggle-path hygiene + Max() seed (review follow-up) 2026-07-05 19:26:28 +02:00
Input feat(#182): route player collision response through the ported UpdateObjectInternal chain (Slice 2b/2c) 2026-07-07 14:25:47 +02:00
Net refactor(app): extract LiveSessionController for network-side session lifecycle (Step 2) 2026-05-17 16:15:57 +02:00
Physics feat(#184): Slice 2b — unify the remote player/NPC fork (players collide faithfully) 2026-07-08 00:45:44 +02:00
Plugins feat(D.2b): IUiRegistry plugin UI surface + buffered drain into UiHost 2026-06-14 17:46:37 +02:00
Rendering fix(#193): clear InstanceGroup.Opacities each frame — stop the ~1 GB/min LOH leak 2026-07-10 14:15:10 +02:00
Streaming fix(streaming): recover the player from the pending bucket — invisible-player / world-not-loading bug 2026-07-03 22:03:44 +02:00
Studio feat(ui): D.2b item interaction + retail cursors + live character sheet 2026-07-03 09:18:43 +02:00
UI fix(ui): retail target-cursor shape (pending over UI, world-object driven) + visible UseDone refusals 2026-07-03 10:27:33 +02:00
AcDream.App.csproj feat(pipeline): MP1a - AcDream.Content assembly scaffold 2026-07-05 20:00:33 +02:00
Program.cs feat(studio): StudioWindow + LayoutSource — render a dat panel standalone 2026-06-25 14:26:47 +02:00
RuntimeOptions.cs feat(ui): D.2b item interaction + retail cursors + live character sheet 2026-07-03 09:18:43 +02:00