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
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 NativeParallelHashMapfor 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 ComponentTypeHandleand 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.