fix(portal): synchronize destination presentation state
This commit is contained in:
parent
4b1bceefbb
commit
e95f55f25b
42 changed files with 2815 additions and 288 deletions
|
|
@ -15,13 +15,14 @@ namespace AcDream.App.Rendering.Wb;
|
|||
/// <para>
|
||||
/// On load: walks the landblock's atlas-tier entities, collects unique
|
||||
/// GfxObj ids from their <c>MeshRefs</c>, calls
|
||||
/// <c>IncrementRefCount</c> per id. Snapshots the id-set per landblock so
|
||||
/// unload can match the load 1:1.
|
||||
/// <c>IncrementRefCount</c> per id, and pins each specialized EnvCell geometry
|
||||
/// id without starting generic GfxObj decode. Snapshots both id-sets per
|
||||
/// landblock so unload can match the load 1:1.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// On unload: looks up the snapshot, calls <c>DecrementRefCount</c> per id,
|
||||
/// drops the snapshot. Unknown / never-loaded landblocks no-op.
|
||||
/// On unload: looks up both snapshots, calls <c>DecrementRefCount</c> per id,
|
||||
/// drops the snapshots. Unknown / never-loaded landblocks no-op.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
|
|
@ -34,11 +35,9 @@ namespace AcDream.App.Rendering.Wb;
|
|||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Thread safety: the underlying <see cref="IWbMeshAdapter"/> implementation
|
||||
/// uses <c>ConcurrentDictionary</c>, so the streaming worker thread may call
|
||||
/// this safely. The internal snapshot dictionary is NOT thread-safe and must
|
||||
/// be called from a single streaming thread (the same thread that fires
|
||||
/// AddLandblock / RemoveLandblock events).
|
||||
/// Thread safety: the internal snapshots are intentionally not synchronized.
|
||||
/// <see cref="AcDream.App.Streaming.GpuWorldState"/> invokes every load, unload, and readiness query
|
||||
/// on the owning render/update thread.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public sealed class LandblockSpawnAdapter
|
||||
|
|
@ -48,6 +47,10 @@ public sealed class LandblockSpawnAdapter
|
|||
// Maps landblock id → unique GfxObj ids registered for that landblock.
|
||||
// Written on load, read+cleared on unload. Single-threaded (streaming worker).
|
||||
private readonly Dictionary<uint, HashSet<ulong>> _idsByLandblock = new();
|
||||
// EnvCell shells are prepared through PrepareEnvCellGeomMeshDataAsync rather
|
||||
// than generic GfxObj loading, but still require explicit lifetime pins.
|
||||
// Keep their synthetic ids separate so registration uses the no-decode pin.
|
||||
private readonly Dictionary<uint, HashSet<ulong>> _additionalReadinessIdsByLandblock = new();
|
||||
|
||||
public LandblockSpawnAdapter(IWbMeshAdapter adapter)
|
||||
{
|
||||
|
|
@ -61,7 +64,9 @@ public sealed class LandblockSpawnAdapter
|
|||
/// unique atlas-tier GfxObj id that has not already been registered for
|
||||
/// this landblock.
|
||||
/// </summary>
|
||||
public void OnLandblockLoaded(LoadedLandblock landblock)
|
||||
public void OnLandblockLoaded(
|
||||
LoadedLandblock landblock,
|
||||
IEnumerable<ulong>? additionalReadinessIds = null)
|
||||
{
|
||||
System.ArgumentNullException.ThrowIfNull(landblock);
|
||||
|
||||
|
|
@ -80,14 +85,51 @@ public sealed class LandblockSpawnAdapter
|
|||
{
|
||||
_idsByLandblock[landblock.LandblockId] = unique;
|
||||
foreach (var id in unique) _adapter.IncrementRefCount(id);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var id in unique)
|
||||
{
|
||||
if (registered.Add(id))
|
||||
_adapter.IncrementRefCount(id);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var id in unique)
|
||||
if (!_additionalReadinessIdsByLandblock.TryGetValue(
|
||||
landblock.LandblockId,
|
||||
out var additional))
|
||||
{
|
||||
if (registered.Add(id))
|
||||
_adapter.IncrementRefCount(id);
|
||||
additional = new HashSet<ulong>();
|
||||
_additionalReadinessIdsByLandblock[landblock.LandblockId] = additional;
|
||||
}
|
||||
if (additionalReadinessIds is not null)
|
||||
{
|
||||
foreach (var id in additionalReadinessIds)
|
||||
if (additional.Add(id))
|
||||
_adapter.PinPreparedRenderData(id);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True only after every render mesh required by this published landblock
|
||||
/// is drawable. This includes both ref-counted static GfxObjs and EnvCell
|
||||
/// shell geometry prepared by the independent indoor pipeline.
|
||||
/// </summary>
|
||||
public bool IsLandblockRenderReady(uint landblockId)
|
||||
{
|
||||
if (!_idsByLandblock.TryGetValue(landblockId, out var registered)
|
||||
|| !_additionalReadinessIdsByLandblock.TryGetValue(landblockId, out var additional))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var id in registered)
|
||||
if (!_adapter.IsRenderDataReady(id))
|
||||
return false;
|
||||
foreach (var id in additional)
|
||||
if (!_adapter.IsRenderDataReady(id))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -99,6 +141,9 @@ public sealed class LandblockSpawnAdapter
|
|||
{
|
||||
if (!_idsByLandblock.TryGetValue(landblockId, out var unique)) return;
|
||||
foreach (var id in unique) _adapter.DecrementRefCount(id);
|
||||
if (_additionalReadinessIdsByLandblock.TryGetValue(landblockId, out var additional))
|
||||
foreach (var id in additional) _adapter.DecrementRefCount(id);
|
||||
_idsByLandblock.Remove(landblockId);
|
||||
_additionalReadinessIdsByLandblock.Remove(landblockId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue