diff --git a/src/AcDream.App/Rendering/Walk/WalkFrameDriver.cs b/src/AcDream.App/Rendering/Walk/WalkFrameDriver.cs index 76192c47..4ba65a75 100644 --- a/src/AcDream.App/Rendering/Walk/WalkFrameDriver.cs +++ b/src/AcDream.App/Rendering/Walk/WalkFrameDriver.cs @@ -302,9 +302,10 @@ internal enum WalkFrameEventKind : byte /// ONLY opaque batches, depth test AND write on, no blending, so opaque /// terrain and opaque statics resolve identically in either submission /// order. A particle turn whose cell has no renderable emitter submits - /// nothing and keeps the batch open (fix round 1, F2). The GPU - /// submission order is therefore identical to replaying each cell - /// alone. + /// nothing and keeps the batch open (fix round 1, F2). The resolved image is therefore identical to replaying each cell + /// alone: every event whose GPU order against opaque terrain is + /// observable still flushes first (§9.7 F10), and opaque-vs-opaque + /// order is depth-resolved. LandCell, /// — diff --git a/tests/AcDream.App.Tests/Rendering/Walk/WalkFrameDriverTests.cs b/tests/AcDream.App.Tests/Rendering/Walk/WalkFrameDriverTests.cs index 0ff7f4dc..f31d4306 100644 --- a/tests/AcDream.App.Tests/Rendering/Walk/WalkFrameDriverTests.cs +++ b/tests/AcDream.App.Tests/Rendering/Walk/WalkFrameDriverTests.cs @@ -1530,12 +1530,18 @@ public sealed class WalkFrameDriverTests leaf.CellsWithoutEmitters.Add(0xAAAA0001u); leaf.CellsWithoutEmitters.Add(0xAAAA0002u); var ctx = new TestContext(); - var driver = new WalkFrameDriver(fx.Dispatcher, leaf, worldData); + var driver = new WalkFrameDriver(fx.Dispatcher, leaf, worldData, new RecordingTrace(log)); IWalkEventSink sink = driver; using DrawScope draw = fx.BeginDraw(); driver.BeginFrame(ctx, Matrix4x4.Identity, Vector3.Zero); - sink.OnLandscapeViews(new WalkPortalView()); + // A REAL full-viewport landscape view: with an empty view the classifier + // admits nothing, the stream never grows, and no StreamMark ever fires + // (the round-2 review caught exactly that vacuity). + var landscapeViews = new WalkPortalView(); + WalkCopyView.AppendFullViewportQuad( + landscapeViews, ctx.Rays, ctx.WorldViewpoint, ctx.ViewportWidth, ctx.ViewportHeight); + sink.OnLandscapeViews(landscapeViews); sink.OnLandCellTurn(0xF4180000u, 8, 0); // landblock A sink.OnLandscapeCellTurn(0xAAAA0001u); // empty particle turn: no growth, no emitters -> no submit, no flush (F2) @@ -1561,6 +1567,8 @@ public sealed class WalkFrameDriverTests Assert.Equal( new[] { + "FLUSH:1:OutdoorStatic", // 0xAAAA0002's statics: the StreamMark draws its range but does NOT flush the terrain batch (F10) + "FLUSH:1:OutdoorStatic", // 0xBBBB0002's statics: same "LANDCELL:f4180000:8:0,f3180000:8:0,f2180001:8:0", "PARTICLES:bbbb0002", "LANDCELL:f3180000:8:1", @@ -1572,6 +1580,9 @@ public sealed class WalkFrameDriverTests log); Assert.DoesNotContain("PARTICLES:aaaa0001", log); Assert.DoesNotContain("PARTICLES:aaaa0002", log); + // Two genuine StreamMarks fired (the ordered stream grew twice) — the + // F10 rule is exercised, not assumed. + Assert.Equal(2, log.Count(entry => entry.StartsWith("FLUSH:", StringComparison.Ordinal))); Assert.Equal(4, leaf.LandCellBatches.Count); Assert.Equal( new (uint LandblockId, int SideCellCount, int CellIndex)[]