Cluster Dormancy (Sleep/Wake)
Clusters untouched for N ticks sleep and are skipped by every dispatch path, waking within one tick of being written to.
Status: β Implemented Β· Visibility: Public Β· Level: π£ Advanced Β· Category: Runtime
π― What it solves
Tier filtering already shrinks the dispatch set for distant regions, but even a coarse tier still contains clusters doing nothing tick after tick β idle units, empty cells, parked props. Dormancy goes one step further: it lets the runtime notice a cluster has gone quiet and stop dispatching it to any system against that archetype, tier-filtered or not, until something inside it changes again β without game code having to track idle state itself or remember to skip it.
βοΈ How it works (in brief)
Each cluster carries a sleep counter, advanced once per tick at the cluster tick fence: a tick with no
dirty write in the cluster increments it, a tick with one resets it to zero. Once the counter reaches a
configurable per-archetype threshold, the cluster flips Active β Sleeping and every parallel dispatch
path drops it from its cluster partition at zero per-entity cost. Because a write to a sleeping cluster
can happen from any worker thread mid-tick, the wake isn't applied inline (that would race on shared
sleep state) β it's recorded on a [ThreadStatic] list and drained single-threaded at the tick fence
(Sleeping β WakePending), then promoted to Active at the very start of the next tick, before
TierClusterIndex rebuilds or any system dispatches. End to end that's at most one tick of wake latency.
An optional heartbeat interval wakes sleeping clusters anyway on a staggered schedule, independent of
writes, for a periodic idle re-check.
π» Usage
Dormancy is fully automatic from a system author's point of view β once thresholds are set for an
archetype, no per-system opt-in is needed. Ordinary writes wake a sleeping cluster; every QuerySystem
against the archetype, tier-filtered or not, silently skips clusters that are Sleeping:
game.QuerySystem("IdleDrift", ctx =>
{
foreach (var id in ctx.Entities) { /* never dispatched against a sleeping cluster */ }
}, input: () => antsView, parallel: true, tier: SimTier.Tier2, cellAmortize: 60);
The sleep threshold and heartbeat interval themselves are per-archetype runtime settings
(SleepThresholdTicks, HeartbeatIntervalTicks) β there is no public fluent configuration wrapper for
them yet; today they're set on the archetype's internal cluster runtime state from the host project
(the same InternalsVisibleTo boundary AntHill.Core builds under for engine integration), not from
arbitrary game code.
| Setting | Default | Effect |
|---|---|---|
SleepThresholdTicks |
0 (disabled) |
Consecutive clean ticks before Active β Sleeping; clamped to [0, 65535] |
HeartbeatIntervalTicks |
0 (off) |
When > 0, sleeping clusters wake on a staggered schedule for a periodic re-check |
β οΈ Guarantees & limits
- Wake is bounded to one tick of latency β a write at tick T is
WakePendingby T's tick fence andActivebefore any system dispatches at tick T+1; never permanently missed. - Zero overhead when nothing sleeps β the dormancy filter in dispatch-prepare is skipped entirely while the sleeping count is zero, for every system, every tick.
- Filters both tier-filtered and unfiltered systems β a
SimTier.Allsystem still skips sleeping clusters; dormancy is not a tier-only mechanism. - Activity means a dirty write, not a spatial-only write β a field written exclusively through the zero-copy spatial-update path does not mark the cluster dirty, so it neither resets the sleep counter nor wakes a sleeping cluster on its own.
- Two wake triggers only: a dirty write and the heartbeat timer. Proximity-based wake and
tier-promotion wake are not implemented β a sleeping cluster promoted to
Tier0stays asleep, excluded from dispatch, until a write or heartbeat wakes it. - No public per-archetype configuration API yet β see Usage above;
ClusterSleepStateis the only public surface today.
π§ͺ Tests
- DormancyTests β
SleepAfterThreshold,SleepingClusterSkippedInDispatch,WakeRequest_Transition,HeartbeatWake,NoOverhead_WhenNoSleeping,SleepCounterReset_OnWrite
π Related
- Source: src/Typhon.Engine/Runtime/internals/DormancyReporter.cs (thread-local deferred wake requests, single-threaded drain)
- Source: src/Typhon.Engine/Ecs/internals/ArchetypeClusterState.cs (
DormancySweep,ProcessWakeRequest,TransitionWakePendingToActive) - Source: src/Typhon.Engine/Ecs/public/ClusterSleepState.cs
- Parent feature: Spatial Tiers & Adaptive Dispatch
- Same mechanism, full configuration walkthrough: Spatial category β Cluster Dormancy (Sleep / Wake)