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:
parent
98f1ac8934
commit
2ff8f844b0
33 changed files with 870 additions and 230 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue