diff options
Diffstat (limited to 'Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Utilities/UtilityJobs.cs')
-rw-r--r-- | Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Utilities/UtilityJobs.cs | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Utilities/UtilityJobs.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Utilities/UtilityJobs.cs new file mode 100644 index 0000000..03565bd --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Utilities/UtilityJobs.cs @@ -0,0 +1,341 @@ +namespace Pathfinding.Jobs { + using UnityEngine; + using Unity.Burst; + using Unity.Collections; + using Unity.Jobs; + using Unity.Mathematics; + using UnityEngine.Assertions; + using Pathfinding.Graphs.Grid; + using Pathfinding.Util; + + /// <summary> + /// Slice of a 3D array. + /// + /// This is a helper struct used in many jobs to make them work on a part of the data. + /// + /// The outer array has the size <see cref="outerSize"/>.x * <see cref="outerSize"/>.y * <see cref="outerSize"/>.z, laid out as if the coordinates were sorted by the tuple (Y,Z,X). + /// The inner array has the size <see cref="slice.size"/>.x * <see cref="slice.size"/>.y * <see cref="slice.size"/>.z, also laid out as if the coordinates were sorted by the tuple (Y,Z,X). + /// </summary> + public readonly struct Slice3D { + public readonly int3 outerSize; + public readonly IntBounds slice; + + public Slice3D (IntBounds outer, IntBounds slice) : this(outer.size, slice.Offset(-outer.min)) {} + + + public Slice3D (int3 outerSize, IntBounds slice) { + this.outerSize = outerSize; + this.slice = slice; + Assert.IsTrue(slice.min.x >= 0 && slice.min.y >= 0 && slice.min.z >= 0); + Assert.IsTrue(slice.max.x <= outerSize.x && slice.max.y <= outerSize.y && slice.max.z <= outerSize.z); + Assert.IsTrue(slice.size.x > 0 && slice.size.y > 0 && slice.size.z > 0); + } + + public void AssertMatchesOuter<T>(UnsafeSpan<T> values) where T : unmanaged { + Assert.AreEqual(outerSize.x * outerSize.y * outerSize.z, values.Length); + } + + public void AssertMatchesOuter<T>(NativeArray<T> values) where T : struct { + Assert.AreEqual(outerSize.x * outerSize.y * outerSize.z, values.Length); + } + + public void AssertMatchesInner<T>(NativeArray<T> values) where T : struct { + Assert.AreEqual(slice.size.x * slice.size.y * slice.size.z, values.Length); + } + + public void AssertSameSize (Slice3D other) { + Assert.AreEqual(slice.size, other.slice.size); + } + + public int InnerCoordinateToOuterIndex (int x, int y, int z) { + var(dx, dy, dz) = outerStrides; + return (x + slice.min.x) * dx + (y + slice.min.y) * dy + (z + slice.min.z) * dz; + } + + public int length => slice.size.x * slice.size.y * slice.size.z; + + public (int, int, int)outerStrides => (1, outerSize.x * outerSize.z, outerSize.x); + public (int, int, int)innerStrides => (1, slice.size.x * slice.size.z, slice.size.x); + public int outerStartIndex { + get { + var(dx, dy, dz) = outerStrides; + return slice.min.x * dx + slice.min.y * dy + slice.min.z * dz; + } + } + + /// <summary>True if the slice covers the whole outer array</summary> + public bool coversEverything => math.all(slice.size == outerSize); + } + + /// <summary>Helpers for scheduling simple NativeArray jobs</summary> + static class NativeArrayExtensions { + /// <summary>this[i] = value</summary> + public static JobMemSet<T> MemSet<T>(this NativeArray<T> self, T value) where T : unmanaged { + return new JobMemSet<T> { + data = self, + value = value, + }; + } + + /// <summary>this[i] &= other[i]</summary> + public static JobAND BitwiseAndWith (this NativeArray<bool> self, NativeArray<bool> other) { + return new JobAND { + result = self, + data = other, + }; + } + + /// <summary>to[i] = from[i]</summary> + public static JobCopy<T> CopyToJob<T>(this NativeArray<T> from, NativeArray<T> to) where T : struct { + return new JobCopy<T> { + from = from, + to = to, + }; + } + + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static SliceActionJob<T> WithSlice<T>(this T action, Slice3D slice) where T : struct, GridIterationUtilities.ISliceAction { + return new SliceActionJob<T> { + action = action, + slice = slice, + }; + } + + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static IndexActionJob<T> WithLength<T>(this T action, int length) where T : struct, GridIterationUtilities.ISliceAction { + return new IndexActionJob<T> { + action = action, + length = length, + }; + } + + public static JobRotate3DArray<T> Rotate3D<T>(this NativeArray<T> arr, int3 size, int dx, int dz) where T : unmanaged { + return new JobRotate3DArray<T> { + arr = arr, + size = size, + dx = dx, + dz = dz, + }; + } + } + + /// <summary> + /// Treats input as a 3-dimensional array and copies it into the output at the specified position. + /// + /// The <see cref="input"/> is a 3D array, and <see cref="inputSlice"/> refers to a rectangular slice of this array. + /// The <see cref="output"/> is defined similarly. + /// + /// The two slices must naturally have the same shape. + /// </summary> + [BurstCompile] + public struct JobCopyRectangle<T> : IJob where T : struct { + [ReadOnly] + [DisableUninitializedReadCheck] // TODO: Fix so that job doesn't run instead + public NativeArray<T> input; + + [WriteOnly] + public NativeArray<T> output; + + public Slice3D inputSlice; + public Slice3D outputSlice; + + public void Execute () { + Copy(input, output, inputSlice, outputSlice); + } + + /// <summary> + /// Treats input as a 3-dimensional array and copies it into the output at the specified position. + /// + /// The input is a 3D array, and inputSlice refers to a rectangular slice of this array. + /// The output is defined similarly. + /// + /// The two slices must naturally have the same shape. + /// </summary> + public static void Copy (NativeArray<T> input, NativeArray<T> output, Slice3D inputSlice, Slice3D outputSlice) { + inputSlice.AssertMatchesOuter(input); + outputSlice.AssertMatchesOuter(output); + inputSlice.AssertSameSize(outputSlice); + + if (inputSlice.coversEverything && outputSlice.coversEverything) { + // One contiguous chunk + // TODO: Check can be made better by only checking if it is a contiguous chunk instead of covering the whole arrays + input.CopyTo(output); + } else { + // Copy row-by-row + for (int y = 0; y < outputSlice.slice.size.y; y++) { + for (int z = 0; z < outputSlice.slice.size.z; z++) { + var rowOffsetInput = inputSlice.InnerCoordinateToOuterIndex(0, y, z); + var rowOffsetOutput = outputSlice.InnerCoordinateToOuterIndex(0, y, z); + // Using a raw MemCpy call is a bit faster, but that requires unsafe code + // Using a for loop is *a lot* slower (except for very small arrays, in which case it is about the same or very slightly faster). + NativeArray<T>.Copy(input, rowOffsetInput, output, rowOffsetOutput, outputSlice.slice.size.x); + } + } + } + } + } + + /// <summary>result[i] = value</summary> + [BurstCompile] + public struct JobMemSet<T> : IJob where T : unmanaged { + [WriteOnly] + public NativeArray<T> data; + + public T value; + + public void Execute() => data.AsUnsafeSpan().Fill(value); + } + + /// <summary>to[i] = from[i]</summary> + [BurstCompile] + public struct JobCopy<T> : IJob where T : struct { + [ReadOnly] + public NativeArray<T> from; + + [WriteOnly] + public NativeArray<T> to; + + public void Execute () { + from.CopyTo(to); + } + } + + [BurstCompile] + public struct IndexActionJob<T> : IJob where T : struct, GridIterationUtilities.ISliceAction { + public T action; + public int length; + + public void Execute () { + for (int i = 0; i < length; i++) action.Execute((uint)i, (uint)i); + } + } + + [BurstCompile] + public struct SliceActionJob<T> : IJob where T : struct, GridIterationUtilities.ISliceAction { + public T action; + public Slice3D slice; + + public void Execute () { + GridIterationUtilities.ForEachCellIn3DSlice(slice, ref action); + } + } + + /// <summary>result[i] &= data[i]</summary> + public struct JobAND : GridIterationUtilities.ISliceAction { + public NativeArray<bool> result; + + [ReadOnly] + public NativeArray<bool> data; + + public void Execute (uint outerIdx, uint innerIdx) { + result[(int)outerIdx] &= data[(int)outerIdx]; + } + } + + [BurstCompile] + public struct JobMaxHitCount : IJob { + [ReadOnly] + public NativeArray<RaycastHit> hits; + public int maxHits; + public int layerStride; + [WriteOnly] + public NativeArray<int> maxHitCount; + public void Execute () { + int maxHit = 0; + + for (; maxHit < maxHits; maxHit++) { + int offset = maxHit * layerStride; + bool any = false; + for (int i = offset; i < offset + layerStride; i++) { + if (math.any(hits[i].normal)) { + any = true; + break; + } + } + + if (!any) break; + } + + maxHitCount[0] = math.max(1, maxHit); + } + } + + /// <summary> + /// Copies hit points and normals. + /// points[i] = hits[i].point (if anything was hit), normals[i] = hits[i].normal.normalized. + /// </summary> + [BurstCompile(FloatMode = FloatMode.Fast)] + public struct JobCopyHits : IJob, GridIterationUtilities.ISliceAction { + [ReadOnly] + public NativeArray<RaycastHit> hits; + + [WriteOnly] + public NativeArray<Vector3> points; + + [WriteOnly] + public NativeArray<float4> normals; + public Slice3D slice; + + public void Execute () { + // The number of hits may be larger than the number of points. The remaining hits are not actually hits. + Assert.IsTrue(hits.Length >= slice.length); + slice.AssertMatchesOuter(points); + slice.AssertMatchesOuter(normals); + GridIterationUtilities.ForEachCellIn3DSlice(slice, ref this); + } + + public void Execute (uint outerIdx, uint innerIdx) { + Unity.Burst.CompilerServices.Aliasing.ExpectNotAliased(points, normals); + var normal = hits[(int)innerIdx].normal; + var normalV4 = new float4(normal.x, normal.y, normal.z, 0); + normals[(int)outerIdx] = math.normalizesafe(normalV4); + + // Check if anything was hit. The normal will be zero otherwise + // If nothing was hit then the existing data in the points array is reused + if (math.lengthsq(normalV4) > math.FLT_MIN_NORMAL) { + points[(int)outerIdx] = hits[(int)innerIdx].point; + } + } + } + + [BurstCompile] + public struct JobRotate3DArray<T>: IJob where T : unmanaged { + public NativeArray<T> arr; + public int3 size; + public int dx, dz; + + public void Execute () { + int width = size.x; + int height = size.y; + int depth = size.z; + var span = arr.AsUnsafeSpan(); + dx = dx % width; + dz = dz % depth; + if (dx != 0) { + if (dx < 0) dx = width + dx; + var tmp = new NativeArray<T>(dx, Allocator.Temp); + var tmpSpan = tmp.AsUnsafeSpan(); + for (int y = 0; y < height; y++) { + var offset = y * width * depth; + for (int z = 0; z < depth; z++) { + span.Slice(offset + z * width + width - dx, dx).CopyTo(tmpSpan); + span.Move(offset + z * width, offset + z * width + dx, width - dx); + tmpSpan.CopyTo(span.Slice(offset + z * width, dx)); + } + } + } + + if (dz != 0) { + if (dz < 0) dz = depth + dz; + var tmp = new NativeArray<T>(dz * width, Allocator.Temp); + var tmpSpan = tmp.AsUnsafeSpan(); + for (int y = 0; y < height; y++) { + var offset = y * width * depth; + span.Slice(offset + (depth - dz) * width, dz * width).CopyTo(tmpSpan); + span.Move(offset, offset + dz * width, (depth - dz) * width); + tmpSpan.CopyTo(span.Slice(offset, dz * width)); + } + } + } + } +} |