diff --git a/README.md b/README.md index 2be6f5b..1583a78 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ A high-performance C# based Archetype & Chunks [Entity Component System](https:/ Download the [package](https://github.com/genaray/Arch/packages/1697222), get started today and join the [Discord](https://discord.gg/htc8tX3NxZ)! ```console -dotnet add PROJECT package Arch --version 1.2.8.1-alpha +dotnet add PROJECT package Arch --version 1.3.0-alpha ``` # ⏩ Quickstart diff --git a/src/Arch.Samples/Systems.cs b/src/Arch.Samples/Systems.cs index 4bcf842..265656b 100644 --- a/src/Arch.Samples/Systems.cs +++ b/src/Arch.Samples/Systems.cs @@ -1,6 +1,4 @@ -using System.Diagnostics; using Arch.Core; -using Arch.Core.Extensions; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; diff --git a/src/Arch.SourceGen/Queries/QueryDescription.cs b/src/Arch.SourceGen/Queries/QueryDescription.cs index d2c3869..cdc7316 100644 --- a/src/Arch.SourceGen/Queries/QueryDescription.cs +++ b/src/Arch.SourceGen/Queries/QueryDescription.cs @@ -23,7 +23,7 @@ public static StringBuilder AppendQueryDescriptionWithAll(this StringBuilder sb, public ref QueryDescription WithAll<{{generics}}>() { All = Component<{{generics}}>.Signature; - _hashCode = -1; + Build(); return ref this; } """; @@ -53,7 +53,7 @@ public static StringBuilder AppendQueryDescriptionWithAny(this StringBuilder sb, public ref QueryDescription WithAny<{{generics}}>() { Any = Component<{{generics}}>.Signature; - _hashCode = -1; + Build(); return ref this; } """; @@ -83,7 +83,7 @@ public static StringBuilder AppendQueryDescriptionWithNone(this StringBuilder sb public ref QueryDescription WithNone<{{generics}}>() { None = Component<{{generics}}>.Signature; - _hashCode = -1; + Build(); return ref this; } """; @@ -113,7 +113,7 @@ public static StringBuilder AppendQueryDescriptionWithExclusive(this StringBuild public ref QueryDescription WithExclusive<{{generics}}>() { Exclusive = Component<{{generics}}>.Signature; - _hashCode = -1; + Build(); return ref this; } """; diff --git a/src/Arch/Arch.csproj b/src/Arch/Arch.csproj index 77dfb6d..a9760fc 100644 --- a/src/Arch/Arch.csproj +++ b/src/Arch/Arch.csproj @@ -14,16 +14,21 @@ Arch Arch - 1.2.8.2-alpha + 1.3.0-alpha genaray Apache-2.0 - A high performance c# net.6 and net.7 archetype based ECS ( Entity component system ). + A high performance c# net.7 and net.8 archetype based ECS ( Entity component system ). Updated CommunityToolkit, increases speed slightly. Fixed some DangerousUtilities. Refactored adding of entities to be faster. Introduced Signature for improved performance. Added MemoryMarshal.CreateSpan in some places for improved performance. -Introduced breaking changes by renaming Group to Component and several other small changes. +Introduced breaking changes by renaming Group to Component and several other small changes. +Updated Arch.LowLevel. +Improved performance of almost all operations. +Refatored QueryDescription, use the constructor or generics now instead. +Improved ASM-Code generation for improved performance for smaller querys. +Removed several methods from arch to slim down the core. c#;.net;.net6;.net7;ecs;game;entity;gamedev; game-development; game-engine; entity-component-system;stride;unity;godot; https://github.com/genaray/Arch diff --git a/src/Arch/Core/Query.cs b/src/Arch/Core/Query.cs index cc35f35..cb45333 100644 --- a/src/Arch/Core/Query.cs +++ b/src/Arch/Core/Query.cs @@ -224,26 +224,26 @@ public partial struct QueryDescription : IEquatable /// /// An of all components that an should have mandatory. - /// If the content of the array is subsequently changed, a should be carried out. + /// If the content of the array is subsequently changed, a should be carried out. /// public Signature All { get; private set; } = Signature.Null; /// /// An array of all components of which an should have at least one. - /// If the content of the array is subsequently changed, a should be carried out. + /// If the content of the array is subsequently changed, a should be carried out. /// public Signature Any { get; private set; } = Signature.Null; /// /// An array of all components of which an should not have any. - /// If the content of the array is subsequently changed, a should be carried out. + /// If the content of the array is subsequently changed, a should be carried out. /// public Signature None { get; private set; } = Signature.Null; /// /// An array of all components that exactly match the structure of an . /// 's with more or less components than those defined in the array are not addressed. - /// If the content of the array is subsequently changed, a should be carried out. + /// If the content of the array is subsequently changed, a should be carried out. /// public Signature Exclusive { get; private set; } = Signature.Null; @@ -292,11 +292,11 @@ public QueryDescription(ComponentType[]? all = null, ComponentType[]? any = null } /// - /// Recreates this instance by calculating a new . + /// Builds this instance by calculating a new . /// Is actually only needed if the passed arrays are changed afterwards. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Rebuild() + public void Build() { _hashCode = -1; _hashCode = GetHashCode(); @@ -312,7 +312,7 @@ public void Rebuild() public ref QueryDescription WithAll() { All = Component.Signature; - _hashCode = -1; + Build(); return ref this; } @@ -326,7 +326,7 @@ public ref QueryDescription WithAll() public ref QueryDescription WithAny() { Any = Component.Signature; - _hashCode = -1; + Build(); return ref this; } @@ -340,7 +340,7 @@ public ref QueryDescription WithAny() public ref QueryDescription WithNone() { None = Component.Signature; - _hashCode = -1; + Build(); return ref this; } @@ -355,7 +355,7 @@ public ref QueryDescription WithNone() public ref QueryDescription WithExclusive() { Exclusive = Component.Signature; - _hashCode = -1; + Build(); return ref this; } diff --git a/src/Arch/Core/World.cs b/src/Arch/Core/World.cs index e6e738a..ee93343 100644 --- a/src/Arch/Core/World.cs +++ b/src/Arch/Core/World.cs @@ -19,12 +19,12 @@ internal record struct RecycledEntity /// /// The recycled id. /// - public int Id; + public readonly int Id; /// /// The new version. /// - public int Version; + public readonly int Version; /// /// Initializes a new instance of the struct. diff --git a/src/Arch/Templates/Chunk.GetArray.tt b/src/Arch/Templates/Chunk.GetArray.tt new file mode 100644 index 0000000..f6f6e09 --- /dev/null +++ b/src/Arch/Templates/Chunk.GetArray.tt @@ -0,0 +1,55 @@ +<#@ template language="C#" #> +<#@ output extension=".cs" #> +<#@ import namespace="System.Text" #> +<#@ include file="Helpers.ttinclude" #> + +using System; +using System.Runtime.CompilerServices; +using CommunityToolkit.HighPerformance; +using Arch.Core.Utils; + +namespace Arch.Core; + +public partial struct Chunk +{ + +<# + for (var index = 1; index < Amount; index++) + { + var generics = AppendGenerics(index); + + var addEvents = new StringBuilder(); + for (var i = 0; i < index; i++) + addEvents.AppendLine($"OnComponentAdded(entity);"); + + var outs = new StringBuilder(); + for (var i = 0; i < index; i++) + outs.Append($"out T{i}[] t{i}Array,"); + outs.Length--; + + var indexes = new StringBuilder(); + for (var i = 0; i < index; i++) + indexes.Append($"out var t{i}Index,"); + indexes.Length--; + + var assignComponents = new StringBuilder(); + for (var i = 0; i < index; i++) + assignComponents.AppendLine($"t{i}Array = Unsafe.As(Unsafe.Add(ref arrays, t{i}Index));"); + +#> + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [Pure] + public void GetArray<<#= generics #>>(<#= outs #>) + { + Index<<#= generics #>>(<#= indexes #>); + ref var arrays = ref Components.DangerousGetReference(); + <#= assignComponents #> + } +<# + } +#> + +} + + diff --git a/src/Arch/Templates/Chunk.GetFirst.tt b/src/Arch/Templates/Chunk.GetFirst.tt new file mode 100644 index 0000000..d387e28 --- /dev/null +++ b/src/Arch/Templates/Chunk.GetFirst.tt @@ -0,0 +1,47 @@ +<#@ template language="C#" #> +<#@ output extension=".cs" #> +<#@ import namespace="System.Text" #> +<#@ include file="Helpers.ttinclude" #> + +using System; +using System.Runtime.CompilerServices; +using CommunityToolkit.HighPerformance; +using Arch.Core.Utils; + +namespace Arch.Core; + +public partial struct Chunk +{ + +<# + for (var index = 1; index < Amount; index++) + { + var generics = AppendGenerics(index); + + var arrays = new StringBuilder(); + for (var i = 0; i <= index; i++) + arrays.Append($"out var t{i}Array,"); + arrays.Length--; + + var insertParams = new StringBuilder(); + for (var i = 0; i <= index; i++) + insertParams.Append($"ref t{i}Array.DangerousGetReference(),"); + insertParams.Length--; + +#> + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [Pure] + public Components<<#= generics #>> GetFirst<<#= generics #>>() + { + GetArray<<#= generics #>>(<#= arrays #>); + return new Components<<#= generics #>>(<#= insertParams #>); + } + +<# + } +#> + +} + + diff --git a/src/Arch/Templates/Chunk.GetSpan.tt b/src/Arch/Templates/Chunk.GetSpan.tt new file mode 100644 index 0000000..ccae9df --- /dev/null +++ b/src/Arch/Templates/Chunk.GetSpan.tt @@ -0,0 +1,49 @@ +<#@ template language="C#" #> +<#@ output extension=".cs" #> +<#@ import namespace="System.Text" #> +<#@ include file="Helpers.ttinclude" #> + +using System; +using System.Runtime.CompilerServices; +using CommunityToolkit.HighPerformance; +using Arch.Core.Utils; + +namespace Arch.Core; + +public partial struct Chunk +{ +<# + for (var index = 1; index < Amount; index++) + { + var generics = AppendGenerics(index); + + var outs = new StringBuilder(); + for (var i = 0; i <= index; i++) + outs.Append($"out Span t{i}Span,"); + outs.Length--; + + var arrays = new StringBuilder(); + for (var i = 0; i <= index; i++) + arrays.Append($"out var t{i}Array,"); + arrays.Length--; + + var assignComponents = new StringBuilder(); + for (var i = 0; i <= index; i++) + assignComponents.AppendLine($"t{i}Span = new Span(t{i}Array);"); + +#> + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [Pure] + public void GetSpan<<#= generics #>>(<#= outs #>) + { + GetArray<<#= generics #>>(<#= arrays #>); + <#= assignComponents #> + } + +<# + } +#> +} + + diff --git a/src/Arch/Templates/World.Create.tt b/src/Arch/Templates/World.Create.tt index 77259c9..43cbc35 100644 --- a/src/Arch/Templates/World.Create.tt +++ b/src/Arch/Templates/World.Create.tt @@ -9,7 +9,8 @@ using CommunityToolkit.HighPerformance; using Arch.Core.Utils; namespace Arch.Core; - +public partial class World +{ <# for (var index = 1; index < Amount; index++) { @@ -22,43 +23,44 @@ namespace Arch.Core; addEvents.AppendLine($"OnComponentAdded(entity);"); #> -[MethodImpl(MethodImplOptions.AggressiveInlining)] -[StructuralChange] -public Entity Create<{{generics}}>({{parameters}}) -{ - var signature = Component<{{generics}}>.Signature; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [StructuralChange] + public Entity Create<<#= generics #>>(<#= parameters #>) + { + var signature = Component<<#= generics #>>.Signature; - // Recycle id or increase - var recycle = RecycledIds.TryDequeue(out var recycledId); - var recycled = recycle ? recycledId : new RecycledEntity(Size, 1); + // Recycle id or increase + var recycle = RecycledIds.TryDequeue(out var recycledId); + var recycled = recycle ? recycledId : new RecycledEntity(Size, 1); - // Create new entity and put it to the back of the array - var entity = new Entity(recycled.Id, Id); + // Create new entity and put it to the back of the array + var entity = new Entity(recycled.Id, Id); - // Add to archetype & mapping - var archetype = GetOrCreate(signature); - var createdChunk = archetype.Add(entity, out var slot); + // Add to archetype & mapping + var archetype = GetOrCreate(signature); + var createdChunk = archetype.Add(entity, out var slot); - archetype.Set<{{generics}}>(ref slot, {{inParameters}}); + archetype.Set<<#= generics #>>(ref slot, <#= inParameters #>); - // Resize map & Array to fit all potential new entities - if (createdChunk) - { - Capacity += archetype.EntitiesPerChunk; - EntityInfo.EnsureCapacity(Capacity); - } + // Resize map & Array to fit all potential new entities + if (createdChunk) + { + Capacity += archetype.EntitiesPerChunk; + EntityInfo.EnsureCapacity(Capacity); + } - // Map - EntityInfo.Add(entity.Id, recycled.Version, archetype, slot); + // Map + EntityInfo.Add(entity.Id, recycled.Version, archetype, slot); - Size++; - OnEntityCreated(entity); + Size++; + OnEntityCreated(entity); - {{addEvents}} - return entity; -} + <#= addEvents #> + return entity; + } <# } #> +}