perf(streaming): reserve destination reveal capacity

Join destination scheduling to the canonical reveal generation, protect its share across every typed frame-budget dimension, and prevent stale work from clearing a replacement reservation. Remove forced incomplete materialization and project retail's centered portal wait cue while the authored tunnel remains active.

Tests: Release build clean; 91 focused reservation/reveal tests; full solution 8,158 passed, 5 skipped.

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Erik 2026-07-24 19:39:23 +02:00
parent 98f1ac8934
commit 2ff8f844b0
33 changed files with 870 additions and 230 deletions

View file

@ -1,5 +1,20 @@
namespace AcDream.App.Streaming;
/// <summary>
/// Generation-scoped destination-work seam. The reveal coordinator is the
/// canonical owner of login/portal lifetime; the streaming scheduler only
/// reserves capacity for the exact generation and destination it is given.
/// </summary>
internal interface IWorldRevealStreamingScheduler
{
void BeginDestinationReservation(
long revealGeneration,
uint destinationCell,
int requiredRenderRadius);
void EndDestinationReservation(long revealGeneration);
}
/// <summary>
/// Owns one login/portal reveal lifetime across readiness and lifecycle
/// diagnostics. This keeps the destination barrier and its observations on a
@ -11,6 +26,7 @@ internal sealed class WorldRevealCoordinator
private readonly WorldRevealReadinessBarrier _readiness;
private readonly WorldRevealLifecycleTelemetry _lifecycle;
private readonly WorldGenerationQuiescence? _quiescence;
private readonly IWorldRevealStreamingScheduler? _streaming;
private long _activeGeneration;
public WorldRevealCoordinator(
@ -22,7 +38,8 @@ internal sealed class WorldRevealCoordinator
Action invalidateCompositeTextures,
Func<uint, bool> isSpawnClaimUnhydratable,
Action<string>? log = null,
WorldGenerationQuiescence? quiescence = null)
WorldGenerationQuiescence? quiescence = null,
IWorldRevealStreamingScheduler? streaming = null)
{
_readiness = new WorldRevealReadinessBarrier(
isRenderNeighborhoodReady,
@ -34,17 +51,26 @@ internal sealed class WorldRevealCoordinator
isSpawnClaimUnhydratable);
_lifecycle = new WorldRevealLifecycleTelemetry(log);
_quiescence = quiescence;
_streaming = streaming;
}
public WorldRevealLifecycleSnapshot Snapshot => _lifecycle.Snapshot;
public int PortalMaterializationCount => _lifecycle.PortalMaterializationCount;
public bool WaitCueShown => _lifecycle.WaitCueShown;
public long Begin(WorldRevealKind kind)
public long Begin(WorldRevealKind kind, uint destinationCell)
{
if (destinationCell == 0u)
throw new ArgumentOutOfRangeException(nameof(destinationCell));
_readiness.Begin();
long generation = _lifecycle.Begin(kind);
_activeGeneration = generation;
_quiescence?.Begin(generation);
_streaming?.BeginDestinationReservation(
generation,
destinationCell,
WorldRevealReadinessBarrier.RequiredRenderRadius(destinationCell));
return generation;
}
@ -70,6 +96,9 @@ internal sealed class WorldRevealCoordinator
public void ObserveWorldViewportVisible() =>
_lifecycle.ObserveWorldViewportVisible();
public bool ObserveWait(TimeSpan elapsed) =>
_lifecycle.ObserveWait(elapsed);
public void Complete()
{
_lifecycle.Complete();
@ -89,6 +118,7 @@ internal sealed class WorldRevealCoordinator
return;
_activeGeneration = 0;
_streaming?.EndDestinationReservation(generation);
_quiescence?.End(generation);
}
}