perf(streaming): cursor publication across frame budgets

This commit is contained in:
Erik 2026-07-24 19:10:18 +02:00
parent bb16f74fd4
commit 98f1ac8934
32 changed files with 2215 additions and 823 deletions

View file

@ -56,6 +56,73 @@ public sealed class LandblockConcretePresentationPipelineTests
Assert.Equal(1, fixture.Static.Diagnostics.CompleteCount);
}
[Fact]
public void MeteredLoaded_RetainsExactHeadAndPublishesSpatiallyOnlyAfterOwnerSuffix()
{
var calls = new List<string>();
ConcreteFixture fixture = Fixture(
calls,
commitEnvCells: _ => calls.Add("envcell"));
fixture.Events.EntitySpawned += _ => calls.Add("plugin");
var pipeline = new LandblockPresentationPipeline(
fixture.Render,
fixture.Physics,
fixture.Static,
fixture.State,
fixture.RetirementOwner,
onLandblockLoaded: _ => calls.Add("live-recovery"));
LandblockStreamResult.Loaded result =
Result(Build(Entity(0x80A9B401u), Entity(0x80A9B402u)));
LandblockStreamCostEstimate estimate =
LandblockStreamResultCost.Estimate(result);
var budget = new StreamingWorkBudget(
TimeSpan.FromSeconds(1),
maxCompletionAdmissions: 64,
maxAdoptedCpuBytes: 1_000_000,
maxEntityOperations: 1,
maxGpuUploadBytes: 1_000_000,
maxGlRetireOperations: 64,
destinationReserveFraction: 0.75f);
LandblockPublicationAdvance advance = default;
for (int frame = 0; frame < 64; frame++)
{
var meter = new StreamingWorkMeter(budget);
advance = frame == 0
? pipeline.PublishLoaded(
result,
estimate,
meter,
ensureProgress: false)
: pipeline.ResumePublication(
result,
meter,
ensureProgress: false);
meter.FinishFrame();
Assert.True(
meter.Snapshot.Used.EntityOperations <= 1,
$"stage={meter.Snapshot.LastStage}, " +
$"entities={meter.Snapshot.Used.EntityOperations}, " +
$"oversized={meter.Snapshot.OversizedProgressCount}");
if (fixture.Static.Diagnostics.CompleteCount == 0)
Assert.False(fixture.State.IsLoaded(LandblockId));
if (advance.Completed)
break;
}
Assert.True(advance.Completed);
Assert.False(pipeline.HasPendingPublication(result));
Assert.True(fixture.State.IsNearTier(LandblockId));
Assert.Equal(
["terrain", "envcell", "plugin", "plugin", "pin", "live-recovery"],
calls);
Assert.Equal(1, fixture.Render.Diagnostics.BeginCount);
Assert.Equal(1, fixture.Physics.Diagnostics.BeginCount);
Assert.Equal(1, fixture.Physics.Diagnostics.CompleteCount);
Assert.Equal(1, fixture.Static.Diagnostics.CompleteCount);
}
[Fact]
public void RenderSuffixFailure_ResumesConcreteReceiptsWithoutReplayingPrefixes()
{

View file

@ -1119,7 +1119,15 @@ public sealed class LandblockPresentationPipelineTests
failReplayOnce = false;
throw new InvalidOperationException("injected replay failure");
}
});
},
workBudgetOptions: new StreamingWorkBudgetOptions(
MaxUpdateMilliseconds: 100,
MaxCompletionAdmissions: 64,
MaxAdoptedCpuBytes: 1_000_000,
MaxEntityOperations: 1_000,
MaxGpuUploadBytes: 1_000_000,
MaxGlRetireOperations: 1_000,
DestinationReserveFraction: 0.75f));
Assert.Throws<InvalidOperationException>(() => controller.Tick(0x90, 0x90));
Assert.True(state.IsNearTier(landblockId));

View file

