void InsertTask(CollisionTask task, int index) { //This allocates a lot of garbage due to frequently resizing, but it does not matter- task registration a one time thing at program initialization. //Having tight bounds is more useful for performance in the end (by virtue of having a marginally simpler heap). int newCount = count + 1; if (tasks == null || newCount > tasks.Length) { Array.Resize(ref tasks, newCount); } if (index < count) { Array.Copy(tasks, index, tasks, index + 1, count - index); //Every task in the array that got moved needs to have its top level index bumped. for (int i = 0; i < topLevelMatrix.Length; ++i) { for (int j = 0; j < topLevelMatrix.Length; ++j) { if (topLevelMatrix[i][j].TaskIndex >= index) { ++topLevelMatrix[i][j].TaskIndex; } } } for (int i = index + 1; i < newCount; ++i) { var t = tasks[i]; if (t.SubtaskIndices != null) { for (int j = 0; j < t.SubtaskIndices.Length; ++j) { ref var subtaskIndex = ref t.SubtaskIndices[j]; if (subtaskIndex >= index) { ++subtaskIndex; } } } } }
public int Register(CollisionTask task) { //Some tasks can generate tasks. Note that this can only be one level deep; nesting compounds is not allowed. //All such generators will be placed at the beginning. var index = task.SubtaskGenerator ? 0 : count; //This allocates a lot of garbage due to frequently resizing, but it does not matter- task registration a one time thing at program initialization. //Having tight bounds is more useful for performance in the end (by virtue of having a marginally simpler heap). int newCount = count + 1; if (tasks == null || newCount > tasks.Length) { Array.Resize(ref tasks, newCount); } if (index < count) { Array.Copy(tasks, index, tasks, index + 1, count - index); //Every task in the array that got moved needs to have its top level index bumped. for (int i = 0; i < topLevelMatrix.Length; ++i) { for (int j = 0; j < topLevelMatrix.Length; ++j) { if (topLevelMatrix[i][j].TaskIndex >= index) { ++topLevelMatrix[i][j].TaskIndex; } } } } tasks[index] = task; count = newCount; var a = task.ShapeTypeIndexA; var b = task.ShapeTypeIndexB; var highestShapeIndex = a > b ? a : b; if (highestShapeIndex >= 0) { //This only handles top level pairs (those associated with shape type pairs). //Some tasks are not directly associated with a top level entrypoint- instead, they're follow ups on top level tasks. Since they're not an entry point, //there is no need for them to appear in the top level matrix. if (highestShapeIndex >= topLevelMatrix.Length) { ResizeMatrix(highestShapeIndex + 1); } var taskInfo = new CollisionTaskReference { TaskIndex = index, BatchSize = task.BatchSize, ExpectedFirstTypeId = task.ShapeTypeIndexA, PairType = task.PairType }; topLevelMatrix[a][b] = taskInfo; topLevelMatrix[b][a] = taskInfo; } #if DEBUG //Ensure that no task dependency cycles exist. bool encounteredNongenerator = false; for (int i = 0; i < count; ++i) { if (encounteredNongenerator) { Debug.Assert(!tasks[i].SubtaskGenerator, "To avoid cycles, the tasks list should be partitioned into two contiguous groups: subtask generators, followed by non-subtask generators."); } else { if (!tasks[i].SubtaskGenerator) { encounteredNongenerator = true; } } } #endif return(index); }