checkpoint(render): preserve pre-overhaul investigation state
This commit is contained in:
parent
e880860291
commit
b3b7d922f1
45 changed files with 3168 additions and 619 deletions
|
|
@ -20,7 +20,6 @@ internal sealed class RetailPViewRenderer
|
|||
|
||||
private static readonly ClipViewSlice NoClipSlice =
|
||||
new(0, new Vector4(-1f, -1f, 1f, 1f), Array.Empty<Vector4>());
|
||||
private static readonly ClipViewSlice[] NoClipSlices = { NoClipSlice };
|
||||
private static readonly IReadOnlySet<uint> NoParticleOwners =
|
||||
new HashSet<uint>();
|
||||
|
||||
|
|
@ -39,6 +38,13 @@ internal sealed class RetailPViewRenderer
|
|||
// cleared in finally.
|
||||
private Action? _walkPreClearDynamics;
|
||||
|
||||
// Output-only Facility Hub staircase probe. This counter is consulted
|
||||
// only while ACDREAM_PROBE_FACILITY_STAIRS=1 and never affects the walk.
|
||||
private ulong _probeFacilityStairFrame;
|
||||
private ulong _probeCathedralShellOrderFrame;
|
||||
private string? _probeFacilityStairRootSignature;
|
||||
private string? _probeCathedralStairRootSignature;
|
||||
|
||||
// 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
|
||||
|
|
@ -72,7 +78,8 @@ internal sealed class RetailPViewRenderer
|
|||
Walk.WalkBuildingRegistry walkBuildings,
|
||||
Walk.WalkLandscapeAssembler walkLandscape,
|
||||
CellVisibility walkCellRegistry,
|
||||
ShadowObjectRegistry shadows)
|
||||
ShadowObjectRegistry shadows,
|
||||
Func<uint, uint?>? findParentLocalId = null)
|
||||
{
|
||||
_renderSceneShadow = renderSceneShadow
|
||||
?? throw new ArgumentNullException(nameof(renderSceneShadow));
|
||||
|
|
@ -84,7 +91,8 @@ internal sealed class RetailPViewRenderer
|
|||
?? throw new ArgumentNullException(nameof(walkCellRegistry));
|
||||
_walkWorldData = new Walk.WalkProductionWorldData(
|
||||
_walkBuildings,
|
||||
shadows ?? throw new ArgumentNullException(nameof(shadows)));
|
||||
shadows ?? throw new ArgumentNullException(nameof(shadows)),
|
||||
findParentLocalId);
|
||||
_walkClearInteriorDepthAction = ClearWalkInteriorDepth;
|
||||
_walkDrawExitSealsAction = DrawWalkExitSeals;
|
||||
}
|
||||
|
|
@ -282,6 +290,134 @@ internal sealed class RetailPViewRenderer
|
|||
_drawableCellsScratch.UnionWith(walkDriver.VisitedCells);
|
||||
walkDriver.CopyVisibleCellsTo(_visibleCellsScratch);
|
||||
|
||||
if (AcDream.Core.Rendering.RenderingDiagnostics
|
||||
.ProbeCathedralShellOrderEnabled
|
||||
&& ((ctx.ViewerCellId & 0xFFFF0000u) == 0xF4180000u
|
||||
|| (ctx.PlayerCellId & 0xFFFF0000u) == 0xF4180000u))
|
||||
{
|
||||
_probeCathedralShellOrderFrame++;
|
||||
walkDriver.TraceCathedralShellOrder(
|
||||
_probeCathedralShellOrderFrame,
|
||||
ctx.ViewerCellId,
|
||||
ctx.PlayerCellId,
|
||||
ctx.RootCell.CellId,
|
||||
ctx.ViewerEyePos);
|
||||
}
|
||||
|
||||
if (AcDream.Core.Rendering.RenderingDiagnostics.ProbeFacilityStairsEnabled)
|
||||
{
|
||||
static int TargetMembership(IReadOnlyCollection<uint> cells)
|
||||
{
|
||||
const uint a = 0x8A02015Eu;
|
||||
const uint b = 0x8A02015Fu;
|
||||
const uint c = 0x8A0201C1u;
|
||||
int mask = 0;
|
||||
foreach (uint cell in cells)
|
||||
{
|
||||
if (cell == a) mask |= 1;
|
||||
if (cell == b) mask |= 2;
|
||||
if (cell == c) mask |= 4;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
_probeFacilityStairFrame++;
|
||||
int floodMask = TargetMembership(walkDriver.InteriorFloodCells);
|
||||
int visitedMask = TargetMembership(walkDriver.VisitedCells);
|
||||
int turnMask = TargetMembership(walkDriver.LookInCells);
|
||||
string signature = $"{ctx.ViewerCellId:X8}:{ctx.PlayerCellId:X8}:"
|
||||
+ $"{ctx.RootCell.CellId:X8}:{floodMask}:{visitedMask}:{turnMask}:"
|
||||
+ $"{walkDriver.InteriorFloodCells.Count}:{walkDriver.VisitedCells.Count}:"
|
||||
+ $"{walkDriver.LookInCellTurns.Count}";
|
||||
bool changed = !string.Equals(
|
||||
_probeFacilityStairRootSignature,
|
||||
signature,
|
||||
StringComparison.Ordinal);
|
||||
_probeFacilityStairRootSignature = signature;
|
||||
if (changed)
|
||||
{
|
||||
static string DescribeMask(int mask) =>
|
||||
$"15e={((mask & 1) != 0 ? 1 : 0)},"
|
||||
+ $"15f={((mask & 2) != 0 ? 1 : 0)},"
|
||||
+ $"1c1={((mask & 4) != 0 ? 1 : 0)}";
|
||||
Console.WriteLine(
|
||||
$"[facility-root] f={_probeFacilityStairFrame} changed={(changed ? 1 : 0)} "
|
||||
+ $"viewer=0x{ctx.ViewerCellId:X8} player=0x{ctx.PlayerCellId:X8} "
|
||||
+ $"root=0x{ctx.RootCell.CellId:X8} res={ctx.CameraCellResolution} "
|
||||
+ $"eye=({ctx.ViewerEyePos.X:F4},{ctx.ViewerEyePos.Y:F4},{ctx.ViewerEyePos.Z:F4}) "
|
||||
+ $"playerPos=({ctx.PlayerViewPosition.X:F4},{ctx.PlayerViewPosition.Y:F4},{ctx.PlayerViewPosition.Z:F4}) "
|
||||
+ $"rootFlood={walkDriver.InteriorFloodCells.Count}"
|
||||
+ $"[{DescribeMask(floodMask)}] "
|
||||
+ $"visited={walkDriver.VisitedCells.Count}"
|
||||
+ $"[{DescribeMask(visitedMask)}] "
|
||||
+ $"turns={walkDriver.LookInCellTurns.Count}"
|
||||
+ $"[{DescribeMask(turnMask)}]");
|
||||
}
|
||||
|
||||
static int CathedralMembership(IReadOnlyCollection<uint> cells)
|
||||
{
|
||||
const uint oldBuilding = 0xF4180107u;
|
||||
const uint stairParent = 0xF4180112u;
|
||||
int mask = 0;
|
||||
foreach (uint cell in cells)
|
||||
{
|
||||
if (cell == oldBuilding) mask |= 1;
|
||||
if (cell == stairParent) mask |= 2;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
static uint BuildingAnchor(Walk.WalkBuilding building)
|
||||
{
|
||||
foreach (Walk.WalkBldPortal portal in building.Portals)
|
||||
{
|
||||
if (portal.OtherCellId != 0xFFFFFFFFu)
|
||||
return portal.OtherCellId;
|
||||
}
|
||||
return 0u;
|
||||
}
|
||||
|
||||
int cathedralFlood = CathedralMembership(walkDriver.InteriorFloodCells);
|
||||
int cathedralVisited = CathedralMembership(walkDriver.VisitedCells);
|
||||
int cathedralLookIn = CathedralMembership(walkDriver.LookInCells);
|
||||
bool bucket107 = _walkWorldData.StaticBucketContains(
|
||||
0xF4180107u,
|
||||
0x020009A2u);
|
||||
bool bucket112 = _walkWorldData.StaticBucketContains(
|
||||
0xF4180112u,
|
||||
0x020009A2u);
|
||||
string buildingAnchors = string.Join(",", walkDriver.VisitedBuildings
|
||||
.ConvertAll(building => $"0x{BuildingAnchor(building):X8}"));
|
||||
string cathedralSignature =
|
||||
$"{ctx.ViewerCellId:X8}:{ctx.PlayerCellId:X8}:{ctx.RootCell.CellId:X8}:"
|
||||
+ $"{cathedralFlood}:{cathedralVisited}:{cathedralLookIn}:"
|
||||
+ $"{(bucket107 ? 1 : 0)}:{(bucket112 ? 1 : 0)}:{buildingAnchors}";
|
||||
bool cathedralChanged = !string.Equals(
|
||||
_probeCathedralStairRootSignature,
|
||||
cathedralSignature,
|
||||
StringComparison.Ordinal);
|
||||
_probeCathedralStairRootSignature = cathedralSignature;
|
||||
if (cathedralChanged
|
||||
&& ((ctx.ViewerCellId & 0xFFFF0000u) == 0xF4180000u
|
||||
|| (ctx.PlayerCellId & 0xFFFF0000u) == 0xF4180000u))
|
||||
{
|
||||
static string DescribeCathedralMask(int mask) =>
|
||||
$"107={((mask & 1) != 0 ? 1 : 0)},"
|
||||
+ $"112={((mask & 2) != 0 ? 1 : 0)}";
|
||||
Console.WriteLine(
|
||||
$"[cathedral-stair] f={_probeFacilityStairFrame} "
|
||||
+ $"viewer=0x{ctx.ViewerCellId:X8} player=0x{ctx.PlayerCellId:X8} "
|
||||
+ $"root=0x{ctx.RootCell.CellId:X8} res={ctx.CameraCellResolution} "
|
||||
+ $"eye=({ctx.ViewerEyePos.X:F4},{ctx.ViewerEyePos.Y:F4},{ctx.ViewerEyePos.Z:F4}) "
|
||||
+ $"playerPos=({ctx.PlayerViewPosition.X:F4},{ctx.PlayerViewPosition.Y:F4},{ctx.PlayerViewPosition.Z:F4}) "
|
||||
+ $"rootFlood=[{DescribeCathedralMask(cathedralFlood)}] "
|
||||
+ $"visited=[{DescribeCathedralMask(cathedralVisited)}] "
|
||||
+ $"lookIn=[{DescribeCathedralMask(cathedralLookIn)}] "
|
||||
+ $"buckets=[107={(bucket107 ? 1 : 0)},112={(bucket112 ? 1 : 0)}] "
|
||||
+ $"buildings=[{buildingAnchors}]");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// FW4 slice 1: the ONE clip-region publication, after any walk
|
||||
|
|
@ -391,14 +527,11 @@ internal sealed class RetailPViewRenderer
|
|||
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);
|
||||
DrawWalkExitPortalMasks(frame, passes, driver);
|
||||
}
|
||||
|
||||
private void ClearWalkFrameBindings()
|
||||
|
|
@ -528,27 +661,27 @@ internal sealed class RetailPViewRenderer
|
|||
/// #456 cathedral seam band (its never-drawn panel family), leaving
|
||||
/// aperture depth unsealed after the interior clear; the end-of-frame
|
||||
/// alpha drain (cell-owned emitters — retail's own timing) then
|
||||
/// z-passes across the whole opening (the falls shine-through). Per-cell
|
||||
/// slice clips still come from the old assembly where present; a cell
|
||||
/// the old apparatus missed seals unclipped (the depth fan is the exact
|
||||
/// dat aperture polygon and z-tests, so over-coverage is benign).</summary>
|
||||
/// z-passes across the whole opening (the falls shine-through). Each
|
||||
/// portal is stamped once per exact walk-owned view captured for that
|
||||
/// flood cell, matching retail's <c>CEnvCell::setup_view</c> loop. The
|
||||
/// legacy visibility assembly has no production role here.</summary>
|
||||
private void DrawWalkExitPortalMasks(
|
||||
RetailPViewFrameInput ctx,
|
||||
RetailPViewPassExecutor passes,
|
||||
ClipFrameAssembly clipAssembly,
|
||||
Walk.WalkFrameDriver driver)
|
||||
{
|
||||
List<uint> floodCells = driver.InteriorFloodCells;
|
||||
for (int i = floodCells.Count - 1; i >= 0; i--)
|
||||
{
|
||||
uint cellId = floodCells[i];
|
||||
foreach (var slice in GetCellSlicesOrNoClip(clipAssembly, cellId))
|
||||
int sliceCount = driver.InteriorFloodViewSliceCountAt(i);
|
||||
for (int sliceIndex = 0; sliceIndex < sliceCount; sliceIndex++)
|
||||
{
|
||||
passes.DrawExitPortalMask(
|
||||
ctx,
|
||||
new RetailPViewCellSliceContext(
|
||||
cellId,
|
||||
slice,
|
||||
NoParticleOwners));
|
||||
cellId,
|
||||
driver.InteriorFloodViewClipPlanesAt(i, sliceIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -573,18 +706,6 @@ internal sealed class RetailPViewRenderer
|
|||
MeshPartCount: 0);
|
||||
}
|
||||
|
||||
private static ClipViewSlice[] GetCellSlicesOrNoClip(
|
||||
ClipFrameAssembly clipAssembly,
|
||||
uint cellId)
|
||||
{
|
||||
if (clipAssembly.CellIdToViewSlices.TryGetValue(cellId, out var slices)
|
||||
&& slices.Length > 0)
|
||||
{
|
||||
return slices;
|
||||
}
|
||||
|
||||
return NoClipSlices;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IRetailPViewCellSource
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue