Skip to content

Commit

Permalink
Add PauseProcess timing
Browse files Browse the repository at this point in the history
  • Loading branch information
Atlinx committed Mar 27, 2024
1 parent c83e8c6 commit 64eb327
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 60 deletions.
66 changes: 32 additions & 34 deletions addons/GDTask/Autoload/GDTaskPlayerLoopAutoload.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
using System;
using System.Linq;
using Fractural.Tasks.Internal;
using System.Threading;
using Fractural.Tasks.Internal;
using Godot;
using System;

namespace Fractural.Tasks
{
Expand All @@ -16,31 +14,8 @@ public enum PlayerLoopTiming
{
Process = 0,
PhysicsProcess = 1,
}

[Flags]
public enum InjectPlayerLoopTimings
{
/// <summary>
/// Preset: All loops(default).
/// </summary>
All = Process | PhysicsProcess,

/// <summary>
/// Preset: All without last except LastPostLateUpdate.
/// </summary>
Standard = Process | PhysicsProcess,

/// <summary>
/// Preset: Minimum pattern, Update | PhysicsProcess | LastPostLateUpdate
/// </summary>
Minimum =
Process | PhysicsProcess,

// PlayerLoopTiming

PhysicsProcess = 1,
Process = 2,
PauseProcess = 2,
PausePhysicsProcess = 3,
}

public interface IPlayerLoopItem
Expand Down Expand Up @@ -85,7 +60,7 @@ public static GDTaskPlayerLoopAutoload Global
get
{
if (s_Global != null) return s_Global;

var newInstance = new GDTaskPlayerLoopAutoload();
newInstance.Initialize();
var currentScene = ((SceneTree)Engine.GetMainLoop()).CurrentScene;
Expand All @@ -104,6 +79,7 @@ public static GDTaskPlayerLoopAutoload Global
private int mainThreadId;
private ContinuationQueue[] yielders;
private PlayerLoopRunner[] runners;
private ProcessListener processListener;

public override void _Ready()
{
Expand All @@ -118,15 +94,25 @@ public override void _Ready()

private void Initialize()
{
ProcessMode = ProcessModeEnum.Pausable;
mainThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId;
yielders = new[] {
new ContinuationQueue(PlayerLoopTiming.Process),
new ContinuationQueue(PlayerLoopTiming.PhysicsProcess),
new ContinuationQueue(PlayerLoopTiming.PauseProcess),
new ContinuationQueue(PlayerLoopTiming.PausePhysicsProcess),
};
runners = new[] {
new PlayerLoopRunner(PlayerLoopTiming.Process),
new PlayerLoopRunner(PlayerLoopTiming.PhysicsProcess),
new PlayerLoopRunner(PlayerLoopTiming.PauseProcess),
new PlayerLoopRunner(PlayerLoopTiming.PausePhysicsProcess),
};
processListener = new ProcessListener();
AddChild(processListener);
processListener.ProcessMode = ProcessModeEnum.Always;
processListener.OnProcess += PauseProcess;
processListener.OnPhysicsProcess += PausePhysicsProcess;
}

public override void _Notification(int what)
Expand All @@ -147,14 +133,26 @@ public override void _Notification(int what)

public override void _Process(double delta)
{
yielders[(int) PlayerLoopTiming.Process].Run();
runners[(int) PlayerLoopTiming.Process].Run();
yielders[(int)PlayerLoopTiming.Process].Run();
runners[(int)PlayerLoopTiming.Process].Run();
}

public override void _PhysicsProcess(double delta)
{
yielders[(int) PlayerLoopTiming.PhysicsProcess].Run();
runners[(int) PlayerLoopTiming.PhysicsProcess].Run();
yielders[(int)PlayerLoopTiming.PhysicsProcess].Run();
runners[(int)PlayerLoopTiming.PhysicsProcess].Run();
}

private void PauseProcess(double delta)
{
yielders[(int)PlayerLoopTiming.PauseProcess].Run();
runners[(int)PlayerLoopTiming.PauseProcess].Run();
}

private void PausePhysicsProcess(double delta)
{
yielders[(int)PlayerLoopTiming.PausePhysicsProcess].Run();
runners[(int)PlayerLoopTiming.PausePhysicsProcess].Run();
}
}
}
Expand Down
21 changes: 21 additions & 0 deletions addons/GDTask/Autoload/ProcessListener.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Godot;
using System;

namespace Fractural.Tasks
{
public partial class ProcessListener : Node
{
public event Action<double> OnProcess;
public event Action<double> OnPhysicsProcess;

public override void _Process(double delta)
{
OnProcess?.Invoke(delta);
}

public override void _PhysicsProcess(double delta)
{
OnPhysicsProcess?.Invoke(delta);
}
}
}
12 changes: 5 additions & 7 deletions addons/GDTask/GDTask.Delay.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
using Fractural.Tasks.Internal;
using Godot;
using System;
using System.Collections;
using System.Runtime.CompilerServices;
using System.Threading;
using Godot;

namespace Fractural.Tasks
{
Expand Down Expand Up @@ -82,15 +81,15 @@ public static GDTask WaitForEndOfFrame(CancellationToken cancellationToken)
}

/// <summary>
/// Same as GDTask.Yield(PlayerLoopTiming.LastPhysicsProcess).
/// Same as GDTask.Yield(PlayerLoopTiming.PhysicsProcess).
/// </summary>
public static YieldAwaitable WaitForPhysicsProcess()
{
return GDTask.Yield(PlayerLoopTiming.PhysicsProcess);
}

/// <summary>
/// Same as GDTask.Yield(PlayerLoopTiming.LastPhysicsProcess, cancellationToken).
/// Same as GDTask.Yield(PlayerLoopTiming.PhysicsProcess, cancellationToken).
/// </summary>
public static GDTask WaitForPhysicsProcess(CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -138,7 +137,6 @@ public static GDTask WaitForPhysicsProcess(CancellationToken cancellationToken)
delayType = DelayType.Realtime;
}
#endif

switch (delayType)
{
case DelayType.Realtime:
Expand Down Expand Up @@ -566,11 +564,11 @@ public bool MoveNext()
}
}

