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 // closure invokes it via _walkPreClearDynamics. For an
// OUTDOOR root there is no clear (retail has none) and the // OUTDOOR root there is no clear (retail has none) and the
// phase runs after the driver, matching the old order. // 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!; Walk.WalkFrameDriver capturedDriver = walkDriver!;
_walkPreClearDynamics = () => _walkPreClearDynamics = () =>
{ {
@ -660,11 +656,7 @@ public sealed class RetailPViewRenderer
ctx, ctx,
passes, passes,
clipAssembly, clipAssembly,
capturedPartition, capturedDriver);
capturedViewcone,
capturedDriver,
capturedPasses,
in capturedView);
}; };
try try
{ {
@ -676,11 +668,7 @@ public sealed class RetailPViewRenderer
ctx, ctx,
passes, passes,
clipAssembly, clipAssembly,
partition, walkDriver!);
viewcone,
walkDriver!,
frameEntityPasses,
in frameView);
} }
} }
finally finally
@ -738,6 +726,13 @@ public sealed class RetailPViewRenderer
in frameView); 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( DrawDynamicsLast(
ctx, ctx,
passes, passes,
@ -746,6 +741,7 @@ public sealed class RetailPViewRenderer
ctx.RootCell.IsOutdoorNode, ctx.RootCell.IsOutdoorNode,
frameEntityPasses, frameEntityPasses,
in frameView); in frameView);
}
// OUTDOOR root: the LScape-boundary alpha drain deferred from the // OUTDOOR root: the LScape-boundary alpha drain deferred from the
// landscape stage runs HERE, after punches, interior shells, cell // 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 /// <summary>Campaign FW3.2b-2: the DYNAMICS-only remainder of the old
/// <see cref="DrawLandscapeThroughOutsideView"/> + <see cref="DrawBuildingLookIns"/> /// <see cref="DrawLandscapeThroughOutsideView"/> + <see cref="DrawBuildingLookIns"/>
/// split — the walk now draws every STATIC route (see /// split. The walk now draws every mesh and every attached emitter at its
/// <see cref="DrawWalkDrivenStatics"/>); this method keeps ONLY what /// own turn. This tail therefore carries only the two landscape services
/// stays on the OLD visibility pipeline per the plan's dual-compute /// that are not scene projections: ownerless outdoor-cell emitters and
/// split: outdoor-cell unattached particles, the late per-slice outside- /// weather. It deliberately submits no packed entity route.</summary>
/// dynamics/weather loop, and the late particle union submission. Look-in
/// dynamics and their cell particles are now walk-turn-owned.</summary>
private void DrawLandscapeDynamicsPhase( private void DrawLandscapeDynamicsPhase(
RetailPViewFrameInput ctx, RetailPViewFrameInput ctx,
IRetailPViewPassExecutor passes, IRetailPViewPassExecutor passes,
ClipFrameAssembly clipAssembly, ClipFrameAssembly clipAssembly,
InteriorEntityPartition.Result? partition, Walk.WalkFrameDriver walkDriver)
ViewconeCuller viewcone,
Walk.WalkFrameDriver walkDriver,
IRenderFrameEntityPassExecutor? frameEntityPasses,
in RenderFrameView frameView)
{ {
if (clipAssembly.OutsideViewSlices.Length == 0) if (clipAssembly.OutsideViewSlices.Length == 0)
return; return;
@ -1471,73 +1461,19 @@ public sealed class RetailPViewRenderer
// per-cell DrawCells turn, before the enclosing building shell. // per-cell DrawCells turn, before the enclosing building shell.
ProbeBuildingLookInFrames(ctx, passes, walkDriver); ProbeBuildingLookInFrames(ctx, passes, walkDriver);
// LATE phase (per slice): outside-stage dynamics' meshes + weather — // GameSky's weather pass still runs through each active landscape
// unchanged from DrawLandscapeThroughOutsideView's own late loop. // view so doorway scissor/clip state matches the old executor. The
_staticParticleUnionScratch.Clear(); // dynamics collection is intentionally empty: the walk already drew
int probeSliceIndex = 0; // those records at OnLandscapeCellTurn.
foreach (var slice in clipAssembly.OutsideViewSlices) foreach (var slice in clipAssembly.OutsideViewSlices)
{ {
passes.SetTerrainClip(slice.Planes); passes.SetTerrainClip(slice.Planes);
passes.ClearClipRouting(); 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( passes.DrawLandscapeSliceLate(
ctx, ctx,
new RetailPViewLandscapeLateSliceContext(slice, _outdoorStaticScratch) new RetailPViewLandscapeLateSliceContext(
{ slice,
EntityDraw = entityDraw, Array.Empty<WorldEntity>()));
});
}
// 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();
} }
passes.UseIndoorMembershipOnlyRouting(); passes.UseIndoorMembershipOnlyRouting();