DLL internal method from a Job is throwing an error when trying to Burst compile

Unity 2022.3.17f1
Burst 1.8.17
Collections 2.5.1

I am currently in the process of jobifying some of my systems which rely on a native plugin.

The following code is throwing errors :

[BurstCompile]
public partial struct RayCastJob : IJob
{
    [NativeDisableUnsafePtrRestriction]
    public NarrowPhaseQuery NarrowPhaseQuery;
    public float3 Origin;
    public float3 Direction;
    public RayCastSettings Settings;
    public CollisionCollectorType CollectorType;
    public RayCastResultCollector Collector;
    [NativeDisableUnsafePtrRestriction]
    public BroadPhaseLayerFilter BroadPhaseLayerFilter;
    [NativeDisableUnsafePtrRestriction]
    public ObjectLayerFilter ObjectLayerFilter;

    public void Execute()
    {
        NarrowPhaseQuery.CastRay(Origin, Direction, Settings, CollectorType, ref Collector, BroadPhaseLayerFilter, ObjectLayerFilter);
    }
}
public unsafe struct NarrowPhaseQuery : IDisposable, IEquatable<NarrowPhaseQuery>
{
    internal readonly NativeHandle<JPH_NarrowPhaseQuery> Handle;

...

    public bool CastRay(float3 origin, float3 direction, RayCastSettings settings,
        CollisionCollectorType collectorType, ref RayCastResultCollector collector,
        BroadPhaseLayerFilter? broadPhaseLayerFilter = null,
        ObjectLayerFilter? objectLayerFilter = null)
    {
        NativeHandle<JPH_BroadPhaseLayerFilter>? broadPhaseLayerFilterHandle = broadPhaseLayerFilter == null ? null : broadPhaseLayerFilter.Value.Handle;
        NativeHandle<JPH_ObjectLayerFilter>? objectLayerFilterHandle = objectLayerFilter == null ? null : objectLayerFilter.Value.Handle;

        return Bindings.JPH_NarrowPhaseQuery_CastRay3(Handle, origin, direction, settings, collectorType, ref collector, broadPhaseLayerFilterHandle, objectLayerFilterHandle, null, null);
    }

...

}
internal static unsafe partial class Bindings
{
    public static bool JPH_NarrowPhaseQuery_CastRay3(NativeHandle<JPH_NarrowPhaseQuery> query, rvec3 origin, float3 direction, RayCastSettings settings, CollisionCollectorType collectorType, ref RayCastResultCollector collector, NativeHandle<JPH_BroadPhaseLayerFilter>? broadPhaseLayerFilter, NativeHandle<JPH_ObjectLayerFilter>? objectLayerFilter, NativeHandle<JPH_BodyFilter>? bodyFilter, NativeHandle<JPH_ShapeFilter>? shapeFilter)
    {
        fixed (RayCastResultCollector* ptr = &collector)
        {
            void* vPtr = ptr;
            return UnsafeBindings.JPH_NarrowPhaseQuery_CastRay3(query, &origin, &direction, &settings, collectorType, RayCastResultCollector.AddResultFuncPointer.Value, vPtr, broadPhaseLayerFilter, objectLayerFilter, bodyFilter, shapeFilter);
        }
    }
}
internal static unsafe partial class UnsafeBindings
{
    [DllImport("joltc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
    [return: NativeTypeName("bool")]
    public static extern NativeBool JPH_NarrowPhaseQuery_CastRay3(
        [NativeTypeName("const JPH_NarrowPhaseQuery *")] JPH_NarrowPhaseQuery* query, 
        [NativeTypeName("const JPH_RVec3 *")] rvec3* origin, 
        [NativeTypeName("const JPH_Vec3 *")] float3* direction, 
        [NativeTypeName("const JPH_RayCastSettings *")] RayCastSettings* rayCastSettings, 
        [NativeTypeName("JPH_CollisionCollectorType")] CollisionCollectorType collectorType, 
        [NativeTypeName("JPH_CastRayResultCallback *")] IntPtr callback, void* userData, 
        JPH_BroadPhaseLayerFilter* broadPhaseLayerFilter, 
        JPH_ObjectLayerFilter* objectLayerFilter, 
        [NativeTypeName("const JPH_BodyFilter *")] JPH_BodyFilter* bodyFilter, 
        [NativeTypeName("const JPH_ShapeFilter *")] JPH_ShapeFilter* shapeFilter);
}

I get the following error :

.\Library\PackageCache\com.unity.collections@2.5.1\Unity.Collections\Memory.cs(89,25): Burst error BC1091: External and internal calls are not allowed inside static constructors: Unity.Collections.LowLevel.Unsafe.UnsafeUtility.FreeTracked(void* memory, Unity.Collections.Allocator allocator)

at Unity.Collections.Memory.Unmanaged.Array.Resize(void* oldPointer, long oldCount, long newCount, Unity.Collections.AllocatorManager.AllocatorHandle allocator, long size, int align) (at .\Library\PackageCache\com.unity.collections@2.5.1\Unity.Collections\Memory.cs:89)
at Unity.Collections.Memory.Unmanaged.Free(void* pointer, Unity.Collections.AllocatorManager.AllocatorHandle allocator) (at .\Library\PackageCache\com.unity.collections@2.5.1\Unity.Collections\Memory.cs:27)
at Unity.Collections.AllocatorManager.TryLegacy(ref Unity.Collections.AllocatorManager.Block block) (at .\Library\PackageCache\com.unity.collections@2.5.1\Unity.Collections\AllocatorManager.cs:1105)
at Unity.Collections.AllocatorManager.Try(ref Unity.Collections.AllocatorManager.Block block) (at .\Library\PackageCache\com.unity.collections@2.5.1\Unity.Collections\AllocatorManager.cs:1129)
at Unity.Collections.Memory.Unmanaged.Array.CustomResize(void* oldPointer, long oldCount, long newCount, Unity.Collections.AllocatorManager.AllocatorHandle allocator, long size, int align) (at .\Library\PackageCache\com.unity.collections@2.5.1\Unity.Collections\Memory.cs:61)

While compiling job:
Unity.Jobs.IJobExtensions+JobStruct`1[[Jolt.Samples.RayCastJob, Jolt.Samples, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]], UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null::Execute(Jolt.Samples.RayCastJob&, Jolt.Samples, 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)

Commenting out the call to UnsafeBindings.JPH_NarrowPhaseQuery_CastRay3 makes the error disappear. I cant really see where static constructors are involved or if they are even involved in the first place.

Any help would be welcomed.

Unless this native plugin was either written by yours truly or you know that it was written with Unity Jobs in mind, this will likely fail in one way or another. Since this is probably in compiled code (dll) you have little chance of actually analyzing what this code does, therefore you mustn’t call it from inside a Job. Otherwise, if you have the source code available, try to port that to a C# jobified version instead.

The called native method would have to be a fully enclosed C method for instance, which only operates on the parameters passed in for it to be thread safe. In all other cases the likelihood of this call not being thread safe are high if not guaranteed.

Burst error BC1091: External and internal calls are not allowed inside static constructors

I’m not seeing the static constructor, it’s probably somewhere outside the snippet. Remember that static field initialization code is compiled into the static ctor, and this pulls in any code referenced from there.

Note that Burst evaluates all static fields and all static constructors at compile time, which is why you can’t allocate any memory there.

I have to admit I am not sure what I am trying to do is achievable as I have no previous experience with interfacing with a native plugin.

I am actually trying to complete this Jolt Physics bindings for Unity which is not originally mine. These bindings rely on this C interface by Amer Koleci and Jolt Physics, that is written in C++ and is designed for multithreading.

This does not extend outwards, eg you can’t call any of its functions and expect it to work in Jobs. The method will have to use the Jolt physics simulation world meaning it does not operate on just the parameters passed into the method.

It may also call other methods internally that offload work onto threads, which would mean even more threads and thus thread congestion is likely to occur. Even if it would work, it might not actually work any faster calling this from a job. Especially if the jobified code doesn’t do anything but call Jolt methods as you currently have it - effectively that makes the Job system just another layer between you and Jolt, and will use more resources to no benefit. The Jolt method may also not be suitable to be called from anything but the main thread.

I have not tested extensively so take what I say with a grain of salt.

At the moment, I have most of the currently implemented bindings working in non Bursted job running on worker threads.

The job example I posted is indeed not relevant but it is only for testing purpose. My idea was to make the bindings Burst compatible so it could be called from Burst compiled jobs that would actually benefits from being Burst compiled (Character controller stuff). You are right about the dll code itself not benefiting from Burst compilation.

In fact it should be easier to separate the work in a chain of Jobs, some of which would not be Burst compiled. But I was just curious as to what is possible with Burst.

Also thanks for the help. It really helps me get a better understanding of how all of this work. Do you know of any good resource other than Unity documentation that cover these topics ?