From 8067d3b04a55ffcfdc40718a13cb90ad6ff767d1 Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 23 Jun 2026 18:10:28 +0200 Subject: [PATCH] perf(pview): batch EnvCell look-in shell opaque pass (interior-root parity) DrawBuildingLookIns had the same per-cell heavy Render pattern. Lift the opaque shell Render out into one per-building batch (after that building's aperture punches); keep the per-cell loop for transparent (skip-empty) + per-cell statics /dynamics/particles. User-verified: no missing walls, look-in interiors correct. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Rendering/RetailPViewRenderer.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/AcDream.App/Rendering/RetailPViewRenderer.cs b/src/AcDream.App/Rendering/RetailPViewRenderer.cs index 3a470c5a..2b93c862 100644 --- a/src/AcDream.App/Rendering/RetailPViewRenderer.cs +++ b/src/AcDream.App/Rendering/RetailPViewRenderer.cs @@ -350,13 +350,26 @@ public sealed class RetailPViewRenderer // Pass 2: shells + statics, far→near. UseIndoorMembershipOnlyRouting(); + + // Opaque shells batched per building into ONE Render (this building's + // aperture punches above already ran; z-buffer handles order and + // lighting is per-instance CellId-keyed) — was one heavy per-frame + // Render per cell. Per-cell entity/particle work stays in the loop. + _shellBatch.Clear(); + foreach (uint cid in frame.OrderedVisibleCells) + _shellBatch.Add(cid); + if (_shellBatch.Count > 0) + _envCells.Render(WbRenderPass.Opaque, _shellBatch); + for (int i = frame.OrderedVisibleCells.Count - 1; i >= 0; i--) { uint cellId = frame.OrderedVisibleCells[i]; _oneCell.Clear(); _oneCell.Add(cellId); - _envCells.Render(WbRenderPass.Opaque, _oneCell); - _envCells.Render(WbRenderPass.Transparent, _oneCell); + // Opaque shell batched above. Transparent stays per-cell (far→near) + // for correct compositing; skipped for opaque-only cells. + if (_envCells.CellHasTransparent(cellId)) + _envCells.Render(WbRenderPass.Transparent, _oneCell); _cellStaticScratch.Clear(); if (partition.ByCell.TryGetValue(cellId, out var bucket))