using System.Collections.Generic; namespace AcDream.App.Rendering.Wb; /// /// Phase A8 (2026-05-28): EnvCell-scoped visibility snapshot. Direct port of /// WB's VisibilitySnapshot at /// references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/VisibilitySnapshot.cs:1-36, /// narrowed to the fields actually consumes /// (BatchedByCell + VisibleLandblocks + PostPreparePoolIndex). /// The scenery-side VisibleGroups / VisibleGfxObjIds / /// IntersectingLandblocks are dropped — we render scenery through /// , not through this snapshot. /// /// Used as an immutable snapshot atomically swapped under the /// renderer's render lock so PrepareRenderBatches (worker-driven) and /// Render (render-thread-driven) can't race on a half-populated dict. /// public sealed class EnvCellVisibilitySnapshot { /// Landblocks fully or partially inside the frustum at prepare time. public List VisibleLandblocks { get; init; } = new(); /// /// Grouped instance data by full 32-bit cell id. /// Outer key: CellId. Inner key: GfxObjId (ulong; bit 33 set for /// deduplicated cell geometry per ). /// Value: list of per-instance transforms (one per cell or per static object /// inside that cell). /// public Dictionary>> BatchedByCell { get; init; } = new(); /// /// Pool-index high-water mark after PrepareRenderBatches' merge phase. /// Mirrors WB VisibilitySnapshot.PostPreparePoolIndex at /// references/WorldBuilder/.../VisibilitySnapshot.cs:31. /// Read by to set the pool /// cursor to a safe region past the snapshot's owned lists, so any /// GetPooledList calls inside Render don't trample data the /// snapshot still references. Dropping this field caused the post-Wave-5 /// visual chaos — see /// docs/research/2026-05-28-a8-env-cell-renderer-audit-findings.md. /// public int PostPreparePoolIndex { get; init; } /// True when no visible cells were produced this prepare cycle. public bool IsEmpty => VisibleLandblocks.Count == 0 && BatchedByCell.Count == 0; }