OperationHandleBaseExtensions.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. #if UNITY_2020_1_OR_NEWER && ! UNITY_2021
  2. #define UNITY_2020_BUG
  3. #endif
  4. using System;
  5. using System.Runtime.CompilerServices;
  6. using YooAsset;
  7. using static Cysharp.Threading.Tasks.Internal.Error;
  8. namespace Cysharp.Threading.Tasks
  9. {
  10. public static class OperationHandleBaseExtensions
  11. {
  12. public static UniTask.Awaiter GetAwaiter(this OperationHandleBase handle)
  13. {
  14. return ToUniTask(handle).GetAwaiter();
  15. }
  16. public static UniTask ToUniTask(this OperationHandleBase handle,
  17. IProgress<float> progress = null,
  18. PlayerLoopTiming timing = PlayerLoopTiming.Update)
  19. {
  20. ThrowArgumentNullException(handle, nameof(handle));
  21. if(!handle.IsValid)
  22. {
  23. return UniTask.CompletedTask;
  24. }
  25. return new UniTask(
  26. OperationHandleBaserConfiguredSource.Create(
  27. handle,
  28. timing,
  29. progress,
  30. out var token
  31. ),
  32. token
  33. );
  34. }
  35. sealed class OperationHandleBaserConfiguredSource : IUniTaskSource,
  36. IPlayerLoopItem,
  37. ITaskPoolNode<OperationHandleBaserConfiguredSource>
  38. {
  39. private static TaskPool<OperationHandleBaserConfiguredSource> pool;
  40. private OperationHandleBaserConfiguredSource nextNode;
  41. public ref OperationHandleBaserConfiguredSource NextNode => ref nextNode;
  42. static OperationHandleBaserConfiguredSource()
  43. {
  44. TaskPool.RegisterSizeGetter(typeof(OperationHandleBaserConfiguredSource), () => pool.Size);
  45. }
  46. private readonly Action<OperationHandleBase> continuationAction;
  47. private OperationHandleBase handle;
  48. private IProgress<float> progress;
  49. private bool completed;
  50. private UniTaskCompletionSourceCore<AsyncUnit> core;
  51. OperationHandleBaserConfiguredSource() { continuationAction = Continuation; }
  52. public static IUniTaskSource Create(OperationHandleBase handle,
  53. PlayerLoopTiming timing,
  54. IProgress<float> progress,
  55. out short token)
  56. {
  57. if(!pool.TryPop(out var result))
  58. {
  59. result = new OperationHandleBaserConfiguredSource();
  60. }
  61. result.handle = handle;
  62. result.progress = progress;
  63. result.completed = false;
  64. TaskTracker.TrackActiveTask(result, 3);
  65. if(progress != null)
  66. {
  67. PlayerLoopHelper.AddAction(timing, result);
  68. }
  69. // BUG 在 Unity 2020.3.36 版本测试中, IL2Cpp 会报 如下错误
  70. // BUG ArgumentException: Incompatible Delegate Types. First is System.Action`1[[YooAsset.AssetOperationHandle, YooAsset, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]] second is System.Action`1[[YooAsset.OperationHandleBase, YooAsset, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]
  71. // BUG 也可能报的是 Action '1' Action '1' 的 InvalidCastException
  72. // BUG 此处不得不这么修改, 如果后续 Unity 修复了这个问题, 可以恢复之前的写法
  73. #if UNITY_2020_BUG
  74. switch(handle)
  75. {
  76. case AssetOperationHandle asset_handle:
  77. asset_handle.Completed += result.AssetContinuation;
  78. break;
  79. case SceneOperationHandle scene_handle:
  80. scene_handle.Completed += result.SceneContinuation;
  81. break;
  82. case SubAssetsOperationHandle sub_asset_handle:
  83. sub_asset_handle.Completed += result.SubContinuation;
  84. break;
  85. }
  86. #else
  87. switch(handle)
  88. {
  89. case AssetOperationHandle asset_handle:
  90. asset_handle.Completed += result.continuationAction;
  91. break;
  92. case SceneOperationHandle scene_handle:
  93. scene_handle.Completed += result.continuationAction;
  94. break;
  95. case SubAssetsOperationHandle sub_asset_handle:
  96. sub_asset_handle.Completed += result.continuationAction;
  97. break;
  98. }
  99. #endif
  100. token = result.core.Version;
  101. return result;
  102. }
  103. #if UNITY_2020_BUG
  104. private void AssetContinuation(AssetOperationHandle handle)
  105. {
  106. handle.Completed -= AssetContinuation;
  107. BaseContinuation();
  108. }
  109. private void SceneContinuation(SceneOperationHandle handle)
  110. {
  111. handle.Completed -= SceneContinuation;
  112. BaseContinuation();
  113. }
  114. private void SubContinuation(SubAssetsOperationHandle handle)
  115. {
  116. handle.Completed -= SubContinuation;
  117. BaseContinuation();
  118. }
  119. #endif
  120. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  121. private void BaseContinuation()
  122. {
  123. if(completed)
  124. {
  125. TryReturn();
  126. }
  127. else
  128. {
  129. completed = true;
  130. if(handle.Status == EOperationStatus.Failed)
  131. {
  132. core.TrySetException(new Exception(handle.LastError));
  133. }
  134. else
  135. {
  136. core.TrySetResult(AsyncUnit.Default);
  137. }
  138. }
  139. }
  140. private void Continuation(OperationHandleBase _)
  141. {
  142. switch(handle)
  143. {
  144. case AssetOperationHandle asset_handle:
  145. asset_handle.Completed -= continuationAction;
  146. break;
  147. case SceneOperationHandle scene_handle:
  148. scene_handle.Completed -= continuationAction;
  149. break;
  150. case SubAssetsOperationHandle sub_asset_handle:
  151. sub_asset_handle.Completed -= continuationAction;
  152. break;
  153. }
  154. BaseContinuation();
  155. }
  156. bool TryReturn()
  157. {
  158. TaskTracker.RemoveTracking(this);
  159. core.Reset();
  160. handle = default;
  161. progress = default;
  162. return pool.TryPush(this);
  163. }
  164. public UniTaskStatus GetStatus(short token) => core.GetStatus(token);
  165. public void OnCompleted(Action<object> continuation, object state, short token)
  166. {
  167. core.OnCompleted(continuation, state, token);
  168. }
  169. public void GetResult(short token) { core.GetResult(token); }
  170. public UniTaskStatus UnsafeGetStatus() => core.UnsafeGetStatus();
  171. public bool MoveNext()
  172. {
  173. if(completed)
  174. {
  175. TryReturn();
  176. return false;
  177. }
  178. if(handle.IsValid)
  179. {
  180. progress?.Report(handle.Progress);
  181. }
  182. return true;
  183. }
  184. }
  185. }
  186. }