PlayerLoopHelper.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
  2. using System;
  3. using System.Linq;
  4. using UnityEngine;
  5. using Cysharp.Threading.Tasks.Internal;
  6. using System.Threading;
  7. #if UNITY_2019_3_OR_NEWER
  8. using UnityEngine.LowLevel;
  9. using PlayerLoopType = UnityEngine.PlayerLoop;
  10. #else
  11. using UnityEngine.Experimental.LowLevel;
  12. using PlayerLoopType = UnityEngine.Experimental.PlayerLoop;
  13. #endif
  14. #if UNITY_EDITOR
  15. using UnityEditor;
  16. #endif
  17. namespace Cysharp.Threading.Tasks
  18. {
  19. public static class UniTaskLoopRunners
  20. {
  21. public struct UniTaskLoopRunnerInitialization { };
  22. public struct UniTaskLoopRunnerEarlyUpdate { };
  23. public struct UniTaskLoopRunnerFixedUpdate { };
  24. public struct UniTaskLoopRunnerPreUpdate { };
  25. public struct UniTaskLoopRunnerUpdate { };
  26. public struct UniTaskLoopRunnerPreLateUpdate { };
  27. public struct UniTaskLoopRunnerPostLateUpdate { };
  28. // Last
  29. public struct UniTaskLoopRunnerLastInitialization { };
  30. public struct UniTaskLoopRunnerLastEarlyUpdate { };
  31. public struct UniTaskLoopRunnerLastFixedUpdate { };
  32. public struct UniTaskLoopRunnerLastPreUpdate { };
  33. public struct UniTaskLoopRunnerLastUpdate { };
  34. public struct UniTaskLoopRunnerLastPreLateUpdate { };
  35. public struct UniTaskLoopRunnerLastPostLateUpdate { };
  36. // Yield
  37. public struct UniTaskLoopRunnerYieldInitialization { };
  38. public struct UniTaskLoopRunnerYieldEarlyUpdate { };
  39. public struct UniTaskLoopRunnerYieldFixedUpdate { };
  40. public struct UniTaskLoopRunnerYieldPreUpdate { };
  41. public struct UniTaskLoopRunnerYieldUpdate { };
  42. public struct UniTaskLoopRunnerYieldPreLateUpdate { };
  43. public struct UniTaskLoopRunnerYieldPostLateUpdate { };
  44. // Yield Last
  45. public struct UniTaskLoopRunnerLastYieldInitialization { };
  46. public struct UniTaskLoopRunnerLastYieldEarlyUpdate { };
  47. public struct UniTaskLoopRunnerLastYieldFixedUpdate { };
  48. public struct UniTaskLoopRunnerLastYieldPreUpdate { };
  49. public struct UniTaskLoopRunnerLastYieldUpdate { };
  50. public struct UniTaskLoopRunnerLastYieldPreLateUpdate { };
  51. public struct UniTaskLoopRunnerLastYieldPostLateUpdate { };
  52. #if UNITY_2020_2_OR_NEWER
  53. public struct UniTaskLoopRunnerTimeUpdate { };
  54. public struct UniTaskLoopRunnerLastTimeUpdate { };
  55. public struct UniTaskLoopRunnerYieldTimeUpdate { };
  56. public struct UniTaskLoopRunnerLastYieldTimeUpdate { };
  57. #endif
  58. }
  59. public enum PlayerLoopTiming
  60. {
  61. Initialization = 0,
  62. LastInitialization = 1,
  63. EarlyUpdate = 2,
  64. LastEarlyUpdate = 3,
  65. FixedUpdate = 4,
  66. LastFixedUpdate = 5,
  67. PreUpdate = 6,
  68. LastPreUpdate = 7,
  69. Update = 8,
  70. LastUpdate = 9,
  71. PreLateUpdate = 10,
  72. LastPreLateUpdate = 11,
  73. PostLateUpdate = 12,
  74. LastPostLateUpdate = 13,
  75. #if UNITY_2020_2_OR_NEWER
  76. // Unity 2020.2 added TimeUpdate https://docs.unity3d.com/2020.2/Documentation/ScriptReference/PlayerLoop.TimeUpdate.html
  77. TimeUpdate = 14,
  78. LastTimeUpdate = 15,
  79. #endif
  80. }
  81. [Flags]
  82. public enum InjectPlayerLoopTimings
  83. {
  84. /// <summary>
  85. /// Preset: All loops(default).
  86. /// </summary>
  87. All =
  88. Initialization | LastInitialization |
  89. EarlyUpdate | LastEarlyUpdate |
  90. FixedUpdate | LastFixedUpdate |
  91. PreUpdate | LastPreUpdate |
  92. Update | LastUpdate |
  93. PreLateUpdate | LastPreLateUpdate |
  94. PostLateUpdate | LastPostLateUpdate
  95. #if UNITY_2020_2_OR_NEWER
  96. | TimeUpdate | LastTimeUpdate,
  97. #else
  98. ,
  99. #endif
  100. /// <summary>
  101. /// Preset: All without last except LastPostLateUpdate.
  102. /// </summary>
  103. Standard =
  104. Initialization |
  105. EarlyUpdate |
  106. FixedUpdate |
  107. PreUpdate |
  108. Update |
  109. PreLateUpdate |
  110. PostLateUpdate | LastPostLateUpdate
  111. #if UNITY_2020_2_OR_NEWER
  112. | TimeUpdate
  113. #endif
  114. ,
  115. /// <summary>
  116. /// Preset: Minimum pattern, Update | FixedUpdate | LastPostLateUpdate
  117. /// </summary>
  118. Minimum =
  119. Update | FixedUpdate | LastPostLateUpdate,
  120. // PlayerLoopTiming
  121. Initialization = 1,
  122. LastInitialization = 2,
  123. EarlyUpdate = 4,
  124. LastEarlyUpdate = 8,
  125. FixedUpdate = 16,
  126. LastFixedUpdate = 32,
  127. PreUpdate = 64,
  128. LastPreUpdate = 128,
  129. Update = 256,
  130. LastUpdate = 512,
  131. PreLateUpdate = 1024,
  132. LastPreLateUpdate = 2048,
  133. PostLateUpdate = 4096,
  134. LastPostLateUpdate = 8192
  135. #if UNITY_2020_2_OR_NEWER
  136. ,
  137. // Unity 2020.2 added TimeUpdate https://docs.unity3d.com/2020.2/Documentation/ScriptReference/PlayerLoop.TimeUpdate.html
  138. TimeUpdate = 16384,
  139. LastTimeUpdate = 32768
  140. #endif
  141. }
  142. public interface IPlayerLoopItem
  143. {
  144. bool MoveNext();
  145. }
  146. public static class PlayerLoopHelper
  147. {
  148. static readonly ContinuationQueue ThrowMarkerContinuationQueue = new ContinuationQueue(PlayerLoopTiming.Initialization);
  149. static readonly PlayerLoopRunner ThrowMarkerPlayerLoopRunner = new PlayerLoopRunner(PlayerLoopTiming.Initialization);
  150. public static SynchronizationContext UnitySynchronizationContext => unitySynchronizationContext;
  151. public static int MainThreadId => mainThreadId;
  152. internal static string ApplicationDataPath => applicationDataPath;
  153. public static bool IsMainThread => Thread.CurrentThread.ManagedThreadId == mainThreadId;
  154. static int mainThreadId;
  155. static string applicationDataPath;
  156. static SynchronizationContext unitySynchronizationContext;
  157. static ContinuationQueue[] yielders;
  158. static PlayerLoopRunner[] runners;
  159. internal static bool IsEditorApplicationQuitting { get; private set; }
  160. static PlayerLoopSystem[] InsertRunner(PlayerLoopSystem loopSystem,
  161. bool injectOnFirst,
  162. Type loopRunnerYieldType, ContinuationQueue cq,
  163. Type loopRunnerType, PlayerLoopRunner runner)
  164. {
  165. #if UNITY_EDITOR
  166. EditorApplication.playModeStateChanged += (state) =>
  167. {
  168. if (state == PlayModeStateChange.EnteredEditMode || state == PlayModeStateChange.ExitingEditMode)
  169. {
  170. IsEditorApplicationQuitting = true;
  171. // run rest action before clear.
  172. if (runner != null)
  173. {
  174. runner.Run();
  175. runner.Clear();
  176. }
  177. if (cq != null)
  178. {
  179. cq.Run();
  180. cq.Clear();
  181. }
  182. IsEditorApplicationQuitting = false;
  183. }
  184. };
  185. #endif
  186. var yieldLoop = new PlayerLoopSystem
  187. {
  188. type = loopRunnerYieldType,
  189. updateDelegate = cq.Run
  190. };
  191. var runnerLoop = new PlayerLoopSystem
  192. {
  193. type = loopRunnerType,
  194. updateDelegate = runner.Run
  195. };
  196. // Remove items from previous initializations.
  197. var source = RemoveRunner(loopSystem, loopRunnerYieldType, loopRunnerType);
  198. var dest = new PlayerLoopSystem[source.Length + 2];
  199. Array.Copy(source, 0, dest, injectOnFirst ? 2 : 0, source.Length);
  200. if (injectOnFirst)
  201. {
  202. dest[0] = yieldLoop;
  203. dest[1] = runnerLoop;
  204. }
  205. else
  206. {
  207. dest[dest.Length - 2] = yieldLoop;
  208. dest[dest.Length - 1] = runnerLoop;
  209. }
  210. return dest;
  211. }
  212. static PlayerLoopSystem[] RemoveRunner(PlayerLoopSystem loopSystem, Type loopRunnerYieldType, Type loopRunnerType)
  213. {
  214. return loopSystem.subSystemList
  215. .Where(ls => ls.type != loopRunnerYieldType && ls.type != loopRunnerType)
  216. .ToArray();
  217. }
  218. static PlayerLoopSystem[] InsertUniTaskSynchronizationContext(PlayerLoopSystem loopSystem)
  219. {
  220. var loop = new PlayerLoopSystem
  221. {
  222. type = typeof(UniTaskSynchronizationContext),
  223. updateDelegate = UniTaskSynchronizationContext.Run
  224. };
  225. // Remove items from previous initializations.
  226. var source = loopSystem.subSystemList
  227. .Where(ls => ls.type != typeof(UniTaskSynchronizationContext))
  228. .ToArray();
  229. var dest = new System.Collections.Generic.List<PlayerLoopSystem>(source);
  230. var index = dest.FindIndex(x => x.type.Name == "ScriptRunDelayedTasks");
  231. if (index == -1)
  232. {
  233. index = dest.FindIndex(x => x.type.Name == "UniTaskLoopRunnerUpdate");
  234. }
  235. dest.Insert(index + 1, loop);
  236. return dest.ToArray();
  237. }
  238. [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
  239. static void Init()
  240. {
  241. // capture default(unity) sync-context.
  242. unitySynchronizationContext = SynchronizationContext.Current;
  243. mainThreadId = Thread.CurrentThread.ManagedThreadId;
  244. try
  245. {
  246. applicationDataPath = Application.dataPath;
  247. }
  248. catch { }
  249. #if UNITY_EDITOR && UNITY_2019_3_OR_NEWER
  250. // When domain reload is disabled, re-initialization is required when entering play mode;
  251. // otherwise, pending tasks will leak between play mode sessions.
  252. var domainReloadDisabled = UnityEditor.EditorSettings.enterPlayModeOptionsEnabled &&
  253. UnityEditor.EditorSettings.enterPlayModeOptions.HasFlag(UnityEditor.EnterPlayModeOptions.DisableDomainReload);
  254. if (!domainReloadDisabled && runners != null) return;
  255. #else
  256. if (runners != null) return; // already initialized
  257. #endif
  258. var playerLoop =
  259. #if UNITY_2019_3_OR_NEWER
  260. PlayerLoop.GetCurrentPlayerLoop();
  261. #else
  262. PlayerLoop.GetDefaultPlayerLoop();
  263. #endif
  264. Initialize(ref playerLoop);
  265. }
  266. #if UNITY_EDITOR
  267. [InitializeOnLoadMethod]
  268. static void InitOnEditor()
  269. {
  270. // Execute the play mode init method
  271. Init();
  272. // register an Editor update delegate, used to forcing playerLoop update
  273. EditorApplication.update += ForceEditorPlayerLoopUpdate;
  274. }
  275. private static void ForceEditorPlayerLoopUpdate()
  276. {
  277. if (EditorApplication.isPlayingOrWillChangePlaymode || EditorApplication.isCompiling || EditorApplication.isUpdating)
  278. {
  279. // Not in Edit mode, don't interfere
  280. return;
  281. }
  282. // EditorApplication.QueuePlayerLoopUpdate causes performance issue, don't call directly.
  283. // EditorApplication.QueuePlayerLoopUpdate();
  284. if (yielders != null)
  285. {
  286. foreach (var item in yielders)
  287. {
  288. if (item != null) item.Run();
  289. }
  290. }
  291. if (runners != null)
  292. {
  293. foreach (var item in runners)
  294. {
  295. if (item != null) item.Run();
  296. }
  297. }
  298. UniTaskSynchronizationContext.Run();
  299. }
  300. #endif
  301. private static int FindLoopSystemIndex(PlayerLoopSystem[] playerLoopList, Type systemType)
  302. {
  303. for (int i = 0; i < playerLoopList.Length; i++)
  304. {
  305. if (playerLoopList[i].type == systemType)
  306. {
  307. return i;
  308. }
  309. }
  310. throw new Exception("Target PlayerLoopSystem does not found. Type:" + systemType.FullName);
  311. }
  312. static void InsertLoop(PlayerLoopSystem[] copyList, InjectPlayerLoopTimings injectTimings, Type loopType, InjectPlayerLoopTimings targetTimings,
  313. int index, bool injectOnFirst, Type loopRunnerYieldType, Type loopRunnerType, PlayerLoopTiming playerLoopTiming)
  314. {
  315. var i = FindLoopSystemIndex(copyList, loopType);
  316. if ((injectTimings & targetTimings) == targetTimings)
  317. {
  318. copyList[i].subSystemList = InsertRunner(copyList[i], injectOnFirst,
  319. loopRunnerYieldType, yielders[index] = new ContinuationQueue(playerLoopTiming),
  320. loopRunnerType, runners[index] = new PlayerLoopRunner(playerLoopTiming));
  321. }
  322. else
  323. {
  324. copyList[i].subSystemList = RemoveRunner(copyList[i], loopRunnerYieldType, loopRunnerType);
  325. }
  326. }
  327. public static void Initialize(ref PlayerLoopSystem playerLoop, InjectPlayerLoopTimings injectTimings = InjectPlayerLoopTimings.All)
  328. {
  329. #if UNITY_2020_2_OR_NEWER
  330. yielders = new ContinuationQueue[16];
  331. runners = new PlayerLoopRunner[16];
  332. #else
  333. yielders = new ContinuationQueue[14];
  334. runners = new PlayerLoopRunner[14];
  335. #endif
  336. var copyList = playerLoop.subSystemList.ToArray();
  337. // Initialization
  338. InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Initialization),
  339. InjectPlayerLoopTimings.Initialization, 0, true,
  340. typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldInitialization), typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization), PlayerLoopTiming.Initialization);
  341. InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Initialization),
  342. InjectPlayerLoopTimings.LastInitialization, 1, false,
  343. typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldInitialization), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastInitialization), PlayerLoopTiming.LastInitialization);
  344. // EarlyUpdate
  345. InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.EarlyUpdate),
  346. InjectPlayerLoopTimings.EarlyUpdate, 2, true,
  347. typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldEarlyUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerEarlyUpdate), PlayerLoopTiming.EarlyUpdate);
  348. InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.EarlyUpdate),
  349. InjectPlayerLoopTimings.LastEarlyUpdate, 3, false,
  350. typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldEarlyUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastEarlyUpdate), PlayerLoopTiming.LastEarlyUpdate);
  351. // FixedUpdate
  352. InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.FixedUpdate),
  353. InjectPlayerLoopTimings.FixedUpdate, 4, true,
  354. typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldFixedUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerFixedUpdate), PlayerLoopTiming.FixedUpdate);
  355. InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.FixedUpdate),
  356. InjectPlayerLoopTimings.LastFixedUpdate, 5, false,
  357. typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldFixedUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastFixedUpdate), PlayerLoopTiming.LastFixedUpdate);
  358. // PreUpdate
  359. InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreUpdate),
  360. InjectPlayerLoopTimings.PreUpdate, 6, true,
  361. typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreUpdate), PlayerLoopTiming.PreUpdate);
  362. InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreUpdate),
  363. InjectPlayerLoopTimings.LastPreUpdate, 7, false,
  364. typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreUpdate), PlayerLoopTiming.LastPreUpdate);
  365. // Update
  366. InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Update),
  367. InjectPlayerLoopTimings.Update, 8, true,
  368. typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerUpdate), PlayerLoopTiming.Update);
  369. InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Update),
  370. InjectPlayerLoopTimings.LastUpdate, 9, false,
  371. typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastUpdate), PlayerLoopTiming.LastUpdate);
  372. // PreLateUpdate
  373. InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreLateUpdate),
  374. InjectPlayerLoopTimings.PreLateUpdate, 10, true,
  375. typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreLateUpdate), PlayerLoopTiming.PreLateUpdate);
  376. InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreLateUpdate),
  377. InjectPlayerLoopTimings.LastPreLateUpdate, 11, false,
  378. typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreLateUpdate), PlayerLoopTiming.LastPreLateUpdate);
  379. // PostLateUpdate
  380. InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PostLateUpdate),
  381. InjectPlayerLoopTimings.PostLateUpdate, 12, true,
  382. typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPostLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPostLateUpdate), PlayerLoopTiming.PostLateUpdate);
  383. InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PostLateUpdate),
  384. InjectPlayerLoopTimings.LastPostLateUpdate, 13, false,
  385. typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPostLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPostLateUpdate), PlayerLoopTiming.LastPostLateUpdate);
  386. #if UNITY_2020_2_OR_NEWER
  387. // TimeUpdate
  388. InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.TimeUpdate),
  389. InjectPlayerLoopTimings.TimeUpdate, 14, true,
  390. typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldTimeUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerTimeUpdate), PlayerLoopTiming.TimeUpdate);
  391. InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.TimeUpdate),
  392. InjectPlayerLoopTimings.LastTimeUpdate, 15, false,
  393. typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldTimeUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastTimeUpdate), PlayerLoopTiming.LastTimeUpdate);
  394. #endif
  395. // Insert UniTaskSynchronizationContext to Update loop
  396. var i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.Update));
  397. copyList[i].subSystemList = InsertUniTaskSynchronizationContext(copyList[i]);
  398. playerLoop.subSystemList = copyList;
  399. PlayerLoop.SetPlayerLoop(playerLoop);
  400. }
  401. public static void AddAction(PlayerLoopTiming timing, IPlayerLoopItem action)
  402. {
  403. var runner = runners[(int)timing];
  404. if (runner == null)
  405. {
  406. ThrowInvalidLoopTiming(timing);
  407. }
  408. runner.AddAction(action);
  409. }
  410. static void ThrowInvalidLoopTiming(PlayerLoopTiming playerLoopTiming)
  411. {
  412. throw new InvalidOperationException("Target playerLoopTiming is not injected. Please check PlayerLoopHelper.Initialize. PlayerLoopTiming:" + playerLoopTiming);
  413. }
  414. public static void AddContinuation(PlayerLoopTiming timing, Action continuation)
  415. {
  416. var q = yielders[(int)timing];
  417. if (q == null)
  418. {
  419. ThrowInvalidLoopTiming(timing);
  420. }
  421. q.Enqueue(continuation);
  422. }
  423. // Diagnostics helper
  424. #if UNITY_2019_3_OR_NEWER
  425. public static void DumpCurrentPlayerLoop()
  426. {
  427. var playerLoop = UnityEngine.LowLevel.PlayerLoop.GetCurrentPlayerLoop();
  428. var sb = new System.Text.StringBuilder();
  429. sb.AppendLine($"PlayerLoop List");
  430. foreach (var header in playerLoop.subSystemList)
  431. {
  432. sb.AppendFormat("------{0}------", header.type.Name);
  433. sb.AppendLine();
  434. if (header.subSystemList is null)
  435. {
  436. sb.AppendFormat("{0} has no subsystems!", header.ToString());
  437. sb.AppendLine();
  438. continue;
  439. }
  440. foreach (var subSystem in header.subSystemList)
  441. {
  442. sb.AppendFormat("{0}", subSystem.type.Name);
  443. sb.AppendLine();
  444. if (subSystem.subSystemList != null)
  445. {
  446. UnityEngine.Debug.LogWarning("More Subsystem:" + subSystem.subSystemList.Length);
  447. }
  448. }
  449. }
  450. UnityEngine.Debug.Log(sb.ToString());
  451. }
  452. public static bool IsInjectedUniTaskPlayerLoop()
  453. {
  454. var playerLoop = UnityEngine.LowLevel.PlayerLoop.GetCurrentPlayerLoop();
  455. foreach (var header in playerLoop.subSystemList)
  456. {
  457. if (header.subSystemList is null)
  458. {
  459. continue;
  460. }
  461. foreach (var subSystem in header.subSystemList)
  462. {
  463. if (subSystem.type == typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization))
  464. {
  465. return true;
  466. }
  467. }
  468. }
  469. return false;
  470. }
  471. #endif
  472. }
  473. }