refactor(render): publish the walk visible-cell set
This commit is contained in:
parent
c9fb7e7d4c
commit
877e935ac4
4 changed files with 29 additions and 46 deletions
|
|
@ -442,14 +442,18 @@ public sealed class RetailPViewRenderer
|
||||||
int terrainUploadCount = checked(1 + clipAssembly.OutsideViewSlices.Length * 2);
|
int terrainUploadCount = checked(1 + clipAssembly.OutsideViewSlices.Length * 2);
|
||||||
passes.PrepareClipFrame(terrainUploadCount);
|
passes.PrepareClipFrame(terrainUploadCount);
|
||||||
|
|
||||||
// #124: look-in cells need prepared shell batches + their statics routed
|
// Production prepares exactly the walk's visited-cell set. The
|
||||||
// into partition.ByCell (consumed ONLY by DrawBuildingLookIns — the main
|
// legacy builder's main-flood/look-in union survives only for
|
||||||
// cell-object pass iterates pvFrame.OrderedVisibleCells, which never
|
// standalone fake executors until the shell-deletion slice.
|
||||||
// contains them). drawableCells itself stays the MAIN flood: it feeds the
|
|
||||||
// seals, the outside-stage predicate, and the frame result.
|
|
||||||
var prepareCells = drawableCells;
|
var prepareCells = drawableCells;
|
||||||
_lookInCellIds.Clear();
|
_lookInCellIds.Clear();
|
||||||
if (_lookInFrames.Count > 0 || walkActive)
|
if (walkActive)
|
||||||
|
{
|
||||||
|
_lookInPrepareScratch.Clear();
|
||||||
|
_lookInPrepareScratch.UnionWith(walkDriver!.VisitedCells);
|
||||||
|
prepareCells = _lookInPrepareScratch;
|
||||||
|
}
|
||||||
|
else if (_lookInFrames.Count > 0)
|
||||||
{
|
{
|
||||||
_lookInPrepareScratch.Clear();
|
_lookInPrepareScratch.Clear();
|
||||||
_lookInPrepareScratch.UnionWith(drawableCells);
|
_lookInPrepareScratch.UnionWith(drawableCells);
|
||||||
|
|
@ -461,13 +465,6 @@ public sealed class RetailPViewRenderer
|
||||||
_lookInCellIds.Add(c);
|
_lookInCellIds.Add(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (walkActive)
|
|
||||||
{
|
|
||||||
// The walk's own flood/look-in cell set — unioned in (never
|
|
||||||
// aliased with drawableCells, which the outside-stage and seal
|
|
||||||
// predicates below still need scoped to the OLD flood only).
|
|
||||||
_lookInPrepareScratch.UnionWith(walkDriver!.VisitedCells);
|
|
||||||
}
|
|
||||||
prepareCells = _lookInPrepareScratch;
|
prepareCells = _lookInPrepareScratch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2876,20 +2873,11 @@ public sealed class RetailPViewFrameResult
|
||||||
public HashSet<uint> DrawableCells { get; private set; } = null!;
|
public HashSet<uint> DrawableCells { get; private set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Every cell this completed view actually reached: the main flood
|
/// The production retail walk's exact visited-cell set. This is the one
|
||||||
/// (<see cref="DrawableCells"/>) plus the building look-in cells. This is
|
/// visibility answer consumed by EnvCell preparation, particles, lights,
|
||||||
/// retail's per-cell <c>in_view</c> answer for effect consumers —
|
/// and directional-shadow filtering.
|
||||||
/// <c>CPhysicsObj::ShouldDrawParticles</c> @0x0050FE60 gates on
|
|
||||||
/// <c>cell->IsInView()</c>, and a cell entered through a building portal
|
|
||||||
/// (<c>PView::ConstructView</c> @0x005A57B0, installed by
|
|
||||||
/// <c>RenderDeviceD3D::DrawBuilding</c> @0x0059F2A0) is drawn by the same
|
|
||||||
/// <c>PView::DrawCells</c> traversal as a flooded cell, so retail marks it
|
|
||||||
/// in view identically. acdream's look-in adaptation keeps those cells out
|
|
||||||
/// of <see cref="DrawableCells"/> (seals / outside-stage predicate stay
|
|
||||||
/// main-flood scoped, #124); particle and light visibility must consume
|
|
||||||
/// THIS set or look-in rooms render with frozen emitters and dark lights.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public HashSet<uint> InViewCells { get; private set; } = null!;
|
public HashSet<uint> VisibleCells { get; private set; } = null!;
|
||||||
|
|
||||||
internal RenderFrameDiagnosticCounts DiagnosticCounts { get; private set; }
|
internal RenderFrameDiagnosticCounts DiagnosticCounts { get; private set; }
|
||||||
internal RenderProjectionCounts SourceCounts { get; private set; }
|
internal RenderProjectionCounts SourceCounts { get; private set; }
|
||||||
|
|
@ -2900,7 +2888,7 @@ public sealed class RetailPViewFrameResult
|
||||||
PortalVisibilityFrame portalFrame,
|
PortalVisibilityFrame portalFrame,
|
||||||
ClipFrameAssembly clipAssembly,
|
ClipFrameAssembly clipAssembly,
|
||||||
HashSet<uint> drawableCells,
|
HashSet<uint> drawableCells,
|
||||||
HashSet<uint> inViewCells,
|
HashSet<uint> visibleCells,
|
||||||
RenderFrameDiagnosticCounts diagnosticCounts,
|
RenderFrameDiagnosticCounts diagnosticCounts,
|
||||||
RenderProjectionCounts sourceCounts,
|
RenderProjectionCounts sourceCounts,
|
||||||
InteriorEntityPartition.Result? diagnosticPartition)
|
InteriorEntityPartition.Result? diagnosticPartition)
|
||||||
|
|
@ -2908,7 +2896,7 @@ public sealed class RetailPViewFrameResult
|
||||||
PortalFrame = portalFrame;
|
PortalFrame = portalFrame;
|
||||||
ClipAssembly = clipAssembly;
|
ClipAssembly = clipAssembly;
|
||||||
DrawableCells = drawableCells;
|
DrawableCells = drawableCells;
|
||||||
InViewCells = inViewCells;
|
VisibleCells = visibleCells;
|
||||||
DiagnosticCounts = diagnosticCounts;
|
DiagnosticCounts = diagnosticCounts;
|
||||||
SourceCounts = sourceCounts;
|
SourceCounts = sourceCounts;
|
||||||
DiagnosticPartition = diagnosticPartition;
|
DiagnosticPartition = diagnosticPartition;
|
||||||
|
|
|
||||||
|
|
@ -217,6 +217,8 @@ internal sealed class WorldSceneRenderer : IPreparedWorldSceneFramePhase
|
||||||
// this phase only enforces it.
|
// this phase only enforces it.
|
||||||
_passes.DrawFlatTerrain(in camera, roots.PlayerLandblockId);
|
_passes.DrawFlatTerrain(in camera, roots.PlayerLandblockId);
|
||||||
terrainDrawn = true;
|
terrainDrawn = true;
|
||||||
|
if (_passes.TerrainVisibleCellIds is { } flatTerrainCells)
|
||||||
|
_particleVisibility.MarkVisibleCells(flatTerrainCells);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -254,14 +256,11 @@ internal sealed class WorldSceneRenderer : IPreparedWorldSceneFramePhase
|
||||||
camera.Camera.View,
|
camera.Camera.View,
|
||||||
_diagnostics.CameraCellResolution));
|
_diagnostics.CameraCellResolution));
|
||||||
|
|
||||||
// Effect visibility consumes InViewCells (main flood ∪ look-in
|
// One visibility answer: the walk's exact visited-cell set
|
||||||
// cells), not the flood-only DrawableCells: retail's
|
// feeds effects and the frame environment (point lights and
|
||||||
// ShouldDrawParticles @0x0050FE60 asks cell->IsInView(), and a
|
// directional-shadow filtering).
|
||||||
// look-in cell drawn via DrawBuilding @0x0059F2A0 is in view
|
_particleVisibility.MarkVisibleCells(pviewResult.VisibleCells);
|
||||||
// exactly like a flooded cell. Flood-only scoping froze
|
_frames.ObserveDrawableCells(pviewResult.VisibleCells);
|
||||||
// emitters and darkened lights in visible adjacent rooms.
|
|
||||||
_particleVisibility.MarkVisibleCells(pviewResult.InViewCells);
|
|
||||||
_frames.ObserveDrawableCells(pviewResult.InViewCells);
|
|
||||||
_diagnostics.EmitPViewInput(
|
_diagnostics.EmitPViewInput(
|
||||||
pviewResult.PortalFrame,
|
pviewResult.PortalFrame,
|
||||||
camera.ViewProjection,
|
camera.ViewProjection,
|
||||||
|
|
@ -441,8 +440,6 @@ internal sealed class WorldSceneRenderer : IPreparedWorldSceneFramePhase
|
||||||
private void CompleteWorldFrame()
|
private void CompleteWorldFrame()
|
||||||
{
|
{
|
||||||
_alpha.EndFrame();
|
_alpha.EndFrame();
|
||||||
if (_passes.TerrainVisibleCellIds is { } visibleTerrainCells)
|
|
||||||
_particleVisibility.MarkVisibleCells(visibleTerrainCells);
|
|
||||||
_particleVisibility.CompleteFrame();
|
_particleVisibility.CompleteFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -271,9 +271,8 @@ public sealed class LightManager
|
||||||
/// geometrically closer than the player's own room's torches and win the cap,
|
/// geometrically closer than the player's own room's torches and win the cap,
|
||||||
/// leaving the visible room dark. Scoping candidacy to the frame's actual
|
/// leaving the visible room dark. Scoping candidacy to the frame's actual
|
||||||
/// visible cells (the render already computes this — callers pass last frame's
|
/// visible cells (the render already computes this — callers pass last frame's
|
||||||
/// <c>RetailPViewFrameResult.InViewCells</c>, the main flood PLUS building
|
/// retail-walk visited-cell set, one frame of latency, to avoid re-threading
|
||||||
/// look-in cells, one frame of latency, to avoid re-threading a mid-render
|
/// a mid-render callback) removes
|
||||||
/// callback; flood-only scoping darkened look-in rooms' lanterns) removes
|
|
||||||
/// non-visible cells from contention before the
|
/// non-visible cells from contention before the
|
||||||
/// cap ever applies. The distance-sort anchor stays the PLAYER either way — this
|
/// cap ever applies. The distance-sort anchor stays the PLAYER either way — this
|
||||||
/// parameter only narrows candidacy, it does not change the sort (the #176
|
/// parameter only narrows candidacy, it does not change the sort (the #176
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,7 @@ public sealed class WorldSceneRendererTests
|
||||||
"flat:clip",
|
"flat:clip",
|
||||||
"flat:sky",
|
"flat:sky",
|
||||||
"flat:terrain",
|
"flat:terrain",
|
||||||
|
"visibility:mark",
|
||||||
"flat:entities",
|
"flat:entities",
|
||||||
"frame:clear-cells",
|
"frame:clear-cells",
|
||||||
"passes:disable-clip",
|
"passes:disable-clip",
|
||||||
|
|
@ -188,7 +189,6 @@ public sealed class WorldSceneRendererTests
|
||||||
"diagnostics:signature:OutdoorRoot",
|
"diagnostics:signature:OutdoorRoot",
|
||||||
"diagnostics:draw",
|
"diagnostics:draw",
|
||||||
"alpha:end",
|
"alpha:end",
|
||||||
"visibility:mark",
|
|
||||||
"visibility:complete",
|
"visibility:complete",
|
||||||
"selection:complete",
|
"selection:complete",
|
||||||
],
|
],
|
||||||
|
|
@ -206,7 +206,7 @@ public sealed class WorldSceneRendererTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void PViewWorld_UsesOnePViewProductAndPublishesItsDrawableCells()
|
public void PViewWorld_PublishesTheWalkVisibleCellsOnce()
|
||||||
{
|
{
|
||||||
var root = new LoadedCell
|
var root = new LoadedCell
|
||||||
{
|
{
|
||||||
|
|
@ -233,7 +233,6 @@ public sealed class WorldSceneRendererTests
|
||||||
"diagnostics:signature:RetailPViewInside",
|
"diagnostics:signature:RetailPViewInside",
|
||||||
"diagnostics:draw",
|
"diagnostics:draw",
|
||||||
"alpha:end",
|
"alpha:end",
|
||||||
"visibility:mark",
|
|
||||||
"visibility:complete",
|
"visibility:complete",
|
||||||
"selection:complete",
|
"selection:complete",
|
||||||
],
|
],
|
||||||
|
|
@ -267,8 +266,8 @@ public sealed class WorldSceneRendererTests
|
||||||
// @0x0050FE60), and a cell entered through a building portal
|
// @0x0050FE60), and a cell entered through a building portal
|
||||||
// (DrawBuilding @0x0059F2A0 -> PView::ConstructView @0x005A57B0) is
|
// (DrawBuilding @0x0059F2A0 -> PView::ConstructView @0x005A57B0) is
|
||||||
// drawn by the same traversal as a flooded cell. The particle gate and
|
// drawn by the same traversal as a flooded cell. The particle gate and
|
||||||
// the light-candidate scope must therefore receive InViewCells (flood
|
// light-candidate scope must therefore receive the walk's one visited
|
||||||
// plus look-ins), not the flood-only DrawableCells.
|
// set, including look-ins.
|
||||||
Assert.Contains(0x01010003u, rig.Visibility.MarkedCells);
|
Assert.Contains(0x01010003u, rig.Visibility.MarkedCells);
|
||||||
Assert.NotNull(rig.Frames.ObservedCells);
|
Assert.NotNull(rig.Frames.ObservedCells);
|
||||||
Assert.Contains(0x01010003u, rig.Frames.ObservedCells!);
|
Assert.Contains(0x01010003u, rig.Frames.ObservedCells!);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue