├── .gitignore ├── Assets ├── JobSystem.meta ├── JobSystem │ ├── Components.meta │ ├── Components │ │ ├── JobManagerComponent.cs │ │ ├── JobManagerComponent.cs.meta │ │ ├── ThreadedBehaviour.cs │ │ └── ThreadedBehaviour.cs.meta │ ├── Core.meta │ ├── Core │ │ ├── JobSystemCore.cs │ │ ├── JobSystemCore.cs.meta │ │ ├── JobSystemWorkers.cs │ │ └── JobSystemWorkers.cs.meta │ ├── Extensions.meta │ └── Extensions │ │ ├── TimeExt.cs │ │ └── TimeExt.cs.meta ├── Sample.meta └── Sample │ ├── TestThread.cs │ ├── TestThread.cs.meta │ ├── test.unity │ └── test.unity.meta ├── LICENSE ├── ProjectSettings ├── AudioManager.asset ├── ClusterInputManager.asset ├── DynamicsManager.asset ├── EditorBuildSettings.asset ├── EditorSettings.asset ├── GraphicsSettings.asset ├── InputManager.asset ├── NavMeshAreas.asset ├── NetworkManager.asset ├── Physics2DSettings.asset ├── ProjectSettings.asset ├── ProjectVersion.txt ├── QualitySettings.asset ├── TagManager.asset ├── TimeManager.asset └── UnityConnectSettings.asset └── README /.gitignore: -------------------------------------------------------------------------------- 1 | /[Ll]ibrary/ 2 | /[Tt]emp/ 3 | /[Oo]bj/ 4 | /[Bb]uild/ 5 | /[Bb]uilds/ 6 | /Assets/AssetStoreTools* 7 | 8 | # Visual Studio 2015 cache directory 9 | /.vs/ 10 | 11 | # Autogenerated VS/MD/Consulo solution and project files 12 | ExportedObj/ 13 | .consulo/ 14 | *.csproj 15 | *.unityproj 16 | *.sln 17 | *.suo 18 | *.tmp 19 | *.user 20 | *.userprefs 21 | *.pidb 22 | *.booproj 23 | *.svd 24 | *.pdb 25 | 26 | # Unity3D generated meta files 27 | *.pidb.meta 28 | 29 | # Unity3D Generated File On Crash Reports 30 | sysinfo.txt 31 | 32 | # Builds 33 | *.apk 34 | *.unitypackage -------------------------------------------------------------------------------- /Assets/JobSystem.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 71aa9d190a77e914e87709d1a320fb40 3 | folderAsset: yes 4 | timeCreated: 1493580272 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/JobSystem/Components.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 07692efd32faf8441b35d5074cd7aad1 3 | folderAsset: yes 4 | timeCreated: 1493580296 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/JobSystem/Components/JobManagerComponent.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 Ian Diaz 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | using System.Collections; 24 | using System.Collections.Generic; 25 | using UnityEngine; 26 | 27 | public class JobManagerComponent : MonoBehaviour 28 | { 29 | public static JobManagerComponent _instance; 30 | public static JobManagerComponent Instance 31 | { 32 | get 33 | { 34 | if (!_instance) 35 | { 36 | var go = new GameObject("JobManager"); 37 | DontDestroyOnLoad(go); 38 | _instance = go.AddComponent(); 39 | } 40 | return _instance; 41 | } 42 | } 43 | 44 | void OnEnable() 45 | { 46 | JobUtility.InitializeWorkers(); 47 | } 48 | 49 | void OnDisable() 50 | { 51 | JobUtility.Shutdown(); 52 | } 53 | 54 | void Update() 55 | { 56 | JobSystem.JobSystemCore.RunThreadedUpdate(); 57 | } 58 | 59 | void FixedUpdate() 60 | { 61 | JobSystem.JobSystemCore.RunThreadedFixedUpdate(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Assets/JobSystem/Components/JobManagerComponent.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a6b0646cf23dca14c9cdde266d4ad4b5 3 | timeCreated: 1493580325 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/JobSystem/Components/ThreadedBehaviour.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 Ian Diaz 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | using System.Collections; 24 | using System.Collections.Generic; 25 | using UnityEngine; 26 | 27 | public class ThreadedBehaviour : MonoBehaviour { 28 | public virtual IEnumerator ThreadedUpdate() 29 | { 30 | ShouldThreadedUpdate = false; 31 | // Signals a break 32 | yield return null; 33 | } 34 | 35 | public virtual IEnumerator ThreadedFixedUpdate() 36 | { 37 | ShouldThreadedFixedUpdate = false; 38 | // Signals a break 39 | yield return null; 40 | } 41 | 42 | void OnEnable() 43 | { 44 | ShouldThreadedUpdate = true; 45 | ShouldThreadedFixedUpdate = true; 46 | JobSystem.JobSystemCore.RegisterComponent(this); 47 | } 48 | 49 | void OnDisable() 50 | { 51 | JobSystem.JobSystemCore.UnregisterComponent(this); 52 | } 53 | 54 | public bool ShouldThreadedUpdate { get; internal set; } 55 | public bool ShouldThreadedFixedUpdate { get; internal set; } 56 | } 57 | -------------------------------------------------------------------------------- /Assets/JobSystem/Components/ThreadedBehaviour.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6d2fc5f2b2f54c34a967aa0b9d575433 3 | timeCreated: 1493580427 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/JobSystem/Core.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ba671e50b77435f419f7d63407ba43ca 3 | folderAsset: yes 4 | timeCreated: 1493580393 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/JobSystem/Core/JobSystemCore.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 Ian Diaz 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | using System.Collections; 24 | using System.Collections.Generic; 25 | using UnityEngine; 26 | 27 | namespace JobSystem 28 | { 29 | public static class JobSystemCore 30 | { 31 | static JobSystemCore() 32 | { 33 | ActiveComponents = new List(); 34 | if (!JobManagerComponent.Instance) 35 | throw new System.InvalidOperationException("Unable to start up JobManager"); 36 | } 37 | 38 | static List ActiveComponents; 39 | public static void RegisterComponent(ThreadedBehaviour component) 40 | { 41 | ActiveComponents.Add(component); 42 | } 43 | 44 | public static void UnregisterComponent(ThreadedBehaviour component) 45 | { 46 | ActiveComponents.Remove(component); 47 | } 48 | 49 | public static void RunThreadedFixedUpdate() 50 | { 51 | Time.Update(); 52 | foreach (var v in ActiveComponents) 53 | { 54 | if (v.ShouldThreadedFixedUpdate) 55 | JobUtility.EnqueueJobOnMostFree(v.ThreadedFixedUpdate()); 56 | } 57 | 58 | while (JobUtility.HasJobs()) 59 | { 60 | JobUtility.RunJobOnMainThread(); 61 | } 62 | } 63 | 64 | public static void RunThreadedUpdate() 65 | { 66 | Time.Update(); 67 | foreach (var v in ActiveComponents) 68 | { 69 | if (v.ShouldThreadedUpdate) 70 | JobUtility.EnqueueJobOnMostFree(v.ThreadedUpdate()); 71 | } 72 | while (JobUtility.HasJobs()) 73 | { 74 | JobUtility.RunJobOnMainThread(); 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Assets/JobSystem/Core/JobSystemCore.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: eb9821ffe4b70264d82152a1e6cb74ee 3 | timeCreated: 1493580484 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/JobSystem/Core/JobSystemWorkers.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 Ian Diaz 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | using System.Collections; 24 | using System.Collections.Generic; 25 | using System.Threading; 26 | using System.Linq; 27 | using UnityEngine; 28 | 29 | using Time = JobSystem.Time; 30 | public static class JobYields 31 | { 32 | public static object SwitchToWorker = new object(); 33 | public static object SwitchToMain = new object(); 34 | public static object Yield = new object(); 35 | 36 | public class BaseJobYield : System.IComparable 37 | { 38 | public int CompareTo(object yield) 39 | { 40 | BaseJobYield rhs = (BaseJobYield)yield; 41 | bool m_yield = ShouldYield(); 42 | bool r_yield = rhs.ShouldYield(); 43 | 44 | if (m_yield && r_yield) 45 | return 0; 46 | 47 | if (m_yield) 48 | return -1; 49 | 50 | return 1; 51 | } 52 | 53 | public virtual bool ShouldYield() 54 | { 55 | return false; 56 | } 57 | } 58 | 59 | public class WaitForSeconds : BaseJobYield 60 | { 61 | public WaitForSeconds(float seconds) 62 | { 63 | endTime = Time.timeSinceLevelLoad + seconds; 64 | } 65 | 66 | public override bool ShouldYield() 67 | { 68 | return Time.timeSinceLevelLoad < endTime; 69 | } 70 | 71 | private float endTime; 72 | } 73 | } 74 | 75 | public static class JobUtility 76 | { 77 | public class SynchronizedList 78 | { 79 | public SynchronizedList() 80 | { 81 | m_List = new List(); 82 | m_lock = new object(); 83 | } 84 | 85 | public SynchronizedList(int size) 86 | { 87 | m_List = new List(size); 88 | m_lock = new object(); 89 | } 90 | 91 | public SynchronizedList(SynchronizedList queue) 92 | { 93 | m_List = new List(queue.m_List); 94 | m_lock = new object(); 95 | } 96 | 97 | public void Add(T type) 98 | { 99 | lock (m_lock) 100 | m_List.Add(type); 101 | } 102 | 103 | public int Count 104 | { 105 | get 106 | { 107 | return m_List.Count; 108 | } 109 | } 110 | 111 | public T this[int i] 112 | { 113 | get 114 | { 115 | return m_List[i]; 116 | } 117 | set 118 | { 119 | lock(m_lock) 120 | m_List[i] = value; 121 | } 122 | } 123 | 124 | private List m_List; 125 | private object m_lock; 126 | } 127 | 128 | public class SynchronizedQueue 129 | { 130 | public SynchronizedQueue() 131 | { 132 | m_Queue = new Queue(); 133 | m_lock = new object(); 134 | } 135 | 136 | public SynchronizedQueue(SynchronizedQueue queue) 137 | { 138 | m_Queue = new Queue(queue.m_Queue); 139 | m_lock = new object(); 140 | } 141 | 142 | public void Enqueue(T type) 143 | { 144 | lock (m_lock) 145 | { 146 | m_Queue.Enqueue(type); 147 | m_cvar.Set(); 148 | } 149 | } 150 | 151 | public bool TryDequeue(out T m_out) 152 | { 153 | lock (m_lock) 154 | { 155 | if (Count > 0) 156 | { 157 | m_out = m_Queue.Dequeue(); 158 | return true; 159 | } 160 | m_out = default(T); 161 | return false; 162 | } 163 | } 164 | 165 | public T Wait() 166 | { 167 | lock(m_lock) 168 | { 169 | while (m_Queue.Count == 0) 170 | m_cvar.WaitOne(100); 171 | 172 | return m_Queue.Dequeue(); 173 | } 174 | } 175 | 176 | public int Count 177 | { 178 | get 179 | { 180 | return m_Queue.Count; 181 | } 182 | } 183 | 184 | public void Sort(System.Func func) 185 | { 186 | lock(m_lock) 187 | m_Queue.OrderByDescending(func); 188 | } 189 | 190 | private Queue m_Queue; 191 | private object m_lock; 192 | private EventWaitHandle m_cvar = new EventWaitHandle(false, EventResetMode.AutoReset); 193 | } 194 | 195 | static AutoResetEvent work_cvar = new AutoResetEvent(false); 196 | static SynchronizedQueue _MainThreadQueue = new SynchronizedQueue(); 197 | static SynchronizedList> _ReadyLists = new SynchronizedList>(); 198 | 199 | struct SleepingJob 200 | { 201 | public IEnumerator job; 202 | public JobYields.BaseJobYield yieldFunc; 203 | } 204 | 205 | static SynchronizedQueue _SleepingJobs = new SynchronizedQueue(); 206 | static List m_Workers; 207 | 208 | static int active_workers = 0; 209 | static bool running = true; 210 | 211 | static void RunJob(IEnumerator job, SynchronizedQueue queue) 212 | { 213 | try 214 | { 215 | while (job.MoveNext()) 216 | { 217 | if (job.Current == null) 218 | { 219 | break; 220 | } 221 | else if (job.Current == JobYields.SwitchToMain && queue != _MainThreadQueue) 222 | { 223 | EnqueueJob(job, _MainThreadQueue); 224 | break; 225 | } 226 | else if (job.Current == JobYields.SwitchToWorker && queue == _MainThreadQueue) 227 | { 228 | EnqueueJobOnMostFree(job); 229 | break; 230 | } 231 | else if (job.Current == JobYields.Yield) 232 | { 233 | EnqueueJob(job, queue); 234 | break; 235 | } 236 | else if (job.Current is JobYields.BaseJobYield) 237 | { 238 | var yield = job.Current as JobYields.BaseJobYield; 239 | if (yield.ShouldYield()) 240 | { 241 | var obj = new SleepingJob(); 242 | obj.yieldFunc = yield; 243 | obj.job = job; 244 | _SleepingJobs.Enqueue(obj); 245 | break; 246 | } 247 | } 248 | } 249 | } 250 | catch(System.Exception ex) 251 | { 252 | Debug.LogError(ex); 253 | Debug.Break(); 254 | } 255 | } 256 | 257 | public static void RunJobOnMainThread() 258 | { 259 | if (_SleepingJobs.Count > 0) 260 | { 261 | _SleepingJobs.Sort((a) => !a.yieldFunc.ShouldYield()); 262 | 263 | SleepingJob sleep; 264 | while(_SleepingJobs.TryDequeue(out sleep) && !sleep.yieldFunc.ShouldYield()) 265 | EnqueueJobOnMostFree(sleep.job); 266 | 267 | if (sleep.yieldFunc.ShouldYield()) 268 | _SleepingJobs.Enqueue(sleep); 269 | } 270 | 271 | int jobs = 0; 272 | jobs += _MainThreadQueue.Count; 273 | for (int i = 0; i < _ReadyLists.Count; ++i) 274 | jobs += _ReadyLists[i].Count; 275 | 276 | IEnumerator job; 277 | if (_MainThreadQueue.TryDequeue(out job)) 278 | { 279 | RunJob(job, _MainThreadQueue); 280 | return; 281 | } 282 | 283 | if ((job = FindWork(0)) != null) 284 | { 285 | RunJob(job, _MainThreadQueue); 286 | } 287 | } 288 | 289 | public static bool HasJobs() 290 | { 291 | if (active_workers > 0) 292 | { 293 | return true; 294 | } 295 | 296 | if (_MainThreadQueue.Count > 0) 297 | { 298 | return true; 299 | } 300 | 301 | for (int i = 0; i < _ReadyLists.Count; ++i) 302 | { 303 | if (_ReadyLists[i].Count > 0) 304 | { 305 | work_cvar.Set(); 306 | return true; 307 | } 308 | } 309 | 310 | return false; 311 | } 312 | 313 | static IEnumerator FindWork(int index) 314 | { 315 | IEnumerator ret; 316 | if (_ReadyLists[index].TryDequeue(out ret)) 317 | return ret; 318 | 319 | for (int i = 0; i < _ReadyLists.Count; ++i) 320 | { 321 | if (i == index) 322 | continue; 323 | 324 | if (_ReadyLists[index].TryDequeue(out ret)) 325 | return ret; 326 | } 327 | return null; 328 | } 329 | 330 | static void WorkerFunc(object index) 331 | { 332 | int threadindex = (int)index; 333 | while (running) 334 | { 335 | IEnumerator job = null; 336 | while ((job = FindWork(threadindex)) == null && running) 337 | { 338 | work_cvar.WaitOne(100); 339 | } 340 | if (job != null) 341 | { 342 | Interlocked.Increment(ref active_workers); 343 | RunJob(job, _ReadyLists[threadindex]); 344 | Interlocked.Decrement(ref active_workers); 345 | } 346 | } 347 | } 348 | 349 | public static void InitializeWorkers() 350 | { 351 | running = true; 352 | m_Workers = new List(); 353 | for (int i = 0; i < System.Environment.ProcessorCount - 1; ++i) 354 | { 355 | _ReadyLists.Add(new SynchronizedQueue()); 356 | 357 | var thr = new Thread(WorkerFunc); 358 | thr.Start(i); 359 | m_Workers.Add(thr); 360 | } 361 | } 362 | 363 | public static void Shutdown() 364 | { 365 | for (int i = 0; i < m_Workers.Count; ++i) 366 | { 367 | work_cvar.Set(); 368 | } 369 | running = false; 370 | for (int i = 0; i < m_Workers.Count; ++i) 371 | { 372 | if (!m_Workers[i].Join(1000)) 373 | m_Workers[i].Abort(); 374 | } 375 | } 376 | 377 | public static void EnqueueJob(IEnumerator coroutine, SynchronizedQueue list) 378 | { 379 | list.Enqueue(coroutine); 380 | work_cvar.Set(); 381 | } 382 | 383 | public static void EnqueueJobOnMostFree(IEnumerator coroutine) 384 | { 385 | SynchronizedQueue least; 386 | if (_ReadyLists.Count > 0) 387 | least = _ReadyLists[0]; 388 | else 389 | least = _MainThreadQueue; 390 | 391 | for (int i = 0; i < _ReadyLists.Count; ++i) 392 | { 393 | if (_ReadyLists[i].Count == 0) 394 | { 395 | least = _ReadyLists[i]; 396 | break; 397 | } 398 | 399 | if (_ReadyLists[i].Count < least.Count) 400 | { 401 | least = _ReadyLists[i]; 402 | } 403 | } 404 | EnqueueJob(coroutine, least); 405 | } 406 | } 407 | -------------------------------------------------------------------------------- /Assets/JobSystem/Core/JobSystemWorkers.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 36d9fcfbbd81e00498ea2137ff60c185 3 | timeCreated: 1493580795 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/JobSystem/Extensions.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fa03ea435cb980d479b8befadfad26bc 3 | folderAsset: yes 4 | timeCreated: 1493596023 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/JobSystem/Extensions/TimeExt.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 Ian Diaz 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | using UnityEngine; 24 | namespace JobSystem 25 | { 26 | public static class Time 27 | { 28 | public static void Update() 29 | { 30 | timeSinceLevelLoad = Time.timeSinceLevelLoad; 31 | deltaTime = Time.deltaTime; 32 | fixedDeltaTime = Time.fixedDeltaTime; 33 | realtimeSinceStartup = Time.realtimeSinceStartup; 34 | unscaledTime = Time.unscaledTime; 35 | } 36 | 37 | public static float timeSinceLevelLoad { get; internal set; } 38 | public static float deltaTime { get; internal set; } 39 | public static float fixedDeltaTime { get; internal set; } 40 | public static float realtimeSinceStartup { get; internal set; } 41 | public static float unscaledTime { get; internal set; } 42 | } 43 | } -------------------------------------------------------------------------------- /Assets/JobSystem/Extensions/TimeExt.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b0875fcbf07d4b84fbe9caf80fa50b0f 3 | timeCreated: 1493596023 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Sample.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 716285830d2bcda478f960d1e28d7db8 3 | folderAsset: yes 4 | timeCreated: 1493586747 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Sample/TestThread.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 Ian Diaz 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | using System.Collections; 24 | using System.Collections.Generic; 25 | using UnityEngine; 26 | 27 | public class TestThread : ThreadedBehaviour { 28 | // Standard coroutine-styled job 29 | IEnumerator ComplicatedJob() 30 | { 31 | var start = System.DateTime.Now; 32 | // NOTE: This is DEFINITELY not the most efficient way to do this, but 33 | // it serves as an example for bouncing back and forth between the foreground 34 | // and the background 35 | List b = new List(); 36 | 37 | // First we're going to sample all of the bounding 38 | // boxes in the scene, and for that we need to be on the main thread... 39 | yield return JobYields.SwitchToMain; 40 | foreach(var v in FindObjectsOfType()) 41 | { 42 | b.Add(v.bounds); 43 | } 44 | 45 | // Now we'll switch to a worker to compute the centerpoint 46 | yield return JobYields.SwitchToWorker; 47 | Vector3 pos = Vector3.zero; 48 | for(int i = 0; i < b.Count; ++i) 49 | { 50 | pos += b[i].center; 51 | } 52 | pos /= (float)b.Count; 53 | 54 | double iterations = 0; 55 | double total = 0; 56 | 57 | // Now let's test the amount of time it takes to make an uncontested context switch 58 | for (int i = 0; i < 10000; ++i) 59 | { 60 | yield return JobYields.SwitchToMain; 61 | ++iterations; 62 | 63 | var startTime = System.DateTime.Now; 64 | yield return JobYields.SwitchToWorker; 65 | total += (System.DateTime.Now - startTime).TotalMilliseconds; 66 | } 67 | Debug.Log("Avg context switch time: " + (total / iterations) + " ms"); 68 | 69 | // Switch back to the main thread to set our position after heavy work 70 | yield return JobYields.SwitchToMain; 71 | transform.position = pos; 72 | } 73 | 74 | IEnumerator JobInsideUpdate() 75 | { 76 | for (int i = 0; i < 10; ++i) 77 | yield return i; 78 | } 79 | 80 | void Start() 81 | { 82 | JobUtility.EnqueueJobOnMostFree(ComplicatedJob()); 83 | } 84 | 85 | // If overridden from the baseclass, runs automatically every Update 86 | public override IEnumerator ThreadedUpdate() 87 | { 88 | // Run a job within a job 89 | var m_job = JobInsideUpdate(); 90 | while (m_job.MoveNext()) 91 | { 92 | int i = (int)m_job.Current; 93 | //Debug.Log(i); // prints 1-9 94 | } 95 | 96 | // Do some complex computation 97 | System.Threading.Thread.Sleep(3); 98 | 99 | for (int i = 0; i < 6; ++i) 100 | { 101 | // Run raycast 102 | yield return JobYields.SwitchToMain; 103 | var start = transform.position; 104 | var dir = transform.forward; 105 | RaycastHit hit; 106 | var didhit = Physics.Raycast(start, dir, out hit); 107 | 108 | yield return JobYields.SwitchToWorker; 109 | 110 | // Complicated math to process the raycast 111 | //System.Threading.Thread.Sleep(1); 112 | } 113 | } 114 | 115 | // If overridden from the baseclass, runs automatically every FixedUpdate 116 | public override IEnumerator ThreadedFixedUpdate() 117 | { 118 | // Currently does nothing other than demonstrating the fact that 119 | // FixedUpdate is threadable 120 | yield return null; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Assets/Sample/TestThread.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5260e8018e8054f4dac8f3adf4456ec7 3 | timeCreated: 1493586753 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Sample/test.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowndacorner/Unity-Multithreaded-Job-System/c4f01930563e2effda6996fa69ddf2c6e4cb6dda/Assets/Sample/test.unity -------------------------------------------------------------------------------- /Assets/Sample/test.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9a41be2873ff81042ac449ea99285038 3 | timeCreated: 1493587184 4 | licenseType: Free 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ian Diaz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /ProjectSettings/AudioManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowndacorner/Unity-Multithreaded-Job-System/c4f01930563e2effda6996fa69ddf2c6e4cb6dda/ProjectSettings/AudioManager.asset -------------------------------------------------------------------------------- /ProjectSettings/ClusterInputManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowndacorner/Unity-Multithreaded-Job-System/c4f01930563e2effda6996fa69ddf2c6e4cb6dda/ProjectSettings/ClusterInputManager.asset -------------------------------------------------------------------------------- /ProjectSettings/DynamicsManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowndacorner/Unity-Multithreaded-Job-System/c4f01930563e2effda6996fa69ddf2c6e4cb6dda/ProjectSettings/DynamicsManager.asset -------------------------------------------------------------------------------- /ProjectSettings/EditorBuildSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowndacorner/Unity-Multithreaded-Job-System/c4f01930563e2effda6996fa69ddf2c6e4cb6dda/ProjectSettings/EditorBuildSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/EditorSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowndacorner/Unity-Multithreaded-Job-System/c4f01930563e2effda6996fa69ddf2c6e4cb6dda/ProjectSettings/EditorSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/GraphicsSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowndacorner/Unity-Multithreaded-Job-System/c4f01930563e2effda6996fa69ddf2c6e4cb6dda/ProjectSettings/GraphicsSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/InputManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowndacorner/Unity-Multithreaded-Job-System/c4f01930563e2effda6996fa69ddf2c6e4cb6dda/ProjectSettings/InputManager.asset -------------------------------------------------------------------------------- /ProjectSettings/NavMeshAreas.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowndacorner/Unity-Multithreaded-Job-System/c4f01930563e2effda6996fa69ddf2c6e4cb6dda/ProjectSettings/NavMeshAreas.asset -------------------------------------------------------------------------------- /ProjectSettings/NetworkManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowndacorner/Unity-Multithreaded-Job-System/c4f01930563e2effda6996fa69ddf2c6e4cb6dda/ProjectSettings/NetworkManager.asset -------------------------------------------------------------------------------- /ProjectSettings/Physics2DSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowndacorner/Unity-Multithreaded-Job-System/c4f01930563e2effda6996fa69ddf2c6e4cb6dda/ProjectSettings/Physics2DSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/ProjectSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowndacorner/Unity-Multithreaded-Job-System/c4f01930563e2effda6996fa69ddf2c6e4cb6dda/ProjectSettings/ProjectSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/ProjectVersion.txt: -------------------------------------------------------------------------------- 1 | m_EditorVersion: 5.5.0f3 2 | -------------------------------------------------------------------------------- /ProjectSettings/QualitySettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowndacorner/Unity-Multithreaded-Job-System/c4f01930563e2effda6996fa69ddf2c6e4cb6dda/ProjectSettings/QualitySettings.asset -------------------------------------------------------------------------------- /ProjectSettings/TagManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowndacorner/Unity-Multithreaded-Job-System/c4f01930563e2effda6996fa69ddf2c6e4cb6dda/ProjectSettings/TagManager.asset -------------------------------------------------------------------------------- /ProjectSettings/TimeManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowndacorner/Unity-Multithreaded-Job-System/c4f01930563e2effda6996fa69ddf2c6e4cb6dda/ProjectSettings/TimeManager.asset -------------------------------------------------------------------------------- /ProjectSettings/UnityConnectSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowndacorner/Unity-Multithreaded-Job-System/c4f01930563e2effda6996fa69ddf2c6e4cb6dda/ProjectSettings/UnityConnectSettings.asset -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is a very basic multithreaded job system for Unity with a simple task scheduler, where each worker thread has its own local queue to try to avoid contention. 2 | 3 | For example usage as a means for natively multithreading a component's Update, look at Sample/TestThread.cs, it's pretty well commented. 4 | 5 | I made this as a quick exercise to see how much effort would be involved in such a system. I might keep working on it, I might not. 6 | 7 | Architecturally, this uses a lockful (if that's a term?) work-stealing job system with n-1 workers (where n is processor count). While jobs are running, the main thread tries to steal background work itself if it doesn't have anything queued to run. 8 | 9 | In the future, I'd like to implement more of the ideas from Naughty Dog's GDC 2015 talk, such as waiting via atomic counters and scheduling jobs from within jobs. But I'd say this is certainly a nice start. It would also be very nice to ensure that each thread is locked to a processor core, but .NET doesn't provide any facilities with which to do this, so it would require native code. Which wouldn't be complicated to implement, however it would lose its ability to easily be deployed cross platform, as each platform and processor architecture would require a precompiled shared library. --------------------------------------------------------------------------------