From c4fd37384afa1d8d10600aab743f429e2d062f97 Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 10 May 2026 07:58:12 +0200 Subject: [PATCH] feat(A.5 T16): wire two-tier streaming into GameWindow GameWindow now constructs StreamingController with nearRadius / farRadius defaults of 4 / 12 (per spec acceptance criterion). Env vars: - ACDREAM_NEAR_RADIUS (default 4) - ACDREAM_FAR_RADIUS (default 12) - ACDREAM_STREAM_RADIUS (legacy; if set, treats as nearRadius and bumps farRadius to max(stream, default)) Fields _nearRadius / _farRadius added alongside legacy _streamingRadius (kept so the debug overlay's getStreamingRadius callback stays valid). ApplyLoadedTerrainLocked routes to TerrainModernRenderer.AddLandblockWithMesh (T15) instead of AddLandblock directly, making the two-tier entry point the canonical call path. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/AcDream.App/Rendering/GameWindow.cs | 46 ++++++++++++++++++------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index 81f6560..e442b94 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -83,7 +83,9 @@ public sealed class GameWindow : IDisposable private AcDream.App.Streaming.LandblockStreamer? _streamer; private AcDream.App.Streaming.GpuWorldState _worldState = new(); private AcDream.App.Streaming.StreamingController? _streamingController; - private int _streamingRadius = 2; // default 5×5 + private int _streamingRadius = 2; // default 5×5 (kept for debug overlay getStreamingRadius callback) + private int _nearRadius = 4; // Phase A.5 T16: two-tier near ring (default 4 → 9×9) + private int _farRadius = 12; // Phase A.5 T16: two-tier far ring (default 12 → 25×25) private uint? _lastLivePlayerLandblockId; // Phase B.3: physics engine — populated from the streaming pipeline. @@ -1575,13 +1577,30 @@ public sealed class GameWindow : IDisposable // the player. _particleRenderer = new ParticleRenderer(_gl, shadersDir, _textureCache, _dats); - // Phase A.1: replace the one-shot 3×3 preload with a streaming controller. - // Parse runtime radius from environment (default 2 → 5×5 window). - // Values outside [0, 8] fall back to the field default of 2. - var radiusEnv = Environment.GetEnvironmentVariable("ACDREAM_STREAM_RADIUS"); - if (int.TryParse(radiusEnv, out var r) && r >= 0 && r <= 8) - _streamingRadius = r; - Console.WriteLine($"streaming: radius={_streamingRadius} (window={2*_streamingRadius+1}×{2*_streamingRadius+1})"); + // Phase A.5 T16: two-tier radius env-var parsing. + // ACDREAM_NEAR_RADIUS / ACDREAM_FAR_RADIUS set the two rings independently. + // Legacy ACDREAM_STREAM_RADIUS is honoured for backward-compat: it sets + // nearRadius and bumps farRadius to max(streamRadius, default farRadius). + { + var nearEnv = Environment.GetEnvironmentVariable("ACDREAM_NEAR_RADIUS"); + var farEnv = Environment.GetEnvironmentVariable("ACDREAM_FAR_RADIUS"); + var legacyEnv = Environment.GetEnvironmentVariable("ACDREAM_STREAM_RADIUS"); + + if (int.TryParse(nearEnv, out var nr) && nr >= 0) _nearRadius = nr; + if (int.TryParse(farEnv, out var fr) && fr >= 0) _farRadius = fr; + + // Legacy override: ACDREAM_STREAM_RADIUS acts as nearRadius and + // ensures farRadius >= streamRadius. + if (int.TryParse(legacyEnv, out var sr) && sr >= 0) + { + _nearRadius = sr; + _streamingRadius = sr; // keep debug overlay in sync + _farRadius = System.Math.Max(sr, _farRadius); + } + } + Console.WriteLine( + $"streaming: nearRadius={_nearRadius} (window={2*_nearRadius+1}x{2*_nearRadius+1})" + + $" farRadius={_farRadius} (window={2*_farRadius+1}x{2*_farRadius+1})"); // Phase A.5 T11+: the streamer now runs on a dedicated worker thread. // loadLandblock acquires _datLock (T10) before touching DatCollection. @@ -1610,8 +1629,8 @@ public sealed class GameWindow : IDisposable drainCompletions: _streamer.DrainCompletions, applyTerrain: ApplyLoadedTerrain, state: _worldState, - nearRadius: _streamingRadius, - farRadius: _streamingRadius, + nearRadius: _nearRadius, + farRadius: _farRadius, removeTerrain: id => { // Phase G.2: release any LightSources attached to entities @@ -5144,9 +5163,10 @@ public sealed class GameWindow : IDisposable (lbY - _liveCenterY) * 192f, 0f); - // Phase A.5 T12: terrain mesh is pre-built by the worker thread and - // passed in via meshData. No longer rebuilt here on the render thread. - _terrain.AddLandblock(lb.LandblockId, meshData, origin); + // Phase A.5 T15/T16: route through AddLandblockWithMesh — the named + // two-tier entry point. Delegates to AddLandblock internally; both + // paths share one GPU upload path. + _terrain.AddLandblockWithMesh(lb.LandblockId, meshData, origin); // Step 4: drain pending LoadedCells from the worker thread. while (_pendingCells.TryTake(out var cell))