fix(physics): seal collision generations before activation
This commit is contained in:
parent
be94bc9b06
commit
d94145e6b8
15 changed files with 1556 additions and 410 deletions
|
|
@ -322,7 +322,7 @@ public sealed class RuntimePhysicsStateTests
|
|||
prepared,
|
||||
CollisionAssets(0xA9B4FFFFu));
|
||||
RuntimeCollisionGenerationCommit commit =
|
||||
first.Physics.CommitCollisionGeneration(newer, prepared);
|
||||
CommitPrepared(first.Physics, newer, prepared);
|
||||
RuntimeCollisionAcknowledgement completed = commit.Acknowledgement;
|
||||
|
||||
Assert.True(commit.Committed);
|
||||
|
|
@ -332,7 +332,7 @@ public sealed class RuntimePhysicsStateTests
|
|||
0,
|
||||
first.Physics.CaptureOwnership().CollisionAdmissionCount);
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
first.Physics.CommitCollisionGeneration(newer, prepared));
|
||||
CommitPrepared(first.Physics, newer, prepared));
|
||||
|
||||
RuntimeCollisionAcknowledgement withdrawn =
|
||||
first.Physics.WithdrawCollision(0xA9B4FFFFu);
|
||||
|
|
@ -355,7 +355,7 @@ public sealed class RuntimePhysicsStateTests
|
|||
firstAdmission,
|
||||
first,
|
||||
CollisionAssets(0xA9B4FFFFu, terrainHeight: 10f));
|
||||
Assert.True(physics.CommitCollisionGeneration(
|
||||
Assert.True(CommitPrepared(physics,
|
||||
firstAdmission,
|
||||
first).Committed);
|
||||
}
|
||||
|
|
@ -375,7 +375,7 @@ public sealed class RuntimePhysicsStateTests
|
|||
Assert.Equal(0, notifications);
|
||||
|
||||
RuntimeCollisionGenerationCommit committed =
|
||||
physics.CommitCollisionGeneration(
|
||||
CommitPrepared(physics,
|
||||
replacementAdmission,
|
||||
replacement);
|
||||
|
||||
|
|
@ -401,7 +401,11 @@ public sealed class RuntimePhysicsStateTests
|
|||
RuntimeCollisionAdmission current =
|
||||
physics.BeginCollisionAdmission(0xA9B4FFFFu);
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
physics.CommitCollisionGeneration(stale, preparedStale));
|
||||
CommitPrepared(physics, stale, preparedStale));
|
||||
physics.CancelCollisionGeneration(stale, preparedStale);
|
||||
Assert.Equal(
|
||||
1,
|
||||
physics.CaptureOwnership().CollisionAdmissionCount);
|
||||
Assert.False(physics.Engine.IsLandblockTerrainResident(0xA9B4FFFFu));
|
||||
|
||||
using PreparedLandblockCollisionGeneration preparedCurrent =
|
||||
|
|
@ -410,14 +414,14 @@ public sealed class RuntimePhysicsStateTests
|
|||
current,
|
||||
preparedCurrent,
|
||||
CollisionAssets(0xA9B4FFFFu, terrainHeight: 20f));
|
||||
Assert.True(physics.CommitCollisionGeneration(
|
||||
Assert.True(CommitPrepared(physics,
|
||||
current,
|
||||
preparedCurrent).Committed);
|
||||
Assert.Equal(20f, physics.Engine.SampleTerrainZ(1f, 1f));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MovingDynamicOwnerDuringStagingMustRefreshBeforeCommit()
|
||||
public void WithdrawnOwnerStateChangeRejectsSealUntilRefreshed()
|
||||
{
|
||||
using var lifetime = new RuntimeEntityObjectLifetime();
|
||||
RuntimePhysicsState physics = lifetime.Physics;
|
||||
|
|
@ -430,7 +434,7 @@ public sealed class RuntimePhysicsStateTests
|
|||
initialAdmission,
|
||||
initial,
|
||||
CollisionAssets(0x0101FFFFu, terrainHeight: 5f));
|
||||
Assert.True(physics.CommitCollisionGeneration(
|
||||
Assert.True(CommitPrepared(physics,
|
||||
initialAdmission,
|
||||
initial).Committed);
|
||||
}
|
||||
|
|
@ -445,6 +449,9 @@ public sealed class RuntimePhysicsStateTests
|
|||
0x0101FFFFu,
|
||||
seedCellId: 0x01010001u,
|
||||
isStatic: false);
|
||||
physics.Engine.ShadowObjects.RemoveLandblock(0x0101FFFFu);
|
||||
Assert.Equal(0, physics.Engine.ShadowObjects.TotalRegistered);
|
||||
Assert.Equal(1, physics.Engine.ShadowObjects.RetainedRegistrationCount);
|
||||
|
||||
RuntimeCollisionAdmission admission =
|
||||
physics.BeginCollisionAdmission(0x0101FFFFu);
|
||||
|
|
@ -454,33 +461,125 @@ public sealed class RuntimePhysicsStateTests
|
|||
admission,
|
||||
prepared,
|
||||
CollisionAssets(0x0101FFFFu, terrainHeight: 15f));
|
||||
uint owner = Assert.Single(physics.CaptureCollisionDynamicOwners(
|
||||
admission,
|
||||
prepared));
|
||||
physics.RefreshCollisionDynamicOwner(admission, prepared, owner);
|
||||
physics.Engine.ShadowObjects.UpdatePosition(
|
||||
owner,
|
||||
new Vector3(30f, 10f, 0f),
|
||||
Quaternion.Identity,
|
||||
0f,
|
||||
0f,
|
||||
0x0101FFFFu,
|
||||
seedCellId: 0x01010009u);
|
||||
uint owner = Assert.Single(SealPrepared(physics, admission, prepared));
|
||||
physics.Engine.ShadowObjects.UpdatePhysicsState(owner, 0x14u);
|
||||
|
||||
RuntimeCollisionGenerationCommit rejected =
|
||||
physics.CommitCollisionGeneration(admission, prepared);
|
||||
Assert.False(rejected.Committed);
|
||||
Assert.Equal(owner, Assert.Single(rejected.DirtyDynamicOwnerIds));
|
||||
Assert.Equal(5f, physics.Engine.SampleTerrainZ(1f, 1f));
|
||||
|
||||
physics.RefreshCollisionDynamicOwner(admission, prepared, owner);
|
||||
Assert.Equal(owner, Assert.Single(SealPrepared(
|
||||
physics,
|
||||
admission,
|
||||
prepared)));
|
||||
Assert.True(physics.CommitCollisionGeneration(
|
||||
admission,
|
||||
prepared).Committed);
|
||||
Assert.Equal(15f, physics.Engine.SampleTerrainZ(1f, 1f));
|
||||
Assert.Contains(
|
||||
physics.Engine.ShadowObjects.GetObjectsInCell(0x01010009u),
|
||||
entry => entry.EntityId == owner);
|
||||
ShadowEntry restored = Assert.Single(
|
||||
physics.Engine.ShadowObjects.AllEntriesForDebug());
|
||||
Assert.Equal(owner, restored.EntityId);
|
||||
Assert.Equal(0x14u, restored.State);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DenseReplacementSealsOneWorkUnitPerStepAndActivatesWithoutAllocation()
|
||||
{
|
||||
using var lifetime = new RuntimeEntityObjectLifetime();
|
||||
RuntimePhysicsState physics = lifetime.Physics;
|
||||
const uint landblockId = 0x0101FFFFu;
|
||||
RuntimeCollisionAdmission initialAdmission =
|
||||
physics.BeginCollisionAdmission(landblockId);
|
||||
using (PreparedLandblockCollisionGeneration initial =
|
||||
physics.PrepareCollisionGeneration(initialAdmission))
|
||||
{
|
||||
physics.StageCollisionAssets(
|
||||
initialAdmission,
|
||||
initial,
|
||||
CollisionAssets(landblockId, terrainHeight: 5f));
|
||||
Assert.True(CommitPrepared(
|
||||
physics,
|
||||
initialAdmission,
|
||||
initial).Committed);
|
||||
}
|
||||
|
||||
const int ownerCount = 256;
|
||||
for (uint index = 0; index < ownerCount; index++)
|
||||
{
|
||||
physics.Engine.ShadowObjects.Register(
|
||||
entityId: 1000u + index,
|
||||
gfxObjId: 0x01000001u,
|
||||
worldPos: new Vector3(
|
||||
8f + (index % 16u) * 0.25f,
|
||||
8f + (index / 16u) * 0.25f,
|
||||
0f),
|
||||
rotation: Quaternion.Identity,
|
||||
radius: 0.5f,
|
||||
worldOffsetX: 0f,
|
||||
worldOffsetY: 0f,
|
||||
landblockId,
|
||||
seedCellId: 0x01010001u,
|
||||
isStatic: false);
|
||||
}
|
||||
|
||||
RuntimeCollisionAdmission admission =
|
||||
physics.BeginCollisionAdmission(landblockId);
|
||||
using PreparedLandblockCollisionGeneration prepared =
|
||||
physics.PrepareCollisionGeneration(admission);
|
||||
physics.StageCollisionAssets(
|
||||
admission,
|
||||
prepared,
|
||||
CollisionAssets(landblockId, terrainHeight: 15f));
|
||||
|
||||
int captureSteps = 0;
|
||||
RuntimeCollisionOwnerCaptureStep capture;
|
||||
do
|
||||
{
|
||||
capture = physics.AdvanceCollisionRetainedOwnerCapture(
|
||||
admission,
|
||||
prepared);
|
||||
captureSteps++;
|
||||
}
|
||||
while (!capture.Completed);
|
||||
Assert.True(captureSteps >= ownerCount);
|
||||
Assert.Equal(ownerCount, prepared.RetainedOwnerIds.Count);
|
||||
foreach (uint ownerId in prepared.RetainedOwnerIds)
|
||||
{
|
||||
physics.RefreshCollisionRetainedOwner(
|
||||
admission,
|
||||
prepared,
|
||||
ownerId);
|
||||
}
|
||||
|
||||
int sealSteps = 0;
|
||||
int workUnits = 0;
|
||||
RuntimeCollisionSealStep seal;
|
||||
do
|
||||
{
|
||||
seal = physics.AdvanceCollisionGenerationSeal(
|
||||
admission,
|
||||
prepared);
|
||||
sealSteps++;
|
||||
Assert.InRange(seal.WorkUnits, 0, 1);
|
||||
workUnits += seal.WorkUnits;
|
||||
}
|
||||
while (!seal.Completed);
|
||||
Assert.False(seal.Restarted);
|
||||
Assert.True(sealSteps > ownerCount);
|
||||
Assert.True(workUnits > ownerCount);
|
||||
|
||||
_ = GC.GetAllocatedBytesForCurrentThread();
|
||||
long before = GC.GetAllocatedBytesForCurrentThread();
|
||||
RuntimeCollisionGenerationCommit commit =
|
||||
physics.CommitCollisionGeneration(admission, prepared);
|
||||
long allocated =
|
||||
GC.GetAllocatedBytesForCurrentThread() - before;
|
||||
|
||||
Assert.True(commit.Committed);
|
||||
Assert.Equal(0L, allocated);
|
||||
Assert.Equal(15f, physics.Engine.SampleTerrainZ(1f, 1f));
|
||||
Assert.Equal(ownerCount, physics.Engine.ShadowObjects.TotalRegistered);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -496,7 +595,7 @@ public sealed class RuntimePhysicsStateTests
|
|||
admission,
|
||||
prepared,
|
||||
CollisionAssets(0x0101FFFFu, terrainHeight: 12f));
|
||||
Assert.Empty(physics.CaptureCollisionDynamicOwners(admission, prepared));
|
||||
Assert.Empty(SealPrepared(physics, admission, prepared));
|
||||
|
||||
physics.Engine.ShadowObjects.Register(
|
||||
77u,
|
||||
|
|
@ -512,16 +611,17 @@ public sealed class RuntimePhysicsStateTests
|
|||
RuntimeCollisionGenerationCommit spawned =
|
||||
physics.CommitCollisionGeneration(admission, prepared);
|
||||
Assert.False(spawned.Committed);
|
||||
Assert.Equal(77u, Assert.Single(spawned.DirtyDynamicOwnerIds));
|
||||
|
||||
physics.RefreshCollisionDynamicOwner(admission, prepared, 77u);
|
||||
Assert.Equal(77u, Assert.Single(SealPrepared(
|
||||
physics,
|
||||
admission,
|
||||
prepared)));
|
||||
physics.Engine.ShadowObjects.Deregister(77u);
|
||||
RuntimeCollisionGenerationCommit deleted =
|
||||
physics.CommitCollisionGeneration(admission, prepared);
|
||||
Assert.False(deleted.Committed);
|
||||
Assert.Equal(77u, Assert.Single(deleted.DirtyDynamicOwnerIds));
|
||||
|
||||
physics.RefreshCollisionDynamicOwner(admission, prepared, 77u);
|
||||
Assert.Empty(SealPrepared(physics, admission, prepared));
|
||||
Assert.True(physics.CommitCollisionGeneration(
|
||||
admission,
|
||||
prepared).Committed);
|
||||
|
|
@ -546,7 +646,7 @@ public sealed class RuntimePhysicsStateTests
|
|||
admission,
|
||||
prepared,
|
||||
CollisionAssets(0x0101FFFFu));
|
||||
_ = lifetime.Physics.CommitCollisionGeneration(admission, prepared);
|
||||
_ = CommitPrepared(lifetime.Physics, admission, prepared);
|
||||
lifetime.Physics.Engine.ShadowObjects.Register(
|
||||
entityId: record.LocalEntityId!.Value,
|
||||
gfxObjId: 0x01000001u,
|
||||
|
|
@ -930,6 +1030,49 @@ public sealed class RuntimePhysicsStateTests
|
|||
Assert.Equal(record.SpatialAuthorityVersion, observed.SpatialAuthorityVersion);
|
||||
}
|
||||
|
||||
private static RuntimeCollisionGenerationCommit CommitPrepared(
|
||||
RuntimePhysicsState physics,
|
||||
RuntimeCollisionAdmission admission,
|
||||
PreparedLandblockCollisionGeneration prepared)
|
||||
{
|
||||
_ = SealPrepared(physics, admission, prepared);
|
||||
return physics.CommitCollisionGeneration(admission, prepared);
|
||||
}
|
||||
|
||||
private static uint[] SealPrepared(
|
||||
RuntimePhysicsState physics,
|
||||
RuntimeCollisionAdmission admission,
|
||||
PreparedLandblockCollisionGeneration prepared)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
while (!physics.AdvanceCollisionRetainedOwnerCapture(
|
||||
admission,
|
||||
prepared).Completed)
|
||||
{
|
||||
}
|
||||
foreach (uint ownerId in prepared.RetainedOwnerIds)
|
||||
{
|
||||
physics.RefreshCollisionRetainedOwner(
|
||||
admission,
|
||||
prepared,
|
||||
ownerId);
|
||||
}
|
||||
RuntimeCollisionSealStep seal;
|
||||
do
|
||||
{
|
||||
seal = physics.AdvanceCollisionGenerationSeal(
|
||||
admission,
|
||||
prepared);
|
||||
Assert.InRange(seal.WorkUnits, 0, 1);
|
||||
}
|
||||
while (!seal.Completed && !seal.Restarted);
|
||||
if (seal.Completed)
|
||||
break;
|
||||
}
|
||||
return [.. prepared.RetainedOwnerIds];
|
||||
}
|
||||
|
||||
private static RuntimeLandblockCollisionAssets CollisionAssets(
|
||||
uint landblockId,
|
||||
float terrainHeight = 0f)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue