Hey everyone,
I am working with ECS for quite a while now, so I am definitely not a beginner. Just yesterday I ran into a baffling burst compile error, that I just can’t seem to track down, so I was hoping I can get some help here.
The error message I am getting:
(0,0): Burst error BC1091: External and internal calls are not allowed inside static constructors: Interop.BCrypt.BCryptGenRandom(System.IntPtr hAlgorithm, byte* pbBuffer, int cbBuffer, int dwFlags)
While compiling job:
Unity.Entities.JobEntityBatchIndexExtensions+JobEntityBatchIndexProducer`1[[Tellas.Storylets.ECS.Systems.FindAvailablePersonsJob, Tellas.Storylets, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]], Unity.Entities, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null::Execute(Unity.Entities.JobEntityBatchIndexExtensions+JobEntityBatchIndexWrapper`1[[Tellas.Storylets.ECS.Systems.FindAvailablePersonsJob, Tellas.Storylets, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]&, Unity.Entities, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null|System.IntPtr, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089|System.IntPtr, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089|Unity.Jobs.LowLevel.Unsafe.JobRanges&, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null|System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
Unity.Entities.JobEntityBatchIndexExtensions+JobEntityBatchIndexProducer`1[[Tellas.Storylets.ECS.Systems.RemoveDuplicatesJob, Tellas.Storylets, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]], Unity.Entities, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null::Execute(Unity.Entities.JobEntityBatchIndexExtensions+JobEntityBatchIndexWrapper`1[[Tellas.Storylets.ECS.Systems.RemoveDuplicatesJob, Tellas.Storylets, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]&, Unity.Entities, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null|System.IntPtr, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089|System.IntPtr, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089|Unity.Jobs.LowLevel.Unsafe.JobRanges&, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null|System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
The system in which the error is supposedly happening:
using Tellas.Character.ECS.Components;
using Tellas.Core.ECS;
using Tellas.PhysicsEngine.ECS.Components;
using Tellas.Storylets.ECS.Components;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Transforms;
namespace Tellas.Storylets.ECS.Systems
{
[UpdateInGroup(typeof(TellasLateSimulationSystemGroup))]
public partial class FindAvailablePersonsSystem : SystemBase
{
EndSimulationEntityCommandBufferSystem commandBufferSystem;
protected override void OnCreate()
{
commandBufferSystem = World.GetExistingSystem<EndSimulationEntityCommandBufferSystem>();
}
protected override void OnUpdate()
{
EntityCommandBuffer ecb = commandBufferSystem.CreateCommandBuffer();
//Can be parallelized
Entities
.WithName("RemoveAvailablePersonTag")
.WithAll<Tag_AvailablePerson>()
.WithStructuralChanges()
.ForEach((Entity e) =>
{
//
EntityManager.RemoveComponent<Tag_AvailablePerson>(e);
}).Run();
//not sure how to parallelize - remove and recreate efficient enough?
Entities
.WithName("ClearAvailableToTalk")
.WithAll<AvailableToTalk>()
.WithAll<AvailableToTalk>()
.WithStructuralChanges()
.ForEach((Entity e) =>
{
//
EntityManager.GetBuffer<AvailableToTalk>(e).Clear();
}).Run();
Dependency = new FindAvailablePersonsJob
{
canTalks = GetComponentDataFromEntity<CanTalk>()
, persons = GetComponentDataFromEntity<Person>()
, availablePersons = GetComponentDataFromEntity<Tag_AvailablePerson>()
, ecb = ecb.AsParallelWriter()
}.ScheduleParallel(Dependency);
Dependency = new RemoveDuplicatesJob
{
ecb = ecb.AsParallelWriter()
}.ScheduleParallel(Dependency);
commandBufferSystem.AddJobHandleForProducer(Dependency);
}
}
[BurstCompile]
[WithAll(typeof(Tag_FindAvailablePersons))]
[WithAll(typeof(Tag_PhysicsTrigger))]
[WithAll(typeof(CollisionTriggerResult))]
public partial struct FindAvailablePersonsJob : IJobEntity
{
[ReadOnly] public ComponentDataFromEntity<CanTalk> canTalks;
[ReadOnly] public ComponentDataFromEntity<Person> persons;
[ReadOnly] public ComponentDataFromEntity<Tag_AvailablePerson> availablePersons;
public EntityCommandBuffer.ParallelWriter ecb;
public void Execute(Entity e, [EntityInQueryIndex] int entityInQueryIndex, in Parent parent, in DynamicBuffer<CollisionTriggerResult> collisionResults)
{
Entity parentEntity = parent.Value;
for(int i = 0; i < collisionResults.Length; ++i)
{
CollisionTriggerResult triggerResult = collisionResults[i];
Entity other = triggerResult.a == e ? triggerResult.b : triggerResult.a;
if(!canTalks.HasComponent(other) || !persons.HasComponent(other))
continue;
if(!availablePersons.HasComponent(other))
ecb.AddComponent<Tag_AvailablePerson>(entityInQueryIndex, other);
AvailableToTalk element = new(other, persons[other]);
ecb.AppendToBuffer(entityInQueryIndex, parentEntity, element);
}
}
}
[BurstCompile]
[WithAll(typeof(AvailableToTalk))]
public partial struct RemoveDuplicatesJob : IJobEntity
{
public EntityCommandBuffer.ParallelWriter ecb;
public void Execute(Entity e, [EntityInQueryIndex] int entityInQueryIndex, ref DynamicBuffer<AvailableToTalk> buffer)
{
ecb.RemoveComponent<AvailableToTalk>(entityInQueryIndex, e);
DynamicBuffer<AvailableToTalk> newBuffer = ecb.AddBuffer<AvailableToTalk>(entityInQueryIndex, e);
NativeParallelHashSet<AvailableToTalk> set = new(buffer.Length, Allocator.Temp);
for(int i = 0; i < buffer.Length; ++i)
{
set.Add(buffer[i]);
}
int j = 0;
newBuffer.Length = set.Count();
foreach(AvailableToTalk availableToTalk in set)
{
newBuffer[j++] = availableToTalk;
}
set.Dispose();
}
}
}
The only constructors being called are on the AvailableToTalk
dynamic buffer element and the hashset for duplicate removal (if there is a faster way, please let me know!)
AvailableToTalk looks like this:
[InternalBufferCapacity(8)]
public struct AvailableToTalk : IBufferElementData, IEquatable<AvailableToTalk>
{
// Actual value each buffer element will store.
public Entity value;
public Person person;
public AvailableToTalk(Entity value, Person person)
{
this.value = value;
this.person = person;
}
public bool Equals(AvailableToTalk other)
{
return value.Equals(other.value) && person.Equals(other.person);
}
public override bool Equals(object obj)
{
return obj is AvailableToTalk other && Equals(other);
}
public override int GetHashCode()
{
return HashCode.Combine(value, person);
}
public static bool operator ==(AvailableToTalk left, AvailableToTalk right)
{
return left.Equals(right);
}
public static bool operator !=(AvailableToTalk left, AvailableToTalk right)
{
return !left.Equals(right);
}
}
And Person is simply an IComponentData with a single int for an ID, nothing more.
I really don’t understand where the error is coming from. I assume it is in the codegen from IJobEntity, but even there I don’t see anything specific.
Before I moved the code to an IJobEntity from a Entities.ForEach it was working fine without errors, but it was also running without burst originally, so no clue if the problem was already occuring before or not.
Any help is greatly appreciated!
Loofou