perf(streaming): cursor publication across frame budgets
This commit is contained in:
parent
bb16f74fd4
commit
98f1ac8934
32 changed files with 2215 additions and 823 deletions
|
|
@ -14,6 +14,10 @@ public readonly record struct LandblockPresentationDiagnostics(
|
|||
LandblockPhysicsPublisherDiagnostics Physics,
|
||||
LandblockStaticPresentationDiagnostics Statics);
|
||||
|
||||
internal readonly record struct LandblockPublicationAdvance(
|
||||
bool Completed,
|
||||
bool Progressed);
|
||||
|
||||
/// <summary>
|
||||
/// Render-thread transaction coordinator for one accepted landblock streaming
|
||||
/// result. Streaming policy (desired tier, generation, priority, and apply
|
||||
|
|
@ -48,6 +52,7 @@ public sealed class LandblockPresentationPipeline
|
|||
public required LandblockBuild Build;
|
||||
public required LandblockMeshData MeshData;
|
||||
public required uint LandblockId;
|
||||
public required LandblockStreamCostEstimate Cost;
|
||||
public LandblockStreamTier Tier;
|
||||
public bool PresentationCommitted;
|
||||
public LandblockRenderPublication? RenderPublication;
|
||||
|
|
@ -186,7 +191,19 @@ public sealed class LandblockPresentationPipeline
|
|||
ArgumentNullException.ThrowIfNull(result);
|
||||
if (!_publications.TryGetValue(result, out PublicationTransaction? transaction))
|
||||
return;
|
||||
Advance(result, transaction);
|
||||
Advance(result, transaction, meter: null, ensureProgress: false);
|
||||
}
|
||||
|
||||
internal LandblockPublicationAdvance ResumePublication(
|
||||
LandblockStreamResult result,
|
||||
StreamingWorkMeter meter,
|
||||
bool ensureProgress)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(result);
|
||||
ArgumentNullException.ThrowIfNull(meter);
|
||||
if (!_publications.TryGetValue(result, out PublicationTransaction? transaction))
|
||||
return new LandblockPublicationAdvance(Completed: true, Progressed: false);
|
||||
return Advance(result, transaction, meter, ensureProgress);
|
||||
}
|
||||
|
||||
public void AdvanceRetirements() => _retirements.Advance();
|
||||
|
|
@ -223,8 +240,28 @@ public sealed class LandblockPresentationPipeline
|
|||
loaded.Build,
|
||||
loaded.MeshData,
|
||||
loaded.LandblockId,
|
||||
loaded.Tier);
|
||||
Advance(loaded, transaction);
|
||||
loaded.Tier,
|
||||
estimate: null);
|
||||
Advance(loaded, transaction, meter: null, ensureProgress: false);
|
||||
}
|
||||
|
||||
internal LandblockPublicationAdvance PublishLoaded(
|
||||
LandblockStreamResult.Loaded loaded,
|
||||
LandblockStreamCostEstimate estimate,
|
||||
StreamingWorkMeter meter,
|
||||
bool ensureProgress)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(loaded);
|
||||
ArgumentNullException.ThrowIfNull(meter);
|
||||
PublicationTransaction transaction = GetOrCreate(
|
||||
loaded,
|
||||
PublicationKind.Loaded,
|
||||
loaded.Build,
|
||||
loaded.MeshData,
|
||||
loaded.LandblockId,
|
||||
loaded.Tier,
|
||||
estimate);
|
||||
return Advance(loaded, transaction, meter, ensureProgress);
|
||||
}
|
||||
|
||||
public void PublishPromoted(
|
||||
|
|
@ -241,8 +278,32 @@ public sealed class LandblockPresentationPipeline
|
|||
promoted.Build,
|
||||
promoted.MeshData,
|
||||
promoted.LandblockId,
|
||||
LandblockStreamTier.Near);
|
||||
Advance(promoted, transaction);
|
||||
LandblockStreamTier.Near,
|
||||
estimate: null);
|
||||
Advance(promoted, transaction, meter: null, ensureProgress: false);
|
||||
}
|
||||
|
||||
internal LandblockPublicationAdvance PublishPromoted(
|
||||
LandblockStreamResult.Promoted promoted,
|
||||
bool mergeIntoExistingLandblock,
|
||||
LandblockStreamCostEstimate estimate,
|
||||
StreamingWorkMeter meter,
|
||||
bool ensureProgress)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(promoted);
|
||||
ArgumentNullException.ThrowIfNull(meter);
|
||||
PublicationKind kind = mergeIntoExistingLandblock
|
||||
? PublicationKind.PromoteExisting
|
||||
: PublicationKind.PromoteSelfContained;
|
||||
PublicationTransaction transaction = GetOrCreate(
|
||||
promoted,
|
||||
kind,
|
||||
promoted.Build,
|
||||
promoted.MeshData,
|
||||
promoted.LandblockId,
|
||||
LandblockStreamTier.Near,
|
||||
estimate);
|
||||
return Advance(promoted, transaction, meter, ensureProgress);
|
||||
}
|
||||
|
||||
public void PublishAsFar(
|
||||
|
|
@ -270,6 +331,11 @@ public sealed class LandblockPresentationPipeline
|
|||
Origin: completedBuild.Origin),
|
||||
MeshData = meshData,
|
||||
LandblockId = farLandblock.LandblockId,
|
||||
Cost = LandblockStreamResultCost.Estimate(
|
||||
new LandblockBuild(
|
||||
farLandblock,
|
||||
Origin: completedBuild.Origin),
|
||||
meshData),
|
||||
Tier = LandblockStreamTier.Far,
|
||||
};
|
||||
_publications.Add(acceptedResult, transaction);
|
||||
|
|
@ -280,7 +346,56 @@ public sealed class LandblockPresentationPipeline
|
|||
$"Landblock publication 0x{transaction.LandblockId:X8} changed kind while pending.");
|
||||
}
|
||||
|
||||
Advance(acceptedResult, transaction);
|
||||
Advance(
|
||||
acceptedResult,
|
||||
transaction,
|
||||
meter: null,
|
||||
ensureProgress: false);
|
||||
}
|
||||
|
||||
internal LandblockPublicationAdvance PublishAsFar(
|
||||
LandblockStreamResult acceptedResult,
|
||||
LandblockBuild completedBuild,
|
||||
LandblockMeshData meshData,
|
||||
StreamingWorkMeter meter,
|
||||
bool ensureProgress)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(acceptedResult);
|
||||
ArgumentNullException.ThrowIfNull(completedBuild);
|
||||
ArgumentNullException.ThrowIfNull(meshData);
|
||||
ArgumentNullException.ThrowIfNull(meter);
|
||||
|
||||
if (!_publications.TryGetValue(
|
||||
acceptedResult,
|
||||
out PublicationTransaction? transaction))
|
||||
{
|
||||
LoadedLandblock completed = completedBuild.Landblock;
|
||||
var farLandblock = new LoadedLandblock(
|
||||
completed.LandblockId,
|
||||
completed.Heightmap,
|
||||
Array.Empty<WorldEntity>(),
|
||||
PhysicsDatBundle.Empty);
|
||||
LandblockBuild farBuild = new(
|
||||
farLandblock,
|
||||
Origin: completedBuild.Origin);
|
||||
transaction = new PublicationTransaction
|
||||
{
|
||||
Kind = PublicationKind.Far,
|
||||
Build = farBuild,
|
||||
MeshData = meshData,
|
||||
LandblockId = farLandblock.LandblockId,
|
||||
Cost = LandblockStreamResultCost.Estimate(farBuild, meshData),
|
||||
Tier = LandblockStreamTier.Far,
|
||||
};
|
||||
_publications.Add(acceptedResult, transaction);
|
||||
}
|
||||
else if (transaction.Kind != PublicationKind.Far)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Landblock publication 0x{transaction.LandblockId:X8} changed kind while pending.");
|
||||
}
|
||||
|
||||
return Advance(acceptedResult, transaction, meter, ensureProgress);
|
||||
}
|
||||
|
||||
private PublicationTransaction GetOrCreate(
|
||||
|
|
@ -289,7 +404,8 @@ public sealed class LandblockPresentationPipeline
|
|||
LandblockBuild build,
|
||||
LandblockMeshData meshData,
|
||||
uint landblockId,
|
||||
LandblockStreamTier tier)
|
||||
LandblockStreamTier tier,
|
||||
LandblockStreamCostEstimate? estimate)
|
||||
{
|
||||
if (_publications.TryGetValue(result, out PublicationTransaction? existing))
|
||||
{
|
||||
|
|
@ -311,71 +427,317 @@ public sealed class LandblockPresentationPipeline
|
|||
Build = build,
|
||||
MeshData = meshData,
|
||||
LandblockId = landblockId,
|
||||
Cost = estimate
|
||||
?? LandblockStreamResultCost.Estimate(build, meshData),
|
||||
Tier = tier,
|
||||
};
|
||||
_publications.Add(result, created);
|
||||
return created;
|
||||
}
|
||||
|
||||
private void Advance(
|
||||
private LandblockPublicationAdvance Advance(
|
||||
LandblockStreamResult result,
|
||||
PublicationTransaction transaction)
|
||||
PublicationTransaction transaction,
|
||||
StreamingWorkMeter? meter,
|
||||
bool ensureProgress)
|
||||
{
|
||||
bool progressed = false;
|
||||
bool TryRun(
|
||||
StreamingWorkCost cost,
|
||||
string stage,
|
||||
Action operation)
|
||||
{
|
||||
if (meter is null)
|
||||
{
|
||||
operation();
|
||||
progressed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
StreamingWorkAdmission admission = meter.TryReserve(
|
||||
cost,
|
||||
stage,
|
||||
ensureProgress && !progressed);
|
||||
if (admission == StreamingWorkAdmission.Yielded)
|
||||
return false;
|
||||
try
|
||||
{
|
||||
operation();
|
||||
meter.Complete();
|
||||
progressed = true;
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
meter.Fail();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
if (!transaction.PresentationCommitted)
|
||||
{
|
||||
if (_renderPublisher is not null
|
||||
&& _physicsPublisher is not null
|
||||
&& _staticPublisher is not null)
|
||||
{
|
||||
transaction.RenderPublication ??=
|
||||
_renderPublisher.PreparePublication(
|
||||
transaction.Build,
|
||||
transaction.MeshData);
|
||||
transaction.PhysicsPublication ??=
|
||||
_physicsPublisher.PreparePublication(
|
||||
transaction.RenderPublication);
|
||||
transaction.StaticPublication ??=
|
||||
_staticPublisher.PreparePublication(
|
||||
transaction.PhysicsPublication);
|
||||
while (transaction.RenderPublication is null)
|
||||
{
|
||||
if (!TryRun(
|
||||
new StreamingWorkCost(EntityOperations: 1),
|
||||
"publication-prepare-render",
|
||||
() => transaction.RenderPublication =
|
||||
_renderPublisher.PreparePublication(
|
||||
transaction.Build,
|
||||
transaction.MeshData)))
|
||||
{
|
||||
return new LandblockPublicationAdvance(false, progressed);
|
||||
}
|
||||
}
|
||||
while (transaction.PhysicsPublication is null)
|
||||
{
|
||||
if (!TryRun(
|
||||
default,
|
||||
"publication-prepare-physics",
|
||||
() => transaction.PhysicsPublication =
|
||||
_physicsPublisher.CreatePublication(
|
||||
transaction.RenderPublication)))
|
||||
{
|
||||
return new LandblockPublicationAdvance(false, progressed);
|
||||
}
|
||||
}
|
||||
while (!transaction.PhysicsPublication.PreparationCommitted)
|
||||
{
|
||||
int entityOperations =
|
||||
transaction.PhysicsPublication.PreparationCursor
|
||||
< transaction.Build.Landblock.Entities.Count
|
||||
? 1
|
||||
: 0;
|
||||
if (!TryRun(
|
||||
new StreamingWorkCost(
|
||||
EntityOperations: entityOperations),
|
||||
"publication-index-physics",
|
||||
() => _physicsPublisher.AdvancePreparationOne(
|
||||
transaction.PhysicsPublication)))
|
||||
{
|
||||
return new LandblockPublicationAdvance(false, progressed);
|
||||
}
|
||||
}
|
||||
while (transaction.StaticPublication is null)
|
||||
{
|
||||
if (!TryRun(
|
||||
default,
|
||||
"publication-prepare-statics",
|
||||
() => transaction.StaticPublication =
|
||||
_staticPublisher.CreatePublication(
|
||||
transaction.PhysicsPublication)))
|
||||
{
|
||||
return new LandblockPublicationAdvance(false, progressed);
|
||||
}
|
||||
}
|
||||
while (!transaction.StaticPublication.PreparationCommitted)
|
||||
{
|
||||
int entityOperations =
|
||||
transaction.StaticPublication.PreparationCursor
|
||||
< transaction.Build.Landblock.Entities.Count
|
||||
? 1
|
||||
: 0;
|
||||
if (!TryRun(
|
||||
new StreamingWorkCost(
|
||||
EntityOperations: entityOperations),
|
||||
"publication-validate-statics",
|
||||
() => _staticPublisher.AdvancePreparationOne(
|
||||
transaction.StaticPublication)))
|
||||
{
|
||||
return new LandblockPublicationAdvance(false, progressed);
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare every immutable receipt before the first external
|
||||
// mutation. Cross-owner identity/data errors therefore block
|
||||
// the publication without leaving a committed prefix.
|
||||
_renderPublisher.BeginPublication(transaction.RenderPublication);
|
||||
_physicsPublisher.BeginPublication(transaction.PhysicsPublication);
|
||||
while (!transaction.RenderPublication.BeginCommitted)
|
||||
{
|
||||
StreamingWorkCost cost =
|
||||
!transaction.RenderPublication.TerrainCommitted
|
||||
? new StreamingWorkCost(
|
||||
GpuUploadBytes:
|
||||
transaction.Cost.TerrainPayloadBytes)
|
||||
: !transaction.RenderPublication.VisibilityCommitted
|
||||
? new StreamingWorkCost(
|
||||
EntityOperations:
|
||||
transaction.Cost.VisibilityCells)
|
||||
: default;
|
||||
if (!TryRun(
|
||||
cost,
|
||||
"publication-render-prefix",
|
||||
() => _renderPublisher.AdvanceBeginOne(
|
||||
transaction.RenderPublication)))
|
||||
{
|
||||
return new LandblockPublicationAdvance(false, progressed);
|
||||
}
|
||||
}
|
||||
while (!transaction.PhysicsPublication.BeginCommitted)
|
||||
{
|
||||
int entityOperations =
|
||||
transaction.PhysicsPublication.TerrainSurface is not null
|
||||
&& transaction.PhysicsPublication.Build.Landblock.PhysicsDats?
|
||||
.Info is { } info
|
||||
&& transaction.PhysicsPublication.CellCursor < info.NumCells
|
||||
? 1
|
||||
: transaction.PhysicsPublication.BuildingCursor
|
||||
< transaction.PhysicsPublication.Buildings.Length
|
||||
? 1
|
||||
: 0;
|
||||
if (!TryRun(
|
||||
new StreamingWorkCost(
|
||||
EntityOperations: entityOperations),
|
||||
"publication-physics-prefix",
|
||||
() => _physicsPublisher.AdvanceBeginOne(
|
||||
transaction.PhysicsPublication)))
|
||||
{
|
||||
return new LandblockPublicationAdvance(false, progressed);
|
||||
}
|
||||
}
|
||||
|
||||
// Retail establishes the building/visible-cell suffix before
|
||||
// each static object publishes lights, collision, and scripts.
|
||||
_renderPublisher.CompletePublication(
|
||||
transaction.RenderPublication);
|
||||
while (!transaction.RenderPublication.CompletionCommitted)
|
||||
{
|
||||
int entityOperations =
|
||||
transaction.RenderPublication.BuildingPublication is
|
||||
{ PreparationCommitted: false } buildingPublication
|
||||
&& buildingPublication.BuildingCursor
|
||||
< buildingPublication.Buildings.Length
|
||||
? 1
|
||||
: transaction.RenderPublication.EnvCellPublication is
|
||||
{ PreparationCommitted: false } envCellPublication
|
||||
&& envCellPublication.ShellCursor
|
||||
< envCellPublication.Build.Shells.Length
|
||||
? 1
|
||||
: !transaction.RenderPublication.EnvCellsCommitted
|
||||
&& transaction.RenderPublication
|
||||
.EnvCellPublication is null
|
||||
? transaction.Cost.EnvCellShells
|
||||
: 0;
|
||||
if (!TryRun(
|
||||
new StreamingWorkCost(
|
||||
EntityOperations: entityOperations),
|
||||
"publication-render-suffix",
|
||||
() => _renderPublisher.AdvanceCompleteOne(
|
||||
transaction.RenderPublication)))
|
||||
{
|
||||
return new LandblockPublicationAdvance(false, progressed);
|
||||
}
|
||||
}
|
||||
|
||||
_staticPublisher.BeginPublication(transaction.StaticPublication);
|
||||
_physicsPublisher.CompletePublication(
|
||||
transaction.PhysicsPublication,
|
||||
entity => _staticPublisher.PrepareEntityBeforeCollision(
|
||||
transaction.StaticPublication,
|
||||
entity));
|
||||
_staticPublisher.CompletePublication(
|
||||
transaction.StaticPublication);
|
||||
while (!transaction.StaticPublication.BeginCommitted)
|
||||
{
|
||||
int entityOperations =
|
||||
transaction.StaticPublication.PriorCleanupCursor
|
||||
< transaction.StaticPublication
|
||||
.OrderedPreviouslyActiveIds.Count
|
||||
? 1
|
||||
: 0;
|
||||
if (!TryRun(
|
||||
new StreamingWorkCost(
|
||||
EntityOperations: entityOperations),
|
||||
"publication-static-cleanup",
|
||||
() => _staticPublisher.AdvanceBeginOne(
|
||||
transaction.StaticPublication)))
|
||||
{
|
||||
return new LandblockPublicationAdvance(false, progressed);
|
||||
}
|
||||
}
|
||||
while (!transaction.PhysicsPublication.CompletionCommitted)
|
||||
{
|
||||
int entityOperations =
|
||||
transaction.PhysicsPublication.GfxCursor
|
||||
< transaction.PhysicsPublication.GfxObjectIds.Length
|
||||
? 1
|
||||
: transaction.PhysicsPublication.PriorStaticCursor
|
||||
< transaction.PhysicsPublication
|
||||
.PriorStaticOwnerIds.Length
|
||||
? 1
|
||||
: transaction.PhysicsPublication.StaticCursor
|
||||
< transaction.Build.Landblock.Entities.Count
|
||||
? 1
|
||||
: transaction.PhysicsPublication
|
||||
.RefloodOwnerIds is not null
|
||||
&& transaction.PhysicsPublication
|
||||
.RefloodCursor
|
||||
< transaction.PhysicsPublication
|
||||
.RefloodOwnerIds.Length
|
||||
? 1
|
||||
: 0;
|
||||
if (!TryRun(
|
||||
new StreamingWorkCost(
|
||||
EntityOperations: entityOperations),
|
||||
"publication-physics-statics",
|
||||
() => _physicsPublisher.AdvanceCompleteOne(
|
||||
transaction.PhysicsPublication,
|
||||
entity =>
|
||||
_staticPublisher
|
||||
.PrepareEntityBeforeCollision(
|
||||
transaction.StaticPublication,
|
||||
entity))))
|
||||
{
|
||||
return new LandblockPublicationAdvance(false, progressed);
|
||||
}
|
||||
}
|
||||
while (!transaction.StaticPublication.CompletionCommitted)
|
||||
{
|
||||
int entityOperations =
|
||||
transaction.StaticPublication.PluginCursor
|
||||
< transaction.StaticPublication.OrderedSnapshots.Count
|
||||
? 1
|
||||
: 0;
|
||||
if (!TryRun(
|
||||
new StreamingWorkCost(
|
||||
EntityOperations: entityOperations),
|
||||
"publication-plugin-projection",
|
||||
() => _staticPublisher.AdvanceCompleteOne(
|
||||
transaction.StaticPublication)))
|
||||
{
|
||||
return new LandblockPublicationAdvance(false, progressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
StreamingWorkCost legacyCost = new(
|
||||
EntityOperations: transaction.Cost.Entities,
|
||||
GpuUploadBytes: transaction.Cost.TerrainPayloadBytes);
|
||||
bool ran = TryRun(
|
||||
legacyCost,
|
||||
"publication-legacy-presentation",
|
||||
() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
(_publishBeforeSpatialCommit
|
||||
?? throw new InvalidOperationException(
|
||||
"The presentation pipeline has no publication owners."))(
|
||||
transaction.Build,
|
||||
transaction.MeshData);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
// Compatibility callbacks cannot report an exact retained
|
||||
// receipt. Preserve the established fail-fast rule only
|
||||
// for callbacks that explicitly report committed mutation.
|
||||
if (error is StreamingMutationException
|
||||
{
|
||||
MutationCommitted: true,
|
||||
})
|
||||
{
|
||||
_publications.Remove(result);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
});
|
||||
if (!ran)
|
||||
{
|
||||
(_publishBeforeSpatialCommit
|
||||
?? throw new InvalidOperationException(
|
||||
"The presentation pipeline has no publication owners."))(
|
||||
transaction.Build,
|
||||
transaction.MeshData);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
// Compatibility callbacks cannot report an exact retained
|
||||
// receipt. Preserve the established fail-fast rule only
|
||||
// for callbacks that explicitly report committed mutation.
|
||||
if (error is StreamingMutationException { MutationCommitted: true })
|
||||
_publications.Remove(result);
|
||||
throw;
|
||||
return new LandblockPublicationAdvance(false, progressed);
|
||||
}
|
||||
}
|
||||
transaction.PresentationCommitted = true;
|
||||
|
|
@ -387,46 +749,70 @@ public sealed class LandblockPresentationPipeline
|
|||
// boundary: bucket mutation, pin/script reconciliation, then the
|
||||
// outer spatial observer commit. If pinning throws, the receipt is
|
||||
// retained and only that suffix retries.
|
||||
using GpuWorldState.MutationBatch mutation = _state.BeginMutationBatch();
|
||||
if (!transaction.SpatialCommitted)
|
||||
{
|
||||
IEnumerable<ulong>? renderIds =
|
||||
transaction.Build.EnvCells?.Shells.Select(static shell => shell.GeometryId);
|
||||
transaction.SpatialPublication = transaction.Kind switch
|
||||
{
|
||||
PublicationKind.Loaded
|
||||
or PublicationKind.PromoteSelfContained
|
||||
or PublicationKind.Far =>
|
||||
_state.CommitLandblockSpatial(
|
||||
transaction.Build.Landblock,
|
||||
renderIds,
|
||||
transaction.Tier),
|
||||
PublicationKind.PromoteExisting =>
|
||||
_state.CommitEntitiesToExistingLandblockSpatial(
|
||||
transaction.LandblockId,
|
||||
transaction.Build.Landblock.Entities,
|
||||
renderIds),
|
||||
_ => throw new InvalidOperationException(
|
||||
$"Unknown landblock publication kind {transaction.Kind}."),
|
||||
};
|
||||
transaction.SpatialCommitted = true;
|
||||
}
|
||||
if (!TryRun(
|
||||
default,
|
||||
"publication-spatial-commit",
|
||||
() =>
|
||||
{
|
||||
using GpuWorldState.MutationBatch mutation =
|
||||
_state.BeginMutationBatch();
|
||||
if (!transaction.SpatialCommitted)
|
||||
{
|
||||
IEnumerable<ulong>? renderIds =
|
||||
transaction.Build.EnvCells?.Shells.Select(
|
||||
static shell => shell.GeometryId);
|
||||
transaction.SpatialPublication = transaction.Kind switch
|
||||
{
|
||||
PublicationKind.Loaded
|
||||
or PublicationKind.PromoteSelfContained
|
||||
or PublicationKind.Far =>
|
||||
_state.CommitLandblockSpatial(
|
||||
transaction.Build.Landblock,
|
||||
renderIds,
|
||||
transaction.Tier),
|
||||
PublicationKind.PromoteExisting =>
|
||||
_state.CommitEntitiesToExistingLandblockSpatial(
|
||||
transaction.LandblockId,
|
||||
transaction.Build.Landblock.Entities,
|
||||
renderIds),
|
||||
_ => throw new InvalidOperationException(
|
||||
$"Unknown landblock publication kind {transaction.Kind}."),
|
||||
};
|
||||
transaction.SpatialCommitted = true;
|
||||
}
|
||||
|
||||
_state.ActivateLandblockPresentation(
|
||||
transaction.SpatialPublication
|
||||
?? throw new InvalidOperationException(
|
||||
"A committed spatial publication has no activation receipt."));
|
||||
transaction.SpatialPresentationCommitted = true;
|
||||
_state.ActivateLandblockPresentation(
|
||||
transaction.SpatialPublication
|
||||
?? throw new InvalidOperationException(
|
||||
"A committed spatial publication has no activation receipt."));
|
||||
transaction.SpatialPresentationCommitted = true;
|
||||
}))
|
||||
{
|
||||
return new LandblockPublicationAdvance(false, progressed);
|
||||
}
|
||||
}
|
||||
|
||||
if (!transaction.EnvCellReplayCommitted)
|
||||
{
|
||||
if (transaction.Kind != PublicationKind.Far
|
||||
&& transaction.Build.EnvCells is { Shells.Length: > 0 } envCells)
|
||||
if (!TryRun(
|
||||
new StreamingWorkCost(
|
||||
EntityOperations: transaction.Cost.EnvCellShells),
|
||||
"publication-envcell-replay",
|
||||
() =>
|
||||
{
|
||||
if (transaction.Kind != PublicationKind.Far
|
||||
&& transaction.Build.EnvCells is
|
||||
{
|
||||
Shells.Length: > 0,
|
||||
} envCells)
|
||||
{
|
||||
_ensureEnvCellMeshes?.Invoke(envCells);
|
||||
}
|
||||
transaction.EnvCellReplayCommitted = true;
|
||||
}))
|
||||
{
|
||||
_ensureEnvCellMeshes?.Invoke(envCells);
|
||||
return new LandblockPublicationAdvance(false, progressed);
|
||||
}
|
||||
transaction.EnvCellReplayCommitted = true;
|
||||
}
|
||||
|
||||
if (!transaction.LiveRecoveryCommitted)
|
||||
|
|
@ -434,10 +820,20 @@ public sealed class LandblockPresentationPipeline
|
|||
// Retained live projections can materialize only after the
|
||||
// canonical bucket exists. Landblock load never reconstructs their
|
||||
// identity or replays create-time resources.
|
||||
_onLandblockLoaded?.Invoke(transaction.LandblockId);
|
||||
transaction.LiveRecoveryCommitted = true;
|
||||
if (!TryRun(
|
||||
default,
|
||||
"publication-live-recovery",
|
||||
() =>
|
||||
{
|
||||
_onLandblockLoaded?.Invoke(transaction.LandblockId);
|
||||
transaction.LiveRecoveryCommitted = true;
|
||||
}))
|
||||
{
|
||||
return new LandblockPublicationAdvance(false, progressed);
|
||||
}
|
||||
}
|
||||
|
||||
_publications.Remove(result);
|
||||
return new LandblockPublicationAdvance(true, progressed);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue