perf(pview): batch EnvCell shell opaque pass + skip empty transparent
Dense-town FPS root cause: DrawEnvCellShells called the heavy per-frame EnvCellRenderer.Render once PER cell x opaque+transparent (~94 calls/frame, 24.75ms = 75% of GPU at Arwic). Batch opaque into one Render(Opaque, allCells) (z-buffer order; per-instance CellId-keyed lighting => safe) + skip the transparent Render for opaque-only cells, keep far->near for the rest. Measured (Arwic, same facing, profiler on): cells 94 calls/24.75ms -> 1 call/ 0.37ms; frame 34ms/29fps -> ~10ms/100fps p50. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f72f7ce1f4
commit
3af7d0048d
1 changed files with 23 additions and 1 deletions
|
|
@ -23,6 +23,11 @@ public sealed class RetailPViewRenderer
|
||||||
new(0, new Vector4(-1f, -1f, 1f, 1f), Array.Empty<Vector4>());
|
new(0, new Vector4(-1f, -1f, 1f, 1f), Array.Empty<Vector4>());
|
||||||
|
|
||||||
private readonly HashSet<uint> _oneCell = new(1);
|
private readonly HashSet<uint> _oneCell = new(1);
|
||||||
|
// Shell-batch scratch: all of a pass's cells collected for ONE batched
|
||||||
|
// opaque Render call (instead of one heavy Render per cell). Reused across
|
||||||
|
// frames + across look-in buildings. Spec:
|
||||||
|
// docs/superpowers/specs/2026-06-23-envcell-shell-batching-design.md
|
||||||
|
private readonly HashSet<uint> _shellBatch = new();
|
||||||
|
|
||||||
// R-A2: per-building flood grouping, reused across frames (inner lists cleared each frame).
|
// R-A2: per-building flood grouping, reused across frames (inner lists cleared each frame).
|
||||||
private readonly Dictionary<uint, List<LoadedCell>> _buildingGroups = new();
|
private readonly Dictionary<uint, List<LoadedCell>> _buildingGroups = new();
|
||||||
|
|
@ -656,11 +661,28 @@ public sealed class RetailPViewRenderer
|
||||||
// Per-cell opaque+transparent keeps the far→near transparent
|
// Per-cell opaque+transparent keeps the far→near transparent
|
||||||
// compositing the per-cell loop already provided.
|
// compositing the per-cell loop already provided.
|
||||||
UseIndoorMembershipOnlyRouting();
|
UseIndoorMembershipOnlyRouting();
|
||||||
|
|
||||||
|
// Opaque: ONE batched Render for all shell cells (was one heavy per-frame
|
||||||
|
// Render call PER cell — the dense-town FPS sink, ~94 calls/24.75ms at
|
||||||
|
// Arwic). Opaque needs no draw order (z-buffer), and lighting is
|
||||||
|
// per-instance (CellId-keyed light SSBO in EnvCellRenderer.RenderModernMDI-
|
||||||
|
// Internal), so cross-cell batching is visually identical. The filtered
|
||||||
|
// Render path already groups all cells' instances into one MDI.
|
||||||
|
_shellBatch.Clear();
|
||||||
|
foreach (var entry in IndoorDrawPlan.ShellPass(pvFrame))
|
||||||
|
_shellBatch.Add(entry.CellId);
|
||||||
|
if (_shellBatch.Count > 0)
|
||||||
|
_envCells.Render(WbRenderPass.Opaque, _shellBatch);
|
||||||
|
|
||||||
|
// Transparent: far→near order matters for compositing, so keep these
|
||||||
|
// per-cell in ShellPass order — but skip cells with no transparent
|
||||||
|
// geometry (most are opaque-only walls/floors), removing the bulk of the
|
||||||
|
// per-cell transparent Render calls.
|
||||||
foreach (var entry in IndoorDrawPlan.ShellPass(pvFrame))
|
foreach (var entry in IndoorDrawPlan.ShellPass(pvFrame))
|
||||||
{
|
{
|
||||||
|
if (!_envCells.CellHasTransparent(entry.CellId)) continue;
|
||||||
_oneCell.Clear();
|
_oneCell.Clear();
|
||||||
_oneCell.Add(entry.CellId);
|
_oneCell.Add(entry.CellId);
|
||||||
_envCells.Render(WbRenderPass.Opaque, _oneCell);
|
|
||||||
_envCells.Render(WbRenderPass.Transparent, _oneCell);
|
_envCells.Render(WbRenderPass.Transparent, _oneCell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue