Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ public void Run()
case PlayerLoopTiming.LastPostLateUpdate:
LastPostLateUpdate();
break;
case PlayerLoopTiming.ManualUpdate:
ManualUpdate();
break;
#if UNITY_2020_2_OR_NEWER
case PlayerLoopTiming.TimeUpdate:
TimeUpdate();
Expand Down Expand Up @@ -148,6 +151,7 @@ public void Run()
void LastPreLateUpdate() => RunCore();
void PostLateUpdate() => RunCore();
void LastPostLateUpdate() => RunCore();
void ManualUpdate() => RunCore();
#if UNITY_2020_2_OR_NEWER
void TimeUpdate() => RunCore();
void LastTimeUpdate() => RunCore();
Expand Down Expand Up @@ -178,7 +182,7 @@ void RunCore()
}
else
{
continue; // next i
continue; // next i
}
}
catch (Exception ex)
Expand Down
25 changes: 19 additions & 6 deletions src/UniTask/Assets/Plugins/UniTask/Runtime/PlayerLoopHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public enum PlayerLoopTiming
TimeUpdate = 14,
LastTimeUpdate = 15,
#endif
ManualUpdate = 16,
}

[Flags]
Expand Down Expand Up @@ -192,6 +193,7 @@ public static class PlayerLoopHelper
static SynchronizationContext unitySynchronizationContext;
static ContinuationQueue[] yielders;
static PlayerLoopRunner[] runners;
static PlayerLoopRunner ManualRunner;
internal static bool IsEditorApplicationQuitting { get; private set; }
static PlayerLoopSystem[] InsertRunner(PlayerLoopSystem loopSystem,
bool injectOnFirst,
Expand Down Expand Up @@ -251,6 +253,10 @@ static PlayerLoopSystem[] InsertRunner(PlayerLoopSystem loopSystem,

return dest;
}
public static void ManualUpdate()
{
ManualRunner.Run();
}

static PlayerLoopSystem[] RemoveRunner(PlayerLoopSystem loopSystem, Type loopRunnerYieldType, Type loopRunnerType)
{
Expand Down Expand Up @@ -302,7 +308,7 @@ static void Init()
catch { }

#if UNITY_EDITOR && UNITY_2019_3_OR_NEWER
// When domain reload is disabled, re-initialization is required when entering play mode;
// When domain reload is disabled, re-initialization is required when entering play mode;
// otherwise, pending tasks will leak between play mode sessions.
var domainReloadDisabled = UnityEditor.EditorSettings.enterPlayModeOptionsEnabled &&
UnityEditor.EditorSettings.enterPlayModeOptions.HasFlag(UnityEditor.EnterPlayModeOptions.DisableDomainReload);
Expand Down Expand Up @@ -405,6 +411,8 @@ public static void Initialize(ref PlayerLoopSystem playerLoop, InjectPlayerLoopT
runners = new PlayerLoopRunner[14];
#endif

ManualRunner = new PlayerLoopRunner(PlayerLoopTiming.ManualUpdate);

var copyList = playerLoop.subSystemList.ToArray();

// Initialization
Expand Down Expand Up @@ -491,6 +499,11 @@ public static void Initialize(ref PlayerLoopSystem playerLoop, InjectPlayerLoopT

public static void AddAction(PlayerLoopTiming timing, IPlayerLoopItem action)
{
if ((int)timing == (int)PlayerLoopTiming.ManualUpdate)
{
ManualRunner.AddAction(action);
return;
}
var runner = runners[(int)timing];
if (runner == null)
{
Expand Down Expand Up @@ -528,8 +541,8 @@ public static void DumpCurrentPlayerLoop()
{
sb.AppendFormat("------{0}------", header.type.Name);
sb.AppendLine();
if (header.subSystemList is null)

if (header.subSystemList is null)
{
sb.AppendFormat("{0} has no subsystems!", header.ToString());
sb.AppendLine();
Expand Down Expand Up @@ -557,11 +570,11 @@ public static bool IsInjectedUniTaskPlayerLoop()

foreach (var header in playerLoop.subSystemList)
{
if (header.subSystemList is null)
{
if (header.subSystemList is null)
{
continue;
}

foreach (var subSystem in header.subSystemList)
{
if (subSystem.type == typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization))
Expand Down
153 changes: 147 additions & 6 deletions src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.Delay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ public enum DelayType
/// <summary>Ignore timescale, use Time.unscaledDeltaTime.</summary>
UnscaledDeltaTime,
/// <summary>use Stopwatch.GetTimestamp().</summary>
Realtime
Realtime,
ManualTime
}

public partial struct UniTask
{
public static float deltaTime;
public static int frameCount;
public static YieldAwaitable Yield()
{
// optimized for single continuation
Expand Down Expand Up @@ -80,7 +83,7 @@ public static async UniTask WaitForEndOfFrame(CancellationToken cancellationToke
{
await Awaitable.EndOfFrameAsync(cancellationToken);
}
#else
#else
[Obsolete("Use WaitForEndOfFrame(MonoBehaviour) instead or UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate). Equivalent for coroutine's WaitForEndOfFrame requires MonoBehaviour(runner of Coroutine).")]
public static YieldAwaitable WaitForEndOfFrame()
{
Expand All @@ -92,7 +95,7 @@ public static UniTask WaitForEndOfFrame(CancellationToken cancellationToken, boo
{
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken, cancelImmediately);
}
#endif
#endif

public static UniTask WaitForEndOfFrame(MonoBehaviour coroutineRunner)
{
Expand Down Expand Up @@ -179,6 +182,10 @@ public static UniTask WaitForFixedUpdate(CancellationToken cancellationToken, bo

switch (delayType)
{
case DelayType.ManualTime:
{
return new UniTask(DelayManualPromise.Create(delayTimeSpan, delayTiming, cancellationToken, cancelImmediately, out var token), token);
}
case DelayType.UnscaledDeltaTime:
{
return new UniTask(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, cancelImmediately, out var token), token);
Expand Down Expand Up @@ -229,7 +236,7 @@ public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken c

result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;

if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
Expand Down Expand Up @@ -320,6 +327,7 @@ static NextFramePromise()
CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
PlayerLoopTiming timing = PlayerLoopTiming.Update;

NextFramePromise()
{
Expand All @@ -337,8 +345,9 @@ public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken c
result = new NextFramePromise();
}

result.frameCount = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
result.frameCount = PlayerLoopHelper.IsMainThread ?(timing == PlayerLoopTiming.ManualUpdate ? UniTask.frameCount : Time.frameCount) : -1;
result.cancellationToken = cancellationToken;
result.timing = timing;
result.cancelImmediately = cancelImmediately;

if (cancelImmediately && cancellationToken.CanBeCanceled)
Expand Down Expand Up @@ -400,7 +409,7 @@ public bool MoveNext()
return false;
}

if (frameCount == Time.frameCount)
if (frameCount == (timing == PlayerLoopTiming.ManualUpdate ? UniTask.frameCount : Time.frameCount))
{
return true;
}
Expand Down Expand Up @@ -695,6 +704,138 @@ bool TryReturn()
}
}

sealed class DelayManualPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayManualPromise>
{
static TaskPool<DelayManualPromise> pool;
DelayManualPromise nextNode;
public ref DelayManualPromise NextNode => ref nextNode;

static DelayManualPromise()
{
TaskPool.RegisterSizeGetter(typeof(DelayManualPromise), () => pool.Size);
}

int initialFrame;
float delayTimeSpan;
float elapsed;
CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;

UniTaskCompletionSourceCore<object> core;

DelayManualPromise()
{
}

public static IUniTaskSource Create(TimeSpan delayTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}

if (!pool.TryPop(out var result))
{
result = new DelayManualPromise();
}

result.elapsed = 0.0f;
result.delayTimeSpan = (float)delayTimeSpan.TotalSeconds;
result.cancellationToken = cancellationToken;
result.initialFrame = PlayerLoopHelper.IsMainThread ? frameCount : -1;
result.cancelImmediately = cancelImmediately;

if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (DelayManualPromise)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}

TaskTracker.TrackActiveTask(result, 3);

PlayerLoopHelper.AddAction(timing, result);

token = result.core.Version;
return result;
}

public void GetResult(short token)
{
try
{
core.GetResult(token);
}
finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{
TryReturn();
}
else
{
TaskTracker.RemoveTracking(this);
}
}
}

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);
}

public bool MoveNext()
{
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return false;
}

if (elapsed == 0.0f)
{
if (initialFrame == Time.frameCount)
{
return true;
}
}

elapsed += Time.deltaTime;
if (elapsed >= delayTimeSpan)
{
core.TrySetResult(null);
return false;
}

return true;
}

bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
delayTimeSpan = default;
elapsed = default;
cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this);
}
}

sealed class DelayPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayPromise>
{
static TaskPool<DelayPromise> pool;
Expand Down