public static void Execute( ref JobEntityBatchWrapper <T> jobWrapper, IntPtr additionalPtr, IntPtr bufferRangePatchData, ref JobRanges ranges, int jobIndex) { ExecuteInternal(ref jobWrapper, ref ranges, jobIndex); }
internal static unsafe JobHandle ScheduleInternal <T>( ref T jobData, EntityQuery query, JobHandle dependsOn, ScheduleMode mode, int batchesPerChunk, bool isParallel = true) where T : struct, IJobEntityBatch { var queryImpl = query._GetImpl(); var queryData = queryImpl->_QueryData; var cachedChunks = queryData->GetMatchingChunkCache(); // Don't schedule the job if there are no chunks to work on var chunkCount = cachedChunks.Length; JobEntityBatchWrapper <T> jobEntityBatchWrapper = new JobEntityBatchWrapper <T> { #if ENABLE_UNITY_COLLECTIONS_CHECKS // All IJobEntityBatch jobs have a EntityManager safety handle to ensure that BeforeStructuralChange throws an error if // jobs without any other safety handles are still running (haven't been synced). safety = new EntitySafetyHandle { m_Safety = queryImpl->SafetyHandles->GetEntityManagerSafetyHandle() }, #endif MatchingArchetypes = queryData->MatchingArchetypes, CachedChunks = cachedChunks, Filter = queryImpl->_Filter, JobData = jobData, JobsPerChunk = batchesPerChunk, IsParallel = isParallel ? 1 : 0 }; var scheduleParams = new JobsUtility.JobScheduleParameters( UnsafeUtility.AddressOf(ref jobEntityBatchWrapper), isParallel ? JobEntityBatchProducer <T> .InitializeParallel() : JobEntityBatchProducer <T> .InitializeSingle(), dependsOn, mode); if (!isParallel) { return(JobsUtility.Schedule(ref scheduleParams)); } else { return(JobsUtility.ScheduleParallelFor(ref scheduleParams, chunkCount * batchesPerChunk, 1)); } }
internal unsafe static void ExecuteInternal( ref JobEntityBatchWrapper <T> jobWrapper, ref JobRanges ranges, int jobIndex) { var chunks = jobWrapper.CachedChunks; bool isParallel = jobWrapper.IsParallel == 1; bool isFiltering = jobWrapper.Filter.RequiresMatchesFilter; while (true) { int beginBatchIndex = 0; int endBatchIndex = chunks.Length; // If we are running the job in parallel, steal some work. if (isParallel) { // If we have no range to steal, exit the loop. if (!JobsUtility.GetWorkStealingRange(ref ranges, jobIndex, out beginBatchIndex, out endBatchIndex)) { break; } } // Do the actual user work. for (int batchIndex = beginBatchIndex; batchIndex < endBatchIndex; ++batchIndex) { var chunkIndex = batchIndex / jobWrapper.JobsPerChunk; var batchIndexInChunk = batchIndex % jobWrapper.JobsPerChunk; var chunk = chunks.Ptr[chunkIndex]; if (isFiltering && chunk->MatchesFilter(jobWrapper.MatchingArchetypes.Ptr[chunks.PerChunkMatchingArchetypeIndex.Ptr[chunkIndex]], ref jobWrapper.Filter)) { continue; } jobWrapper.JobData.Execute(ArchetypeChunk.EntityBatchFromChunk(chunk, jobWrapper.JobsPerChunk, batchIndexInChunk, chunks.EntityComponentStore), batchIndex); } // If we are not running in parallel, our job is done. if (!isParallel) { break; } } }
internal unsafe static void ExecuteInternal( ref JobEntityBatchWrapper <T> jobWrapper, ref JobRanges ranges, int jobIndex) { var batches = jobWrapper.Batches; bool isParallel = jobWrapper.IsParallel == 1; while (true) { int beginBatchIndex = 0; int endBatchIndex = batches.Length; // If we are running the job in parallel, steal some work. if (isParallel) { // If we have no range to steal, exit the loop. if (!JobsUtility.GetWorkStealingRange(ref ranges, jobIndex, out beginBatchIndex, out endBatchIndex)) { break; } } // Do the actual user work. for (int batchIndex = beginBatchIndex; batchIndex < endBatchIndex; ++batchIndex) { jobWrapper.JobData.Execute(batches[batchIndex], batchIndex); } // If we are not running in parallel, our job is done. if (!isParallel) { break; } } }
internal static unsafe JobHandle ScheduleInternal <T>( ref T jobData, EntityQuery query, JobHandle dependsOn, ScheduleMode mode, int batchesPerChunk, bool isParallel = true) where T : struct, IJobEntityBatch { var queryImpl = query._GetImpl(); var filteredChunkCount = queryImpl->CalculateChunkCount(); var batches = new NativeArray <ArchetypeChunk>(filteredChunkCount * batchesPerChunk, Allocator.TempJob); var prefilterHandle = new PrefilterForJobEntityBatch { MatchingArchetypes = queryImpl->_QueryData->MatchingArchetypes, Filter = queryImpl->_Filter, BatchesPerChunk = batchesPerChunk, EntityComponentStore = queryImpl->_Access->EntityComponentStore, Batches = batches }.Schedule(dependsOn); JobEntityBatchWrapper <T> jobEntityBatchWrapper = new JobEntityBatchWrapper <T> { #if ENABLE_UNITY_COLLECTIONS_CHECKS // All IJobEntityBatch jobs have a EntityManager safety handle to ensure that BeforeStructuralChange throws an error if // jobs without any other safety handles are still running (haven't been synced). safety = new EntitySafetyHandle { m_Safety = queryImpl->SafetyHandles->GetEntityManagerSafetyHandle() }, #endif JobData = jobData, Batches = batches, JobsPerChunk = batchesPerChunk, IsParallel = isParallel ? 1 : 0 }; var scheduleParams = new JobsUtility.JobScheduleParameters( UnsafeUtility.AddressOf(ref jobEntityBatchWrapper), isParallel ? JobEntityBatchProducer <T> .InitializeParallel() : JobEntityBatchProducer <T> .InitializeSingle(), prefilterHandle, mode); #if ENABLE_UNITY_COLLECTIONS_CHECKS try { #endif if (!isParallel) { return(JobsUtility.Schedule(ref scheduleParams)); } else { return(JobsUtility.ScheduleParallelFor(ref scheduleParams, filteredChunkCount * batchesPerChunk, 1)); } #if ENABLE_UNITY_COLLECTIONS_CHECKS } catch (InvalidOperationException e) { prefilterHandle.Complete(); batches.Dispose(); throw e; } #endif }
internal unsafe static void ExecuteInternal( ref JobEntityBatchWrapper <T> jobWrapper, IntPtr bufferRangePatchData, ref JobRanges ranges, int jobIndex) { var chunks = jobWrapper.CachedChunks; var prebuiltBatches = (ArchetypeChunk *)jobWrapper.PrebuiltBatchList.Ptr; bool isParallel = jobWrapper.IsParallel == 1; bool isFiltering = jobWrapper.Filter.RequiresMatchesFilter; while (true) { int beginBatchIndex = 0; int endBatchIndex = jobWrapper.UsePrebuiltBatchList == 1 ? jobWrapper.PrebuiltBatchList.Length : chunks.Length; // If we are running the job in parallel, steal some work. if (isParallel) { // If we have no range to steal, exit the loop. if (!JobsUtility.GetWorkStealingRange(ref ranges, jobIndex, out beginBatchIndex, out endBatchIndex)) { break; } JobsUtility.PatchBufferMinMaxRanges(bufferRangePatchData, UnsafeUtility.AddressOf(ref jobWrapper), 0, 0); } // Do the actual user work. if (jobWrapper.UsePrebuiltBatchList == 1) { for (int batchIndex = beginBatchIndex; batchIndex < endBatchIndex; ++batchIndex) { var batch = prebuiltBatches[batchIndex]; if (isFiltering && !batch.m_Chunk->MatchesFilter(jobWrapper.MatchingArchetypes.Ptr[jobWrapper.PrebuiltBatchListMatchingArchetypeIndices.Ptr[batchIndex]], ref jobWrapper.Filter)) { continue; } Assert.AreNotEqual(0, batch.Count); jobWrapper.JobData.Execute(batch, batchIndex); } } else { if (jobWrapper.JobsPerChunk == 1) { // 1 batch per chunk, with/without filtering for (int batchIndex = beginBatchIndex; batchIndex < endBatchIndex; ++batchIndex) { var chunkIndex = batchIndex; var chunk = chunks.Ptr[chunkIndex]; if (isFiltering && !chunk->MatchesFilter(jobWrapper.MatchingArchetypes.Ptr[chunks.PerChunkMatchingArchetypeIndex.Ptr[chunkIndex]], ref jobWrapper.Filter)) { continue; } var batch = new ArchetypeChunk(chunk, chunks.EntityComponentStore); Assert.AreNotEqual(0, batch.Count); jobWrapper.JobData.Execute(batch, batchIndex); } } else { // 2+ batches per chunk, with/without filtering // This is the most general case; if only one code path survives, it should be this one. for (int batchIndex = beginBatchIndex; batchIndex < endBatchIndex; ++batchIndex) { var chunkIndex = batchIndex / jobWrapper.JobsPerChunk; var batchIndexInChunk = batchIndex % jobWrapper.JobsPerChunk; var chunk = chunks.Ptr[chunkIndex]; if (isFiltering && !chunk->MatchesFilter( jobWrapper.MatchingArchetypes.Ptr[ chunks.PerChunkMatchingArchetypeIndex.Ptr[chunkIndex]], ref jobWrapper.Filter)) { continue; } if (ArchetypeChunk.EntityBatchFromChunk(chunk, chunk->Count, jobWrapper.JobsPerChunk, batchIndexInChunk, chunks.EntityComponentStore, out var batch)) { jobWrapper.JobData.Execute(batch, batchIndex); } } } } // If we are not running in parallel, our job is done. if (!isParallel) { break; } } }
internal static unsafe JobHandle ScheduleInternal <T>( ref T jobData, EntityQuery query, JobHandle dependsOn, ScheduleMode mode, int batchesPerChunk, bool isParallel = true, NativeArray <Entity> limitToEntityArray = default(NativeArray <Entity>)) where T : struct, IJobEntityBatch { var queryImpl = query._GetImpl(); var queryData = queryImpl->_QueryData; var cachedChunks = queryData->GetMatchingChunkCache(); // Don't schedule the job if there are no chunks to work on var chunkCount = cachedChunks.Length; var useEntityArray = limitToEntityArray.IsCreated; var prebuiltBatchList = default(UnsafeList); var perBatchMatchingArchetypeIndex = default(UnsafeIntList); var batchCount = chunkCount * batchesPerChunk; if (useEntityArray) { prebuiltBatchList = new UnsafeList(Allocator.TempJob); perBatchMatchingArchetypeIndex = new UnsafeIntList(0, Allocator.TempJob); // Forces the creation of an EntityQueryMask, which is necessary to filter batches. var access = queryImpl->_Access; access->EntityQueryManager->GetEntityQueryMask(queryData, access->EntityComponentStore); ChunkIterationUtility.FindBatchesForEntityArrayWithQuery( queryImpl->_Access->EntityComponentStore, queryData, ref queryImpl->_Filter, (Entity *)limitToEntityArray.GetUnsafePtr(), limitToEntityArray.Length, ref prebuiltBatchList, ref perBatchMatchingArchetypeIndex); batchCount = prebuiltBatchList.Length; } JobEntityBatchWrapper <T> jobEntityBatchWrapper = new JobEntityBatchWrapper <T> { #if ENABLE_UNITY_COLLECTIONS_CHECKS // All IJobEntityBatch jobs have a EntityManager safety handle to ensure that BeforeStructuralChange throws an error if // jobs without any other safety handles are still running (haven't been synced). safety = new EntitySafetyHandle { m_Safety = queryImpl->SafetyHandles->GetEntityManagerSafetyHandle() }, #endif MatchingArchetypes = queryData->MatchingArchetypes, CachedChunks = cachedChunks, Filter = queryImpl->_Filter, JobData = jobData, JobsPerChunk = batchesPerChunk, IsParallel = isParallel ? 1 : 0, UsePrebuiltBatchList = useEntityArray ? 1: 0, PrebuiltBatchList = prebuiltBatchList, PrebuiltBatchListMatchingArchetypeIndices = perBatchMatchingArchetypeIndex }; var scheduleParams = new JobsUtility.JobScheduleParameters( UnsafeUtility.AddressOf(ref jobEntityBatchWrapper), isParallel ? JobEntityBatchProducer <T> .InitializeParallel() : JobEntityBatchProducer <T> .InitializeSingle(), dependsOn, mode); var result = default(JobHandle); if (!isParallel) { result = JobsUtility.Schedule(ref scheduleParams); } else { result = JobsUtility.ScheduleParallelFor(ref scheduleParams, batchCount, 1); } if (useEntityArray) { result = prebuiltBatchList.Dispose(result); result = perBatchMatchingArchetypeIndex.Dispose(result); } return(result); }