Improve parallelization in map and game screen updates
Refactored update logic in MapEntity and GameScreen to use more granular and conditional parallelization, reducing unnecessary allocations and improving performance. Updates to hulls, structures, items, and physics bodies are now executed in parallel where safe, and item updates are only performed when necessary. Also parallelized submarine and physics body transform updates.
This commit is contained in:
@@ -652,28 +652,34 @@ namespace Barotrauma
|
|||||||
bool shouldUpdatePower = mapEntityUpdateTick % PoweredUpdateInterval == 0;
|
bool shouldUpdatePower = mapEntityUpdateTick % PoweredUpdateInterval == 0;
|
||||||
|
|
||||||
// Buffer lists to avoid repeated allocations
|
// Buffer lists to avoid repeated allocations
|
||||||
List<Hull> hullList = new List<Hull>(Hull.HullList);
|
var hullList = shouldUpdateMapEntities ? Hull.HullList.ToList() : null;
|
||||||
List<Structure> structureList = new List<Structure>(Structure.WallList);
|
var structureList = shouldUpdateMapEntities ? Structure.WallList.ToList() : null;
|
||||||
List<Gap> gapList = new List<Gap>(Gap.GapList);
|
var gapList = Gap.GapList.ToList();
|
||||||
List<Item> itemList = new List<Item>(Item.ItemList);
|
var itemList = shouldUpdateMapEntities ? Item.ItemList.ToList() : null;
|
||||||
|
|
||||||
// First phase: parallel updates that have no order dependencies
|
// First phase: parallel updates that have no order dependencies
|
||||||
Parallel.Invoke(parallelOptions,
|
Parallel.Invoke(parallelOptions,
|
||||||
// Hull parallel update
|
// Hull parallel update
|
||||||
() =>
|
() =>
|
||||||
{
|
{
|
||||||
Parallel.ForEach(hullList, parallelOptions, hull =>
|
if (shouldUpdateMapEntities && hullList != null)
|
||||||
{
|
{
|
||||||
hull.Update(deltaTime, cam);
|
Parallel.ForEach(hullList, parallelOptions, hull =>
|
||||||
});
|
{
|
||||||
|
hull.Update(deltaTime * MapEntityUpdateInterval, cam);
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// Structure parallel update
|
// Structure parallel update
|
||||||
() =>
|
() =>
|
||||||
{
|
{
|
||||||
Parallel.ForEach(structureList, parallelOptions, structure =>
|
if (shouldUpdateMapEntities && structureList != null)
|
||||||
{
|
{
|
||||||
structure.Update(deltaTime, cam);
|
Parallel.ForEach(structureList, parallelOptions, structure =>
|
||||||
});
|
{
|
||||||
|
structure.Update(deltaTime * MapEntityUpdateInterval, cam);
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// Gap reset (must be done before update)
|
// Gap reset (must be done before update)
|
||||||
() =>
|
() =>
|
||||||
@@ -688,7 +694,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (shouldUpdatePower)
|
if (shouldUpdatePower)
|
||||||
{
|
{
|
||||||
Powered.UpdatePower(deltaTime);
|
Powered.UpdatePower(deltaTime * PoweredUpdateInterval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -715,30 +721,33 @@ namespace Barotrauma
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Item update (Item.Update() is not thread-safe and must be executed on the main thread)
|
// Item update (Item.Update() is not thread-safe and must be executed on the main thread)
|
||||||
Item.UpdatePendingConditionUpdates(deltaTime);
|
if (shouldUpdateMapEntities && itemList != null)
|
||||||
|
|
||||||
float scaledDeltaTime = deltaTime;
|
|
||||||
Item lastUpdatedItem = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
foreach (Item item in itemList)
|
Item.UpdatePendingConditionUpdates(deltaTime);
|
||||||
|
|
||||||
|
float scaledDeltaTime = deltaTime * MapEntityUpdateInterval;
|
||||||
|
Item lastUpdatedItem = null;
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
lastUpdatedItem = item;
|
foreach (Item item in itemList)
|
||||||
item.Update(scaledDeltaTime, cam);
|
{
|
||||||
|
lastUpdatedItem = item;
|
||||||
|
item.Update(scaledDeltaTime, cam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException e)
|
||||||
|
{
|
||||||
|
GameAnalyticsManager.AddErrorEventOnce(
|
||||||
|
"MapEntity.UpdateAll:ItemUpdateInvalidOperation",
|
||||||
|
GameAnalyticsManager.ErrorSeverity.Critical,
|
||||||
|
$"Error while updating item {lastUpdatedItem?.Name ?? "null"}: {e.Message}");
|
||||||
|
throw new InvalidOperationException($"Error while updating item {lastUpdatedItem?.Name ?? "null"}", innerException: e);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (InvalidOperationException e)
|
|
||||||
{
|
|
||||||
GameAnalyticsManager.AddErrorEventOnce(
|
|
||||||
"MapEntity.UpdateAll:ItemUpdateInvalidOperation",
|
|
||||||
GameAnalyticsManager.ErrorSeverity.Critical,
|
|
||||||
$"Error while updating item {lastUpdatedItem?.Name ?? "null"}: {e.Message}");
|
|
||||||
throw new InvalidOperationException($"Error while updating item {lastUpdatedItem?.Name ?? "null"}", innerException: e);
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateAllProjSpecific(scaledDeltaTime);
|
UpdateAllProjSpecific(scaledDeltaTime);
|
||||||
Spawner?.Update();
|
Spawner?.Update();
|
||||||
|
}
|
||||||
|
|
||||||
#if CLIENT
|
#if CLIENT
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
|
|||||||
@@ -151,16 +151,23 @@ namespace Barotrauma
|
|||||||
|
|
||||||
var physicsBodies = PhysicsBody.List.ToList();
|
var physicsBodies = PhysicsBody.List.ToList();
|
||||||
|
|
||||||
Parallel.ForEach(physicsBodies, parallelOptions, body =>
|
Parallel.Invoke(parallelOptions,
|
||||||
|
() =>
|
||||||
{
|
{
|
||||||
if ((body.Enabled || body.UserData is Character) &&
|
Parallel.ForEach(physicsBodies, parallelOptions, body =>
|
||||||
body.BodyType != BodyType.Static)
|
{
|
||||||
{
|
if ((body.Enabled || body.UserData is Character) &&
|
||||||
body.Update();
|
body.BodyType != BodyType.Static)
|
||||||
}
|
{
|
||||||
});
|
body.Update();
|
||||||
|
}
|
||||||
GameMain.GameSession?.Update((float)deltaTime);
|
});
|
||||||
|
},
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
GameMain.GameSession?.Update((float)deltaTime);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
foreach (PhysicsBody body in physicsBodies)
|
foreach (PhysicsBody body in physicsBodies)
|
||||||
{
|
{
|
||||||
@@ -176,8 +183,10 @@ namespace Barotrauma
|
|||||||
var sw = new System.Diagnostics.Stopwatch();
|
var sw = new System.Diagnostics.Stopwatch();
|
||||||
sw.Start();
|
sw.Start();
|
||||||
|
|
||||||
GameMain.ParticleManager.Update((float)deltaTime);
|
Parallel.Invoke(parallelOptions,
|
||||||
if (Level.Loaded != null) Level.Loaded.Update((float)deltaTime, cam);
|
() => GameMain.ParticleManager.Update((float)deltaTime),
|
||||||
|
() => { if (Level.Loaded != null) Level.Loaded.Update((float)deltaTime, cam); }
|
||||||
|
);
|
||||||
|
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
GameMain.PerformanceCounter.AddElapsedTicks("Update:Particles+Level", sw.ElapsedTicks);
|
GameMain.PerformanceCounter.AddElapsedTicks("Update:Particles+Level", sw.ElapsedTicks);
|
||||||
@@ -243,25 +252,25 @@ namespace Barotrauma
|
|||||||
Character.Controlled?.UpdateLocalCursor(cam);
|
Character.Controlled?.UpdateLocalCursor(cam);
|
||||||
|
|
||||||
#elif SERVER
|
#elif SERVER
|
||||||
if (Level.Loaded != null) Level.Loaded.Update((float)deltaTime, Camera.Instance);
|
Parallel.Invoke(parallelOptions,
|
||||||
|
() => { if (Level.Loaded != null) Level.Loaded.Update((float)deltaTime, Camera.Instance); },
|
||||||
Character.UpdateAll((float)deltaTime, Camera.Instance);
|
() => Character.UpdateAll((float)deltaTime, Camera.Instance)
|
||||||
|
);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var submarines = Submarine.Loaded.ToList();
|
var submarines = Submarine.Loaded.ToList();
|
||||||
foreach(Submarine sub in submarines)
|
Parallel.ForEach(submarines, parallelOptions, sub =>
|
||||||
{
|
{
|
||||||
sub.SetPrevTransform(sub.Position);
|
sub.SetPrevTransform(sub.Position);
|
||||||
}
|
});
|
||||||
|
|
||||||
//
|
Parallel.ForEach(physicsBodies, parallelOptions, body =>
|
||||||
foreach (PhysicsBody body in physicsBodies)
|
|
||||||
{
|
{
|
||||||
if (body.Enabled && body.BodyType != FarseerPhysics.BodyType.Static)
|
if (body.Enabled && body.BodyType != FarseerPhysics.BodyType.Static)
|
||||||
{
|
{
|
||||||
body.SetPrevTransform(body.SimPosition, body.Rotation);
|
body.SetPrevTransform(body.SimPosition, body.Rotation);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
#if CLIENT
|
#if CLIENT
|
||||||
MapEntity.UpdateAll((float)deltaTime, cam, parallelOptions);
|
MapEntity.UpdateAll((float)deltaTime, cam, parallelOptions);
|
||||||
@@ -293,10 +302,11 @@ namespace Barotrauma
|
|||||||
GameMain.PerformanceCounter.AddElapsedTicks("Update:Ragdolls", sw.ElapsedTicks);
|
GameMain.PerformanceCounter.AddElapsedTicks("Update:Ragdolls", sw.ElapsedTicks);
|
||||||
sw.Restart();
|
sw.Restart();
|
||||||
#endif
|
#endif
|
||||||
foreach (var sub in submarines)
|
|
||||||
|
Parallel.ForEach(submarines, parallelOptions, sub =>
|
||||||
{
|
{
|
||||||
sub.Update((float)deltaTime);
|
sub.Update((float)deltaTime);
|
||||||
}
|
});
|
||||||
|
|
||||||
#if CLIENT
|
#if CLIENT
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
|
|||||||
Reference in New Issue
Block a user