I’m still working on my spawning system, and I think I’ve got it working (at least the entity debugger works correctly). But, I’m getting some errors in my log about thread safety.
2 each of these (I have two teams in the current setup):
InvalidOperationException: The previously scheduled job SpawnAssignmentSystem:GatherEntitiesJob writes to the NativeArray GatherEntitiesJob.Iterator. You are trying to schedule a new job SpawnAssignmentSystem:GatherEntitiesJob, which writes to the same NativeArray (via GatherEntitiesJob.Iterator). To guarantee safety, you must include SpawnAssignmentSystem:GatherEntitiesJob as a dependency of the newly scheduled job.
Unity.Entities.JobForEachExtensions.Schedule (System.Void* fullData, Unity.Collections.NativeArray`1[T] prefilterData, System.Int32 unfilteredLength, System.Int32 innerloopBatchCount, System.Boolean isParallelFor, System.Boolean isFiltered, Unity.Entities.JobForEachExtensions+JobForEachCache& cache, System.Void* deferredCountData, Unity.Jobs.JobHandle dependsOn, Unity.Jobs.LowLevel.Unsafe.ScheduleMode mode) (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/IJobForEach.cs:451)
Unity.Entities.JobForEachExtensions.ScheduleInternal_EC[T] (T& jobData, Unity.Entities.ComponentSystemBase system, Unity.Entities.EntityQuery query, System.Int32 innerloopBatchCount, Unity.Jobs.JobHandle dependsOn, Unity.Jobs.LowLevel.Unsafe.ScheduleMode mode) (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/IJobForEach.gen.cs:1607)
Unity.Entities.JobForEachExtensions.Schedule[T] (T jobData, Unity.Entities.EntityQuery query, Unity.Jobs.JobHandle dependsOn) (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/IJobForEach.gen.cs:1154)
LSS.DCHL.Systems.SpawnAssignmentSystem.OnUpdate (Unity.Jobs.JobHandle inputDependencies) (at Assets/LSS/DCHL/Systems/SpawnAssignmentSystem.cs:77)
Unity.Entities.JobComponentSystem.InternalUpdate () (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ComponentSystem.cs:951)
Unity.Entities.ComponentSystemBase.Update () (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ComponentSystem.cs:287)
Unity.Entities.ComponentSystemGroup.OnUpdate () (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ComponentSystemGroup.cs:451)
UnityEngine.Debug:LogException(Exception)
Unity.Debug:LogException(Exception) (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/Stubs/Unity/Debug.cs:25)
Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ComponentSystemGroup.cs:455)
Unity.Entities.ComponentSystem:InternalUpdate() (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ComponentSystem.cs:818)
Unity.Entities.ComponentSystemBase:Update() (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ComponentSystem.cs:287)
Unity.Entities.DummyDelegateWrapper:TriggerUpdate() (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ScriptBehaviourUpdateOrder.cs:135)
InvalidOperationException: The previously scheduled job SpawnAssignmentSystem:RemoveUnusedSpawnsJob writes to the NativeArray RemoveUnusedSpawnsJob.Iterator. You are trying to schedule a new job SpawnAssignmentSystem:GatherEntitiesJob, which writes to the same NativeArray (via GatherEntitiesJob.Iterator). To guarantee safety, you must include SpawnAssignmentSystem:RemoveUnusedSpawnsJob as a dependency of the newly scheduled job.
Unity.Entities.JobForEachExtensions.Schedule (System.Void* fullData, Unity.Collections.NativeArray`1[T] prefilterData, System.Int32 unfilteredLength, System.Int32 innerloopBatchCount, System.Boolean isParallelFor, System.Boolean isFiltered, Unity.Entities.JobForEachExtensions+JobForEachCache& cache, System.Void* deferredCountData, Unity.Jobs.JobHandle dependsOn, Unity.Jobs.LowLevel.Unsafe.ScheduleMode mode) (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/IJobForEach.cs:451)
Unity.Entities.JobForEachExtensions.ScheduleInternal_EC[T] (T& jobData, Unity.Entities.ComponentSystemBase system, Unity.Entities.EntityQuery query, System.Int32 innerloopBatchCount, Unity.Jobs.JobHandle dependsOn, Unity.Jobs.LowLevel.Unsafe.ScheduleMode mode) (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/IJobForEach.gen.cs:1607)
Unity.Entities.JobForEachExtensions.Schedule[T] (T jobData, Unity.Entities.EntityQuery query, Unity.Jobs.JobHandle dependsOn) (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/IJobForEach.gen.cs:1154)
LSS.DCHL.Systems.SpawnAssignmentSystem.OnUpdate (Unity.Jobs.JobHandle inputDependencies) (at Assets/LSS/DCHL/Systems/SpawnAssignmentSystem.cs:75)
Unity.Entities.JobComponentSystem.InternalUpdate () (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ComponentSystem.cs:951)
Unity.Entities.ComponentSystemBase.Update () (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ComponentSystem.cs:287)
Unity.Entities.ComponentSystemGroup.OnUpdate () (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ComponentSystemGroup.cs:451)
UnityEngine.Debug:LogException(Exception)
Unity.Debug:LogException(Exception) (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/Stubs/Unity/Debug.cs:25)
Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ComponentSystemGroup.cs:455)
Unity.Entities.ComponentSystem:InternalUpdate() (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ComponentSystem.cs:818)
Unity.Entities.ComponentSystemBase:Update() (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ComponentSystem.cs:287)
Unity.Entities.DummyDelegateWrapper:TriggerUpdate() (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ScriptBehaviourUpdateOrder.cs:135)
And then 2 of these:
A Native Collection has not been disposed, resulting in a memory leak. Enable Full StackTraces to get more details.
Here’s my code, I’m not sure where I’m not including the proper job dependencies (I believe I’m tracking the handles appropriately), or where I’m failing to clean up the NativeArrays (which are marked in the last job that uses them to be deallocated with DeallocateOnJobCompletion) – my assumption is that I don’t need to track them so they can be disposed once I’ve done that). I do see this issue being looked into internally ([DeallocateOnJobCompletion] not working? · Issue #116 · Unity-Technologies/EntityComponentSystemSamples · GitHub), so might or might not be an issue.
On another note, I was trying to use EntityQuery.ToEntityArray rather than rolling my own gathering job, and the JobHandle parameter doesn’t seem to be working as a dependency.
using System.Collections.Generic;
using LSS.DCHL.Components;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
namespace LSS.DCHL.Systems
{
[UpdateInGroup( typeof( InitializationSystemGroup ) )]
public sealed class SpawnAssignmentSystem : JobComponentSystem
{
protected override void OnCreate()
{
m_UnassignedPlayers = GetEntityQuery( new EntityQueryDesc
{
All = new[]
{
ComponentType.ReadOnly< PlayerDataComponent >(),
ComponentType.ReadOnly< TeamAssignment >(),
},
None = new[]
{
ComponentType.ReadOnly< SpawnAssignment >(),
},
} );
m_UnassignedSpawns = GetEntityQuery( new EntityQueryDesc
{
All = new[]
{
ComponentType.ReadOnly< CharacterSpawn >(),
ComponentType.ReadOnly< TeamAssignment >(),
},
None = new[]
{
ComponentType.ReadOnly< AssignedSpawn >(),
},
} );
m_CommandBuffer = World.GetOrCreateSystem< BeginInitializationEntityCommandBufferSystem >();
}
protected override JobHandle OnUpdate( JobHandle inputDependencies )
{
EntityManager.GetAllUniqueSharedComponentData( m_AvailableTeams );
JobHandle _merged = inputDependencies;
for ( var _index = 1; _index < m_AvailableTeams.Count; _index++ )
{
TeamAssignment _assignment = m_AvailableTeams[_index];
m_UnassignedSpawns.SetFilter( _assignment );
m_UnassignedPlayers.SetFilter( _assignment );
int _spawnCount = m_UnassignedSpawns.CalculateLength();
int _playerCount = m_UnassignedPlayers.CalculateLength();
if ( _spawnCount == 0 ) continue;
if ( _playerCount == 0 )
{
JobHandle _removeUnusedSpawnsJobHandle = new RemoveUnusedSpawnsJob
{
CommandBuffer = m_CommandBuffer.CreateCommandBuffer().ToConcurrent(),
}.Schedule( m_UnassignedSpawns, inputDependencies );
m_CommandBuffer.AddJobHandleForProducer( _removeUnusedSpawnsJobHandle );
_merged = JobHandle.CombineDependencies( _merged, _removeUnusedSpawnsJobHandle );
continue;
}
var _spawns = new NativeArray< Entity >( _spawnCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory );
JobHandle _toArrayJob = new GatherEntitiesJob
{
Entities = _spawns,
}.Schedule( m_UnassignedSpawns, inputDependencies );
JobHandle _handle = new AssignSpawnsToPlayersJob
{
CommandBuffer = m_CommandBuffer.CreateCommandBuffer().ToConcurrent(),
Spawns = _spawns,
}.Schedule( m_UnassignedPlayers, _toArrayJob );
m_CommandBuffer.AddJobHandleForProducer( _handle );
_merged = JobHandle.CombineDependencies( _merged, _handle );
}
m_AvailableTeams.Clear();
m_UnassignedPlayers.ResetFilter();
m_UnassignedSpawns.ResetFilter();
return _merged;
}
private struct GatherEntitiesJob : IJobForEachWithEntity< CharacterSpawn >
{
[WriteOnly]
public NativeArray< Entity > Entities;
public void Execute( Entity entity, int index, ref CharacterSpawn c0 )
{
Entities[index] = entity;
}
}
private struct AssignSpawnsToPlayersJob : IJobForEachWithEntity< PlayerDataComponent >
{
public EntityCommandBuffer.Concurrent CommandBuffer;
[DeallocateOnJobCompletion]
public NativeArray< Entity > Spawns;
public void Execute( Entity entity, int index, ref PlayerDataComponent playerData )
{
CommandBuffer.AddComponent( index, entity, new SpawnAssignment {Spawn = Spawns[index]} );
CommandBuffer.AddComponent( index, Spawns[index], new AssignedSpawn() );
}
}
private struct RemoveUnusedSpawnsJob : IJobForEachWithEntity< CharacterSpawn >
{
public EntityCommandBuffer.Concurrent CommandBuffer;
public void Execute( Entity entity, int index, ref CharacterSpawn spawn )
{
CommandBuffer.DestroyEntity( index, entity );
}
}
private EntityQuery m_UnassignedPlayers;
private EntityQuery m_UnassignedSpawns;
private EntityCommandBufferSystem m_CommandBuffer;
private readonly List< TeamAssignment > m_AvailableTeams = new List< TeamAssignment >();
}
}
Any ideas?