if (timing == PlayerLoopTiming.Process)
if (timing == PlayerLoopTiming.Process || timing == PlayerLoopTiming.PauseProcess)
elapsed += GDTaskPlayerLoopAutoload.Global.DeltaTime;
else
elapsed += GDTaskPlayerLoopAutoload.Global.PhysicsDeltaTime;

if (elapsed >= delayTimeSpan)
{
core.TrySetResult(null);
Expand Down
8 changes: 7 additions & 1 deletion addons/GDTask/Internal/ContinuationQueue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,11 @@ public void Run()
case PlayerLoopTiming.Process:
Process();
break;
default:
case PlayerLoopTiming.PausePhysicsProcess:
PausePhysicsProcess();
break;
case PlayerLoopTiming.PauseProcess:
PauseProcess();
break;
}
#else
Expand All @@ -105,6 +109,8 @@ public void Run()

void PhysicsProcess() => RunCore();
void Process() => RunCore();
void PausePhysicsProcess() => RunCore();
void PauseProcess() => RunCore();

[System.Diagnostics.DebuggerHidden]
void RunCore()
Expand Down
8 changes: 8 additions & 0 deletions addons/GDTask/Internal/PlayerLoopRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ public void Run()
case PlayerLoopTiming.Process:
Process();
break;
case PlayerLoopTiming.PausePhysicsProcess:
PausePhysicsProcess();
break;
case PlayerLoopTiming.PauseProcess:
PauseProcess();
break;
}
#else
RunCore();
Expand All @@ -90,6 +96,8 @@ public void Run()

void PhysicsProcess() => RunCore();
void Process() => RunCore();
void PausePhysicsProcess() => RunCore();
void PauseProcess() => RunCore();


[System.Diagnostics.DebuggerHidden]
Expand Down
88 changes: 74 additions & 14 deletions tests/manual/Test.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,56 +5,69 @@

