perf(physics): reuse reset-complete transition scratch
Mirror retail's ten-deep LIFO transition lifetime, retain all query scratch with complete reset contracts, and remove Tier-0 enum boxing without changing collision decisions. Fresh and retained engines are bit-identical across the expanded oracle, while measured transition profiles now allocate 0 bytes per resolve.
This commit is contained in:
parent
2effba5127
commit
16d182c2f0
19 changed files with 3037 additions and 1228 deletions
|
|
@ -9,11 +9,11 @@ using Xunit.Abstractions;
|
|||
namespace AcDream.Core.Tests.Physics;
|
||||
|
||||
/// <summary>
|
||||
/// Slice I0 evidence harness for the four production
|
||||
/// Slice I allocation gate for the four production
|
||||
/// <see cref="PhysicsEngine.ResolveWithTransition"/> call shapes. This is not a
|
||||
/// budget assertion: I1 deliberately changes the expected allocation from the
|
||||
/// recorded graph-path baseline to zero steady transition/query-scratch bytes.
|
||||
/// The harness stays in-tree so the before/after figure is reproducible.
|
||||
/// broad frame-allocation budget: it isolates transition/query scratch and
|
||||
/// requires zero steady bytes after all retained buffers and tiered code paths
|
||||
/// have warmed.
|
||||
/// </summary>
|
||||
public sealed class TransitionAllocationBaselineTests
|
||||
{
|
||||
|
|
@ -27,32 +27,33 @@ public sealed class TransitionAllocationBaselineTests
|
|||
=> _output = output;
|
||||
|
||||
[Fact]
|
||||
public void GraphPath_RecordsPerResolveAllocationForEveryMoverFamily()
|
||||
public void ReusedScratch_AllocatesZeroBytesPerResolveForEveryMoverFamily()
|
||||
{
|
||||
var (root, resolved) = BSPStepUpFixtures.TallWall();
|
||||
var engine = BuildEngine(root, resolved);
|
||||
var groundEngine = BuildGroundEngine();
|
||||
|
||||
long player = Measure(() => ResolvePlayer(engine));
|
||||
long groundedPublication = Measure(
|
||||
() => ResolveGroundedPublication(groundEngine));
|
||||
long remote = Measure(() => ResolveRemote(engine));
|
||||
long projectile = Measure(() => ResolveProjectile(engine));
|
||||
long camera = Measure(() => ResolveCamera(engine));
|
||||
|
||||
_output.WriteLine(
|
||||
$"Slice I0 graph-path allocation baseline ({MeasuredIterations:N0} resolves/profile):");
|
||||
$"Slice I1 steady scratch gate ({MeasuredIterations:N0} resolves/profile):");
|
||||
_output.WriteLine($" player: {player,8:N0} B/resolve");
|
||||
_output.WriteLine(
|
||||
$" grounded: {groundedPublication,8:N0} B/resolve");
|
||||
_output.WriteLine($" remote: {remote,8:N0} B/resolve");
|
||||
_output.WriteLine($" projectile: {projectile,8:N0} B/resolve");
|
||||
_output.WriteLine($" camera: {camera,8:N0} B/resolve");
|
||||
|
||||
// The current graph path constructs a Transition object graph and
|
||||
// CollisionSphere helpers on every resolve. I1 replaces that lifetime
|
||||
// shape; this lower bound merely proves the baseline capture actually
|
||||
// observed those allocations instead of an optimized-away call.
|
||||
Assert.All(
|
||||
new[] { player, remote, projectile, camera },
|
||||
bytes => Assert.True(
|
||||
bytes > 0,
|
||||
"The I0 baseline must observe the current per-resolve allocation."));
|
||||
Assert.Equal(0, player);
|
||||
Assert.Equal(0, groundedPublication);
|
||||
Assert.Equal(0, remote);
|
||||
Assert.Equal(0, projectile);
|
||||
Assert.Equal(0, camera);
|
||||
}
|
||||
|
||||
private static long Measure(Func<ResolveResult> resolve)
|
||||
|
|
@ -106,6 +107,24 @@ public sealed class TransitionAllocationBaselineTests
|
|||
movingEntityId: 0x80000001u);
|
||||
}
|
||||
|
||||
private static ResolveResult ResolveGroundedPublication(PhysicsEngine engine)
|
||||
{
|
||||
var body = Bodies.Grounded;
|
||||
ResetBody(body, PhysicsStateFlags.Gravity);
|
||||
return engine.ResolveWithTransition(
|
||||
currentPos: new Vector3(8f, 8f, 0f),
|
||||
targetPos: new Vector3(8.12f, 8f, 0f),
|
||||
cellId: CellId,
|
||||
sphereRadius: BSPStepUpFixtures.SphereRadius,
|
||||
sphereHeight: 1.20f,
|
||||
stepUpHeight: 0.40f,
|
||||
stepDownHeight: 1.50f,
|
||||
isOnGround: true,
|
||||
body: body,
|
||||
moverFlags: ObjectInfoState.IsPlayer | ObjectInfoState.EdgeSlide,
|
||||
movingEntityId: 0x5000000Bu);
|
||||
}
|
||||
|
||||
private static ResolveResult ResolveProjectile(PhysicsEngine engine)
|
||||
{
|
||||
var body = Bodies.Projectile;
|
||||
|
|
@ -197,9 +216,25 @@ public sealed class TransitionAllocationBaselineTests
|
|||
return engine;
|
||||
}
|
||||
|
||||
private static PhysicsEngine BuildGroundEngine()
|
||||
{
|
||||
var heights = new byte[81];
|
||||
var heightTable = new float[256];
|
||||
var engine = new PhysicsEngine();
|
||||
engine.AddLandblock(
|
||||
0xA9B4FFFFu,
|
||||
new TerrainSurface(heights, heightTable),
|
||||
Array.Empty<CellSurface>(),
|
||||
Array.Empty<PortalPlane>(),
|
||||
0f,
|
||||
0f);
|
||||
return engine;
|
||||
}
|
||||
|
||||
private static class Bodies
|
||||
{
|
||||
internal static readonly PhysicsBody Player = new();
|
||||
internal static readonly PhysicsBody Grounded = new();
|
||||
internal static readonly PhysicsBody Remote = new();
|
||||
internal static readonly PhysicsBody Projectile = new();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue