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

@ -17,8 +17,15 @@ namespace AcDream.App.Streaming;
/// Threading: not thread-safe. All calls must happen on the render thread.
/// </remarks>
/// </summary>
public sealed class StreamingController : IStreamingFrameBackend
public sealed class StreamingController
: IStreamingFrameBackend,
IWorldRevealStreamingScheduler
{
private readonly record struct DestinationReservation(
long RevealGeneration,
uint LandblockId,
int Radius);
private sealed class OriginRecenterRetirement
{
public bool RadiiConverged;
@ -61,6 +68,7 @@ public sealed class StreamingController : IStreamingFrameBackend
private readonly StreamingCompletionQueue _completionQueue = new();
private long _nextCompletionSequence;
private int _legacyCompletionProfile = 4;
private DestinationReservation? _destinationReservation;
private readonly GpuWorldState _state;
private StreamingRegion? _region;
@ -120,24 +128,54 @@ public sealed class StreamingController : IStreamingFrameBackend
}
}
/// <summary>
/// #138: the teleport destination landblock id. Loaded or promoted results
/// inside its priority ring enter the destination FIFO, ahead of ordinary
/// Near/Far work, but still consume the same typed frame budget.
///
/// <para>Set by <c>GameWindow</c> when a teleport starts; reset to 0
/// once the destination landblock has been applied (or when the
/// teleport destination changes).</para>
/// </summary>
public uint PriorityLandblockId { get; set; }
internal long ActiveRevealGeneration =>
_destinationReservation?.RevealGeneration ?? 0L;
/// <summary>
/// Radius in landblocks (Chebyshev distance) around
/// <see cref="PriorityLandblockId"/> classified as destination work.
/// Zero means only the center. This changes queue priority, never budget
/// enforcement or atomic publication semantics.
/// </summary>
public int PriorityRadius { get; set; }
internal uint DestinationLandblockId =>
_destinationReservation?.LandblockId ?? 0u;
internal int DestinationRadius =>
_destinationReservation?.Radius ?? 0;
internal void BeginDestinationReservation(
long revealGeneration,
uint destinationCell,
int requiredRenderRadius)
{
if (revealGeneration <= 0)
throw new ArgumentOutOfRangeException(nameof(revealGeneration));
if (destinationCell == 0u)
throw new ArgumentOutOfRangeException(nameof(destinationCell));
if (requiredRenderRadius < 0)
throw new ArgumentOutOfRangeException(nameof(requiredRenderRadius));
_destinationReservation = new DestinationReservation(
revealGeneration,
(destinationCell & 0xFFFF0000u) | 0xFFFFu,
requiredRenderRadius);
}
internal void EndDestinationReservation(long revealGeneration)
{
if (_destinationReservation is { } active
&& active.RevealGeneration == revealGeneration)
{
_destinationReservation = null;
}
}
void IWorldRevealStreamingScheduler.BeginDestinationReservation(
long revealGeneration,
uint destinationCell,
int requiredRenderRadius) =>
BeginDestinationReservation(
revealGeneration,
destinationCell,
requiredRenderRadius);
void IWorldRevealStreamingScheduler.EndDestinationReservation(
long revealGeneration) =>
EndDestinationReservation(revealGeneration);
// [FRAME-DIAG] (read by GameWindow's ACDREAM_WB_DIAG rollup): the standing
// accepted completion backlog. A non-zero value during/after a teleport is
@ -507,6 +545,11 @@ public sealed class StreamingController : IStreamingFrameBackend
for (int i = 0; i < pending.Count; i++)
{
LandblockStreamResult result = pending[i];
using StreamingWorkMeter.LaneScope lane =
_activeWorkMeter.EnterLane(
IsDestinationWork(result.LandblockId)
? StreamingWorkLane.Destination
: StreamingWorkLane.NonDestination);
LandblockPublicationAdvance advance =
_presentation.ResumePublication(
result,
@ -549,7 +592,9 @@ public sealed class StreamingController : IStreamingFrameBackend
throw new InvalidOperationException(
"StreamingController.Tick cannot be reentered.");
var meter = new StreamingWorkMeter(_workBudget);
var meter = new StreamingWorkMeter(
_workBudget,
destinationReservationActive: _destinationReservation is not null);
_activeWorkMeter = meter;
try
{
@ -901,15 +946,13 @@ public sealed class StreamingController : IStreamingFrameBackend
}
/// <summary>
/// True when <paramref name="id"/> is the priority center or within
/// <see cref="PriorityRadius"/> landblocks of it (Chebyshev) — i.e. inside the teleport
/// near ring that <see cref="DrainAndApply"/> prioritizes. With the default radius 0
/// this reduces to an exact match on <see cref="PriorityLandblockId"/> (the original
/// single-landblock priority behaviour).
/// True when <paramref name="id"/> belongs to the canonical reveal
/// generation's destination neighborhood.
/// </summary>
private bool IsWithinPriorityRing(uint id)
=> PriorityLandblockId != 0u
&& ChebyshevLandblocks(id, PriorityLandblockId) <= PriorityRadius;
private bool IsDestinationWork(uint id)
=> _destinationReservation is { } reservation
&& ChebyshevLandblocks(id, reservation.LandblockId)
<= reservation.Radius;
/// <summary>
/// Dungeon-exit edge (portal to outdoors / teleport): rebuild the full
@ -1498,6 +1541,11 @@ public sealed class StreamingController : IStreamingFrameBackend
"The completion queue returned a null head.");
try
{
using StreamingWorkMeter.LaneScope lane = meter.EnterLane(
work.Priority == StreamingCompletionPriority.Destination
&& work.RevealGeneration == ActiveRevealGeneration
? StreamingWorkLane.Destination
: StreamingWorkLane.NonDestination);
LandblockPublicationAdvance advance = ApplyResult(
work,
meter,
@ -1554,6 +1602,9 @@ public sealed class StreamingController : IStreamingFrameBackend
"The completion source returned a null peek.");
bool stale = IsStaleGeneration(result)
&& !_presentation.HasPendingPublication(result);
StreamingCompletionPriority priority = stale
? StreamingCompletionPriority.Control
: ClassifyCompletion(result);
LandblockStreamCostEstimate estimate =
LandblockStreamResultCost.Estimate(result);
StreamingWorkCost admissionCost = stale
@ -1563,6 +1614,10 @@ public sealed class StreamingController : IStreamingFrameBackend
estimate.Work.CompletionAdmissions,
AdoptedCpuBytes:
estimate.Work.AdoptedCpuBytes);
using StreamingWorkMeter.LaneScope lane = meter.EnterLane(
priority == StreamingCompletionPriority.Destination
? StreamingWorkLane.Destination
: StreamingWorkLane.NonDestination);
StreamingWorkAdmission admission = meter.TryReserve(
admissionCost,
"completion-admission");
@ -1584,7 +1639,10 @@ public sealed class StreamingController : IStreamingFrameBackend
_completionQueue.Enqueue(new StreamingQueuedCompletion(
result,
estimate,
ClassifyCompletion(result),
priority,
priority == StreamingCompletionPriority.Destination
? ActiveRevealGeneration
: 0L,
result.Generation,
checked(_nextCompletionSequence++),
Stopwatch.GetTimestamp()));
@ -1604,7 +1662,7 @@ public sealed class StreamingController : IStreamingFrameBackend
{
if (result is LandblockStreamResult.Loaded
or LandblockStreamResult.Promoted
&& IsWithinPriorityRing(result.LandblockId))
&& IsDestinationWork(result.LandblockId))
{
return StreamingCompletionPriority.Destination;
}