InvalidProgramException: Invalid IL code in TestSystem:OnUpdate ()

(I’ve created a minimal project to reproduce the issue, attached bellow)

I’m syncing the entity reference to MonoBehaviour script for creating a user interface.

Originally I needed both the entity and the buffer reference from MonoBehaviour so this was working as expected, but then I removed the buffer and suddenly the error started being thrown.

Here is the SystemBase code from the .unitypackage attached:

using UnityEngine;
using Unity.Entities;

public class TestSystem : SystemBase
{
    protected override void OnUpdate()
    {
        Test test = GameObject.Find("Test").GetComponent<Test>();
        Entity matchedEntity = Entity.Null;

        Entities
        .WithAll<TestTag>()
        .ForEach((
            Entity entity) =>
            {
                // should be only one TestTag entity in the scene
                matchedEntity = entity;
            }
        )
        .Run();

        // this is what I want to sync and it works but only if there is
        // a step of copying to native array like bellow
        test.testEntity = matchedEntity;

        if (matchedEntity == Entity.Null) return;

        // THIS HERE: if the following is commented, the error is thrown
        // "InvalidProgramException: Invalid IL code in TestSystem:OnUpdate () ..."

        GetBufferFromEntity<TestBufferData>(true)[matchedEntity]
        .ToNativeArray(Unity.Collections.Allocator.Temp)
        .CopyTo(test.testBuffer);
    }
}

So when the copying to NativeArray is commented, the error is thrown.

I’m not sure how to debug this further. Any suggestions?

6450075–724054–Reproduce_ECS_Bug.unitypackage (3.49 KB)

Setting InternalBufferCapacity to 0 still works, so copying to an empty NativeArray makes it work, but without copying it throws

Seems that the buffer is not necessary, copying like this works too:

(new NativeArray<int>(0, Allocator.Temp))
.CopyTo(test._fakeArrayToMakeECSWork);

I’m curious what the code gen looks like and how its screwed it up, can you post it?

Sure, where can I find generated code?

DOTS → Dots Compiler → Open inspector

Here is the diff with the working code:

109:(-) Test component = GameObject.Find("Test").GetComponent<Test>();
109:(+) GameObject.Find("Test").GetComponent<Test>();

127:(-) component.testEntity = displayClass.matchedEntity;
128:(-) if (!(displayClass.matchedEntity == Entity.Null))
129:(-) {
130:(-)     new NativeArray<int>(0, Allocator.Temp).CopyTo(component.intNativeArray);
131:(-) }
127:(+) Entity matchedEntity = displayClass.matchedEntity;
128:(+) ((Test)/*Error near IL_00b7: Stack underflow*/).testEntity = matchedEntity;
129:(+) _ = (displayClass.matchedEntity == Entity.Null);

And here is the full failing code:

#define ENABLE_PROFILER
using System.Runtime.CompilerServices;
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Entities;
using Unity.Entities.CodeGeneratedJobForEach;
using Unity.Jobs.LowLevel.Unsafe;
using Unity.Profiling;
using UnityEngine;

public class TestSystem : SystemBase
{
    [Unity.Entities.DOTSCompilerGenerated]
    [BurstCompile]
    [NoAlias]
    private struct <>c__DisplayClass_OnUpdate_LambdaJob0 : IJobChunk
    {
        private struct LambdaParameterValueProviders
        {
            [NoAlias]
            public struct Runtimes
            {
                [NoAlias]
                public LambdaParameterValueProvider_Entity.Runtime runtime_entity;
            }

            [ReadOnly]
            [NoAlias]
            private LambdaParameterValueProvider_Entity forParameter_entity;

            public void ScheduleTimeInitialize(TestSystem componentSystem)
            {
                forParameter_entity.ScheduleTimeInitialize(componentSystem, isReadOnly: true);
            }

            public Runtimes PrepareToExecuteOnEntitiesInMethod(ref ArchetypeChunk p0, int p1, int p2)
            {
                Runtimes result = default(Runtimes);
                result.runtime_entity = forParameter_entity.PrepareToExecuteOnEntitiesIn(ref p0);
                return result;
            }
        }

        public Entity matchedEntity;

        private LambdaParameterValueProviders _lambdaParameterValueProviders;

        [NativeDisableUnsafePtrRestriction]
        private unsafe LambdaParameterValueProviders.Runtimes* _runtimes;

        private static InternalCompilerInterface.JobChunkRunWithoutJobSystemDelegate s_RunWithoutJobSystemDelegateFieldNoBurst;

        private static InternalCompilerInterface.JobChunkRunWithoutJobSystemDelegate s_RunWithoutJobSystemDelegateFieldBurst;

        internal void OriginalLambdaBody(Entity entity)
        {
            matchedEntity = entity;
        }

        public void ReadFromDisplayClass(ref <>c__DisplayClass0_0 displayClass)
        {
            matchedEntity = displayClass.matchedEntity;
        }

        public void WriteToDisplayClass(ref <>c__DisplayClass0_0 displayClass)
        {
            displayClass.matchedEntity = matchedEntity;
        }

        public unsafe void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
        {
            LambdaParameterValueProviders.Runtimes runtimes = _lambdaParameterValueProviders.PrepareToExecuteOnEntitiesInMethod(ref chunk, chunkIndex, firstEntityIndex);
            _runtimes = &runtimes;
            IterateEntities(ref chunk, ref *_runtimes);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public void IterateEntities(ref ArchetypeChunk chunk, [NoAlias] ref LambdaParameterValueProviders.Runtimes runtimes)
        {
            int count = chunk.Count;
            for (int i = 0; i < count; i++)
            {
                OriginalLambdaBody(runtimes.runtime_entity.For(i));
            }
        }

        public void ScheduleTimeInitialize(TestSystem componentSystem, ref <>c__DisplayClass0_0 displayClass)
        {
            _lambdaParameterValueProviders.ScheduleTimeInitialize(componentSystem);
            ReadFromDisplayClass(ref displayClass);
        }

        [BurstCompile]
        [Unity.Entities.MonoPInvokeCallback(typeof(InternalCompilerInterface.JobChunkRunWithoutJobSystemDelegate))]
        public unsafe static void RunWithoutJobSystem(ArchetypeChunkIterator* archetypeChunkIterator, void* jobData)
        {
            UnsafeUtility.AsRef<<>c__DisplayClass_OnUpdate_LambdaJob0>(jobData).RunWithoutJobs(ref *archetypeChunkIterator);
        }
    }

    private EntityQuery <>OnUpdate_LambdaJob0_entityQuery;

    private ProfilerMarker <>OnUpdate_LambdaJob0_profilerMarker;

    protected unsafe override void OnUpdate()
    {
        <>c__DisplayClass0_0 displayClass = default(<>c__DisplayClass0_0);
        Test component = GameObject.Find("Test").GetComponent<Test>();

        GameObject.Find("Test").GetComponent<Test>();
        displayClass.matchedEntity = Entity.Null;
        _ = base.Entities;
        <>c__DisplayClass_OnUpdate_LambdaJob0 jobData = default(<>c__DisplayClass_OnUpdate_LambdaJob0);
        jobData.ScheduleTimeInitialize(this, ref displayClass);
        CompleteDependency();
        EntityQuery query = <>OnUpdate_LambdaJob0_entityQuery;
        InternalCompilerInterface.JobChunkRunWithoutJobSystemDelegate functionPointer = JobsUtility.JobCompilerEnabled ? <>c__DisplayClass_OnUpdate_LambdaJob0.s_RunWithoutJobSystemDelegateFieldBurst : <>c__DisplayClass_OnUpdate_LambdaJob0.s_RunWithoutJobSystemDelegateFieldNoBurst;
        <>OnUpdate_LambdaJob0_profilerMarker.Begin();
        try
        {
            InternalCompilerInterface.RunJobChunk(ref jobData, query, functionPointer);
        }
        finally
        {
            <>OnUpdate_LambdaJob0_profilerMarker.End();
        }
        jobData.WriteToDisplayClass(ref displayClass);
        Entity matchedEntity = displayClass.matchedEntity;
        ((Test)/*Error near IL_00b7: Stack underflow*/).testEntity = matchedEntity;
        _ = (displayClass.matchedEntity == Entity.Null);
    }

    protected internal unsafe override void OnCreateForCompiler()
    {
        base.OnCreateForCompiler();
        <>OnUpdate_LambdaJob0_entityQuery = <>GetEntityQuery_ForOnUpdate_LambdaJob0_From(this);
        <>c__DisplayClass_OnUpdate_LambdaJob0.s_RunWithoutJobSystemDelegateFieldNoBurst = <>c__DisplayClass_OnUpdate_LambdaJob0.RunWithoutJobSystem;
        <>c__DisplayClass_OnUpdate_LambdaJob0.s_RunWithoutJobSystemDelegateFieldBurst = InternalCompilerInterface.BurstCompile(<>c__DisplayClass_OnUpdate_LambdaJob0.s_RunWithoutJobSystemDelegateFieldNoBurst);
        <>OnUpdate_LambdaJob0_profilerMarker = new ProfilerMarker("OnUpdate_LambdaJob0");
    }

    public static EntityQuery <>GetEntityQuery_ForOnUpdate_LambdaJob0_From(ComponentSystemBase componentSystem)
    {
        EntityQueryDesc[] array = new EntityQueryDesc[1];
        (array[0] = new EntityQueryDesc()).All = new ComponentType[1]
        {
            ComponentType.ReadOnly<TestTag>()
        };
        return componentSystem.GetEntityQuery(array);
    }
}

Unity.Entities version: "com.unity.entities": "0.14.0-preview.19"
Tested on Unity 2020.1.7f1 and 2020.1.10f1