Struct ClusterRef<TArch>
Per-cluster accessor providing typed, zero-copy access to component SoA arrays. Created by ClusterEnumerator<TArch>. Must not outlive the enumerator.
public ref struct ClusterRef<TArch> where TArch : class
Type Parameters
TArch
- Inherited Members
Remarks
Component data is laid out in Structure-of-Arrays format within the cluster:
Component₀[N], Component₁[N], ... where N is the cluster size (8..64).
Iteration pattern using OccupancyBits TZCNT loop:
ulong bits = cluster.OccupancyBits;
while (bits != 0)
{
int idx = BitOperations.TrailingZeroCount(bits);
bits &= bits - 1;
ref var pos = ref cluster.Get(Ant.Position, idx);
// ...
}
Properties
ChunkId
The chunk ID of this cluster within the archetype's segment.
public int ChunkId { get; }
Property Value
ClusterSize
Cluster size N (number of slots, 8..64).
public int ClusterSize { get; }
Property Value
EntityIds
Entity keys for all N slots. Use with slot index to reconstruct EntityId.
public ReadOnlySpan<long> EntityIds { get; }
Property Value
FullMask
Full mask with lower N bits set.
public ulong FullMask { get; }
Property Value
IsFull
True when all slots are occupied.
public bool IsFull { get; }
Property Value
LiveCount
Number of live entities in this cluster (PopCount of OccupancyBits).
public int LiveCount { get; }
Property Value
OccupancyBits
Bitmask of occupied slots. Bit i = 1 means slot i contains a live entity.
public ulong OccupancyBits { get; }
Property Value
SpatialBounds
Tight AABB of all entities in this cluster. Returns the empty sentinel (min = +inf, max = -inf) when the archetype has no spatial index. For 2D archetypes, MinZ/MaxZ are ±infinity sentinels — use MinX/MinY/MaxX/MaxY only.
public ref readonly ClusterSpatialAabb SpatialBounds { get; }
Property Value
Methods
ActiveBits(int)
Combined mask: alive AND component at slot enabled.
public ulong ActiveBits(int slot)
Parameters
slotint
Returns
EnabledBits(int)
Bitmask of entities with component at slot enabled.
public ulong EnabledBits(int slot)
Parameters
slotint
Returns
GetEntityId(int)
Read EntityId for the entity at the given slot (stored as full packed EntityId).
public EntityId GetEntityId(int slotIndex)
Parameters
slotIndexint
Returns
GetReadOnlySpan<T>(Comp<T>)
Get a read-only span of component data for all N slots.
public ReadOnlySpan<T> GetReadOnlySpan<T>(Comp<T> comp) where T : unmanaged
Parameters
compComp<T>
Returns
- ReadOnlySpan<T>
Type Parameters
T
GetReadOnly<T>(Comp<T>, int)
Get a read-only reference to a single component value at the given slot index.
public ref readonly T GetReadOnly<T>(Comp<T> comp, int slotIndex) where T : unmanaged
Parameters
Returns
- T
Type Parameters
T
GetSpan<T>(Comp<T>)
Get a mutable span of the component's data across all N slots (its SoA array). For Versioned components use GetReadOnlySpan<T>(Comp<T>) instead — writing directly to the cluster slot bypasses the revision chain and breaks MVCC snapshot isolation.
public Span<T> GetSpan<T>(Comp<T> comp) where T : unmanaged
Parameters
compComp<T>Handle identifying the component within the archetype.
Returns
- Span<T>
A mutable span of length ClusterSize over the component's SoA array.
Type Parameters
TComponent value type.
Exceptions
- InvalidOperationException
Thrown, when strict checks are enabled (Enabled), if
Tis a Versioned component.
Get<T>(Comp<T>, int)
Get a mutable reference to a single component value at the given slot index.
public ref T Get<T>(Comp<T> comp, int slotIndex) where T : unmanaged
Parameters
Returns
- T
Type Parameters
T
WriteSpatial<T>(Comp<T>, int, in T)
Write-barrier API for spatial components — the canonical replacement for cluster.GetSpan<T>()[slotIndex] = ... when T contains the
archetype's SpatialIndexAttribute-marked field. Performs (in order):
- Reads the OLD spatial-field bytes at
slotIndex - Writes
newValueto the slot - Updates Typhon.Engine.Internals.ArchetypeClusterState.ClusterAabbs inline on AABB grow (O(1) CAS per axis)
- Flags Typhon.Engine.Internals.ArchetypeClusterState.ClusterShrinkPendingAxes for axes where this slot was at an extreme and moved inward — fence rescans only this cluster on those axes
- Flags Typhon.Engine.Internals.ArchetypeClusterState.ClusterMigrationPendingSlots when the new position crosses the cell+hysteresis boundary — fence drains the migration without any full scan
- Sets the cluster's bit in Typhon.Engine.Internals.ArchetypeClusterState.ClusterProcessBitmap so the fence loop visits this cluster
V1 supports AABB2F only (AntHill's WorldBounds). Other field types throw NotSupportedException.
WriteSpatial does NOT mark the slot dirty (via SetDirty(int, int)).
The dirty bitmap drives WAL serialization and change-filtered dispatch — for high-frequency simulation state (e.g., AntHill's ant positions), marking
every slot dirty floods the WAL writer with one frame per entity per tick → backpressure that stalls TickDriver. The fence-time spatial maintenance does
not need the dirty bit; it consumes Typhon.Engine.Internals.ArchetypeClusterState.ClusterMigrationPendingSlots /
Typhon.Engine.Internals.ArchetypeClusterState.ClusterProcessBitmap directly. If your workload genuinely needs WAL persistence of the spatial field (e.g.,
resumable autosave), either write through the MVCC Transaction.OpenMut + Write path (which marks dirty), or
call SetDirty(int, int) explicitly after WriteSpatial.
Thread safety: safe to call concurrently from multiple workers operating on different slots of any cluster (including the same cluster). All bookkeeping writes use Interlocked primitives.
public void WriteSpatial<T>(Comp<T> comp, int slotIndex, in T newValue) where T : unmanaged
Parameters
Type Parameters
T