checkpoint: preserve user-gated FW closeout fixes

This commit is contained in:
Erik 2026-08-31 08:27:37 +02:00
parent a2f2eb7d78
commit b8befded8b
22 changed files with 1005 additions and 198 deletions

View file

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Numerics;
using AcDream.App.Rendering.Scene;
using AcDream.Core.Physics;
using AcDream.Core.World;
namespace AcDream.App.Rendering;
@ -29,6 +30,7 @@ internal sealed class RetailPViewRenderer
// frames instead of `new HashSet<uint>(pvFrame.OrderedVisibleCells)` every
// call. Every walk consumer reads it synchronously in this frame.
private readonly HashSet<uint> _drawableCellsScratch = new();
private readonly HashSet<uint> _visibleCellsScratch = new();
// FW3 visual-gate fix: the interior root's dynamics phase, invoked by
// the driver's clearInteriorDepth closure at the walk's pre-clear
@ -37,6 +39,20 @@ internal sealed class RetailPViewRenderer
// cleared in finally.
private Action? _walkPreClearDynamics;
// FW6 allocation closeout: the walk's large event/view/route scratch,
// frame context, and one-cell leaf collections are renderer-lifetime
// owners. Only their frame-local bindings change. Before this cutover all
// three were constructed inside DrawInside and accounted for the measured
// ~1.5 MiB/frame dense-town tail.
private Walk.WalkFrameDriver? _walkFrameDriverScratch;
private Walk.WalkProductionFrameContext? _walkFrameContextScratch;
private WalkProductionLeafRenderer? _walkLeafRendererScratch;
private readonly Action _walkClearInteriorDepthAction;
private readonly Action _walkDrawExitSealsAction;
private RetailPViewPassExecutor? _activeWalkPasses;
private RetailPViewFrameInput? _activeWalkFrame;
private ClipFrameAssembly? _activeWalkClipAssembly;
// ACDREAM_PROBE_WALK_ROOT (FW3 visual-gate apparatus, throwaway): the
// previous frame's root kind + a post-flip frame countdown so each
// interior/outdoor transition dumps 8 frames of rooting facts.
@ -62,7 +78,8 @@ internal sealed class RetailPViewRenderer
RenderSceneShadowRuntime renderSceneShadow,
Walk.WalkBuildingRegistry walkBuildings,
Walk.WalkLandscapeAssembler walkLandscape,
CellVisibility walkCellRegistry)
CellVisibility walkCellRegistry,
ShadowObjectRegistry shadows)
{
_renderSceneShadow = renderSceneShadow
?? throw new ArgumentNullException(nameof(renderSceneShadow));
@ -72,7 +89,11 @@ internal sealed class RetailPViewRenderer
?? throw new ArgumentNullException(nameof(walkLandscape));
_walkCellRegistry = walkCellRegistry
?? throw new ArgumentNullException(nameof(walkCellRegistry));
_walkWorldData = new Walk.WalkProductionWorldData(_walkBuildings);
_walkWorldData = new Walk.WalkProductionWorldData(
_walkBuildings,
shadows ?? throw new ArgumentNullException(nameof(shadows)));
_walkClearInteriorDepthAction = ClearWalkInteriorDepth;
_walkDrawExitSealsAction = DrawWalkExitSeals;
}
// T2 (BR-4): retail has NO distance constant on the flood-admission chain
@ -129,8 +150,7 @@ internal sealed class RetailPViewRenderer
// stream appends classify records immediately (WalkStaticStreamPopulator
// runs at append time, not at Replay time), so the world data must
// already be rebuilt for this frame before the walk starts.
Walk.WalkFrameDriver? walkDriver = null;
WalkProductionLeafRenderer walkLeafRenderer;
Walk.WalkFrameDriver walkDriver;
{
Matrix4x4 view = ctx.CameraView;
var forward = Vector3.Normalize(new Vector3(-view.M13, -view.M23, -view.M33));
@ -140,14 +160,27 @@ internal sealed class RetailPViewRenderer
// reached before the world pass has published its scope.
float viewportWidth = attachment?.Width ?? 1024f;
float viewportHeight = attachment?.Height ?? 720f;
var walkContext = new Walk.WalkProductionFrameContext(
_walkCellRegistry!,
_walkBuildings!,
ctx.ViewerEyePos,
forward,
ctx.ViewProjection,
viewportWidth,
viewportHeight);
if (_walkFrameContextScratch is null)
{
_walkFrameContextScratch = new Walk.WalkProductionFrameContext(
_walkCellRegistry,
_walkBuildings,
ctx.ViewerEyePos,
forward,
ctx.ViewProjection,
viewportWidth,
viewportHeight);
}
else
{
_walkFrameContextScratch.Reset(
ctx.ViewerEyePos,
forward,
ctx.ViewProjection,
viewportWidth,
viewportHeight);
}
Walk.WalkProductionFrameContext walkContext = _walkFrameContextScratch;
_walkLandscape!.SetViewer(ctx.ViewerCellId, ctx.ViewerEyePos);
Walk.WalkLandscape walkLandscape = _walkLandscape.Landscape;
@ -185,47 +218,60 @@ internal sealed class RetailPViewRenderer
ctx.RenderCenterLbX,
ctx.RenderCenterLbY);
Action clearInteriorDepth = () =>
_activeWalkPasses = walkExecutor;
_activeWalkFrame = ctx;
_activeWalkClipAssembly = clipAssembly;
if (_walkLeafRendererScratch is null)
{
// FW3 visual-gate fix (owner report: doors/candles invisible
// looking out; the crossing vanish): retail draws the
// OUTSIDE world's objects INSIDE LScape::draw — strictly
// BEFORE the depth clear + seals (the #118 house-exit
// clip+vanish lesson: anything drawn after the seals z-fails
// against their true-depth stamp the moment it stands beyond
// the door plane). The surviving dynamic routes + outdoor
// particles + weather therefore run HERE, at the walk's
// pre-clear boundary, for an interior root.
_walkPreClearDynamics?.Invoke();
// Retail PView::DrawCells 0x005A4872 drains the landscape
// alpha list immediately before the gated full depth clear —
// mirrors DrawLandscapeThroughOutsideView's own pre-clear
// drain.
passes.FlushLandscapeAlpha();
passes.ClearInteriorDepth();
};
// FW4 slice 2: the seals stamp the WALK'S OWN flood cells (see
// DrawWalkExitPortalMasks). walkDriver is assigned below, before
// any Replay can fire this closure.
Action drawExitSeals = () =>
DrawWalkExitPortalMasks(ctx, passes, clipAssembly, walkDriver!);
_walkLeafRendererScratch = new WalkProductionLeafRenderer(
walkExecutor,
ctx,
clipAssembly,
_walkClearInteriorDepthAction,
_walkDrawExitSealsAction);
}
else
{
_walkLeafRendererScratch.Reset(
walkExecutor,
ctx,
clipAssembly,
_walkClearInteriorDepthAction,
_walkDrawExitSealsAction);
}
walkLeafRenderer = new WalkProductionLeafRenderer(
walkExecutor!, ctx, clipAssembly, clearInteriorDepth, drawExitSeals);
walkDriver = new Walk.WalkFrameDriver(
walkExecutor!.Dispatcher,
walkLeafRenderer,
_walkWorldData,
clipFrame: clipAssembly.Frame);
if (_walkFrameDriverScratch is null)
{
_walkFrameDriverScratch = new Walk.WalkFrameDriver(
walkExecutor.Dispatcher,
_walkLeafRendererScratch,
_walkWorldData,
clipFrame: clipAssembly.Frame);
}
else
{
_walkFrameDriverScratch.RebindFrame(
_walkLeafRendererScratch,
clipAssembly.Frame);
}
walkDriver = _walkFrameDriverScratch;
if (AcDream.Core.Rendering.RenderingDiagnostics.ProbeWalkRootEnabled)
{
AcDream.Core.Rendering.RenderingDiagnostics.WalkPortalProbeThisFrame =
_probeWalkRootFrame % 90 == 0;
}
walkDriver.Collect(
_frameWalk, ctx.ViewerCellId, walkCameraCell, walkLandscape, walkContext,
ctx.ViewProjection, ctx.CameraWorldPosition);
try
{
walkDriver.Collect(
_frameWalk, ctx.ViewerCellId, walkCameraCell, walkLandscape, walkContext,
ctx.ViewProjection, ctx.CameraWorldPosition);
}
catch
{
ClearWalkFrameBindings();
throw;
}
AcDream.Core.Rendering.RenderingDiagnostics.WalkPortalProbeThisFrame = false;
// FW4 slice 1: an interior root's terrain/sky/punch clip slices
@ -248,6 +294,7 @@ internal sealed class RetailPViewRenderer
// OrderedVisibleCells side-channel for every production consumer.
_drawableCellsScratch.Clear();
_drawableCellsScratch.UnionWith(walkDriver.VisitedCells);
walkDriver.CopyVisibleCellsTo(_visibleCellsScratch);
// Phase I cathedral instrumentation (synthesis §Phase I.3): the
// continuous rooting line SEPARATES the true root flood
@ -326,7 +373,7 @@ internal sealed class RetailPViewRenderer
RetailPViewFrameResult result = _frameResultScratch.Reset(
clipAssembly,
drawableCells,
prepareCells,
_visibleCellsScratch,
counts,
sourceCounts,
diagnosticPartition: null);
@ -360,6 +407,7 @@ internal sealed class RetailPViewRenderer
finally
{
_walkPreClearDynamics = null;
ClearWalkFrameBindings();
}
// OUTDOOR root: the LScape-boundary alpha drain deferred from the
@ -386,6 +434,46 @@ internal sealed class RetailPViewRenderer
private readonly Walk.RetailFrameWalk _frameWalk = new();
private void ClearWalkInteriorDepth()
{
RetailPViewPassExecutor passes = _activeWalkPasses
?? throw new InvalidOperationException(
"The retained walk leaf has no active pass binding.");
// FW3 visual-gate fix (owner report: doors/candles invisible looking
// out; the crossing vanish): retail draws outside objects inside
// LScape::draw, before the clear+seals.
_walkPreClearDynamics?.Invoke();
passes.FlushLandscapeAlpha();
passes.ClearInteriorDepth();
}
private void DrawWalkExitSeals()
{
RetailPViewFrameInput frame = _activeWalkFrame
?? throw new InvalidOperationException(
"The retained walk leaf has no active frame binding.");
RetailPViewPassExecutor passes = _activeWalkPasses
?? throw new InvalidOperationException(
"The retained walk leaf has no active pass binding.");
ClipFrameAssembly clipAssembly = _activeWalkClipAssembly
?? throw new InvalidOperationException(
"The retained walk leaf has no active clip binding.");
Walk.WalkFrameDriver driver = _walkFrameDriverScratch
?? throw new InvalidOperationException(
"The retained walk leaf has no active driver binding.");
DrawWalkExitPortalMasks(frame, passes, clipAssembly, driver);
}
private void ClearWalkFrameBindings()
{
_walkFrameDriverScratch?.AbortFrame();
_activeWalkPasses = null;
_activeWalkFrame = null;
_activeWalkClipAssembly = null;
}
/// <summary>Campaign FW3.2b-2 — THE PRODUCTION ROOTING; Campaign FW3.4a —
/// REPLAY ONLY. <paramref name="driver"/> already ran its Collect pass
/// earlier in <see cref="DrawInside"/> (before <c>PrepareCellBatches</c>);