using System.Numerics; using AcDream.App.Rendering.Gpu; using AcDream.App.Rendering.Scene; using AcDream.App.Rendering.Sky; using AcDream.App.Rendering.Wb; using AcDream.App.Rendering.Walk; using AcDream.Core.Rendering; using AcDream.Core.Vfx; using AcDream.Core.World; namespace AcDream.App.Rendering; internal interface IEnvCellImmediateDrawSink { void DrawImmediate( uint cellId, EnvCellTransparentRoute route, bool detailSurfaceActive); } // S3 chunk 4 fix round 2 (L2): RetailPViewFramebufferSize / IRetailPViewFramebufferSource // / SilkRetailPViewFramebufferSource are deleted — their only consumer was // the RHI surface's NDC-to-pixel scissor conversion, and that scissor // mechanism itself is deleted in the same round (no producer of a narrowed // rectangle remains anywhere in the walk). internal sealed class RetailPViewCellSource : IRetailPViewCellSource { private readonly CellVisibility _cells; public RetailPViewCellSource(CellVisibility cells) => _cells = cells ?? throw new ArgumentNullException(nameof(cells)); public LoadedCell? Find(uint cellId) => _cells.TryGetCell(cellId, out LoadedCell? cell) ? cell : null; } /// /// Concrete GL implementation of the named passes ordered by /// . It owns reusable pass-local particle /// classifications but borrows every renderer and world source. /// The order it implements is retail PView::DrawCells @ 0x005A4840: /// landscape, delayed-alpha flush, optional interior depth clear, exit masks, /// cell shells/objects, then surviving dynamics. Landscape sky/terrain/weather /// placement follows LScape::draw @ 0x00506330. /// /// /// Campaign V slice V6j: backend-neutral. The order it implements is retail's and /// is written once; the four places it touches graphics state directly are owned /// by . /// internal sealed partial class RetailPViewPassExecutor : IEnvCellImmediateDrawSink { private readonly IWorldPassSurface _surface; private readonly IRenderFrameGlState _frameGlState; private readonly ClipFrame _clipFrame; private readonly TerrainModernRenderer? _terrain; private readonly EnvCellRenderer _envCells; private readonly WbDrawDispatcher _entities; private readonly SkyRenderer? _sky; private readonly ParticleSystem? _particles; private readonly ParticleRenderer? _particleRenderer; private readonly PortalDepthMaskRenderer? _portalDepthMask; private readonly RetailAlphaQueue _alpha; private readonly WorldRenderDiagnostics _diagnostics; private readonly TerrainDrawDiagnosticsController _terrainDiagnostics; private readonly HashSet _noSceneParticleEntityIds = []; private readonly EnvCellAlphaDrawSource _envCellClipAlphaSource; private readonly EnvCellAlphaDrawSource _envCellBlendAlphaSource; internal delegate void RenderImmediateEnvCellRoute( uint cellId, EnvCellTransparentRoute route, bool detailSurfaceActive); public RetailPViewPassExecutor( IWorldPassSurface surface, IRenderFrameGlState frameGlState, ClipFrame clipFrame, TerrainModernRenderer? terrain, EnvCellRenderer envCells, WbDrawDispatcher entities, SkyRenderer? sky, ParticleSystem? particles, ParticleRenderer? particleRenderer, PortalDepthMaskRenderer? portalDepthMask, RetailAlphaQueue alpha, WorldRenderDiagnostics diagnostics, TerrainDrawDiagnosticsController terrainDiagnostics) { _surface = surface ?? throw new ArgumentNullException(nameof(surface)); _frameGlState = frameGlState ?? throw new ArgumentNullException(nameof(frameGlState)); _clipFrame = clipFrame ?? throw new ArgumentNullException(nameof(clipFrame)); _terrain = terrain; _envCells = envCells ?? throw new ArgumentNullException(nameof(envCells)); _entities = entities ?? throw new ArgumentNullException(nameof(entities)); _sky = sky; _particles = particles; _particleRenderer = particleRenderer; _portalDepthMask = portalDepthMask; _alpha = alpha ?? throw new ArgumentNullException(nameof(alpha)); _diagnostics = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics)); _terrainDiagnostics = terrainDiagnostics ?? throw new ArgumentNullException(nameof(terrainDiagnostics)); _envCellClipAlphaSource = new EnvCellAlphaDrawSource( _envCells.RenderTransparentOrdered, EnvCellTransparentRoute.Clip); _envCellBlendAlphaSource = new EnvCellAlphaDrawSource( _envCells.RenderTransparentOrdered, EnvCellTransparentRoute.Alpha); } public void BeginFrame() { // Campaign OVERHAUL S2 chunk 6: no per-frame particle-owner // classification to reset any more — particle draws are cell-scoped // and read live from ParticleSystem's own retained cell index. } /// Campaign FW3.2b-2: the shared dispatcher, for /// 's WalkFrameDriver /// construction — the driver's ctor takes a /// directly (it calls SubmitOrderedStream itself; see that /// class's own doc comment). internal WbDrawDispatcher Dispatcher => _entities; /// Campaign FW3.2b-2: forwards to /// — the frame/ /// encoder pair the walk driver submits its stream flushes into. internal (IGpuFrame Frame, IGpuPassEncoder Encoder) RequireWalkSubmission() => _entities.RequireWalkSubmission(); /// Campaign FW3.2b-2: forwards to /// — the real /// viewport size for WalkProductionFrameContext. internal (int Width, int Height)? WalkAttachmentExtent => _entities.WalkAttachmentExtent; public void AbortFrame() { List? failures = null; TryAbort(_frameGlState.RestoreFrameDefaults); TryAbort(_noSceneParticleEntityIds.Clear); if (failures is { Count: > 0 }) throw new AggregateException("Retail PView pass abort failed.", failures); void TryAbort(Action operation) { try { operation(); } catch (Exception error) { (failures ??= []).Add(error); } } } public ClipFrameAssembly BeginWalkClipFrame( bool outdoorRoot, ClipFrameAssembly reuseAssembly) => ClipFrameAssembler.BeginWalkFrame(_clipFrame, outdoorRoot, reuseAssembly); public void PrepareClipFrame() => _surface.PrepareClipFrame(); public void PrepareCellBatches( RetailPViewFrameInput frame, HashSet visibleCellIds) => _envCells.PrepareRenderBatches( frame.ViewProjection, frame.CameraWorldPosition, filter: visibleCellIds, centerLbX: frame.RenderCenterLbX, centerLbY: frame.RenderCenterLbY, renderRadius: frame.RenderRadius); public void DrawOpaqueCellShells(HashSet cellIds) => _envCells.Render(WbRenderPass.Opaque, cellIds); public bool CellHasTransparentShell(uint cellId) => _envCells.CellHasTransparent(cellId); public void DrawTransparentCellShellsOrdered(IReadOnlyList cellIds) => _envCells.RenderTransparentOrdered(cellIds); /// /// S4-c2 fix round 1 (M6, contract C4): retail DrawEnvCell /// @0x0059f1c2 installs the environment detail surface (may be null) /// before DrawMesh @0x0059f212 and clears it @0x0059f21a — detail /// ON makes eligible subsets take Row 1 (immediate, WITH detail — /// 's own doc /// comment); detail OFF still routes each real subset by the exact /// TextureBatchData.RetailSurfaceMask retained on /// ObjectRenderBatch. A pure Base1ClipMap mask 0x08 reaches CLIP; /// alpha-family 0x02 reaches ALPHA; a table-Immediate mask stays at the /// cell turn. The queue coalesces only to one token per (cell,list), the /// accepted AP-238 granularity residual. /// internal void SubmitOrDrawTransparentCellShell(uint cellId) { bool detailSurfaceActive = _envCells.TransparentDetailEnabled; EnvCellTransparentRoute routes = _envCells.GetTransparentRoutes( cellId, detailSurfaceActive); DispatchTransparentCellShell( cellId, routes, detailSurfaceActive, _alpha, _envCellClipAlphaSource, _envCellBlendAlphaSource, this); } private readonly List _singleCellListScratch = new(1); private void DrawImmediateEnvCellRoute( uint cellId, EnvCellTransparentRoute route, bool detailSurfaceActive) { _singleCellListScratch.Clear(); _singleCellListScratch.Add(cellId); _envCells.RenderTransparentOrdered( _singleCellListScratch, route, detailSurfaceActive); } void IEnvCellImmediateDrawSink.DrawImmediate( uint cellId, EnvCellTransparentRoute route, bool detailSurfaceActive) => DrawImmediateEnvCellRoute(cellId, route, detailSurfaceActive); /// /// Production dispatch for one cell after /// scanned its real transparent batches. Separate source identities keep /// CLIP and ALPHA replay filters attached to their tokens. Immediate /// subsets draw at the cell turn; each deferred list receives at most one /// token for this cell. /// internal static void DispatchTransparentCellShell( uint cellId, EnvCellTransparentRoute routes, bool detailSurfaceActive, RetailAlphaQueue queue, EnvCellAlphaDrawSource clipSource, EnvCellAlphaDrawSource alphaSource, IEnvCellImmediateDrawSink renderImmediate) { if ((routes & EnvCellTransparentRoute.Immediate) != 0) { renderImmediate.DrawImmediate( cellId, EnvCellTransparentRoute.Immediate, detailSurfaceActive); } if ((routes & EnvCellTransparentRoute.Clip) != 0) { int token = clipSource.AddPendingCellId(cellId); queue.TryAppend(RetailAlphaList.Clip, clipSource, token, overrideClipmap: true); } if ((routes & EnvCellTransparentRoute.Alpha) != 0) { int token = alphaSource.AddPendingCellId(cellId); queue.TryAppend(RetailAlphaList.Alpha, alphaSource, token, overrideClipmap: false); } } /// Delegate adapter retained for focused route/allocation tests; /// production calls the interface overload above with this, so no /// per-cell delegate conversion or retained callback enters the owner. internal static void DispatchTransparentCellShell( uint cellId, EnvCellTransparentRoute routes, bool detailSurfaceActive, RetailAlphaQueue queue, EnvCellAlphaDrawSource clipSource, EnvCellAlphaDrawSource alphaSource, RenderImmediateEnvCellRoute renderImmediate) { if ((routes & EnvCellTransparentRoute.Immediate) != 0) { renderImmediate( cellId, EnvCellTransparentRoute.Immediate, detailSurfaceActive); } if ((routes & EnvCellTransparentRoute.Clip) != 0) { int token = clipSource.AddPendingCellId(cellId); queue.TryAppend(RetailAlphaList.Clip, clipSource, token, overrideClipmap: true); } if ((routes & EnvCellTransparentRoute.Alpha) != 0) { int token = alphaSource.AddPendingCellId(cellId); queue.TryAppend(RetailAlphaList.Alpha, alphaSource, token, overrideClipmap: false); } } /// /// The queue-facing half of : /// retains the cell ids submitted to one fixed list in append order and replays /// a drained slice through /// (production wiring) — one token per cell, never per subset (M6's /// documented granularity residual). 's /// "only adjacent same-source entries batch" invariant means a particle /// or ordinary GfxObj instance appended BETWEEN two cell tokens keeps /// its own position in the combined drain — this source never /// re-orders across another entry. Depends on a delegate rather than /// the concrete type (marked /// , not ) so /// EnvCellAlphaDrawSourceTests can pin the queue mechanics /// without standing up a GPU-backed renderer. /// internal delegate void RenderEnvCellsByRoute( IReadOnlyList cellIds, EnvCellTransparentRoute route, bool detailSurfaceActive); internal sealed class EnvCellAlphaDrawSource( RenderEnvCellsByRoute renderTransparentOrdered, EnvCellTransparentRoute route) : IRetailAlphaDrawSource { private readonly List _pendingCellIds = new(); private readonly List _preparedCellIds = new(); private readonly List _drawScratch = new(); /// Returns this cell's token — its index into /// at the moment it was added. internal int AddPendingCellId(uint cellId) { int token = _pendingCellIds.Count; _pendingCellIds.Add(cellId); return token; } public void PrepareAlphaDraws(ReadOnlySpan tokens) { _preparedCellIds.Clear(); for (int i = 0; i < tokens.Length; i++) _preparedCellIds.Add(_pendingCellIds[tokens[i]]); } public void DrawPreparedAlphaBatch(int firstPreparedDraw, int drawCount) { if (drawCount <= 0) return; _drawScratch.Clear(); for (int i = 0; i < drawCount; i++) _drawScratch.Add(_preparedCellIds[firstPreparedDraw + i]); renderTransparentOrdered(_drawScratch, route, detailSurfaceActive: false); } public void ResetAlphaSubmissions() { _pendingCellIds.Clear(); _preparedCellIds.Clear(); _drawScratch.Clear(); } internal int PendingCount => _pendingCellIds.Count; } /// /// S3 chunk 1 fix round 2 (§11.6 H1): retail draws the weather pass /// EXACTLY ONCE per frame — GameSky::Draw @0x00506ff0 with /// arg2==1 runs AFTER LScape::draw's landblock loop /// finishes (@0x00506396), not once per active landscape view. This call /// is UNCLIPPED and sets no scissor — S3 chunk 4 (§10.2) deleted the /// former per-outside-view-slice loop (the walk's own screen-space /// terrain-clip writer, its per-frame clip-routing reset call, and the /// old DrawLandscapeSliceLate leaf, one call per active landscape /// view) that used to run before this /// call and re-submit the rain mesh once per doorway aperture; this is /// now the ONLY weather call site, matching retail's ONE unclipped /// GameSky::Draw(sky,1). /// /// S3 chunk 4 (§10.2): the rain PARTICLE emitters /// (ParticleRenderPass.SkyPostScene) moved here too, as ONE /// unclipped submission — retail inserts emitters into one unclipped /// alpha list (no per-doorway clipSlot), the same rule as the /// mesh draw above. /// /// /// Gated exactly as before — /// , retail's /// SmartBox::is_player_outside @0x00451e80 check ANDed with the /// two render toggles — via _sky?.RenderWeather(...) (null- /// conditional, matching every other _sky?.RenderSky(...) call /// site in this codebase): a missing sky asset silently skips the GL /// draw. /// /// /// S3 chunk 4 (O3): the transcript "OC" print that used to live here /// moved to WalkFrameDriver.OnWeatherTurn, fired by /// RetailFrameWalk.DrawLandscape at Collect time (retail's real /// turn — between the landscape and the flood for an interior root, at /// the end of the walk for an outdoor root) instead of wherever the GPU /// draw happens to run at Replay. /// /// /// S3 chunk 4 fix round 1 (K2): the caller () now gates /// this call on WalkFrameDriver.WeatherTurnFired — the walk's own /// record of "a Landscape turn ran with the weather gate open" — rather /// than re-deriving the gate independently, so this method's own /// check is a /// provably redundant safety net (the caller reads the same /// 's toggles the flag was computed from). /// /// /// S3 chunk 4 fix round 1 (K8): the weather MESH draws before the rain /// PARTICLE emitters below purely as this method's own call order — /// retail's single GameSky::Draw(sky,1) call imposes no ordering /// between acdream's two substitutes (the mesh and the particle /// system), so this order is a bookkeeping choice, not a retail fact. /// /// public void DrawWeatherOnce(RetailPViewFrameInput frame) { if (!ShouldDrawWeatherOnce(frame.RenderSky, frame.RenderWeather, frame.PlayerCellId)) return; _sky?.RenderWeather( frame.Camera, frame.CameraWorldPosition, frame.DayFraction, frame.ActiveDayGroup, frame.SkyKeyframe, frame.EnvironOverrideActive); if (_particles is not null && _particleRenderer is not null) { _particleRenderer.Draw( frame.Camera, frame.CameraWorldPosition, ParticleRenderPass.SkyPostScene); } } /// /// S3 chunk 1 fix round 2 (§11.6 H1): the weather pass's gate, extracted /// as a pure predicate — retail's SmartBox::is_player_outside /// @0x00451e80 check, (player objcell_id & 0xFFFF) < 0x100, /// ANDed with the two render toggles. Internal (not private) so a test /// can pin "no weather while the player stands indoors" and "no weather /// with either toggle off" without a live GL/DAT /// — itself still needs one to actually /// draw, but this predicate alone decides whether it would even try (and /// therefore whether the OC print fires). /// internal static bool ShouldDrawWeatherOnce( bool renderSky, bool renderWeather, uint playerCellId) => renderSky && renderWeather && (playerCellId & 0xFFFFu) < 0x100u; public void DrawLandscapeStaticParticles( RetailPViewFrameInput frame, uint cellId) { // One unclipped submission per cell per frame. Retail never clips a // particle to a portal view — its polys join the one alpha list during // the owner cell's walk turn and the depth test at the flush decides // occlusion (FlushAlphaList @0x0059D2E0). The former per-slice call // with the slice's clip slot both hardware-cut effects at aperture // boundaries and double-submitted owners visible in two slices. // Retail CPhysicsObj::add_particle_shadow_to_cell (0x00514a70): an // emitter owns one shadow in its OWN current cell, so this is a cell // lookup, not an owner union — a hidden/suspended owner's emitter // still draws here. if (_particles is not null && _particleRenderer is not null) { _particleRenderer.DrawForCell( frame.Camera, frame.CameraWorldPosition, ParticleRenderPass.Scene, cellId, clipSlot: 0); } } public void ClearInteriorDepth() { _surface.ClearInteriorDepth(); } /// Returns the number of exit-seal fans ATTEMPTED this turn — /// S4-c1 fix round 1 F2 corrected this from "matches what reached the /// GPU": retail increments portalsDrawnCount BEFORE /// polyClipFinish runs (0x59BD70-0x59BD74 precedes 0x59BDB0), so /// the counter records guard-passing ATTEMPTS, not successful GPU fans. /// A portal whose polygon has fewer than 3 vertices is counted here even /// though the GPU renderer's own internal <3 guard /// (DrawDepthFan) draws nothing for it /// (RetailPViewPassExecutorTests.DrawExitPortalMask_CountsAnUnclippableTwoVertexPolygon_ButDrawsNothing /// pins exactly this). public int DrawExitPortalMask( RetailPViewFrameInput frame, uint cellId, ReadOnlySpan clipPlanes) => DrawPortalDepthWrite( cellId, clipPlanes, frame, forceFarZ: frame.RootCell.IsOutdoorNode); public void DrawUnattachedSceneParticles( RetailPViewFrameInput frame, bool outdoorCells) { if (_particles is null || _particleRenderer is null) return; // Retail draws an unattached emitter once, during its owner CELL's // walk turn, with NO portal-view clip (CPhysicsObj::ShouldDrawParticles // @0x0050FE60 gates by cell in-view + distance; occlusion is the depth // test at FlushAlphaList @0x0059D2E0). Outdoor-cell emitters submit in // the landscape stage, interior-cell emitters in the final world stage. // The former once-per-OutsideView-slice submission with that slice's // hardware clip slot made effects vanish by view direction (zero // outside slices in view = zero submissions) — invented behavior. _particleRenderer.DrawForOwners( frame.Camera, frame.CameraWorldPosition, ParticleRenderPass.Scene, _noSceneParticleEntityIds, includeUnattached: true, clipSlot: 0, unattachedCellScope: outdoorCells ? UnattachedEmitterCellScope.OutdoorCells : UnattachedEmitterCellScope.InteriorCells); } /// PView::DrawCells @0x005a4840's own /// FlushAlphaList(0f) @0x005a4872 (OH1 contract §7 site 3). public void FlushLandscapeAlpha() => _alpha.Flush(RetailAlphaFlushSite.LandscapeFlush, 0f); /// RenderDeviceD3D::DrawBuilding @0x0059f2a0's own /// FlushAlphaList(0f) @0x0059f30b (OH1 contract §7 site 1) — the /// building alpha barrier. Distinct call from /// even though both drain under the same 0f threshold (S4-c2: the two /// sites were sharing one undifferentiated call before the two-list /// FIFO cutover, when no site label existed to distinguish them). internal void FlushBuildingAlpha() => _alpha.Flush(RetailAlphaFlushSite.DrawBuilding, 0f); /// RenderDeviceD3D::DrawBlock @0x005a17c0's (per-land-cell /// loop head @0x005a18d0) own FlushAlphaList(::flush) @0x005a1a07, /// the immutable global 0.75f pressure valve (OH1 contract §7 site /// 2). internal void FlushSortCellExitAlpha() => _alpha.Flush(RetailAlphaFlushSite.SortCellExit, 0.75f); public void DrawCellParticles( RetailPViewFrameInput frame, uint cellId) { if (_particles is null || _particleRenderer is null) return; // Retail never clips cell particles to a portal view: the owner // cell's walls own occlusion via the depth test at the alpha flush. // CPhysicsObj::add_particle_shadow_to_cell (0x00514a70) draws an // emitter in its OWN current cell, so this is a cell lookup, not an // owner union — a hidden/suspended owner's emitter still draws here. _particleRenderer.DrawForCell( frame.Camera, frame.CameraWorldPosition, ParticleRenderPass.Scene, cellId, clipSlot: 0); } public void EmitDiagnostics( RetailPViewFrameInput frame, RetailPViewFrameResult result) => _diagnostics.EmitRetailPViewDiagnostics( RenderingDiagnostics.ProbeVisibilityEnabled, RenderingDiagnostics.ProbeFlapEnabled, result, frame.RootCell, frame.ViewerCellId, frame.PlayerCellId, frame.CameraWorldPosition, frame.PlayerViewPosition, frame.CameraCellResolution); /// Retail D3DPolyRender::DrawPortalPolyInternal /// @0x0059BC90. Main interior roots stamp true depth (seal); outdoor and /// look-in apertures stamp far depth (punch). The renderer owns that /// choice. Returns the number of portals ATTEMPTED (not necessarily /// drawn) this turn — S4-c1 fix round 1 F2 retired the ">=3 vertices" /// pre-filter that used to gate the count: every portal with /// OtherCellId == 0xFFFF that survives the ±12 boundary guard /// () /// is counted here, BEFORE the vertex-count check that decides whether /// its fan actually draws — matching retail's own count-BEFORE-clip /// order (0x59BD70-0x59BD74 precedes 0x59BDB0). This is the same /// enumeration WalkFrameDriver.OnInteriorFloodDrawTurn counts /// through DrawExitSeals's return value. private int DrawPortalDepthWrite( uint cellId, ReadOnlySpan clipPlanes, RetailPViewFrameInput frame, bool forceFarZ, int? onlyPortalIndex = null) { if (_portalDepthMask is null) return 0; LoadedCell? cell = frame.Cells.Find(cellId); if (cell is null) return 0; int submitted = 0; Span world = stackalloc Vector3[32]; for (int index = 0; index < cell.Portals.Count; index++) { if (onlyPortalIndex.HasValue && index != onlyPortalIndex.Value) continue; if (cell.Portals[index].OtherCellId != 0xFFFF) continue; if (index >= cell.PortalPolygons.Count) break; Vector3[] localVertices = cell.PortalPolygons[index]; // S4-c1 C1 (fix round 1 F1): DrawPortalPolyInternal's // degenerate-input guard // (WalkVisibilityMath.IsRejectedByPortalPolygonBoundaryGuard's // own doc comment) — tested on the LOCAL portal-polygon // vertices, BEFORE the world-transform loop below. A hit drops // the whole polygon: no transform, no fan submission, no // `submitted` increment (retail's reject -> transform -> clip // -> count order). No length pre-filter runs before this any // more (fix round 1 F2 — see the count comment below). if (WalkVisibilityMath.IsRejectedByPortalPolygonBoundaryGuard(localVertices)) continue; int count = Math.Min(localVertices.Length, world.Length); for (int vertex = 0; vertex < count; vertex++) { // FW3.3: fans draw at the dat aperture verbatim (the // ShellDrawLiftZ retirement — shells draw unlifted too). world[vertex] = Vector3.Transform( localVertices[vertex], cell.WorldTransform); } _diagnostics.EmitSeamMask( RenderingDiagnostics.ProbeSeamDrawEnabled, RenderingDiagnostics.SeamDrawTargetCells, cellId, index, forceFarZ, world[..count]); // S4-c1 fix round 1 F2: retail increments portalsDrawnCount // (0x59BD70-0x59BD74) BEFORE polyClipFinish runs (0x59BDB0) — // the counter records accepted ATTEMPTS, not successful GPU // fans (oh1-depth-lifecycle.md's arbitration table). A polygon // with fewer than 3 vertices is counted here even though // DrawDepthFan below draws nothing — its own `< 3` guard is // this port's stand-in for retail's post-clip `var_4 >= 3` // check. Round 0 counted AFTER that guard (a `< 3` continue // ahead of both the boundary guard and this increment), which // silently dropped the count for such a polygon — never // observed on authored dat data (every real portal polygon has // >= 3 vertices) but wrong order all the same. submitted++; _portalDepthMask.DrawDepthFan( world[..count], frame.ViewProjection, clipPlanes, forceFarZ); } return submitted; } // S3 chunk 4 fix round 1 (K4): the sky's own doorway-scissor bracket and // the EnableClipDistances wrapper around it are deleted — their only // caller was DrawWalkSky's per-outside-view-slice loop // (RetailPViewPassExecutor.WalkLeaf.cs), itself deleted by the same fix // (retail draws the sky ONCE, unclipped, exactly like the terrain and // the weather — see DrawWalkSky's own doc comment). // // S3 chunk 4 fix round 2 (L2): round 1's own comment here asserted that // the interior depth clear still had a live scissor to end at that // point — a mechanism that no longer existed ANYWHERE by then (K4 had // already deleted the sky's own scissor bracket, the last production // producer of a narrowed rectangle). The interface's own scissor // begin/end pair and their RhiWorldPassSurface bodies are deleted outright // in this round; the interior depth clear no longer ends anything // because nothing narrows a rectangle any more — the pass encoder sets // the full-attachment scissor once, at pass begin // (VulkanGpuPassEncoder.cs:87), and it stays that way for the life of // the pass. // // S3 review fix round 1 (F3): round 2's own comment above claimed "the // KEEP clips (exit seals, punch fans) bracket their own draws with // Enable/DisableClipDistances below" — FALSE. Neither // DrawExitPortalMask/DrawPortalDepthWrite nor // RetailPViewPassExecutor.WalkLeaf.cs's DrawWalkPunchFan ever called // Enable/DisableClipDistances; every one of this file's six // DisableClipDistances() call sites sat around the UNCLIPPED particle // draws (DrawWeatherOnce, DrawLandscapeStaticParticles x2, // DrawUnattachedSceneParticles, DrawCellParticles x2) — bodies that were // already no-ops on the only backend // (RhiWorldPassSurface.EnableClipDistances/DisableClipDistances, see // WorldPassSurface.cs). A no-op call bracketing an unclipped draw is not // a clip bracket at all; it asserted a mechanism this file never had. // Deleted outright with this correction, along with the private // DisableClipDistances() wrapper (a method that does nothing on the // only backend is a false mechanism too). IWorldPassSurface. // EnableClipDistances/DisableClipDistances stay on the INTERFACE — // WorldScenePassExecutor (the separate flat-world path) still calls // them — only this file's now-pointless use of them is gone. }