refactor(runtime): own world reveal generation

This commit is contained in:
Erik 2026-07-26 17:09:54 +02:00
parent 4ab98b080e
commit a6860d5563
26 changed files with 1294 additions and 502 deletions

View file

@ -1,9 +1,13 @@
using AcDream.Runtime;
using AcDream.Runtime.World;
namespace AcDream.App.Streaming;
/// <summary>
/// Generation-scoped destination-work seam. The reveal coordinator is the
/// canonical owner of login/portal lifetime; the streaming scheduler only
/// reserves capacity for the exact generation and destination it is given.
/// graphical host for Runtime's canonical login/portal lifetime; the
/// streaming scheduler only reserves capacity for the exact generation and
/// destination it is given.
/// </summary>
internal interface IWorldRevealStreamingScheduler
{
@ -26,23 +30,23 @@ internal interface IWorldRevealRenderResourceScheduler
}
/// <summary>
/// Owns one login/portal reveal lifetime across readiness and lifecycle
/// diagnostics. This keeps the destination barrier and its observations on a
/// single seam so callers cannot begin, prepare, or evaluate one owner while
/// forgetting the other.
/// Graphical-host adapter for one canonical Runtime login/portal reveal
/// lifetime. App owns render/collision readiness and destination resource
/// reservations; Runtime owns generation, destination, materialization,
/// simulation availability, completion, and cancellation.
/// </summary>
internal sealed class WorldRevealCoordinator
{
private readonly RuntimeWorldTransitState _transit;
private readonly WorldRevealReadinessBarrier _readiness;
private readonly WorldRevealLifecycleTelemetry _lifecycle;
private readonly WorldGenerationQuiescence? _quiescence;
private readonly IWorldRevealStreamingScheduler? _streaming;
private readonly IWorldRevealRenderResourceScheduler? _renderResources;
private long _activeGeneration;
private bool _worldSimulationReleased;
private long _reservationGeneration;
private bool _worldViewportReleased;
public WorldRevealCoordinator(
RuntimeWorldTransitState transit,
Func<uint, int, bool> isRenderNeighborhoodReady,
Func<uint, bool> isSpawnCellReady,
Func<uint, int, bool> isTerrainNeighborhoodReady,
@ -50,11 +54,11 @@ internal sealed class WorldRevealCoordinator
Action<uint, int> prepareCompositeTextures,
Action invalidateCompositeTextures,
Func<uint, bool> isSpawnClaimUnhydratable,
Action<string>? log = null,
WorldGenerationQuiescence? quiescence = null,
IWorldRevealStreamingScheduler? streaming = null,
IWorldRevealRenderResourceScheduler? renderResources = null)
{
_transit = transit ?? throw new ArgumentNullException(nameof(transit));
_readiness = new WorldRevealReadinessBarrier(
isRenderNeighborhoodReady,
isSpawnCellReady,
@ -63,27 +67,28 @@ internal sealed class WorldRevealCoordinator
prepareCompositeTextures,
invalidateCompositeTextures,
isSpawnClaimUnhydratable);
_lifecycle = new WorldRevealLifecycleTelemetry(log);
_quiescence = quiescence;
_streaming = streaming;
_renderResources = renderResources;
}
public WorldRevealLifecycleSnapshot Snapshot => _lifecycle.Snapshot;
public int PortalMaterializationCount => _lifecycle.PortalMaterializationCount;
public bool WaitCueShown => _lifecycle.WaitCueShown;
public RuntimePortalSnapshot Snapshot => _transit.Snapshot;
public int PortalMaterializationCount =>
_transit.Snapshot.PortalMaterializationCount;
public bool WaitCueShown => _transit.Snapshot.WaitCueShown;
public long Begin(WorldRevealKind kind, uint destinationCell)
public long Begin(RuntimePortalKind kind, uint destinationCell)
{
if (destinationCell == 0u)
throw new ArgumentOutOfRangeException(nameof(destinationCell));
WorldGenerationQuiescenceEdge quiescenceEdge =
_quiescence?.CaptureBegin() ?? default;
_readiness.Begin();
long generation = _lifecycle.Begin(kind);
_activeGeneration = generation;
_worldSimulationReleased = false;
long generation = _transit.BeginReveal(kind, destinationCell);
_quiescence?.CommitBegin(quiescenceEdge);
_reservationGeneration = generation;
_worldViewportReleased = false;
_quiescence?.Begin(generation);
_streaming?.BeginDestinationReservation(
generation,
destinationCell,
@ -105,7 +110,20 @@ internal sealed class WorldRevealCoordinator
public WorldRevealReadinessSnapshot Evaluate(uint destinationCell)
{
WorldRevealReadinessSnapshot snapshot = _readiness.Evaluate(destinationCell);
_lifecycle.ObserveReadiness(snapshot);
RuntimePortalSnapshot portal = _transit.Snapshot;
if (portal.Generation != 0)
{
_transit.AcknowledgeDestinationReadiness(
new RuntimeDestinationReadiness(
portal.Generation,
snapshot.DestinationCell,
snapshot.IsIndoor,
snapshot.IsUnhydratable,
snapshot.RequiredRenderRadius,
snapshot.IsRenderNeighborhoodReady,
snapshot.AreCompositeTexturesReady,
snapshot.IsCollisionReady));
}
return snapshot;
}
@ -122,15 +140,30 @@ internal sealed class WorldRevealCoordinator
/// </remarks>
public void ObserveMaterialized()
{
_lifecycle.ObserveMaterialized();
ReleaseWorldSimulation();
RuntimePortalSnapshot snapshot = _transit.Snapshot;
if (snapshot.Generation == 0)
return;
bool wasAvailable = _transit.IsWorldSimulationAvailable;
_transit.AcknowledgeMaterialized(
snapshot.Generation,
snapshot.DestinationCell);
ObserveSimulationRelease(wasAvailable);
}
public void ObserveWorldViewportVisible() =>
_lifecycle.ObserveWorldViewportVisible();
public void ObserveWorldViewportVisible()
{
long generation = _transit.Snapshot.Generation;
if (generation != 0)
_transit.AcknowledgeWorldViewportVisible(generation);
}
public bool ObserveWait(TimeSpan elapsed) =>
_lifecycle.ObserveWait(elapsed);
public bool ObserveWait(TimeSpan elapsed)
{
long generation = _transit.Snapshot.Generation;
return generation != 0
&& _transit.ObserveWait(generation, elapsed);
}
/// <summary>
/// Releases the destination streaming/resource reservation at retail's
@ -140,7 +173,7 @@ internal sealed class WorldRevealCoordinator
/// </summary>
public void RevealWorldViewport()
{
long generation = _activeGeneration;
long generation = _reservationGeneration;
if (generation == 0 || _worldViewportReleased)
return;
@ -151,36 +184,53 @@ internal sealed class WorldRevealCoordinator
public void Complete()
{
_lifecycle.Complete();
EndLifetime();
long generation = _transit.Snapshot.Generation;
if (generation == 0)
return;
bool wasAvailable = _transit.IsWorldSimulationAvailable;
_transit.Complete(generation);
ObserveSimulationRelease(wasAvailable);
EndHostLifetime();
}
public void Cancel()
{
_lifecycle.Cancel();
EndLifetime();
}
private void EndLifetime()
{
long generation = _activeGeneration;
long generation = _transit.Snapshot.Generation;
if (generation == 0)
return;
bool wasAvailable = _transit.IsWorldSimulationAvailable;
_transit.Cancel(generation);
ObserveSimulationRelease(wasAvailable);
EndHostLifetime();
}
public void ResetSession()
{
bool wasAvailable = _transit.IsWorldSimulationAvailable;
long generation = _transit.Snapshot.Generation;
if (generation != 0)
_transit.Cancel(generation);
ObserveSimulationRelease(wasAvailable);
EndHostLifetime();
_transit.ResetSession();
}
private void EndHostLifetime()
{
long generation = _reservationGeneration;
if (generation == 0)
return;
ReleaseWorldSimulation();
RevealWorldViewport();
_activeGeneration = 0;
_worldSimulationReleased = false;
_reservationGeneration = 0;
_worldViewportReleased = false;
}
private void ReleaseWorldSimulation()
private void ObserveSimulationRelease(bool wasAvailable)
{
long generation = _activeGeneration;
if (generation == 0 || _worldSimulationReleased)
return;
_quiescence?.End(generation);
_worldSimulationReleased = true;
if (!wasAvailable && _transit.IsWorldSimulationAvailable)
_quiescence?.ObserveReleased();
}
}