summaryrefslogtreecommitdiff
path: root/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Navmesh/Jobs/JobConnectTiles.cs
blob: 0782ecf7df5cb9423463d6ccc4686e5595c978c3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
using Unity.Collections;
using Unity.Jobs;
using Unity.Jobs.LowLevel.Unsafe;
using Unity.Mathematics;
using UnityEngine;

namespace Pathfinding.Graphs.Navmesh.Jobs {
	/// <summary>
	/// Connects adjacent tiles together.
	///
	/// This only creates connections between tiles. Connections internal to a tile should be handled by <see cref="JobCalculateTriangleConnections"/>.
	///
	/// Use the <see cref="ScheduleBatch"/> method to connect a bunch of tiles efficiently using maximum parallelism.
	/// </summary>
	public struct JobConnectTiles : IJob {
		/// <summary>GCHandle referring to a NavmeshTile[] array of size tileRect.Width*tileRect.Height</summary>
		public System.Runtime.InteropServices.GCHandle tiles;
		public int coordinateSum;
		public int direction;
		public int zOffset;
		public int zStride;
		Vector2 tileWorldSize;
		IntRect tileRect;
		/// <summary>Maximum vertical distance between two tiles to create a connection between them</summary>
		public float maxTileConnectionEdgeDistance;

		static readonly Unity.Profiling.ProfilerMarker ConnectTilesMarker = new Unity.Profiling.ProfilerMarker("ConnectTiles");

		/// <summary>
		/// Schedule jobs to connect all the given tiles with each other while exploiting as much parallelism as possible.
		/// tilesHandle should be a GCHandle referring to a NavmeshTile[] array of size tileRect.Width*tileRect.Height.
		/// </summary>
		public static JobHandle ScheduleBatch (System.Runtime.InteropServices.GCHandle tilesHandle, JobHandle dependency, IntRect tileRect, Vector2 tileWorldSize, float maxTileConnectionEdgeDistance) {
			// First connect all tiles with an EVEN coordinate sum
			// This would be the white squares on a chess board.
			// Then connect all tiles with an ODD coordinate sum (which would be all black squares on a chess board).
			// This will prevent the different threads that do all
			// this in parallel from conflicting with each other.
			// The directions are also done separately
			// first they are connected along the X direction and then along the Z direction.
			// Looping over 0 and then 1

			int workers = Mathf.Max(1, JobsUtility.JobWorkerCount);
			var handles = new NativeArray<JobHandle>(workers, Allocator.Temp);
			for (int coordinateSum = 0; coordinateSum <= 1; coordinateSum++) {
				for (int direction = 0; direction <= 1; direction++) {
					for (int i = 0; i < workers; i++) {
						handles[i] = new JobConnectTiles {
							tiles = tilesHandle,
							tileRect = tileRect,
							tileWorldSize = tileWorldSize,
							coordinateSum = coordinateSum,
							direction = direction,
							maxTileConnectionEdgeDistance = maxTileConnectionEdgeDistance,
							zOffset = i,
							zStride = workers,
						}.Schedule(dependency);
					}
					dependency = JobHandle.CombineDependencies(handles);
				}
			}

			return dependency;
		}

		/// <summary>
		/// Schedule jobs to connect all the given tiles inside innerRect with tiles that are outside it, while exploiting as much parallelism as possible.
		/// tilesHandle should be a GCHandle referring to a NavmeshTile[] array of size tileRect.Width*tileRect.Height.
		/// </summary>
		public static JobHandle ScheduleRecalculateBorders (System.Runtime.InteropServices.GCHandle tilesHandle, JobHandle dependency, IntRect tileRect, IntRect innerRect, Vector2 tileWorldSize, float maxTileConnectionEdgeDistance) {
			var w = innerRect.Width;
			var h = innerRect.Height;

			// Note: conservative estimate of number of handles. There may be fewer in reality.
			var allDependencies = new NativeArray<JobHandle>(2*w + 2*math.max(0, h - 2), Allocator.Temp);
			int count = 0;
			for (int z = 0; z < h; z++) {
				for (int x = 0; x < w; x++) {
					// Check if the tile is on the border of the inner rect
					if (!(x == 0 || z == 0 || x == w - 1 || z == h - 1)) continue;

					var tileX = innerRect.xmin + x;
					var tileZ = innerRect.ymin + z;

					// For a corner tile, the jobs need to run sequentially
					var dep = dependency;
					for (int direction = 0; direction < 4; direction++) {
						var nx = tileX + (direction == 0 ? 1 : direction == 1 ? -1 : 0);
						var nz = tileZ + (direction == 2 ? 1 : direction == 3 ? -1 : 0);
						if (innerRect.Contains(nx, nz) || !tileRect.Contains(nx, nz)) {
							continue;
						}

						dep = new JobConnectTilesSingle {
							tiles = tilesHandle,
							tileIndex1 = tileX + tileZ * tileRect.Width,
							tileIndex2 = nx + nz * tileRect.Width,
							tileWorldSize = tileWorldSize,
							maxTileConnectionEdgeDistance = maxTileConnectionEdgeDistance,
						}.Schedule(dep);
					}

					allDependencies[count++] = dep;
				}
			}
			return JobHandle.CombineDependencies(allDependencies);
		}

		public void Execute () {
			var tiles = (NavmeshTile[])this.tiles.Target;

			var tileRectDepth = tileRect.Height;
			var tileRectWidth = tileRect.Width;
			for (int z = zOffset; z < tileRectDepth; z += zStride) {
				for (int x = 0; x < tileRectWidth; x++) {
					if ((x + z) % 2 == coordinateSum) {
						int tileIndex1 = x + z * tileRectWidth;
						int tileIndex2;
						if (direction == 0 && x < tileRectWidth - 1) {
							tileIndex2 = x + 1 + z * tileRectWidth;
						} else if (direction == 1 && z < tileRectDepth - 1) {
							tileIndex2 = x + (z + 1) * tileRectWidth;
						} else {
							continue;
						}

						ConnectTilesMarker.Begin();
						NavmeshBase.ConnectTiles(tiles[tileIndex1], tiles[tileIndex2], tileWorldSize.x, tileWorldSize.y, maxTileConnectionEdgeDistance);
						ConnectTilesMarker.End();
					}
				}
			}
		}
	}

	/// <summary>
	/// Connects two adjacent tiles together.
	///
	/// This only creates connections between tiles. Connections internal to a tile should be handled by <see cref="JobCalculateTriangleConnections"/>.
	/// </summary>
	struct JobConnectTilesSingle : IJob {
		/// <summary>GCHandle referring to a NavmeshTile[] array of size tileRect.Width*tileRect.Height</summary>
		public System.Runtime.InteropServices.GCHandle tiles;
		/// <summary>Index of the first tile in the <see cref="tiles"/> array</summary>
		public int tileIndex1;
		/// <summary>Index of the second tile in the <see cref="tiles"/> array</summary>
		public int tileIndex2;
		/// <summary>Size of a tile in world units</summary>
		public Vector2 tileWorldSize;
		/// <summary>Maximum vertical distance between two tiles to create a connection between them</summary>
		public float maxTileConnectionEdgeDistance;

		public void Execute () {
			var tiles = (NavmeshTile[])this.tiles.Target;

			NavmeshBase.ConnectTiles(tiles[tileIndex1], tiles[tileIndex2], tileWorldSize.x, tileWorldSize.y, maxTileConnectionEdgeDistance);
		}
	}
}