perf(streaming): quiesce retired generations and budget teardown
Publish the retail blocking-for-cells edge before deferred recenter work, freeze old-world presentation/simulation/audio, and advance full-window retirement from exact metered entity and owner cursors. This removes synchronous portal teardown without allowing retained owners to remain observable.
This commit is contained in:
parent
b8f6317fe1
commit
bb16f74fd4
38 changed files with 1691 additions and 170 deletions
109
src/AcDream.App/Streaming/WorldGenerationQuiescence.cs
Normal file
109
src/AcDream.App/Streaming/WorldGenerationQuiescence.cs
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
using AcDream.App.Audio;
|
||||
using AcDream.Core.Selection;
|
||||
|
||||
namespace AcDream.App.Streaming;
|
||||
|
||||
/// <summary>
|
||||
/// Read-only generation gate shared by world simulation and presentation.
|
||||
/// A hard login/portal reveal boundary makes the prior world unavailable
|
||||
/// immediately while its physical owners retire over later frames.
|
||||
/// </summary>
|
||||
public interface IWorldGenerationAvailability
|
||||
{
|
||||
bool IsWorldAvailable { get; }
|
||||
long QuiescedGeneration { get; }
|
||||
}
|
||||
|
||||
internal sealed class AlwaysAvailableWorldGeneration
|
||||
: IWorldGenerationAvailability
|
||||
{
|
||||
public static AlwaysAvailableWorldGeneration Instance { get; } = new();
|
||||
public bool IsWorldAvailable => true;
|
||||
public long QuiescedGeneration => 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Single-writer state behind <see cref="IWorldGenerationAvailability"/>.
|
||||
/// Superseding reveal generations stay continuously quiesced; only the exact
|
||||
/// active generation may reopen the world.
|
||||
/// </summary>
|
||||
internal sealed class WorldGenerationAvailabilityState
|
||||
: IWorldGenerationAvailability
|
||||
{
|
||||
public bool IsWorldAvailable => QuiescedGeneration == 0;
|
||||
public long QuiescedGeneration { get; private set; }
|
||||
|
||||
public void Begin(long generation)
|
||||
{
|
||||
if (generation <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(generation));
|
||||
|
||||
QuiescedGeneration = generation;
|
||||
}
|
||||
|
||||
public bool End(long generation)
|
||||
{
|
||||
if (generation <= 0 || QuiescedGeneration != generation)
|
||||
return false;
|
||||
|
||||
QuiescedGeneration = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Owns the immediate observable edge of retail
|
||||
/// <c>SmartBox::UseTime @ 0x00455410</c>'s <c>blocking_for_cells</c>
|
||||
/// branch. Deferred teardown may retain memory, but the old generation cannot
|
||||
/// keep rendering, targeting, ticking, or producing world audio.
|
||||
/// </summary>
|
||||
internal sealed class WorldGenerationQuiescence
|
||||
{
|
||||
private readonly WorldGenerationAvailabilityState _availability;
|
||||
private readonly SelectionState _selection;
|
||||
private readonly GpuWorldState _world;
|
||||
private readonly IWorldAudioQuiescence? _audio;
|
||||
|
||||
public WorldGenerationQuiescence(
|
||||
WorldGenerationAvailabilityState availability,
|
||||
SelectionState selection,
|
||||
GpuWorldState world,
|
||||
IWorldAudioQuiescence? audio)
|
||||
{
|
||||
_availability = availability
|
||||
?? throw new ArgumentNullException(nameof(availability));
|
||||
_selection = selection ?? throw new ArgumentNullException(nameof(selection));
|
||||
_world = world ?? throw new ArgumentNullException(nameof(world));
|
||||
_audio = audio;
|
||||
}
|
||||
|
||||
public IWorldGenerationAvailability Availability => _availability;
|
||||
|
||||
public void Begin(long generation)
|
||||
{
|
||||
bool firstEdge = _availability.IsWorldAvailable;
|
||||
uint? selected = firstEdge ? _selection.SelectedObjectId : null;
|
||||
bool clearWorldSelection =
|
||||
selected is uint selectedGuid
|
||||
&& _world.IsLiveEntityVisible(selectedGuid);
|
||||
|
||||
_availability.Begin(generation);
|
||||
if (!firstEdge)
|
||||
return;
|
||||
|
||||
if (clearWorldSelection)
|
||||
{
|
||||
_selection.Clear(
|
||||
SelectionChangeSource.System,
|
||||
SelectionChangeReason.Cleared);
|
||||
}
|
||||
|
||||
_audio?.SuspendWorldAudio();
|
||||
}
|
||||
|
||||
public void End(long generation)
|
||||
{
|
||||
if (_availability.End(generation))
|
||||
_audio?.ResumeWorldAudio();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue