test(streaming): enforce slice E physical evidence

Record route-lifetime streaming overruns and maximum operation costs, require canonical reveal/resource convergence at every connected checkpoint, and keep recenter semantics independent of the production wall-clock budget.

Co-authored-by: Erik Nilsson <erikn@users.noreply.github.com>
This commit is contained in:
Erik 2026-07-24 19:46:17 +02:00
parent 2ff8f844b0
commit 621f70eab6
6 changed files with 215 additions and 4 deletions

View file

@ -69,6 +69,12 @@ public sealed class StreamingController
private long _nextCompletionSequence; private long _nextCompletionSequence;
private int _legacyCompletionProfile = 4; private int _legacyCompletionProfile = 4;
private DestinationReservation? _destinationReservation; private DestinationReservation? _destinationReservation;
private long _lifetimeWorkOverruns;
private long _lifetimeOversizedProgress;
private double _maximumWorkFrameMilliseconds;
private string? _maximumWorkFrameStage;
private double _maximumWorkOperationMilliseconds;
private string? _maximumWorkOperationStage;
private readonly GpuWorldState _state; private readonly GpuWorldState _state;
private StreamingRegion? _region; private StreamingRegion? _region;
@ -190,6 +196,12 @@ public sealed class StreamingController
_completionQueue.CaptureSnapshot(); _completionQueue.CaptureSnapshot();
return new StreamingWorkDiagnostics( return new StreamingWorkDiagnostics(
_lastWorkMeter, _lastWorkMeter,
_lifetimeWorkOverruns,
_lifetimeOversizedProgress,
_maximumWorkFrameMilliseconds,
_maximumWorkFrameStage,
_maximumWorkOperationMilliseconds,
_maximumWorkOperationStage,
queued.Count, queued.Count,
queued.RetainedCpuBytes, queued.RetainedCpuBytes,
queued.OldestAgeMilliseconds, queued.OldestAgeMilliseconds,
@ -690,11 +702,42 @@ public sealed class StreamingController
finally finally
{ {
meter.FinishFrame(); meter.FinishFrame();
_lastWorkMeter = meter.Snapshot; StreamingWorkMeterSnapshot snapshot = meter.Snapshot;
ObserveWorkLifetime(snapshot);
_lastWorkMeter = snapshot;
_activeWorkMeter = null; _activeWorkMeter = null;
} }
} }
private void ObserveWorkLifetime(StreamingWorkMeterSnapshot snapshot)
{
_lifetimeWorkOverruns = SaturatingAdd(
_lifetimeWorkOverruns,
snapshot.OverrunCount);
_lifetimeOversizedProgress = SaturatingAdd(
_lifetimeOversizedProgress,
snapshot.OversizedProgressCount);
if (_maximumWorkFrameStage is null
|| snapshot.ElapsedMilliseconds > _maximumWorkFrameMilliseconds)
{
_maximumWorkFrameMilliseconds = snapshot.ElapsedMilliseconds;
_maximumWorkFrameStage = snapshot.LastStage;
}
if (_maximumWorkOperationStage is null
|| snapshot.MaximumOperationMilliseconds
> _maximumWorkOperationMilliseconds)
{
_maximumWorkOperationMilliseconds =
snapshot.MaximumOperationMilliseconds;
_maximumWorkOperationStage = snapshot.MaximumOperationStage;
}
}
private static long SaturatingAdd(long left, int right) =>
left > long.MaxValue - right ? long.MaxValue : left + right;
private void AdvanceRadiiReconfiguration() private void AdvanceRadiiReconfiguration()
{ {
if (_advancingRadiiReconfiguration) if (_advancingRadiiReconfiguration)

View file

@ -155,6 +155,8 @@ public readonly record struct StreamingWorkMeterSnapshot(
int OversizedProgressCount, int OversizedProgressCount,
int FailureCount, int FailureCount,
double ElapsedMilliseconds, double ElapsedMilliseconds,
double MaximumOperationMilliseconds,
string? MaximumOperationStage,
string? LastStage, string? LastStage,
StreamingWorkLimit LastLimit); StreamingWorkLimit LastLimit);
@ -163,6 +165,12 @@ public readonly record struct StreamingWorkMeterSnapshot(
/// </summary> /// </summary>
public readonly record struct StreamingWorkDiagnostics( public readonly record struct StreamingWorkDiagnostics(
StreamingWorkMeterSnapshot LastFrame, StreamingWorkMeterSnapshot LastFrame,
long LifetimeFrameOverrunCount,
long LifetimeOversizedProgressCount,
double MaximumFrameMilliseconds,
string? MaximumFrameStage,
double MaximumOperationMilliseconds,
string? MaximumOperationStage,
int DeferredCompletions, int DeferredCompletions,
long DeferredAdoptedCpuBytes, long DeferredAdoptedCpuBytes,
double OldestDeferredAgeMilliseconds, double OldestDeferredAgeMilliseconds,
@ -193,6 +201,8 @@ public sealed class StreamingWorkMeter
private long _destinationElapsedTicks; private long _destinationElapsedTicks;
private long _nonDestinationElapsedTicks; private long _nonDestinationElapsedTicks;
private long _activeOperationStart; private long _activeOperationStart;
private double _maximumOperationMilliseconds;
private string? _maximumOperationStage;
private int _operations; private int _operations;
private int _completed; private int _completed;
private int _yields; private int _yields;
@ -303,8 +313,8 @@ public sealed class StreamingWorkMeter
CompleteLaneTiming(); CompleteLaneTiming();
_completed++; _completed++;
_reservationActive = false; _reservationActive = false;
_activeStage = null;
ObserveElapsedOverrun(); ObserveElapsedOverrun();
_activeStage = null;
} }
public void Fail() public void Fail()
@ -313,8 +323,8 @@ public sealed class StreamingWorkMeter
CompleteLaneTiming(); CompleteLaneTiming();
_failures++; _failures++;
_reservationActive = false; _reservationActive = false;
_activeStage = null;
ObserveElapsedOverrun(); ObserveElapsedOverrun();
_activeStage = null;
} }
public void FinishFrame() public void FinishFrame()
@ -341,6 +351,8 @@ public sealed class StreamingWorkMeter
_oversizedProgress, _oversizedProgress,
_failures, _failures,
ElapsedMilliseconds(now), ElapsedMilliseconds(now),
_maximumOperationMilliseconds,
_maximumOperationStage,
_lastStage, _lastStage,
_lastLimit); _lastLimit);
} }
@ -428,6 +440,13 @@ public sealed class StreamingWorkMeter
private void CompleteLaneTiming() private void CompleteLaneTiming()
{ {
long raw = Math.Max(0L, _timestamp() - _activeOperationStart); long raw = Math.Max(0L, _timestamp() - _activeOperationStart);
double elapsedMilliseconds = raw * 1000.0 / _frequency;
if (_maximumOperationStage is null
|| elapsedMilliseconds > _maximumOperationMilliseconds)
{
_maximumOperationMilliseconds = elapsedMilliseconds;
_maximumOperationStage = _activeStage;
}
long elapsed = raw > long.MaxValue / TimeSpan.TicksPerSecond long elapsed = raw > long.MaxValue / TimeSpan.TicksPerSecond
? long.MaxValue ? long.MaxValue
: raw * TimeSpan.TicksPerSecond / _frequency; : raw * TimeSpan.TicksPerSecond / _frequency;

View file

@ -191,6 +191,46 @@ public sealed class ConnectedR6SoakContractTests
foreach (string field in zeroFields) foreach (string field in zeroFields)
Assert.Contains($"'{field}'", source, StringComparison.Ordinal); Assert.Contains($"'{field}'", source, StringComparison.Ordinal);
string[] revealFields =
[
"materialized",
"completed",
"worldViewportObserved",
"cancelled",
"invariantFailureCount",
"isReady",
];
foreach (string field in revealFields)
Assert.Contains($"'{field}'", source, StringComparison.Ordinal);
string[] streamingZeroFields =
[
"deferredCompletions",
"deferredAdoptedCpuBytes",
"pendingPublications",
"pendingRetirements",
"workerCompletionBacklog",
"destinationBacklog",
"controlBacklog",
"unloadBacklog",
"nearBacklog",
"farBacklog",
];
foreach (string field in streamingZeroFields)
Assert.Contains($"'{field}'", source, StringComparison.Ordinal);
string[] streamingEvidenceFields =
[
"lifetimeFrameOverrunCount",
"lifetimeOversizedProgressCount",
"maximumFrameMilliseconds",
"maximumFrameStage",
"maximumOperationMilliseconds",
"maximumOperationStage",
];
foreach (string field in streamingEvidenceFields)
Assert.Contains($"'{field}'", source, StringComparison.Ordinal);
Assert.Equal(1, CountOccurrences( Assert.Equal(1, CountOccurrences(
source, source,
"$privateLimit = [Math]::Max(192.0, $pair.First.PrivateMiB * 0.20)")); "$privateLimit = [Math]::Max(192.0, $pair.First.PrivateMiB * 0.20)"));

View file

@ -633,7 +633,15 @@ public sealed class LandblockPresentationPipelineTests
applyTerrain: (_, _) => publications++, applyTerrain: (_, _) => publications++,
state, state,
nearRadius: 2, nearRadius: 2,
farRadius: 4); farRadius: 4,
workBudgetOptions: new StreamingWorkBudgetOptions(
MaxUpdateMilliseconds: 100,
MaxCompletionAdmissions: 64,
MaxAdoptedCpuBytes: 1_000_000,
MaxEntityOperations: 1_000,
MaxGpuUploadBytes: 1_000_000,
MaxGlRetireOperations: 1_000,
DestinationReserveFraction: 0.75f));
var recenter = new StreamingOriginRecenterCoordinator(controller, origin); var recenter = new StreamingOriginRecenterCoordinator(controller, origin);
Assert.False(recenter.Begin(0x30, 0x30, isSealedDungeon: true)); Assert.False(recenter.Begin(0x30, 0x30, isSealedDungeon: true));

