checkpoint: preserve user-gated FW closeout fixes
This commit is contained in:
parent
a2f2eb7d78
commit
b8befded8b
22 changed files with 1005 additions and 198 deletions
|
|
@ -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>);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue