perf(streaming): cache per-column surface heights to fix move lag spikes

RecomputeTargetSet sampled TerrainSampler.HeightAt (a 169-point biome
blend kernel) 5x per column over the whole render distance on the main
thread every chunk crossing (~1,445 calls = ~250k biome lookups), causing
multi-frame hitches while moving. Memoize each chunk column's (min,max)
surface range, reused across ticks and pruned to the horizontal window, so
each column is sampled once. Exact (no terrain change); spike is amortized
to a thin edge strip per crossing and zero when stationary.
This commit is contained in:
MDietermann
2026-06-01 20:43:31 +02:00
parent 94930faccb
commit 9c28bf152f
2 changed files with 51 additions and 1 deletions

View File

@@ -447,6 +447,29 @@ public class ChunkStreamerTests
Assert.Null(world.GetChunk(new Vector3i(0, 5, 0))); // air above surface — not loaded
}
[Fact]
public void RecomputeTargetSet_CachesSurfaceHeights_OnlySamplesNewColumns()
{
var cfg = new Configuration
{
RenderDistanceHorizontal = 1,
RenderDistanceVertical = 0,
SpawnReadyRadiusHorizontal = 1,
MaxGenerateJobsInFlight = 100,
MaxMeshUploadsPerFrame = 100,
};
int calls = 0;
var (streamer, _, _) = CreateStreamer(cfg, surfaceHeightAt: (_, _) => { calls++; return 8; });
streamer.Tick(new Vector3i(0, 0, 0));
int afterFirst = calls; // 9 columns × 5 samples = 45 (spawn-check reuses cache)
streamer.Tick(new Vector3i(1, 0, 0)); // overlaps 6 columns; only the cx=2 strip is new
int newCalls = calls - afterFirst;
Assert.Equal(45, afterFirst);
Assert.Equal(15, newCalls); // 3 new columns × 5 — cached columns are not re-sampled
}
[Fact]
public void DrawVisible_DrawsOnlyChunksPassingPredicate_AndReturnsCount()
{