[SOLVED] Generic component type breaks standalone build

I have implemented a job component system that uses generic jobs and components.
All works well in the editor but when I try to do a standalone build, it fails with this error:

Unable to find interface method Unity.Entities.IJobForEach_C1<GenericComponent1<T>>.Execute(ref T0) from type GenericJob1 at Unity.Entities.JobForEachExtensions.JobStruct_Process_C2<GenericJob1<Unity.Transforms.Translation>,GenericComponent1>.ExecuteChunk(ref Unity.Entities.JobForEachExtensions.JobStruct_Process_C2<GenericJob1<Unity.Transforms.Translation>,GenericComponent1<T>> jobData, System.IntPtr bufferRangePatchData, int begin, int end, Unity.Entities.ArchetypeChunk* chunks, int* entityIndices) at Unity.Entities.JobForEachExtensions.JobStruct_Process_C2<GenericJob1<Unity.Transforms.Translation>,GenericComponent1>.Execute(ref Unity.Entities.JobForEachExtensions.JobStruct_Process_C2<GenericJob1<Unity.Transforms.Translation>,GenericComponent`1> jobData, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, ref Unity.Jobs.LowLevel.Unsafe.JobRanges ranges, int jobIndex)

The standalone build succeeds if I remove [BurstCompile].
Build target is Windows x86 Standalone
I have tried both Mono and IL2CPP backends and I am using:
Unity 2019.3.0b8
Burst 1.1.2
DOTS Windows Platform 0.1.6
Entities 0.1.1

This is my code:

using Unity.Entities;
using Unity.Burst;
using Unity.Jobs;
using Unity.Transforms;

[assembly: RegisterGenericComponentType(typeof(GenericJob<Translation>))]
[assembly: RegisterGenericComponentType(typeof(GenericComponent<Translation>))]

public class MySystem : JobComponentSystem
{
    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        inputDeps = new GenericJob<Translation>().Schedule(this);
        inputDeps.Complete();
        return inputDeps;
    }
}

public struct GenericComponent<T> : IComponentData 
where T : struct, IComponentData
{
    public T data;
}

[BurstCompile]
public struct GenericJob<T> : IJobForEach<GenericComponent<T>> where T : struct, IComponentData
{
    public void Execute(ref GenericComponent<T> componentData)
    {
     
    }
}

I do not believe declaring a IJobForEach like that is currently supported by burst. There are currently some limitations on how generics work, specifically with IJobForEach but I can’t remember precisely.

1 Like

I should add generics do work on most other job types without issue so while it might be a little more boilerplate you can still get an generic jobs working.

using Unity.Entities;
using Unity.Burst;
using Unity.Jobs;
using Unity.Transforms;

[assembly: RegisterGenericComponentType(typeof(GenericComponent<Translation>))]

public class TranslationMySystem : MySystem<Translation>
{
}

public class MySystem<T> : JobComponentSystem
    where T : struct, IComponentData
{
    private EntityQuery query;

    protected override void OnCreate()
    {
        // data for testing
        this.EntityManager.CreateEntity(typeof(T));

        this.query = this.GetEntityQuery(ComponentType.ReadWrite<T>());
    }

    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        inputDeps = new GenericJob<T>
            {
                TType = this.GetArchetypeChunkComponentType<T>(),
            }
            .Schedule(this.query, inputDeps);

        return inputDeps;
    }
}

[BurstCompile]
public struct GenericJob<T> : IJobChunk
    where T : struct, IComponentData
{
    public ArchetypeChunkComponentType<T> TType;

    public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
    {
        var array = chunk.GetNativeArray(this.TType);

        for (var i = 0; i < chunk.Count; i++)
        {
            T componentData = array[i];
        }
    }
}

public struct GenericComponent<T> : IComponentData
    where T : struct, IComponentData
{
    public T data;
}
1 Like

Works like a charm! :smile:

Thanks!

Keep in mind Burst probably isn’t running in a build for generic jobs scheduled by a generic method or system.

Oh yeah, that’s totally true (and I just tested).

This is not something I have really tried but seemed like a quick solution to throw up but I guess it’s not really a solution then!

However, something like this does work though (and I actually tested it this time.)

using Unity.Entities;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Transforms;

[assembly: RegisterGenericComponentType(typeof(GenericComponent<Translation>))]

public class MySystem : JobComponentSystem
{
    private EntityQuery query;

    protected override void OnCreate()
    {
        // data for testing
        using (var n = new NativeArray<Entity>(1000000, Allocator.Temp))
        {
            var a = this.EntityManager.CreateArchetype(typeof(GenericComponent<Translation>));
            this.EntityManager.CreateEntity(a, n);
        }

        this.query = this.GetEntityQuery(ComponentType.ReadWrite<GenericComponent<Translation>>());
    }

    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        inputDeps = new GenericJob<GenericComponent<Translation>>
            {
                TType = this.GetArchetypeChunkComponentType<GenericComponent<Translation>>(),
            }
            .Schedule(this.query, inputDeps);

        return inputDeps;
    }
}

[BurstCompile]
public struct GenericJob<T> : IJobChunk
    where T : struct, IComponentData
{
    public ArchetypeChunkComponentType<T> TType;

    public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
    {
        var array = chunk.GetNativeArray(this.TType);

        for (var i = 0; i < chunk.Count; i++)
        {
            T componentData = array[i];
        }
    }
}

public struct GenericComponent<T> : IComponentData
    where T : struct, IComponentData
{
    public T data;
}

In case anyone stumbles on the same problem, I ended up with a slightly modified version of tertle’s code in order to access GenericComponent instead of T in my job:

using Unity.Entities;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Transforms;

[assembly: RegisterGenericComponentType(typeof(GenericComponent<Translation>))]

public class TranslationMySystem : MySystem<Translation>
{
}

public class MySystem<T> : JobComponentSystem where T : struct, IComponentData
{
    private EntityQuery query;

    protected override void OnCreate()
    {
        // data for testing
        using (var n = new NativeArray<Entity>(1000000, Allocator.Temp))
        {
            var a = this.EntityManager.CreateArchetype(typeof(GenericComponent<T>));
            this.EntityManager.CreateEntity(a, n);
        }

        this.query = this.GetEntityQuery(ComponentType.ReadWrite<GenericComponent<T>>());
    }

    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        inputDeps = new GenericJob<T>
        {
            TType = this.GetArchetypeChunkComponentType<GenericComponent<T>>(),
        }
        .Schedule(this.query, inputDeps);

        return inputDeps;
    }
}

[BurstCompile]
public struct GenericJob<T> : IJobChunk where T : struct, IComponentData
{
    public ArchetypeChunkComponentType<GenericComponent<T>> TType;
   
    public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
    {
        var array = chunk.GetNativeArray(this.TType);
       
        for (var i = 0; i < chunk.Count; i++)
        {
            var genericComponent = array[i];
            genericComponent.metaData++;
            array[i] = genericComponent;
        }
    }
}

public struct GenericComponent<T> : IComponentData where T : struct, IComponentData
{
    public T data;
    public int metaData;
}

Now I can simply declare a class WhateverSystem : MySystem and metadata on all GenericComponent will automatically be processed in parallel.

Thanks for the help! :smile:

@kingstone426 , could you please share your use case ? I ask because I very well understand this must be useful for my procedural generation, I just don’t know how yet, trying to wrap my mind around the data design of my project. Thanks.

My particular use case is interpolation, i.e. blending/tweening/lerping between two values:

public struct InterpolationComponent<T> : IComponentData where T : IComponentData
{
    public ulong startTime;
    public ulong targetTime;
    public T startValue;
    public T targetValue;

    public float percentage;
}

This way, if an entity has several components that require interpolation, I can add separate interpolation components using generics, e.g., InterpolationComponent InterpolationComponent etc.

I am sure there are many other use cases where you will want to attach some MetaData to an entity with T.

3 Likes