View file

@ -241,6 +241,9 @@ public sealed class StreamingWorkBudgetTests
Assert.Equal(1, snapshot.OverrunCount); Assert.Equal(1, snapshot.OverrunCount);
Assert.Equal(1, snapshot.FailureCount); Assert.Equal(1, snapshot.FailureCount);
Assert.Equal(0, snapshot.CompletedOperations); Assert.Equal(0, snapshot.CompletedOperations);
Assert.Equal(3, snapshot.MaximumOperationMilliseconds);
Assert.Equal("slow", snapshot.MaximumOperationStage);
Assert.Equal("slow", snapshot.LastStage);
Assert.Equal(StreamingWorkLimit.Time, snapshot.LastLimit); Assert.Equal(StreamingWorkLimit.Time, snapshot.LastLimit);
} }
@ -465,6 +468,29 @@ public sealed class StreamingWorkBudgetTests
Assert.Equal(4, second.LastFrame.CompletedOperations); Assert.Equal(4, second.LastFrame.CompletedOperations);
} }
[Fact]
public void ControllerPublishesLifetimeOversizeAndMaximumOperationEvidence()
{
uint center = StreamingRegion.EncodeLandblockIdForTest(32, 32);
var source = new QueueCompletionSource(Loaded(center));
StreamingController controller = Controller(
source,
static () => { },
WorkOptions(
admissions: 4,
cpuBytes: 1_000,
gpuBytes: 1));
controller.Tick(32, 32);
StreamingWorkDiagnostics diagnostics = controller.WorkDiagnostics;
Assert.True(diagnostics.LifetimeOversizedProgressCount >= 1);
Assert.True(diagnostics.MaximumOperationMilliseconds >= 0);
Assert.NotNull(diagnostics.MaximumOperationStage);
Assert.True(diagnostics.MaximumFrameMilliseconds >=
diagnostics.MaximumOperationMilliseconds);
}
[Fact] [Fact]
public void ControllerBoundsProductionAdmissionByCountBeforeAdoption() public void ControllerBoundsProductionAdmissionByCountBeforeAdoption()
{ {

View file

@ -336,6 +336,42 @@ function Add-CanonicalCheckpointFailures([Diagnostics.Process]$Client) {
$failures.Add("canonical checkpoint '$expectedName' came from process $($checkpoint.processId), expected $($Client.Id)") $failures.Add("canonical checkpoint '$expectedName' came from process $($checkpoint.processId), expected $($Client.Id)")
} }
if ($null -eq $checkpoint.reveal) {
$failures.Add("canonical checkpoint '$expectedName' is missing reveal data")
}
else {
foreach ($field in @(
'materialized',
'completed',
'worldViewportObserved')) {
$property = $checkpoint.reveal.PSObject.Properties[$field]
if ($null -eq $property -or -not [bool]$property.Value) {
$failures.Add("canonical checkpoint '$expectedName' has reveal.$field=false or missing")
}
}
$cancelled =
$checkpoint.reveal.PSObject.Properties['cancelled']
if ($null -eq $cancelled -or [bool]$cancelled.Value) {
$failures.Add("canonical checkpoint '$expectedName' has reveal.cancelled=true or missing")
}
$invariants =
$checkpoint.reveal.PSObject.Properties['invariantFailureCount']
if ($null -eq $invariants -or [long]$invariants.Value -ne 0) {
$failures.Add("canonical checkpoint '$expectedName' has reveal invariant failure(s)")
}
$readiness =
$checkpoint.reveal.PSObject.Properties['readiness']
$isReady = if ($null -ne $readiness) {
$readiness.Value.PSObject.Properties['isReady']
}
if ($null -eq $isReady -or -not [bool]$isReady.Value) {
$failures.Add("canonical checkpoint '$expectedName' has incomplete reveal readiness")
}
}
if ($null -eq $checkpoint.render -or $null -eq $checkpoint.resources) { if ($null -eq $checkpoint.render -or $null -eq $checkpoint.resources) {
$failures.Add("canonical checkpoint '$expectedName' is missing render/resources data") $failures.Add("canonical checkpoint '$expectedName' is missing render/resources data")
continue continue
@ -403,6 +439,45 @@ function Add-CanonicalCheckpointFailures([Diagnostics.Process]$Client) {
$failures.Add("canonical checkpoint '$expectedName' has resources.$field=$($property.Value), expected zero") $failures.Add("canonical checkpoint '$expectedName' has resources.$field=$($property.Value), expected zero")
} }
} }
$streamingWork =
$checkpoint.resources.PSObject.Properties['streamingWork']
if ($null -eq $streamingWork) {
$failures.Add("canonical checkpoint '$expectedName' is missing resources.streamingWork")
}
else {
foreach ($field in @(
'lifetimeFrameOverrunCount',
'lifetimeOversizedProgressCount',
'maximumFrameMilliseconds',
'maximumFrameStage',
'maximumOperationMilliseconds',
'maximumOperationStage')) {
if ($null -eq $streamingWork.Value.PSObject.Properties[$field]) {
$failures.Add("canonical checkpoint '$expectedName' is missing resources.streamingWork.$field")
}
}
foreach ($field in @(
'deferredCompletions',
'deferredAdoptedCpuBytes',
'pendingPublications',
'pendingRetirements',
'workerCompletionBacklog',
'destinationBacklog',
'controlBacklog',
'unloadBacklog',
'nearBacklog',
'farBacklog')) {
$property = $streamingWork.Value.PSObject.Properties[$field]
if ($null -eq $property) {
$failures.Add("canonical checkpoint '$expectedName' is missing resources.streamingWork.$field")
}
elseif ([long]$property.Value -ne 0) {
$failures.Add("canonical checkpoint '$expectedName' has resources.streamingWork.$field=$($property.Value), expected zero")
}
}
}
} }
if ($DenseTown) { if ($DenseTown) {