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

@ -53,7 +53,8 @@ public sealed class PortalTunnelPresentation : IDisposable
private readonly WorldEntity _entity;
private readonly PortalTunnelCamera _camera = new();
private readonly Random _random;
private readonly Action<string>? _displayNotice;
private readonly Action<string?>? _displayNotice;
private IDisposable? _displayNoticeLifetime;
private readonly PortalMeshReferenceOwner _meshReferences;
private bool _visible;
@ -62,6 +63,7 @@ public sealed class PortalTunnelPresentation : IDisposable
private float _rotationStartAngle;
private float _rotationEndAngle;
private float _rotationCurrentAngle;
private bool _waitCueVisible;
private bool _disposeRequested;
private bool _disposing;
private bool _disposed;
@ -77,7 +79,8 @@ public sealed class PortalTunnelPresentation : IDisposable
IAnimationLoader animationLoader,
IAnimationHookSink hookSink,
Random random,
Action<string>? displayNotice)
Action<string?>? displayNotice,
IDisposable? displayNoticeLifetime)
{
_gl = gl;
_dispatcher = dispatcher;
@ -90,6 +93,7 @@ public sealed class PortalTunnelPresentation : IDisposable
_sequence.HookObj = _animationHooks;
_random = random;
_displayNotice = displayNotice;
_displayNoticeLifetime = displayNoticeLifetime;
_entity = new WorldEntity
{
@ -119,7 +123,8 @@ public sealed class PortalTunnelPresentation : IDisposable
WbDrawDispatcher dispatcher,
SceneLightingUboBinding lightUbo,
IWbMeshAdapter meshAdapter,
Action<string>? displayNotice = null,
Action<string?>? displayNotice = null,
IDisposable? displayNoticeLifetime = null,
Random? random = null)
{
ArgumentNullException.ThrowIfNull(gl);
@ -154,7 +159,8 @@ public sealed class PortalTunnelPresentation : IDisposable
animationLoader,
hookSink,
random ?? Random.Shared,
displayNotice);
displayNotice,
displayNoticeLifetime);
}
internal static void EnsureRequiredAssets(
@ -211,6 +217,7 @@ public sealed class PortalTunnelPresentation : IDisposable
_rotationEndAngle = 0f;
_rotationCurrentAngle = 0f;
_camera.DirectionDegrees = 0f;
SetWaitCue(false);
_visible = true;
RebuildPose();
}
@ -221,6 +228,7 @@ public sealed class PortalTunnelPresentation : IDisposable
if (_disposed)
return;
_visible = false;
SetWaitCue(false);
_animationHooks.Clear();
_sequence.ClearAnimations();
}
@ -236,6 +244,16 @@ public sealed class PortalTunnelPresentation : IDisposable
TickRotation(dt);
}
public void SetWaitCue(bool visible)
{
if (_waitCueVisible == visible)
return;
_waitCueVisible = visible;
_displayNotice?.Invoke(
visible ? "In Portal Space - Please Wait..." : null);
}
/// <summary>
/// Draw retail portal space into the active viewport. The caller suppresses
/// the normal world viewport while this scene is visible, then draws the
@ -298,7 +316,8 @@ public sealed class PortalTunnelPresentation : IDisposable
_rotationDuration = NextDouble(RotationDurationMin, RotationDurationMax);
_rotationStartAngle = _rotationCurrentAngle;
_rotationEndAngle = (float)NextDouble(0.0, 360.0);
_displayNotice?.Invoke("In Portal Space - Please Wait...");
if (_waitCueVisible)
_displayNotice?.Invoke("In Portal Space - Please Wait...");
}
else
{
@ -389,11 +408,16 @@ public sealed class PortalTunnelPresentation : IDisposable
try
{
_visible = false;
SetWaitCue(false);
_animationHooks.Clear();
_sequence.ClearAnimations();
_meshReferences.Dispose();
if (_meshReferences.IsDisposed)
{
_displayNoticeLifetime?.Dispose();
_displayNoticeLifetime = null;
_disposed = true;
}
}
finally
{