wave8-ijobchunk-use-enabled-mask-guard
Correctly handle the enableable component bitmask in IJobChunk to avoid processing disabled entities or skipping enabled ones.
- License 许可
- MIT
- 最近更新
- 2026-07-25
- Stars
- ★ 8
- Forks
- 0
01 前言 Front Matter
- name
- wave8-ijobchunk-use-enabled-mask-guard
- tags
- [ "jobs", "chunks", "enableable", "antipattern" ]
- metadata
- { "tier": 3, "internal-only": true }
- description
- Correctly handle the enableable component bitmask in IJobChunk to avoid processing disabled entities or skipping enabled ones.
03 SKILL.md 正文
IJobChunk useEnabledMask Guard
Intent
Correctly handle the enableable component bitmask in IJobChunk to avoid processing disabled entities or skipping enabled ones.
Use When
- An IJobChunk implementation iterates over chunks that contain one or more IEnableableComponent types
- The query includes any component that implements IEnableableComponent
Avoid When
- The job uses IJobEntity — IJobEntity handles the enableable mask automatically; this guard is only needed in IJobChunk
- No IEnableableComponent types are in the query — useEnabledMask will always be false; the fast path handles all entities
Senior Pattern
[BurstCompile]
public struct ProcessActiveEntitiesJob : IJobChunk
{
public ComponentTypeHandle<Velocity> VelocityHandle;
[ReadOnly] public ComponentTypeHandle<ActiveTag> ActiveTagHandle; // IEnableableComponent
[ReadOnly] public EntityTypeHandle EntityHandle;
[BurstCompile]
public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex,
bool useEnabledMask, in v128 chunkEnabledMask)
{
var velocities = chunk.GetNativeArray(ref VelocityHandle);
if (useEnabledMask)
{
// Some entities in chunk may be disabled — iterate only enabled ones:
var enumerator = new ChunkEntityEnumerator(useEnabledMask, chunkEnabledMask, chunk.Count);
while (enumerator.NextEntityIndex(out int i))
{
velocities[i] = new Velocity { Value = velocities[i].Value * 0.9f };
}
}
else
{
// All entities in chunk are enabled — iterate all without mask overhead:
for (int i = 0; i < chunk.Count; i++)
{
velocities[i] = new Velocity { Value = velocities[i].Value * 0.9f };
}
}
}
}
// Schedule — query includes IEnableableComponent type:
EntityQuery query = state.GetEntityQuery(
ComponentType.ReadWrite<Velocity>(),
ComponentType.ReadOnly<ActiveTag>());
state.Dependency = new ProcessActiveEntitiesJob
{
VelocityHandle = state.GetComponentTypeHandle<Velocity>(),
ActiveTagHandle = state.GetComponentTypeHandle<ActiveTag>(isReadOnly: true),
EntityHandle = state.GetEntityTypeHandle()
}.ScheduleParallel(query, state.Dependency);
Anti-Patterns
- Ignoring
useEnabledMaskand iterating allchunk.Countentities — silently processes disabled entities; incorrect simulation results for all state-machine patterns built on IEnableableComponent. - Using a manual bitmask check instead of ChunkEntityEnumerator — error-prone; re-invents platform-specific SIMD bitmask iteration logic.
- Calling
ArchetypeChunk.GetEnabledMask()without checkinguseEnabledMaskfirst — unnecessary work when all entities in the chunk are enabled. - Using IJobChunk when IJobEntity would suffice — IJobEntity handles the mask automatically; prefer it unless chunk-level access is required.
Runtime Risks
- Skipping the useEnabledMask guard silently processes disabled entities — no exception, no warning.
- For IEnableableComponent-based state machines (dead/alive, sleeping/awake), this means dead or sleeping entities receive updates they should not — correctness bug that manifests as incorrect game behavior.
Performance Notes
- ChunkEntityEnumerator uses SIMD bitmask iteration internally — faster than a manual branch-per-entity loop.
- The
useEnabledMask == falsefast path skips bitmask overhead entirely for fully-enabled chunks, which is the common case in well-organized archetypes. - Prefer IJobEntity over IJobChunk for new systems — it eliminates this boilerplate and handles the mask automatically.
Architecture Guidance
- For new systems, prefer IJobEntity (mask is automatic). Use IJobChunk only when chunk-level access is required (pointer arithmetic, DidChange, per-chunk scratch buffers).
- When using IJobChunk with any IEnableableComponent in the query, the
useEnabledMaskguard +ChunkEntityEnumeratorpattern is mandatory — treat it as non-negotiable.
Related Skills
[[wave4-ijobchunk-full-anatomy]], [[enableable-component]], [[wave6-with-disabled-query-filter]], [[wave8-enableable-component-query-mismatch]]
04 同仓库更多 Skills
dots-update-groups
How to choose the right ECS system group and ordering so per-frame and per-fixed-step work is deterministic and never races its own readers. Covers InitializationSystemGroup / SimulationSystemGroup / PresentationSystemGroup / FixedStepSimulationSystemGroup / VariableRateSimulationSystemGroup, UpdateBefore / UpdateAfter peer ordering, OrderFirst / OrderLast group-boundary placement, RateManager, and the cross-group ordering trap. Use when adding an ISystem, fixing "value visible one frame late" bugs, or coupling simulation to a fixed timestep.
dots-transform-patterns
The Entities 1.x transform model — LocalTransform (writable input) vs LocalToWorld (system-owned matrix), Parent/Child hierarchy, reparenting cost, uniform vs non-uniform scale (PostTransformMatrix), and when to opt out with custom transform systems. Use when designing parent/child hierarchies, runtime reparenting (mount/dismount/socket), or choosing transform components at the Baker boundary.
dots-singleton-patterns
Model cross-system shared configuration and game state as a single-entity component with a strict single-writer contract. Covers RequireForUpdate gating, SystemAPI.GetSingleton / GetSingletonRW / HasSingleton / TryGetSingleton, bake-time vs runtime-created vs ECB-system singletons, the "exactly one entity" runtime invariant, and the single-writer rule. Use when designing frame-stable global config, shared mutable state, or any component intended to exist on exactly one entity.
dots-hybrid-bridge
The one-way bridge between DOTS-owned runtime state and Unity-owned presentation. Covers UnityObjectRef for managed asset refs in components, the DOTS-writes / Unity-reads ownership rule, when to use managed IComponentData vs UnityObjectRef vs CompanionComponent, baker-bridge vs runtime-bridge, and SubScene boundary rules. Use when an entity must reference Unity assets (Mesh, Material, AudioClip, ScriptableObject), a MonoBehaviour view must read entity state, or input/UI must trigger ECS work.
dots-event-driven-ecs
Model one-frame events and external requests in ECS without dual ownership, polling, or persistent state leaks. Covers request-tag pattern with ECB, enableable command components (no structural change), event buffers (IBufferElementData), explicit consumer ownership, one-frame latency rules, and the banned C# event / UnityEvent / static bus anti-patterns. Use when input/UI/network must trigger ECS work, when a one-frame signal fans out to N consumers (damage, hit, collision), or when state changes must be processed exactly once.
valid_skill
A complete, well-formed test fixture skill for SkillHub validation testing. Covers basic task routing and code review for Unity projects.