fix(G.3): collapse streaming to the single dungeon landblock indoors (#133 FPS)
Dungeon FPS sat at ~30 (frame ~33ms) because the 25x25 streaming window around the dungeon landblock pulled in ~129 NEIGHBORING landblocks + their thousands of torch/particle emitters, all drawn though never visible. In AC all dungeons are packed adjacent in the unused "ocean" map grid, so those neighbors are unrelated dungeons. The FPS timeline proved it: 247 fps at login (lb 0/0, ~10K entities) → 17 → 30 as landblocks streamed in (lb 0→129) — the cost tracked LANDBLOCK count, not entities. Retail-faithful: ACE LandblockManager.GetAdjacentIDs returns ZERO adjacents for a dungeon (`if (landblock.IsDungeon) return adjacents;`, Landblock.cs:577-582) — every dungeon is a self-contained landblock you never see out of. Fix: when the player stands in a sealed indoor cell (CurrCell.IsEnv && !SeenOutside — the same predicate that kills the sun/sky), collapse streaming to just the player's dungeon landblock and unload the neighbors. Building interiors (cottage/inn) have SeenOutside cells, so they are NOT gated and keep their surrounding terrain (the frozen building/cellar demo is unaffected). Unloading the neighbors also tears down their lights (removeTerrain → UnregisterOwner), shrinking LightManager._all from ~2227 toward retail's ≤40 — which directly helps the A7 lighting bake landing next. Mechanics (StreamingController): - Edge IN: ClearPendingLoads() cancels the in-flight 25x25 window (new streamer ClearLoads control job — worker drops queued Loads, keeps Unloads), unload every resident neighbor, pin a radius-0 StreamingRegion, (re)load the dungeon block if needed. - Stay collapsed: sweep any straggler that finished loading after the edge (a Load the worker had already dequeued before ClearLoads). - Edge OUT (portal/teleport to outdoors): rebuild the full two-tier window at the new center, unload anything stale. AP-36 added to the divergence register (the gate uses the cheap SeenOutside cell predicate as an approximation of ACE's full landblock IsDungeon classification). GameWindow also carries a TEMP ACDREAM_LOG_FPS=1 headless FPS line (strip after the A7 FPS+lighting verification). Build green; 58 streaming tests green (6 new dungeon-gate tests). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
007e287309
commit
56860501b6
6 changed files with 324 additions and 6 deletions
|
|
@ -141,6 +141,22 @@ public sealed class LandblockStreamer : IDisposable
|
|||
_inbox.Writer.TryWrite(new LandblockStreamJob.Unload(landblockId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel every queued-but-not-started Load. Posts a
|
||||
/// <see cref="LandblockStreamJob.ClearLoads"/> control job which the worker
|
||||
/// honours at read time, dropping all pending Loads from both priority
|
||||
/// queues (Unloads survive). Used on the dungeon-entry edge to abort the
|
||||
/// in-flight 25×25 neighbor window so the ~129 ocean-grid dungeons never
|
||||
/// finish loading (#133 FPS). Loads the worker has ALREADY dequeued still
|
||||
/// complete; the StreamingController's collapsed-sweep unloads those few.
|
||||
/// </summary>
|
||||
public void ClearPendingLoads()
|
||||
{
|
||||
if (System.Threading.Volatile.Read(ref _disposed) != 0)
|
||||
throw new ObjectDisposedException(nameof(LandblockStreamer));
|
||||
_inbox.Writer.TryWrite(new LandblockStreamJob.ClearLoads());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drain up to <paramref name="maxBatchSize"/> completed results.
|
||||
/// Non-blocking. Call from the render thread once per OnUpdate.
|
||||
|
|
@ -180,7 +196,18 @@ public sealed class LandblockStreamer : IDisposable
|
|||
}
|
||||
|
||||
while (_inbox.Reader.TryRead(out var job))
|
||||
{
|
||||
if (job is LandblockStreamJob.ClearLoads)
|
||||
{
|
||||
// Dungeon-entry cancellation: drop every queued Load,
|
||||
// keep Unloads. Handled at read time so it supersedes
|
||||
// Loads sitting in the priority queues ahead of it.
|
||||
DropLoadJobs(highPriority);
|
||||
DropLoadJobs(lowPriority);
|
||||
continue;
|
||||
}
|
||||
EnqueuePrioritized(job, highPriority, lowPriority);
|
||||
}
|
||||
|
||||
if (highPriority.Count == 0 && lowPriority.Count == 0)
|
||||
continue;
|
||||
|
|
@ -233,6 +260,22 @@ public sealed class LandblockStreamer : IDisposable
|
|||
lowPriority.Enqueue(job);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drop every <see cref="LandblockStreamJob.Load"/> from a priority queue,
|
||||
/// preserving Unloads (and any other control jobs). Rotates the queue once
|
||||
/// in place. Used by the <see cref="LandblockStreamJob.ClearLoads"/> path.
|
||||
/// </summary>
|
||||
private static void DropLoadJobs(Queue<LandblockStreamJob> queue)
|
||||
{
|
||||
int count = queue.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var job = queue.Dequeue();
|
||||
if (job is not LandblockStreamJob.Load)
|
||||
queue.Enqueue(job);
|
||||
}
|
||||
}
|
||||
|
||||
private static void RemoveLowPriorityJobsForLandblock(
|
||||
Queue<LandblockStreamJob> queue,
|
||||
uint landblockId,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue