feat(rendering): journal static scene projections

Append exact static and EnvCell-shell projection deltas only at committed spatial activation and detach receipts. Same-landblock rehydrate now reconciles retained, new, and omitted presentation identities without constructing or drawing the shadow scene in production.

Co-authored-by: Erik Nilsson <erikn@users.noreply.github.com>
This commit is contained in:
Erik 2026-07-24 21:57:33 +02:00
parent dbd8318417
commit 5d19c56d15
10 changed files with 997 additions and 10 deletions

View file

@ -1,4 +1,5 @@
using AcDream.App.Rendering.Wb;
using AcDream.App.Rendering.Scene;
using AcDream.Core.Terrain;
using AcDream.Core.World;
@ -73,6 +74,7 @@ public sealed class LandblockPresentationPipeline
private readonly GpuWorldState _state;
private readonly Action<uint>? _onLandblockLoaded;
private readonly Action<EnvCellLandblockBuild>? _ensureEnvCellMeshes;
private readonly IRenderStaticProjectionJournalSink? _staticProjectionSink;
private readonly LandblockRetirementCoordinator _retirements;
private readonly Dictionary<LandblockStreamResult, PublicationTransaction> _publications =
new(ReferenceEqualityComparer.Instance);
@ -84,7 +86,8 @@ public sealed class LandblockPresentationPipeline
Action<EnvCellLandblockBuild>? ensureEnvCellMeshes = null,
LandblockRetirementCoordinator? retirementCoordinator = null,
Action<uint>? removeTerrain = null,
Action<uint>? demoteNearLayer = null)
Action<uint>? demoteNearLayer = null,
IRenderStaticProjectionJournalSink? staticProjectionSink = null)
{
ArgumentNullException.ThrowIfNull(publishBeforeSpatialCommit);
ArgumentNullException.ThrowIfNull(state);
@ -93,6 +96,7 @@ public sealed class LandblockPresentationPipeline
_state = state;
_onLandblockLoaded = onLandblockLoaded;
_ensureEnvCellMeshes = ensureEnvCellMeshes;
_staticProjectionSink = staticProjectionSink;
if (retirementCoordinator is not null
&& !retirementCoordinator.MatchesState(_state))
{
@ -120,6 +124,27 @@ public sealed class LandblockPresentationPipeline
LandblockPresentationRetirementOwner retirementOwner,
Action<uint>? onLandblockLoaded = null,
Action<EnvCellLandblockBuild>? ensureEnvCellMeshes = null)
: this(
renderPublisher,
physicsPublisher,
staticPublisher,
state,
retirementOwner,
onLandblockLoaded,
ensureEnvCellMeshes,
staticProjectionSink: null)
{
}
internal LandblockPresentationPipeline(
LandblockRenderPublisher renderPublisher,
LandblockPhysicsPublisher physicsPublisher,
LandblockStaticPresentationPublisher staticPublisher,
GpuWorldState state,
LandblockPresentationRetirementOwner retirementOwner,
Action<uint>? onLandblockLoaded,
Action<EnvCellLandblockBuild>? ensureEnvCellMeshes,
IRenderStaticProjectionJournalSink? staticProjectionSink)
{
_renderPublisher = renderPublisher
?? throw new ArgumentNullException(nameof(renderPublisher));
@ -146,10 +171,14 @@ public sealed class LandblockPresentationPipeline
}
_onLandblockLoaded = onLandblockLoaded;
_ensureEnvCellMeshes = ensureEnvCellMeshes;
_staticProjectionSink = staticProjectionSink;
_retirements = LandblockRetirementCoordinator.CreateBudgeted(
state,
retirementOwner.AdvanceOne,
retirementOwner.Advance);
retirementOwner.Advance,
staticProjectionSink is null
? null
: staticProjectionSink.Retire);
}
/// <summary>
@ -785,6 +814,12 @@ public sealed class LandblockPresentationPipeline
transaction.SpatialPublication
?? throw new InvalidOperationException(
"A committed spatial publication has no activation receipt."));
if (transaction.SpatialPublication.RequiresActivation)
{
_staticProjectionSink?.Reconcile(
transaction.Build,
transaction.SpatialPublication);
}
transaction.SpatialPresentationCommitted = true;
}))
{