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