Esempio n. 1
0
        public static unsafe JobHandle ScheduleParallelFor(ref JobScheduleParameters parameters, int arrayLength, int innerloopBatchCount)
        {
            UnsafeUtility.AssertHeap(parameters.JobDataPtr.ToPointer());
            UnsafeUtility.AssertHeap(parameters.ReflectionData.ToPointer());
            ReflectionDataProxy jobReflectionData = UnsafeUtility.AsRef <ReflectionDataProxy>(parameters.ReflectionData.ToPointer());

            Assert.IsFalse(jobReflectionData.GenExecuteFunctionPtr.ToPointer() == null);
            Assert.IsFalse(jobReflectionData.GenCleanupFunctionPtr.ToPointer() == null);

            void *      jobMetaPtr  = parameters.JobDataPtr.ToPointer();
            JobMetaData jobMetaData = default;

            jobMetaData.JobRanges.ArrayLength     = arrayLength;
            jobMetaData.JobRanges.IndicesPerPhase = GetDefaultIndicesPerPhase(arrayLength);
            UnsafeUtility.CopyStructureToPtr(ref jobMetaData, jobMetaPtr);

#if UNITY_SINGLETHREADED_JOBS
            // In the single threaded case, this is synchronous execution.
            UnsafeUtility.CallFunctionPtr_pi(jobReflectionData.GenExecuteFunctionPtr.ToPointer(), jobMetaPtr, 0);
            UnsafeUtility.CallFunctionPtr_p(jobReflectionData.GenCleanupFunctionPtr.ToPointer(), jobMetaPtr);

            // This checks that the generated code was actually called; the last responsibility of
            // the generated code is to clean up the memory. Unfortunately only works in single threaded mode,
            Assert.IsTrue(UnsafeUtility.GetLastFreePtr() == jobMetaPtr);
            return(new JobHandle());
#else
            return(ScheduleJobParallelFor(jobReflectionData.GenExecuteFunctionPtr, jobReflectionData.GenCleanupFunctionPtr,
                                          parameters.JobDataPtr, arrayLength, innerloopBatchCount, parameters.Dependency));
#endif
        }
Esempio n. 2
0
        public static unsafe IntPtr CreateJobReflectionData(Type type, Type _, JobType jobType,
                                                            Delegate executeDelegate,
                                                            Delegate cleanupDelegate = null,
                                                            ManagedJobForEachDelegate codegenExecuteDelegate = null, // Note that the 2 param form is used for both Normal and ParallelFor
                                                            ManagedJobDelegate codegenCleanupDelegate        = null)
        {
            // Tiny doesn't use this on any codepath currently; may need future support for custom jobs.
            if (cleanupDelegate != null)
            {
                throw new ArgumentException("Runtime needs support for cleanup delegates in jobs.");
            }

            if (codegenExecuteDelegate == null)
            {
                throw new CodegenShouldReplaceException("Code gen should have supplied an execute wrapper.");
            }
            if (jobType == JobType.ParallelFor)
            {
                if (codegenCleanupDelegate == null)
                {
                    throw new CodegenShouldReplaceException("For ParallelFor jobs, code gen should have supplied a cleanup wrapper.");
                }
            }

            var reflectionDataPtr = UnsafeUtility.Malloc(UnsafeUtility.SizeOf <ReflectionDataProxy>(),
                                                         UnsafeUtility.AlignOf <ReflectionDataProxy>(), Allocator.Persistent);

            var reflectionData = new ReflectionDataProxy();

            reflectionData.JobType = jobType;

            // Protect against garbage collector relocating delegate
            ReflectionDataStore store = new ReflectionDataStore(executeDelegate, codegenCleanupDelegate, codegenExecuteDelegate);

            store.next = reflectionDataStoreRoot;
            reflectionDataStoreRoot = store;

            reflectionData.GenExecuteFunctionPtr = Marshal.GetFunctionPointerForDelegate(codegenExecuteDelegate);
            if (codegenCleanupDelegate != null)
            {
                reflectionData.GenCleanupFunctionPtr = Marshal.GetFunctionPointerForDelegate(codegenCleanupDelegate);
            }

            UnsafeUtility.CopyStructureToPtr(ref reflectionData, reflectionDataPtr);

            return(new IntPtr(reflectionDataPtr));
        }
