Stumped with "Compilation was requested for method..."

Currently stumped with “Compilation was requested for method…” warning. It’s annoying because it pauses the editor when playing right after compilation. I usually solve this using RegisterGenericJobType, but now it’s not working. Maybe I miss something so I need a new set of eyes. This is the whole warning that stops the editor:

Compilation was requested for method `BovineLabs.Event.Jobs.JobEvent+JobEventProducer`2[[Game.BaseLabelingSystem`3+ReaderJob[[Game.WorkerReachabilityLabel, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],
[Game.WorkerReachabilityLabelingStrategy, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],
[Game.MarkWorkerReachabilityLabelAsDirty, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]], 
Game.CoreGlue, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],
[Game.MarkWorkerReachabilityLabelAsDirty, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]], 
BovineLabs.Event, Version=0.0.0.0, Culture=neutral, 
PublicKeyToken=null::Execute(BovineLabs.Event.Jobs.JobEvent+JobEventProducer`2[[Game.BaseLabelingSystem`3+ReaderJob[[Game.WorkerReachabilityLabel, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],
[Game.WorkerReachabilityLabelingStrategy, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],
[Game.MarkWorkerReachabilityLabelAsDirty, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]], Game.CoreGlue, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],
[Game.MarkWorkerReachabilityLabelAsDirty, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]&, BovineLabs.Event, 
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)` but it is not a known 
Burst entry point. This may be because the [BurstCompile] method is defined in a generic class, and the generic class is not 
instantiated with concrete types anywhere in your code.

The code are the following. I omitted most of it for brevity. I only added the important stuff. This is the base system class:

public abstract partial class EventReaderSystem<TJob, TData> : JobSystemBase
        where TJob : struct, IJobEvent<TData>
        where TData : unmanaged {
        private GameEventSystem? eventSystem;

        protected override void OnCreate() {
            this.eventSystem = this.World.GetOrCreateSystemManaged<GameEventSystem>();
        }

        protected override JobHandle OnUpdate(JobHandle inputDeps) {
            TJob job = PrepareJob();
            JobHandle handle = job.Schedule<TJob, TData>(this.eventSystem, inputDeps);
            
            ...

            return handle;
        }

        protected abstract TJob PrepareJob();
    }

This is another generic system class that is a child class of EventReaderSystem:

public abstract partial class BaseLabelingSystem<TLabel, TLabelingStrategy, TMarkDirtyEvent> : 
    EventReaderSystem<BaseLabelingSystem<TLabel, TLabelingStrategy, TMarkDirtyEvent>.ReaderJob, TMarkDirtyEvent>
    where TLabel : unmanaged, IComponentData, ITileLabel
    where TLabelingStrategy : unmanaged, ITileLabelingStrategy
    where TMarkDirtyEvent : unmanaged {
    private GameEventSystem? eventSystem;

    private FloodFiller<TLabel, TLabelingStrategy> floodFiller;
    private NativeCounter dirtyCounter;
    
    protected override void OnCreate() {
        base.OnCreate();

        this.eventSystem = GetOrCreateSystemManaged<GameEventSystem>();

        this.floodFiller =
            new FloodFiller<TLabel, TLabelingStrategy>(PROCESS_COUNT_PER_FRAME, Allocator.Persistent);
        this.dirtyCounter = new NativeCounter(Allocator.Persistent);
    }
    
    protected override void OnDestroy() {
        base.OnDestroy();
        this.floodFiller.Dispose();
        this.dirtyCounter.Dispose();
    }
    
    protected override ReaderJob PrepareJob() {
        return new ReaderJob() {
            dirtyCounter = this.dirtyCounter
        };
    }

    protected override JobHandle OnUpdate(JobHandle inputDeps) {
        JobHandle handle = base.OnUpdate(inputDeps);
        
        UpdateFloodFillerJob job = new() {
            allTiles = GetComponentLookup<Tile>(),
            allLabels = GetComponentLookup<TLabel>(),
            strategy = PrepareStrategy(),
            floodFiller = this.floodFiller
        };
        handle = job.Schedule(inputDeps);

        return handle;
    }

    protected abstract TLabelingStrategy PrepareStrategy();
    
    [BurstCompile]
    public struct UpdateFloodFillerJob : IJob {
        ...
    }
    
    [BurstCompile]
    public struct ReaderJob : IJobEvent<TMarkDirtyEvent> {
        ...
    }
}

A concrete implementation of BaseLabelingSystem is done like this:

public partial class WorkerReachabilityLabelingSystem : BaseLabelingSystem<WorkerReachabilityLabel,
        WorkerReachabilityLabelingStrategy, MarkWorkerReachabilityLabelAsDirty> {
    private MultipleGrid2dSystem? gridSystem;

    protected override void OnCreate() {
        base.OnCreate();
        this.gridSystem = GetOrCreateSystemManaged<MultipleGrid2dSystem>();
    }

    protected override WorkerReachabilityLabelingStrategy PrepareStrategy() {
        return new WorkerReachabilityLabelingStrategy() {
            grid = this.gridSystem.GridWrapper,
            allTiles = GetComponentLookup<Tile>()
        };
    }
}

The code where the warning is issued is the JobEventProducer.Execute() here. JobEvent.Schedule() is the one called when scheduling jobs of type IJobEvent. (I’m using an old version of BovineLabs Event system).

[JobProducerType(typeof(JobEvent.JobEventProducer<,>))]
public interface IJobEvent<T> {
    void Execute(T parameter);
}

public static class JobEvent {
    public static JobHandle Schedule<TJob, T>(this TJob jobData, EventSystemBase eventSystem,
        JobHandle dependsOn = default) 
        where TJob : struct, IJobEvent<T> 
        where T : unmanaged {
        return ScheduleInternal<TJob, T>(jobData, eventSystem, dependsOn, false);
    }

    private static unsafe JobHandle ScheduleInternal<TJob, T>(this TJob jobData, EventSystemBase eventSystem,
            JobHandle dependsOn, bool isParallel) 
            where TJob : struct, IJobEvent<T> 
            where T : unmanaged {
            dependsOn = eventSystem.GetEventReaders<T>(dependsOn, out IReadOnlyList<NativeEventStream.Reader> events);

            for (int i = 0; i < events.Count; i++) {
                NativeEventStream.Reader reader = events[i];

                JobEventProducer<TJob, T> fullData = new JobEventProducer<TJob, T> {
                    Reader = reader, JobData = jobData, IsParallel = isParallel
                };

    #if UNITY_2020_2_OR_NEWER
                const ScheduleMode scheduleMode = ScheduleMode.Parallel;
    #else
                const ScheduleMode scheduleMode = ScheduleMode.Batched;
    #endif

                JobsUtility.JobScheduleParameters scheduleParams = new JobsUtility.JobScheduleParameters(
                    UnsafeUtility.AddressOf(ref fullData),
                    isParallel ? JobEventProducer<TJob, T>.InitializeParallel() :
                        JobEventProducer<TJob, T>.InitializeSingle(), dependsOn, scheduleMode);

                dependsOn = isParallel ? JobsUtility.ScheduleParallelFor(ref scheduleParams, reader.ForEachCount, 1) :
                    JobsUtility.Schedule(ref scheduleParams);
            }

            eventSystem.AddJobHandleForConsumer<T>(dependsOn);

            return dependsOn;
        }

    public struct JobEventProducer<TJob, T> 
        where TJob : struct, IJobEvent<T> 
        where T : unmanaged {
        ...

        private delegate void ExecuteJobFunction(ref JobEventProducer<TJob, T> fullData, IntPtr additionalPtr,
            IntPtr bufferRangePatchData, ref JobRanges ranges, int jobIndex);

        public static IntPtr InitializeSingle() {
            ...
        }

        public static void Execute(ref JobEventProducer<TJob, T> fullData, IntPtr additionalPtr,
            IntPtr bufferRangePatchData, ref JobRanges ranges, int jobIndex) {
            while (true) {
                int begin = 0;
                int end = fullData.Reader.ForEachCount;

                // If we are running the job in parallel, steal some work.
                if (fullData.IsParallel) {
                    if (!JobsUtility.GetWorkStealingRange(ref ranges, jobIndex, out begin, out end)) {
                        return;
                    }
                }

                for (int i = begin; i < end; i++) {
                    int count = fullData.Reader.BeginForEachIndex(i);

                    for (int j = 0; j < count; j++) {
                        T e = fullData.Reader.Read<T>();
                        fullData.JobData.Execute(e);
                    }

                    fullData.Reader.EndForEachIndex();
                }

                if (!fullData.IsParallel) {
                    break;
                }
            }
        }
    }
}

I only see two relevant generic jobs:

  • BaseLabelingSystem.UpdateFloodFillerJob
  • BaseLabelingSystem.ReaderJob

So in my AssemblyInfo, I have this:

[assembly: RegisterGenericJobType(typeof(BaseLabelingSystem<WorkerReachabilityLabel, WorkerReachabilityLabelingStrategy, MarkWorkerReachabilityLabelAsDirty>.ReaderJob))]
[assembly: RegisterGenericJobType(typeof(BaseLabelingSystem<WorkerReachabilityLabel, WorkerReachabilityLabelingStrategy, MarkWorkerReachabilityLabelAsDirty>.UpdateFloodFillerJob))]

From here, I don’t know what else to add in AssemblyInfo. There are no generic components and RegisterGenericSystemType only works with struct systems.

Any ideas?

Wow this is a blast from the past. Been years since I’ve seen this code. Anyway.

[JobProducerType(typeof(JobEvent.JobEventProducer<,>))]
public interface IJobEvent<T> {
    void Execute(T parameter);
}

If I recall, generic job interfaces like this aren’t really allowed anymore in 1.0+ because they can’t be used in ISystem/bursted code as unity can’t patch the IL Init code for it.

I recall having to update all my job interfaces to be generic free.

Aww. It’s a project that was started years ago. I’m upgrading it now and there a lot of code like this.

I don’t think it’ll actually break anything as long as you don’t use it in burst, just cause annoying errors.

If you do want to fix it, you’ll have to change it to something like

public interface IJobEvent {
    void Execute(void* parameter);
}

and then case it to your type inside the Execute
or pass the index in and put the container in the job and just read the element etc.

public interface IJobEvent {
    void Execute(int index);
}
1 Like

On a different note, how did you get your code colorized in your posts? I used the preformatted text but it’s not colorized.

Just add cs after the three `

1 Like