refactor(render): retire packed dynamic walk passes

This commit is contained in:
Erik 2026-08-31 04:05:17 +02:00
parent 2127c955f6
commit ef12170a3c

View file

@ -644,10 +644,6 @@ public sealed class RetailPViewRenderer
// closure invokes it via _walkPreClearDynamics. For an
// OUTDOOR root there is no clear (retail has none) and the
// phase runs after the driver, matching the old order.
RenderFrameView capturedView = frameView;
IRenderFrameEntityPassExecutor? capturedPasses = frameEntityPasses;
InteriorEntityPartition.Result? capturedPartition = partition;
ViewconeCuller capturedViewcone = viewcone;
Walk.WalkFrameDriver capturedDriver = walkDriver!;
_walkPreClearDynamics = () =>
{
@ -660,11 +656,7 @@ public sealed class RetailPViewRenderer
ctx,
passes,
clipAssembly,
capturedPartition,
capturedViewcone,
capturedDriver,
capturedPasses,
in capturedView);
capturedDriver);
};
try
{
@ -676,11 +668,7 @@ public sealed class RetailPViewRenderer
ctx,
passes,
clipAssembly,
partition,
viewcone,
walkDriver!,
frameEntityPasses,
in frameView);
walkDriver!);
}
}
finally
@ -738,14 +726,22 @@ public sealed class RetailPViewRenderer
in frameView);
}
DrawDynamicsLast(
ctx,
passes,
partition,
viewcone,
ctx.RootCell.IsOutdoorNode,
frameEntityPasses,
in frameView);
// Campaign FW4: the retail walk now owns live objects at their
// exact DrawObjCell turns (outdoor cells, look-ins, and the root
// flood). The packed DynamicLast route is a legacy duplicate and
// must not run on a walk frame. Standalone/fake executors retain
// the old path only until the FW4 shell-deletion slice removes it.
if (!walkActive)
{
DrawDynamicsLast(
ctx,
passes,
partition,
viewcone,
ctx.RootCell.IsOutdoorNode,
frameEntityPasses,
in frameView);
}
// OUTDOOR root: the LScape-boundary alpha drain deferred from the
// landscape stage runs HERE, after punches, interior shells, cell
@ -1441,21 +1437,15 @@ public sealed class RetailPViewRenderer
/// <summary>Campaign FW3.2b-2: the DYNAMICS-only remainder of the old
/// <see cref="DrawLandscapeThroughOutsideView"/> + <see cref="DrawBuildingLookIns"/>
/// split — the walk now draws every STATIC route (see
/// <see cref="DrawWalkDrivenStatics"/>); this method keeps ONLY what
/// stays on the OLD visibility pipeline per the plan's dual-compute
/// split: outdoor-cell unattached particles, the late per-slice outside-
/// dynamics/weather loop, and the late particle union submission. Look-in
/// dynamics and their cell particles are now walk-turn-owned.</summary>
/// split. The walk now draws every mesh and every attached emitter at its
/// own turn. This tail therefore carries only the two landscape services
/// that are not scene projections: ownerless outdoor-cell emitters and
/// weather. It deliberately submits no packed entity route.</summary>
private void DrawLandscapeDynamicsPhase(
RetailPViewFrameInput ctx,
IRetailPViewPassExecutor passes,
ClipFrameAssembly clipAssembly,
InteriorEntityPartition.Result? partition,
ViewconeCuller viewcone,
Walk.WalkFrameDriver walkDriver,
IRenderFrameEntityPassExecutor? frameEntityPasses,
in RenderFrameView frameView)
Walk.WalkFrameDriver walkDriver)
{
if (clipAssembly.OutsideViewSlices.Length == 0)
return;
@ -1471,73 +1461,19 @@ public sealed class RetailPViewRenderer
// per-cell DrawCells turn, before the enclosing building shell.
ProbeBuildingLookInFrames(ctx, passes, walkDriver);
// LATE phase (per slice): outside-stage dynamics' meshes + weather —
// unchanged from DrawLandscapeThroughOutsideView's own late loop.
_staticParticleUnionScratch.Clear();
int probeSliceIndex = 0;
// GameSky's weather pass still runs through each active landscape
// view so doorway scissor/clip state matches the old executor. The
// dynamics collection is intentionally empty: the walk already drew
// those records at OnLandscapeCellTurn.
foreach (var slice in clipAssembly.OutsideViewSlices)
{
passes.SetTerrainClip(slice.Planes);
passes.ClearClipRouting();
_outdoorStaticScratch.Clear();
_lateParticleOwnerScratch.Clear();
foreach (var e in _outsideStageDynamics)
{
EntitySphere(e, out var c, out float r);
if (viewcone.SphereVisibleInOutsideSlice(probeSliceIndex, c, r))
{
_outdoorStaticScratch.Add(e);
if (!InteriorEntityPartition.IsIndoorCellId(e.ParentCellId))
_lateParticleOwnerScratch.Add(e.Id);
}
}
if (frameEntityPasses is not null)
{
RenderFrameRouteOwnerSelector.Union(
_lateParticleOwnerScratch,
in frameView,
RenderFrameCandidateRoute.LandscapeOutsideDynamic,
probeSliceIndex,
0);
}
_candidateObserver?.ObservePViewBucket(
CurrentRenderPViewRoute.LandscapeOutsideDynamic,
probeSliceIndex,
0,
_outdoorStaticScratch);
RenderFrameEntityDrawRequest? entityDraw =
frameEntityPasses is null
? null
: new RenderFrameEntityDrawRequest(
frameView,
RenderFrameCandidateRoute.LandscapeOutsideDynamic,
probeSliceIndex,
0,
ctx.PlayerLandblockId ?? 0);
probeSliceIndex++;
_staticParticleUnionScratch.UnionWith(_lateParticleOwnerScratch);
passes.DrawLandscapeSliceLate(
ctx,
new RetailPViewLandscapeLateSliceContext(slice, _outdoorStaticScratch)
{
EntityDraw = entityDraw,
});
}
// Late-particle union submission — DynamicLast owners excluded, same
// as DrawLandscapeThroughOutsideView's own final submission.
if (frameEntityPasses is not null)
{
RenderFrameRouteOwnerSelector.ExceptRoute(
_staticParticleUnionScratch, in frameView, RenderFrameCandidateRoute.DynamicLast);
}
if (_staticParticleUnionScratch.Count > 0)
{
passes.DrawLandscapeStaticParticles(
ctx,
new RetailPViewLandscapeStaticParticleContext(_staticParticleUnionScratch));
_staticParticleUnionScratch.Clear();
new RetailPViewLandscapeLateSliceContext(
slice,
Array.Empty<WorldEntity>()));
}
passes.UseIndoorMembershipOnlyRouting();