Unstable 0.2 Defer physics operations during parallel updates

Introduces a thread-safe queue for deferring physics operations (such as body creation and transforms) to the main thread, ensuring Farseer Physics is not accessed from parallel contexts. Updates Holdable, Item, MapEntity, and GameScreen to use the new PhysicsBodyQueue for safe physics operations during parallel updates, and refactors PhysicsBodyQueue to support general deferred physics actions.
This commit is contained in:
Eero
2025-12-28 15:10:06 +08:00
parent 49355fe32b
commit f485583621
5 changed files with 261 additions and 46 deletions
@@ -660,7 +660,15 @@ namespace Barotrauma
{
Parallel.ForEach(hullList, parallelOptions, hull =>
{
hull.Update(deltaTime, cam);
PhysicsBodyQueue.IsInParallelContext = true;
try
{
hull.Update(deltaTime, cam);
}
finally
{
PhysicsBodyQueue.IsInParallelContext = false;
}
});
},
// Structure parallel update
@@ -668,7 +676,15 @@ namespace Barotrauma
{
Parallel.ForEach(structureList, parallelOptions, structure =>
{
structure.Update(deltaTime, cam);
PhysicsBodyQueue.IsInParallelContext = true;
try
{
structure.Update(deltaTime, cam);
}
finally
{
PhysicsBodyQueue.IsInParallelContext = false;
}
});
},
// Gap reset (must be done before update)
@@ -686,9 +702,9 @@ namespace Barotrauma
}
);
// Process any physics body creation operations queued during Hull/Structure updates.
// BallastFlora growth (from Hull.Update) may queue physics body creations.
PhysicsBodyQueue.ProcessPendingCreations();
// Process any physics operations queued during Hull/Structure updates.
// BallastFlora growth (from Hull.Update) may queue physics body creations/transforms.
PhysicsBodyQueue.ProcessPendingOperations();
#if CLIENT
// Hull Cheats need to be executed after Hull update
@@ -699,8 +715,19 @@ namespace Barotrauma
var shuffledGaps = gapList.OrderBy(g => Rand.Int(int.MaxValue)).ToList();
Parallel.ForEach(gapList, parallelOptions, gap =>
{
gap.Update(deltaTime, cam);
PhysicsBodyQueue.IsInParallelContext = true;
try
{
gap.Update(deltaTime, cam);
}
finally
{
PhysicsBodyQueue.IsInParallelContext = false;
}
});
// Process any physics operations queued during Gap updates.
PhysicsBodyQueue.ProcessPendingOperations();
#if CLIENT
sw.Stop();
@@ -718,8 +745,16 @@ namespace Barotrauma
{
Parallel.ForEach(itemList, parallelOptions, item =>
{
lastUpdatedItem = item;
item.Update(scaledDeltaTime, cam);
PhysicsBodyQueue.IsInParallelContext = true;
try
{
lastUpdatedItem = item;
item.Update(scaledDeltaTime, cam);
}
finally
{
PhysicsBodyQueue.IsInParallelContext = false;
}
});
}
catch (InvalidOperationException e)
@@ -731,9 +766,9 @@ namespace Barotrauma
throw new InvalidOperationException($"Error while updating item {lastUpdatedItem?.Name ?? "null"}", innerException: e);
}
// Process any physics body creation operations that were queued during the parallel update.
// Process any physics operations that were queued during the parallel update.
// This must be done on the main thread because Farseer Physics is not thread-safe.
PhysicsBodyQueue.ProcessPendingCreations();
PhysicsBodyQueue.ProcessPendingOperations();
UpdateAllProjSpecific(scaledDeltaTime);
Spawner?.Update();