IJobProcessComponentData with generics

I’m getting a weird error when i try to use generics with IJobProcessComponentData or IJobProcessComponentDataWithEntity i can use generics on other types of jobs but not this ones.
I get ArgumentNullException: String reference not set to an instance of a String. as soon as i run the scene. I don’t even have to use that specific job just nee to have it on a system.

Here is an example of a problematic job.

  [BurstCompile]
  struct SampleJob02<T> : IJobProcessComponentData<T, Position> where T : struct, IComponentData {
    public void Execute(ref T data0, ref Position data1) {
      // *******************************
      // Apply some transformations here
      // *******************************
    }
  }

Bellow is the full stack of the error
Error stack

ArgumentNullException: String reference not set to an instance of a String.
Parameter name: s
System.Text.Encoding.GetBytes (System.String s) (at :0)
Unity.Entities.TypeManager.CalculateMemoryOrdering (System.Type type) (at Library/PackageCache/com.unity.entities@0.0.12-preview.21/Unity.Entities/Types/TypeManager.cs:178)
Unity.Entities.TypeManager.BuildComponentType (System.Type type) (at Library/PackageCache/com.unity.entities@0.0.12-preview.21/Unity.Entities/Types/TypeManager.cs:206)
Unity.Entities.TypeManager.CreateTypeIndexThreadSafe (System.Type type) (at Library/PackageCache/com.unity.entities@0.0.12-preview.21/Unity.Entities/Types/TypeManager.cs:159)
Unity.Entities.TypeManager.GetTypeIndex (System.Type type) (at Library/PackageCache/com.unity.entities@0.0.12-preview.21/Unity.Entities/Types/TypeManager.cs:122)
Unity.Entities.ComponentType…ctor (System.Type type, Unity.Entities.ComponentType+AccessMode accessModeType) (at Library/PackageCache/com.unity.entities@0.0.12-preview.21/Unity.Entities/Types/ComponentType.cs:73)
Unity.Entities.IJobProcessComponentDataUtility.GetComponentTypes (System.Type jobType, System.Type interfaceType, System.Int32& processCount, Unity.Entities.ComponentType[ ]& changedFilter) (at Library/PackageCache/com.unity.entities@0.0.12-preview.21/Unity.Entities/IJobProcessComponentData.cs:244)
Unity.Entities.IJobProcessComponentDataUtility.GetComponentTypes (System.Type jobType) (at Library/PackageCache/com.unity.entities@0.0.12-preview.21/Unity.Entities/IJobProcessComponentData.cs:219)
Unity.Entities.JobProcessComponentDataExtensions.GetComponentGroupForIJobProcessComponentData (Unity.Entities.ComponentSystemBase system, System.Type jobType) (at Library/PackageCache/com.unity.entities@0.0.12-preview.21/Unity.Entities/IJobProcessComponentData.cs:397)
Unity.Entities.ComponentSystemBase.InjectNestedIJobProcessComponentDataJobs () (at Library/PackageCache/com.unity.entities@0.0.12-preview.21/Unity.Entities/ComponentSystem.cs:113)
Unity.Entities.ComponentSystemBase.OnBeforeCreateManagerInternal (Unity.Entities.World world) (at Library/PackageCache/com.unity.entities@0.0.12-preview.21/Unity.Entities/ComponentSystem.cs:104)
Unity.Entities.JobComponentSystem.OnBeforeCreateManagerInternal (Unity.Entities.World world) (at Library/PackageCache/com.unity.entities@0.0.12-preview.21/Unity.Entities/ComponentSystem.cs:522)
Unity.Entities.ScriptBehaviourManager.CreateInstance (Unity.Entities.World world) (at Library/PackageCache/com.unity.entities@0.0.12-preview.21/Unity.Entities/ScriptBehaviourManager.cs:21)
Unity.Entities.World.CreateManagerInternal (System.Type type, System.Object[ ] constructorArguments) (at Library/PackageCache/com.unity.entities@0.0.12-preview.21/Unity.Entities/Injection/World.cs:137)
Unity.Entities.World.GetOrCreateManagerInternal (System.Type type) (at Library/PackageCache/com.unity.entities@0.0.12-preview.21/Unity.Entities/Injection/World.cs:165)
Unity.Entities.World.GetOrCreateManager (System.Type type) (at Library/PackageCache/com.unity.entities@0.0.12-preview.21/Unity.Entities/Injection/World.cs:218)
Unity.Entities.DefaultWorldInitialization.GetBehaviourManagerAndLogException (Unity.Entities.World world, System.Type type) (at Library/PackageCache/com.unity.entities@0.0.12-preview.21/Unity.Entities.Hybrid/Injection/DefaultWorldInitialization.cs:21)
UnityEngine.Debug:LogException(Exception)
Unity.Debug:LogException(Exception) (at Library/PackageCache/com.unity.entities@0.0.12-preview.21/Unity.Entities/Stubs/Unity/Debug.cs:25)
Unity.Entities.DefaultWorldInitialization:GetBehaviourManagerAndLogException(World, Type) (at Library/PackageCache/com.unity.entities@0.0.12-preview.21/Unity.Entities.Hybrid/Injection/DefaultWorldInitialization.cs:25)
Unity.Entities.DefaultWorldInitialization:CreateBehaviourManagersForMatchingTypes(Boolean, IEnumerable`1, World) (at Library/PackageCache/com.unity.entities@0.0.12-preview.21/Unity.Entities.Hybrid/Injection/DefaultWorldInitialization.cs:75)
Unity.Entities.DefaultWorldInitialization:Initialize(String, Boolean) (at Library/PackageCache/com.unity.entities@0.0.12-preview.21/Unity.Entities.Hybrid/Injection/DefaultWorldInitialization.cs:57)
Unity.Entities.AutomaticWorldBootstrap:Initialize() (at Library/PackageCache/com.unity.entities@0.0.12-preview.21/Unity.Entities.Hybrid/Injection/AutomaticWorldBootstrap.cs:11)

Is this a bug?
I’m using Unity 2018.3.0f2 with Entities package preview 21

Thank you

Has anyone else experienced this?

Strings are not permitted in burst. Generally in whole ECS, as far I am concerned.
I think you should not use T, but predefined IComponentData.

I’ve used generic jobs like this before fine. Can you post the full job and the component data

Here it is:

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

public interface IFloatValue {
  float GetValue();
}

public struct MyComponentData : IComponentData, IFloatValue {
  public float Value;

  public float GetValue() {
    return Value;
  }
}

public class SampleSystem : JobComponentSystem
{
  [BurstCompile]
  struct SampleJob<T> : IJobProcessComponentData<T, Position> where T : struct, IComponentData, IFloatValue {
    public void Execute(ref T data0, ref Position data1) {
      data1.Value = new float3(data0.GetValue());
    }
  }

  protected override JobHandle OnUpdate(JobHandle inputDeps) {
    inputDeps = new SampleJob<MyComponentData>().Schedule(this, inputDeps);
    return base.OnUpdate(inputDeps);
  }
}

@tertle @GilCat I am curious what problem you two are trying to solve with generics?

Sounds like some boilerplate.

An example.

    /// <summary>
    /// Remove a component from an entity.
    /// </summary>
    public struct RemoveComponentJob<T> : IJob
        where T : struct, IComponentData
    {
        public EntityCommandBuffer EntityCommandBuffer;

        public NativeQueue<Entity> Queue;

        /// <inheritdoc />
        public void Execute()
        {
            while (this.Queue.TryDequeue(out var entity))
            {
                this.EntityCommandBuffer.RemoveComponent<T>(entity);
            }
        }
    }

Though I’ve realized since most of my generic jobs use EntityCommandBuffer I’m not sure I’ve ever tried it with burst.

-edit-

here’s another similar from my batch system that was posted a few days ago

        private class EventBatch<T> : IEventBatch
            where T : struct, IComponentData
        {
            // ...

            [BurstCompile]
            private struct SetJob : IJob
            {
                public NativeQueue<T> Queue;

                public NativeArray<ArchetypeChunk> Chunks;

                public NativeUnit<int> ChunkIndex;

                public NativeUnit<int> EntityIndex;

                public ArchetypeChunkComponentType<T> ComponentType;

                /// <inheritdoc />
                public void Execute()
                {
                    for (; this.ChunkIndex.Value < this.Chunks.Length; this.ChunkIndex.Value++)
                    {
                        var chunk = this.Chunks[this.ChunkIndex.Value];

                        var components = chunk.GetNativeArray(this.ComponentType);

                        var intLocalIndex = this.EntityIndex.Value;

                        while (this.Queue.TryDequeue(out var item) && intLocalIndex < components.Length)
                        {
                            components[intLocalIndex++] = item;
                        }

                        this.EntityIndex.Value = intLocalIndex < components.Length ? intLocalIndex : 0;
                    }
                }
            }
        }

This one is in a generic class though instead of being a specific generic job (don’t try to understand it lol. I should probably add a comment before i forget what it does.)

Those work fine for me too the only ones that doesn’t are when those generic are used in IJobProcessComponentData or IJobProcessComponentDataWithEntity. Burst or without Burst

Perhaps it is the case. I sure understand that this is most likely never needed but the thing is that the error exist when they are applied.
I would use this to get references from a hashmap outside.
Here is something resembling what i use:

public interface IHashValue {
  int GetValue();
}

public struct Node : IComponentData, IHashValue {
  public int Value;
  public int GetValue() {
    return Value;
  }
}

public struct Connection : IComponentData, IHashValue {
  public int Value;
  public Entity Source;
  public Entity Target;

  public int GetValue() {
    return Value;
  }
}

public class SampleSystem : JobComponentSystem
{
  /// <summary>
  /// Hash map to the model objects
  /// </summary>
  public static Dictionary<int, ViZObject> HashMap = new Dictionary<int, ViZObject>();

  [BurstCompile]
  struct CalcJob<T> : IJobProcessComponentData<T> where T : struct, IComponentData, IHashValue {
    public void Execute(ref T hashData) {
      var myModelObj = HashMap[hashData.GetValue()];
      // Calculate 3D visualization for the object model
      myModelObj.CalcModel();
    }
  }

  protected override JobHandle OnUpdate(JobHandle inputDeps) {
    inputDeps = new CalcJob<Node>().Schedule(this, inputDeps);
    inputDeps = new CalcJob<Connection>().Schedule(this, inputDeps);
    return base.OnUpdate(inputDeps);
  }
}
}

What using generics solves here is the need to create one job for calculating the 3D model Node and another for Connection. Boilerplate or not i think it is handy in very few cases.
And yes the design was not thinking in ECS when this was created a few years ago, so it’s being ported and in the future will most likely wont need to use generics.

Thanks for all the very helpful insights!

IJobProcessComponentData is different than the other jobs, since the struct declaration is used to create the component group needed to run the job. For all other jobs the group has to be setup from outside. So guessing you hit an edge case they haven’t fully implemented yet.