diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index c0e2084..4981785 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -2,7 +2,7 @@ name: Build And Test on: [push] env: - BuildVersion: '12.0.${{github.run_number}}' + BuildVersion: '13.0.${{github.run_number}}' SolutionFile: 'src/EcsR3.sln' jobs: diff --git a/docs/ecs-r3/framework/computeds.md b/docs/ecs-r3/framework/computeds.md index 507a1c3..397fc97 100644 --- a/docs/ecs-r3/framework/computeds.md +++ b/docs/ecs-r3/framework/computeds.md @@ -28,12 +28,18 @@ Now we can all laugh at the name here, but this is basically the same as the pre This provides you with an `IComputedEntityGroup` as the `DataSource` then you translate them into whatever you want `T` to be. +> This inherits from the lazy computed line, so it will only refresh its value when you read its `Value`(and it has awaiting changes) or you explicitly call `ForceRefresh`. + ## `IComputedComponentGroup` While this is listed as a high level `Computed` and not a convention, in reality it is a `ComputedFromEntityGroup>>` which is a bit of a mouthful, but it provides a really performant way to access components for `Entities`. > For example if you have a group that requires `ComponentA`, `ComponentB` then this computed will provide `ComponentBatch` for each entity, allowing quick lookup and processing, this is what `BatchedSystems` use under the hood to resolve components. +> This inherits from the lazy computed line, so it will only refresh its value when you read its `Value`(and it has awaiting changes) or you explicitly call `ForceRefresh`. + ### `ComputedFromComponentGroup` -This provides you a `IComputedComponentGroup` as the `DataSource` and lets you process it however you want into `T`. \ No newline at end of file +This provides you a `IComputedComponentGroup` as the `DataSource` and lets you process it however you want into `T`. + +> This inherits from the lazy computed line, so it will only refresh its value when you read its `Value`(and it has awaiting changes) or you explicitly call `ForceRefresh`. diff --git a/docs/ecs-r3/framework/systems.md b/docs/ecs-r3/framework/systems.md index 0adc328..cc3fcc0 100644 --- a/docs/ecs-r3/framework/systems.md +++ b/docs/ecs-r3/framework/systems.md @@ -77,6 +77,63 @@ public class BatchedExampleSystem : BatchedMixedSystem Due to the MANY permutations of this that you can have its recommended that if you have very specific scenarios you just copy the code for the current `BatchedMixedSystem` and alter the signatures however you need. +### `MultiplexingSystem` + `IMultiplexedJob` + +This is very much like a batched system, but it acts as a sort of multiplexer to allow you to run multiple `jobs` within one update, a `job` is basically the same as a batched systems `Process` method but standalone. + +> This is really useful if you have multiple systems which all require same components and execute at same time, this ensures that it only needs to lookup the batches once and then runs all jobs back to back with the batch data, this can allow for better utilisation of CPU/Memory. + +```csharp +// Make as many jobs as you want, you can also use ISystemPreProcessor/PostProcessor with it +public class Job1 : IMultiplexedJob +{ + // Notice we dont give it a schedule, it is handled by the systems schedule + public void Process(Entity entity, ClassComponent component1, ClassComponent2 component2, ClassComponent3 component3) + { + component1.Position += Vector3.One; + component1.Something += 10; + component2.IsTrue = true; + component2.Value += 10; + } +} + +// Notice that even though we don't use Component2 here, and the previous didnt use Component1 it doesnt matter too much as we +// still get a performance bonus due to it scheduling both things in same block +public class Job2 : IMultiplexedJob +{ + public void Process(Entity entity, ClassComponent component1, ClassComponent2 component2, ClassComponent3 component3) + { + component1.Position += Vector3.One; + component1.Something += 10; + component3.IsTrue = true; + component3.Value += 10; + } +} + + +// This acts like a normal batched system, but it just expects you to provide it the jobs you want to run +public class ExampleMultiplexedSystem : MultiplexingBatchedSystem +{ + public ExampleMultiplexedSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + // This is scheduling when all jobs should be run + protected override Observable ReactWhen() => Observable.EveryUpdate(); + + // This is a simple example, but you can always DI in the jobs and pass them into here for more complex use cases + protected override IEnumerable> ResolveJobs() + { return [new Job1(), new Job2()]; } +} +``` +On one hand this may seem slightly more complex but in a way it also makes things a bit simpler as your Jobs are lightweight objects that can just be scheduled together and you have a smaller logic footprint. + +> Remember you dont need to have 100% overlap on required components etc, there will be a tipping point but if you have several systems which all use 75% of the same components you can possibly get a decent performance bonus making them into jobs and giving them all the same components, even if a few of the jobs ignore a component or two it may still end up being more efficient than running them as fully fledged systems. + +### `MultiplexingBatchedRefSystem` + `IMultiplexedRefJob` +Same as above but it lets you pass the components to jobs with `ref` keyword, mainly meant for struct scenarios. + +> There is currently no mixed one but it may be added in the future, there is so many varieties of approaches to mix `ref` it is currently left to you to implement your own variants if you need them. + ### `IBasicEntitySystem` This system is like a `IBasicSystem` allowing you to process each entity within the group on every update cycle. diff --git a/docs/systems-r3/framework/computeds.md b/docs/systems-r3/framework/computeds.md index 40f8a19..36fe366 100644 --- a/docs/systems-r3/framework/computeds.md +++ b/docs/systems-r3/framework/computeds.md @@ -6,11 +6,14 @@ Computed values are basically read only values which are proxy a data source and ## Computed Types -There are 3 default computed types available within the system: +There are 4 default computed types available within the system: ### `IComputed` (For computed single values) Simplest computed and provides a current value and allows subscription to when the value changes, this can be very useful for precomputing things based off other data, i.e calculating MaxHp once all buffs have been taken into account. +### `ILazyComputed` (Same as above) +Same as normal Computed but only updates when value is read or `ForceRefresh` is called, triggering `OnChange` exposes `OnHasChange` as well to indicate the dependent data has changed but not been refreshed. + ### `IComputedCollection` (For computed collections of data) A reactive collection which provides an up to date collection of values and allows you to subscribe to when it changes, this could be useful for tracking all beneficial buffs on a player where the source data is just ALL buffs/debuffs on the entity. @@ -64,6 +67,9 @@ var computedFirstPlaceRacer = new ComputedFirstPlace(collectionOfRacers); // inh RacerHud.CurrentWinner.Text = computedFirstPlaceRacer.Value.Name; ``` +### `LazyComputedFromData` +Same as previous `ComputedFromData` but a lazily evaluated variant. + ### `ComputedFromObservable` Much like the above `ComputedFromData` but the `DataSource` needs to be an `Observable`, and will listen for changes on the observable and update its internal state accordingly, these are often known as **Pure Computeds** as they just proxy the underlying Observable. diff --git a/src/EcsR3.Benchmarks/Benchmarks/BatchSystemMultiThreadingBenchmark.cs b/src/EcsR3.Benchmarks/Benchmarks/BatchSystemMultiThreadingBenchmark.cs index 6be6178..75153e2 100644 --- a/src/EcsR3.Benchmarks/Benchmarks/BatchSystemMultiThreadingBenchmark.cs +++ b/src/EcsR3.Benchmarks/Benchmarks/BatchSystemMultiThreadingBenchmark.cs @@ -63,7 +63,7 @@ public override void Setup() public override void Cleanup() { - EntityCollection.RemoveAll(); + EntityCollection.Clear(); BatchingSystem.StopSystem(); } diff --git a/src/EcsR3.Benchmarks/Benchmarks/BatchVsMultiplexedClassComponentBenchmark.cs b/src/EcsR3.Benchmarks/Benchmarks/BatchVsMultiplexedClassComponentBenchmark.cs new file mode 100644 index 0000000..5009d93 --- /dev/null +++ b/src/EcsR3.Benchmarks/Benchmarks/BatchVsMultiplexedClassComponentBenchmark.cs @@ -0,0 +1,194 @@ +using System.Collections.Generic; +using System.Numerics; +using BenchmarkDotNet.Attributes; +using EcsR3.Blueprints; +using EcsR3.Components.Database; +using EcsR3.Computeds.Components.Registries; +using EcsR3.Entities; +using EcsR3.Entities.Accessors; +using EcsR3.Examples.Custom.BatchTests.Components; +using EcsR3.Extensions; +using EcsR3.Systems.Batching.Convention; +using EcsR3.Systems.Batching.Convention.Multiplexing; +using EcsR3.Systems.Batching.Convention.Multiplexing.Handlers; +using R3; +using SystemsR3.Threading; + +namespace EcsR3.Benchmarks.Benchmarks; + +[BenchmarkCategory("Systems")] +public class BatchVsMultiplexedClassComponentBenchmark : EcsR3Benchmark +{ + public class BatchVsMultiplexedBlueprint : IBlueprint, IBatchedBlueprint + { + public void Apply(IEntityComponentAccessor entityComponentAccessor, Entity entity) + { entityComponentAccessor.AddComponents(entity, new ClassComponent(), new ClassComponent2(), new ClassComponent3()); } + + public void Apply(IEntityComponentAccessor entityComponentAccessor, Entity[] entities) + { entityComponentAccessor.CreateComponents(entities); } + } + +#region Batch Systems + public class ClassBatchSystem1 : BatchedSystem + { + public ClassBatchSystem1(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + protected override Observable ReactWhen() => Observable.Never(); + public void ForceRun() => ProcessBatch(); + public bool UseMultithreading(bool should) => ShouldMultithread = should; + + protected override void Process(Entity entity, ClassComponent component2, ClassComponent2 component3) + { + component2.Position += Vector3.One; + component2.Something += 10; + component3.IsTrue = true; + component3.Value += 10; + } + } + + public class ClassBatchSystem2 : BatchedSystem + { + public ClassBatchSystem2(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + protected override Observable ReactWhen() => Observable.Never(); + public void ForceRun() => ProcessBatch(); + public bool UseMultithreading(bool should) => ShouldMultithread = should; + + protected override void Process(Entity entity, ClassComponent component2, ClassComponent3 component3) + { + component2.Position += Vector3.One; + component2.Something += 10; + component3.IsTrue = true; + component3.Value += 10; + } + } + + public class ClassBatchSystem3 : BatchedSystem + { + public ClassBatchSystem3(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + protected override Observable ReactWhen() => Observable.Never(); + public void ForceRun() => ProcessBatch(); + public bool UseMultithreading(bool should) => ShouldMultithread = should; + + protected override void Process(Entity entity, ClassComponent2 component2, ClassComponent3 component3) + { + component2.IsTrue = true; + component2.Value += 10; + component3.IsTrue = true; + component3.Value += 10; + } + } +#endregion + +#region Multiplex Systems + public class Job1 : IMultiplexedJob + { + public void Process(Entity entity, ClassComponent component1, ClassComponent2 component2, ClassComponent3 component3) + { + component1.Position += Vector3.One; + component1.Something += 10; + component2.IsTrue = true; + component2.Value += 10; + } + } + + public class Job2 : IMultiplexedJob + { + public void Process(Entity entity, ClassComponent component1, ClassComponent2 component2, ClassComponent3 component3) + { + component1.Position += Vector3.One; + component1.Something += 10; + component3.IsTrue = true; + component3.Value += 10; + } + } + + public class Job3 : IMultiplexedJob + { + public void Process(Entity entity, ClassComponent component1, ClassComponent2 component2, ClassComponent3 component3) + { + component2.IsTrue = true; + component2.Value += 10; + component3.IsTrue = true; + component3.Value += 10; + } + } + + public class ExampleMultiplexedSystem : MultiplexingBatchedSystem + { + public ExampleMultiplexedSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + protected override Observable ReactWhen() => Observable.Never(); + public void ForceRun() => ProcessBatch(); + public bool UseMultithreading(bool should) => ShouldMultithread = should; + + protected override IEnumerable> ResolveJobs() + { return [new Job1(), new Job2(), new Job3()]; } + } + +#endregion + + [Params(1000)] + public int EntityCount; + + [Params(false, true)] + public bool UseMultithreading; + + [Params(1000)] + public int Invocations; + + public ClassBatchSystem1 BatchingSystem1 { get; private set; } + public ClassBatchSystem2 BatchingSystem2 { get; private set; } + public ClassBatchSystem3 BatchingSystem3 { get; private set; } + public ExampleMultiplexedSystem MultiplexedSystem { get; private set; } + + public override void Setup() + { + BatchingSystem1 = new ClassBatchSystem1(ComponentDatabase, EntityComponentAccessor, ComputedComponentGroupRegistry, new DefaultThreadHandler()); + BatchingSystem1.StartSystem(); + BatchingSystem2 = new ClassBatchSystem2(ComponentDatabase, EntityComponentAccessor, ComputedComponentGroupRegistry, new DefaultThreadHandler()); + BatchingSystem2.StartSystem(); + BatchingSystem3 = new ClassBatchSystem3(ComponentDatabase, EntityComponentAccessor, ComputedComponentGroupRegistry, new DefaultThreadHandler()); + BatchingSystem3.StartSystem(); + MultiplexedSystem = new ExampleMultiplexedSystem(ComponentDatabase, EntityComponentAccessor, ComputedComponentGroupRegistry, new DefaultThreadHandler()); + MultiplexedSystem.StartSystem(); + + EntityCollection.CreateMany(EntityComponentAccessor, EntityCount); + } + + public override void Cleanup() + { + EntityCollection.Clear(); + BatchingSystem1.StopSystem(); + BatchingSystem2.StopSystem(); + BatchingSystem3.StopSystem(); + MultiplexedSystem.StopSystem(); + } + + [Benchmark] + public void ForceBatchRuns_Class() + { + BatchingSystem1.UseMultithreading(UseMultithreading); + BatchingSystem2.UseMultithreading(UseMultithreading); + BatchingSystem3.UseMultithreading(UseMultithreading); + for (var i = 0; i < Invocations; i++) + { + BatchingSystem1.ForceRun(); + BatchingSystem2.ForceRun(); + BatchingSystem3.ForceRun(); + } + } + + [Benchmark] + public void ForceMultiplexRuns_Class() + { + MultiplexedSystem.UseMultithreading(UseMultithreading); + for (var i = 0; i < Invocations; i++) + { MultiplexedSystem.ForceRun(); } + } +} \ No newline at end of file diff --git a/src/EcsR3.Benchmarks/Benchmarks/BatchVsMultiplexedStructComponentBenchmark.cs b/src/EcsR3.Benchmarks/Benchmarks/BatchVsMultiplexedStructComponentBenchmark.cs new file mode 100644 index 0000000..67fbf5e --- /dev/null +++ b/src/EcsR3.Benchmarks/Benchmarks/BatchVsMultiplexedStructComponentBenchmark.cs @@ -0,0 +1,195 @@ +using System.Collections.Generic; +using System.Numerics; +using BenchmarkDotNet.Attributes; +using EcsR3.Blueprints; +using EcsR3.Components.Database; +using EcsR3.Computeds.Components.Registries; +using EcsR3.Entities; +using EcsR3.Entities.Accessors; +using EcsR3.Examples.Custom.BatchTests.Components; +using EcsR3.Examples.ExampleApps.Performance.Components.Struct; +using EcsR3.Extensions; +using EcsR3.Systems.Batching.Convention; +using EcsR3.Systems.Batching.Convention.Multiplexing; +using EcsR3.Systems.Batching.Convention.Multiplexing.Handlers; +using R3; +using SystemsR3.Threading; + +namespace EcsR3.Benchmarks.Benchmarks; + +[BenchmarkCategory("Systems")] +public class BatchVsMultiplexedStructComponentBenchmark : EcsR3Benchmark +{ + public class BatchVsMultiplexedBlueprint : IBlueprint, IBatchedBlueprint + { + public void Apply(IEntityComponentAccessor entityComponentAccessor, Entity entity) + { entityComponentAccessor.AddComponents(entity, new StructComponent1Multiplexed(), new StructComponent2Multiplexed(), new StructComponent3Multiplexed()); } + + public void Apply(IEntityComponentAccessor entityComponentAccessor, Entity[] entities) + { entityComponentAccessor.CreateComponents(entities); } + } + +#region Batch Systems + public class ClassBatchSystem1 : BatchedRefSystem + { + public ClassBatchSystem1(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + protected override Observable ReactWhen() => Observable.Never(); + public void ForceRun() => ProcessBatch(); + public bool UseMultithreading(bool should) => ShouldMultithread = should; + + protected override void Process(Entity entity, ref StructComponent1Multiplexed component2, ref StructComponent2Multiplexed component3) + { + component2.Position += Vector3.One; + component2.Something += 10; + component3.IsTrue = true; + component3.Value += 10; + } + } + + public class ClassBatchSystem2 : BatchedRefSystem + { + public ClassBatchSystem2(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + protected override Observable ReactWhen() => Observable.Never(); + public void ForceRun() => ProcessBatch(); + public bool UseMultithreading(bool should) => ShouldMultithread = should; + + protected override void Process(Entity entity, ref StructComponent1Multiplexed component2, ref StructComponent3Multiplexed component3) + { + component2.Position += Vector3.One; + component2.Something += 10; + component3.IsTrue = true; + component3.Value += 10; + } + } + + public class ClassBatchSystem3 : BatchedRefSystem + { + public ClassBatchSystem3(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + protected override Observable ReactWhen() => Observable.Never(); + public void ForceRun() => ProcessBatch(); + public bool UseMultithreading(bool should) => ShouldMultithread = should; + + protected override void Process(Entity entity, ref StructComponent2Multiplexed component2, ref StructComponent3Multiplexed component3) + { + component2.IsTrue = true; + component2.Value += 10; + component3.IsTrue = true; + component3.Value += 10; + } + } +#endregion + +#region Multiplex Systems + public class Job1 : IMultiplexedRefJob + { + public void Process(Entity entity, ref StructComponent1Multiplexed component1, ref StructComponent2Multiplexed component2, ref StructComponent3Multiplexed component3) + { + component1.Position += Vector3.One; + component1.Something += 10; + component2.IsTrue = true; + component2.Value += 10; + } + } + + public class Job2 : IMultiplexedRefJob + { + public void Process(Entity entity, ref StructComponent1Multiplexed component1, ref StructComponent2Multiplexed component2, ref StructComponent3Multiplexed component3) + { + component1.Position += Vector3.One; + component1.Something += 10; + component3.IsTrue = true; + component3.Value += 10; + } + } + + public class Job3 : IMultiplexedRefJob + { + public void Process(Entity entity, ref StructComponent1Multiplexed component1, ref StructComponent2Multiplexed component2, ref StructComponent3Multiplexed component3) + { + component2.IsTrue = true; + component2.Value += 10; + component3.IsTrue = true; + component3.Value += 10; + } + } + + public class ExampleMultiplexedSystem : MultiplexingBatchedRefSystem + { + public ExampleMultiplexedSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + protected override Observable ReactWhen() => Observable.Never(); + public void ForceRun() => ProcessBatch(); + public bool UseMultithreading(bool should) => ShouldMultithread = should; + + protected override IEnumerable> ResolveJobs() + { return [new Job1(), new Job2(), new Job3()]; } + } + +#endregion + + [Params(1000)] + public int EntityCount; + + [Params(false, true)] + public bool UseMultithreading; + + [Params(1000)] + public int Invocations; + + public ClassBatchSystem1 BatchingSystem1 { get; private set; } + public ClassBatchSystem2 BatchingSystem2 { get; private set; } + public ClassBatchSystem3 BatchingSystem3 { get; private set; } + public ExampleMultiplexedSystem MultiplexedSystem { get; private set; } + + public override void Setup() + { + BatchingSystem1 = new ClassBatchSystem1(ComponentDatabase, EntityComponentAccessor, ComputedComponentGroupRegistry, new DefaultThreadHandler()); + BatchingSystem1.StartSystem(); + BatchingSystem2 = new ClassBatchSystem2(ComponentDatabase, EntityComponentAccessor, ComputedComponentGroupRegistry, new DefaultThreadHandler()); + BatchingSystem2.StartSystem(); + BatchingSystem3 = new ClassBatchSystem3(ComponentDatabase, EntityComponentAccessor, ComputedComponentGroupRegistry, new DefaultThreadHandler()); + BatchingSystem3.StartSystem(); + MultiplexedSystem = new ExampleMultiplexedSystem(ComponentDatabase, EntityComponentAccessor, ComputedComponentGroupRegistry, new DefaultThreadHandler()); + MultiplexedSystem.StartSystem(); + + EntityCollection.CreateMany(EntityComponentAccessor, EntityCount); + } + + public override void Cleanup() + { + EntityCollection.Clear(); + BatchingSystem1.StopSystem(); + BatchingSystem2.StopSystem(); + BatchingSystem3.StopSystem(); + MultiplexedSystem.StopSystem(); + } + + [Benchmark] + public void ForceBatchRuns_Struct() + { + BatchingSystem1.UseMultithreading(UseMultithreading); + BatchingSystem2.UseMultithreading(UseMultithreading); + BatchingSystem3.UseMultithreading(UseMultithreading); + for (var i = 0; i < Invocations; i++) + { + BatchingSystem1.ForceRun(); + BatchingSystem2.ForceRun(); + BatchingSystem3.ForceRun(); + } + } + + [Benchmark] + public void ForceMultiplexRuns_Struct() + { + MultiplexedSystem.UseMultithreading(UseMultithreading); + for (var i = 0; i < Invocations; i++) + { MultiplexedSystem.ForceRun(); } + } +} \ No newline at end of file diff --git a/src/EcsR3.Benchmarks/Benchmarks/ComputedComponentGroupsAddAndRemoveBenchmark.cs b/src/EcsR3.Benchmarks/Benchmarks/ComputedComponentGroupsAddAndRemoveBenchmark.cs new file mode 100644 index 0000000..64ffaa6 --- /dev/null +++ b/src/EcsR3.Benchmarks/Benchmarks/ComputedComponentGroupsAddAndRemoveBenchmark.cs @@ -0,0 +1,39 @@ +using BenchmarkDotNet.Attributes; +using EcsR3.Entities; +using EcsR3.Examples.ExampleApps.Performance.Components.Class; +using EcsR3.Extensions; +using ClassComponent2 = EcsR3.Examples.Custom.BatchTests.Components.ClassComponent2; + +namespace EcsR3.Benchmarks.Benchmarks; + +[BenchmarkCategory("Groups")] +public class ComputedComponentGroupsAddAndRemoveBenchmark : EcsR3Benchmark +{ + [Params(1000, 10000)] + public int EntityCount; + + [Params(5, 10, 20)] + public int Iterations; + + public override void Setup() + { + ComputedComponentGroupRegistry.GetComputedGroup(); + } + + public override void Cleanup() + { + EntityCollection.Clear(); + } + + [Benchmark] + public void ComputedComponentGroupAddRemove() + { + for (var j = 0; j < Iterations; j++) + { + var entities = EntityCollection.CreateMany(EntityCount); + EntityComponentAccessor.CreateComponents(entities); + EntityComponentAccessor.RemoveAllComponents(entities); + EntityCollection.Clear(); + } + } +} \ No newline at end of file diff --git a/src/EcsR3.Benchmarks/Benchmarks/ComputedEntityGroupsAddAndRemoveBenchmark.cs b/src/EcsR3.Benchmarks/Benchmarks/ComputedEntityGroupsAddAndRemoveBenchmark.cs index 9bedf1f..cdaab93 100644 --- a/src/EcsR3.Benchmarks/Benchmarks/ComputedEntityGroupsAddAndRemoveBenchmark.cs +++ b/src/EcsR3.Benchmarks/Benchmarks/ComputedEntityGroupsAddAndRemoveBenchmark.cs @@ -43,7 +43,7 @@ public override void Setup() public override void Cleanup() { - EntityCollection.RemoveAll(); + EntityCollection.Clear(); } [Benchmark] diff --git a/src/EcsR3.Benchmarks/Benchmarks/ComputedEntityGroupsAddAndRemoveWithNoiseBenchmark.cs b/src/EcsR3.Benchmarks/Benchmarks/ComputedEntityGroupsAddAndRemoveWithNoiseBenchmark.cs index 7295faf..2f07cff 100644 --- a/src/EcsR3.Benchmarks/Benchmarks/ComputedEntityGroupsAddAndRemoveWithNoiseBenchmark.cs +++ b/src/EcsR3.Benchmarks/Benchmarks/ComputedEntityGroupsAddAndRemoveWithNoiseBenchmark.cs @@ -57,7 +57,7 @@ public override void Setup() public override void Cleanup() { - EntityCollection.RemoveAll(); + EntityCollection.Clear(); } [Benchmark] diff --git a/src/EcsR3.Benchmarks/Benchmarks/ExecutorAddAndRemoveEntitiesBenchmark.cs b/src/EcsR3.Benchmarks/Benchmarks/ExecutorAddAndRemoveEntitiesBenchmark.cs index 04a8d87..ab9ff39 100644 --- a/src/EcsR3.Benchmarks/Benchmarks/ExecutorAddAndRemoveEntitiesBenchmark.cs +++ b/src/EcsR3.Benchmarks/Benchmarks/ExecutorAddAndRemoveEntitiesBenchmark.cs @@ -71,7 +71,7 @@ public override void Setup() public override void Cleanup() { - EntityCollection.RemoveAll(); + EntityCollection.Clear(); } [Benchmark] diff --git a/src/EcsR3.Benchmarks/Benchmarks/MultipleComputedEntityGroupsAddAndRemoveBenchmark.cs b/src/EcsR3.Benchmarks/Benchmarks/MultipleComputedEntityGroupsAddAndRemoveBenchmark.cs index 4f3cc01..bf6ecbe 100644 --- a/src/EcsR3.Benchmarks/Benchmarks/MultipleComputedEntityGroupsAddAndRemoveBenchmark.cs +++ b/src/EcsR3.Benchmarks/Benchmarks/MultipleComputedEntityGroupsAddAndRemoveBenchmark.cs @@ -55,7 +55,7 @@ public override void Setup() public override void Cleanup() { - EntityCollection.RemoveAll(); + EntityCollection.Clear(); var manager = (ComputedEntityGroupRegistry as ComputedEntityGroupRegistry); var allComputedEntityGroups = manager._computedGroups; allComputedEntityGroups.Values.DisposeAll(); diff --git a/src/EcsR3.Benchmarks/Benchmarks/New/EntityAdd_ClassComponents_Benchmark.cs b/src/EcsR3.Benchmarks/Benchmarks/New/EntityAdd_ClassComponents_Benchmark.cs index 259044a..8802c79 100644 --- a/src/EcsR3.Benchmarks/Benchmarks/New/EntityAdd_ClassComponents_Benchmark.cs +++ b/src/EcsR3.Benchmarks/Benchmarks/New/EntityAdd_ClassComponents_Benchmark.cs @@ -7,30 +7,16 @@ namespace EcsR3.Benchmarks.Benchmarks.New [BenchmarkCategory("Entities")] public class EntityAdd_ClassComponents_Benchmark : EcsR3Benchmark { - [Params(100000)] + [Params(10000)] public int EntityCount; - [Params(1, 2 ,3, 10)] + [Params(1, 2, 3)] public int ComponentCount; - [Params(true, false)] - public bool PreAllocate; - [IterationSetup] - public void IterationSetup() - { - if (PreAllocate) - { - GetPoolFor().Expand(EntityCount); - if(ComponentCount >= 2) { GetPoolFor().Expand(EntityCount); } - if(ComponentCount == 3) { GetPoolFor().Expand(EntityCount); } - } - } - - [IterationCleanup] - public override void Cleanup() + public override void Setup() { - EntityCollection.RemoveAll(); + EntityCollection.Clear(); GetPoolFor().Clear(); if(ComponentCount >= 2) { GetPoolFor().Clear(); } if(ComponentCount == 3) { GetPoolFor().Clear(); } @@ -39,15 +25,12 @@ public override void Cleanup() [Benchmark] public void EntitiesBatchAdd_ClassComponents() { - for (var i = 0; i < EntityCount; i++) + var entities = EntityCollection.CreateMany(EntityCount); + switch (ComponentCount) { - var entityId = EntityCollection.Create(); - switch (ComponentCount) - { - case 1: EntityComponentAccessor.CreateComponent(entityId); break; - case 2: EntityComponentAccessor.CreateComponents(entityId); break; - case 3: EntityComponentAccessor.CreateComponents(entityId); break; - } + case 1: EntityComponentAccessor.CreateComponent(entities); break; + case 2: EntityComponentAccessor.CreateComponents(entities); break; + case 3: EntityComponentAccessor.CreateComponents(entities); break; } } diff --git a/src/EcsR3.Benchmarks/Benchmarks/New/EntityAdd_ClassComponents_PreAllocatedBenchmark.cs b/src/EcsR3.Benchmarks/Benchmarks/New/EntityAdd_ClassComponents_PreAllocatedBenchmark.cs new file mode 100644 index 0000000..5c0903b --- /dev/null +++ b/src/EcsR3.Benchmarks/Benchmarks/New/EntityAdd_ClassComponents_PreAllocatedBenchmark.cs @@ -0,0 +1,61 @@ +using BenchmarkDotNet.Attributes; +using EcsR3.Examples.ExampleApps.Performance.Components.Class; +using EcsR3.Extensions; + +namespace EcsR3.Benchmarks.Benchmarks.New +{ + [BenchmarkCategory("Entities")] + public class EntityAdd_ClassComponents_PreAllocatedBenchmark : EcsR3Benchmark + { + [Params(10000)] + public int EntityCount; + + [Params(1, 2, 3)] + public int ComponentCount; + + [IterationSetup] + public override void Cleanup() + { + EntityCollection.Clear(); + GetPoolFor().Clear(); + if(ComponentCount >= 2) { GetPoolFor().Clear(); } + if(ComponentCount == 3) { GetPoolFor().Clear(); } + } + + [Benchmark] + public void EntitiesBatchAdd_ClassComponents_PreAllocated() + { + EntityAllocationDatabase.PreAllocate(EntityCount); + GetPoolFor().Expand(EntityCount); + if(ComponentCount >= 2) { GetPoolFor().Expand(EntityCount); } + if(ComponentCount == 3) { GetPoolFor().Expand(EntityCount); } + + var entities = EntityCollection.CreateMany(EntityCount); + + switch (ComponentCount) + { + case 1: EntityComponentAccessor.CreateComponent(entities); break; + case 2: EntityComponentAccessor.CreateComponents(entities); break; + case 3: EntityComponentAccessor.CreateComponents(entities); break; + } + + } + + [Benchmark] + public void EntitiesAddIndividual_ClassComponents_PreAllocated() + { + EntityAllocationDatabase.PreAllocate(EntityCount); + GetPoolFor().Expand(EntityCount); + if(ComponentCount >= 2) { GetPoolFor().Expand(EntityCount); } + if(ComponentCount == 3) { GetPoolFor().Expand(EntityCount); } + + for (var i = 0; i < EntityCount; i++) + { + var entityId = EntityCollection.Create(); + EntityComponentAccessor.CreateComponent(entityId); + if(ComponentCount >= 2) { EntityComponentAccessor.CreateComponent(entityId); } + if(ComponentCount >= 3) { EntityComponentAccessor.CreateComponent(entityId); } + } + } + } +} \ No newline at end of file diff --git a/src/EcsR3.Benchmarks/Documentation/EntityCreationBenchmarks.cs b/src/EcsR3.Benchmarks/Documentation/EntityCreationBenchmarks.cs new file mode 100644 index 0000000..2ab3e35 --- /dev/null +++ b/src/EcsR3.Benchmarks/Documentation/EntityCreationBenchmarks.cs @@ -0,0 +1,6 @@ +namespace EcsR3.Benchmarks.Documentation; + +public class EntityCreationBenchmarks +{ + +} \ No newline at end of file diff --git a/src/EcsR3.Benchmarks/EcsR3Benchmark.cs b/src/EcsR3.Benchmarks/EcsR3Benchmark.cs index 1fa57aa..db4f05a 100644 --- a/src/EcsR3.Benchmarks/EcsR3Benchmark.cs +++ b/src/EcsR3.Benchmarks/EcsR3Benchmark.cs @@ -1,3 +1,4 @@ +using System; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Jobs; using EcsR3.Collections.Entities; @@ -41,9 +42,10 @@ public abstract class EcsR3Benchmark public virtual ComponentDatabaseConfig OverrideComponentDatabaseConfig() => new() { OnlyPreAllocatePoolsWithConfig = true }; public IComponentPool GetPoolFor() where T : IComponent - { - return ComponentDatabase.GetPoolFor(ComponentTypeLookup.GetComponentTypeId(typeof(T))); - } + { return ComponentDatabase.GetPoolFor(ComponentTypeLookup.GetComponentTypeId(typeof(T))); } + + public IComponentPool GetPoolFor(Type componentType) + { return ComponentDatabase.GetPoolFor(ComponentTypeLookup.GetComponentTypeId(componentType)); } protected EcsR3Benchmark() { diff --git a/src/EcsR3.Benchmarks/Exploratory/ForVsForEachIteratorTypes.cs b/src/EcsR3.Benchmarks/Exploratory/ForVsForEachIteratorTypes.cs new file mode 100644 index 0000000..4f90be7 --- /dev/null +++ b/src/EcsR3.Benchmarks/Exploratory/ForVsForEachIteratorTypes.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; + +namespace EcsR3.Benchmarks.Exploratory; + +[SimpleJob(RuntimeMoniker.Net90, warmupCount: 1, invocationCount: 1, iterationCount: 3)] +[MemoryDiagnoser] +public class ForVsForEachIteratorTypes +{ + [Params(100,10000,100000)] + public int ElementCount; + + [Params(1000)] + public int Iterations; + + [Benchmark] + public int[] For_Array() + { + var arrayIn = new int[ElementCount]; + var arrayOut = new int[ElementCount]; + + for (var _ = 0; _ < Iterations; _++) + { + var index = -1; + for (var i = 0; i < ElementCount; i++) + { + index++; + arrayOut[index] = arrayIn[index]; + } + } + return arrayOut; + } + + [Benchmark] + public int[] For_IReadOnlyList() + { + var arrayIn = new int[ElementCount]; + var arrayOut = new int[ElementCount]; + IReadOnlyList customIn = arrayIn; + + for (var _ = 0; _ < Iterations; _++) + { + var index = -1; + for (var i = 0; i < ElementCount; i++) + { + index++; + arrayOut[index] = customIn[index]; + } + } + return arrayOut; + } + + [Benchmark] + public int[] For_Span() + { + var arrayIn = new int[ElementCount]; + var arrayOut = new int[ElementCount]; + Span customIn = arrayIn; + + for (var _ = 0; _ < Iterations; _++) + { + var index = -1; + for (var i = 0; i < ElementCount; i++) + { + index++; + arrayOut[index] = customIn[index]; + } + } + return arrayOut; + } + + + [Benchmark] + public int[] ForEach_Array() + { + var arrayIn = new int[ElementCount]; + var arrayOut = new int[ElementCount]; + + for (var _ = 0; _ < Iterations; _++) + { + var index = -1; + foreach (var value in arrayIn) + { + index++; + arrayOut[index] = value; + } + } + return arrayOut; + } + + [Benchmark] + public int[] ForEach_ReadOnlyCollection() + { + var arrayIn = new int[ElementCount]; + var arrayOut = new int[ElementCount]; + IReadOnlyCollection customIn = arrayIn; + + for (var _ = 0; _ < Iterations; _++) + { + var index = -1; + foreach (var value in customIn) + { + index++; + arrayOut[index] = value; + } + } + return arrayOut; + } + + + [Benchmark] + public int[] ForEach_ReadOnlyList() + { + var arrayIn = new int[ElementCount]; + var arrayOut = new int[ElementCount]; + IReadOnlyList customIn = arrayIn; + + for (var _ = 0; _ < Iterations; _++) + { + var index = -1; + foreach (var value in customIn) + { + index++; + arrayOut[index] = value; + } + } + return arrayOut; + } + + [Benchmark] + public int[] ForEach_Span() + { + var arrayIn = new int[ElementCount]; + var arrayOut = new int[ElementCount]; + Span customIn = arrayIn; + + for (var _ = 0; _ < Iterations; _++) + { + var index = -1; + foreach (var value in customIn) + { + index++; + arrayOut[index] = value; + } + } + return arrayOut; + } +} \ No newline at end of file diff --git a/src/EcsR3.Benchmarks/Program.cs b/src/EcsR3.Benchmarks/Program.cs index d9d050f..7d192d8 100644 --- a/src/EcsR3.Benchmarks/Program.cs +++ b/src/EcsR3.Benchmarks/Program.cs @@ -2,6 +2,7 @@ using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Running; using EcsR3.Benchmarks.Benchmarks; +using EcsR3.Benchmarks.Benchmarks.New; using EcsR3.Benchmarks.Exploratory; using SystemsR3.Extensions; @@ -17,7 +18,8 @@ class Program /// static void Main(string[] args) { - var benchmarks = new [] + // General all purpose benchmarks for library internals + var generalBenchmarks = new [] { BenchmarkConverter.TypeToBenchmarks(typeof(IdPoolBenchmarks)), BenchmarkConverter.TypeToBenchmarks(typeof(MultithreadedIdPoolBenchmarks)), @@ -27,9 +29,16 @@ static void Main(string[] args) BenchmarkConverter.TypeToBenchmarks(typeof(MultipleComputedEntityGroupsAddAndRemoveBenchmark)), BenchmarkConverter.TypeToBenchmarks(typeof(ComputedEntityGroupsAddAndRemoveBenchmark)), BenchmarkConverter.TypeToBenchmarks(typeof(ComputedEntityGroupsAddAndRemoveWithNoiseBenchmark)), + BenchmarkConverter.TypeToBenchmarks(typeof(ComputedComponentGroupsAddAndRemoveBenchmark)), BenchmarkConverter.TypeToBenchmarks(typeof(ExecutorAddAndRemoveEntitySystemBenchmark)), BenchmarkConverter.TypeToBenchmarks(typeof(BatchSystemMultiThreadingBenchmark)), - + BenchmarkConverter.TypeToBenchmarks(typeof(BatchVsMultiplexedClassComponentBenchmark)), + BenchmarkConverter.TypeToBenchmarks(typeof(BatchVsMultiplexedStructComponentBenchmark)), + }; + + // Specific benchmarks to test theories on performance of non-internal lib things + var exploratoryBenchmarks = new[] + { BenchmarkConverter.TypeToBenchmarks(typeof(StackBenchmarks)), BenchmarkConverter.TypeToBenchmarks(typeof(ArrayResizeBenchmarks)), BenchmarkConverter.TypeToBenchmarks(typeof(KeyedCollectionVsDictionaryBenchmarks)), @@ -37,8 +46,15 @@ static void Main(string[] args) BenchmarkConverter.TypeToBenchmarks(typeof(MultiDimensionalArrayResizeBenchmarks)), BenchmarkConverter.TypeToBenchmarks(typeof(IntValueLookupBenchmarks)) }; + + // Potentially newer more sane specific tests + var newBenchmarks = new[] + { + BenchmarkConverter.TypeToBenchmarks(typeof(EntityAdd_ClassComponents_Benchmark)), + BenchmarkConverter.TypeToBenchmarks(typeof(EntityAdd_ClassComponents_PreAllocatedBenchmark)), + }; - var summaries = BenchmarkRunner.Run(benchmarks); + var summaries = BenchmarkRunner.Run(generalBenchmarks); var consoleLogger = ConsoleLogger.Default; consoleLogger.Flush(); summaries.ForEachRun(x => diff --git a/src/EcsR3.Examples/Custom/BatchTests/ClassBatchedSystemApplication.cs b/src/EcsR3.Examples/Custom/BatchTests/ClassBatchedSystemApplication.cs index 59c1821..cdd98fe 100644 --- a/src/EcsR3.Examples/Custom/BatchTests/ClassBatchedSystemApplication.cs +++ b/src/EcsR3.Examples/Custom/BatchTests/ClassBatchedSystemApplication.cs @@ -22,7 +22,6 @@ public class ClassBatchedSystemApplication : EcsR3ConsoleApplication { public IThreadHandler ThreadHandler { get; private set; } - [MultiThread] public class ClassBatchSystem : BatchedSystem { public ClassBatchSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) @@ -51,10 +50,9 @@ protected override void ResolveApplicationDependencies() protected override void ApplicationStarted() { - var entities = EntityCollection.CreateMany(EntityComponentAccessor, 100000); var batchSystem = new ClassBatchSystem(ComponentDatabase, EntityComponentAccessor, ComputedComponentGroupRegistry, ThreadHandler); batchSystem.StartSystem(); - GC.Collect(); + var entities = EntityCollection.CreateMany(EntityComponentAccessor, 100000); Console.WriteLine("Starting"); var stopwatch = new Stopwatch(); diff --git a/src/EcsR3.Examples/Custom/BatchTests/Components/ClassComponent3.cs b/src/EcsR3.Examples/Custom/BatchTests/Components/ClassComponent3.cs new file mode 100644 index 0000000..83c5074 --- /dev/null +++ b/src/EcsR3.Examples/Custom/BatchTests/Components/ClassComponent3.cs @@ -0,0 +1,9 @@ +using EcsR3.Components; + +namespace EcsR3.Examples.Custom.BatchTests.Components; + +public class ClassComponent3 : IComponent +{ + public bool IsTrue { get; set; } + public float Value { get; set; } +} \ No newline at end of file diff --git a/src/EcsR3.Examples/Custom/BatchTests/Components/StructComponent1Multiplexed.cs b/src/EcsR3.Examples/Custom/BatchTests/Components/StructComponent1Multiplexed.cs new file mode 100644 index 0000000..04bb84c --- /dev/null +++ b/src/EcsR3.Examples/Custom/BatchTests/Components/StructComponent1Multiplexed.cs @@ -0,0 +1,10 @@ +using System.Numerics; +using EcsR3.Components; + +namespace EcsR3.Examples.Custom.BatchTests.Components; + +public struct StructComponent1Multiplexed : IComponent +{ + public Vector3 Position; + public float Something; +} \ No newline at end of file diff --git a/src/EcsR3.Examples/Custom/BatchTests/Components/StructComponent2Multiplexed.cs b/src/EcsR3.Examples/Custom/BatchTests/Components/StructComponent2Multiplexed.cs new file mode 100644 index 0000000..c2598c0 --- /dev/null +++ b/src/EcsR3.Examples/Custom/BatchTests/Components/StructComponent2Multiplexed.cs @@ -0,0 +1,9 @@ +using EcsR3.Components; + +namespace EcsR3.Examples.Custom.BatchTests.Components; + +public struct StructComponent2Multiplexed : IComponent +{ + public bool IsTrue; + public int Value; +} \ No newline at end of file diff --git a/src/EcsR3.Examples/Custom/BatchTests/Components/StructComponent3Multiplexed.cs b/src/EcsR3.Examples/Custom/BatchTests/Components/StructComponent3Multiplexed.cs new file mode 100644 index 0000000..4d675ea --- /dev/null +++ b/src/EcsR3.Examples/Custom/BatchTests/Components/StructComponent3Multiplexed.cs @@ -0,0 +1,9 @@ +using EcsR3.Components; + +namespace EcsR3.Examples.Custom.BatchTests.Components; + +public struct StructComponent3Multiplexed : IComponent +{ + public bool IsTrue; + public float Value; +} \ No newline at end of file diff --git a/src/EcsR3.Examples/Custom/BatchTests/ManualClassBatchingApplication.cs b/src/EcsR3.Examples/Custom/BatchTests/ManualClassBatchingApplication.cs index 7d4596a..0bb5078 100644 --- a/src/EcsR3.Examples/Custom/BatchTests/ManualClassBatchingApplication.cs +++ b/src/EcsR3.Examples/Custom/BatchTests/ManualClassBatchingApplication.cs @@ -26,7 +26,7 @@ protected override void SetupEntities() base.SetupEntities(); _computedComponentGroup = ComputedComponentGroupRegistry.GetComputedGroup(); - _computedComponentGroup.RefreshData(); + _computedComponentGroup.ForceRefresh(); } protected override string Description { get; } = "Uses auto batching to allow the components to be clustered better in memory"; diff --git a/src/EcsR3.Examples/Custom/BatchTests/ManualStructBatchingApplication.cs b/src/EcsR3.Examples/Custom/BatchTests/ManualStructBatchingApplication.cs index 4ee0fe2..31a9ae7 100644 --- a/src/EcsR3.Examples/Custom/BatchTests/ManualStructBatchingApplication.cs +++ b/src/EcsR3.Examples/Custom/BatchTests/ManualStructBatchingApplication.cs @@ -29,7 +29,7 @@ protected override void SetupEntities() base.SetupEntities(); _computedComponentGroup = ComputedComponentGroupRegistry.GetComputedGroup(); - _computedComponentGroup.RefreshData(); + _computedComponentGroup.ForceRefresh(); } protected override string Description { get; } = "Uses auto batching to group components for cached lookups and quicker reads/writes"; diff --git a/src/EcsR3.Examples/Custom/ComputedComponents/ComputedComponentApplication.cs b/src/EcsR3.Examples/Custom/ComputedComponents/ComputedComponentApplication.cs index c3ea315..6618983 100644 --- a/src/EcsR3.Examples/Custom/ComputedComponents/ComputedComponentApplication.cs +++ b/src/EcsR3.Examples/Custom/ComputedComponents/ComputedComponentApplication.cs @@ -15,7 +15,7 @@ protected override void ApplicationStarted() var entities = EntityCollection.CreateMany(EntityComponentAccessor, 100000); var componentGroup = ComputedComponentGroupRegistry.GetComputedGroup(); - componentGroup.RefreshData(); + componentGroup.ForceRefresh(); var computed = new ComputedComponentProcessor(ComponentDatabase, componentGroup); @@ -24,7 +24,7 @@ protected override void ApplicationStarted() stopwatch.Start(); for (var i = 0; i < 100; i++) { - computed.RefreshData(); + computed.ForceRefresh(); Console.WriteLine($"Computed Value: {computed.ComputedData}"); } stopwatch.Stop(); diff --git a/src/EcsR3.Examples/Custom/ComputedComponents/ComputedComponentBatchApplication.cs b/src/EcsR3.Examples/Custom/ComputedComponents/ComputedComponentBatchApplication.cs new file mode 100644 index 0000000..c86b6a0 --- /dev/null +++ b/src/EcsR3.Examples/Custom/ComputedComponents/ComputedComponentBatchApplication.cs @@ -0,0 +1,30 @@ +using System; +using System.Diagnostics; +using EcsR3.Examples.Application; +using EcsR3.Examples.ExampleApps.Performance.Components.Class; +using EcsR3.Extensions; + +namespace EcsR3.Examples.Custom.ComputedComponents; + +public class ComputedComponentBatchApplication : EcsR3ConsoleApplication +{ + protected override void ApplicationStarted() + { + var entityCount = 10000; + ComputedComponentGroupRegistry.GetComputedGroup(); + + Console.WriteLine("Starting"); + var stopwatch = new Stopwatch(); + stopwatch.Start(); + + var entities = EntityCollection.CreateMany(entityCount); + Console.WriteLine($"Created Entities: {stopwatch.ElapsedMilliseconds}ms"); + EntityComponentAccessor.CreateComponents(entities); + Console.WriteLine($"Created Components: {stopwatch.ElapsedMilliseconds}ms"); + EntityComponentAccessor.RemoveAllComponents(entities); + Console.WriteLine($"Removed Components: {stopwatch.ElapsedMilliseconds}ms"); + + stopwatch.Stop(); + Console.WriteLine($"Time Taken: {stopwatch.ElapsedMilliseconds}ms"); + } +} \ No newline at end of file diff --git a/src/EcsR3.Examples/Custom/ComputedComponents/ComputedComponentGroupTestApplication.cs b/src/EcsR3.Examples/Custom/ComputedComponents/ComputedComponentGroupTestApplication.cs index 7156447..15c21b4 100644 --- a/src/EcsR3.Examples/Custom/ComputedComponents/ComputedComponentGroupTestApplication.cs +++ b/src/EcsR3.Examples/Custom/ComputedComponents/ComputedComponentGroupTestApplication.cs @@ -14,13 +14,13 @@ protected override void ApplicationStarted() var entities = EntityCollection.CreateMany(EntityComponentAccessor, 100000); var componentGroup = ComputedComponentGroupRegistry.GetComputedGroup(); - componentGroup.RefreshData(); + componentGroup.ForceRefresh(); Console.WriteLine("Starting"); var stopwatch = new Stopwatch(); stopwatch.Start(); for (var i = 0; i < 1000; i++) - { componentGroup.RefreshData(); } + { componentGroup.ForceRefresh(); } stopwatch.Stop(); Console.WriteLine($"Time Taken: {stopwatch.ElapsedMilliseconds}ms"); } diff --git a/src/EcsR3.Examples/Custom/ComputedComponents/ComputedEntityApplication.cs b/src/EcsR3.Examples/Custom/ComputedComponents/ComputedEntityApplication.cs index ea6eb1b..3d4b100 100644 --- a/src/EcsR3.Examples/Custom/ComputedComponents/ComputedEntityApplication.cs +++ b/src/EcsR3.Examples/Custom/ComputedComponents/ComputedEntityApplication.cs @@ -23,7 +23,7 @@ protected override void ApplicationStarted() stopwatch.Start(); for (var i = 0; i < 100; i++) { - computed.RefreshData(); + computed.ForceRefresh(); Console.WriteLine($"Computed Value: {computed.ComputedData}"); } stopwatch.Stop(); diff --git a/src/EcsR3.Examples/Custom/ComputedComponents/ComputedStructComponentApplication.cs b/src/EcsR3.Examples/Custom/ComputedComponents/ComputedStructComponentApplication.cs index 4e0dd2a..88d89d9 100644 --- a/src/EcsR3.Examples/Custom/ComputedComponents/ComputedStructComponentApplication.cs +++ b/src/EcsR3.Examples/Custom/ComputedComponents/ComputedStructComponentApplication.cs @@ -15,7 +15,7 @@ protected override void ApplicationStarted() var entities = EntityCollection.CreateMany(EntityComponentAccessor, 100000); var componentGroup = ComputedComponentGroupRegistry.GetComputedGroup(); - componentGroup.RefreshData(); + componentGroup.ForceRefresh(); var computed = new ComputedStructComponentProcessor(ComponentDatabase, componentGroup); @@ -24,7 +24,7 @@ protected override void ApplicationStarted() stopwatch.Start(); for (var i = 0; i < 100; i++) { - computed.RefreshData(); + computed.ForceRefresh(); Console.WriteLine($"Computed Value: {computed.ComputedData}"); } stopwatch.Stop(); diff --git a/src/EcsR3.Examples/Custom/ComputedComponents/CustomComputedStructComponentApplication.cs b/src/EcsR3.Examples/Custom/ComputedComponents/CustomComputedStructComponentApplication.cs index 61e3274..0c93e64 100644 --- a/src/EcsR3.Examples/Custom/ComputedComponents/CustomComputedStructComponentApplication.cs +++ b/src/EcsR3.Examples/Custom/ComputedComponents/CustomComputedStructComponentApplication.cs @@ -15,7 +15,7 @@ protected override void ApplicationStarted() var entities = EntityCollection.CreateMany(EntityComponentAccessor, 100000); var componentGroup = ComputedComponentGroupRegistry.GetComputedGroup(); - componentGroup.RefreshData(); + componentGroup.ForceRefresh(); var computed = new CustomComputedStructComponentProcessor(ComponentDatabase, componentGroup); diff --git a/src/EcsR3.Examples/ExampleApps/Performance/GroupPerformanceApplication.cs b/src/EcsR3.Examples/ExampleApps/Performance/GroupPerformanceApplication.cs index 86e35fd..70f39e1 100644 --- a/src/EcsR3.Examples/ExampleApps/Performance/GroupPerformanceApplication.cs +++ b/src/EcsR3.Examples/ExampleApps/Performance/GroupPerformanceApplication.cs @@ -39,7 +39,7 @@ protected override void ApplicationStarted() private TimeSpan ProcessEntities(int amount) { - EntityCollection.RemoveAll(); + EntityCollection.Clear(); GC.Collect(); var timer = Stopwatch.StartNew(); diff --git a/src/EcsR3.Examples/ExampleApps/Performance/OptimizedGroupPerformanceApplication.cs b/src/EcsR3.Examples/ExampleApps/Performance/OptimizedGroupPerformanceApplication.cs index c57a882..6e24741 100644 --- a/src/EcsR3.Examples/ExampleApps/Performance/OptimizedGroupPerformanceApplication.cs +++ b/src/EcsR3.Examples/ExampleApps/Performance/OptimizedGroupPerformanceApplication.cs @@ -62,7 +62,7 @@ protected override void ApplicationStarted() private TimeSpan ProcessEntities(int amount) { - EntityCollection.RemoveAll(); + EntityCollection.Clear(); GC.Collect(); var timer = Stopwatch.StartNew(); diff --git a/src/EcsR3.Examples/Program.cs b/src/EcsR3.Examples/Program.cs index 60ead46..b02a788 100644 --- a/src/EcsR3.Examples/Program.cs +++ b/src/EcsR3.Examples/Program.cs @@ -1,5 +1,6 @@ using System; using EcsR3.Examples.Custom.BatchTests; +using EcsR3.Examples.Custom.ComputedComponents; using EcsR3.Examples.Custom.Specific; using EcsR3.Examples.Custom.SystemPriority; using EcsR3.Examples.ExampleApps.BatchedGroupExample; diff --git a/src/EcsR3.Tests/EcsR3/Collections/EntityCollectionTests.cs b/src/EcsR3.Tests/EcsR3/Collections/EntityCollectionTests.cs index f1fd8cb..3955073 100644 --- a/src/EcsR3.Tests/EcsR3/Collections/EntityCollectionTests.cs +++ b/src/EcsR3.Tests/EcsR3/Collections/EntityCollectionTests.cs @@ -80,7 +80,7 @@ public void should_remove_components_from_all_entity_when_removing_all_from_coll entityCollection.EntityLookup.Add(entity2); entityCollection.EntityLookup.Add(entity3); - entityCollection.RemoveAll(); + entityCollection.Clear(); entityComponentAccessor.Received(1).RemoveAllComponents(entity1); entityComponentAccessor.Received(1).RemoveAllComponents(entity2); diff --git a/src/EcsR3.Tests/EcsR3/Observables/ComputedComponentGroupTests.cs b/src/EcsR3.Tests/EcsR3/Observables/ComputedComponentGroupTests.cs index 55ab83e..fedd772 100644 --- a/src/EcsR3.Tests/EcsR3/Observables/ComputedComponentGroupTests.cs +++ b/src/EcsR3.Tests/EcsR3/Observables/ComputedComponentGroupTests.cs @@ -23,12 +23,12 @@ public void should_create_batch_with_correct_values() var entity2 = new Entity(2, 0); var fakeEntities = new List { entity1, entity2 }; - var onChangedHandler = new Subject>(); + var onHasChanged = new Subject(); var dummyGroup = new LookupGroup([0,1], []); var mockComputedEntityGroup = Substitute.For(); mockComputedEntityGroup.Count.Returns(fakeEntities.Count); mockComputedEntityGroup.Group.Returns(dummyGroup); - mockComputedEntityGroup.OnChanged.Returns(onChangedHandler); + mockComputedEntityGroup.OnHasChanged.Returns(onHasChanged); mockComputedEntityGroup.GetEnumerator().Returns(fakeEntities.GetEnumerator()); var mockTypeLookup = Substitute.For(); diff --git a/src/EcsR3.Tests/EcsR3/Observables/ComputedEntityGroupTests.cs b/src/EcsR3.Tests/EcsR3/Observables/ComputedEntityGroupTests.cs index eb7c5fd..a048a09 100644 --- a/src/EcsR3.Tests/EcsR3/Observables/ComputedEntityGroupTests.cs +++ b/src/EcsR3.Tests/EcsR3/Observables/ComputedEntityGroupTests.cs @@ -35,9 +35,9 @@ public void should_include_matching_entity_snapshot_on_creation() var observableGroup = new ComputedEntityGroup(accessorToken, mockGroupTracker, mockEntityCollection); - Assert.Equal(2, observableGroup.CachedEntityIds.Count); - Assert.Contains(entity1, observableGroup.CachedEntityIds); - Assert.Contains(entity2, observableGroup.CachedEntityIds); + Assert.Equal(2, observableGroup.CachedEntities.Count); + Assert.Contains(entity1, observableGroup.CachedEntities); + Assert.Contains(entity2, observableGroup.CachedEntities); } [Fact] @@ -65,7 +65,7 @@ public void should_add_entity_and_raise_event_when_applicable_entity_joined() onJoinedSubject.OnNext(entity1); Assert.NotEmpty(invocations); - Assert.Single(observableGroup.CachedEntityIds, entity1); + Assert.Single(observableGroup.CachedEntities, entity1); Assert.Single(invocations, entity1); } @@ -88,7 +88,7 @@ public void should_remove_entity_and_raise_event_when_applicable_entity_removed( mockEntityCollection.GetEnumerator().Returns(entities.GetEnumerator()); var observableGroup = new ComputedEntityGroup(accessorToken, mockGroupTracker, mockEntityCollection); - observableGroup.CachedEntityIds.Add(entity1); + observableGroup.CachedEntities.Add(entity1); var removingInvocations = new List(); observableGroup.OnRemoving.Subscribe(removingInvocations.Add); @@ -103,7 +103,7 @@ public void should_remove_entity_and_raise_event_when_applicable_entity_removed( Assert.Single(removingInvocations, entity1); Assert.NotEmpty(removedInvocations); Assert.Single(removedInvocations, entity1); - Assert.Empty(observableGroup.CachedEntityIds); + Assert.Empty(observableGroup.CachedEntities); } [Fact] @@ -125,7 +125,7 @@ public void should_not_remove_entity_and_raise_event_when_applicable_entity_remo mockEntityCollection.GetEnumerator().Returns(entities.GetEnumerator()); var observableGroup = new ComputedEntityGroup(accessorToken, mockGroupTracker, mockEntityCollection); - observableGroup.CachedEntityIds.Add(entity1); + observableGroup.CachedEntities.Add(entity1); var removingInvocations = new List(); observableGroup.OnRemoving.Subscribe(removingInvocations.Add); @@ -138,7 +138,7 @@ public void should_not_remove_entity_and_raise_event_when_applicable_entity_remo Assert.NotEmpty(removingInvocations); Assert.Single(removingInvocations, entity1); Assert.Empty(removedInvocations); - Assert.Single(observableGroup.CachedEntityIds, entity1); + Assert.Single(observableGroup.CachedEntities, entity1); } [Fact] @@ -169,7 +169,7 @@ public void should_notify_change_on_adding_or_removed_happening() Assert.Equal(2, changeInvocations.Count); Assert.Equal(1, changeInvocations[0].Length); Assert.Equal(0, changeInvocations[1].Length); - Assert.Empty(observableGroup.CachedEntityIds); + Assert.Empty(observableGroup.CachedEntities); } } } diff --git a/src/EcsR3.Tests/Sanity/SanityTests.cs b/src/EcsR3.Tests/Sanity/SanityTests.cs index 283338d..c4a68f8 100644 --- a/src/EcsR3.Tests/Sanity/SanityTests.cs +++ b/src/EcsR3.Tests/Sanity/SanityTests.cs @@ -497,10 +497,10 @@ public void should_allocate_and_get_entity_correctly() { var (_, entityCollection, _, _, _, _, _) = CreateFramework(); var castEntityCollection = (EntityCollection)entityCollection; - var eca = castEntityCollection.EntityAllocationDatabase; + var ead = castEntityCollection.EntityAllocationDatabase; var entity1 = entityCollection.Create(); - var sameEntity1 = eca.GetEntity(entity1.Id); + var sameEntity1 = ead.GetEntity(entity1.Id); Assert.Equal(entity1, sameEntity1); } diff --git a/src/EcsR3/Collections/Entities/EntityAllocationDatabase.cs b/src/EcsR3/Collections/Entities/EntityAllocationDatabase.cs index cc752ea..3d2a897 100644 --- a/src/EcsR3/Collections/Entities/EntityAllocationDatabase.cs +++ b/src/EcsR3/Collections/Entities/EntityAllocationDatabase.cs @@ -20,6 +20,7 @@ public class EntityAllocationDatabase : IEntityAllocationDatabase, IDisposable public int ComponentLength { get; protected set; } public int EntityLength { get; protected set; } + public int EntityCount => EntityLength; private CompositeDisposable _subs = new CompositeDisposable(); private object _lock = new object(); @@ -48,10 +49,15 @@ public EntityAllocationDatabase(IEntityIdPool entityIdPool, IComponentDatabase c .AddTo(_subs); ComponentLength = componentTypeLookup.AllComponentTypeIds.Length; - ComponentAllocationData = new int[ComponentLength, EntityLength]; + ResetSize(); + ResizeAllEntityAllocations(EntityLength); + } + + public void ResetSize() + { EntityCreationHashes = new int[EntityLength]; + ComponentAllocationData = new int[ComponentLength, EntityLength]; new Span2D(ComponentAllocationData).Fill(NoAllocation); - ResizeAllEntityAllocations(EntityLength); } public Entity AllocateEntity(int? id = null) @@ -323,6 +329,12 @@ public int GetEntityComponentAllocation(int componentTypeId, Entity entity) public void PreAllocate(int count) { ResizeAllEntityAllocations(count); } + public void Clear() + { + EntityIdPool.Clear(); + ResetSize(); + } + public int[] GetEntityComponentAllocation(int componentTypeId, Entity[] entities) { ReadOnlySpan2D spanData; diff --git a/src/EcsR3/Collections/Entities/EntityCollection.cs b/src/EcsR3/Collections/Entities/EntityCollection.cs index 635f3d3..dd9daf1 100644 --- a/src/EcsR3/Collections/Entities/EntityCollection.cs +++ b/src/EcsR3/Collections/Entities/EntityCollection.cs @@ -71,7 +71,7 @@ public void Remove(Entity entity) _onRemoved.OnNext(entity); } - public void RemoveAll() + public void Clear() { Entity[] entities; lock (_lock) @@ -103,7 +103,7 @@ public void Dispose() { lock (_lock) { - RemoveAll(); + Clear(); _onAdded.Dispose(); _onRemoved.Dispose(); } diff --git a/src/EcsR3/Collections/Entities/IEntityAllocationDatabase.cs b/src/EcsR3/Collections/Entities/IEntityAllocationDatabase.cs index 3a820ac..9b2098b 100644 --- a/src/EcsR3/Collections/Entities/IEntityAllocationDatabase.cs +++ b/src/EcsR3/Collections/Entities/IEntityAllocationDatabase.cs @@ -6,6 +6,11 @@ public interface IEntityAllocationDatabase : IBatchEntityAllocationDatabase { public const int NoAllocation = -1; + /// + /// The amount of entities it currently tracks + /// + int EntityCount { get; } + Entity? GetEntity(int entityId); Entity AllocateEntity(int? id = null); Entity[] AllocateEntities(int count); @@ -19,6 +24,7 @@ public interface IEntityAllocationDatabase : IBatchEntityAllocationDatabase Entity[] GetEntitiesWithComponent(int componentTypeId); int GetEntityComponentAllocation(int componentTypeId, Entity entity); void PreAllocate(int count); + void Clear(); } public interface IBatchEntityAllocationDatabase diff --git a/src/EcsR3/Collections/Entities/IEntityCollection.cs b/src/EcsR3/Collections/Entities/IEntityCollection.cs index 644d88f..f70f8a9 100644 --- a/src/EcsR3/Collections/Entities/IEntityCollection.cs +++ b/src/EcsR3/Collections/Entities/IEntityCollection.cs @@ -40,6 +40,6 @@ public interface IEntityCollection : IReadOnlyEntityCollection /// /// Removes all entities from the collection /// - void RemoveAll(); + void Clear(); } } diff --git a/src/EcsR3/Computeds/Components/ComputedComponentGroup.cs b/src/EcsR3/Computeds/Components/ComputedComponentGroup.cs index 5361534..bdffb1f 100644 --- a/src/EcsR3/Computeds/Components/ComputedComponentGroup.cs +++ b/src/EcsR3/Computeds/Components/ComputedComponentGroup.cs @@ -6,7 +6,6 @@ using EcsR3.Computeds.Entities.Conventions; using EcsR3.Extensions; using EcsR3.Groups; -using R3; namespace EcsR3.Computeds.Components { @@ -17,9 +16,16 @@ public class ComputedComponentGroup : ComputedFromEntityGroup[] _internalCache = Array.Empty>(); - + public ref ReadOnlyMemory> Batches + { + get + { + if (IsDirty) { ForceRefresh(); } + return ref ComputedData; + } + } + public ComputedComponentGroup(IComponentTypeLookup componentTypeLookup, IEntityAllocationDatabase allocationDatabase, IComputedEntityGroup computedEntityGroup) : base(computedEntityGroup) { AllocationDatabase = allocationDatabase; @@ -29,7 +35,6 @@ public ComputedComponentGroup(IComponentTypeLookup componentTypeLookup, IEntityA { throw new ArgumentException("ComputedEntityGroup must match component types"); } Group = DataSource.Group; - RefreshData(); } protected override bool UpdateComputedData() @@ -58,7 +63,15 @@ public class ComputedComponentGroup : ComputedFromEntityGroup[] _internalCache = Array.Empty>(); public LookupGroup Group { get; } - + public ref ReadOnlyMemory> Batches + { + get + { + if (IsDirty) { ForceRefresh(); } + return ref ComputedData; + } + } + public ComputedComponentGroup(IComponentTypeLookup componentTypeLookup, IEntityAllocationDatabase allocationDatabase, IComputedEntityGroup computedEntityGroup) : base(computedEntityGroup) { @@ -73,7 +86,6 @@ public ComputedComponentGroup(IComponentTypeLookup componentTypeLookup, IEntityA Group = DataSource.Group; ComputedData = new ReadOnlyMemory>(_internalCache); - RefreshData(); } protected override bool UpdateComputedData() @@ -108,6 +120,14 @@ public class ComputedComponentGroup : ComputedFromEntityGroup[] _internalCache = Array.Empty>(); public LookupGroup Group { get; } + public ref ReadOnlyMemory> Batches + { + get + { + if (IsDirty) { ForceRefresh(); } + return ref ComputedData; + } + } public ComputedComponentGroup(IComponentTypeLookup componentTypeLookup, IEntityAllocationDatabase allocationDatabase, IComputedEntityGroup computedEntityGroup) : base( computedEntityGroup) @@ -123,7 +143,6 @@ public ComputedComponentGroup(IComponentTypeLookup componentTypeLookup, IEntityA } Group = DataSource.Group; - RefreshData(); } protected override bool UpdateComputedData() @@ -161,6 +180,14 @@ public class ComputedComponentGroup : ComputedFromEntityGroup[] _internalCache = Array.Empty>(); public LookupGroup Group { get; } + public ref ReadOnlyMemory> Batches + { + get + { + if (IsDirty) { ForceRefresh(); } + return ref ComputedData; + } + } public ComputedComponentGroup(IComponentTypeLookup componentTypeLookup, IEntityAllocationDatabase allocationDatabase, IComputedEntityGroup computedEntityGroup) : base( computedEntityGroup) @@ -177,7 +204,6 @@ public ComputedComponentGroup(IComponentTypeLookup componentTypeLookup, IEntityA } Group = DataSource.Group; - RefreshData(); } protected override bool UpdateComputedData() @@ -218,6 +244,14 @@ public class ComputedComponentGroup : ComputedFromEntityGrou private ComponentBatch[] _internalCache = Array.Empty>(); public LookupGroup Group { get; } + public ref ReadOnlyMemory> Batches + { + get + { + if (IsDirty) { ForceRefresh(); } + return ref ComputedData; + } + } public ComputedComponentGroup(IComponentTypeLookup componentTypeLookup, IEntityAllocationDatabase allocationDatabase, IComputedEntityGroup computedEntityGroup) : base( computedEntityGroup) @@ -235,7 +269,6 @@ public ComputedComponentGroup(IComponentTypeLookup componentTypeLookup, IEntityA } Group = DataSource.Group; - RefreshData(); } protected override bool UpdateComputedData() @@ -279,7 +312,14 @@ public class ComputedComponentGroup : ComputedFromEntity private ComponentBatch[] _internalCache = Array.Empty>(); public LookupGroup Group { get; } - public Observable OnRefreshed => OnChanged.Select(x => Unit.Default); + public ref ReadOnlyMemory> Batches + { + get + { + if (IsDirty) { ForceRefresh(); } + return ref ComputedData; + } + } public ComputedComponentGroup(IComponentTypeLookup componentTypeLookup, IEntityAllocationDatabase allocationDatabase, IComputedEntityGroup computedEntityGroup) : base( computedEntityGroup) @@ -298,7 +338,6 @@ public ComputedComponentGroup(IComponentTypeLookup componentTypeLookup, IEntityA } Group = DataSource.Group; - RefreshData(); } protected override bool UpdateComputedData() @@ -345,7 +384,14 @@ public class ComputedComponentGroup : ComputedFromEn private ComponentBatch[] _internalCache = Array.Empty>(); public LookupGroup Group { get; } - public Observable OnRefreshed => OnChanged.Select(x => Unit.Default); + public ref ReadOnlyMemory> Batches + { + get + { + if (IsDirty) { ForceRefresh(); } + return ref ComputedData; + } + } public ComputedComponentGroup(IComponentTypeLookup componentTypeLookup, IEntityAllocationDatabase allocationDatabase, IComputedEntityGroup computedEntityGroup) : base( computedEntityGroup) @@ -365,7 +411,6 @@ public ComputedComponentGroup(IComponentTypeLookup componentTypeLookup, IEntityA } Group = DataSource.Group; - RefreshData(); } protected override bool UpdateComputedData() diff --git a/src/EcsR3/Computeds/Components/Conventions/ComputedFromComponentGroup.cs b/src/EcsR3/Computeds/Components/Conventions/ComputedFromComponentGroup.cs index 01a326a..b6f611c 100644 --- a/src/EcsR3/Computeds/Components/Conventions/ComputedFromComponentGroup.cs +++ b/src/EcsR3/Computeds/Components/Conventions/ComputedFromComponentGroup.cs @@ -7,7 +7,7 @@ namespace EcsR3.Computeds.Components.Conventions { - public abstract class ComputedFromComponentGroup : ComputedFromData> where T1 : IComponent + public abstract class ComputedFromComponentGroup : LazyComputedFromData> where T1 : IComponent { public IComponentDatabase ComponentDatabase { get; } protected (Entity, T1)[] BatchCache = Array.Empty<(Entity, T1)>(); @@ -21,7 +21,7 @@ protected ComputedFromComponentGroup(IComponentDatabase componentDatabase, ComponentPool1 = componentDatabase.GetPoolFor(); } - protected override Observable RefreshWhen() => DataSource.OnChanged.Select(x => Unit.Default); + protected override Observable RefreshWhen() => DataSource.OnHasChange.Select(x => Unit.Default); protected abstract bool UpdateComputedData(ReadOnlyMemory<(Entity, T1)> componentData); @@ -44,7 +44,7 @@ protected override bool UpdateComputedData() } } - public abstract class ComputedFromComponentGroup : ComputedFromData> + public abstract class ComputedFromComponentGroup : LazyComputedFromData> where T1 : IComponent where T2 : IComponent { public IComponentDatabase ComponentDatabase { get; } @@ -61,7 +61,7 @@ protected ComputedFromComponentGroup(IComponentDatabase componentDatabase, ComponentPool2 = componentDatabase.GetPoolFor(); } - protected override Observable RefreshWhen() => DataSource.OnChanged.Select(x => Unit.Default); + protected override Observable RefreshWhen() => DataSource.OnHasChange.Select(x => Unit.Default); protected abstract bool UpdateComputedData(ReadOnlyMemory<(Entity, T1, T2)> componentData); @@ -85,7 +85,7 @@ protected override bool UpdateComputedData() } } - public abstract class ComputedFromComponentGroup : ComputedFromData> + public abstract class ComputedFromComponentGroup : LazyComputedFromData> where T1 : IComponent where T2 : IComponent where T3 : IComponent { public IComponentDatabase ComponentDatabase { get; } @@ -104,7 +104,7 @@ protected ComputedFromComponentGroup(IComponentDatabase componentDatabase, ComponentPool3 = componentDatabase.GetPoolFor(); } - protected override Observable RefreshWhen() => DataSource.OnChanged.Select(x => Unit.Default); + protected override Observable RefreshWhen() => DataSource.OnHasChange.Select(x => Unit.Default); protected abstract bool UpdateComputedData(ReadOnlyMemory<(Entity, T1, T2, T3)> componentData); @@ -130,7 +130,7 @@ protected override bool UpdateComputedData() } } - public abstract class ComputedFromComponentGroup : ComputedFromData> + public abstract class ComputedFromComponentGroup : LazyComputedFromData> where T1 : IComponent where T2 : IComponent where T3 : IComponent where T4 : IComponent { public IComponentDatabase ComponentDatabase { get; } @@ -151,7 +151,7 @@ protected ComputedFromComponentGroup(IComponentDatabase componentDatabase, ComponentPool4 = componentDatabase.GetPoolFor(); } - protected override Observable RefreshWhen() => DataSource.OnChanged.Select(x => Unit.Default); + protected override Observable RefreshWhen() => DataSource.OnHasChange.Select(x => Unit.Default); protected abstract bool UpdateComputedData(ReadOnlyMemory<(Entity, T1, T2, T3, T4)> componentData); diff --git a/src/EcsR3/Computeds/Components/IComputedComponentGroup.cs b/src/EcsR3/Computeds/Components/IComputedComponentGroup.cs index 0f571ac..9246f8a 100644 --- a/src/EcsR3/Computeds/Components/IComputedComponentGroup.cs +++ b/src/EcsR3/Computeds/Components/IComputedComponentGroup.cs @@ -8,14 +8,67 @@ namespace EcsR3.Computeds.Components public interface IComputedComponentGroup : IComputedGroup { IComputedEntityGroup DataSource { get; } - void RefreshData(); - } - - public interface IComputedComponentGroup : IComputed>>, IComputedComponentGroup where T1 : IComponent {} - public interface IComputedComponentGroup : IComputed>>, IComputedComponentGroup where T1 : IComponent where T2 : IComponent {} - public interface IComputedComponentGroup : IComputed>>, IComputedComponentGroup where T1 : IComponent where T2 : IComponent where T3 : IComponent {} - public interface IComputedComponentGroup : IComputed>>, IComputedComponentGroup where T1 : IComponent where T2 : IComponent where T3 : IComponent where T4 : IComponent {} - public interface IComputedComponentGroup : IComputed>>, IComputedComponentGroup where T1 : IComponent where T2 : IComponent where T3 : IComponent where T4 : IComponent where T5 : IComponent {} - public interface IComputedComponentGroup : IComputed>>, IComputedComponentGroup where T1 : IComponent where T2 : IComponent where T3 : IComponent where T4 : IComponent where T5 : IComponent where T6 : IComponent {} - public interface IComputedComponentGroup : IComputed>>, IComputedComponentGroup where T1 : IComponent where T2 : IComponent where T3 : IComponent where T4 : IComponent where T5 : IComponent where T6 : IComponent where T7 : IComponent {} + } + + public interface IComputedComponentGroup : ILazyComputed>>, + IComputedComponentGroup where T1 : IComponent + { + ref ReadOnlyMemory> Batches { get; } + } + + public interface IComputedComponentGroup : ILazyComputed>>, + IComputedComponentGroup where T1 : IComponent where T2 : IComponent + { + ref ReadOnlyMemory> Batches { get; } + } + + public interface IComputedComponentGroup : ILazyComputed>>, + IComputedComponentGroup where T1 : IComponent where T2 : IComponent where T3 : IComponent + { + ref ReadOnlyMemory> Batches { get; } + } + + public interface + IComputedComponentGroup : ILazyComputed>>, + IComputedComponentGroup where T1 : IComponent where T2 : IComponent where T3 : IComponent where T4 : IComponent + { + ref ReadOnlyMemory> Batches { get; } + } + + public interface + IComputedComponentGroup : ILazyComputed>>, + IComputedComponentGroup where T1 : IComponent + where T2 : IComponent + where T3 : IComponent + where T4 : IComponent + where T5 : IComponent + { + ref ReadOnlyMemory> Batches { get; } + } + + public interface + IComputedComponentGroup : + ILazyComputed>>, IComputedComponentGroup where T1 : IComponent + where T2 : IComponent + where T3 : IComponent + where T4 : IComponent + where T5 : IComponent + where T6 : IComponent + { + ref ReadOnlyMemory> Batches { get; } + } + + public interface + IComputedComponentGroup : + ILazyComputed>>, + IComputedComponentGroup where T1 : IComponent + where T2 : IComponent + where T3 : IComponent + where T4 : IComponent + where T5 : IComponent + where T6 : IComponent + where T7 : IComponent + { + ref ReadOnlyMemory> Batches { get; } + } } \ No newline at end of file diff --git a/src/EcsR3/Computeds/Entities/ComputedEntityGroup.cs b/src/EcsR3/Computeds/Entities/ComputedEntityGroup.cs index c6243f1..d60f2f2 100644 --- a/src/EcsR3/Computeds/Entities/ComputedEntityGroup.cs +++ b/src/EcsR3/Computeds/Entities/ComputedEntityGroup.cs @@ -1,6 +1,5 @@ using System.Collections; using System.Collections.Generic; -using System.Linq; using EcsR3.Collections.Entities; using EcsR3.Entities; using EcsR3.Groups; @@ -14,21 +13,13 @@ public class ComputedEntityGroup : IComputedEntityGroup { public LookupGroup Group { get; } - public readonly HashSet CachedEntityIds; + public readonly HashSet CachedEntities; public readonly CompositeDisposable Subscriptions; - public IReadOnlyCollection Value - { - get - { - lock (_lock) - { return EnumerableEntities.ToArray(); } - } - } - - public IEnumerable EnumerableEntities => CachedEntityIds; + public IReadOnlyCollection Value => CachedEntities; public Observable> OnChanged => Observable.Merge(OnAdded, OnRemoved).Select(x => Value); + public Observable OnHasChanged => Observable.Merge(OnAdded, OnRemoved).Select(x => Unit.Default); public Observable OnAdded => _onEntityAdded; public Observable OnRemoved => _onEntityRemoved; @@ -52,7 +43,7 @@ public ComputedEntityGroup(LookupGroup group, IComputedEntityGroupTracker tracke _onEntityRemoving = new Subject(); Subscriptions = new CompositeDisposable(); - CachedEntityIds = new HashSet(); + CachedEntities = new HashSet(); GroupTracker.OnEntityJoinedGroup .Subscribe(OnEntityJoinedGroup) @@ -66,13 +57,12 @@ public ComputedEntityGroup(LookupGroup group, IComputedEntityGroupTracker tracke .Subscribe(OnEntityLeftGroup) .AddTo(Subscriptions); - CachedEntityIds = new HashSet(GroupTracker.GetMatchedEntities()); - + CachedEntities = new HashSet(GroupTracker.GetMatchedEntities()); } public void OnEntityJoinedGroup(Entity entity) { - lock (_lock) { CachedEntityIds.Add(entity); } + lock (_lock) { CachedEntities.Add(entity); } _onEntityAdded.OnNext(entity); } @@ -83,14 +73,14 @@ public void OnEntityLeavingGroup(Entity entity) public void OnEntityLeftGroup(Entity entity) { - lock (_lock) { CachedEntityIds.Remove(entity); } + lock (_lock) { CachedEntities.Remove(entity); } _onEntityRemoved.OnNext(entity); } public bool Contains(Entity entity) { lock(_lock) - { return CachedEntityIds.Contains(entity); } + { return CachedEntities.Contains(entity); } } public void Dispose() @@ -109,12 +99,12 @@ public int Count get { lock (_lock) - { return CachedEntityIds.Count; } + { return CachedEntities.Count; } } } public IEnumerator GetEnumerator() - { return EnumerableEntities.GetEnumerator(); } + { return CachedEntities.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } diff --git a/src/EcsR3/Computeds/Entities/Conventions/ComputedEntityGroupFromEntityGroup.cs b/src/EcsR3/Computeds/Entities/Conventions/ComputedEntityGroupFromEntityGroup.cs index 965abcc..686a138 100644 --- a/src/EcsR3/Computeds/Entities/Conventions/ComputedEntityGroupFromEntityGroup.cs +++ b/src/EcsR3/Computeds/Entities/Conventions/ComputedEntityGroupFromEntityGroup.cs @@ -28,11 +28,12 @@ public IReadOnlyCollection Value } public Observable> OnChanged => Observable.Merge(OnAdded, OnRemoved).Select(x => Value); + public Observable OnHasChanged => Observable.Merge(OnAdded, OnRemoved).Select(x => Unit.Default); public Observable OnAdded => _onEntityAdded; public Observable OnRemoved => _onEntityRemoved; public Observable OnRemoving => _onEntityRemoving; - + private readonly Subject _onEntityAdded, _onEntityRemoved, _onEntityRemoving; private readonly object _lock = new object(); diff --git a/src/EcsR3/Computeds/Entities/Conventions/ComputedFromEntityGroup.cs b/src/EcsR3/Computeds/Entities/Conventions/ComputedFromEntityGroup.cs index 4f6f701..dd61032 100644 --- a/src/EcsR3/Computeds/Entities/Conventions/ComputedFromEntityGroup.cs +++ b/src/EcsR3/Computeds/Entities/Conventions/ComputedFromEntityGroup.cs @@ -3,11 +3,11 @@ namespace EcsR3.Computeds.Entities.Conventions { - public abstract class ComputedFromEntityGroup : ComputedFromData + public abstract class ComputedFromEntityGroup : LazyComputedFromData { protected ComputedFromEntityGroup(IComputedEntityGroup dataSource) : base(dataSource) {} - protected override Observable RefreshWhen() => DataSource.OnChanged.Select(x => Unit.Default); + protected override Observable RefreshWhen() => DataSource.OnHasChanged; } } \ No newline at end of file diff --git a/src/EcsR3/Computeds/Entities/IComputedEntityGroup.cs b/src/EcsR3/Computeds/Entities/IComputedEntityGroup.cs index b52e712..bc0dc94 100644 --- a/src/EcsR3/Computeds/Entities/IComputedEntityGroup.cs +++ b/src/EcsR3/Computeds/Entities/IComputedEntityGroup.cs @@ -21,5 +21,10 @@ public interface IComputedEntityGroup : IReadOnlyEntityCollection, IComputedGrou /// Event stream for when an entity is about to be removed from this group /// Observable OnRemoving { get; } + + /// + /// Event stream for when data has changed but you dont care about what the data is + /// + Observable OnHasChanged { get; } } } \ No newline at end of file diff --git a/src/EcsR3/Entities/Accessors/EntityComponentAccessor.cs b/src/EcsR3/Entities/Accessors/EntityComponentAccessor.cs index 6c01715..1754089 100644 --- a/src/EcsR3/Entities/Accessors/EntityComponentAccessor.cs +++ b/src/EcsR3/Entities/Accessors/EntityComponentAccessor.cs @@ -15,7 +15,7 @@ public class EntityComponentAccessor : IEntityComponentAccessor public IEntityAllocationDatabase EntityAllocationDatabase { get; } public IComponentDatabase ComponentDatabase { get; } public IEntityChangeRouter EntityChangeRouter { get; } - + public EntityComponentAccessor(IComponentTypeLookup componentTypeLookup, IEntityAllocationDatabase entityAllocationDatabase, IComponentDatabase componentDatabase, IEntityChangeRouter entityChangeRouter) { ComponentTypeLookup = componentTypeLookup; @@ -57,7 +57,7 @@ public ref T CreateComponent(Entity entity) where T : struct, IComponent ComponentDatabase.Set(componentTypeId, allocationIds, newComponents); return componentTypeId; } - + public void CreateComponent(Entity[] entities) where T : IComponent, new() { var componentTypeIds = new[] @@ -238,7 +238,7 @@ public RefBuffer GetComponentRef(Entity[] entities) where T : struct, ICom var allocationIds = EntityAllocationDatabase.GetEntityComponentAllocation(componentTypeId, entities); return ComponentDatabase.GetRef(componentTypeId, allocationIds); } - + public void UpdateComponent(Entity entity, T newValue) where T : struct, IComponent { var componentTypeId = ComponentTypeLookup.GetComponentTypeId(typeof(T)); diff --git a/src/EcsR3/Entities/Routing/EntityChangeRouter.cs b/src/EcsR3/Entities/Routing/EntityChangeRouter.cs index a91ef55..63f5705 100644 --- a/src/EcsR3/Entities/Routing/EntityChangeRouter.cs +++ b/src/EcsR3/Entities/Routing/EntityChangeRouter.cs @@ -103,8 +103,9 @@ There is a worry that anyone could subscribe elsewhere and want to use the data need to convert it to an array in that scenario, if they didnt they would just have garbage data. */ ReadOnlyMemory bufferAsMemory = buffer; + var sourceContract = source[currentContract]; for (var j = 0; j < entities.Length; j++) - { source[currentContract].OnNext(new EntityChanges(entities[j], bufferAsMemory[..(lastUsedIndexInBuffer+1)])); } + { sourceContract.OnNext(new EntityChanges(entities[j], bufferAsMemory[..(lastUsedIndexInBuffer+1)])); } } } } diff --git a/src/EcsR3/Extensions/IEntityComponentAccessorExtensions.cs b/src/EcsR3/Extensions/IEntityComponentAccessorExtensions.cs index 47c2d52..1b46bc9 100644 --- a/src/EcsR3/Extensions/IEntityComponentAccessorExtensions.cs +++ b/src/EcsR3/Extensions/IEntityComponentAccessorExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using EcsR3.Components; using EcsR3.Entities; @@ -68,5 +69,11 @@ public static bool MatchesGroup(this IEntityComponentAccessor accessor, Entity e return group.ExcludedComponents.Length == 0 || !accessor.HasAnyComponents(entity, group.ExcludedComponents); } + + public static void RemoveAllComponents(this IEntityComponentAccessor accessor, IReadOnlyList entities) + { + for (var i = 0; i < entities.Count; i++) + { accessor.RemoveAllComponents(entities[i]); } + } } } \ No newline at end of file diff --git a/src/EcsR3/Systems/Batching/Convention/BatchedMixedSystem.cs b/src/EcsR3/Systems/Batching/Convention/BatchedMixedSystem.cs index 307ee66..ea6d796 100644 --- a/src/EcsR3/Systems/Batching/Convention/BatchedMixedSystem.cs +++ b/src/EcsR3/Systems/Batching/Convention/BatchedMixedSystem.cs @@ -18,19 +18,15 @@ public abstract class BatchedMixedSystem : RawBatchedSystem protected BatchedMixedSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) {} - protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[]) componentPools) + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[]) componentPools) { - var (components1, components2) = componentPools; if (ShouldMultithread) { - ThreadHandler.For(0, componentBatches.Length, i => - { - var batch = componentBatches.Span[i]; - Process(batch.Entity, ref components1[batch.Component1Allocation], components2[batch.Component2Allocation]); - }); + ProcessGroupWithMultithreading(ref componentBatches, componentPools); return; } + var (components1, components2) = componentPools; var batches = componentBatches.Span; for (var i = 0; i < batches.Length; i++) { @@ -38,6 +34,18 @@ protected override void ProcessGroup(ReadOnlyMemory> comp Process(batch.Entity, ref components1[batch.Component1Allocation], components2[batch.Component2Allocation]); } } + + protected void ProcessGroupWithMultithreading(ref ReadOnlyMemory> componentBatches, + (T1[], T2[]) componentPools) + { + var (components1, components2) = componentPools; + var closureBatches = componentBatches; + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + Process(batch.Entity, ref components1[batch.Component1Allocation], components2[batch.Component2Allocation]); + }); + } } public abstract class BatchedMixedSystem : RawBatchedSystem @@ -51,21 +59,15 @@ protected BatchedMixedSystem(IComponentDatabase componentDatabase, IEntityCompon { } - protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[]) componentPools) + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[]) componentPools) { - var (components1, components2, components3) = componentPools; - if (ShouldMultithread) { - ThreadHandler.For(0, componentBatches.Length, i => - { - var batch = componentBatches.Span[i]; - Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation], - components3[batch.Component3Allocation]); - }); + ProcessGroupWithMultithreading(ref componentBatches, componentPools); return; } - + + var (components1, components2, components3) = componentPools; var batches = componentBatches.Span; for (var i = 0; i < batches.Length; i++) { @@ -74,6 +76,18 @@ protected override void ProcessGroup(ReadOnlyMemory> components3[batch.Component3Allocation]); } } + + protected void ProcessGroupWithMultithreading(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[]) componentPools) + { + var (components1, components2, components3) = componentPools; + var closureBatches = componentBatches; + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation], + components3[batch.Component3Allocation]); + }); + } } public abstract class BatchedMixedSystem : RawBatchedSystem @@ -88,21 +102,16 @@ protected BatchedMixedSystem(IComponentDatabase componentDatabase, IEntityCompon { } - protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[]) componentPools) + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[]) componentPools) { - var (components1, components2, components3, components4) = componentPools; if (ShouldMultithread) { - ThreadHandler.For(0, componentBatches.Length, i => - { - var batch = componentBatches.Span[i]; - Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation], - components3[batch.Component3Allocation], components4[batch.Component4Allocation]); - }); + ProcessGroupWithMultithreading(ref componentBatches, componentPools); return; } - + + var (components1, components2, components3, components4) = componentPools; var batches = componentBatches.Span; for (var i = 0; i < batches.Length; i++) { @@ -111,6 +120,18 @@ protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[]) componentPools) + { + var (components1, components2, components3, components4) = componentPools; + var closureBatches = componentBatches; + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation], + components3[batch.Component3Allocation], components4[batch.Component4Allocation]); + }); + } } public abstract class BatchedMixedSystem : RawBatchedSystem @@ -123,25 +144,17 @@ public abstract class BatchedMixedSystem : RawBatchedSystem< protected abstract void Process(Entity entity, ref T1 component1, ref T2 component2, ref T3 component3, T4 component4, T5 component5); protected BatchedMixedSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) - { - } + {} - protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[]) componentPools) + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[]) componentPools) { - var (components1, components2, components3, components4, components5) = componentPools; - if (ShouldMultithread) { - ThreadHandler.For(0, componentBatches.Length, i => - { - var batch = componentBatches.Span[i]; - Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation], - ref components3[batch.Component3Allocation], components4[batch.Component4Allocation], - components5[batch.Component5Allocation]); - }); + ProcessGroupWithMultithreading(ref componentBatches, componentPools); return; } + var (components1, components2, components3, components4, components5) = componentPools; var batches = componentBatches.Span; for (var i = 0; i < componentBatches.Length; i++) { @@ -151,6 +164,19 @@ protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[]) componentPools) + { + var (components1, components2, components3, components4, components5) = componentPools; + var closureBatches = componentBatches; + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation], + ref components3[batch.Component3Allocation], components4[batch.Component4Allocation], + components5[batch.Component5Allocation]); + }); + } } public abstract class BatchedMixedSystem : RawBatchedSystem @@ -164,34 +190,40 @@ public abstract class BatchedMixedSystem : RawBatchedSys protected abstract void Process(Entity entity, ref T1 component1, ref T2 component2, ref T3 component3, T4 component4, T5 component5, T6 component6); protected BatchedMixedSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) - { - } + {} - protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[]) componentPools) + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[]) componentPools) { - var (components1, components2, components3, components4, components5, components6) = componentPools; if (ShouldMultithread) { - ThreadHandler.For(0, componentBatches.Length, i => - { - var batch = componentBatches.Span[i]; - Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation], - ref components3[batch.Component3Allocation], components4[batch.Component4Allocation], - components5[batch.Component5Allocation], components6[batch.Component6Allocation]);; - }); + ProcessGroupWithMultithreading(ref componentBatches, componentPools); return; } + var (components1, components2, components3, components4, components5, components6) = componentPools; var batches = componentBatches.Span; for (var i = 0; i < componentBatches.Length; i++) { var batch = batches[i]; Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation], ref components3[batch.Component3Allocation], components4[batch.Component4Allocation], - components5[batch.Component5Allocation], components6[batch.Component6Allocation]);;; + components5[batch.Component5Allocation], components6[batch.Component6Allocation]); } } + + protected void ProcessGroupWithMultithreading(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[]) componentPools) + { + var (components1, components2, components3, components4, components5, components6) = componentPools; + var closureBatches = componentBatches; + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation], + ref components3[batch.Component3Allocation], components4[batch.Component4Allocation], + components5[batch.Component5Allocation], components6[batch.Component6Allocation]); + }); + } } public abstract class BatchedMixedSystem : RawBatchedSystem @@ -209,23 +241,15 @@ protected BatchedMixedSystem(IComponentDatabase componentDatabase, IEntityCompon { } - protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[], T7[]) componentPools) + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[], T7[]) componentPools) { - var (components1, components2, components3, components4, components5, components6, components7) = componentPools; - if (ShouldMultithread) { - ThreadHandler.For(0, componentBatches.Length, i => - { - var batch = componentBatches.Span[i]; - Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation], - ref components3[batch.Component3Allocation], ref components4[batch.Component4Allocation], - components5[batch.Component5Allocation], components6[batch.Component6Allocation], - components7[batch.Component7Allocation]); - }); + ProcessGroupWithMultithreading(ref componentBatches, componentPools); return; } + var (components1, components2, components3, components4, components5, components6, components7) = componentPools; var batches = componentBatches.Span; for (var i = 0; i < componentBatches.Length; i++) { @@ -236,5 +260,19 @@ protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[], T7[]) componentPools) + { + var (components1, components2, components3, components4, components5, components6, components7) = componentPools; + var closureBatches = componentBatches; + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation], + ref components3[batch.Component3Allocation], ref components4[batch.Component4Allocation], + components5[batch.Component5Allocation], components6[batch.Component6Allocation], + components7[batch.Component7Allocation]); + }); + } } } \ No newline at end of file diff --git a/src/EcsR3/Systems/Batching/Convention/BatchedRefSystem.cs b/src/EcsR3/Systems/Batching/Convention/BatchedRefSystem.cs index 78bad78..94ed218 100644 --- a/src/EcsR3/Systems/Batching/Convention/BatchedRefSystem.cs +++ b/src/EcsR3/Systems/Batching/Convention/BatchedRefSystem.cs @@ -15,18 +15,13 @@ public abstract class BatchedRefSystem : RawBatchedSystem protected abstract void Process(Entity entity, ref T1 component1); protected BatchedRefSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) - { - } + {} - protected override void ProcessGroup(ReadOnlyMemory> componentBatches, T1[] componentPools) + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, T1[] componentPools) { if (ShouldMultithread) { - ThreadHandler.For(0, componentBatches.Length, i => - { - var batch = componentBatches.Span[i]; - Process(batch.Entity, ref componentPools[batch.Component1Allocation]); - }); + ProcessGroupWithMultithreading(ref componentBatches, componentPools); return; } @@ -37,6 +32,17 @@ protected override void ProcessGroup(ReadOnlyMemory> componen Process(batch.Entity, ref componentPools[batch.Component1Allocation]); } } + + protected void ProcessGroupWithMultithreading(ref ReadOnlyMemory> componentBatches, + T1[] componentPools) + { + var closureBatches = componentBatches; + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + Process(batch.Entity, ref componentPools[batch.Component1Allocation]); + }); + } } public abstract class BatchedRefSystem : RawBatchedSystem @@ -49,19 +55,15 @@ protected BatchedRefSystem(IComponentDatabase componentDatabase, IEntityComponen { } - protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[]) componentPools) + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[]) componentPools) { - var (components1, components2) = componentPools; if (ShouldMultithread) { - ThreadHandler.For(0, componentBatches.Length, i => - { - var batch = componentBatches.Span[i]; - Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation]); - }); + ProcessGroupWithMultithreading(ref componentBatches, componentPools); return; } + var (components1, components2) = componentPools; var batches = componentBatches.Span; for (var i = 0; i < batches.Length; i++) { @@ -69,6 +71,18 @@ protected override void ProcessGroup(ReadOnlyMemory> comp Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation]); } } + + protected void ProcessGroupWithMultithreading(ref ReadOnlyMemory> componentBatches, + (T1[], T2[]) componentPools) + { + var (components1, components2) = componentPools; + var closureBatches = componentBatches; + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation]); + }); + } } public abstract class BatchedRefSystem : RawBatchedSystem @@ -79,24 +93,18 @@ public abstract class BatchedRefSystem : RawBatchedSystem protected abstract void Process(Entity entity, ref T1 component1, ref T2 component2, ref T3 component3); protected BatchedRefSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) - { - } + {} - protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[]) componentPools) + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[]) componentPools) { - var (components1, components2, components3) = componentPools; if (ShouldMultithread) { - ThreadHandler.For(0, componentBatches.Length, i => - { - var batch = componentBatches.Span[i]; - Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation], - ref components3[batch.Component3Allocation]); - }); + ProcessGroupWithMultithreading(ref componentBatches, componentPools); return; } + var (components1, components2, components3) = componentPools; var batches = componentBatches.Span; for (var i = 0; i < batches.Length; i++) { @@ -105,6 +113,18 @@ protected override void ProcessGroup(ReadOnlyMemory> ref components3[batch.Component3Allocation]); } } + + protected void ProcessGroupWithMultithreading(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[]) componentPools) + { + var (components1, components2, components3) = componentPools; + var closureBatches = componentBatches; + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation], + ref components3[batch.Component3Allocation]); + }); + } } public abstract class BatchedRefSystem : RawBatchedSystem @@ -116,24 +136,17 @@ public abstract class BatchedRefSystem : RawBatchedSystem> componentBatches, (T1[], T2[], T3[], T4[]) componentPools) + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[]) componentPools) { - var (components1, components2, components3, components4) = componentPools; - if (ShouldMultithread) { - ThreadHandler.For(0, componentBatches.Length, i => - { - var batch = componentBatches.Span[i]; - Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation], - ref components3[batch.Component3Allocation], ref components4[batch.Component4Allocation]); - }); + ProcessGroupWithMultithreading(ref componentBatches, componentPools); return; } - + + var (components1, components2, components3, components4) = componentPools; var batches = componentBatches.Span; for (var i = 0; i < batches.Length; i++) { @@ -142,6 +155,18 @@ protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[]) componentPools) + { + var (components1, components2, components3, components4) = componentPools; + var closureBatches = componentBatches; + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation], + ref components3[batch.Component3Allocation], ref components4[batch.Component4Allocation]); + }); + } } public abstract class BatchedRefSystem : RawBatchedSystem @@ -154,25 +179,17 @@ public abstract class BatchedRefSystem : RawBatchedSystem> componentBatches, (T1[], T2[], T3[], T4[], T5[]) componentPools) + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[]) componentPools) { - var (components1, components2, components3, components4, components5) = componentPools; - if (ShouldMultithread) { - ThreadHandler.For(0, componentBatches.Length, i => - { - var batch = componentBatches.Span[i]; - Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation], - ref components3[batch.Component3Allocation], ref components4[batch.Component4Allocation], - ref components5[batch.Component5Allocation]); - }); + ProcessGroupWithMultithreading(ref componentBatches, componentPools); return; } + var (components1, components2, components3, components4, components5) = componentPools; var batches = componentBatches.Span; for (var i = 0; i < batches.Length; i++) { @@ -182,6 +199,19 @@ protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[]) componentPools) + { + var (components1, components2, components3, components4, components5) = componentPools; + var closureBatches = componentBatches; + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation], + ref components3[batch.Component3Allocation], ref components4[batch.Component4Allocation], + ref components5[batch.Component5Allocation]); + }); + } } public abstract class BatchedRefSystem : RawBatchedSystem @@ -195,25 +225,17 @@ public abstract class BatchedRefSystem : RawBatchedSyste protected abstract void Process(Entity entity, ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4, ref T5 component5, ref T6 component6); protected BatchedRefSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) - { - } + {} - protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[]) componentPools) + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[]) componentPools) { - var (components1, components2, components3, components4, components5, components6) = componentPools; - if (ShouldMultithread) { - ThreadHandler.For(0, componentBatches.Length, i => - { - var batch = componentBatches.Span[i]; - Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation], - ref components3[batch.Component3Allocation], ref components4[batch.Component4Allocation], - ref components5[batch.Component5Allocation], ref components6[batch.Component6Allocation]);; - }); + ProcessGroupWithMultithreading(ref componentBatches, componentPools); return; } - + + var (components1, components2, components3, components4, components5, components6) = componentPools; var batches = componentBatches.Span; for (var i = 0; i < batches.Length; i++) { @@ -223,6 +245,19 @@ protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[]) componentPools) + { + var (components1, components2, components3, components4, components5, components6) = componentPools; + var closureBatches = componentBatches; + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation], + ref components3[batch.Component3Allocation], ref components4[batch.Component4Allocation], + ref components5[batch.Component5Allocation], ref components6[batch.Component6Allocation]); + }); + } } public abstract class BatchedRefSystem : RawBatchedSystem @@ -240,23 +275,16 @@ protected BatchedRefSystem(IComponentDatabase componentDatabase, IEntityComponen { } - protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[], T7[]) componentPools) + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[], T7[]) componentPools) { - var (components1, components2, components3, components4, components5, components6, components7) = componentPools; if (ShouldMultithread) { - ThreadHandler.For(0, componentBatches.Length, i => - { - var batch = componentBatches.Span[i]; - Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation], - ref components3[batch.Component3Allocation], ref components4[batch.Component4Allocation], - ref components5[batch.Component5Allocation], ref components6[batch.Component6Allocation], - ref components7[batch.Component7Allocation]); - }); + ProcessGroupWithMultithreading(ref componentBatches, componentPools); return; } + var (components1, components2, components3, components4, components5, components6, components7) = componentPools; var batches = componentBatches.Span; for (var i = 0; i < batches.Length; i++) { @@ -267,5 +295,19 @@ protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[], T7[]) componentPools) + { + var (components1, components2, components3, components4, components5, components6, components7) = componentPools; + var closureBatches = componentBatches; + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + Process(batch.Entity, ref components1[batch.Component1Allocation], ref components2[batch.Component2Allocation], + ref components3[batch.Component3Allocation], ref components4[batch.Component4Allocation], + ref components5[batch.Component5Allocation], ref components6[batch.Component6Allocation], + ref components7[batch.Component7Allocation]); + }); + } } } \ No newline at end of file diff --git a/src/EcsR3/Systems/Batching/Convention/BatchedSystem.cs b/src/EcsR3/Systems/Batching/Convention/BatchedSystem.cs index f5fc897..93eff23 100644 --- a/src/EcsR3/Systems/Batching/Convention/BatchedSystem.cs +++ b/src/EcsR3/Systems/Batching/Convention/BatchedSystem.cs @@ -18,15 +18,11 @@ protected BatchedSystem(IComponentDatabase componentDatabase, IEntityComponentAc protected abstract void Process(Entity entity, T1 component1); - protected override void ProcessGroup(ReadOnlyMemory> componentBatches, T1[] componentPools) + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, T1[] componentPools) { if (ShouldMultithread) { - ThreadHandler.For(0, componentBatches.Length, i => - { - var batch = componentBatches.Span[i]; - Process(batch.Entity, componentPools[batch.Component1Allocation]); - }); + ProcessGroupWithMultithreading(ref componentBatches, componentPools); return; } @@ -37,6 +33,17 @@ protected override void ProcessGroup(ReadOnlyMemory> componen Process(batch.Entity, componentPools[batch.Component1Allocation]); } } + + protected void ProcessGroupWithMultithreading(ref ReadOnlyMemory> componentBatches, + T1[] componentPools) + { + var closureBatches = componentBatches; + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + Process(batch.Entity, componentPools[batch.Component1Allocation]); + }); + } } public abstract class BatchedSystem : RawBatchedSystem @@ -44,38 +51,19 @@ public abstract class BatchedSystem : RawBatchedSystem where T2 : IComponent { protected BatchedSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) - { - } + {} protected abstract void Process(Entity entity, T1 component1, T2 component2); - protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[]) componentPools) + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[]) componentPools) { - var (components1, components2) = componentPools; if (ShouldMultithread) { - // TODO: This *SHOULD* be faster, but its not, so maybe investigate this in the future - /* - Parallel.ForEach(Partitioner.Create(0, componentBatches.Length), item => - { - var batches = componentBatches.Span; - for (var i = item.Item1; i < item.Item2; i++) - { - var batch = batches[i]; - Process(batch.EntityId, components1[batch.Component1Allocation], - components2[batch.Component2Allocation]); - } - });*/ - - ThreadHandler.For(0, componentBatches.Length, i => - { - var batch = componentBatches.Span[i]; - Process(batch.Entity, components1[batch.Component1Allocation], components2[batch.Component2Allocation]); - }); - + ProcessGroupWithMultithreading(ref componentBatches, componentPools); return; } + var (components1, components2) = componentPools; var batches = componentBatches.Span; for (var i = 0; i < batches.Length; i++) { @@ -83,6 +71,32 @@ protected override void ProcessGroup(ReadOnlyMemory> comp Process(batch.Entity, components1[batch.Component1Allocation], components2[batch.Component2Allocation]); } } + + protected void ProcessGroupWithMultithreading(ref ReadOnlyMemory> componentBatches, + (T1[], T2[]) componentPools) + { + + // TODO: This *SHOULD* be faster, but its not, so maybe investigate this in the future + /* + Parallel.ForEach(Partitioner.Create(0, componentBatches.Length), item => + { + var batches = componentBatches.Span; + for (var i = item.Item1; i < item.Item2; i++) + { + var batch = batches[i]; + Process(batch.EntityId, components1[batch.Component1Allocation], + components2[batch.Component2Allocation]); + } + });*/ + + var (components1, components2) = componentPools; + var closureBatches = componentBatches; + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + Process(batch.Entity, components1[batch.Component1Allocation], components2[batch.Component2Allocation]); + }); + } } public abstract class BatchedSystem : RawBatchedSystem @@ -96,19 +110,15 @@ protected BatchedSystem(IComponentDatabase componentDatabase, IEntityComponentAc protected abstract void Process(Entity entity, T1 component1, T2 component2, T3 component3); - protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[]) componentPools) + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[]) componentPools) { - var (components1, components2, components3) = componentPools; if (ShouldMultithread) { - ThreadHandler.For(0, componentBatches.Length, i => - { - var batch = componentBatches.Span[i]; - Process(batch.Entity, components1[batch.Component1Allocation], components2[batch.Component2Allocation], components3[batch.Component3Allocation]); - }); + ProcessGroupWithMultithreading(ref componentBatches, componentPools); return; } + var (components1, components2, components3) = componentPools; var batches = componentBatches.Span; for (var i = 0; i < componentBatches.Length; i++) { @@ -116,6 +126,18 @@ protected override void ProcessGroup(ReadOnlyMemory> Process(batch.Entity, components1[batch.Component1Allocation], components2[batch.Component2Allocation], components3[batch.Component3Allocation]); } } + + protected void ProcessGroupWithMultithreading(ref ReadOnlyMemory> componentBatches, + (T1[], T2[], T3[]) componentPools) + { + var (components1, components2, components3) = componentPools; + var closureBatches = componentBatches; + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + Process(batch.Entity, components1[batch.Component1Allocation], components2[batch.Component2Allocation], components3[batch.Component3Allocation]); + }); + } } public abstract class BatchedSystem : RawBatchedSystem @@ -130,28 +152,35 @@ protected BatchedSystem(IComponentDatabase componentDatabase, IEntityComponentAc protected abstract void Process(Entity entity, T1 component1, T2 component2, T3 component3, T4 component4); - protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[]) componentPools) + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[]) componentPools) { - var (components1, components2, components3, components4) = componentPools; if (ShouldMultithread) { - ThreadHandler.For(0, componentBatches.Length, i => - { - var batch = componentBatches.Span[i]; - Process(batch.Entity, components1[batch.Component1Allocation], components2[batch.Component2Allocation], - components3[batch.Component3Allocation], components4[batch.Component4Allocation]);; - }); + ProcessGroupWithMultithreading(ref componentBatches, componentPools); return; } + var (components1, components2, components3, components4) = componentPools; var batches = componentBatches.Span; for (var i = 0; i < componentBatches.Length; i++) { var batch = batches[i]; Process(batch.Entity, components1[batch.Component1Allocation], components2[batch.Component2Allocation], - components3[batch.Component3Allocation], components4[batch.Component4Allocation]);; + components3[batch.Component3Allocation], components4[batch.Component4Allocation]); } } + + protected void ProcessGroupWithMultithreading(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[]) componentPools) + { + var (components1, components2, components3, components4) = componentPools; + var closureBatches = componentBatches; + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + Process(batch.Entity, components1[batch.Component1Allocation], components2[batch.Component2Allocation], + components3[batch.Component3Allocation], components4[batch.Component4Allocation]); + }); + } } public abstract class BatchedSystem : RawBatchedSystem @@ -167,21 +196,15 @@ protected BatchedSystem(IComponentDatabase componentDatabase, IEntityComponentAc protected abstract void Process(Entity entity, T1 component1, T2 component2, T3 component3, T4 component4, T5 component5); - protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[]) componentPools) + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[]) componentPools) { - var (components1, components2, components3, components4, components5) = componentPools; if (ShouldMultithread) { - ThreadHandler.For(0, componentBatches.Length, i => - { - var batch = componentBatches.Span[i]; - Process(batch.Entity, components1[batch.Component1Allocation], components2[batch.Component2Allocation], - components3[batch.Component3Allocation], components4[batch.Component4Allocation], - components5[batch.Component5Allocation]); - }); + ProcessGroupWithMultithreading(ref componentBatches, componentPools); return; } + var (components1, components2, components3, components4, components5) = componentPools; var batches = componentBatches.Span; for (var i = 0; i < componentBatches.Length; i++) { @@ -191,6 +214,19 @@ protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[]) componentPools) + { + var (components1, components2, components3, components4, components5) = componentPools; + var closureBatches = componentBatches; + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + Process(batch.Entity, components1[batch.Component1Allocation], components2[batch.Component2Allocation], + components3[batch.Component3Allocation], components4[batch.Component4Allocation], + components5[batch.Component5Allocation]); + }); + } } public abstract class BatchedSystem : RawBatchedSystem @@ -207,21 +243,15 @@ protected BatchedSystem(IComponentDatabase componentDatabase, IEntityComponentAc protected abstract void Process(Entity entity, T1 component1, T2 component2, T3 component3, T4 component4, T5 component5, T6 component6); - protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[]) componentPools) + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[]) componentPools) { - var (components1, components2, components3, components4, components5, components6) = componentPools; if (ShouldMultithread) { - ThreadHandler.For(0, componentBatches.Length, i => - { - var batch = componentBatches.Span[i]; - Process(batch.Entity, components1[batch.Component1Allocation], components2[batch.Component2Allocation], - components3[batch.Component3Allocation], components4[batch.Component4Allocation], - components5[batch.Component5Allocation], components6[batch.Component6Allocation]); - }); + ProcessGroupWithMultithreading(ref componentBatches, componentPools); return; } + var (components1, components2, components3, components4, components5, components6) = componentPools; var batches = componentBatches.Span; for (var i = 0; i < componentBatches.Length; i++) { @@ -231,6 +261,19 @@ protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[]) componentPools) + { + var (components1, components2, components3, components4, components5, components6) = componentPools; + var closureBatches = componentBatches; + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + Process(batch.Entity, components1[batch.Component1Allocation], components2[batch.Component2Allocation], + components3[batch.Component3Allocation], components4[batch.Component4Allocation], + components5[batch.Component5Allocation], components6[batch.Component6Allocation]); + }); + } } public abstract class BatchedSystem : RawBatchedSystem @@ -248,22 +291,15 @@ protected BatchedSystem(IComponentDatabase componentDatabase, IEntityComponentAc protected abstract void Process(Entity entity, T1 component1, T2 component2, T3 component3, T4 component4, T5 component5, T6 component6, T7 component7); - protected override void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[], T7[]) componentPools) + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[], T7[]) componentPools) { - var (components1, components2, components3, components4, components5, components6, components7) = componentPools; if (ShouldMultithread) { - ThreadHandler.For(0, componentBatches.Length, i => - { - var batch = componentBatches.Span[i]; - Process(batch.Entity, components1[batch.Component1Allocation], components2[batch.Component2Allocation], - components3[batch.Component3Allocation], components4[batch.Component4Allocation], - components5[batch.Component5Allocation], components6[batch.Component6Allocation], - components7[batch.Component7Allocation]); - }); + ProcessGroupWithMultithreading(ref componentBatches, componentPools); return; } + var (components1, components2, components3, components4, components5, components6, components7) = componentPools; var batches = componentBatches.Span; for (var i = 0; i < componentBatches.Length; i++) { @@ -274,5 +310,20 @@ protected override void ProcessGroup(ReadOnlyMemory> componentBatches, + (T1[], T2[], T3[], T4[], T5[], T6[], T7[]) componentPools) + { + var (components1, components2, components3, components4, components5, components6, components7) = componentPools; + var closureBatches = componentBatches; + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + Process(batch.Entity, components1[batch.Component1Allocation], components2[batch.Component2Allocation], + components3[batch.Component3Allocation], components4[batch.Component4Allocation], + components5[batch.Component5Allocation], components6[batch.Component6Allocation], + components7[batch.Component7Allocation]); + }); + } } } \ No newline at end of file diff --git a/src/EcsR3/Systems/Batching/Convention/Multiplexing/Handlers/IMultiplexedJob.cs b/src/EcsR3/Systems/Batching/Convention/Multiplexing/Handlers/IMultiplexedJob.cs new file mode 100644 index 0000000..22d16c5 --- /dev/null +++ b/src/EcsR3/Systems/Batching/Convention/Multiplexing/Handlers/IMultiplexedJob.cs @@ -0,0 +1,46 @@ +using EcsR3.Components; +using EcsR3.Entities; + +namespace EcsR3.Systems.Batching.Convention.Multiplexing.Handlers +{ + public interface IMultiplexedJob where T : IComponent + { + void Process(Entity entity, T component); + } + + public interface IMultiplexedJob + where T1 : IComponent where T2 : IComponent + { + void Process(Entity entity, T1 component1, T2 component2); + } + + public interface IMultiplexedJob + where T1 : IComponent where T2 : IComponent where T3 : IComponent + { + void Process(Entity entity, T1 component1, T2 component2, T3 component3); + } + + public interface IMultiplexedJob + where T1 : IComponent where T2 : IComponent where T3 : IComponent where T4 : IComponent + { + void Process(Entity entity, T1 component1, T2 component2, T3 component3, T4 component4); + } + + public interface IMultiplexedJob + where T1 : IComponent where T2 : IComponent where T3 : IComponent where T4 : IComponent where T5 : IComponent + { + void Process(Entity entity, T1 component1, T2 component2, T3 component3, T4 component4, T5 component5); + } + + public interface IMultiplexedJob + where T1 : IComponent where T2 : IComponent where T3 : IComponent where T4 : IComponent where T5 : IComponent where T6 : IComponent + { + void Process(Entity entity, T1 component1, T2 component2, T3 component3, T4 component4, T5 component5, T6 component6); + } + + public interface IMultiplexedJob + where T1 : IComponent where T2 : IComponent where T3 : IComponent where T4 : IComponent where T5 : IComponent where T6 : IComponent where T7 : IComponent + { + void Process(Entity entity, T1 component1, T2 component2, T3 component3, T4 component4, T5 component5, T6 component6, T7 component7); + } +} \ No newline at end of file diff --git a/src/EcsR3/Systems/Batching/Convention/Multiplexing/Handlers/IMultiplexedRefJob.cs b/src/EcsR3/Systems/Batching/Convention/Multiplexing/Handlers/IMultiplexedRefJob.cs new file mode 100644 index 0000000..3544200 --- /dev/null +++ b/src/EcsR3/Systems/Batching/Convention/Multiplexing/Handlers/IMultiplexedRefJob.cs @@ -0,0 +1,46 @@ +using EcsR3.Components; +using EcsR3.Entities; + +namespace EcsR3.Systems.Batching.Convention.Multiplexing.Handlers +{ + public interface IMultiplexedRefJob where T : IComponent + { + void Process(Entity entity, ref T component); + } + + public interface IMultiplexedRefJob + where T1 : IComponent where T2 : IComponent + { + void Process(Entity entity, ref T1 component1, ref T2 component2); + } + + public interface IMultiplexedRefJob + where T1 : IComponent where T2 : IComponent where T3 : IComponent + { + void Process(Entity entity, ref T1 component1, ref T2 component2, ref T3 component3); + } + + public interface IMultiplexedRefJob + where T1 : IComponent where T2 : IComponent where T3 : IComponent where T4 : IComponent + { + void Process(Entity entity, ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4); + } + + public interface IMultiplexedRefJob + where T1 : IComponent where T2 : IComponent where T3 : IComponent where T4 : IComponent where T5 : IComponent + { + void Process(Entity entity, ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4, ref T5 component5); + } + + public interface IMultiplexedRefJob + where T1 : IComponent where T2 : IComponent where T3 : IComponent where T4 : IComponent where T5 : IComponent where T6 : IComponent + { + void Process(Entity entity, ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4, ref T5 component5, ref T6 component6); + } + + public interface IMultiplexedRefJob + where T1 : IComponent where T2 : IComponent where T3 : IComponent where T4 : IComponent where T5 : IComponent where T6 : IComponent where T7 : IComponent + { + void Process(Entity entity, ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4, ref T5 component5, ref T6 component6, ref T7 component7); + } +} \ No newline at end of file diff --git a/src/EcsR3/Systems/Batching/Convention/Multiplexing/MultiplexingBatchedRefSystem.cs b/src/EcsR3/Systems/Batching/Convention/Multiplexing/MultiplexingBatchedRefSystem.cs new file mode 100644 index 0000000..b749d73 --- /dev/null +++ b/src/EcsR3/Systems/Batching/Convention/Multiplexing/MultiplexingBatchedRefSystem.cs @@ -0,0 +1,520 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using EcsR3.Components; +using EcsR3.Components.Database; +using EcsR3.Computeds.Components; +using EcsR3.Computeds.Components.Registries; +using EcsR3.Entities.Accessors; +using EcsR3.Systems.Augments; +using EcsR3.Systems.Batching.Convention.Multiplexing.Handlers; +using SystemsR3.Threading; + +namespace EcsR3.Systems.Batching.Convention.Multiplexing +{ + public abstract class MultiplexingBatchedRefSystem : RawBatchedSystem + where T : IComponent + { + public IMultiplexedRefJob[] Jobs { get; private set; } + public ISystemPreProcessor[] PreProcessorJobs { get; private set; } + public ISystemPostProcessor[] PostProcessorJobs { get; private set; } + + protected abstract IEnumerable> ResolveJobs(); + + public MultiplexingBatchedRefSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + public override void StartSystem() + { + base.StartSystem(); + Jobs = ResolveJobs().ToArray(); + + PreProcessorJobs = Jobs.Where(x => x is ISystemPreProcessor) + .Cast() + .ToArray(); + + PostProcessorJobs = Jobs.Where(x => x is ISystemPostProcessor) + .Cast() + .ToArray(); + } + + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, T[] componentPools) + { + for(var i = 0; i < PreProcessorJobs.Length; i++) + { PreProcessorJobs[i].BeforeProcessing(); } + + if (ShouldMultithread) + { ProcessJobsMultithreaded(ref componentBatches, componentPools); } + else + { ProcessJobs(ref componentBatches, componentPools); } + + for(var i = 0; i < PostProcessorJobs.Length; i++) + { PostProcessorJobs[i].AfterProcessing(); } + } + + protected void ProcessJobsMultithreaded(ref ReadOnlyMemory> componentBatches, T[] componentPools) + { + var closureBatches = componentBatches; + foreach (var job in Jobs) + { + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + job.Process(batch.Entity, ref componentPools[batch.Component1Allocation]); + }); + } + } + + protected void ProcessJobs(ref ReadOnlyMemory> componentBatches, T[] componentPools) + { + var batchesSpan = componentBatches.Span; + foreach (var job in Jobs) + { + for(var i=0;i : RawBatchedSystem + where T1 : IComponent where T2 : IComponent + { + public IMultiplexedRefJob[] Jobs { get; private set; } + public ISystemPreProcessor[] PreProcessorJobs { get; private set; } + public ISystemPostProcessor[] PostProcessorJobs { get; private set; } + + protected abstract IEnumerable> ResolveJobs(); + + public MultiplexingBatchedRefSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + public override void StartSystem() + { + base.StartSystem(); + Jobs = ResolveJobs().ToArray(); + + PreProcessorJobs = Jobs.Where(x => x is ISystemPreProcessor) + .Cast() + .ToArray(); + + PostProcessorJobs = Jobs.Where(x => x is ISystemPostProcessor) + .Cast() + .ToArray(); + } + + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[]) componentPools) + { + foreach (var preProcessor in PreProcessorJobs) + { preProcessor.BeforeProcessing(); } + + if (ShouldMultithread) + { ProcessJobsMultithreaded(ref componentBatches, componentPools); } + else + { ProcessJobs(ref componentBatches, componentPools); } + + foreach (var postProcessor in PostProcessorJobs) + { postProcessor.AfterProcessing(); } + } + + protected void ProcessJobsMultithreaded(ref ReadOnlyMemory> componentBatches, + (T1[], T2[]) componentPools) + { + var (components1, components2) = componentPools; + var closureBatches = componentBatches; + foreach (var job in Jobs) + { + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + job.Process(batch.Entity, ref components1[batch.Component1Allocation], + ref components2[batch.Component2Allocation]); + }); + } + } + + protected void ProcessJobs(ref ReadOnlyMemory> componentBatches, (T1[], T2[]) componentPools) + { + var (components1, components2) = componentPools; + var batchesSpan = componentBatches.Span; + foreach (var job in Jobs) + { + for(var i=0;i : RawBatchedSystem + where T1 : IComponent where T2 : IComponent where T3 : IComponent + { + public IMultiplexedRefJob[] Jobs { get; private set; } + public ISystemPreProcessor[] PreProcessorJobs { get; private set; } + public ISystemPostProcessor[] PostProcessorJobs { get; private set; } + + protected abstract IEnumerable> ResolveJobs(); + + public MultiplexingBatchedRefSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + public override void StartSystem() + { + base.StartSystem(); + Jobs = ResolveJobs().ToArray(); + + PreProcessorJobs = Jobs.Where(x => x is ISystemPreProcessor) + .Cast() + .ToArray(); + + PostProcessorJobs = Jobs.Where(x => x is ISystemPostProcessor) + .Cast() + .ToArray(); + } + + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[]) componentPools) + { + foreach (var preProcessor in PreProcessorJobs) + { preProcessor.BeforeProcessing(); } + + if (ShouldMultithread) + { ProcessJobsMultithreaded(ref componentBatches, componentPools); } + else + { ProcessJobs(ref componentBatches, componentPools); } + + foreach (var postProcessor in PostProcessorJobs) + { postProcessor.AfterProcessing(); } + } + + protected void ProcessJobsMultithreaded(ref ReadOnlyMemory> componentBatches, + (T1[], T2[], T3[]) componentPools) + { + var (components1, components2, components3) = componentPools; + var scopedComponentBatches = componentBatches; + foreach (var job in Jobs) + { + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = scopedComponentBatches.Span[i]; + job.Process(batch.Entity, ref components1[batch.Component1Allocation], + ref components2[batch.Component2Allocation], ref components3[batch.Component3Allocation]); + }); + } + } + + protected void ProcessJobs(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[]) componentPools) + { + var (components1, components2, components3) = componentPools; + var batchesSpan = componentBatches.Span; + foreach (var job in Jobs) + { + for(var i=0;i : RawBatchedSystem + where T1 : IComponent where T2 : IComponent where T3 : IComponent where T4 : IComponent + { + public IMultiplexedRefJob[] Jobs { get; private set; } + public ISystemPreProcessor[] PreProcessorJobs { get; private set; } + public ISystemPostProcessor[] PostProcessorJobs { get; private set; } + + protected abstract IEnumerable> ResolveJobs(); + + public MultiplexingBatchedRefSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + public override void StartSystem() + { + base.StartSystem(); + Jobs = ResolveJobs().ToArray(); + + PreProcessorJobs = Jobs.Where(x => x is ISystemPreProcessor) + .Cast() + .ToArray(); + + PostProcessorJobs = Jobs.Where(x => x is ISystemPostProcessor) + .Cast() + .ToArray(); + } + + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[]) componentPools) + { + for(var i = 0; i < PreProcessorJobs.Length; i++) + { PreProcessorJobs[i].BeforeProcessing(); } + + if (ShouldMultithread) + { ProcessJobsMultithreaded(ref componentBatches, componentPools); } + else + { ProcessJobs(ref componentBatches, componentPools); } + + for(var i = 0; i < PostProcessorJobs.Length; i++) + { PostProcessorJobs[i].AfterProcessing(); } + } + + protected void ProcessJobsMultithreaded(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[]) componentPools) + { + var (components1, components2, components3, components4) = componentPools; + var scopedComponentBatches = componentBatches; + foreach (var job in Jobs) + { + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = scopedComponentBatches.Span[i]; + job.Process(batch.Entity, ref components1[batch.Component1Allocation], + ref components2[batch.Component2Allocation], ref components3[batch.Component3Allocation], + ref components4[batch.Component4Allocation]); + }); + } + } + + protected void ProcessJobs(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[]) componentPools) + { + var (components1, components2, components3, components4) = componentPools; + var batchesSpan = componentBatches.Span; + foreach (var job in Jobs) + { + for(var i=0;i : RawBatchedSystem + where T1 : IComponent where T2 : IComponent where T3 : IComponent where T4 : IComponent where T5 : IComponent + { + public IMultiplexedRefJob[] Jobs { get; private set; } + public ISystemPreProcessor[] PreProcessorJobs { get; private set; } + public ISystemPostProcessor[] PostProcessorJobs { get; private set; } + + protected abstract IEnumerable> ResolveJobs(); + + public MultiplexingBatchedRefSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + public override void StartSystem() + { + base.StartSystem(); + Jobs = ResolveJobs().ToArray(); + + PreProcessorJobs = Jobs.Where(x => x is ISystemPreProcessor) + .Cast() + .ToArray(); + + PostProcessorJobs = Jobs.Where(x => x is ISystemPostProcessor) + .Cast() + .ToArray(); + } + + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[]) componentPools) + { + for(var i = 0; i < PreProcessorJobs.Length; i++) + { PreProcessorJobs[i].BeforeProcessing(); } + + if (ShouldMultithread) + { ProcessJobsMultithreaded(ref componentBatches, componentPools); } + else + { ProcessJobs(ref componentBatches, componentPools); } + + for(var i = 0; i < PostProcessorJobs.Length; i++) + { PostProcessorJobs[i].AfterProcessing(); } + } + + protected void ProcessJobsMultithreaded(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[]) componentPools) + { + var (components1, components2, components3, components4, components5) = componentPools; + var scopedComponentBatches = componentBatches; + foreach (var job in Jobs) + { + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = scopedComponentBatches.Span[i]; + job.Process(batch.Entity, ref components1[batch.Component1Allocation], + ref components2[batch.Component2Allocation], ref components3[batch.Component3Allocation], + ref components4[batch.Component4Allocation], ref components5[batch.Component5Allocation]); + }); + } + } + + protected void ProcessJobs(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[]) componentPools) + { + var (components1, components2, components3, components4, components5) = componentPools; + + var batchesSpan = componentBatches.Span; + foreach (var job in Jobs) + { + for(var i=0;i : RawBatchedSystem + where T1 : IComponent where T2 : IComponent where T3 : IComponent where T4 : IComponent where T5 : IComponent where T6 : IComponent + { + public IMultiplexedRefJob[] Jobs { get; private set; } + public ISystemPreProcessor[] PreProcessorJobs { get; private set; } + public ISystemPostProcessor[] PostProcessorJobs { get; private set; } + + protected abstract IEnumerable> ResolveJobs(); + + public MultiplexingBatchedRefSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + public override void StartSystem() + { + base.StartSystem(); + Jobs = ResolveJobs().ToArray(); + + PreProcessorJobs = Jobs.Where(x => x is ISystemPreProcessor) + .Cast() + .ToArray(); + + PostProcessorJobs = Jobs.Where(x => x is ISystemPostProcessor) + .Cast() + .ToArray(); + } + + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[]) componentPools) + { + for(var i = 0; i < PreProcessorJobs.Length; i++) + { PreProcessorJobs[i].BeforeProcessing(); } + + if (ShouldMultithread) + { ProcessJobsMultithreaded(ref componentBatches, componentPools); } + else + { ProcessJobs(ref componentBatches, componentPools); } + + for(var i = 0; i < PostProcessorJobs.Length; i++) + { PostProcessorJobs[i].AfterProcessing(); } + } + + protected void ProcessJobsMultithreaded(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[]) componentPools) + { + var (components1, components2, components3, components4, components5, components6) = componentPools; + var scopedComponentBatches = componentBatches; + foreach (var job in Jobs) + { + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = scopedComponentBatches.Span[i]; + job.Process(batch.Entity, ref components1[batch.Component1Allocation], + ref components2[batch.Component2Allocation], ref components3[batch.Component3Allocation], + ref components4[batch.Component4Allocation], ref components5[batch.Component5Allocation], + ref components6[batch.Component6Allocation]); + }); + } + } + + protected void ProcessJobs(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[]) componentPools) + { + var (components1, components2, components3, components4, components5, components6) = componentPools; + var batchesSpan = componentBatches.Span; + foreach (var job in Jobs) + { + for(var i=0;i : RawBatchedSystem + where T1 : IComponent where T2 : IComponent where T3 : IComponent where T4 : IComponent where T5 : IComponent where T6 : IComponent where T7 : IComponent + { + public IMultiplexedRefJob[] Jobs { get; private set; } + public ISystemPreProcessor[] PreProcessorJobs { get; private set; } + public ISystemPostProcessor[] PostProcessorJobs { get; private set; } + + protected abstract IEnumerable> ResolveJobs(); + + public MultiplexingBatchedRefSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + public override void StartSystem() + { + base.StartSystem(); + Jobs = ResolveJobs().ToArray(); + + PreProcessorJobs = Jobs.Where(x => x is ISystemPreProcessor) + .Cast() + .ToArray(); + + PostProcessorJobs = Jobs.Where(x => x is ISystemPostProcessor) + .Cast() + .ToArray(); + } + + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[], T7[]) componentPools) + { + for(var i = 0; i < PreProcessorJobs.Length; i++) + { PreProcessorJobs[i].BeforeProcessing(); } + + if (ShouldMultithread) + { ProcessJobsMultithreaded(ref componentBatches, componentPools); } + else + { ProcessJobs(ref componentBatches, componentPools); } + + for(var i = 0; i < PostProcessorJobs.Length; i++) + { PostProcessorJobs[i].AfterProcessing(); } + } + + protected void ProcessJobsMultithreaded(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[], T7[]) componentPools) + { + var (components1, components2, components3, components4, components5, components6, components7) = componentPools; + var scopedComponentBatches = componentBatches; + foreach (var job in Jobs) + { + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = scopedComponentBatches.Span[i]; + job.Process(batch.Entity, ref components1[batch.Component1Allocation], + ref components2[batch.Component2Allocation], ref components3[batch.Component3Allocation], + ref components4[batch.Component4Allocation], ref components5[batch.Component5Allocation], + ref components6[batch.Component6Allocation], ref components7[batch.Component7Allocation]); + }); + } + } + + protected void ProcessJobs(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[], T7[]) componentPools) + { + var (components1, components2, components3, components4, components5, components6, components7) = componentPools; + var batchesSpan = componentBatches.Span; + foreach (var job in Jobs) + { + for(var i=0;i : RawBatchedSystem + where T : IComponent + { + public IMultiplexedJob[] Jobs { get; private set; } + public ISystemPreProcessor[] PreProcessorJobs { get; private set; } + public ISystemPostProcessor[] PostProcessorJobs { get; private set; } + + protected abstract IEnumerable> ResolveJobs(); + + public MultiplexingBatchedSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + public override void StartSystem() + { + base.StartSystem(); + Jobs = ResolveJobs().ToArray(); + + PreProcessorJobs = Jobs.Where(x => x is ISystemPreProcessor) + .Cast() + .ToArray(); + + PostProcessorJobs = Jobs.Where(x => x is ISystemPostProcessor) + .Cast() + .ToArray(); + } + + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, T[] componentPools) + { + for(var i = 0; i < PreProcessorJobs.Length; i++) + { PreProcessorJobs[i].BeforeProcessing(); } + + if (ShouldMultithread) + { ProcessJobsMultithreaded(ref componentBatches, componentPools); } + else + { ProcessJobs(ref componentBatches, componentPools); } + + for(var i = 0; i < PostProcessorJobs.Length; i++) + { PostProcessorJobs[i].AfterProcessing(); } + } + + protected void ProcessJobsMultithreaded(ref ReadOnlyMemory> componentBatches, T[] componentPools) + { + var closureBatches = componentBatches; + foreach (var job in Jobs) + { + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + job.Process(batch.Entity, componentPools[batch.Component1Allocation]); + }); + } + } + + protected void ProcessJobs(ref ReadOnlyMemory> componentBatches, T[] componentPools) + { + var batchesSpan = componentBatches.Span; + foreach (var job in Jobs) + { + for(var i=0;i : RawBatchedSystem + where T1 : IComponent where T2 : IComponent + { + public IMultiplexedJob[] Jobs { get; private set; } + public ISystemPreProcessor[] PreProcessorJobs { get; private set; } + public ISystemPostProcessor[] PostProcessorJobs { get; private set; } + + protected abstract IEnumerable> ResolveJobs(); + + public MultiplexingBatchedSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + public override void StartSystem() + { + base.StartSystem(); + Jobs = ResolveJobs().ToArray(); + + PreProcessorJobs = Jobs.Where(x => x is ISystemPreProcessor) + .Cast() + .ToArray(); + + PostProcessorJobs = Jobs.Where(x => x is ISystemPostProcessor) + .Cast() + .ToArray(); + } + + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[]) componentPools) + { + foreach (var preProcessor in PreProcessorJobs) + { preProcessor.BeforeProcessing(); } + + if (ShouldMultithread) + { ProcessJobsMultithreaded(ref componentBatches, componentPools); } + else + { ProcessJobs(ref componentBatches, componentPools); } + + foreach (var postProcessor in PostProcessorJobs) + { postProcessor.AfterProcessing(); } + } + + protected void ProcessJobsMultithreaded(ref ReadOnlyMemory> componentBatches, + (T1[], T2[]) componentPools) + { + var (components1, components2) = componentPools; + var closureBatches = componentBatches; + foreach (var job in Jobs) + { + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = closureBatches.Span[i]; + job.Process(batch.Entity, components1[batch.Component1Allocation], + components2[batch.Component2Allocation]); + }); + } + } + + protected void ProcessJobs(ref ReadOnlyMemory> componentBatches, (T1[], T2[]) componentPools) + { + var (components1, components2) = componentPools; + var batchesSpan = componentBatches.Span; + foreach (var job in Jobs) + { + for(var i=0;i : RawBatchedSystem + where T1 : IComponent where T2 : IComponent where T3 : IComponent + { + public IMultiplexedJob[] Jobs { get; private set; } + public ISystemPreProcessor[] PreProcessorJobs { get; private set; } + public ISystemPostProcessor[] PostProcessorJobs { get; private set; } + + protected abstract IEnumerable> ResolveJobs(); + + public MultiplexingBatchedSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + public override void StartSystem() + { + base.StartSystem(); + Jobs = ResolveJobs().ToArray(); + + PreProcessorJobs = Jobs.Where(x => x is ISystemPreProcessor) + .Cast() + .ToArray(); + + PostProcessorJobs = Jobs.Where(x => x is ISystemPostProcessor) + .Cast() + .ToArray(); + } + + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[]) componentPools) + { + foreach (var preProcessor in PreProcessorJobs) + { preProcessor.BeforeProcessing(); } + + if (ShouldMultithread) + { ProcessJobsMultithreaded(ref componentBatches, componentPools); } + else + { ProcessJobs(ref componentBatches, componentPools); } + + foreach (var postProcessor in PostProcessorJobs) + { postProcessor.AfterProcessing(); } + } + + protected void ProcessJobsMultithreaded(ref ReadOnlyMemory> componentBatches, + (T1[], T2[], T3[]) componentPools) + { + var (components1, components2, components3) = componentPools; + var scopedComponentBatches = componentBatches; + foreach (var job in Jobs) + { + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = scopedComponentBatches.Span[i]; + job.Process(batch.Entity, components1[batch.Component1Allocation], + components2[batch.Component2Allocation], components3[batch.Component3Allocation]); + }); + } + } + + protected void ProcessJobs(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[]) componentPools) + { + var (components1, components2, components3) = componentPools; + var batchesSpan = componentBatches.Span; + foreach (var job in Jobs) + { + for(var i=0;i : RawBatchedSystem + where T1 : IComponent where T2 : IComponent where T3 : IComponent where T4 : IComponent + { + public IMultiplexedJob[] Jobs { get; private set; } + public ISystemPreProcessor[] PreProcessorJobs { get; private set; } + public ISystemPostProcessor[] PostProcessorJobs { get; private set; } + + protected abstract IEnumerable> ResolveJobs(); + + public MultiplexingBatchedSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + public override void StartSystem() + { + base.StartSystem(); + Jobs = ResolveJobs().ToArray(); + + PreProcessorJobs = Jobs.Where(x => x is ISystemPreProcessor) + .Cast() + .ToArray(); + + PostProcessorJobs = Jobs.Where(x => x is ISystemPostProcessor) + .Cast() + .ToArray(); + } + + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[]) componentPools) + { + for(var i = 0; i < PreProcessorJobs.Length; i++) + { PreProcessorJobs[i].BeforeProcessing(); } + + if (ShouldMultithread) + { ProcessJobsMultithreaded(ref componentBatches, componentPools); } + else + { ProcessJobs(ref componentBatches, componentPools); } + + for(var i = 0; i < PostProcessorJobs.Length; i++) + { PostProcessorJobs[i].AfterProcessing(); } + } + + protected void ProcessJobsMultithreaded(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[]) componentPools) + { + var (components1, components2, components3, components4) = componentPools; + var scopedComponentBatches = componentBatches; + foreach (var job in Jobs) + { + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = scopedComponentBatches.Span[i]; + job.Process(batch.Entity, components1[batch.Component1Allocation], + components2[batch.Component2Allocation], components3[batch.Component3Allocation], + components4[batch.Component4Allocation]); + }); + } + } + + protected void ProcessJobs(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[]) componentPools) + { + var (components1, components2, components3, components4) = componentPools; + var batchesSpan = componentBatches.Span; + foreach (var job in Jobs) + { + for(var i=0;i : RawBatchedSystem + where T1 : IComponent where T2 : IComponent where T3 : IComponent where T4 : IComponent where T5 : IComponent + { + public IMultiplexedJob[] Jobs { get; private set; } + public ISystemPreProcessor[] PreProcessorJobs { get; private set; } + public ISystemPostProcessor[] PostProcessorJobs { get; private set; } + + protected abstract IEnumerable> ResolveJobs(); + + public MultiplexingBatchedSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + public override void StartSystem() + { + base.StartSystem(); + Jobs = ResolveJobs().ToArray(); + + PreProcessorJobs = Jobs.Where(x => x is ISystemPreProcessor) + .Cast() + .ToArray(); + + PostProcessorJobs = Jobs.Where(x => x is ISystemPostProcessor) + .Cast() + .ToArray(); + } + + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[]) componentPools) + { + for(var i = 0; i < PreProcessorJobs.Length; i++) + { PreProcessorJobs[i].BeforeProcessing(); } + + if (ShouldMultithread) + { ProcessJobsMultithreaded(ref componentBatches, componentPools); } + else + { ProcessJobs(ref componentBatches, componentPools); } + + for(var i = 0; i < PostProcessorJobs.Length; i++) + { PostProcessorJobs[i].AfterProcessing(); } + } + + protected void ProcessJobsMultithreaded(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[]) componentPools) + { + var (components1, components2, components3, components4, components5) = componentPools; + var scopedComponentBatches = componentBatches; + foreach (var job in Jobs) + { + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = scopedComponentBatches.Span[i]; + job.Process(batch.Entity, components1[batch.Component1Allocation], + components2[batch.Component2Allocation], components3[batch.Component3Allocation], + components4[batch.Component4Allocation], components5[batch.Component5Allocation]); + }); + } + } + + protected void ProcessJobs(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[]) componentPools) + { + var (components1, components2, components3, components4, components5) = componentPools; + + var batchesSpan = componentBatches.Span; + foreach (var job in Jobs) + { + for(var i=0;i : RawBatchedSystem + where T1 : IComponent where T2 : IComponent where T3 : IComponent where T4 : IComponent where T5 : IComponent where T6 : IComponent + { + public IMultiplexedJob[] Jobs { get; private set; } + public ISystemPreProcessor[] PreProcessorJobs { get; private set; } + public ISystemPostProcessor[] PostProcessorJobs { get; private set; } + + protected abstract IEnumerable> ResolveJobs(); + + public MultiplexingBatchedSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + public override void StartSystem() + { + base.StartSystem(); + Jobs = ResolveJobs().ToArray(); + + PreProcessorJobs = Jobs.Where(x => x is ISystemPreProcessor) + .Cast() + .ToArray(); + + PostProcessorJobs = Jobs.Where(x => x is ISystemPostProcessor) + .Cast() + .ToArray(); + } + + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[]) componentPools) + { + for(var i = 0; i < PreProcessorJobs.Length; i++) + { PreProcessorJobs[i].BeforeProcessing(); } + + if (ShouldMultithread) + { ProcessJobsMultithreaded(ref componentBatches, componentPools); } + else + { ProcessJobs(ref componentBatches, componentPools); } + + for(var i = 0; i < PostProcessorJobs.Length; i++) + { PostProcessorJobs[i].AfterProcessing(); } + } + + protected void ProcessJobsMultithreaded(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[]) componentPools) + { + var (components1, components2, components3, components4, components5, components6) = componentPools; + var scopedComponentBatches = componentBatches; + foreach (var job in Jobs) + { + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = scopedComponentBatches.Span[i]; + job.Process(batch.Entity, components1[batch.Component1Allocation], + components2[batch.Component2Allocation], components3[batch.Component3Allocation], + components4[batch.Component4Allocation], components5[batch.Component5Allocation], + components6[batch.Component6Allocation]); + }); + } + } + + protected void ProcessJobs(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[]) componentPools) + { + var (components1, components2, components3, components4, components5, components6) = componentPools; + var batchesSpan = componentBatches.Span; + foreach (var job in Jobs) + { + for(var i=0;i : RawBatchedSystem + where T1 : IComponent where T2 : IComponent where T3 : IComponent where T4 : IComponent where T5 : IComponent where T6 : IComponent where T7 : IComponent + { + public IMultiplexedJob[] Jobs { get; private set; } + public ISystemPreProcessor[] PreProcessorJobs { get; private set; } + public ISystemPostProcessor[] PostProcessorJobs { get; private set; } + + protected abstract IEnumerable> ResolveJobs(); + + public MultiplexingBatchedSystem(IComponentDatabase componentDatabase, IEntityComponentAccessor entityComponentAccessor, IComputedComponentGroupRegistry computedComponentGroupRegistry, IThreadHandler threadHandler) : base(componentDatabase, entityComponentAccessor, computedComponentGroupRegistry, threadHandler) + {} + + public override void StartSystem() + { + base.StartSystem(); + Jobs = ResolveJobs().ToArray(); + + PreProcessorJobs = Jobs.Where(x => x is ISystemPreProcessor) + .Cast() + .ToArray(); + + PostProcessorJobs = Jobs.Where(x => x is ISystemPostProcessor) + .Cast() + .ToArray(); + } + + protected override void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[], T7[]) componentPools) + { + for(var i = 0; i < PreProcessorJobs.Length; i++) + { PreProcessorJobs[i].BeforeProcessing(); } + + if (ShouldMultithread) + { ProcessJobsMultithreaded(ref componentBatches, componentPools); } + else + { ProcessJobs(ref componentBatches, componentPools); } + + for(var i = 0; i < PostProcessorJobs.Length; i++) + { PostProcessorJobs[i].AfterProcessing(); } + } + + protected void ProcessJobsMultithreaded(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[], T7[]) componentPools) + { + var (components1, components2, components3, components4, components5, components6, components7) = componentPools; + var scopedComponentBatches = componentBatches; + foreach (var job in Jobs) + { + ThreadHandler.For(0, componentBatches.Length, i => + { + var batch = scopedComponentBatches.Span[i]; + job.Process(batch.Entity, components1[batch.Component1Allocation], + components2[batch.Component2Allocation], components3[batch.Component3Allocation], + components4[batch.Component4Allocation], components5[batch.Component5Allocation], + components6[batch.Component6Allocation], components7[batch.Component7Allocation]); + }); + } + } + + protected void ProcessJobs(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[], T7[]) componentPools) + { + var (components1, components2, components3, components4, components5, components6, components7) = componentPools; + var batchesSpan = componentBatches.Span; + foreach (var job in Jobs) + { + for(var i=0;i> componentBatches, T1[] componentPools); + protected abstract void ProcessGroup(ref ReadOnlyMemory> componentBatches, T1[] componentPools); } public abstract class RawBatchedSystem : ManualBatchedSystem @@ -51,10 +51,10 @@ protected override IComputedComponentGroup GetComponentGroup() protected override void ProcessBatch() { var componentArrays = BatchPoolAccessor.GetPoolArrays(); - ProcessGroup(CastComponentGroup.Value, componentArrays); + ProcessGroup(ref CastComponentGroup.Batches, componentArrays); } - protected abstract void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[]) componentPools); + protected abstract void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[]) componentPools); } public abstract class RawBatchedSystem : ManualBatchedSystem @@ -79,10 +79,10 @@ protected override IComputedComponentGroup GetComponentGroup() protected override void ProcessBatch() { var componentArrays = _batchPoolAccessor.GetPoolArrays(); - ProcessGroup(_computedComponentGroup.Value, componentArrays); + ProcessGroup(ref _computedComponentGroup.Batches, componentArrays); } - protected abstract void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[]) componentPools); + protected abstract void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[]) componentPools); } public abstract class RawBatchedSystem : ManualBatchedSystem @@ -108,10 +108,10 @@ protected override IComputedComponentGroup GetComponentGroup() protected override void ProcessBatch() { var componentArrays = _batchPoolAccessor.GetPoolArrays(); - ProcessGroup(_computedComponentGroup.Value, componentArrays); + ProcessGroup(ref _computedComponentGroup.Batches, componentArrays); } - protected abstract void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[]) componentPools); + protected abstract void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[]) componentPools); } public abstract class RawBatchedSystem : ManualBatchedSystem @@ -138,10 +138,10 @@ protected override IComputedComponentGroup GetComponentGroup() protected override void ProcessBatch() { var componentArrays = _batchPoolAccessor.GetPoolArrays(); - ProcessGroup(_computedComponentGroup.Value, componentArrays); + ProcessGroup(ref _computedComponentGroup.Batches, componentArrays); } - protected abstract void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[]) componentPools); + protected abstract void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[]) componentPools); } public abstract class RawBatchedSystem : ManualBatchedSystem @@ -169,10 +169,10 @@ protected override IComputedComponentGroup GetComponentGroup() protected override void ProcessBatch() { var componentArrays = _batchPoolAccessor.GetPoolArrays(); - ProcessGroup(_computedComponentGroup.Value, componentArrays); + ProcessGroup(ref _computedComponentGroup.Batches, componentArrays); } - protected abstract void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[]) componentPools); + protected abstract void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[]) componentPools); } public abstract class RawBatchedSystem : ManualBatchedSystem @@ -201,9 +201,9 @@ protected override IComputedComponentGroup GetComponentGroup() protected override void ProcessBatch() { var componentArrays = _batchPoolAccessor.GetPoolArrays(); - ProcessGroup(_computedComponentGroup.Value, componentArrays); + ProcessGroup(ref _computedComponentGroup.Batches, componentArrays); } - protected abstract void ProcessGroup(ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[], T7[]) componentPools); + protected abstract void ProcessGroup(ref ReadOnlyMemory> componentBatches, (T1[], T2[], T3[], T4[], T5[], T6[], T7[]) componentPools); } } \ No newline at end of file diff --git a/src/SystemsR3/Computeds/Conventions/LazyComputedFrom.cs b/src/SystemsR3/Computeds/Conventions/LazyComputedFrom.cs new file mode 100644 index 0000000..21a0fb8 --- /dev/null +++ b/src/SystemsR3/Computeds/Conventions/LazyComputedFrom.cs @@ -0,0 +1,65 @@ +using R3; +using SystemsR3.Extensions; + +namespace SystemsR3.Computeds.Conventions +{ + public abstract class LazyComputedFrom : ILazyComputed + { + public TOutput ComputedData; + public TInput DataSource { get; } + + /// + /// Lazily evaluates the data forcing a refresh if it's dirty + /// + public TOutput Value + { + get + { + if (IsDirty) { ForceRefresh(); } + return ComputedData; + } + } + + public Observable OnChanged => OnDataChanged; + public Observable OnHasChange => OnDataHasChanged; + + protected readonly CompositeDisposable Subscriptions; + protected readonly Subject OnDataChanged; + protected readonly Subject OnDataHasChanged; + protected bool IsDirty = true; + protected readonly object Lock = new object(); + + public LazyComputedFrom(TInput dataSource) + { + DataSource = dataSource; + Subscriptions = new CompositeDisposable(); + OnDataChanged = new Subject(); + OnDataHasChanged = new Subject(); + } + + public void ForceRefresh() + { + bool hasChanged; + lock (Lock) + { + hasChanged = UpdateComputedData(); + IsDirty = false; + } + + if(hasChanged) + { OnDataChanged.OnNext(ComputedData); } + } + + /// + /// The method to update the ComputedData from the DataSource + /// + /// True if the data has changed, false if it has not + protected abstract bool UpdateComputedData(); + + public virtual void Dispose() + { + Subscriptions.DisposeAll(); + OnDataChanged.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/SystemsR3/Computeds/Conventions/LazyComputedFromData.cs b/src/SystemsR3/Computeds/Conventions/LazyComputedFromData.cs new file mode 100644 index 0000000..999ad5f --- /dev/null +++ b/src/SystemsR3/Computeds/Conventions/LazyComputedFromData.cs @@ -0,0 +1,27 @@ +using R3; + +namespace SystemsR3.Computeds.Conventions +{ + public abstract class LazyComputedFromData : LazyComputedFrom + { + public LazyComputedFromData(TInput dataSource) : base(dataSource) + { Initialize(); } + + public void Initialize() + { + RefreshWhen() + .Subscribe(_ => IsDirty = true) + .AddTo(Subscriptions); } + + /// + /// The method to indicate when the listings should be updated + /// + /// + /// If there is no checking required outside of adding/removing this can + /// return an empty observable, but common usages would be to refresh every update. + /// The bool is throw away, but is a workaround for not having a Unit class + /// + /// An observable trigger that should trigger when the group should refresh + protected abstract Observable RefreshWhen(); + } +} \ No newline at end of file diff --git a/src/SystemsR3/Computeds/ILazyComputed.cs b/src/SystemsR3/Computeds/ILazyComputed.cs new file mode 100644 index 0000000..62d373a --- /dev/null +++ b/src/SystemsR3/Computeds/ILazyComputed.cs @@ -0,0 +1,10 @@ +using R3; + +namespace SystemsR3.Computeds +{ + public interface ILazyComputed : IComputed + { + Observable OnHasChange { get; } + void ForceRefresh(); + } +} \ No newline at end of file diff --git a/src/SystemsR3/Pools/IPool.cs b/src/SystemsR3/Pools/IPool.cs index c2f5fe1..c13c333 100644 --- a/src/SystemsR3/Pools/IPool.cs +++ b/src/SystemsR3/Pools/IPool.cs @@ -26,6 +26,11 @@ public interface IPool : IDisposable /// /// An instance to use T Allocate(); + + /// + /// Clears and empties the pool + /// + void Clear(); /// /// Frees up the pooled item for re-allocation diff --git a/src/SystemsR3/Pools/IdPool.cs b/src/SystemsR3/Pools/IdPool.cs index 35814a2..11dd3cb 100644 --- a/src/SystemsR3/Pools/IdPool.cs +++ b/src/SystemsR3/Pools/IdPool.cs @@ -27,7 +27,7 @@ public IdPool(PoolConfig poolConfig = null) AvailableIds = Enumerable.Range(1, _lastMax).ToList(); _onSizeChanged = new Subject(); } - + public int Allocate() { lock (_lock) @@ -83,6 +83,15 @@ public void Expand(int? newId = null) _onSizeChanged.OnNext(Size); } } + + public void Clear() + { + lock (_lock) + { + _lastMax = 0; + AvailableIds.Clear(); + } + } public void Dispose() { _onSizeChanged?.Dispose(); }