Skip to content

Game.InstanceCountSystem

Assembly: Assembly-CSharp
Namespace: Game.Prefabs

Type: class

Base: GameSystemBase, IPreDeserialize

Summary:
InstanceCountSystem maintains an up-to-date count of instantiated prefabs (by prefab Entity) in the world. It scans entities that have a PrefabRef component and increments or decrements per-prefab counters using a NativeParallelHashMap. Counting work is performed in a burst-compiled IJobChunk (UpdateCountsJob) for performance. The system exposes APIs to obtain the counts and to register read/write JobHandle dependencies so other systems can safely read or write the map. PreDeserialize clears the counts and marks the system as "loaded" so the next update performs a full recount.


Fields

  • private EntityQuery m_UpdatedInstancesQuery
    Query that selects entities with PrefabRef and either Created or Deleted, excluding Temp. Used to perform incremental updates when not performing a full recount.

  • private EntityQuery m_AllInstancesQuery
    Query that selects all entities with PrefabRef (excluding Temp). Used to perform a full recount (e.g., after load).

  • private NativeParallelHashMap<Entity, int> m_InstanceCounts
    The map that stores counts per prefab Entity. Allocated with capacity 100 and Allocator.Persistent. Must be disposed in OnDestroy.

  • private JobHandle m_ReadDependencies
    Combined JobHandle representing jobs that have read access to m_InstanceCounts. Used to sequence writers after readers.

  • private JobHandle m_WriteDependencies
    Combined JobHandle representing jobs that write to m_InstanceCounts. Used to sequence readers/writers correctly.

  • private bool m_Loaded
    Flag that is set in PreDeserialize to request a full recount on the next update. GetLoaded() flips it to false and returns prior value.

  • private TypeHandle __TypeHandle
    Holds ComponentTypeHandle instances for PrefabRef and Deleted used by UpdateCountsJob. Populated via __AssignHandles in OnCreateForCompiler.

  • private struct UpdateCountsJob (nested)
    A Burst-compiled IJobChunk that iterates chunks, reads PrefabRef components and Deleted presence, and increments or decrements counts in m_InstanceCounts accordingly. Uses NativeParallelHashMap for concurrent access (the job gets a direct reference to the map in this implementation).

Behavior: - If the chunk contains Deleted component, the job decrements the count for each PrefabRef found; if resulting count <= 0, the map entry is removed. - Otherwise, the job increments the count for each PrefabRef, adding new entries when necessary.

  • private struct TypeHandle (nested)
    Wrapper storing ComponentTypeHandle and ComponentTypeHandle (both read-only) and an inline method to assign them from a SystemState.

Properties

  • None (no public properties). The system exposes GetInstanceCounts(readOnly, out JobHandle) to obtain the counts and the JobHandle dependencies.

Constructors

  • public InstanceCountSystem()
    Default constructor. Marked with [Preserve]. No custom initialization beyond what base constructor does; actual initialization occurs in OnCreate.

Methods

  • [Preserve] protected override void OnCreate() : System.Void
    Sets up EntityQuery instances:
  • m_UpdatedInstancesQuery: PrefabRef (All) and (Created || Deleted) in Any, excluding Temp.
  • m_AllInstancesQuery: PrefabRef (exclude Temp).
    Allocates m_InstanceCounts as NativeParallelHashMap(100, Allocator.Persistent).

  • [Preserve] protected override void OnDestroy() : System.Void
    Disposes the m_InstanceCounts NativeParallelHashMap and calls base.OnDestroy().

  • private bool GetLoaded() : System.Boolean
    Returns true if m_Loaded was set (and resets m_Loaded to false). Used to switch OnUpdate between full recount and incremental update.

  • [Preserve] protected override void OnUpdate() : System.Void
    Chooses the query to run: m_AllInstancesQuery if GetLoaded() returned true (full recount), otherwise m_UpdatedInstancesQuery (incremental). Schedules the burst-compiled UpdateCountsJob over the chosen query if the query is not empty. The job receives ComponentTypeHandles obtained from __TypeHandle and the NativeParallelHashMap via GetInstanceCounts(...). The scheduled job handle is recorded by calling AddCountWriter(jobHandle) and assigned to base.Dependency to maintain system scheduling correctness.

  • public NativeParallelHashMap<Entity, int> GetInstanceCounts(bool readOnly, out JobHandle dependencies) : NativeParallelHashMap
    Returns the internal m_InstanceCounts. Also outputs current dependency JobHandle:

  • If readOnly == true: dependencies = m_WriteDependencies (so readers wait on writers).
  • If readOnly == false: dependencies = Combine(m_ReadDependencies, m_WriteDependencies) (writer must wait on existing readers and writers). This API is how other systems/jobs can obtain the map and the JobHandle they should depend on.

  • public void AddCountReader(JobHandle jobHandle) : System.Void
    Combine the provided jobHandle into m_ReadDependencies (readers) to update the dependency tracking.

  • public void AddCountWriter(JobHandle jobHandle) : System.Void
    Combine the provided jobHandle into m_WriteDependencies (writers) to update the dependency tracking.

  • public void PreDeserialize(Context context) : System.Void
    Called before deserialization/load. It obtains the instance counts map (completing outstanding dependencies), clears the map, and sets m_Loaded = true to trigger a full recount on the next update.

  • [MethodImpl(MethodImplOptions.AggressiveInlining)] private void __AssignQueries(ref SystemState state) : System.Void
    Compiler helper used during generated code path (no runtime query assignment here beyond OnCreate). Called from OnCreateForCompiler.

  • protected override void OnCreateForCompiler() : System.Void
    Compiler-time helper. Calls __AssignQueries and assigns component type handles via __TypeHandle.__AssignHandles.

  • private struct TypeHandle.__AssignHandles(ref SystemState state) : System.Void
    Populates the ComponentTypeHandle fields for PrefabRef and Deleted by calling state.GetComponentTypeHandle(isReadOnly:true).

  • private struct UpdateCountsJob.Execute(...) : System.Void
    The job execute method described above. There is an explicit IJobChunk.Execute implementation forwarding to the strongly-typed Execute.

Usage Example

// Example: reading counts from another system safely
protected override void OnCreate()
{
    base.OnCreate();
    // ... obtain reference to the InstanceCountSystem (DI or World.GetExistingSystem)
}

protected override void OnUpdate()
{
    // Suppose instanceCountSystem is a reference to Game.Prefabs.InstanceCountSystem
    JobHandle deps;
    var counts = instanceCountSystem.GetInstanceCounts(readOnly: true, out deps);

    // Ensure any jobs that wrote the map are finished before accessing on main thread:
    deps.Complete();

    // Read counts on main thread (not recommended for heavy work)
    if (counts.TryGetValue(somePrefabEntity, out var count))
    {
        UnityEngine.Debug.Log($"Instances of prefab {somePrefabEntity}: {count}");
    }

    // If you schedule a job that reads the map, register it as a reader:
    // instanceCountSystem.AddCountReader(someJobHandle);
}

// Example: PreDeserialize (system will clear counts and request full recount)
instanceCountSystem.PreDeserialize(someContext);
// The next Update will run a full recount using m_AllInstancesQuery.

Notes and tips: - The system uses a persistent NativeParallelHashMap which must be disposed; this is handled in OnDestroy. - Use GetInstanceCounts to get the map and the appropriate dependency JobHandle; always respect the returned dependencies (Complete or combine into your job) to avoid race conditions. - For large numbers of prefabs/entities consider the initial capacity (100) and increase if needed to avoid rehashing. - PreDeserialize is intended to be called during save/load so the system will re-scan all instances and rebuild accurate counts.