namespace Tests.Manual
{
public partial class Test : Node2D
public partial class Test : Node
{
[Export]
private bool runTestOnReady;
[Export]
private NodePath spritePath;
[Export]
private Label pauseLabel;
public Sprite2D sprite;

public override void _Ready()
{
sprite = GetNode<Sprite2D>(spritePath);
if (runTestOnReady)
Run().Forget();
ProcessMode = ProcessModeEnum.Always;
pauseLabel.Text = GetTree().Paused ? "Paused" : "Unpaused";
}

public override void _Input(InputEvent @event)
{
if (@event.IsActionReleased("ui_select"))
if (@event.IsActionReleased("ui_left"))
{
Run().Forget();
}
else if (@event.IsActionReleased("ui_right"))
{
RunPause().Forget();
}
else if (@event.IsActionReleased("ui_up"))
{
GetTree().Paused = !GetTree().Paused;
pauseLabel.Text = GetTree().Paused ? "Paused" : "Unpaused";
}
}

private async GDTaskVoid Run()
{
GD.Print("Pre delay");
GD.Print("Run: Pre delay");
sprite.Visible = false;
await GDTask.Delay(TimeSpan.FromSeconds(3));
sprite.Visible = true;
GD.Print("Post delay after 3 seconds");
GD.Print("Run: Post delay after 3 seconds");

GD.Print("Pre RunWithResult");
GD.Print("Run: Pre RunWithResult");
string result = await RunWithResult();
GD.Print($"Post got result: {result}");
GD.Print($"Run: Post got result: {result}");

GD.Print("LongTask started");
GD.Print("Run: LongTask started");
var cts = new CancellationTokenSource();

CancellableReallyLongTask(cts.Token).Forget();

await GDTask.Delay(TimeSpan.FromSeconds(3));
cts.Cancel();
GD.Print("LongTask cancelled");
GD.Print("Run: LongTask cancelled");

await GDTask.WaitForEndOfFrame();
GD.Print("WaitForEndOfFrame");
GD.Print("Run: WaitForEndOfFrame");
await GDTask.WaitForPhysicsProcess();
GD.Print("WaitForPhysicsProcess");
GD.Print("Run: WaitForPhysicsProcess");
await GDTask.NextFrame();
GD.Print("NextFrame");
GD.Print("Run: NextFrame");
}

private async GDTask<string> RunWithResult()
Expand All @@ -66,13 +79,60 @@ private async GDTask<string> RunWithResult()
private async GDTaskVoid CancellableReallyLongTask(CancellationToken cancellationToken)
{
int seconds = 10;
GD.Print($"Starting long task ({seconds} seconds long).");
GD.Print($"Run: Starting long task ({seconds} seconds long).");
for (int i = 0; i < seconds; i++)
{
GD.Print($"Working on long task for {i} seconds...");
GD.Print($"Run: Working on long task for {i} seconds...");
await GDTask.Delay(TimeSpan.FromSeconds(1), cancellationToken: cancellationToken);
}
GD.Print("Finished long task.");
GD.Print("Run: Finished long task.");
}

private async GDTaskVoid RunPause()
{
GD.Print("RunPause: Pre delay");
sprite.Visible = false;
await GDTask.Delay(TimeSpan.FromSeconds(3), PlayerLoopTiming.PauseProcess);
sprite.Visible = true;
GD.Print("RunPause: Post delay after 3 seconds");

GD.Print("RunPause: Pre RunWithResult");
string result = await RunWithResultPause();
GD.Print($"RunPause: Post got result: {result}");

GD.Print("RunPause: LongTask started");
var cts = new CancellationTokenSource();

CancellableReallyLongTaskPause(cts.Token).Forget();

await GDTask.Delay(TimeSpan.FromSeconds(3), PlayerLoopTiming.PauseProcess);
cts.Cancel();
GD.Print("RunPause: LongTask cancelled");

await GDTask.Yield(PlayerLoopTiming.PauseProcess);
GD.Print("RunPause: Yield(PlayerLoopTiming.PauseProcess)");
await GDTask.Yield(PlayerLoopTiming.PausePhysicsProcess);
GD.Print("RunPause: Yield(PlayerLoopTiming.PausePhysicsProcess)");
await GDTask.NextFrame(PlayerLoopTiming.PauseProcess);
GD.Print("RunPause: NextFrame");
}

private async GDTask<string> RunWithResultPause()
{
await GDTask.Delay(TimeSpan.FromSeconds(2), PlayerLoopTiming.PauseProcess);
return "Hello";
}

private async GDTaskVoid CancellableReallyLongTaskPause(CancellationToken cancellationToken)
{
int seconds = 10;
GD.Print($"RunPause: Starting long task ({seconds} seconds long).");
for (int i = 0; i < seconds; i++)
{
GD.Print($"RunPause: Working on long task for {i} seconds...");
await GDTask.Delay(TimeSpan.FromSeconds(1), PlayerLoopTiming.PauseProcess, cancellationToken);
}
GD.Print("RunPause: Finished long task.");
}
}
}
Loading

0 comments on commit 64eb327

Please sign in to comment.