structure for running engines in parallel
parent
fd43036b2b
commit
61057f1f44
|
@ -28,6 +28,7 @@ namespace Encompass
|
|||
|
||||
protected List<T> _vertices = new List<T>();
|
||||
protected Dictionary<T, HashSet<T>> _neighbors = new Dictionary<T, HashSet<T>>();
|
||||
protected Dictionary<T, HashSet<T>> _reverseNeighbors = new Dictionary<T, HashSet<T>>();
|
||||
|
||||
public IEnumerable<T> Vertices { get { return _vertices; } }
|
||||
|
||||
|
@ -41,6 +42,7 @@ namespace Encompass
|
|||
{
|
||||
_vertices.Add(vertex);
|
||||
_neighbors.Add(vertex, new HashSet<T>());
|
||||
_reverseNeighbors.Add(vertex, new HashSet<T>());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,6 +88,7 @@ namespace Encompass
|
|||
if (VertexExists(v) && VertexExists(u))
|
||||
{
|
||||
_neighbors[v].Add(u);
|
||||
_reverseNeighbors[u].Add(v);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,7 +113,19 @@ namespace Encompass
|
|||
}
|
||||
else
|
||||
{
|
||||
return Enumerable.Empty<T>();
|
||||
return Enumerable.Empty<T>(); // should throw instead
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<T> IncomingEdges(T vertex)
|
||||
{
|
||||
if (VertexExists(vertex))
|
||||
{
|
||||
return _reverseNeighbors[vertex];
|
||||
}
|
||||
else
|
||||
{
|
||||
return Enumerable.Empty<T>(); // should throw instead
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,7 +196,7 @@ namespace Encompass
|
|||
var distances = new Dictionary<T, int>();
|
||||
foreach (var node in Vertices)
|
||||
{
|
||||
distances[node] = int.MaxValue;
|
||||
distances[node] = int.MinValue;
|
||||
}
|
||||
distances[source] = 0;
|
||||
|
||||
|
@ -191,7 +206,7 @@ namespace Encompass
|
|||
{
|
||||
foreach (var neighbor in Neighbors(node))
|
||||
{
|
||||
if (distances[neighbor] > distances[node] + 1)
|
||||
if (distances[neighbor] < distances[node] + 1)
|
||||
{
|
||||
distances[neighbor] = distances[node] + 1;
|
||||
}
|
||||
|
|
|
@ -1,24 +1,26 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Encompass
|
||||
{
|
||||
public class World
|
||||
{
|
||||
private readonly List<Engine> enginesInOrder;
|
||||
private readonly ILookup<int, Engine> engineGroups;
|
||||
private readonly EntityManager entityManager;
|
||||
private readonly ComponentManager componentManager;
|
||||
private readonly MessageManager messageManager;
|
||||
private readonly RenderManager renderManager;
|
||||
|
||||
internal World(
|
||||
List<Engine> enginesInOrder,
|
||||
ILookup<int, Engine> engineGroups,
|
||||
EntityManager entityManager,
|
||||
ComponentManager componentManager,
|
||||
MessageManager messageManager,
|
||||
RenderManager renderManager
|
||||
)
|
||||
{
|
||||
this.enginesInOrder = enginesInOrder;
|
||||
this.engineGroups = engineGroups;
|
||||
this.entityManager = entityManager;
|
||||
this.componentManager = componentManager;
|
||||
this.messageManager = messageManager;
|
||||
|
@ -27,9 +29,23 @@ namespace Encompass
|
|||
|
||||
public void Update(double dt)
|
||||
{
|
||||
foreach (var engine in enginesInOrder)
|
||||
foreach (var engineGroup in engineGroups.OrderBy(lookup => lookup.Key))
|
||||
{
|
||||
engine.Update(dt);
|
||||
try
|
||||
{
|
||||
Parallel.ForEach(engineGroup, engine => engine.Update(dt));
|
||||
}
|
||||
catch (System.AggregateException e)
|
||||
{
|
||||
if (e.InnerException != null)
|
||||
{
|
||||
throw e.InnerException;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
messageManager.ClearMessages();
|
||||
|
|
|
@ -7,6 +7,11 @@ using Encompass.Engines;
|
|||
|
||||
namespace Encompass
|
||||
{
|
||||
class WorldEngine : Engine // used to simulate the World, removed when passed to World
|
||||
{
|
||||
public override void Update(double dt) { }
|
||||
}
|
||||
|
||||
public class WorldBuilder
|
||||
{
|
||||
private readonly List<Engine> engines = new List<Engine>();
|
||||
|
@ -165,7 +170,7 @@ namespace Encompass
|
|||
return renderer;
|
||||
}
|
||||
|
||||
private void BuildEngineGraph()
|
||||
private void BuildEngineGraph(Engine worldEngine)
|
||||
{
|
||||
foreach (var senderEngine in senders)
|
||||
{
|
||||
|
@ -183,11 +188,24 @@ namespace Encompass
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
engineGraph.AddVertex(worldEngine);
|
||||
|
||||
foreach (var engine in engines.Where(engine => engine.GetType().IsGenericType && engine.GetType().GetGenericTypeDefinition() == typeof(ComponentMessageEmitter<>)))
|
||||
{
|
||||
engineGraph.AddEdge(worldEngine, engine);
|
||||
}
|
||||
|
||||
foreach (var engine in engines.Where(engine => !engineGraph.IncomingEdges(engine).Any()))
|
||||
{
|
||||
engineGraph.AddEdge(worldEngine, engine);
|
||||
}
|
||||
}
|
||||
|
||||
public World Build()
|
||||
{
|
||||
BuildEngineGraph();
|
||||
var worldEngine = new WorldEngine();
|
||||
BuildEngineGraph(worldEngine);
|
||||
|
||||
if (engineGraph.Cyclic())
|
||||
{
|
||||
|
@ -245,14 +263,8 @@ namespace Encompass
|
|||
throw new EngineUpdateConflictException(errorString);
|
||||
}
|
||||
|
||||
var engineOrder = new List<Engine>();
|
||||
foreach (var engine in engineGraph.TopologicalSort())
|
||||
{
|
||||
engineOrder.Add(engine);
|
||||
}
|
||||
|
||||
var world = new World(
|
||||
engineOrder,
|
||||
engineGraph.LongestPaths(worldEngine).Where(pair => pair.Item2 != 0).ToLookup(pair => pair.Item2, pair => pair.Item1),
|
||||
entityManager,
|
||||
componentManager,
|
||||
messageManager,
|
||||
|
|
|
@ -123,6 +123,7 @@ namespace Tests
|
|||
}
|
||||
}
|
||||
|
||||
static bool hasComponentResult = false;
|
||||
[ReadsPending(typeof(MockComponent))]
|
||||
class HasMockComponentEngine : Engine
|
||||
{
|
||||
|
@ -135,7 +136,7 @@ namespace Tests
|
|||
|
||||
public override void Update(double dt)
|
||||
{
|
||||
Assert.IsTrue(HasComponent<MockComponent>(entity));
|
||||
hasComponentResult = HasComponent<MockComponent>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,6 +153,8 @@ namespace Tests
|
|||
var world = worldBuilder.Build();
|
||||
|
||||
world.Update(0.01);
|
||||
|
||||
Assert.IsTrue(hasComponentResult);
|
||||
}
|
||||
|
||||
[Receives(typeof(EntityMessage))]
|
||||
|
|
|
@ -179,10 +179,10 @@ namespace Tests
|
|||
var result = graph.LongestPaths('r');
|
||||
result.Should().Contain(('r', 0));
|
||||
result.Should().Contain(('s', 1));
|
||||
result.Should().Contain(('t', 1));
|
||||
result.Should().Contain(('x', 2));
|
||||
result.Should().Contain(('y', 2));
|
||||
result.Should().Contain(('z', 3));
|
||||
result.Should().Contain(('t', 2));
|
||||
result.Should().Contain(('x', 3));
|
||||
result.Should().Contain(('y', 4));
|
||||
result.Should().Contain(('z', 5));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
|
@ -191,6 +191,8 @@ namespace Tests
|
|||
|
||||
var world = worldBuilder.Build();
|
||||
|
||||
Action updateWorld = () => world.Update(0.01);
|
||||
|
||||
var ex = Assert.Throws<IllegalUpdateException>(() => world.Update(0.01f));
|
||||
Assert.That(ex.Message, Is.EqualTo("Engine UndeclaredUpdateComponentTestEngine tried to update undeclared Component MockComponent"));
|
||||
}
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
|
||||
using Encompass;
|
||||
using Encompass.Engines;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Tests
|
||||
{
|
||||
public class SpawnerTest
|
||||
{
|
||||
struct TestComponent : IComponent { }
|
||||
struct SpawnMessageA : IMessage { }
|
||||
public struct TestComponent : IComponent { }
|
||||
public struct SpawnMessageA : IMessage { }
|
||||
|
||||
static Entity resultEntity;
|
||||
|
||||
|
@ -23,17 +21,22 @@ namespace Tests
|
|||
}
|
||||
}
|
||||
|
||||
static bool spawnResult = false;
|
||||
|
||||
[Activates(typeof(TestComponent))]
|
||||
class TestSpawner : Spawner<SpawnMessageA>
|
||||
public class TestSpawner : Spawner<SpawnMessageA>
|
||||
{
|
||||
protected override void Spawn(SpawnMessageA message)
|
||||
protected override void Spawn(SpawnMessageA message)
|
||||
{
|
||||
resultEntity = CreateEntity();
|
||||
AddComponent(resultEntity, new TestComponent());
|
||||
Assert.Pass();
|
||||
spawnResult = true;
|
||||
}
|
||||
}
|
||||
|
||||
interface TestSpawnerProtectedMembers
|
||||
{
|
||||
void Spawn(SpawnMessageA message);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RunsSpawnMethodOnMessageRead()
|
||||
{
|
||||
|
@ -44,6 +47,8 @@ namespace Tests
|
|||
var world = worldBuilder.Build();
|
||||
|
||||
world.Update(0.01);
|
||||
|
||||
Assert.IsTrue(spawnResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
using NUnit.Framework;
|
||||
|
||||
using Encompass;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using Encompass.Exceptions;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Tests
|
||||
{
|
||||
|
@ -206,7 +207,7 @@ namespace Tests
|
|||
|
||||
public class LegalEngines
|
||||
{
|
||||
static List<Engine> order = new List<Engine>();
|
||||
static ConcurrentQueue<Engine> queue = new ConcurrentQueue<Engine>();
|
||||
|
||||
struct AComponent : IComponent { }
|
||||
struct BComponent : IComponent { }
|
||||
|
@ -221,7 +222,7 @@ namespace Tests
|
|||
{
|
||||
public override void Update(double dt)
|
||||
{
|
||||
order.Add(this);
|
||||
queue.Enqueue(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,7 +231,7 @@ namespace Tests
|
|||
{
|
||||
public override void Update(double dt)
|
||||
{
|
||||
order.Add(this);
|
||||
queue.Enqueue(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,7 +241,7 @@ namespace Tests
|
|||
{
|
||||
public override void Update(double dt)
|
||||
{
|
||||
order.Add(this);
|
||||
queue.Enqueue(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,7 +250,7 @@ namespace Tests
|
|||
{
|
||||
public override void Update(double dt)
|
||||
{
|
||||
order.Add(this);
|
||||
queue.Enqueue(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -269,6 +270,15 @@ namespace Tests
|
|||
|
||||
world.Update(0.01f);
|
||||
|
||||
var order = new List<Engine>();
|
||||
|
||||
while(!queue.IsEmpty)
|
||||
{
|
||||
Engine engine;
|
||||
queue.TryDequeue(out engine);
|
||||
order.Add(engine);
|
||||
}
|
||||
|
||||
Assert.That(order.IndexOf(engineA), Is.LessThan(order.IndexOf(engineC)));
|
||||
Assert.That(order.IndexOf(engineB), Is.LessThan(order.IndexOf(engineC)));
|
||||
Assert.That(order.IndexOf(engineC), Is.LessThan(order.IndexOf(engineD)));
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<RootNamespace>Tests</RootNamespace>
|
||||
<AssemblyName>EncompassECS.Framework.Tests</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="5.7.0" />
|
||||
<PackageReference Include="nunit" Version="3.11.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.11.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\encompass-cs\encompass-cs.csproj" />
|
||||
</ItemGroup>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<RootNamespace>Tests</RootNamespace>
|
||||
<AssemblyName>EncompassECS.Framework.Tests</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="5.7.0"/>
|
||||
<PackageReference Include="nunit" Version="3.11.0"/>
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.11.0"/>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\encompass-cs\encompass-cs.csproj"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
Reference in New Issue