Advertisement
Guest User

Untitled

a guest
Feb 18th, 2020
153
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 29.08 KB | None | 0 0
  1. #define DEBUG_JOBS
  2. namespace Pathfinding.Jobs {
  3.     using System.Reflection;
  4.     using Unity.Collections;
  5.     using Unity.Jobs;
  6.     using Unity.Burst;
  7.     using System.Collections.Generic;
  8.     using Unity.Collections.LowLevel.Unsafe;
  9.     using Pathfinding.Util;
  10.     using Unity.Mathematics;
  11.     using System.Threading;
  12.     using System.Runtime.InteropServices;
  13.  
  14.     /** Disable the check that prevents jobs from including uninitialized native arrays open for reading.
  15.      *
  16.      * Sometimes jobs have to include a readable native array that starts out uninitialized.
  17.      * The job might for example write to it and later read from it in the same job.
  18.      *
  19.      * \see #JobDependencyHandler.NewNativeArray
  20.      */
  21.     class DisableUninitializedReadCheckAttribute : System.Attribute {
  22.     }
  23.  
  24.     /** Very simple list based on NativeList */
  25.     struct NativeList<T> where T : struct {
  26.         public int count;
  27.         public NativeArray<T> data;
  28.  
  29.         public void Add (T item) {
  30.             if (count == data.Length || !data.IsCreated) Memory.Realloc(ref data, math.ceilpow2(count+1), Allocator.Persistent, NativeArrayOptions.ClearMemory);
  31.             data[count] = item;
  32.             count++;
  33.         }
  34.  
  35.         public void Clear () {
  36.             count = 0;
  37.             if (data.IsCreated) data.Dispose();
  38.         }
  39.     }
  40.  
  41.     struct NativeArrayArena {
  42.         List<NativeArray<byte> > buffer;
  43.  
  44.         public void Add<T>(NativeArray<T> data) where T : struct {
  45.             if (buffer == null) buffer = ListPool<NativeArray<byte> >.Claim();
  46.             buffer.Add(data.Reinterpret<byte>(UnsafeUtility.SizeOf<T>()));
  47.         }
  48.  
  49.         public void DisposeAll () {
  50.             UnityEngine.Profiling.Profiler.BeginSample("Disposing");
  51.             if (buffer != null) {
  52.                 for (int i = 0; i < buffer.Count; i++) buffer[i].Dispose();
  53.                 ListPool<NativeArray<byte> >.Release(ref buffer);
  54.             }
  55.             UnityEngine.Profiling.Profiler.EndSample();
  56.         }
  57.     }
  58.  
  59.     public struct JobHandleWithMainThreadWork {
  60.         JobHandle handle;
  61.         JobDependencyTracker tracker;
  62.         IEnumerator<JobHandle> coroutine;
  63.  
  64.         public JobHandleWithMainThreadWork (JobHandle handle, JobDependencyTracker tracker) {
  65.             this.handle = handle;
  66.             this.tracker = tracker;
  67.             this.coroutine = null;
  68.         }
  69.  
  70.         public JobHandleWithMainThreadWork (IEnumerator<JobHandle> handles, JobDependencyTracker tracker) {
  71.             this.handle = default;
  72.             this.coroutine = handles;
  73.             this.tracker = tracker;
  74.         }
  75.  
  76.         public void Complete () {
  77.             do {
  78.                 // Calling Complete on an empty struct should also work
  79.                 if (tracker != null) tracker.CompleteMainThreadWork();
  80.                 handle.Complete();
  81.                 if (coroutine != null && coroutine.MoveNext()) handle = coroutine.Current;
  82.                 else coroutine = null;
  83.             } while (coroutine != null);
  84.         }
  85.  
  86.  
  87.         public System.Collections.IEnumerable CompleteTimeSliced (float maxMillisPerStep) {
  88.             do {
  89.                 if (tracker != null) {
  90.                     foreach (var _ in tracker.CompleteMainThreadWorkTimeSliced(maxMillisPerStep)) yield return null;
  91.                 }
  92.                 while (!handle.IsCompleted) yield return null;
  93.                 handle.Complete();
  94.                 if (coroutine != null && coroutine.MoveNext()) handle = coroutine.Current;
  95.                 else coroutine = null;
  96.             } while (coroutine != null);
  97.         }
  98.     }
  99.  
  100.     /** Automatic dependency tracking for the Unity Job System.
  101.      *
  102.      * Uses reflection to find the [ReadOnly] and [WriteOnly] attributes on job data struct fields.
  103.      * These are used to automatically figure out dependencies between jobs.
  104.      *
  105.      * A job that reads from an array depends on the last job that wrote to that array.
  106.      * A job that writes to an array depends on the last job that wrote to the array as well as all jobs that read from the array.
  107.      *
  108.      * \snippet MiscSnippets.cs JobDependencyTracker
  109.      *
  110.      * \see #Pathfinding.Util.IJobExtensions
  111.      */
  112.     public class JobDependencyTracker : IAstarPooledObject {
  113.         internal List<NativeArraySlot> slots = ListPool<NativeArraySlot>.Claim();
  114.         internal List<MainThreadWork> mainThreadWork = ListPool<MainThreadWork>.Claim();
  115.         NativeList<JobHandle> temporaryJobs;
  116.         List<GCHandle> gcHandlesToFree;
  117.         NativeArrayArena arena;
  118.         internal NativeArray<JobHandle> dependenciesScratchBuffer;
  119.         public bool forceLinearDependencies { get; private set; }
  120.  
  121.         internal struct MainThreadWork {
  122.             public JobHandle dependsOn;
  123.             public IJob job;
  124.             public ManualResetEvent doneEvent;
  125.             public ManualResetEvent dependenciesDoneEvent;
  126. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  127.             public AtomicSafetyHandle safetyHandle;
  128. #endif
  129.  
  130.             public bool Complete (TimeSlice timeSlice) {
  131.                 JobHandle.ScheduleBatchedJobs();
  132.                 // Calling 'Complete' can cause rare deadlocks.
  133.                 // A slightly less efficient solution is to use the dependenciesDoneEvent.
  134.                 // This seems to work without any rare deadlocks.
  135.                 //
  136.                 // This happens because if Complete is called the main thread may be scheduled to run the
  137.                 // job that waits for the main thread job. This will cause a deadlock.
  138.                 // dependsOn.Complete();
  139.                 dependenciesDoneEvent.WaitOne();
  140.                 UnityEngine.Profiling.Profiler.BeginSample("Main thread job");
  141.                 bool finished = true;
  142.  
  143.                 try {
  144.                     // Note: if this method throws an exception then finished will be true and the job marked as completed
  145.                     if (job is IJobTimeSliced sliced) finished = sliced.Execute(timeSlice);
  146.                     else job.Execute();
  147.                 } finally {
  148.                     UnityEngine.Profiling.Profiler.EndSample();
  149.                     if (finished) {
  150.                         doneEvent.Set();
  151. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  152.                         AtomicSafetyHandle.CheckDeallocateAndThrow(safetyHandle);
  153.                         AtomicSafetyHandle.Release(safetyHandle);
  154. #endif
  155.                     }
  156.                 }
  157.                 return finished;
  158.             }
  159.  
  160.             public bool RunIfReady (TimeSlice timeSlice) {
  161.                 //if (dependsOn.IsCompleted) {
  162.                 if (dependenciesDoneEvent.WaitOne(0)) {
  163.                     return Complete(timeSlice);
  164.                 }
  165.                 return false;
  166.             }
  167.         }
  168.  
  169.         internal struct JobInstance {
  170.             public JobHandle handle;
  171.             public int hash;
  172.             #if DEBUG_JOBS
  173.             public string name;
  174.             #endif
  175.         }
  176.  
  177.         internal struct NativeArraySlot {
  178.             public long hash;
  179.             public JobInstance lastWrite;
  180.             public List<JobInstance> lastReads;
  181.             public bool initialized;
  182.             public bool hasWrite;
  183.         }
  184.  
  185.         // Note: burst compiling even an empty job can avoid the overhead of going from unmanaged to managed code.
  186.         /* [BurstCompile]
  187.         struct JobDispose<T> : IJob where T : struct {
  188.             [DeallocateOnJobCompletion]
  189.             [DisableUninitializedReadCheck]
  190.             public NativeArray<T> data;
  191.  
  192.             public void Execute () {
  193.             }
  194.         }*/
  195.  
  196.         struct JobRaycastCommandDummy : IJob {
  197.             [ReadOnly]
  198.             public NativeArray<UnityEngine.RaycastCommand> commands;
  199.             [WriteOnly]
  200.             public NativeArray<UnityEngine.RaycastHit> results;
  201.  
  202.             public void Execute () {}
  203.         }
  204.  
  205.         /** JobHandle that represents a dependency for all jobs.
  206.          * All native arrays that are written (and have been tracked by this tracker) to will have their final results in them
  207.          * when the returned job handle is complete.
  208.          */
  209.         public JobHandle AllWritesDependency {
  210.             get {
  211.                 var handles = new NativeArray<JobHandle>(slots.Count, Allocator.Temp);
  212.                 for (int i = 0; i < slots.Count; i++) handles[i] = slots[i].lastWrite.handle;
  213.                 var dependencies = JobHandle.CombineDependencies(handles);
  214.                 handles.Dispose();
  215.                 return dependencies;
  216.             }
  217.         }
  218.  
  219.         /** Disable dependency tracking and just run jobs one after the other.
  220.          * This may be faster in some cases since dependency tracking has some overhead.
  221.          */
  222.         public void SetLinearDependencies (bool linearDependencies) {
  223.             if (linearDependencies) {
  224.                 CompleteMainThreadWork();
  225.                 AllWritesDependency.Complete();
  226.             }
  227.             forceLinearDependencies = linearDependencies;
  228.         }
  229.  
  230.         public NativeArray<T> NewNativeArray<T>(int length, Allocator allocator, NativeArrayOptions options = NativeArrayOptions.ClearMemory) where T : struct {
  231.             var res = new NativeArray<T>(length, allocator, options);
  232.  
  233.             unsafe {
  234.                 slots.Add(new NativeArraySlot {
  235.                     hash = (long)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(res),
  236.                     lastWrite = default,
  237.                     lastReads = ListPool<JobInstance>.Claim(),
  238.                     initialized = options == NativeArrayOptions.ClearMemory,
  239.                 });
  240.  
  241.                 UnityEngine.Debug.Log("Allocating new native array with ptr " + slots[slots.Count-1].hash + " as " + options);
  242.             }
  243.             arena.Add(res);
  244.             return res;
  245.         }
  246.  
  247.         /** Schedules a raycast batch command.
  248.          * Like RaycastCommand.ScheduleBatch, but dependencies are tracked automatically.
  249.          */
  250.         public JobHandle ScheduleBatch (NativeArray<UnityEngine.RaycastCommand> commands, NativeArray<UnityEngine.RaycastHit> results, int minCommandsPerJob) {
  251.             if (forceLinearDependencies) {
  252.                 UnityEngine.RaycastCommand.ScheduleBatch(commands, results, minCommandsPerJob).Complete();
  253.                 return default;
  254.             }
  255.  
  256.             // Create a dummy structure to allow the analyzer to determine how the job reads/writes data
  257.             var dummy = new JobRaycastCommandDummy { commands = commands, results = results };
  258.             var dependencies = JobDependencyAnalyzer<JobRaycastCommandDummy>.GetDependencies(ref dummy, this);
  259.             var job = UnityEngine.RaycastCommand.ScheduleBatch(commands, results, minCommandsPerJob, dependencies);
  260.  
  261.             JobDependencyAnalyzer<JobRaycastCommandDummy>.Scheduled(ref dummy, this, job);
  262.             return job;
  263.         }
  264.  
  265.         /* Disposes the native array after all the current jobs are finished with it */
  266.         //public void DeferDispose<T>(NativeArray<T> data) where T : struct {
  267.         //temporaryJobs.Add(new JobDispose<T> { data = data }.Schedule(this));
  268.         //}
  269.  
  270.         /** Frees the GCHandle when the JobDependencyTracker is disposed */
  271.         public void DeferFree (GCHandle handle, JobHandle dependsOn) {
  272.             if (gcHandlesToFree == null) gcHandlesToFree = ListPool<GCHandle>.Claim();
  273.             gcHandlesToFree.Add(handle);
  274.         }
  275.  
  276.         public void CompleteMainThreadWork () {
  277.             if (mainThreadWork != null) {
  278.                 for (int i = 0; i < mainThreadWork.Count; i++) {
  279.                     mainThreadWork[i].Complete(TimeSlice.Infinite);
  280.                 }
  281.                 mainThreadWork.Clear();
  282.             }
  283.         }
  284.  
  285.         /** Runs main thread work with a given time budget per step.
  286.          * \note Only main thread jobs implementing the IJobTimeSliced interface can be time sliced. IJob main thread jobs will always run in a single step, regardless of how long it takes.
  287.          */
  288.         public System.Collections.IEnumerable CompleteMainThreadWorkTimeSliced (float maxMillis) {
  289.             if (mainThreadWork != null) {
  290.                 int i = 0;
  291.                 while (i < mainThreadWork.Count) {
  292.                     var slice = TimeSlice.MillisFromNow(maxMillis);
  293.                     while (i < mainThreadWork.Count && mainThreadWork[i].RunIfReady(slice)) i++;
  294.                     yield return null;
  295.                 }
  296.                 mainThreadWork.Clear();
  297.             }
  298.         }
  299.  
  300.         #if DEBUG_JOBS
  301.         internal void JobReadsFrom (JobHandle job, long nativeArrayHash, int jobHash, string jobName)
  302.         #else
  303.         internal void JobReadsFrom (JobHandle job, long nativeArrayHash, int jobHash)
  304.         #endif
  305.         {
  306.             for (int j = 0; j < slots.Count; j++) {
  307.                 var slot = slots[j];
  308.                 if (slot.hash == nativeArrayHash) {
  309.                     // If the job only reads from the array then we just add this job to the list of readers
  310.                     slot.lastReads.Add(new JobInstance {
  311.                         handle = job,
  312.                         hash = jobHash,
  313.                         #if DEBUG_JOBS
  314.                         name = jobName,
  315.                         #endif
  316.                     });
  317.                     break;
  318.                 }
  319.             }
  320.         }
  321.  
  322.         #if DEBUG_JOBS
  323.         internal void JobWritesTo (JobHandle job, long nativeArrayHash, int jobHash, string jobName)
  324.         #else
  325.         internal void JobWritesTo (JobHandle job, long nativeArrayHash, int jobHash)
  326.         #endif
  327.         {
  328.             for (int j = 0; j < slots.Count; j++) {
  329.                 var slot = slots[j];
  330.                 if (slot.hash == nativeArrayHash) {
  331.                     // If the job writes to the array then this job is now the last writer
  332.                     slot.lastWrite = new JobInstance {
  333.                         handle = job,
  334.                         hash = jobHash,
  335.                         #if DEBUG_JOBS
  336.                         name = jobName,
  337.                         #endif
  338.                     };
  339.                     slot.lastReads.Clear();
  340.                     // The array no longer contains uninitialized data.
  341.                     // Parts of it may still be uninitialized if the job doesn't write to everything, but that's something that this class cannot track.
  342.                     slot.initialized = true;
  343.                     UnityEngine.Debug.Log("Job writes to native array ptr " + slot.hash);
  344.                     slot.hasWrite = true;
  345.                     slots[j] = slot;
  346.                     break;
  347.                 }
  348.             }
  349.         }
  350.  
  351.         /** Diposes this tracker.
  352.          * This will pool all used lists which makes the GC happy.
  353.          *
  354.          * \note It is necessary to call this method to avoid memory leaks if you are using the DeferDispose method. But it's a good thing to do otherwise as well.
  355.          * It is automatically called if you are using the ObjectPool<T>.Release method.
  356.          */
  357.         void Dispose () {
  358.             if (mainThreadWork.Count > 0) throw new System.InvalidOperationException("Cannot pool the JobDependencyTracker while there are still main thread work items to complete. Call CompleteMainThreadWork() first.");
  359.             for (int i = 0; i < slots.Count; i++) ListPool<JobInstance>.Release(slots[i].lastReads);
  360.  
  361.             // Need to call complete on e.g. dispose jobs, otherwise these will create small memory leaks due to the handles being kept allocated
  362.             for (int i = 0; i < temporaryJobs.count; i++) temporaryJobs.data[i].Complete();
  363.             temporaryJobs.Clear();
  364.  
  365.             if (gcHandlesToFree != null) {
  366.                 for (int i = 0; i < gcHandlesToFree.Count; i++) gcHandlesToFree[i].Free();
  367.                 ListPool<GCHandle>.Release(ref gcHandlesToFree);
  368.             }
  369.  
  370.             slots.Clear();
  371.             mainThreadWork.Clear();
  372.             arena.DisposeAll();
  373.             forceLinearDependencies = false;
  374.             if (dependenciesScratchBuffer.IsCreated) dependenciesScratchBuffer.Dispose();
  375.         }
  376.  
  377.         void IAstarPooledObject.OnEnterPool () {
  378.             Dispose();
  379.         }
  380.     }
  381.  
  382.     public struct TimeSlice {
  383.         public long endTick;
  384.         public static readonly TimeSlice Infinite = new TimeSlice { endTick = long.MaxValue };
  385.         public bool expired => System.DateTime.UtcNow.Ticks > endTick;
  386.         public static TimeSlice MillisFromNow (float millis) {
  387.             return new TimeSlice { endTick = System.DateTime.UtcNow.Ticks + (long)(millis * 10000) };
  388.         }
  389.     }
  390.  
  391.     public interface IJobTimeSliced : IJob {
  392.         bool Execute(TimeSlice timeSlice);
  393.     }
  394.  
  395.     /** Extension methods for IJob and related interfaces */
  396.     public static class IJobExtensions {
  397.         struct ManagedJob : IJob {
  398.             public GCHandle handle;
  399.  
  400.             public void Execute () {
  401.                 ((IJob)handle.Target).Execute();
  402.                 handle.Free();
  403.             }
  404.         }
  405.  
  406.         struct ManagedActionJob : IJob {
  407.             public GCHandle handle;
  408.  
  409.             public void Execute () {
  410.                 ((System.Action)handle.Target)();
  411.                 handle.Free();
  412.             }
  413.         }
  414.  
  415.         struct ManagedJobParallelForBatch : IJobParallelForBatched {
  416.             public GCHandle handle;
  417.  
  418.             public bool allowBoundsChecks => false;
  419.  
  420.             public void Execute (int startIndex, int count) {
  421.                 ((Pathfinding.Jobs.IJobParallelForBatched)handle.Target).Execute(startIndex, count);
  422.             }
  423.         }
  424.  
  425.         /** Schedule a job and handle dependencies automatically.
  426.          * You need to have "using Pathfinding.Util" in your script to be able to use this extension method.
  427.          *
  428.          * \see #Pathfinding.Util.JobDependencyTracker
  429.          */
  430.         public static JobHandle Schedule<T>(this T data, JobDependencyTracker tracker) where T : struct, IJob {
  431.             if (tracker.forceLinearDependencies) {
  432.                 data.Run();
  433.                 return default;
  434.             } else {
  435.                 var job = data.Schedule(JobDependencyAnalyzer<T>.GetDependencies(ref data, tracker));
  436.                 JobDependencyAnalyzer<T>.Scheduled(ref data, tracker, job);
  437.                 return job;
  438.             }
  439.         }
  440.  
  441.         public static JobHandle ScheduleBatch<T>(this T data, int arrayLength, int minIndicesPerJobCount, JobDependencyTracker tracker, JobHandle additionalDependency = default) where T : struct, IJobParallelForBatched {
  442.             if (tracker.forceLinearDependencies) {
  443.                 additionalDependency.Complete();
  444.                 //data.ScheduleBatch(arrayLength, minIndicesPerJobCount, additionalDependency).Complete();
  445.                 data.RunBatch(arrayLength);
  446.                 return default;
  447.             } else {
  448.                 var job = data.ScheduleBatch(arrayLength, minIndicesPerJobCount, JobDependencyAnalyzer<T>.GetDependencies(ref data, tracker, additionalDependency));
  449.  
  450.                 JobDependencyAnalyzer<T>.Scheduled(ref data, tracker, job);
  451.                 return job;
  452.             }
  453.         }
  454.  
  455.         public static JobHandle ScheduleManaged<T>(this T data, JobHandle dependsOn) where T : struct, IJob {
  456.             return new ManagedJob { handle = GCHandle.Alloc(data) }.Schedule(dependsOn);
  457.         }
  458.  
  459.         public static JobHandle ScheduleManaged (this System.Action data, JobHandle dependsOn) {
  460.             return new ManagedActionJob {
  461.                        handle = GCHandle.Alloc(data)
  462.             }.Schedule(dependsOn);
  463.         }
  464.  
  465.         static readonly UnityEngine.Profiling.CustomSampler waitingForMainThreadSampler = UnityEngine.Profiling.CustomSampler.Create("Waiting for main thread work (sleep)");
  466.  
  467.         /** Schedules a job to run in the main Unity thread.
  468.          *
  469.          * You must call #JobDependencyTracker.RunMainThreadWork() repeatedly in the main thread to allow the work to be done.
  470.          *
  471.          * \warning This method is to be avoided unless absolutely necessary. It may cause a worker thread to sleep until the main thread has had time to run the job, preventing other jobs from running in that worker thread.
  472.          *
  473.          * \note In the future Unity may allow more direct control over the semaphores used in the job system, and then this method may become more efficient.
  474.          */
  475.         public static JobHandle ScheduleManagedInMainThread<T>(this T data, JobDependencyTracker tracker) where T : struct, IJob {
  476.             if (tracker.forceLinearDependencies) {
  477.                 UnityEngine.Profiling.Profiler.BeginSample("Main Thread Work");
  478.                 data.Execute();
  479.                 UnityEngine.Profiling.Profiler.EndSample();
  480.                 return default;
  481.             }
  482.  
  483. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  484.             // Replacing the atomic safety handle because otherwise we will not be able to read/write from native arrays in the job.
  485.             // This is because other jobs may be scheduled that also read/write from them. The JobDependencyTracker ensures
  486.             // that we do not read/write at the same time, but the job system doesn't know that.
  487.             var safetyHandle = AtomicSafetyHandle.Create();
  488.             JobDependencyAnalyzer<T>.SetSafetyHandle(ref data, safetyHandle);
  489. #endif
  490.             var ijob = (IJob)data;
  491.             var dependsOn = JobDependencyAnalyzer<T>.GetDependencies(ref data, tracker);
  492.             var dependenciesDoneEvent = new ManualResetEvent(false);
  493.             var doneEvent = new ManualResetEvent(false);
  494.  
  495.             tracker.mainThreadWork.Add(new JobDependencyTracker.MainThreadWork {
  496.                 dependsOn = dependsOn,
  497.                 dependenciesDoneEvent = dependenciesDoneEvent,
  498.                 doneEvent = doneEvent,
  499.                 job = ijob,
  500. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  501.                 safetyHandle = safetyHandle,
  502. #endif
  503.             });
  504.  
  505.             System.Action waitAction = () => {
  506.                 waitingForMainThreadSampler.Begin();
  507.                 dependenciesDoneEvent.Set();
  508.                 doneEvent.WaitOne();
  509.                 waitingForMainThreadSampler.End();
  510.             };
  511.             var waitJob = waitAction.ScheduleManaged(dependsOn);
  512.  
  513.             JobDependencyAnalyzer<T>.Scheduled(ref data, tracker, waitJob);
  514.             return waitJob;
  515.         }
  516.     }
  517.  
  518.     internal static class JobDependencyAnalyzerAssociated {
  519.         internal static UnityEngine.Profiling.CustomSampler getDependenciesSampler = UnityEngine.Profiling.CustomSampler.Create("GetDependencies");
  520.         internal static UnityEngine.Profiling.CustomSampler iteratingSlotsSampler = UnityEngine.Profiling.CustomSampler.Create("IteratingSlots");
  521.         internal static UnityEngine.Profiling.CustomSampler initSampler = UnityEngine.Profiling.CustomSampler.Create("Init");
  522.         internal static UnityEngine.Profiling.CustomSampler combineSampler = UnityEngine.Profiling.CustomSampler.Create("Combining");
  523.         internal static int[] tempJobDependencyHashes = new int[16];
  524.         internal static int jobCounter = 1;
  525.     }
  526.  
  527.     struct JobDependencyAnalyzer<T> where T : struct {
  528.         static ReflectionData reflectionData;
  529.         static readonly int BufferOffset = UnsafeUtility.GetFieldOffset(typeof(NativeArray<>).GetField("m_Buffer", BindingFlags.Instance | BindingFlags.NonPublic));
  530.         struct ReflectionData {
  531.             public int[] fieldOffsets;
  532.             public bool[] writes;
  533.             public bool[] checkUninitializedRead;
  534.             public string[] fieldNames;
  535.  
  536.             public void Build () {
  537.                 // Find the byte offsets within the struct to all m_Buffer fields in all the native arrays in the struct
  538.                 var fields = new List<int>();
  539.                 var writes = new List<bool>();
  540.                 var reads = new List<bool>();
  541.                 var names = new List<string>();
  542.  
  543.                 Build(typeof(T), fields, writes, reads, names, BufferOffset, false, false, false);
  544.                 this.fieldOffsets = fields.ToArray();
  545.                 this.writes = writes.ToArray();
  546.                 this.fieldNames = names.ToArray();
  547.                 this.checkUninitializedRead = reads.ToArray();
  548.             }
  549.  
  550.             void Build (System.Type type, List<int> fields, List<bool> writes, List<bool> reads, List<string> names, int offset, bool forceReadOnly, bool forceWriteOnly, bool forceDisableUninitializedCheck) {
  551.                 foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) {
  552.                     // Check if this field is a NativeArray
  553.                     if (field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(NativeArray<>)) {
  554.                         fields.Add(offset + UnsafeUtility.GetFieldOffset(field));
  555.                         writes.Add(!forceReadOnly && field.GetCustomAttribute(typeof(ReadOnlyAttribute)) == null);
  556.                         reads.Add(!forceWriteOnly && !forceDisableUninitializedCheck && field.GetCustomAttribute(typeof(WriteOnlyAttribute)) == null && field.GetCustomAttribute(typeof(DisableUninitializedReadCheckAttribute)) == null);
  557.                         names.Add(field.Name);
  558.                     } else if (!field.FieldType.IsPrimitive && field.FieldType.IsValueType && !field.FieldType.IsEnum) {
  559.                         // Recurse to handle nested types
  560.                         bool readOnly = field.GetCustomAttribute(typeof(ReadOnlyAttribute)) != null;
  561.                         bool writeOnly = field.GetCustomAttribute(typeof(WriteOnlyAttribute)) != null;
  562.                         bool disableUninitializedCheck = field.GetCustomAttribute(typeof(DisableUninitializedReadCheckAttribute)) != null;
  563.                         Build(field.FieldType, fields, writes, reads, names, offset + UnsafeUtility.GetFieldOffset(field), readOnly, writeOnly, disableUninitializedCheck);
  564.                     }
  565.                 }
  566.             }
  567.         }
  568.  
  569.         static void initReflectionData () {
  570.             if (reflectionData.fieldOffsets == null) {
  571.                 reflectionData.Build();
  572.             }
  573.         }
  574.  
  575.         static bool HasHash (int[] hashes, int hash, int count) {
  576.             for (int i = 0; i < count; i++) if (hashes[i] == hash) return true;
  577.             return false;
  578.         }
  579.  
  580. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  581.         public static void SetSafetyHandle (ref T data, AtomicSafetyHandle safetyHandle) {
  582.             initReflectionData();
  583.             var offsets = reflectionData.fieldOffsets;
  584.             unsafe {
  585.                 // Note: data is a struct. It is stored on the stack and can thus not be moved by the GC.
  586.                 // Therefore we do not need to pin it first.
  587.                 // It is guaranteed to be stored on the stack since the Schedule method takes the data parameter by value and not by reference.
  588.                 byte* dataPtr = (byte*)UnsafeUtility.AddressOf(ref data);
  589.  
  590.                 for (int i = 0; i < offsets.Length; i++) {
  591.                     // This is the NativeArray<T> field (offsets[i] includes the offset to the field NativeArray<>.m_buffer, so we compensate by subtracting it away again)
  592.                     void* ptr = dataPtr + offsets[i] - BufferOffset;
  593.                     Unity.Collections.LowLevel.Unsafe.NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref System.Runtime.CompilerServices.Unsafe.AsRef<NativeArray<byte> >(ptr), safetyHandle);
  594.                 }
  595.             }
  596.         }
  597. #endif
  598.  
  599.         /** Returns the dependencies for the given job.
  600.          *
  601.          * \param data Job data. Must be allocated on the stack.
  602.          */
  603.         public static JobHandle GetDependencies (ref T data, JobDependencyTracker tracker) {
  604.             return GetDependencies(ref data, tracker, default, false);
  605.         }
  606.  
  607.         public static JobHandle GetDependencies (ref T data, JobDependencyTracker tracker, JobHandle additionalDependency) {
  608.             return GetDependencies(ref data, tracker, additionalDependency, true);
  609.         }
  610.  
  611.         static JobHandle GetDependencies (ref T data, JobDependencyTracker tracker, JobHandle additionalDependency, bool useAdditionalDependency) {
  612.             //JobDependencyAnalyzerAssociated.getDependenciesSampler.Begin();
  613.             //JobDependencyAnalyzerAssociated.initSampler.Begin();
  614.             if (!tracker.dependenciesScratchBuffer.IsCreated) tracker.dependenciesScratchBuffer = new NativeArray<JobHandle>(16, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
  615.             var dependencies = tracker.dependenciesScratchBuffer;
  616.             var slots = tracker.slots;
  617.             var dependencyHashes = JobDependencyAnalyzerAssociated.tempJobDependencyHashes;
  618.  
  619.             int numDependencies = 0;
  620.  
  621.             //JobDependencyAnalyzerAssociated.initSampler.End();
  622.             initReflectionData();
  623.             #if DEBUG_JOBS
  624.             string dependenciesDebug = "";
  625.             #endif
  626.             unsafe {
  627.                 // Note: data is a struct. It is stored on the stack and can thus not be moved by the GC.
  628.                 // Therefore we do not need to pin it first.
  629.                 // It is guaranteed to be stored on the stack since the Schedule method takes the data parameter by value and not by reference.
  630.                 byte* dataPtr = (byte*)UnsafeUtility.AddressOf(ref data);
  631.  
  632.                 var offsets = reflectionData.fieldOffsets;
  633.                 for (int i = 0; i < offsets.Length; i++) {
  634.                     // This is the internal value of the m_Buffer field of the NativeArray
  635.                     void* nativeArrayBufferPtr = *(void**)(dataPtr + offsets[i]);
  636.  
  637.                     // Use the pointer as a hash to uniquely identify a NativeArray
  638.                     var hash = (long)nativeArrayBufferPtr;
  639.  
  640.                     //JobDependencyAnalyzerAssociated.iteratingSlotsSampler.Begin();
  641.                     for (int j = 0; j <= slots.Count; j++) {
  642.                         // No slot found. Add a new one
  643.                         if (j == slots.Count) {
  644.                             slots.Add(new JobDependencyTracker.NativeArraySlot {
  645.                                 hash = hash,
  646.                                 lastWrite = default,
  647.                                 lastReads = ListPool<JobDependencyTracker.JobInstance>.Claim(),
  648.                                 initialized = true, // We don't know anything about the array, so assume it contains initialized data. JobDependencyTracker.NewNativeArray should be used otherwise.
  649.                                 hasWrite = false,
  650.                             });
  651.                         }
  652.  
  653.                         // Check if we know about this NativeArray yet
  654.                         var slot = slots[j];
  655.                         if (slot.hash == hash) {
  656.                             dependenciesDebug += "\nArray ptr: " + hash + " => ";
  657.                             if (reflectionData.checkUninitializedRead[i] && !slot.initialized) {
  658.                                 throw new System.InvalidOperationException("A job tries to read from the native array " + typeof(T).Name + "." + reflectionData.fieldNames[i] + " (ptr=" + hash + ") which contains uninitialized data");
  659.                             }
  660.  
  661.                             if (slot.hasWrite && !HasHash(dependencyHashes, slot.lastWrite.hash, numDependencies)) {
  662.                                 // Reads/writes always depend on the last write to the native array
  663.                                 dependencies[numDependencies] = slot.lastWrite.handle;
  664.                                 dependencyHashes[numDependencies] = slot.lastWrite.hash;
  665.                                 numDependencies++;
  666.                                 if (numDependencies >= dependencies.Length) throw new System.Exception("Too many dependencies for job");
  667.                                 #if DEBUG_JOBS
  668.                                 dependenciesDebug += slot.lastWrite.name;
  669.                                 #endif
  670.                             }
  671.  
  672.                             // If we want to write to the array we additionally depend on all previous reads of the array
  673.                             if (reflectionData.writes[i]) {
  674.                                 for (int q = 0; q < slot.lastReads.Count; q++) {
  675.                                     if (!HasHash(dependencyHashes, slot.lastReads[q].hash, numDependencies)) {
  676.                                         dependencies[numDependencies] = slot.lastReads[q].handle;
  677.                                         dependencyHashes[numDependencies] = slot.lastReads[q].hash;
  678.                                         numDependencies++;
  679.                                         if (numDependencies >= dependencies.Length) throw new System.Exception("Too many dependencies for job");
  680.                                         #if DEBUG_JOBS
  681.                                         dependenciesDebug += slot.lastReads[q].name;
  682.                                         #endif
  683.                                     }
  684.                                 }
  685.                             }
  686.                             break;
  687.                         }
  688.                     }
  689.                     //JobDependencyAnalyzerAssociated.iteratingSlotsSampler.End();
  690.                 }
  691.  
  692.                 if (useAdditionalDependency) {
  693.                     dependencies[numDependencies] = additionalDependency;
  694.                     numDependencies++;
  695.                     #if DEBUG_JOBS
  696.                     dependenciesDebug += "[additional dependency]";
  697.                     #endif
  698.                 }
  699.  
  700.                 #if DEBUG_JOBS
  701.                 UnityEngine.Debug.Log(typeof(T) + " depends on " + dependenciesDebug);
  702.                 #endif
  703.  
  704.                 if (numDependencies == 0) {
  705.                     return default;
  706.                 } else if (numDependencies == 1) {
  707.                     return dependencies[0];
  708.                 } else {
  709.                     //JobDependencyAnalyzerAssociated.combineSampler.Begin();
  710.                     return JobHandle.CombineDependencies(dependencies.Slice(0, numDependencies));
  711.                     //JobDependencyAnalyzerAssociated.combineSampler.End();
  712.                 }
  713.             }
  714.         }
  715.  
  716.         internal static void Scheduled (ref T data, JobDependencyTracker tracker, JobHandle job) {
  717.             unsafe {
  718.                 int jobHash = JobDependencyAnalyzerAssociated.jobCounter++;
  719.                 // Note: data is a struct. It is stored on the stack and can thus not be moved by the GC.
  720.                 // Therefore we do not need to pin it first.
  721.                 // It is guaranteed to be stored on the stack since the Schedule method takes the data parameter by value and not by reference.
  722.                 byte* dataPtr = (byte*)UnsafeUtility.AddressOf(ref data);
  723.                 for (int i = 0; i < reflectionData.fieldOffsets.Length; i++) {
  724.                     // This is the internal value of the m_Buffer field of the NativeArray
  725.                     void* nativeArrayBufferPtr = *(void**)(dataPtr + reflectionData.fieldOffsets[i]);
  726.  
  727.                     // Use the pointer as a hash to uniquely identify a NativeArray
  728.                     var hash = (long)nativeArrayBufferPtr;
  729.                     #if DEBUG_JOBS
  730.                     if (reflectionData.writes[i]) tracker.JobWritesTo(job, hash, jobHash, typeof(T).Name);
  731.                     else tracker.JobReadsFrom(job, hash, jobHash, typeof(T).Name);
  732.                     #else
  733.                     if (reflectionData.writes[i]) tracker.JobWritesTo(job, hash, jobHash);
  734.                     else tracker.JobReadsFrom(job, hash, jobHash);
  735.                     #endif
  736.                 }
  737.             }
  738.         }
  739.     }
  740. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement