Table of Contents

Struct TransactionClusterSpatialQuery<TArch>

Namespace
Typhon.Engine
Assembly
Typhon.Engine.dll

ClusterSpatialQuery<TArch> made through a transaction (tx.ClusterSpatialQuery{TArch}()): the same queries, minus every entity that transaction destroyed and has not committed (CLUSTERWALK-04). A destroy made while a query runs applies from the next cluster it opens.

public readonly ref struct TransactionClusterSpatialQuery<TArch> where TArch : Archetype<TArch>, new()

Type Parameters

TArch
Inherited Members

Remarks

A wrapper rather than a field on ClusterSpatialQuery<TArch>, on purpose: the engine form's AABB is inlined on an IL budget the JIT counts before it folds the tier branches, and threading the filter through it cost every engine query ~25 ns (measured, #1482). Here the filter is attached to the enumerator after the engine form built it, so only this path pays: one more copy of the returned enumerator.

Methods

AABB<TBox>(in TBox, uint)

Query all entities in this archetype whose spatial bounds intersect the axis-aligned box carried by box. The generic type parameter TBox determines the dimensionality and precision of the query region; it must match the archetype's cluster storage tier or an InvalidOperationException is thrown (issue #230 Phase 2.5).

public AabbClusterEnumerator AABB<TBox>(in TBox box, uint categoryMask = 4294967295) where TBox : struct, ISpatialBox

Parameters

box TBox

Query region, passed by in to avoid a defensive copy.

categoryMask uint

Category bitmask; a cluster is skipped if its union mask does not intersect. Pass MaxValue (default) to accept every cluster.

Returns

AabbClusterEnumerator

A zero-allocation enumerator suitable for foreach.

Type Parameters

TBox

One of AABB2F, AABB3F, AABB2D, AABB3D. The generic constraint narrows to ISpatialBox, and the JIT specializes this method per concrete TBox so each monomorphized version contains only the code path for its concrete type (all other dispatch branches are dead-code-eliminated at specialization time).

Remarks

AggressiveInlining is load-bearing and was measured, not assumed (#919 AC-6). Adding the two f64 dispatch branches took this method's IL from 716 to 814 bytes. The JIT decides inlining from IL size before it folds the typeof(TBox) == typeof(...) tests, so the specialised body that actually runs stayed small while the method stopped being inlined — and because it RETURNS a AabbClusterEnumerator by value, that cost every query one extra copy of a 1.6 KB ref struct. Measured interleaved against the pre-#914 build: +44 ns on every query, fixed rather than per-cell, which at a 3×3-cell query is a tenth of the whole thing. The attribute takes it back to +6 ns.

So: if another box variant is added here, re-measure. The branches are free once specialised, but each one spends IL budget that the inliner counts before it can know that.

Exceptions

InvalidOperationException

Thrown when the archetype has no spatial index, or when TBox's tier does not match the archetype's cluster storage tier.

NotSupportedException

Thrown only for an ISpatialBox implementer with no dispatch branch here. All four box types that exist are supported since #914.

CountRadius(ReadOnlySpan<BSphere2F>, Span<int>, uint)

For each sphere in members, how many entities Radius(in BSphere2F, uint) returns for it: counts[j] == Radius(members[j], categoryMask).Count(). The cells, cell halves and clusters the members share are walked and opened once for the whole set instead of once per member. The archetype must be 2D (tier Tier2F).

public void CountRadius(ReadOnlySpan<BSphere2F> members, Span<int> counts, uint categoryMask = 4294967295)

Parameters

members ReadOnlySpan<BSphere2F>

The query spheres, in world units.

counts Span<int>

Receives one count per member, in members' order; entries past its length are left alone.

categoryMask uint

As for Radius(in BSphere2F, uint), for every member.

Remarks

When it pays. When the members are close together — one source cluster's entities, a squad, a crowd's observers — their own queries walk nearly the same cells and open the same clusters, which the batch then does once; each member is still tested only against the clusters its own box overlaps. A member far from the others costs what its own query costs: no cell outside a member's own range is visited.

At most 64 members. The caller must be inside an Typhon.Engine.Internals.EpochGuard scope, as for a single query.

Exceptions

ArgumentException

More than 64 members, a member that is not finite, or counts shorter than members. Checked before anything is written: counts is then left as it was.

InvalidOperationException

The archetype has no spatial index, or is not 2D f32.

ForEachInRadius<TSink>(ReadOnlySpan<BSphere2F>, ref TSink, uint)

For each sphere in members, the entities Radius(in BSphere2F, uint) returns for it, handed to sink as (member index, hit): each member's hits in its own query's MoveNext order, with the same bounds and squared distance. The cells, cell halves and clusters the members share are walked and opened once for the whole set. The archetype must be 2D (tier Tier2F).

public void ForEachInRadius<TSink>(ReadOnlySpan<BSphere2F> members, ref TSink sink, uint categoryMask = 4294967295) where TSink : struct, IRadiusBatchSink, allows ref struct

Parameters

members ReadOnlySpan<BSphere2F>

The query spheres, in world units.

sink TSink

Receives the hits. Copied into the batch and copied back when it returns or throws, so read its state after the call, not from inside a Hit(int, in ClusterSpatialQueryResult) through another reference to the caller's variable.

categoryMask uint

As for Radius(in BSphere2F, uint), for every member.

Type Parameters

TSink

Remarks

The sink returns false to retire a member: it receives nothing further, and the others carry on. Members' hits interleave cluster by cluster; only each member's own sequence is ordered.

The sink may run its own spatial queries: they take a page window of their own. If it throws, the batch hands its window back and the exception propagates; sink then holds the state it had reached.

At most 64 members. The caller must be inside an Typhon.Engine.Internals.EpochGuard scope, as for a single query. See CountRadius(ReadOnlySpan<BSphere2F>, Span<int>, uint) for when a batch pays.

Exceptions

ArgumentException

More than 64 members, or a member that is not finite; checked before any hit is delivered.

InvalidOperationException

The archetype has no spatial index, or is not 2D f32.

Radius(in BSphere2D, uint)

f64 counterpart of Radius(in BSphere2F, uint) (#914). The archetype must be 2D f64 (tier Tier2D).

public AabbClusterEnumerator Radius(in BSphere2D sphere, uint categoryMask = 4294967295)

Parameters

sphere BSphere2D
categoryMask uint

Returns

AabbClusterEnumerator

Radius(in BSphere2F, uint)

Query all entities in this archetype whose spatial bounds are within sphere's radius of its center, using the closest-point-on-AABB distance semantic. DistanceSq is populated for each hit, letting callers sort by distance without re-reading bounds. The archetype must be 2D (tier Tier2F); 3D archetypes use the BSphere3F overload.

public AabbClusterEnumerator Radius(in BSphere2F sphere, uint categoryMask = 4294967295)

Parameters

sphere BSphere2F

Query sphere (center + radius), in world units.

categoryMask uint

Category bitmask; a cluster is skipped if its union mask does not intersect. Pass MaxValue (default) to accept every cluster.

Returns

AabbClusterEnumerator

Radius(in BSphere3D, uint)

f64 counterpart of Radius(in BSphere3F, uint) (#914). The archetype must be 3D f64 (tier Tier3D).

public AabbClusterEnumerator Radius(in BSphere3D sphere, uint categoryMask = 4294967295)

Parameters

sphere BSphere3D
categoryMask uint

Returns

AabbClusterEnumerator

Radius(in BSphere3F, uint)

3D variant of Radius(in BSphere2F, uint). The archetype must be 3D (tier Tier3F).

public AabbClusterEnumerator Radius(in BSphere3F sphere, uint categoryMask = 4294967295)

Parameters

sphere BSphere3F
categoryMask uint

Returns

AabbClusterEnumerator