Esempio n. 3
0
        public static unsafe IntPtr CreateJobReflectionData(Type type, Type _,
                                                            Delegate executeDelegate,
                                                            Delegate cleanupDelegate = null,
                                                            ManagedJobForEachDelegate codegenExecuteDelegate = null,
                                                            ManagedJobDelegate codegenCleanupDelegate        = null,
                                                            int codegenUnmanagedJobSize = -1,
                                                            ManagedJobMarshalDelegate codegenMarshalToBurstDelegate = null)
        {
            // Tiny doesn't use this on any codepath currently; may need future support for custom jobs.
            Assert.IsTrue(cleanupDelegate == null, "Runtime needs support for cleanup delegates in jobs.");

            Assert.IsTrue(codegenExecuteDelegate != null, "Code gen should have supplied an execute wrapper.");
#if ENABLE_UNITY_COLLECTIONS_CHECKS && !UNITY_DOTSRUNTIME_IL2CPP
            Assert.IsTrue((codegenUnmanagedJobSize != -1 && codegenMarshalToBurstDelegate != null) || (codegenUnmanagedJobSize == -1 && codegenMarshalToBurstDelegate == null), "Code gen should have supplied a marshal wrapper.");
#endif

            ReflectionDataProxy *reflectionDataPtr = (ReflectionDataProxy *)UnsafeUtility.Malloc(UnsafeUtility.SizeOf <ReflectionDataProxy>(),
                                                                                                 UnsafeUtility.AlignOf <ReflectionDataProxy>(), Allocator.Persistent);

            var reflectionData = new ReflectionDataProxy();

            // Protect against garbage collector relocating delegate
            ReflectionDataStore store = new ReflectionDataStore(executeDelegate, codegenCleanupDelegate, codegenExecuteDelegate, codegenMarshalToBurstDelegate);
            store.next = Managed.reflectionDataStoreRoot;
            Managed.reflectionDataStoreRoot = store;

            reflectionData.ExecuteFunctionPtr = store.CodeGenExecuteFunctionPtr;
            if (codegenCleanupDelegate != null)
            {
                reflectionData.CleanupFunctionPtr = store.CodeGenCleanupFunctionPtr;
            }

#if ENABLE_UNITY_COLLECTIONS_CHECKS && !UNITY_DOTSRUNTIME_IL2CPP
            reflectionData.UnmanagedSize = codegenUnmanagedJobSize;
            if (codegenUnmanagedJobSize != -1)
            {
                reflectionData.MarshalToBurstFunctionPtr = store.CodeGenMarshalToBurstFunctionPtr;
            }
#endif

            UnsafeUtility.CopyStructureToPtr(ref reflectionData, reflectionDataPtr);

            return((IntPtr)reflectionDataPtr);
        }
Esempio n. 4
0
        public static unsafe JobHandle Schedule(ref JobScheduleParameters parameters)
        {
            // Heap memory must be passed to schedule, so that Cleanup can free() it.
            UnsafeUtility.AssertHeap(parameters.JobDataPtr.ToPointer());
            UnsafeUtility.AssertHeap(parameters.ReflectionData.ToPointer());
            ReflectionDataProxy jobReflectionData = UnsafeUtility.AsRef <ReflectionDataProxy>(parameters.ReflectionData.ToPointer());

            Assert.IsFalse(jobReflectionData.GenExecuteFunctionPtr.ToPointer() == null);
            Assert.IsTrue(jobReflectionData.GenCleanupFunctionPtr.ToPointer() == null);

            void *jobMetaPtr = parameters.JobDataPtr.ToPointer();

#if UNITY_SINGLETHREADED_JOBS
            // In the single threaded case, this is synchronous execution.
            UnsafeUtility.CallFunctionPtr_pi(jobReflectionData.GenExecuteFunctionPtr.ToPointer(), jobMetaPtr, 0);

            // This checks that the generated code was actually called; the last responsibility of
            // the generated code is to clean up the memory. Unfortunately only works in single threaded mode,
            Assert.IsTrue(UnsafeUtility.GetLastFreePtr() == jobMetaPtr);
            return(new JobHandle());
#else
            return(ScheduleJob(jobReflectionData.GenExecuteFunctionPtr, parameters.JobDataPtr, parameters.Dependency));
#endif
        }
