diff --git a/src/AcDream.App/Streaming/StreamingController.cs b/src/AcDream.App/Streaming/StreamingController.cs index 35362a6..6bdd957 100644 --- a/src/AcDream.App/Streaming/StreamingController.cs +++ b/src/AcDream.App/Streaming/StreamingController.cs @@ -26,26 +26,22 @@ public sealed class StreamingController public int Radius { get; set; } /// - /// Cap on completions drained per call. Defaults to - /// effectively unlimited because the current LandblockStreamer - /// is synchronous — every EnqueueLoad writes to the outbox on - /// the same thread, so by the time we drain there's no backlog - /// to spread, and the cap only serves to *delay* applying landblocks - /// the user is already trying to look at. + /// Cap on completions drained per call. The cap is + /// the GPU upload budget for one frame: terrain mesh + per-entity GfxObj + /// sub-mesh uploads + texture uploads for one landblock take a few ms; + /// applying 25 of them in a single frame produces a memory spike + /// (observed: out-of-memory crash on the 5×5 first-frame load). /// /// - /// The original async design used a small cap (4) to limit per-frame - /// GPU upload spikes. That reasoning becomes relevant again if/when - /// the streamer moves back to async loading; lower this knob then. - /// Crucially, dropping completions to a lower frame is what was - /// silently breaking live spawns: the post-login spawn flood would - /// arrive on a frame where only 4 of the 25 visible-window landblocks - /// had been applied, the spawns for the other 21 hit - /// AppendLiveEntity with no matching loaded slot, and got - /// dropped (now: parked in the pending bucket). + /// 4 is the original async-streamer value; it spreads a 5×5 first-frame + /// load over ~7 frames (~116ms at 60fps), which is below the human + /// perception threshold. Spawn races that previously dropped entities + /// while landblocks were in flight are now handled by + /// 's pending-spawn list, so spreading + /// completions doesn't lose any data. /// /// - public int MaxCompletionsPerFrame { get; set; } = int.MaxValue; + public int MaxCompletionsPerFrame { get; set; } = 4; public StreamingController( Action enqueueLoad,