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:
parent
3971997689
commit
749e8ceeb1
225 changed files with 29107 additions and 3914 deletions
|
|
@ -0,0 +1,77 @@
|
|||
using System.Numerics;
|
||||
using AcDream.App.Streaming;
|
||||
using AcDream.Core.World;
|
||||
using DatReaderWriter.DBObjs;
|
||||
|
||||
namespace AcDream.App.Tests.Streaming;
|
||||
|
||||
public sealed class GpuWorldStateAnimatedIndexTests
|
||||
{
|
||||
private const uint Landblock = 0x0101FFFFu;
|
||||
|
||||
[Fact]
|
||||
public void StableLandblock_ReusesAnimatedIndexAcrossFrames()
|
||||
{
|
||||
var state = new GpuWorldState();
|
||||
WorldEntity entity = Entity(1u, 0u);
|
||||
state.AddLandblock(Loaded(entity));
|
||||
|
||||
IReadOnlyDictionary<uint, WorldEntity>? first = SingleIndex(state);
|
||||
IReadOnlyDictionary<uint, WorldEntity>? second = SingleIndex(state);
|
||||
|
||||
Assert.Same(first, second);
|
||||
Assert.Same(entity, first![entity.Id]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReplacedLandblockRecord_RebuildsOnlyThatGeneration()
|
||||
{
|
||||
var state = new GpuWorldState();
|
||||
WorldEntity original = Entity(1u, 0u);
|
||||
WorldEntity live = Entity(2u, 0x70000002u);
|
||||
state.AddLandblock(Loaded(original));
|
||||
IReadOnlyDictionary<uint, WorldEntity>? first = SingleIndex(state);
|
||||
|
||||
state.PlaceLiveEntityProjection(Landblock, live);
|
||||
IReadOnlyDictionary<uint, WorldEntity>? second = SingleIndex(state);
|
||||
|
||||
Assert.NotSame(first, second);
|
||||
Assert.Same(original, second![original.Id]);
|
||||
Assert.Same(live, second[live.Id]);
|
||||
Assert.Same(second, SingleIndex(state));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveThenReload_DoesNotRetainRemovedGeneration()
|
||||
{
|
||||
var state = new GpuWorldState();
|
||||
WorldEntity removed = Entity(1u, 0u);
|
||||
state.AddLandblock(Loaded(removed));
|
||||
IReadOnlyDictionary<uint, WorldEntity>? first = SingleIndex(state);
|
||||
|
||||
state.RemoveLandblock(Landblock);
|
||||
WorldEntity replacement = Entity(2u, 0u);
|
||||
state.AddLandblock(Loaded(replacement));
|
||||
IReadOnlyDictionary<uint, WorldEntity>? second = SingleIndex(state);
|
||||
|
||||
Assert.NotSame(first, second);
|
||||
Assert.False(second!.ContainsKey(removed.Id));
|
||||
Assert.Same(replacement, second[replacement.Id]);
|
||||
}
|
||||
|
||||
private static IReadOnlyDictionary<uint, WorldEntity>? SingleIndex(GpuWorldState state) =>
|
||||
Assert.Single(state.LandblockEntries).AnimatedById;
|
||||
|
||||
private static LoadedLandblock Loaded(params WorldEntity[] entities) =>
|
||||
new(Landblock, new LandBlock(), entities);
|
||||
|
||||
private static WorldEntity Entity(uint id, uint serverGuid) => new()
|
||||
{
|
||||
Id = id,
|
||||
ServerGuid = serverGuid,
|
||||
SourceGfxObjOrSetupId = 0x02000001u,
|
||||
Position = Vector3.Zero,
|
||||
Rotation = Quaternion.Identity,
|
||||
MeshRefs = Array.Empty<MeshRef>(),
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue