diff --git a/docs/ISSUES.md b/docs/ISSUES.md index fd4b9ed5..375816d1 100644 --- a/docs/ISSUES.md +++ b/docs/ISSUES.md @@ -148,6 +148,35 @@ of running per arm (`artifacts/owner-gate/player-present-429-packON.csv` frames spend their extra time (the pack's CPU stage profiler names stages) and where the player position is sampled relative to it. +**ROOT CAUSE FOUND 2026-08-23 (frame-history run, 33,350 frames with stage +attribution — `ACDREAM_FRAME_PROF=1` + `ACDREAM_FRAME_HISTORY`):** of 708 +stall frames (>12 ms), 701 allocate 30-77 MB IN THAT SINGLE FRAME (normal +frames: ~22 KB, p99 94 KB). The time and the allocation sit on the render +path outside the tracked stages. The bursts arrive in ~8-frame clusters +during movement — the streaming MESH COMPLETION path: each frame completes +up to `MaxCompletionsPerFrame` (quality High = 4) newly streamed meshes on +the render thread, and each completion in +`ObjectMeshManager.UploadGfxObjMeshData` (~line 2043) runs LINQ chains — +`TextureBatches.Values.SelectMany(...).Select(b => b.Indices.ToArray()) +.ToArray()`, plus the retained pick-support copies +`CPUPositions = Vertices.Select(v => v.Position).ToArray()` and +`CPUIndices = ...SelectMany(...).SelectMany(...).ToArray()` — megabytes of +enumerator/intermediate-List garbage per mesh, tens of MB per frame. +Gen0 runs 70-145 collections per 5 s during movement (vs ~1/6 s idle). + +**Fix shape (a bounded slice, not a quickie — this is the production mesh +pipeline):** +1. De-LINQ the conversion: direct pre-sized loops for the index batches and + the CPU pick copies (sizes are known up front from the batch counts). +2. Consider byte-budgeted completions (4 huge EnvCell meshes is not the + same frame cost as 4 fence posts) and/or moving the CPU-side conversion + onto the existing mesh-preparation scheduler thread so the render thread + only adopts finished arrays. +3. Allocation-gate test in the I1 style: a completion of a representative + mesh set must allocate near its retained-copy size, not multiples of it. +The pack-ON player-jump phase question remains as the second defect but +becomes mostly moot once the stalls themselves shrink. + **Next probes (in order):** 1. `ACDREAM_DUMP_MOTION=1` + a temporary inbound-position log for the LOCAL guid: does ACE send position sets for the local player every