public void AddComponentWithValidation(UnsafeMatchingArchetypePtrList archetypeList, EntityQueryFilter filter, ComponentType componentType, ComponentDependencyManager *dependencyManager) { AssertCanAddComponent(archetypeList, componentType); var chunks = ChunkIterationUtility.CreateArchetypeChunkArray(archetypeList, Collections.Allocator.TempJob, ref filter, dependencyManager); if (chunks.Length > 0) { //@TODO the fast path for a chunk that contains a single entity is only possible if the chunk doesn't have a Locked Entity Order //but we should still be allowed to add zero sized components to chunks with a Locked Entity Order, even ones that only contain a single entity /* * if ((chunks.Length == 1) && (chunks[0].Count == 1)) * { * var entityPtr = (Entity*) chunks[0].m_Chunk->Buffer; * StructuralChange.AddComponentEntity(EntityComponentStore, entityPtr, componentType.TypeIndex); * } * else * { */ AddComponent((ArchetypeChunk *)NativeArrayUnsafeUtility.GetUnsafePtr(chunks), chunks.Length, componentType); /* * } */ } chunks.Dispose(); }
public void RemoveComponentWithValidation(UnsafeMatchingArchetypePtrList archetypeList, EntityQueryFilter filter, ComponentType componentType, ComponentDependencyManager *dependencyManager) { var chunks = ChunkIterationUtility.CreateArchetypeChunkArray(archetypeList, Collections.Allocator.TempJob, ref filter, dependencyManager); RemoveComponentWithValidation(chunks, componentType); chunks.Dispose(); }
internal unsafe static void ExecuteInternal( ref JobEntityBatchIndexWrapper <T> jobWrapper, IntPtr bufferRangePatchData, ref JobRanges ranges, int jobIndex) { ChunkIterationUtility.UnpackPrefilterData(jobWrapper.PrefilterData, out var filteredChunks, out var entityIndices, out var batchCount); bool isParallel = jobWrapper.IsParallel == 1; while (true) { int beginBatchIndex = 0; int endBatchIndex = batchCount; // 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 batch = filteredChunks[batchIndex]; Assert.IsTrue(batch.Count > 0); // Empty batches are expected to be skipped by the prefilter job! var entityOffset = entityIndices[batchIndex]; #if ENABLE_UNITY_COLLECTIONS_CHECKS if (isParallel) { JobsUtility.PatchBufferMinMaxRanges(bufferRangePatchData, UnsafeUtility.AddressOf(ref jobWrapper), entityOffset, batch.Count); } #endif jobWrapper.JobData.Execute(batch, batchIndex, entityOffset); } // If we are not running in parallel, our job is done. if (!isParallel) { break; } } }
internal unsafe static void ExecuteInternal( ref JobEntityBatchIndexWrapper <T> jobWrapper, ref JobRanges ranges, int jobIndex) { ChunkIterationUtility.UnpackPrefilterData(jobWrapper.PrefilterData, out var filteredChunks, out var entityIndices, out var batchCount); bool isParallel = jobWrapper.IsParallel == 1; while (true) { int beginBatchIndex = 0; int endBatchIndex = batchCount; // 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(filteredChunks[batchIndex], batchIndex, entityIndices[batchIndex]); } // If we are not running in parallel, our job is done. if (!isParallel) { break; } } }
public static unsafe void RunWithoutJobsInternal <T>(ref T jobData, ref EntityQuery query, Entity *limitToEntityArray, int limitToEntityArrayLength) where T : struct, IJobEntityBatch { var prebuiltBatchList = new UnsafeList(Allocator.TempJob); try { ChunkIterationUtility.FindFilteredBatchesForEntityArrayWithQuery( query._GetImpl(), limitToEntityArray, limitToEntityArrayLength, ref prebuiltBatchList); ArchetypeChunk *chunks = (ArchetypeChunk *)prebuiltBatchList.Ptr; int chunkCounts = prebuiltBatchList.Length; for (int i = 0; i != chunkCounts; i++) { jobData.Execute(chunks[i], i); } } finally { prebuiltBatchList.Dispose(); } }
public void Execute() { *OutChunkCount = ChunkIterationUtility.CalculateChunkCount(MatchingArchetypes, ref Filter); }
/// <summary> /// Calculates the total number of chunks this iterator can access. /// </summary> /// <returns>Number of chunks that can be accessed.</returns> internal int CalculateChunkCount() { return(ChunkIterationUtility.CalculateChunkCount(m_MatchingArchetypeList, ref m_Filter)); }
internal void *GetCurrentChunkComponentDataPtr(bool isWriting, int indexInEntityQuery) { int indexInArchetype = CurrentMatchingArchetype->IndexInArchetype[indexInEntityQuery]; return(ChunkIterationUtility.GetChunkComponentDataPtr(CurrentChunk, isWriting, indexInArchetype, m_GlobalSystemVersion)); }
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); }
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, IJobEntityBatchWithIndex { var queryImpl = query._GetImpl(); var queryData = queryImpl->_QueryData; var batchCount = 0; var filteredChunkCount = 0; var useEntityArray = limitToEntityArray.IsCreated; var prebuiltBatchList = new UnsafeList(Allocator.TempJob); var perBatchMatchingArchetypeIndex = new UnsafeIntList(0, Allocator.TempJob); if (useEntityArray) { // 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; } else { filteredChunkCount = query.CalculateChunkCount(); batchCount = filteredChunkCount * batchesPerChunk; } // Allocate one buffer for all prefilter data and distribute it // We keep the full buffer as a "dummy array" so we can deallocate it later with [DeallocateOnJobCompletion] var sizeofBatchArray = sizeof(ArchetypeChunk) * batchCount; var sizeofIndexArray = sizeof(int) * batchCount; var prefilterDataSize = sizeofBatchArray + sizeofIndexArray + sizeof(int); var prefilterData = (byte *)Memory.Unmanaged.Allocate(prefilterDataSize, 64, Allocator.TempJob); var prefilterDataArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray <byte>(prefilterData, prefilterDataSize, Allocator.TempJob); #if ENABLE_UNITY_COLLECTIONS_CHECKS NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref prefilterDataArray, AtomicSafetyHandle.Create()); #endif var prefilterHandle = dependsOn; if (useEntityArray) { var prefilterJob = new PrefilterForJobEntityBatchWithIndex_EntityArray { MatchingArchetypes = queryImpl->_QueryData->MatchingArchetypes, Filter = queryImpl->_Filter, EntityComponentStore = queryImpl->_Access->EntityComponentStore, PrefilterData = prefilterData, PrebuiltBatches = prebuiltBatchList, PerBatchMatchingArchetypeIndex = perBatchMatchingArchetypeIndex }; if (mode != ScheduleMode.Run) { prefilterHandle = prefilterJob.Schedule(dependsOn); } else { prefilterJob.Run(); } prefilterHandle = prebuiltBatchList.Dispose(prefilterHandle); prefilterHandle = perBatchMatchingArchetypeIndex.Dispose(prefilterHandle); } else { var prefilterJob = new PrefilterForJobEntityBatchWithIndex { MatchingArchetypes = queryImpl->_QueryData->MatchingArchetypes, Filter = queryImpl->_Filter, BatchesPerChunk = batchesPerChunk, EntityComponentStore = queryImpl->_Access->EntityComponentStore, PrefilterData = prefilterData, FilteredChunkCount = filteredChunkCount }; if (mode != ScheduleMode.Run) { prefilterHandle = prefilterJob.Schedule(dependsOn); } else { prefilterJob.Run(); } } JobEntityBatchIndexWrapper <T> jobEntityBatchIndexWrapper = new JobEntityBatchIndexWrapper <T> { #if ENABLE_UNITY_COLLECTIONS_CHECKS // All IJobEntityBatchWithIndex 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, PrefilterData = prefilterDataArray, JobsPerChunk = batchesPerChunk, IsParallel = isParallel ? 1 : 0 }; var scheduleParams = new JobsUtility.JobScheduleParameters( UnsafeUtility.AddressOf(ref jobEntityBatchIndexWrapper), isParallel ? JobEntityBatchIndexProducer <T> .InitializeParallel() : JobEntityBatchIndexProducer <T> .InitializeSingle(), prefilterHandle, mode); #if UNITY_DOTSRUNTIME // This should just be a call to FinalizeScheduleChecked, but DOTSR requires the JobsUtility calls to be // in this specific function. #if ENABLE_UNITY_COLLECTIONS_CHECKS try { #endif if (!isParallel) { return(JobsUtility.Schedule(ref scheduleParams)); } else { return(JobsUtility.ScheduleParallelFor(ref scheduleParams, batchCount, 1)); } #if ENABLE_UNITY_COLLECTIONS_CHECKS } catch (InvalidOperationException e) { prefilterHandle.Complete(); prefilterDataArray.Dispose(); throw e; } #endif #else // We can't use try {} catch {} with 2020.2 as we will be burst compiling the schedule code. // Burst doesn't support exception handling. bool executedManaged = false; JobHandle result = default; FinalizeScheduleChecked(isParallel, batchCount, prefilterHandle, prefilterDataArray, ref scheduleParams, ref executedManaged, ref result); if (executedManaged) { return(result); } return(FinalizeScheduleNoExceptions(isParallel, batchCount, ref scheduleParams)); #endif }