fix(streaming): close #54 — plumb JobKind through BuildLandblockForStreaming
Bug A's fix (commit `9217fd9`) patched at the worker output by stripping
entities from far-tier `LoadedLandblock`s after the full `LoadNear` path
ran. The worker still wasted CPU on `LandBlockInfo` reads + entity
hydration + `SceneryGenerator` math + interior-cell walks for ~544
far-tier LBs at radius=12, just to throw the work away.
This commit plumbs `LandblockStreamJobKind` through to the factory so the
worker can branch at the source:
- `LandblockStreamer.cs`: replace the `Func<uint, LoadedLandblock?>`
factory with `Func<uint, LandblockStreamJobKind, LoadedLandblock?>` as
the primary ctor signature. Add a back-compat overload that wraps the
old single-arg signature (`(id, _) => loadLandblock(id)`) so existing
test code keeps compiling without modification — the 5 ctor sites in
`LandblockStreamerTests.cs` now resolve to the overload. `HandleJob`
passes `load.Kind` to the factory; the post-load entity-strip is
retained as a `Debug.Assert` + Release safety net.
- `GameWindow.cs`: `BuildLandblockForStreaming(uint, JobKind)` branches
on `kind == LoadFar` at the top — reads only the `LandBlock` heightmap
dat and returns a `LoadedLandblock` with `Array.Empty<WorldEntity>()`.
Skips `LandblockLoader.Load` (which reads `LandBlockInfo`),
`BuildSceneryEntitiesForStreaming`, and `BuildInteriorEntitiesForStreaming`
entirely. Near-tier path is unchanged. Both call sites updated to pass
the kind through the lambda: `(id, kind) => BuildLandblockForStreaming(id, kind)`.
Tests: 1688/1696 (8 pre-existing physics/input failures unchanged).
Streaming-targeted filter (30 tests covering LandblockStreamer +
StreamingController + StreamingRegion) all green via the back-compat
overload — no test code needed updating.
Per-LB worker cost on far-tier: was ~tens of ms (full hydration,
including LandBlockInfo + scenery generation + interior cells); now a
single `LandBlock` dat read (~sub-ms).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b19f1d10ec
commit
bf31e59805
2 changed files with 66 additions and 22 deletions
|
|
@ -1652,7 +1652,7 @@ public sealed class GameWindow : IDisposable
|
|||
// it can call LandblockMesh.Build without a dat read — _heightTable and
|
||||
// _blendCtx are read-only after init, _surfaceCache is ConcurrentDictionary (T9).
|
||||
_streamer = new AcDream.App.Streaming.LandblockStreamer(
|
||||
loadLandblock: id => BuildLandblockForStreaming(id),
|
||||
loadLandblock: (id, kind) => BuildLandblockForStreaming(id, kind),
|
||||
buildMeshOrNull: (id, lb) =>
|
||||
{
|
||||
if (lb is null || _heightTable is null || _blendCtx is null || _surfaceCache is null)
|
||||
|
|
@ -4639,8 +4639,18 @@ public sealed class GameWindow : IDisposable
|
|||
/// DatReaderWriter) and pure CPU work. No GL calls here.
|
||||
///
|
||||
/// MVP scope: stabs only. Scenery + interior added in Task 8.
|
||||
///
|
||||
/// ISSUE #54 (post-A.5): far-tier loads (<c>kind == LoadFar</c>) skip
|
||||
/// LandBlockInfo + scenery + interior hydration. They return only the
|
||||
/// LandBlock heightmap dat record + an empty entity list — enough for
|
||||
/// terrain-mesh build on the next phase. Near-tier loads run the full
|
||||
/// path. This replaces Bug A's post-load entity strip in
|
||||
/// <see cref="AcDream.App.Streaming.LandblockStreamer"/> with an
|
||||
/// early-out at the source.
|
||||
/// </summary>
|
||||
private AcDream.Core.World.LoadedLandblock? BuildLandblockForStreaming(uint landblockId)
|
||||
private AcDream.Core.World.LoadedLandblock? BuildLandblockForStreaming(
|
||||
uint landblockId,
|
||||
AcDream.App.Streaming.LandblockStreamJobKind kind)
|
||||
{
|
||||
if (_dats is null) return null;
|
||||
|
||||
|
|
@ -4653,14 +4663,31 @@ public sealed class GameWindow : IDisposable
|
|||
// contention by pre-building render-thread work on the worker.
|
||||
lock (_datLock)
|
||||
{
|
||||
return BuildLandblockForStreamingLocked(landblockId);
|
||||
return BuildLandblockForStreamingLocked(landblockId, kind);
|
||||
}
|
||||
}
|
||||
|
||||
private AcDream.Core.World.LoadedLandblock? BuildLandblockForStreamingLocked(uint landblockId)
|
||||
private AcDream.Core.World.LoadedLandblock? BuildLandblockForStreamingLocked(
|
||||
uint landblockId,
|
||||
AcDream.App.Streaming.LandblockStreamJobKind kind)
|
||||
{
|
||||
if (_dats is null) return null;
|
||||
|
||||
// ISSUE #54: far-tier early-out — heightmap only, empty entities.
|
||||
// Skips the LandBlockInfo dat read AND all entity hydration (stabs
|
||||
// + buildings) AND the SceneryGenerator AND interior cells. Cuts
|
||||
// worker-thread cost per far-tier LB from ~tens of ms to a single
|
||||
// dat read.
|
||||
if (kind == AcDream.App.Streaming.LandblockStreamJobKind.LoadFar)
|
||||
{
|
||||
var heightmapOnly = _dats.Get<DatReaderWriter.DBObjs.LandBlock>(landblockId);
|
||||
if (heightmapOnly is null) return null;
|
||||
return new AcDream.Core.World.LoadedLandblock(
|
||||
landblockId,
|
||||
heightmapOnly,
|
||||
System.Array.Empty<AcDream.Core.World.WorldEntity>());
|
||||
}
|
||||
|
||||
var baseLoaded = AcDream.Core.World.LandblockLoader.Load(_dats, landblockId);
|
||||
if (baseLoaded is null) return null;
|
||||
|
||||
|
|
@ -8157,7 +8184,7 @@ public sealed class GameWindow : IDisposable
|
|||
_streamer.Dispose();
|
||||
|
||||
_streamer = new AcDream.App.Streaming.LandblockStreamer(
|
||||
loadLandblock: id => BuildLandblockForStreaming(id),
|
||||
loadLandblock: (id, kind) => BuildLandblockForStreaming(id, kind),
|
||||
buildMeshOrNull: (id, lb) =>
|
||||
{
|
||||
if (lb is null || _heightTable is null || _blendCtx is null || _surfaceCache is null)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue