Turn broadphase contacts into a job (#5245)

Okay when I said no more physics this was a low-hanging fruit as we can get rid of the mapmanager getmapentityid for every contact so.
This commit is contained in:
metalgearsloth
2024-06-20 17:19:26 +10:00
committed by GitHub
parent 14138fbcc2
commit 095fe9d60f

View File

@@ -31,11 +31,14 @@ namespace Robust.Shared.Physics.Systems
[Dependency] private readonly SharedTransformSystem _transform = default!;
private EntityQuery<BroadphaseComponent> _broadphaseQuery;
private EntityQuery<FixturesComponent> _fixturesQuery;
private EntityQuery<MapGridComponent> _gridQuery;
private EntityQuery<PhysicsComponent> _physicsQuery;
private EntityQuery<TransformComponent> _xformQuery;
private EntityQuery<PhysicsMapComponent> _mapQuery;
private float _broadphaseExpand;
/*
* Okay so Box2D has its own "MoveProxy" stuff so you can easily find new contacts when required.
* Our problem is that we have nested broadphases (rather than being on separate maps) which makes this
@@ -43,23 +46,21 @@ namespace Robust.Shared.Physics.Systems
* Hence we need to check which broadphases it does intersect and checkar for colliding bodies.
*/
/// <summary>
/// How much to expand bounds by to check cross-broadphase collisions.
/// Ideally you want to set this to your largest body size.
/// This only has a noticeable performance impact where multiple broadphases are in close proximity.
/// </summary>
private float _broadphaseExpand;
private const int PairBufferParallel = 8;
private ObjectPool<List<FixtureProxy>> _bufferPool =
new DefaultObjectPool<List<FixtureProxy>>(new ListPolicy<FixtureProxy>(), 2048);
private BroadphaseContactJob _contactJob;
public override void Initialize()
{
base.Initialize();
_contactJob = new()
{
_mapManager = _mapManager,
System = this,
BroadphaseExpand = _broadphaseExpand,
};
_broadphaseQuery = GetEntityQuery<BroadphaseComponent>();
_fixturesQuery = GetEntityQuery<FixturesComponent>();
_gridQuery = GetEntityQuery<MapGridComponent>();
_physicsQuery = GetEntityQuery<PhysicsComponent>();
_xformQuery = GetEntityQuery<TransformComponent>();
@@ -71,7 +72,11 @@ namespace Robust.Shared.Physics.Systems
Subs.CVar(_cfg, CVars.BroadphaseExpand, SetBroadphaseExpand, true);
}
private void SetBroadphaseExpand(float value) => _broadphaseExpand = value;
private void SetBroadphaseExpand(float value)
{
_contactJob.BroadphaseExpand = value;
_broadphaseExpand = value;
}
#region Find Contacts
@@ -176,65 +181,34 @@ namespace Robust.Shared.Physics.Systems
if (moveBuffer.Count == 0)
return;
var count = moveBuffer.Count;
var contactBuffer = ArrayPool<List<FixtureProxy>>.Shared.Rent(count);
var pMoveBuffer = ArrayPool<(FixtureProxy Proxy, Box2 AABB)>.Shared.Rent(count);
var idx = 0;
_contactJob.MapUid = _mapManager.GetMapEntityIdOrThrow(mapId);
_contactJob.MoveBuffer.Clear();
foreach (var (proxy, aabb) in moveBuffer)
{
contactBuffer[idx] = _bufferPool.Get();
pMoveBuffer[idx++] = (proxy, aabb);
_contactJob.MoveBuffer.Add((proxy, aabb));
}
var options = new ParallelOptions
for (var i = _contactJob.ContactBuffer.Count; i < _contactJob.MoveBuffer.Count; i++)
{
MaxDegreeOfParallelism = _parallel.ParallelProcessCount,
};
_contactJob.ContactBuffer.Add(new List<FixtureProxy>());
}
var batches = (int)MathF.Ceiling((float) count / PairBufferParallel);
var count = moveBuffer.Count;
Parallel.For(0, batches, options, i =>
{
var start = i * PairBufferParallel;
var end = Math.Min(start + PairBufferParallel, count);
for (var j = start; j < end; j++)
{
var (proxy, worldAABB) = pMoveBuffer[j];
var buffer = contactBuffer[j];
var proxyBody = proxy.Body;
DebugTools.Assert(!proxyBody.Deleted);
var state = (this, proxy, worldAABB, buffer);
// Get every broadphase we may be intersecting.
_mapManager.FindGridsIntersecting(mapId, worldAABB.Enlarged(_broadphaseExpand), ref state,
static (EntityUid uid, MapGridComponent _, ref (
SharedBroadphaseSystem system,
FixtureProxy proxy,
Box2 worldAABB,
List<FixtureProxy> pairBuffer) tuple) =>
{
ref var buffer = ref tuple.pairBuffer;
tuple.system.FindPairs(tuple.proxy, tuple.worldAABB, uid, buffer);
return true;
}, approx: true, includeMap: false);
// Struct ref moment, I have no idea what's fastest.
buffer = state.buffer;
FindPairs(proxy, worldAABB, _mapManager.GetMapEntityId(mapId), buffer);
}
});
_parallel.ProcessNow(_contactJob, count);
for (var i = 0; i < count; i++)
{
var proxyA = pMoveBuffer[i].Proxy;
var proxies = contactBuffer[i];
var proxies = _contactJob.ContactBuffer[i];
if (proxies.Count == 0)
continue;
var proxyA = _contactJob.MoveBuffer[i].Proxy;
var proxyABody = proxyA.Body;
FixturesComponent? manager = null;
_fixturesQuery.TryGetComponent(proxyA.Entity, out var manager);
foreach (var other in proxies)
{
@@ -253,13 +227,8 @@ namespace Robust.Shared.Physics.Systems
_physicsSystem.AddPair(proxyA.FixtureId, other.FixtureId, proxyA, other);
}
_bufferPool.Return(contactBuffer[i]);
pMoveBuffer[i] = default;
}
ArrayPool<List<FixtureProxy>>.Shared.Return(contactBuffer);
ArrayPool<(FixtureProxy Proxy, Box2 AABB)>.Shared.Return(pMoveBuffer);
moveBuffer.Clear();
movedGrids.Clear();
}
@@ -516,5 +485,51 @@ namespace Robust.Shared.Physics.Systems
}
}
}
private record struct BroadphaseContactJob() : IParallelRobustJob
{
public SharedBroadphaseSystem System = default!;
public IMapManager _mapManager = default!;
public float BroadphaseExpand;
public EntityUid MapUid;
public List<List<FixtureProxy>> ContactBuffer = new();
public List<(FixtureProxy Proxy, Box2 WorldAABB)> MoveBuffer = new();
public int BatchSize => 8;
public void Execute(int index)
{
var (proxy, worldAABB) = MoveBuffer[index];
var buffer = ContactBuffer[index];
buffer.Clear();
var proxyBody = proxy.Body;
DebugTools.Assert(!proxyBody.Deleted);
var state = (System, proxy, worldAABB, buffer);
// Get every broadphase we may be intersecting.
_mapManager.FindGridsIntersecting(MapUid, worldAABB.Enlarged(BroadphaseExpand), ref state,
static (EntityUid uid, MapGridComponent _, ref (
SharedBroadphaseSystem system,
FixtureProxy proxy,
Box2 worldAABB,
List<FixtureProxy> pairBuffer) tuple) =>
{
ref var buffer = ref tuple.pairBuffer;
tuple.system.FindPairs(tuple.proxy, tuple.worldAABB, uid, buffer);
return true;
},
approx: true,
includeMap: false);
// Struct ref moment, I have no idea what's fastest.
buffer = state.buffer;
System.FindPairs(proxy, worldAABB, MapUid, buffer);
}
}
}
}