@ -1,5 +1,6 @@
using System.Numerics;
using System.Reflection;
using System.Collections.Immutable;
using AcDream.App.Rendering;
using AcDream.App.Rendering.Wb;
using AcDream.App.Streaming;
@ -194,6 +195,47 @@ public sealed class LandblockRenderPublisherTests
Assert.Equal(1, first.Diagnostics.CompleteCount);
}
[Fact]
public void RetainedEnvCellPublisher_AdvancesOneShellBeforeAtomicCommit()
{
var envPublisher = new RecordingEnvCellPublisher();
EnvCellLandblockBuild envCells = new(
LandblockId,
Array.Empty<LoadedCell>(),
[
Shell(0xA9B40100u, 1),
Shell(0xA9B40101u, 2),
Shell(0xA9B40102u, 3),
]);
var publisher = new LandblockRenderPublisher(
publishTerrain: static (_, _, _) => { },
removeTerrain: static _ => { },
cellVisibility: new CellVisibility(),
worldState: new GpuWorldState(),
envCellPublisher: envPublisher);
LandblockRenderPublication receipt = publisher.PreparePublication(
Build(envCells),
EmptyMesh());
publisher.BeginPublication(receipt);
Assert.False(publisher.AdvanceCompleteOne(receipt));
Assert.Equal(0, envPublisher.AdvancedShells);
Assert.Equal(0, envPublisher.CommitCount);
for (int shell = 1; shell <= 3; shell++)
{
Assert.False(publisher.AdvanceCompleteOne(receipt));
Assert.Equal(shell, envPublisher.AdvancedShells);
Assert.Equal(0, envPublisher.CommitCount);
}
Assert.False(publisher.AdvanceCompleteOne(receipt));
Assert.Equal(0, envPublisher.CommitCount);
Assert.False(publisher.AdvanceCompleteOne(receipt));
Assert.Equal(1, envPublisher.CommitCount);
Assert.True(publisher.AdvanceCompleteOne(receipt));
Assert.Equal(1, publisher.Diagnostics.CompleteCount);
}
[Fact]
public void PrepareAndRetirementOperationsStayBalancedAtTheirOwnerBoundary()
{
@ -341,6 +383,19 @@ public sealed class LandblockRenderPublisherTests
InverseWorldTransform = Matrix4x4.Identity,
};
private static EnvCellShellPlacement Shell(uint cellId, ulong geometryId) =>
new(
cellId,
geometryId,
EnvironmentId: 1u,
CellStructure: 0,
Surfaces: ImmutableArray<ushort>.Empty,
WorldPosition: Vector3.Zero,
Rotation: Quaternion.Identity,
Transform: Matrix4x4.Identity,
LocalBounds: new WbBoundingBox(Vector3.Zero, Vector3.One),
WorldBounds: new WbBoundingBox(Vector3.Zero, Vector3.One));
private static LandblockMeshData MeshAtHeights(float first, float second) => new(
[
new TerrainVertex(new Vector3(0f, 0f, first), Vector3.UnitZ, 0, 0, 0, 0),
@ -362,4 +417,42 @@ public sealed class LandblockRenderPublisherTests
}
throw new DirectoryNotFoundException("Could not locate repository root.");
}
private sealed class RecordingEnvCellPublisher :
IEnvCellLandblockPublisher
{
private readonly object _owner = new();
public int AdvancedShells { get; private set; }
public int CommitCount { get; private set; }
public EnvCellLandblockPublication PreparePublication(
EnvCellLandblockBuild build) =>
new(_owner, build);
public bool AdvancePreparationOne(
EnvCellLandblockPublication publication)
{
Assert.Same(_owner, publication.Owner);
if (publication.PreparationCommitted)
return true;
if (publication.ShellCursor < publication.Build.Shells.Length)
{
publication.ShellCursor++;
AdvancedShells++;
return false;
}
publication.PreparationCommitted = true;
return true;
}
public void CommitPublication(
EnvCellLandblockPublication publication)
{
Assert.True(publication.PreparationCommitted);
publication.PublicationCommitted = true;
CommitCount++;
}
}
}

View file

@ -507,7 +507,16 @@ public sealed class LandblockRetirementCoordinatorTests
nearRadius: 0,
farRadius: 0,
retirementCoordinator: coordinator,
workBudgetOptions: workBudgetOptions);
workBudgetOptions: workBudgetOptions ?? GenerousWorkBudget());
private static StreamingWorkBudgetOptions GenerousWorkBudget() => new(
MaxUpdateMilliseconds: 100,
MaxCompletionAdmissions: 64,
MaxAdoptedCpuBytes: 64 * StreamingWorkBudgetOptions.MiB,
MaxEntityOperations: 1_024,
MaxGpuUploadBytes: 64 * StreamingWorkBudgetOptions.MiB,
MaxGlRetireOperations: 256,
DestinationReserveFraction: 0.75f);
private static IReadOnlyList<LandblockStreamResult> Drain(
Queue<LandblockStreamResult> pending,

View file

@ -238,7 +238,8 @@ public sealed class StreamingControllerReadinessTests
ensureCalls++;
});
controller.Tick(0x12, 0x36);
for (int frame = 0; frame < 32 && ensureCalls == 0; frame++)
controller.Tick(0x12, 0x36);
Assert.Equal(1, ensureCalls);
}
@ -294,7 +295,8 @@ public sealed class StreamingControllerReadinessTests
ensureCalls++;
});
controller.Tick(0x12, 0x36);
for (int frame = 0; frame < 32 && ensureCalls == 0; frame++)
controller.Tick(0x12, 0x36);
Assert.Equal(1, ensureCalls);
Assert.Equal(1, meshes.ReferenceCounts[geometryId]);

View file

@ -337,7 +337,7 @@ public sealed class StreamingFrameControllerTests
}
[Fact]
public void StreamingPublicationMakesSameFrameInboundProjectionResident()
public void CompletedStreamingPublicationMakesInboundProjectionResident()
{
const uint landblock = 0x0A14FFFFu;
const uint cell = 0x0A140001u;
@ -359,7 +359,8 @@ public sealed class StreamingFrameControllerTests
farRadius: 0);
var fixture = new Fixture(streamingBackend: streaming);
fixture.Controller.Tick();
for (int frame = 0; frame < 32 && !state.IsLoaded(landblock); frame++)
fixture.Controller.Tick();
Assert.True(state.IsLoaded(landblock));
var runtime = Runtime(state);

View file

@ -347,8 +347,8 @@ public sealed class StreamingWorkBudgetTests
Assert.Equal(1, first.DeferredCompletions);
Assert.Equal(44, first.DeferredAdoptedCpuBytes);
Assert.True(first.OldestDeferredAgeMilliseconds >= 0);
Assert.Equal(3, first.LastFrame.Operations);
Assert.Equal(3, first.LastFrame.CompletedOperations);
Assert.Equal(6, first.LastFrame.Operations);
Assert.Equal(6, first.LastFrame.CompletedOperations);
Assert.Equal(1, first.LastFrame.YieldCount);
Assert.Equal(88, first.LastFrame.Used.AdoptedCpuBytes);
@ -358,7 +358,7 @@ public sealed class StreamingWorkBudgetTests
Assert.Equal(0, second.DeferredCompletions);
Assert.Equal(0, second.DeferredAdoptedCpuBytes);
Assert.Equal(0, second.OldestDeferredAgeMilliseconds);
Assert.Equal(1, second.LastFrame.CompletedOperations);
Assert.Equal(4, second.LastFrame.CompletedOperations);
}
[Fact]
@ -409,7 +409,7 @@ public sealed class StreamingWorkBudgetTests
}
[Fact]
public void DestinationAndUnloadPriorityNeverBypassExecutionBudgets()
public void DestinationAndEmptyUnloadPriorityNeverBypassPublicationBudget()
{
uint center = StreamingRegion.EncodeLandblockIdForTest(32, 32);
uint ordinary = StreamingRegion.EncodeLandblockIdForTest(33, 33);
@ -432,16 +432,16 @@ public sealed class StreamingWorkBudgetTests
controller.Tick(32, 32);
Assert.Equal([center], applied);
Assert.Equal(2, controller.WorkDiagnostics.DeferredCompletions);
Assert.Equal(1, controller.WorkDiagnostics.DeferredCompletions);
Assert.Equal(1, controller.WorkDiagnostics.LastFrame.YieldCount);
Assert.Equal(44, controller.WorkDiagnostics.LastFrame.Used.GpuUploadBytes);
Assert.Equal(1, controller.WorkDiagnostics.LastFrame.Used.GlRetireOperations);
Assert.Equal(0, controller.WorkDiagnostics.LastFrame.Used.GlRetireOperations);
controller.Tick(32, 32);
Assert.Equal([center, ordinary], applied);
Assert.Equal(0, controller.WorkDiagnostics.DeferredCompletions);
Assert.Equal(1, controller.WorkDiagnostics.LastFrame.Used.GlRetireOperations);
Assert.Equal(0, controller.WorkDiagnostics.LastFrame.Used.GlRetireOperations);
}
[Fact]