fix(rendering): bound portal resource lifetime

Separate logical ownership, render publication, and GPU retirement across live entities, landblocks, particles, textures, mesh arenas, portal/UI teardown, and per-frame scratch storage. Add bounded DAT/texture caches, upload budgets, three-frame fence retirement, exact-incarnation appearance reconciliation, frame pacing, and extensive lifetime conformance coverage.\n\nThe seven-destination connected route now cuts peak working/private memory roughly in half, returns Caul to 125-153 FPS locally, and produces no WER or AMD reset.\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-18 21:35:16 +02:00
parent 3971997689
commit 749e8ceeb1
225 changed files with 29107 additions and 3914 deletions

View file

@ -307,6 +307,74 @@ public class LandblockStreamerTests
Assert.NotEqual(testThreadId, loaderThreadId.Value);
}
[Fact]
public async Task DisposeAndConcurrentDisposeWaitForInFlightLoad()
{
using var entered = new ManualResetEventSlim();
using var release = new ManualResetEventSlim();
var streamer = new LandblockStreamer(loadLandblock: _ =>
{
entered.Set();
release.Wait();
return null;
});
try
{
streamer.Start();
streamer.EnqueueLoad(0x12340000u);
Assert.True(entered.Wait(TimeSpan.FromSeconds(2)));
Task firstDispose = Task.Run(streamer.Dispose);
Task secondDispose = Task.Run(streamer.Dispose);
await Task.Delay(50);
Assert.False(firstDispose.IsCompleted);
Assert.False(secondDispose.IsCompleted);
release.Set();
await Task.WhenAll(firstDispose, secondDispose).WaitAsync(TimeSpan.FromSeconds(2));
}
finally
{
release.Set();
streamer.Dispose();
}
}
[Fact]
public void DisposeRejectsEveryEnqueueKind()
{
var streamer = new LandblockStreamer(loadLandblock: _ => null);
streamer.Start();
streamer.Dispose();
Assert.Throws<ObjectDisposedException>(() => streamer.EnqueueLoad(0x12340000u));
Assert.Throws<ObjectDisposedException>(() => streamer.EnqueueUnload(0x12340000u));
Assert.Throws<ObjectDisposedException>(streamer.ClearPendingLoads);
}
[Fact]
public async Task ConcurrentStartAndDisposeLeaveAClosedStreamer()
{
for (int iteration = 0; iteration < 20; iteration++)
{
var streamer = new LandblockStreamer(loadLandblock: _ => null);
Exception? startFailure = null;
Task start = Task.Run(() =>
{
try { streamer.Start(); }
catch (ObjectDisposedException ex) { startFailure = ex; }
});
Task dispose = Task.Run(streamer.Dispose);
await Task.WhenAll(start, dispose).WaitAsync(TimeSpan.FromSeconds(2));
Assert.True(startFailure is null or ObjectDisposedException);
Assert.Throws<ObjectDisposedException>(() => streamer.EnqueueLoad(0x12340000u));
streamer.Dispose();
}
}
private static async Task<LandblockStreamResult> DrainFirstAsync(LandblockStreamer streamer)
{
for (int i = 0; i < SpinMaxIterations; i++)

View file

@ -149,6 +149,58 @@ public class StreamingControllerPriorityApplyTests
Assert.Equal(3, applied.Count);
}
[Fact]
public void DeferredCompaction_ApplyFailureRetainsExactResultWithoutReplayingCommittedPrefix()
{
uint priority = StreamingRegion.EncodeLandblockIdForTest(169, 180);
uint first = StreamingRegion.EncodeLandblockIdForTest(169, 182);
uint retry = StreamingRegion.EncodeLandblockIdForTest(169, 183);
uint last = StreamingRegion.EncodeLandblockIdForTest(169, 184);
var outbox = new Queue<LandblockStreamResult>(
[
LoadedOf(first),
LoadedOf(retry),
LoadedOf(last),
]);
var applied = new List<uint>();
bool failRetryOnce = true;
var ctrl = new StreamingController(
enqueueLoad: (_, _) => { },
enqueueUnload: _ => { },
drainCompletions: max =>
{
var batch = new List<LandblockStreamResult>();
while (batch.Count < max && outbox.Count > 0)
batch.Add(outbox.Dequeue());
return batch;
},
applyTerrain: (build, _) =>
{
if (build.LandblockId == retry && failRetryOnce)
{
failRetryOnce = false;
throw new InvalidOperationException("synthetic upload failure");
}
applied.Add(build.LandblockId);
},
state: new GpuWorldState(),
nearRadius: 4,
farRadius: 12)
{
MaxCompletionsPerFrame = 3,
PriorityLandblockId = priority,
};
Assert.Throws<InvalidOperationException>(() => ctrl.Tick(169, 180));
Assert.Equal([first], applied);
Assert.Equal(2, ctrl.DeferredApplyBacklog);
ctrl.Tick(169, 180);
Assert.Equal([first, retry, last], applied);
Assert.Equal(0, ctrl.DeferredApplyBacklog);
}
[Fact]
public void ForceReloadWindow_DiscardsBufferedCompletionsFromOldWindow()
{

View file

@ -11,10 +11,15 @@ public class StreamingControllerTests
private sealed class FakeStreamer
{
public List<uint> Loads { get; } = new();
public List<(uint Id, LandblockStreamJobKind Kind)> LoadJobs { get; } = new();
public List<uint> Unloads { get; } = new();
public Queue<LandblockStreamResult> Pending { get; } = new();
public void EnqueueLoad(uint id, LandblockStreamJobKind _) => Loads.Add(id);
public void EnqueueLoad(uint id, LandblockStreamJobKind kind)
{
Loads.Add(id);
LoadJobs.Add((id, kind));
}
public void EnqueueUnload(uint id) => Unloads.Add(id);
public IReadOnlyList<LandblockStreamResult> DrainCompletions(int max)
{
@ -65,6 +70,285 @@ public class StreamingControllerTests
Assert.Empty(fake.Unloads);
}
[Fact]
public void ReconfigureRadii_SmallerWindowUnloadsOnlyOutsideResidents()
{
var state = new GpuWorldState();
var fake = new FakeStreamer();
int pendingClears = 0;
var controller = new StreamingController(
fake.EnqueueLoad,
fake.EnqueueUnload,
fake.DrainCompletions,
(_, _) => { },
state,
nearRadius: 2,
farRadius: 2,
clearPendingLoads: () => pendingClears++);
controller.Tick(50, 50);
for (int dx = -2; dx <= 2; dx++)
for (int dy = -2; dy <= 2; dy++)
AddPublished(state, 50 + dx, 50 + dy, LandblockStreamTier.Near);
fake.Loads.Clear();
fake.LoadJobs.Clear();
controller.ReconfigureRadii(0, 0);
Assert.Equal(1, pendingClears);
Assert.Empty(fake.Loads);
Assert.Equal(24, fake.Unloads.Count);
Assert.DoesNotContain(0x3232FFFFu, fake.Unloads);
}
[Fact]
public void ReconfigureRadii_LargerWindowLoadsOnlyMissingResidents()
{
var state = new GpuWorldState();
var fake = new FakeStreamer();
var controller = new StreamingController(
fake.EnqueueLoad,
fake.EnqueueUnload,
fake.DrainCompletions,
(_, _) => { },
state,
nearRadius: 0,
farRadius: 0);
controller.Tick(50, 50);
AddPublished(state, 50, 50, LandblockStreamTier.Near);
fake.Loads.Clear();
fake.LoadJobs.Clear();
controller.ReconfigureRadii(1, 1);
Assert.Equal(8, fake.LoadJobs.Count);
Assert.All(
fake.LoadJobs,
static job => Assert.Equal(LandblockStreamJobKind.LoadNear, job.Kind));
Assert.DoesNotContain(fake.LoadJobs, static job => job.Id == 0x3232FFFFu);
Assert.Empty(fake.Unloads);
}
[Fact]
public void ReconfigureRadii_UnchangedWindowDoesNotClearOrRebootstrap()
{
var state = new GpuWorldState();
var fake = new FakeStreamer();
int pendingClears = 0;
var controller = new StreamingController(
fake.EnqueueLoad,
fake.EnqueueUnload,
fake.DrainCompletions,
(_, _) => { },
state,
nearRadius: 1,
farRadius: 2,
clearPendingLoads: () => pendingClears++);
controller.Tick(50, 50);
fake.Loads.Clear();
fake.LoadJobs.Clear();
controller.ReconfigureRadii(1, 2);
Assert.Equal(0, pendingClears);
Assert.Empty(fake.Loads);
Assert.Empty(fake.Unloads);
}
[Fact]
public void ReconfigureRadii_ClearFailureRetainsOldWindowAndResumesWithoutDuplicateLoads()
{
var state = new GpuWorldState();
var fake = new FakeStreamer();
int clearAttempts = 0;
var controller = new StreamingController(
fake.EnqueueLoad,
fake.EnqueueUnload,
fake.DrainCompletions,
(_, _) => { },
state,
nearRadius: 0,
farRadius: 0,
clearPendingLoads: () =>
{
clearAttempts++;
if (clearAttempts == 1)
throw new InvalidOperationException("clear not admitted");
});
controller.Tick(50, 50);
AddPublished(state, 50, 50, LandblockStreamTier.Near);
fake.LoadJobs.Clear();
Assert.Throws<AggregateException>(() => controller.ReconfigureRadii(1, 1));
Assert.Equal(0, controller.NearRadius);
Assert.Equal(0, controller.FarRadius);
Assert.Empty(fake.LoadJobs);
controller.ReconfigureRadii(1, 1);
Assert.Equal(2, clearAttempts);
Assert.Equal(8, fake.LoadJobs.Count);
Assert.Equal(8, fake.LoadJobs.Select(static job => job.Id).Distinct().Count());
Assert.Equal(1, controller.NearRadius);
Assert.Equal(1, controller.FarRadius);
}
[Fact]
public void ReconfigureRadii_QueueFailureRetriesOnlyUncommittedAdmission()
{
var state = new GpuWorldState();
var fake = new FakeStreamer();
int loadAttempts = 0;
bool failNext = false;
void EnqueueLoad(uint id, LandblockStreamJobKind kind)
{
loadAttempts++;
if (failNext)
{
failNext = false;
throw new InvalidOperationException("load not admitted");
}
fake.EnqueueLoad(id, kind);
}
var controller = new StreamingController(
EnqueueLoad,
fake.EnqueueUnload,
fake.DrainCompletions,
(_, _) => { },
state,
nearRadius: 0,
farRadius: 0);
controller.Tick(50, 50);
AddPublished(state, 50, 50, LandblockStreamTier.Near);
fake.LoadJobs.Clear();
loadAttempts = 0;
failNext = true;
Assert.Throws<AggregateException>(() => controller.ReconfigureRadii(1, 1));
Assert.Equal(8, loadAttempts);
Assert.Equal(7, fake.LoadJobs.Count);
Assert.Equal(0, controller.NearRadius);
controller.ReconfigureRadii(1, 1);
Assert.Equal(9, loadAttempts);
Assert.Equal(8, fake.LoadJobs.Count);
Assert.Equal(8, fake.LoadJobs.Select(static job => job.Id).Distinct().Count());
Assert.Equal(1, controller.NearRadius);
}
[Fact]
public void ReconfigureRadii_CommittedQueueFailureIsReportedButNeverReplayed()
{
var state = new GpuWorldState();
var fake = new FakeStreamer();
bool throwCommitted = false;
int loadAttempts = 0;
void EnqueueLoad(uint id, LandblockStreamJobKind kind)
{
loadAttempts++;
fake.EnqueueLoad(id, kind);
if (throwCommitted)
{
throwCommitted = false;
throw new StreamingMutationException(
"post-admission diagnostic failed",
mutationCommitted: true);
}
}
var controller = new StreamingController(
EnqueueLoad,
fake.EnqueueUnload,
fake.DrainCompletions,
(_, _) => { },
state,
nearRadius: 0,
farRadius: 0);
controller.Tick(50, 50);
AddPublished(state, 50, 50, LandblockStreamTier.Near);
fake.LoadJobs.Clear();
loadAttempts = 0;
throwCommitted = true;
Assert.Throws<AggregateException>(() => controller.ReconfigureRadii(1, 1));
Assert.Equal(8, loadAttempts);
Assert.Equal(8, fake.LoadJobs.Count);
Assert.Equal(1, controller.NearRadius);
controller.ReconfigureRadii(1, 1);
Assert.Equal(8, loadAttempts);
}
[Fact]
public void ReconfigureRadii_ClearCallbackReentryDefersWithoutRecursiveClear()
{
var state = new GpuWorldState();
var fake = new FakeStreamer();
StreamingController? controller = null;
int clearCalls = 0;
controller = new StreamingController(
fake.EnqueueLoad,
fake.EnqueueUnload,
fake.DrainCompletions,
(_, _) => { },
state,
nearRadius: 0,
farRadius: 0,
clearPendingLoads: () =>
{
clearCalls++;
controller!.ReconfigureRadii(1, 1);
});
controller.Tick(50, 50);
AddPublished(state, 50, 50, LandblockStreamTier.Near);
fake.LoadJobs.Clear();
controller.ReconfigureRadii(1, 1);
Assert.Equal(1, clearCalls);
Assert.Equal(8, fake.LoadJobs.Count);
Assert.Equal(1, controller.NearRadius);
}
[Fact]
public void ReconfigureRadii_QueueCallbackReentryDefersWithoutDuplicateAdmission()
{
var state = new GpuWorldState();
var fake = new FakeStreamer();
StreamingController? controller = null;
bool reenter = false;
int loadCalls = 0;
void EnqueueLoad(uint id, LandblockStreamJobKind kind)
{
loadCalls++;
fake.EnqueueLoad(id, kind);
if (reenter)
{
reenter = false;
controller!.ReconfigureRadii(1, 1);
}
}
controller = new StreamingController(
EnqueueLoad,
fake.EnqueueUnload,
fake.DrainCompletions,
(_, _) => { },
state,
nearRadius: 0,
farRadius: 0);
controller.Tick(50, 50);
AddPublished(state, 50, 50, LandblockStreamTier.Near);
fake.LoadJobs.Clear();
loadCalls = 0;
reenter = true;
controller.ReconfigureRadii(1, 1);
Assert.Equal(8, loadCalls);
Assert.Equal(8, fake.LoadJobs.Count);
Assert.Equal(8, fake.LoadJobs.Select(static job => job.Id).Distinct().Count());
Assert.Equal(1, controller.NearRadius);
}
[Fact]
public void DrainingLoadedResult_AddsToState()
{
@ -115,4 +399,59 @@ public class StreamingControllerTests
Assert.False(state.IsLoaded(landblockId));
}
[Fact]
public void DrainingUnloadedResult_CommitsStateBeforePresentationTeardown()
{
var state = new GpuWorldState();
var fake = new FakeStreamer();
const uint landblockId = 0x2020FFFFu;
var lb = new LoadedLandblock(
landblockId,
new LandBlock(),
new[]
{
new WorldEntity
{
Id = 1,
SourceGfxObjOrSetupId = 0x01000000u,
Position = System.Numerics.Vector3.Zero,
Rotation = System.Numerics.Quaternion.Identity,
MeshRefs = Array.Empty<MeshRef>(),
},
});
state.AddLandblock(lb);
bool callbackSawDetachedState = false;
var controller = new StreamingController(
fake.EnqueueLoad,
fake.EnqueueUnload,
fake.DrainCompletions,
(_, _) => { },
state,
nearRadius: 2,
farRadius: 2,
removeTerrain: id =>
{
callbackSawDetachedState = id == landblockId
&& !state.TryGetLandblock(id, out _);
});
fake.Pending.Enqueue(new LandblockStreamResult.Unloaded(landblockId));
controller.Tick(50, 50);
Assert.True(callbackSawDetachedState);
Assert.False(state.IsLoaded(landblockId));
}
private static void AddPublished(
GpuWorldState state,
int x,
int y,
LandblockStreamTier tier)
{
uint id = ((uint)x << 24) | ((uint)y << 16) | 0xFFFFu;
state.AddLandblock(
new LoadedLandblock(id, new LandBlock(), Array.Empty<WorldEntity>()),
tier: tier);
}
}