Add UniTask.WaitForEndOfFrame(MonoBehaviour), Obsolete no args.
parent
5c668717d8
commit
3c99010ba0
|
@ -86,8 +86,8 @@ async UniTask<string> DemoAsync()
|
|||
await UniTask.Yield();
|
||||
await UniTask.NextFrame();
|
||||
|
||||
// replacement of WaitForEndOfFrame(same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate))
|
||||
await UniTask.WaitForEndOfFrame();
|
||||
// replacement of WaitForEndOfFrame(requires MonoBehaviour(CoroutineRunner))
|
||||
await UniTask.WaitForEndOfFrame(this); // this is MonoBehaviour
|
||||
|
||||
// replacement of yield return new WaitForFixedUpdate(same as UniTask.Yield(PlayerLoopTiming.FixedUpdate))
|
||||
await UniTask.WaitForFixedUpdate();
|
||||
|
@ -496,9 +496,9 @@ public enum PlayerLoopTiming
|
|||
|
||||
It indicates when to run, you can check [PlayerLoopList.md](https://gist.github.com/neuecc/bc3a1cfd4d74501ad057e49efcd7bdae) to Unity's default playerloop and injected UniTask's custom loop.
|
||||
|
||||
`PlayerLoopTiming.Update` is similar to `yield return null` in a coroutine, but it is called before Update(Update and uGUI events(button.onClick, etc...) are called on `ScriptRunBehaviourUpdate`, yield return null is called on `ScriptRunDelayedDynamicFrameRate`). `PlayerLoopTiming.FixedUpdate` is similar to `WaitForFixedUpdate`, `PlayerLoopTiming.LastPostLateUpdate` is similar to `WaitForEndOfFrame` in coroutine.
|
||||
`PlayerLoopTiming.Update` is similar to `yield return null` in a coroutine, but it is called before Update(Update and uGUI events(button.onClick, etc...) are called on `ScriptRunBehaviourUpdate`, yield return null is called on `ScriptRunDelayedDynamicFrameRate`). `PlayerLoopTiming.FixedUpdate` is similar to `WaitForFixedUpdate`.
|
||||
|
||||
> `await UniTask.WaitForEndOfFrame()` is not equivalent to coroutine's `yield return new WaitForEndOfFrame()`. Coroutine's WaitForEndOfFrame seems to run after the PlayerLoop is done. Some methods that require coroutine's end of frame(`ScreenCapture.CaptureScreenshotAsTexture`, `CommandBuffer`, etc) do not work correctly when replaced with async/await. In these cases, use a coroutine instead.
|
||||
> `PlayerLoopTiming.LastPostLateUpdate` is not equivalent to coroutine's `yield return new WaitForEndOfFrame()`. Coroutine's WaitForEndOfFrame seems to run after the PlayerLoop is done. Some methods that require coroutine's end of frame(`Texture2D.ReadPixels`, `ScreenCapture.CaptureScreenshotAsTexture`, `CommandBuffer`, etc) do not work correctly when replaced with async/await. In these cases, pass MonoBehaviour(coroutine runnner) to `UniTask.WaitForEndOfFrame`. For example, `await UniTask.WaitForEndOfFrame(this);` is lightweight allocation free alternative of `yield return new WaitForEndOfFrame()`.
|
||||
|
||||
`yield return null` and `UniTask.Yield` are similar but different. `yield return null` always returns next frame but `UniTask.Yield` returns next called. That is, call `UniTask.Yield(PlayerLoopTiming.Update)` on `PreUpdate`, it returns same frame. `UniTask.NextFrame()` guarantees return next frame, you can expect this to behave exactly the same as `yield return null`.
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
|
@ -74,23 +75,24 @@ namespace Cysharp.Threading.Tasks
|
|||
return new UniTask(NextFramePromise.Create(timing, cancellationToken, out var token), token);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate).
|
||||
/// </summary>
|
||||
[Obsolete("Use WaitForEndOfFrame(MonoBehaviour) instead or UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate). Equivalent for coroutine's WaitForEndOfFrame requires MonoBehaviour(runner of Coroutine).")]
|
||||
public static YieldAwaitable WaitForEndOfFrame()
|
||||
{
|
||||
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken).
|
||||
/// </summary>
|
||||
[Obsolete("Use WaitForEndOfFrame(MonoBehaviour) instead or UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate). Equivalent for coroutine's WaitForEndOfFrame requires MonoBehaviour(runner of Coroutine).")]
|
||||
public static UniTask WaitForEndOfFrame(CancellationToken cancellationToken)
|
||||
{
|
||||
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken);
|
||||
}
|
||||
|
||||
public static UniTask WaitForEndOfFrame(MonoBehaviour coroutineRunner, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var source = WaitForEndOfFramePromise.Create(coroutineRunner, cancellationToken, out var token);
|
||||
return new UniTask(source, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as UniTask.Yield(PlayerLoopTiming.FixedUpdate).
|
||||
/// </summary>
|
||||
|
@ -352,6 +354,113 @@ namespace Cysharp.Threading.Tasks
|
|||
}
|
||||
}
|
||||
|
||||
sealed class WaitForEndOfFramePromise : IUniTaskSource, ITaskPoolNode<WaitForEndOfFramePromise>, System.Collections.IEnumerator
|
||||
{
|
||||
static TaskPool<WaitForEndOfFramePromise> pool;
|
||||
WaitForEndOfFramePromise nextNode;
|
||||
public ref WaitForEndOfFramePromise NextNode => ref nextNode;
|
||||
|
||||
static WaitForEndOfFramePromise()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(WaitForEndOfFramePromise), () => pool.Size);
|
||||
}
|
||||
|
||||
CancellationToken cancellationToken;
|
||||
UniTaskCompletionSourceCore<object> core;
|
||||
|
||||
WaitForEndOfFramePromise()
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(MonoBehaviour coroutineRunner, CancellationToken cancellationToken, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new WaitForEndOfFramePromise();
|
||||
}
|
||||
|
||||
result.cancellationToken = cancellationToken;
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
coroutineRunner.StartCoroutine(result);
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
Reset(); // Reset Enumerator
|
||||
cancellationToken = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
|
||||
// Coroutine Runner implementation
|
||||
|
||||
static readonly WaitForEndOfFrame waitForEndOfFrameYieldInstruction = new WaitForEndOfFrame();
|
||||
bool isFirst = true;
|
||||
|
||||
object IEnumerator.Current => waitForEndOfFrameYieldInstruction;
|
||||
|
||||
bool IEnumerator.MoveNext()
|
||||
{
|
||||
if (isFirst)
|
||||
{
|
||||
isFirst = false;
|
||||
return true; // start WaitForEndOfFrame
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
core.TrySetResult(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
isFirst = true;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class DelayFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayFramePromise>
|
||||
{
|
||||
static TaskPool<DelayFramePromise> pool;
|
||||
|
|
|
@ -543,7 +543,7 @@ public class SandboxMain : MonoBehaviour
|
|||
Debug.LogError(e);
|
||||
return;
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Debug.Log("TestAsync Finished.");
|
||||
|
@ -555,6 +555,7 @@ public class SandboxMain : MonoBehaviour
|
|||
async UniTaskVoid Start()
|
||||
{
|
||||
|
||||
|
||||
// UniTask.Delay(TimeSpan.FromSeconds(1)).TimeoutWithoutException
|
||||
|
||||
|
||||
|
@ -562,61 +563,35 @@ public class SandboxMain : MonoBehaviour
|
|||
PlayerLoopHelper.Initialize(ref currentLoop, InjectPlayerLoopTimings.Minimum); // minimum is Update | FixedUpdate | LastPostLateUpdate
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var cancelToken = new CancellationTokenSource();
|
||||
cancelButton.onClick.AddListener(()=>
|
||||
{
|
||||
cancelToken.Cancel(); // cancel from button click.
|
||||
});
|
||||
|
||||
var timeoutToken = new CancellationTokenSource();
|
||||
timeoutToken.CancelAfterSlim(TimeSpan.FromSeconds(5)); // 5sec timeout.
|
||||
|
||||
try
|
||||
{
|
||||
// combine token
|
||||
var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancelToken.Token, timeoutToken.Token);
|
||||
|
||||
await UnityWebRequest.Get("http://foo").SendWebRequest().WithCancellation(linkedTokenSource.Token);
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
if (timeoutToken.IsCancellationRequested)
|
||||
{
|
||||
UnityEngine.Debug.Log("Timeout.");
|
||||
}
|
||||
else if (cancelToken.IsCancellationRequested)
|
||||
{
|
||||
UnityEngine.Debug.Log("Cancel clicked.");
|
||||
}
|
||||
_ = ex;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// TestAsync(cts.Token).Forget();
|
||||
|
||||
okButton.onClick.AddListener(UniTask.UnityAction(async () =>
|
||||
{
|
||||
// try timeout
|
||||
try
|
||||
{
|
||||
//await UniTask.Delay(TimeSpan.FromSeconds(2), cancellationToken: timeoutController.Timeout(TimeSpan.FromSeconds(3)));
|
||||
UnityEngine.Debug.Log("Delay Complete, Reset(and reuse).");
|
||||
//timeoutController.Reset();
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
//UnityEngine.Debug.Log("Timeout! FromTimeout?:" + timeoutController.IsTimeout());
|
||||
_ = ex;
|
||||
}
|
||||
await UniTask.WaitForEndOfFrame(this);
|
||||
var texture = new Texture2D(Screen.width, Screen.height);
|
||||
texture.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
|
||||
texture.Apply();
|
||||
|
||||
await UniTask.Yield();
|
||||
var jpg = texture.EncodeToJPG();
|
||||
File.WriteAllBytes("testscreencapture.jpg", jpg);
|
||||
Debug.Log("ok?");
|
||||
|
||||
//var texture = ScreenCapture.CaptureScreenshotAsTexture();
|
||||
//if (texture == null)
|
||||
//{
|
||||
// Debug.Log("fail");
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// var jpg = texture.EncodeToJPG();
|
||||
// File.WriteAllBytes("testscreencapture.jpg", jpg);
|
||||
// Debug.Log("ok?");
|
||||
//}
|
||||
}));
|
||||
|
||||
cancelButton.onClick.AddListener(UniTask.UnityAction(async () =>
|
||||
|
|
Loading…
Reference in New Issue