Table of Contents

Struct ClusterRef<TArch>

Namespace
Typhon.Engine
Assembly
Typhon.Engine.dll

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

int

ClusterSize

Cluster size N (number of slots, 8..64).

public int ClusterSize { get; }

Property Value

int

EntityIds

Entity keys for all N slots. Use with slot index to reconstruct EntityId.

public ReadOnlySpan<long> EntityIds { get; }

Property Value

ReadOnlySpan<long>

FullMask

Full mask with lower N bits set.

public ulong FullMask { get; }

Property Value

ulong

IsFull

True when all slots are occupied.

public bool IsFull { get; }

Property Value

bool

LiveCount

Number of live entities in this cluster (PopCount of OccupancyBits).

public int LiveCount { get; }

Property Value

int

OccupancyBits

Bitmask of occupied slots. Bit i = 1 means slot i contains a live entity.

public ulong OccupancyBits { get; }

Property Value

ulong

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

ClusterSpatialAabb

Methods

ActiveBits(int)

Combined mask: alive AND component at slot enabled.

public ulong ActiveBits(int slot)

Parameters

slot int

Returns

ulong

EnabledBits(int)

Bitmask of entities with component at slot enabled.

public ulong EnabledBits(int slot)

Parameters

slot int

Returns

ulong

GetEntityId(int)

Read EntityId for the entity at the given slot (stored as full packed EntityId).

public EntityId GetEntityId(int slotIndex)

Parameters

slotIndex int

Returns

EntityId

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

comp Comp<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

comp Comp<T>
slotIndex int

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

comp Comp<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

T

Component value type.

Exceptions

InvalidOperationException

Thrown, when strict checks are enabled (Enabled), if T is 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

comp Comp<T>
slotIndex int

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):

  1. Reads the OLD spatial-field bytes at slotIndex
  2. Writes newValue to the slot
  3. Updates Typhon.Engine.Internals.ArchetypeClusterState.ClusterAabbs inline on AABB grow (O(1) CAS per axis)
  4. 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
  5. Flags Typhon.Engine.Internals.ArchetypeClusterState.ClusterMigrationPendingSlots when the new position crosses the cell+hysteresis boundary — fence drains the migration without any full scan
  6. 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

comp Comp<T>
slotIndex int
newValue T

Type Parameters

T