Esempio n. 5
0
        public static unsafe JobHandle Schedule(ref JobScheduleParameters parameters)
        {
            // Ensure the user has not set the schedule mode to a currently unsupported type
            Assert.IsTrue(parameters.ScheduleMode != ScheduleMode.Single);

            // Heap memory must be passed to schedule, so that Cleanup can free() it.
            UnsafeUtility.AssertHeap(parameters.JobDataPtr);
            UnsafeUtility.AssertHeap(parameters.ReflectionData);
            ReflectionDataProxy jobReflectionData = UnsafeUtility.AsRef <ReflectionDataProxy>(parameters.ReflectionData);

            Assert.IsTrue(jobReflectionData.ExecuteFunctionPtr.ToPointer() != null);
            Assert.IsTrue(jobReflectionData.CleanupFunctionPtr.ToPointer() != null);

#if ENABLE_UNITY_COLLECTIONS_CHECKS && !UNITY_DOTSRUNTIME_IL2CPP
            Assert.IsTrue((jobReflectionData.UnmanagedSize != -1 && jobReflectionData.MarshalToBurstFunctionPtr != IntPtr.Zero) ||
                          (jobReflectionData.UnmanagedSize == -1 && jobReflectionData.MarshalToBurstFunctionPtr == IntPtr.Zero));
#endif

            JobMetaData *managedJobDataPtr = parameters.JobDataPtr;
            JobMetaData  jobMetaData;

            Assert.IsTrue(sizeof(JobRanges) <= JobMetaData.kJobMetaDataIsParallelOffset);
            UnsafeUtility.CopyPtrToStructure(managedJobDataPtr, out jobMetaData);
            Assert.IsTrue(jobMetaData.jobDataSize > 0); // set by JobScheduleParameters
            jobMetaData.managedPtr    = managedJobDataPtr;
            jobMetaData.isParallelFor = 0;
            UnsafeUtility.CopyStructureToPtr(ref jobMetaData, managedJobDataPtr);

            JobHandle jobHandle = default;
#if !UNITY_SINGLETHREADED_JOBS
            bool runSingleThreadSynchronous =
                parameters.ScheduleMode == ScheduleMode.RunOnMainThread ||
                parameters.ScheduleMode == ScheduleMode.Run ||
                parameters.ScheduleMode == ScheduleMode.ScheduleOnMainThread;
#else
            bool runSingleThreadSynchronous = true;
#endif

            if (runSingleThreadSynchronous)
            {
                bool syncNow = parameters.ScheduleMode == ScheduleMode.Run || parameters.ScheduleMode == ScheduleMode.RunOnMainThread;

#if UNITY_SINGLETHREADED_JOBS
                if (!syncNow)
                {
                    jobHandle.JobGroup = GetFakeJobGroupId();
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                    DebugDidScheduleJob(ref jobHandle, (JobHandle *)UnsafeUtility.AddressOf(ref parameters.Dependency), 1);
#endif
                }
#endif

                parameters.Dependency.Complete();
                UnsafeUtility.SetInJob(1);
                try
                {
                    // We assume there are no non-blittable fields in a bursted job (i.e. DisposeSentinel) if
                    // collections checks are not enabled
#if ENABLE_UNITY_COLLECTIONS_CHECKS && !UNITY_DOTSRUNTIME_IL2CPP
                    // If the job was bursted, and the job structure contained non-blittable fields, the UnmanagedSize will
                    // be something other than -1 meaning we need to marshal the managed representation before calling the ExecuteFn
                    if (jobReflectionData.UnmanagedSize != -1)
                    {
                        JobMetaData *unmanagedJobData = AllocateJobHeapMemory(jobReflectionData.UnmanagedSize, 1);

                        void *dst = (byte *)unmanagedJobData + sizeof(JobMetaData);
                        void *src = (byte *)managedJobDataPtr + sizeof(JobMetaData);

                        UnsafeUtility.EnterTempScope();
                        try
                        {
                            UnsafeUtility.CallFunctionPtr_pp(jobReflectionData.MarshalToBurstFunctionPtr.ToPointer(), dst, src);

                            // In the single threaded case, this is synchronous execution.
                            // The cleanup *is* bursted, so pass in the unmanangedJobDataPtr
                            CopyMetaDataToJobData(ref jobMetaData, managedJobDataPtr, unmanagedJobData);

                            UnsafeUtility.CallFunctionPtr_pi(jobReflectionData.ExecuteFunctionPtr.ToPointer(), unmanagedJobData, k_MainThreadWorkerIndex);
                        }
                        finally
                        {
                            UnsafeUtility.ExitTempScope();
                        }
                    }
                    else
#endif
                    {
                        CopyMetaDataToJobData(ref jobMetaData, managedJobDataPtr, null);

                        // In the single threaded case, this is synchronous execution.
                        UnsafeUtility.EnterTempScope();
                        try
                        {
                            UnsafeUtility.CallFunctionPtr_pi(jobReflectionData.ExecuteFunctionPtr.ToPointer(), managedJobDataPtr, k_MainThreadWorkerIndex);
                        }
                        finally
                        {
                            UnsafeUtility.ExitTempScope();
                        }
                    }
                }
                finally
                {
                    UnsafeUtility.SetInJob(0);
                }

                return(jobHandle);
            }
#if !UNITY_SINGLETHREADED_JOBS
#if ENABLE_UNITY_COLLECTIONS_CHECKS && !UNITY_DOTSRUNTIME_IL2CPP
            // If the job was bursted, and the job structure contained non-blittable fields, the UnmanagedSize will
            // be something other than -1 meaning we need to marshal the managed representation before calling the ExecuteFn.
            // This time though, we have a whole bunch of jobs that need to be processed.
            if (jobReflectionData.UnmanagedSize != -1)
            {
                JobMetaData *unmanagedJobData = AllocateJobHeapMemory(jobReflectionData.UnmanagedSize, 1);

                void *dst = (byte *)unmanagedJobData + sizeof(JobMetaData);
                void *src = (byte *)managedJobDataPtr + sizeof(JobMetaData);
                UnsafeUtility.CallFunctionPtr_pp(jobReflectionData.MarshalToBurstFunctionPtr.ToPointer(), dst, src);

                CopyMetaDataToJobData(ref jobMetaData, managedJobDataPtr, unmanagedJobData);
                jobHandle = ScheduleJob(jobReflectionData.ExecuteFunctionPtr, unmanagedJobData, parameters.Dependency);
            }
            else
#endif
            {
                CopyMetaDataToJobData(ref jobMetaData, managedJobDataPtr, null);
                jobHandle = ScheduleJob(jobReflectionData.ExecuteFunctionPtr, parameters.JobDataPtr, parameters.Dependency);
            }
#endif
            return(jobHandle);
        }
Esempio n. 6
0
        static unsafe JobHandle ScheduleParallelForInternal(ref JobScheduleParameters parameters, int arrayLength, void *deferredDataPtr, int innerloopBatchCount)
        {
            // Ensure the user has not set the schedule mode to a currently unsupported type
            Assert.IsTrue(parameters.ScheduleMode != ScheduleMode.Single);

            // May provide an arrayLength (>=0) OR a deferredDataPtr, but both is senseless.
            Assert.IsTrue((arrayLength >= 0 && deferredDataPtr == null) || (arrayLength < 0 && deferredDataPtr != null));

            UnsafeUtility.AssertHeap(parameters.JobDataPtr);
            UnsafeUtility.AssertHeap(parameters.ReflectionData);
            ReflectionDataProxy jobReflectionData = UnsafeUtility.AsRef <ReflectionDataProxy>(parameters.ReflectionData);

            Assert.IsFalse(jobReflectionData.ExecuteFunctionPtr.ToPointer() == null);
            Assert.IsFalse(jobReflectionData.CleanupFunctionPtr.ToPointer() == null);
#if ENABLE_UNITY_COLLECTIONS_CHECKS && !UNITY_DOTSRUNTIME_IL2CPP
            Assert.IsTrue((jobReflectionData.UnmanagedSize != -1 && jobReflectionData.MarshalToBurstFunctionPtr != IntPtr.Zero) ||
                          (jobReflectionData.UnmanagedSize == -1 && jobReflectionData.MarshalToBurstFunctionPtr == IntPtr.Zero));
#endif
            JobMetaData *managedJobDataPtr = parameters.JobDataPtr;
            JobMetaData  jobMetaData;

            UnsafeUtility.CopyPtrToStructure(parameters.JobDataPtr, out jobMetaData);
            Assert.IsTrue(jobMetaData.jobDataSize > 0); // set by JobScheduleParameters
            Assert.IsTrue(sizeof(JobRanges) <= JobMetaData.kJobMetaDataIsParallelOffset);
            jobMetaData.JobRanges.ArrayLength     = (arrayLength >= 0) ? arrayLength : 0;
            jobMetaData.JobRanges.IndicesPerPhase = (arrayLength >= 0) ? GetDefaultIndicesPerPhase(arrayLength) : 1; // TODO indicesPerPhase isn't actually used, except as a flag.
            // If this is set to -1 by codegen, that indicates an error if we schedule the job as parallel for because
            // it potentially consists of write operations which are not parallel compatible
            if (jobMetaData.isParallelFor == -1)
            {
                throw new InvalidOperationException("Parallel writing not supported in this job. Parallel scheduling invalid.");
            }
            jobMetaData.isParallelFor   = 1;
            jobMetaData.deferredDataPtr = deferredDataPtr;

            JobHandle jobHandle = default;
#if !UNITY_SINGLETHREADED_JOBS
            bool runSingleThreadSynchronous =
                parameters.ScheduleMode == ScheduleMode.RunOnMainThread ||
                parameters.ScheduleMode == ScheduleMode.ScheduleOnMainThread;
#else
            bool runSingleThreadSynchronous = true;
#endif

            jobMetaData.JobRanges.runOnMainThread = runSingleThreadSynchronous ? 1 : 0;

            if (runSingleThreadSynchronous)
            {
                bool syncNow = parameters.ScheduleMode == ScheduleMode.Run || parameters.ScheduleMode == ScheduleMode.RunOnMainThread;
#if UNITY_SINGLETHREADED_JOBS
                // Nativejobs needs further support in creating a JobHandle not linked to an actual job in order to support this correctly
                // in multithreaded builds
                if (!syncNow)
                {
                    jobHandle.JobGroup = GetFakeJobGroupId();
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                    DebugDidScheduleJob(ref jobHandle, (JobHandle *)UnsafeUtility.AddressOf(ref parameters.Dependency), 1);
#endif
                }
#endif

                parameters.Dependency.Complete();
                UnsafeUtility.SetInJob(1);
                try
                {
                    // We assume there are no non-blittable fields in a bursted job (i.e. DisposeSentinel) if
                    // collections checks are not enabled
#if ENABLE_UNITY_COLLECTIONS_CHECKS && !UNITY_DOTSRUNTIME_IL2CPP
                    // If the job was bursted, and the job structure contained non-blittable fields, the UnmanagedSize will
                    // be something other than -1 meaning we need to marshal the managed representation before calling the ExecuteFn
                    if (jobReflectionData.UnmanagedSize != -1)
                    {
                        JobMetaData *unmanagedJobData = AllocateJobHeapMemory(jobReflectionData.UnmanagedSize, 1);

                        void *dst = (byte *)unmanagedJobData + sizeof(JobMetaData);
                        void *src = (byte *)managedJobDataPtr + sizeof(JobMetaData);

                        // In the single threaded case, this is synchronous execution.
                        UnsafeUtility.EnterTempScope();
                        try
                        {
                            UnsafeUtility.CallFunctionPtr_pp(jobReflectionData.MarshalToBurstFunctionPtr.ToPointer(), dst, src);

                            CopyMetaDataToJobData(ref jobMetaData, managedJobDataPtr, unmanagedJobData);

                            UnsafeUtility.CallFunctionPtr_pi(jobReflectionData.ExecuteFunctionPtr.ToPointer(), unmanagedJobData, k_MainThreadWorkerIndex);
                            UnsafeUtility.CallFunctionPtr_p(jobReflectionData.CleanupFunctionPtr.ToPointer(), unmanagedJobData);
                        }
                        finally
                        {
                            UnsafeUtility.ExitTempScope();
                        }
                    }
                    else
#endif
                    {
                        CopyMetaDataToJobData(ref jobMetaData, managedJobDataPtr, null);

                        // In the single threaded case, this is synchronous execution.
                        UnsafeUtility.EnterTempScope();
                        try
                        {
                            UnsafeUtility.CallFunctionPtr_pi(jobReflectionData.ExecuteFunctionPtr.ToPointer(), managedJobDataPtr, k_MainThreadWorkerIndex);
                            UnsafeUtility.CallFunctionPtr_p(jobReflectionData.CleanupFunctionPtr.ToPointer(), managedJobDataPtr);
                        }
                        finally
                        {
                            UnsafeUtility.ExitTempScope();
                        }
                    }
                }
                finally
                {
                    UnsafeUtility.SetInJob(0);
                }

                return(jobHandle);
            }
#if !UNITY_SINGLETHREADED_JOBS
#if ENABLE_UNITY_COLLECTIONS_CHECKS && !UNITY_DOTSRUNTIME_IL2CPP
            // If the job was bursted, and the job structure contained non-blittable fields, the UnmanagedSize will
            // be something other than -1 meaning we need to marshal the managed representation before calling the ExecuteFn
            if (jobReflectionData.UnmanagedSize != -1)
            {
                int          nWorker          = JobWorkerCount > 1 ? JobWorkerCount : 1;
                JobMetaData *unmanagedJobData = AllocateJobHeapMemory(jobReflectionData.UnmanagedSize, nWorker);

                for (int i = 0; i < nWorker; i++)
                {
                    void *dst = (byte *)unmanagedJobData + sizeof(JobMetaData) + i * jobReflectionData.UnmanagedSize;
                    void *src = (byte *)managedJobDataPtr + sizeof(JobMetaData) + i * jobMetaData.jobDataSize;
                    UnsafeUtility.CallFunctionPtr_pp(jobReflectionData.MarshalToBurstFunctionPtr.ToPointer(), dst, src);
                }

                // Need to change the jobDataSize so the job will have the correct stride when finding
                // the correct jobData for a thread.
                JobMetaData unmanagedJobMetaData = jobMetaData;
                unmanagedJobMetaData.jobDataSize = jobReflectionData.UnmanagedSize;
                CopyMetaDataToJobData(ref unmanagedJobMetaData, managedJobDataPtr, unmanagedJobData);

                jobHandle = ScheduleJobParallelFor(jobReflectionData.ExecuteFunctionPtr,
                                                   jobReflectionData.CleanupFunctionPtr, unmanagedJobData, arrayLength,
                                                   innerloopBatchCount, parameters.Dependency);
            }
            else
#endif
            {
                CopyMetaDataToJobData(ref jobMetaData, managedJobDataPtr, null);
                jobHandle = ScheduleJobParallelFor(jobReflectionData.ExecuteFunctionPtr,
                                                   jobReflectionData.CleanupFunctionPtr, parameters.JobDataPtr, arrayLength,
                                                   innerloopBatchCount, parameters.Dependency);
            }

            if (parameters.ScheduleMode == ScheduleMode.Run)
            {
                jobHandle.Complete();
            }
#endif
            return(jobHandle);
        }