// Tests for WbDrawDispatcher's Phase U.4 per-instance clip-slot resolution // (ResolveEntitySlot / ResolveSlotForFrame). Code review of the U.4 commit // (7993e06) flagged this gate-critical routing as untested: if it breaks, // every indoor instance is sent to the wrong clip slot (or wrongly culled), // producing total visual garbage at the portal-visibility gate. The logic is // a pure function of (ServerGuid, ParentCellId, the clip-routing state), so we // extract it to internal static helpers and test the branches directly — no GL // context required. // // Branch map (ResolveSlotForFrame, the call-site policy): // routing inactive (outdoor root) → slot 0, NOT culled (≡ U.3) // ServerGuid != 0 (live dynamic) → slot 0, NOT culled (unclipped) // ParentCellId in cellIdToSlot → that cell's slot // ParentCellId NOT in cellIdToSlot → CULL // ParentCellId == null, outdoorVisible → outdoorSlot // ParentCellId == null, !outdoorVisible → CULL using System.Collections.Generic; using AcDream.App.Rendering.Wb; using Xunit; namespace AcDream.App.Tests.Rendering.Wb; public sealed class WbDrawDispatcherClipSlotTests { // Full cell-id space keys (lbMask | OtherCellId). 0xA9B4 is the Holtburg // landblock prefix used throughout the indoor-walking work; the low word is // the EnvCell index. ParentCellId on a cell static is the SAME full id — see // the L.2e bare-low-byte finding (a 0x29 low-byte key would cull everything). private const uint VisibleCellA = 0xA9B4_0164u; private const uint VisibleCellB = 0xA9B4_0165u; private const uint NotVisibleCell = 0xA9B4_0999u; private const int SlotA = 3; private const int SlotB = 7; private const int OutsideViewSlot = 11; private static IReadOnlyDictionary Routing() => new Dictionary { [VisibleCellA] = SlotA, [VisibleCellB] = SlotB, }; // ── Raw resolver (ResolveEntitySlot): only reached when routing is active ── [Fact] public void RawResolve_LiveEntity_IsUnclippedSlot0_WhenParentCellNull() { // ServerGuid != 0 ⇒ unclipped (slot 0) regardless of cell state. int slot = WbDrawDispatcher.ResolveEntitySlot( serverGuid: 0x5000_000Au, parentCellId: null, cellIdToSlot: Routing(), outdoorSlot: OutsideViewSlot, outdoorVisible: true); Assert.Equal(0, slot); } [Fact] public void RawResolve_LiveEntity_IsUnclippedSlot0_EvenWhenParentCellVisible() { // A live entity whose ParentCellId IS a visible cell still goes to slot 0, // NOT SlotA — the live-dynamic check must precede the cell lookup. int slot = WbDrawDispatcher.ResolveEntitySlot( serverGuid: 0x5000_000Au, parentCellId: VisibleCellA, cellIdToSlot: Routing(), outdoorSlot: OutsideViewSlot, outdoorVisible: true); Assert.Equal(0, slot); Assert.NotEqual(SlotA, slot); // guards against ordering regression } [Fact] public void RawResolve_CellStatic_InVisibleSet_GetsThatCellSlot() { int slot = WbDrawDispatcher.ResolveEntitySlot( serverGuid: 0u, parentCellId: VisibleCellB, cellIdToSlot: Routing(), outdoorSlot: OutsideViewSlot, outdoorVisible: true); Assert.Equal(SlotB, slot); } [Fact] public void RawResolve_CellStatic_NotInVisibleSet_IsCulled() { int slot = WbDrawDispatcher.ResolveEntitySlot( serverGuid: 0u, parentCellId: NotVisibleCell, cellIdToSlot: Routing(), outdoorSlot: OutsideViewSlot, outdoorVisible: true); Assert.Equal(WbDrawDispatcher.ClipSlotCull, slot); } [Fact] public void RawResolve_OutdoorStab_OutdoorsVisible_GetsOutsideViewSlot() { int slot = WbDrawDispatcher.ResolveEntitySlot( serverGuid: 0u, parentCellId: null, cellIdToSlot: Routing(), outdoorSlot: OutsideViewSlot, outdoorVisible: true); Assert.Equal(OutsideViewSlot, slot); } [Fact] public void RawResolve_OutdoorStab_OutdoorsNotVisible_IsCulled() { int slot = WbDrawDispatcher.ResolveEntitySlot( serverGuid: 0u, parentCellId: null, cellIdToSlot: Routing(), outdoorSlot: OutsideViewSlot, outdoorVisible: false); Assert.Equal(WbDrawDispatcher.ClipSlotCull, slot); } // ── Call-site policy (ResolveSlotForFrame): adds the clipRoutingActive gate ── // Cases mirror the raw resolver but return the (slot, culled) pair the loop // body consumes, and add the routing-inactive (outdoor-root) branch. [Fact] public void ForFrame_RoutingInactive_EveryEntityIsSlot0AndNotCulled() { // The bit-identical-to-U.3 property: when the camera is at an outdoor root // (ClearClipRouting), ResolveEntitySlot is never consulted — every entity // maps to slot 0 and nothing is clip-culled. Exercised here for BOTH a // live entity and a cell static that would otherwise cull, with a null // routing map to prove the resolver is bypassed entirely. var live = WbDrawDispatcher.ResolveSlotForFrame( clipRoutingActive: false, serverGuid: 0x5000_000Au, parentCellId: null, cellIdToSlot: null, outdoorSlot: OutsideViewSlot, outdoorVisible: true); Assert.Equal(0u, live.Slot); Assert.False(live.Culled); var wouldCull = WbDrawDispatcher.ResolveSlotForFrame( clipRoutingActive: false, serverGuid: 0u, parentCellId: NotVisibleCell, cellIdToSlot: null, outdoorSlot: OutsideViewSlot, outdoorVisible: false); Assert.Equal(0u, wouldCull.Slot); Assert.False(wouldCull.Culled); } [Fact] public void ForFrame_RoutingActive_LiveEntity_Slot0NotCulled() { var r = WbDrawDispatcher.ResolveSlotForFrame( clipRoutingActive: true, serverGuid: 0x5000_000Au, parentCellId: VisibleCellA, cellIdToSlot: Routing(), outdoorSlot: OutsideViewSlot, outdoorVisible: true); Assert.Equal(0u, r.Slot); Assert.False(r.Culled); } [Fact] public void ForFrame_RoutingActive_CellStaticVisible_GetsCellSlotNotCulled() { var r = WbDrawDispatcher.ResolveSlotForFrame( clipRoutingActive: true, serverGuid: 0u, parentCellId: VisibleCellA, cellIdToSlot: Routing(), outdoorSlot: OutsideViewSlot, outdoorVisible: true); Assert.Equal((uint)SlotA, r.Slot); Assert.False(r.Culled); } [Fact] public void ForFrame_RoutingActive_CellStaticNotVisible_Culled() { var r = WbDrawDispatcher.ResolveSlotForFrame( clipRoutingActive: true, serverGuid: 0u, parentCellId: NotVisibleCell, cellIdToSlot: Routing(), outdoorSlot: OutsideViewSlot, outdoorVisible: true); Assert.True(r.Culled); // When culled the loop body forces slot 0 (the value is never emitted). Assert.Equal(0u, r.Slot); } [Fact] public void ForFrame_RoutingActive_OutdoorStabVisible_GetsOutsideViewSlot() { var r = WbDrawDispatcher.ResolveSlotForFrame( clipRoutingActive: true, serverGuid: 0u, parentCellId: null, cellIdToSlot: Routing(), outdoorSlot: OutsideViewSlot, outdoorVisible: true); Assert.Equal((uint)OutsideViewSlot, r.Slot); Assert.False(r.Culled); } [Fact] public void ForFrame_RoutingActive_OutdoorStabNotVisible_Culled() { var r = WbDrawDispatcher.ResolveSlotForFrame( clipRoutingActive: true, serverGuid: 0u, parentCellId: null, cellIdToSlot: Routing(), outdoorSlot: OutsideViewSlot, outdoorVisible: false); Assert.True(r.Culled); Assert.Equal(0u, r.Slot); } }