diff --git a/docs/ISSUES.md b/docs/ISSUES.md
index 4df0c10c..c3441905 100644
--- a/docs/ISSUES.md
+++ b/docs/ISSUES.md
@@ -135,6 +135,60 @@ choreography (TunnelContinue 2–5 s + two 1 s fades) is retail behavior
and is NOT a tuning target. Predicted result: gate-ready ~2–3 s +
authored exit ≈ 5–8 s total — retail-feel at 2.2x retail's window.
+**2026-08-17 (hold-widening round, this commit): hypothesis (1) LANDED —
+the drip collapsed exactly as predicted (5 s → 2 s) — and the login total
+did NOT move, which is itself the round's finding: a concurrent
+render-thread phase was hiding under the drip and is now the exposed long
+pole.** The frame meter now runs a hold-widened profile ONLY while a
+destination reservation is active
+(`StreamingWorkBudget.WidenForDestinationHold`, keyed off the existing
+`BeginDestinationReservation`/`EndDestinationReservation` bracket — no new
+flags — and derived per-Tick from the CURRENT budget so a mid-hold quality
+swap composes): destination-lane time ceiling 2 ms → 8 ms (absolute
+default; measurement-only env `ACDREAM_STREAM_WORK_HOLD_DEST_MS`), every
+count/byte dimension scaled by the same factor (the measured binder is
+TIME — `[stream-tick] yieldReasons=publication-*/Timex…` at both the 2 ms
+and the 8 ms ceiling; proportional scaling keeps time authoritative
+instead of letting a byte cap become the accidental binder), and the
+reserve fraction re-derived (0.75 → 0.9375) so the NON-destination lane's
+absolute caps are unchanged (floor-exact at defaults:
+floor(256×0.0625) = floor(64×0.25) = 16). No-hold frames use the authored
+budget verbatim (`NoReservation_UsesTheAuthoredBudgetUnchanged` pins it);
+portal holds ride the same bracket by construction
+(`HoldWidening_AppliesToLoginAndPortalRevealsAlikeAndRevertsAtEnd` pins
+kind parity through the real coordinator + controller) and were PROVEN
+live: a @telepoi-yaraq portal hold in the same session recorded
+`kind=portal` gate-ready **3589 ms** / totalMs **8681 ms** at 64–66 fps.
+**Measured (two login A/B runs + one login-then-portal run, this
+binary): loaded 625/625 at 3062/3064/3023 ms (was ~6–7 s), tunnel frames
+64–66 fps through the widened burst (the 28–29 fps first-second window is
+process/session start, present at baseline) — yet gate-ready
+8594/8752/8368 ms and totalMs 12634/12727/12726 (baseline 12689/12734):
+UNCHANGED.** Attribution: after the last publication (~3.0 s) the
+streaming meter is fully idle (`[stream-tick] ops=0 yields=0`) for
+~4.7 s until render-ready (7941/8099/7715 ms) — the pacer is the
+render-thread upload/registration barrier behind
+`GpuWorldState.IsRenderReady` (terrain upload crossing the render-thread
+barrier + spawn-adapter registration,
+`StreamingController.IsRenderNeighborhoodResident`), which ran
+CONCURRENTLY under the old drip — that is why the old breakdown showed it
+as only a ~0.4–1.2 s TAIL (the probe doc's serial-edge caveat) — and
+takes ~7.7–8.1 s from process start regardless of the drip. The
+warmed-process portal run replaces the SAME 625-block window to
+render-ready in ~3.0 s, so the login cost is dominated by cold-start
+work (first-touch DAT decode / texture and mesh cache fill / driver +
+JIT warmup), not by window size or queue capacity. Login acceptance
+(gate-ready < 3500 / total < 10000) NOT met; the widening stays — it
+removes the drip as a pacer everywhere, collapses the portal-hold
+gate-ready to ~3.6 s, and is what exposed the real limiter. No register
+row: the streamed result and the reveal gate are byte-identical; only the
+scheduling rate during a hidden hold changed (same precedent as the
+previous round). **Ladder: 27.4 → 12.7 → login 12.6–12.7 (unchanged, now
+attributed to the cold render-thread barrier); portal-hold gate-ready
+~3.6 s / total ~8.7 s. Next lever: instrument and cut the login-cold
+render-thread upload/registration phase (t≈1–8 s, concurrent) — budgets
+are exonerated three times over.**
+
## #417 — World ambience keeps playing (and re-firing) on the character-select screen after the in-world logoff
**Status:** ✅ FIXED 2026-08-17 (logout-audio round; fix + tests in the same
diff --git a/src/AcDream.App/Streaming/StreamingController.cs b/src/AcDream.App/Streaming/StreamingController.cs
index 998410db..9cd398cf 100644
--- a/src/AcDream.App/Streaming/StreamingController.cs
+++ b/src/AcDream.App/Streaming/StreamingController.cs
@@ -665,9 +665,21 @@ public sealed class StreamingController
throw new InvalidOperationException(
"StreamingController.Tick cannot be reentered.");
+ // #418: while the destination reservation hides the world behind the
+ // authored tunnel, the frame runs on the hold-widened profile so the
+ // reveal-critical drip is not paced by the authored mid-game budget.
+ // Keyed off the existing reservation bracket only, derived from the
+ // CURRENT budget every frame (so a mid-hold quality swap composes),
+ // and reverting the moment EndDestinationReservation clears the
+ // reservation. A frame with no reservation uses _workBudget verbatim.
+ bool destinationHold = _destinationReservation is not null;
var meter = new StreamingWorkMeter(
- _workBudget,
- destinationReservationActive: _destinationReservation is not null);
+ destinationHold
+ ? _workBudget.WidenForDestinationHold(
+ _configuredWorkBudgetOptions
+ .HoldDestinationCeilingMilliseconds)
+ : _workBudget,
+ destinationReservationActive: destinationHold);
_activeWorkMeter = meter;
try
{
diff --git a/src/AcDream.App/Streaming/StreamingWorkBudget.cs b/src/AcDream.App/Streaming/StreamingWorkBudget.cs
index 1d04f352..4918b835 100644
--- a/src/AcDream.App/Streaming/StreamingWorkBudget.cs
+++ b/src/AcDream.App/Streaming/StreamingWorkBudget.cs
@@ -70,6 +70,56 @@ public readonly record struct StreamingWorkBudget
nameof(StreamingWorkBudget));
}
}
+
+ ///
+ /// Hold-scoped destination-lane widening (#418). While a destination
+ /// reservation is active the world behind the reveal gate is hidden by
+ /// the authored tunnel, so destination publication may spend more
+ /// update-thread time per frame without any visible pop-in. This scales
+ /// the WHOLE profile by over the
+ /// current time ceiling — every count/byte dimension by the same factor,
+ /// so elapsed time remains the authoritative guard exactly as the
+ /// authored profile intends (measured: the hold's meter yields are
+ /// Time-limit yields; the count/byte dimensions never bound first) —
+ /// and re-derives the reserve fraction so the NON-destination lane's
+ /// absolute per-frame caps are unchanged: with the shipped profile the
+ /// widened caps are floor-exact (e.g. completions
+ /// floor(256 x 0.0625) = floor(64 x 0.25) = 16); a fractional
+ /// measurement override can drift a lane floor by one unit.
+ /// A ceiling at or below the current time ceiling returns this profile
+ /// unchanged — widening never shrinks, and a no-hold frame never calls
+ /// this at all.
+ ///
+ public StreamingWorkBudget WidenForDestinationHold(
+ double ceilingMilliseconds)
+ {
+ if (!double.IsFinite(ceilingMilliseconds) || ceilingMilliseconds <= 0)
+ return this;
+
+ double scale =
+ ceilingMilliseconds / MaxUpdateTime.TotalMilliseconds;
+ if (scale <= 1.0)
+ return this;
+
+ // The unreserved (non-destination) share shrinks by the same factor
+ // the totals grow, keeping the non-destination lane's absolute caps
+ // at the authored values. Extreme measurement overrides could round
+ // the float to 1.0, which the ctor rejects; the largest float below
+ // one keeps the lane at its Max(1, ...) floor instead.
+ float widenedReserve = (float)(
+ 1.0 - (1.0 - DestinationReserveFraction) / scale);
+ if (widenedReserve >= 1f)
+ widenedReserve = MathF.BitDecrement(1f);
+
+ return new StreamingWorkBudget(
+ TimeSpan.FromMilliseconds(ceilingMilliseconds),
+ StreamingWorkBudgetOptions.Scale(MaxCompletionAdmissions, scale),
+ StreamingWorkBudgetOptions.Scale(MaxAdoptedCpuBytes, scale),
+ StreamingWorkBudgetOptions.Scale(MaxEntityOperations, scale),
+ StreamingWorkBudgetOptions.Scale(MaxGpuUploadBytes, scale),
+ StreamingWorkBudgetOptions.Scale(MaxGlRetireOperations, scale),
+ widenedReserve);
+ }
}
///
diff --git a/src/AcDream.App/Streaming/StreamingWorkBudgetOptions.cs b/src/AcDream.App/Streaming/StreamingWorkBudgetOptions.cs
index edc4800c..7d3f07c3 100644
--- a/src/AcDream.App/Streaming/StreamingWorkBudgetOptions.cs
+++ b/src/AcDream.App/Streaming/StreamingWorkBudgetOptions.cs
@@ -6,6 +6,16 @@ namespace AcDream.App.Streaming;
/// Startup-time scheduling ceilings for update-thread streaming work.
/// These values limit work per frame; they never reduce the amount or
/// quality of content that eventually becomes resident.
+/// (#418) is the
+/// hold-scoped destination-lane time ceiling: while a destination
+/// reservation hides the world behind the authored tunnel, the frame meter
+/// runs on of the
+/// current profile instead of the profile itself. It is an absolute ceiling
+/// (not scaled by the legacy quality selector), it never shrinks a profile
+/// whose own ceiling is already at least as large, and it is NOT a user
+/// setting — the environment override
+/// (ACDREAM_STREAM_WORK_HOLD_DEST_MS) exists for A/B measurement
+/// only, matching the rest of the ACDREAM_STREAM_WORK_* family.
///
public sealed record StreamingWorkBudgetOptions(
double MaxUpdateMilliseconds,
@@ -14,7 +24,8 @@ public sealed record StreamingWorkBudgetOptions(
int MaxEntityOperations,
long MaxGpuUploadBytes,
int MaxGlRetireOperations,
- float DestinationReserveFraction)
+ float DestinationReserveFraction,
+ double HoldDestinationCeilingMilliseconds = 8.0)
{
public const long MiB = 1024L * 1024L;
@@ -30,7 +41,8 @@ public sealed record StreamingWorkBudgetOptions(
MaxEntityOperations: 4_096,
MaxGpuUploadBytes: 8 * MiB,
MaxGlRetireOperations: 64,
- DestinationReserveFraction: 0.75f);
+ DestinationReserveFraction: 0.75f,
+ HoldDestinationCeilingMilliseconds: 8.0);
internal static StreamingWorkBudgetOptions Parse(
Func env)
@@ -58,7 +70,10 @@ public sealed record StreamingWorkBudgetOptions(
defaults.MaxGlRetireOperations),
DestinationReserveFraction: ParseReservePercent(
env("ACDREAM_STREAM_WORK_DEST_RESERVE_PERCENT"),
- defaults.DestinationReserveFraction));
+ defaults.DestinationReserveFraction),
+ HoldDestinationCeilingMilliseconds: ParsePositiveDouble(
+ env("ACDREAM_STREAM_WORK_HOLD_DEST_MS"),
+ defaults.HoldDestinationCeilingMilliseconds));
}
public StreamingWorkBudget ToBudget() => new(
@@ -75,6 +90,10 @@ public sealed record StreamingWorkBudgetOptions(
/// landblock count now selects a complete work profile instead of acting
/// as a second hidden execution throttle. Four is the historical High
/// profile and therefore preserves this configured profile exactly.
+ /// deliberately rides
+ /// through unscaled: it is an absolute hold-scoped ceiling, so a smaller
+ /// quality profile widens further toward the same ceiling during a hold
+ /// and a profile already at or above it widens not at all.
///
public StreamingWorkBudgetOptions ScaleForLegacyCompletionCount(int count)
{
@@ -149,7 +168,7 @@ public sealed record StreamingWorkBudgetOptions(
return percent / 100f;
}
- private static int Scale(int value, double scale)
+ internal static int Scale(int value, double scale)
{
if (scale >= int.MaxValue / (double)value)
return int.MaxValue;
@@ -158,7 +177,7 @@ public sealed record StreamingWorkBudgetOptions(
(int)Math.Round(value * scale, MidpointRounding.AwayFromZero));
}
- private static long Scale(long value, double scale)
+ internal static long Scale(long value, double scale)
{
if (scale >= long.MaxValue / (double)value)
return long.MaxValue;
diff --git a/tests/AcDream.App.Tests/Streaming/StreamingWorkBudgetTests.cs b/tests/AcDream.App.Tests/Streaming/StreamingWorkBudgetTests.cs
index 6ef578a4..71d15663 100644
--- a/tests/AcDream.App.Tests/Streaming/StreamingWorkBudgetTests.cs
+++ b/tests/AcDream.App.Tests/Streaming/StreamingWorkBudgetTests.cs
@@ -3,8 +3,11 @@ using System.Numerics;
using AcDream.App.Rendering;
using AcDream.App.Rendering.Wb;
using AcDream.App.Streaming;
+using AcDream.Core.Physics;
using AcDream.Core.Terrain;
using AcDream.Core.World;
+using AcDream.Runtime;
+using AcDream.Runtime.World;
using DatReaderWriter.DBObjs;
namespace AcDream.App.Tests.Streaming;
@@ -232,6 +235,107 @@ public sealed class StreamingWorkBudgetTests
Assert.Equal(2, meter.Snapshot.CompletedOperations);
}
+ [Fact]
+ public void WidenForDestinationHold_ScalesEveryDimensionAndReservesTheAuthoredNonDestinationShare()
+ {
+ StreamingWorkBudget widened = StreamingWorkBudgetOptions.Default
+ .ToBudget()
+ .WidenForDestinationHold(8.0);
+
+ Assert.Equal(TimeSpan.FromMilliseconds(8), widened.MaxUpdateTime);
+ Assert.Equal(256, widened.MaxCompletionAdmissions);
+ Assert.Equal(
+ 32 * StreamingWorkBudgetOptions.MiB,
+ widened.MaxAdoptedCpuBytes);
+ Assert.Equal(16_384, widened.MaxEntityOperations);
+ Assert.Equal(
+ 32 * StreamingWorkBudgetOptions.MiB,
+ widened.MaxGpuUploadBytes);
+ Assert.Equal(256, widened.MaxGlRetireOperations);
+ // 1 - (1 - 0.75)/4: the non-destination share of the widened totals
+ // is the authored absolute share (floor(256 x 0.0625) =
+ // floor(64 x 0.25) = 16 completions, and so on per dimension).
+ Assert.Equal(0.9375f, widened.DestinationReserveFraction);
+ }
+
+ [Fact]
+ public void WidenForDestinationHold_IsANoOpAtOrBelowTheCurrentCeiling()
+ {
+ StreamingWorkBudget authored =
+ StreamingWorkBudgetOptions.Default.ToBudget();
+
+ Assert.Equal(authored, authored.WidenForDestinationHold(2.0));
+ Assert.Equal(authored, authored.WidenForDestinationHold(1.0));
+ Assert.Equal(authored, authored.WidenForDestinationHold(0.0));
+ Assert.Equal(authored, authored.WidenForDestinationHold(double.NaN));
+ }
+
+ [Fact]
+ public void WidenForDestinationHold_DoesNotWidenTheNonDestinationLane()
+ {
+ StreamingWorkBudget authored =
+ StreamingWorkBudgetOptions.Default.ToBudget();
+ foreach (StreamingWorkBudget budget in new[]
+ {
+ authored,
+ authored.WidenForDestinationHold(8.0),
+ })
+ {
+ var meter = new StreamingWorkMeter(
+ budget,
+ static () => 0,
+ timestampFrequency: 1_000,
+ destinationReservationActive: true);
+
+ // The non-destination lane clips at the SAME absolute cap
+ // (16 completion admissions) whether or not the profile is
+ // hold-widened: widening must never hand mid-game background
+ // work extra capacity.
+ Assert.Equal(
+ StreamingWorkAdmission.Admitted,
+ meter.TryReserve(
+ new StreamingWorkCost(CompletionAdmissions: 16),
+ "ordinary"));
+ meter.Complete();
+ Assert.Equal(
+ StreamingWorkAdmission.Yielded,
+ meter.TryReserve(
+ new StreamingWorkCost(CompletionAdmissions: 1),
+ "ordinary-over-share"));
+ Assert.Equal(
+ StreamingWorkLimit.CompletionAdmissions,
+ meter.Snapshot.LastLimit);
+ }
+
+ // The destination lane of the widened profile owns the widened
+ // remainder: 240 more admissions on top of the ordinary 16.
+ var widenedMeter = new StreamingWorkMeter(
+ authored.WidenForDestinationHold(8.0),
+ static () => 0,
+ timestampFrequency: 1_000,
+ destinationReservationActive: true);
+ Assert.Equal(
+ StreamingWorkAdmission.Admitted,
+ widenedMeter.TryReserve(
+ new StreamingWorkCost(CompletionAdmissions: 16),
+ "ordinary"));
+ widenedMeter.Complete();
+ using (widenedMeter.EnterLane(StreamingWorkLane.Destination))
+ {
+ Assert.Equal(
+ StreamingWorkAdmission.Admitted,
+ widenedMeter.TryReserve(
+ new StreamingWorkCost(CompletionAdmissions: 240),
+ "destination"));
+ widenedMeter.Complete();
+ Assert.Equal(
+ StreamingWorkAdmission.Yielded,
+ widenedMeter.TryReserve(
+ new StreamingWorkCost(CompletionAdmissions: 1),
+ "destination-over-widened-total"));
+ }
+ }
+
[Fact]
public void MeterUsesInjectedMonotonicClockAndRecordsOverrunAndFailure()
{
@@ -598,6 +702,180 @@ public sealed class StreamingWorkBudgetTests
Assert.Equal(0, source.BacklogCount);
}
+ [Fact]
+ public void ActiveHold_WidensTheDestinationLaneAndRevertsWhenTheReservationEnds()
+ {
+ uint center = StreamingRegion.EncodeLandblockIdForTest(32, 32);
+ var source = new QueueCompletionSource(
+ Loaded(center),
+ Loaded(StreamingRegion.EncodeLandblockIdForTest(32, 33)),
+ Loaded(StreamingRegion.EncodeLandblockIdForTest(33, 32)),
+ Loaded(StreamingRegion.EncodeLandblockIdForTest(33, 33)),
+ Loaded(StreamingRegion.EncodeLandblockIdForTest(32, 31)));
+ int applied = 0;
+ // Authored 100 ms / hold ceiling 200 ms: a deterministic 2x widening
+ // that the wall clock cannot reach, so only the admission count can
+ // clip. 2 authored admissions -> 4 during the hold.
+ StreamingController controller = Controller(
+ source,
+ () => applied++,
+ WorkOptions(admissions: 2, cpuBytes: 1_000) with
+ {
+ HoldDestinationCeilingMilliseconds = 200,
+ });
+ controller.BeginDestinationReservation(1, center, 1);
+
+ controller.Tick(32, 32);
+
+ Assert.Equal(4, applied);
+ Assert.Equal(
+ 4,
+ controller.WorkDiagnostics.LastFrame.Used.CompletionAdmissions);
+ Assert.Equal(1, source.BacklogCount);
+
+ // The moment the reservation ends the authored profile is back:
+ // the same backlog now admits 2 per frame, not 4.
+ controller.EndDestinationReservation(1);
+ source.Enqueue(
+ Loaded(StreamingRegion.EncodeLandblockIdForTest(33, 31)));
+ source.Enqueue(
+ Loaded(StreamingRegion.EncodeLandblockIdForTest(31, 32)));
+
+ controller.Tick(32, 32);
+
+ Assert.Equal(6, applied);
+ Assert.Equal(
+ 2,
+ controller.WorkDiagnostics.LastFrame.Used.CompletionAdmissions);
+ Assert.Equal(1, source.BacklogCount);
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void HoldWidening_AppliesToLoginAndPortalRevealsAlikeAndRevertsAtEnd(
+ bool portalKind)
+ {
+ // #418 kind-parity proof: BeginLogin and TryBeginPortal converge on
+ // the SAME BeginHostLifetime -> BeginDestinationReservation bracket
+ // (WorldRevealCoordinator), and the widening keys off that bracket
+ // alone — so a portal hold widens exactly like a login hold, and
+ // both revert the moment the reveal releases the reservation. The
+ // real StreamingController is the coordinator's scheduler here; the
+ // observable is the per-frame admission cap (2 authored, 4 widened
+ // by the 100 ms -> 200 ms hold ceiling).
+ uint center = StreamingRegion.EncodeLandblockIdForTest(32, 32);
+ const uint destinationCell = 0x20200021u; // outdoor cell of (32,32)
+ var source = new QueueCompletionSource(
+ Loaded(center),
+ Loaded(StreamingRegion.EncodeLandblockIdForTest(32, 33)),
+ Loaded(StreamingRegion.EncodeLandblockIdForTest(33, 32)),
+ Loaded(StreamingRegion.EncodeLandblockIdForTest(33, 33)),
+ Loaded(StreamingRegion.EncodeLandblockIdForTest(32, 31)),
+ Loaded(StreamingRegion.EncodeLandblockIdForTest(33, 31)),
+ Loaded(StreamingRegion.EncodeLandblockIdForTest(31, 32)));
+ int applied = 0;
+ StreamingController controller = Controller(
+ source,
+ () => applied++,
+ WorkOptions(admissions: 2, cpuBytes: 1_000) with
+ {
+ HoldDestinationCeilingMilliseconds = 200,
+ });
+ var transit = new RuntimeWorldTransitState(null);
+ var coordinator = new WorldRevealCoordinator(
+ transit,
+ revealWindow: static () => new StreamingRevealWindow(1, 1),
+ isRenderNeighborhoodReady: static (_, _, _) => false,
+ isSpawnCellReady: static _ => false,
+ isTerrainNeighborhoodReady: static (_, _) => false,
+ areCompositeTexturesReady: static () => false,
+ prepareCompositeTextures: static (_, _) => { },
+ invalidateCompositeTextures: static () => { },
+ isSpawnClaimUnhydratable: static _ => false,
+ streaming: controller);
+
+ if (portalKind)
+ {
+ Assert.True(transit.TryQueueTeleportStart(1));
+ Assert.True(transit.ActivateQueuedTeleport());
+ Assert.True(transit.OfferTeleportDestination(
+ new RuntimeTeleportDestination(
+ EntityGuid: 0x50000001u,
+ InstanceSequence: 1,
+ PositionSequence: 1,
+ TeleportSequence: 1,
+ ForcePositionSequence: 1,
+ Position: new Position(
+ destinationCell,
+ Vector3.Zero,
+ Quaternion.Identity)),
+ teleportTimestampAdvanced: true));
+ Assert.True(coordinator.TryBeginPortal(
+ 1,
+ destinationCell,
+ out _));
+ Assert.Equal(
+ RuntimePortalKind.Portal,
+ coordinator.Snapshot.Kind);
+ }
+ else
+ {
+ coordinator.BeginLogin(destinationCell);
+ Assert.Equal(
+ RuntimePortalKind.Login,
+ coordinator.Snapshot.Kind);
+ }
+
+ controller.Tick(32, 32);
+
+ Assert.Equal(4, applied);
+ Assert.Equal(
+ 4,
+ controller.WorkDiagnostics.LastFrame.Used.CompletionAdmissions);
+
+ // Releasing the reveal releases the reservation; the very next frame
+ // runs the authored profile again.
+ coordinator.Cancel();
+ controller.Tick(32, 32);
+
+ Assert.Equal(6, applied);
+ Assert.Equal(
+ 2,
+ controller.WorkDiagnostics.LastFrame.Used.CompletionAdmissions);
+ }
+
+ [Fact]
+ public void NoReservation_UsesTheAuthoredBudgetUnchanged()
+ {
+ // Constraint pin (#418): with NO destination reservation the frame
+ // meter runs on the authored budget verbatim — the configured hold
+ // ceiling must be invisible to mid-game streaming.
+ uint center = StreamingRegion.EncodeLandblockIdForTest(32, 32);
+ var source = new QueueCompletionSource(
+ Loaded(center),
+ Loaded(StreamingRegion.EncodeLandblockIdForTest(32, 33)),
+ Loaded(StreamingRegion.EncodeLandblockIdForTest(33, 32)),
+ Loaded(StreamingRegion.EncodeLandblockIdForTest(33, 33)),
+ Loaded(StreamingRegion.EncodeLandblockIdForTest(32, 31)));
+ int applied = 0;
+ StreamingController controller = Controller(
+ source,
+ () => applied++,
+ WorkOptions(admissions: 2, cpuBytes: 1_000) with
+ {
+ HoldDestinationCeilingMilliseconds = 200,
+ });
+
+ controller.Tick(32, 32);
+
+ Assert.Equal(2, applied);
+ Assert.Equal(
+ 2,
+ controller.WorkDiagnostics.LastFrame.Used.CompletionAdmissions);
+ Assert.Equal(3, source.BacklogCount);
+ }
+
[Fact]
public void IncompleteReveal_DoesNotStarveOrdinaryWorkBeforeDestinationArrives()
{