SortJob - Generic Jobs in Burst

I’m on Unity 2022.3 , Burst 1.8.17, Collections 2.5.0 pre2
Seems like Burst doesn’t like calling schedule to Generic Jobs inside Bursted code.

Found this which is somehow related. SortJob with IComparer. but since the thread is closed since 2021, and nobody is mentioning this error, I was a bit lost.

I’m trying to call NativeArray.SortJob<T,U> (type and comparer) on a type that is generic too. And It complains by saying this:

InvalidOperationException: System.InvalidOperationException: Reflection data was not set up by an Initialize() call. Support for burst compiled calls to Schedule depends on the Collections package.

I know there’s something about Generic Jobs in the docs, and that it can be solved by defining the specific types on the assembly by adding this attribute:
[assembly: RegisterGenericJobType(typeof(NativeArray<MyGenericStruct<A,B>>.SortJob))]

https://docs.unity3d.com/Packages/com.unity.entities@0.17/manual/ecs_generic_jobs.html

It is also done automatically if you create the Job and storing it on a var, then call Schedule separately.

My problem is that SortJob is in NativeArray’s class. I can’t add that attribute on top.
I also tried doing the very very specific call with types and storing on a variable. It doesn’t want it too (??).

So how do I use that attribute or make this work?
Is there any new solution to this?
Is there any way to use SortJob on Burst at all?

Is there a reason you don’t call Sort() inside an IJob? Unless you have a huge array, single-threaded sorting is typically a lot more efficient.

1 Like

You’re working off a slight misunderstanding: SortJob is an extension method in the Collections package. There are two underlying job types:

Unity.Collections.SortJob<T,U>.SegmentSort

Unity.Collections.SortJob<T,U>.SegmentSortMerge

You need to register specializations of both these job types.

Here’s a complete and functional example:

using System.Collections.Generic;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;


[assembly: RegisterGenericJobType(typeof(SortJob<int2, CustomComparer>.SegmentSort))]
[assembly: RegisterGenericJobType(typeof(SortJob<int2, CustomComparer>.SegmentSortMerge))]

[UpdateInGroup(typeof(SimulationSystemGroup))]
internal partial struct SampleSystem1 : ISystem
{
    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        NativeArray<int2> array = CollectionHelper.CreateNativeArray<int2>(2, state.WorldUpdateAllocator);
        array[0] = new(5, 5);
        array[1] = new(4, 5);
        SortJob<int2, CustomComparer> job = array.SortJob<int2, CustomComparer>(default);
        // equivalent to:
        //SortJob<int2, CustomComparer> job = NativeSortExtension.SortJob<int2, CustomComparer>(array, default);
        var handle = job.Schedule();
        handle.Complete();
        UnityEngine.Debug.Log($"{array[0]}");
    }
}

struct CustomComparer : IComparer<int2>
{
    public int Compare(int2 x, int2 y)
    {
        int comparison = x.x.CompareTo(y.x);
        if (comparison != 0)
        {
            return comparison;
        }
        return x.y.CompareTo(y.y);
    }
}
1 Like

Ah, that’s another option, yes. I didn’t think of it.

But the array could have maybe 100.000 items. And each item contains a bunch of data. Sorting structs is not fast. It contains an ID and some other data that changes depending on the type.

I was doing a Radix Sort on just the indices array (or a basic sort) then organize the rest with the final positions. How about that?

Erm. I couldn’t place the attribute somewhere else. I received an error that I wasn’t placed on top of the job. Maybe I misunderstood. Let me have a look at that and I’ll come back. Thanks!

What do you think about the other solution proposed to use an IJob and a simple Sort inside?

As he said, it’s the better choice if the size isn’t on the high end.

sorting 100.000 structs with around 4 ints/floats each is too much?

The answer is always “profle it”

1 Like

My comparer is more complex than that. It is generic.


struct CustomComparer<Identity, Data> : IComparer<CustomComparer<Identity, Data>>
    where Identity : unmanaged, IIdentity<Identity>
    where Data : unmanaged, IEquatable<Data>
{
    ....
}

So I would do this, right?
(ID is a type that implements IIdentity)

[assembly: RegisterGenericJobType(typeof(SortJob<ID, ComparerStruct<ID, DataType>>.SegmentSort))]

But I get that the second generic type (in SortJob<T,U>) , it can’t be that because it has no boxing conversion from that type (comparerstruct<etc…>) to ID’s IComparer. ¿¿¿

Your type argument for IComparer<T> looks wrong. It should be the element type of the array, which typically wouldn’t be the comparer type itself.

1 Like

Ah, I see. I added IComparer and added the compareTo method to the type to compare itself. So this should be a different struct then?

But it’s the same, really. I get an error.

[assembly: RegisterGenericJobType(typeof(SortJob<SomeIDType, MyClassComparer<typeA, typeB>.SegmentSort))]

It says SegmentSort doesn’t exist.

[Edit] OK. I was confusing everything. This worked (at least compiles)

[assembly: RegisterGenericJobType(typeof(SortJob<MyClass<MyID, MyDataType>, MyClassComparer<MyID, MyType>>.SegmentSort))]

2 Likes