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:
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user