Add UniTask.WaitForEndOfFrame(MonoBehaviour), Obsolete no args.
parent
5c668717d8
commit
3c99010ba0
|
@ -86,8 +86,8 @@ async UniTask<string> DemoAsync()
|
||||||
await UniTask.Yield();
|
await UniTask.Yield();
|
||||||
await UniTask.NextFrame();
|
await UniTask.NextFrame();
|
||||||
|
|
||||||
// replacement of WaitForEndOfFrame(same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate))
|
// replacement of WaitForEndOfFrame(requires MonoBehaviour(CoroutineRunner))
|
||||||
await UniTask.WaitForEndOfFrame();
|
await UniTask.WaitForEndOfFrame(this); // this is MonoBehaviour
|
||||||
|
|
||||||
// replacement of yield return new WaitForFixedUpdate(same as UniTask.Yield(PlayerLoopTiming.FixedUpdate))
|
// replacement of yield return new WaitForFixedUpdate(same as UniTask.Yield(PlayerLoopTiming.FixedUpdate))
|
||||||
await UniTask.WaitForFixedUpdate();
|
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.
|
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`.
|
`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 Cysharp.Threading.Tasks.Internal;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
@ -74,23 +75,24 @@ namespace Cysharp.Threading.Tasks
|
||||||
return new UniTask(NextFramePromise.Create(timing, cancellationToken, out var token), token);
|
return new UniTask(NextFramePromise.Create(timing, cancellationToken, out var token), token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("Use WaitForEndOfFrame(MonoBehaviour) instead or UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate). Equivalent for coroutine's WaitForEndOfFrame requires MonoBehaviour(runner of Coroutine).")]
|
||||||
/// <summary>
|
|
||||||
/// Same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate).
|
|
||||||
/// </summary>
|
|
||||||
public static YieldAwaitable WaitForEndOfFrame()
|
public static YieldAwaitable WaitForEndOfFrame()
|
||||||
{
|
{
|
||||||
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate);
|
return 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).")]
|
||||||
/// Same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken).
|
|
||||||
/// </summary>
|
|
||||||
public static UniTask WaitForEndOfFrame(CancellationToken cancellationToken)
|
public static UniTask WaitForEndOfFrame(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, 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>
|
/// <summary>
|
||||||
/// Same as UniTask.Yield(PlayerLoopTiming.FixedUpdate).
|
/// Same as UniTask.Yield(PlayerLoopTiming.FixedUpdate).
|
||||||
/// </summary>
|
/// </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>
|
sealed class DelayFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayFramePromise>
|
||||||
{
|
{
|
||||||
static TaskPool<DelayFramePromise> pool;
|
static TaskPool<DelayFramePromise> pool;
|
||||||
|
|
|
@ -543,7 +543,7 @@ public class SandboxMain : MonoBehaviour
|
||||||
Debug.LogError(e);
|
Debug.LogError(e);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Debug.Log("TestAsync Finished.");
|
Debug.Log("TestAsync Finished.");
|
||||||
|
@ -555,6 +555,7 @@ public class SandboxMain : MonoBehaviour
|
||||||
async UniTaskVoid Start()
|
async UniTaskVoid Start()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
// UniTask.Delay(TimeSpan.FromSeconds(1)).TimeoutWithoutException
|
// 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
|
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();
|
// TestAsync(cts.Token).Forget();
|
||||||
|
|
||||||
okButton.onClick.AddListener(UniTask.UnityAction(async () =>
|
okButton.onClick.AddListener(UniTask.UnityAction(async () =>
|
||||||
{
|
{
|
||||||
// try timeout
|
await UniTask.WaitForEndOfFrame(this);
|
||||||
try
|
var texture = new Texture2D(Screen.width, Screen.height);
|
||||||
{
|
texture.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
|
||||||
//await UniTask.Delay(TimeSpan.FromSeconds(2), cancellationToken: timeoutController.Timeout(TimeSpan.FromSeconds(3)));
|
texture.Apply();
|
||||||
UnityEngine.Debug.Log("Delay Complete, Reset(and reuse).");
|
|
||||||
//timeoutController.Reset();
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException ex)
|
|
||||||
{
|
|
||||||
//UnityEngine.Debug.Log("Timeout! FromTimeout?:" + timeoutController.IsTimeout());
|
|
||||||
_ = ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 () =>
|
cancelButton.onClick.AddListener(UniTask.UnityAction(async () =>
|
||||||
|
|
Loading…
Reference in New Issue