Enable and improve threaded physics execution

Activated the RUN_PHYSICS_IN_SEPARATE_THREAD directive and refactored the physics threading logic. Added cancellation support, improved error handling, and used synchronization primitives to coordinate physics updates. These changes aim to improve performance and stability when running physics in a separate thread.
This commit is contained in:
eero
2025-12-22 00:16:25 +08:00
parent dc08b6b92a
commit 7d9642a5b1

View File

@@ -1,4 +1,4 @@
//#define RUN_PHYSICS_IN_SEPARATE_THREAD
#define RUN_PHYSICS_IN_SEPARATE_THREAD
using Microsoft.Xna.Framework;
using System.Threading;
@@ -6,10 +6,10 @@ using FarseerPhysics.Dynamics;
using FarseerPhysics;
using System.Threading.Tasks;
using System.Linq;
using System;
#if DEBUG && CLIENT
using System;
using Barotrauma.Sounds;
using Microsoft.Xna.Framework.Input;
#endif
@@ -18,9 +18,15 @@ namespace Barotrauma
{
partial class GameScreen : Screen
{
private object updateLock = new object();
private readonly object updateLock = new object();
private double physicsTime;
#if RUN_PHYSICS_IN_SEPARATE_THREAD
private CancellationTokenSource physicsCancellation;
private readonly AutoResetEvent physicsEvent = new AutoResetEvent(false);
private const int PHYSICS_WAIT_TIMEOUT_MS = 2;
#endif
#if CLIENT
private readonly Camera cam;
@@ -71,12 +77,13 @@ namespace Barotrauma
MapEntity.ClearHighlightedEntities();
Task PhysicsTask = Task.Factory.StartNew(() => ExecutePhysics());
#if RUN_PHYSICS_IN_SEPARATE_THREAD
physicsCancellation = new CancellationTokenSource();
var physicsThread = new Thread(ExecutePhysics)
{
Name = "Physics thread",
IsBackground = true
IsBackground = true,
Priority = ThreadPriority.AboveNormal
};
physicsThread.Start();
#endif
@@ -85,6 +92,12 @@ namespace Barotrauma
public override void Deselect()
{
base.Deselect();
#if RUN_PHYSICS_IN_SEPARATE_THREAD
physicsCancellation?.Cancel();
physicsEvent?.Set();
#endif
#if CLIENT
var config = GameSettings.CurrentConfig;
config.CrewMenuOpen = CrewManager.PreferCrewMenuOpen;
@@ -101,6 +114,7 @@ namespace Barotrauma
#endif
#endif
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
@@ -111,9 +125,11 @@ namespace Barotrauma
#warning For now CL side performence counter is partly useless bucz multiple changes on such things. Need time to take care of it
#if RUN_PHYSICS_IN_SEPARATE_THREAD
physicsTime += deltaTime;
lock (updateLock)
{
{
physicsTime += deltaTime;
}
physicsEvent?.Set();
#endif
@@ -334,27 +350,78 @@ namespace Barotrauma
GameMain.PerformanceCounter.AddElapsedTicks("Update:Physics", sw.ElapsedTicks);
#endif
UpdateProjSpecific(deltaTime);
#if RUN_PHYSICS_IN_SEPARATE_THREAD
}
#endif
}
partial void UpdateProjSpecific(double deltaTime);
private void ExecutePhysics()
{
while (true)
#if RUN_PHYSICS_IN_SEPARATE_THREAD
var token = physicsCancellation.Token;
try
{
while (physicsTime >= Timing.Step)
while (!token.IsCancellationRequested)
{
bool hasWork = false;
lock (updateLock)
{
GameMain.World.Step((float)Timing.Step);
physicsTime -= Timing.Step;
while (physicsTime >= Timing.Step)
{
try
{
GameMain.World.Step((float)Timing.Step);
physicsTime -= Timing.Step;
hasWork = true;
}
catch (WorldLockedException e)
{
string errorMsg = $"Physics thread WorldLockedException: {e.Message}\n{e.StackTrace}";
DebugConsole.ThrowError(errorMsg, e);
GameAnalyticsManager.AddErrorEventOnce(
"GameScreen.ExecutePhysics:WorldLockedException" + e.Message,
GameAnalyticsManager.ErrorSeverity.Error,
errorMsg);
break;
}
catch (Exception e)
{
string errorMsg = $"Physics step error: {e.Message}\n{e.StackTrace}";
DebugConsole.ThrowError(errorMsg, e);
GameAnalyticsManager.AddErrorEventOnce(
"GameScreen.ExecutePhysics:PhysicsStepError" + e.GetType().Name,
GameAnalyticsManager.ErrorSeverity.Error,
errorMsg);
break;
}
}
}
if (!hasWork)
{
physicsEvent.WaitOne(PHYSICS_WAIT_TIMEOUT_MS);
}
}
}
catch (ThreadAbortException)
{
DebugConsole.Log("Physics thread aborted.");
}
catch (Exception e)
{
string errorMsg = $"Fatal error in physics thread: {e.Message}\n{e.StackTrace}";
DebugConsole.ThrowError(errorMsg, e);
GameAnalyticsManager.AddErrorEventOnce(
"GameScreen.ExecutePhysics:FatalError",
GameAnalyticsManager.ErrorSeverity.Critical,
errorMsg);
}
finally
{
DebugConsole.Log("Physics thread terminated.");
}
#endif
}
}
}