Skip to content

Commit 856e10a

Browse files
committed
Refactor AI loop for improved async handling
- Replaced threading model with Task and CancellationToken for better responsiveness and cancellation control. - Added display dimension properties (ScreenWidth, ScreenHeight, ScreenLeft) for enhanced overlay management. - Optimized input array handling by dynamically calculating required lengths. - Improved error handling in model inference to prevent memory leaks. - Refactored HandleStickyAim for better target acquisition logic. - Ensured proper disposal of AIManager in MainWindow.xaml.cs to prevent resource leaks. - Overall, these changes modernize the codebase with a focus on asynchronous programming and resource management.
1 parent a5c44dd commit 856e10a

File tree

2 files changed

+62
-35
lines changed

2 files changed

+62
-35
lines changed

Aimmy2/AILogic/AIManager.cs

Lines changed: 61 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,15 @@
66
using Microsoft.ML.OnnxRuntime.Tensors;
77
using Newtonsoft.Json.Linq;
88
using Other;
9-
using Supercluster.KDTree;
109
using System.Diagnostics;
1110
using System.Drawing;
1211
using System.Drawing.Imaging;
1312
using System.IO;
1413
using System.Runtime.CompilerServices;
1514
using System.Windows;
1615
using Visuality;
17-
using static Other.LogManager;
1816
using static Aimmy2.AILogic.MathUtil;
17+
using static Other.LogManager;
1918

2019
namespace Aimmy2.AILogic
2120
{
@@ -60,7 +59,7 @@ public void RequestSizeChange(int newSize)
6059
private KalmanPrediction kalmanPrediction;
6160
private WiseTheFoxPrediction wtfpredictionManager;
6261

63-
62+
6463

6564
// Display-aware properties
6665
private int ScreenWidth => DisplayManager.ScreenWidth;
@@ -72,7 +71,10 @@ public void RequestSizeChange(int newSize)
7271
private readonly RunOptions? _modeloptions;
7372
private InferenceSession? _onnxModel;
7473

75-
private Thread? _aiLoopThread;
74+
//private Thread? _aiLoopThread;
75+
76+
private Task? _aiLoopTask;
77+
private CancellationTokenSource? _cts;
7678
private volatile bool _isAiLoopRunning;
7779

7880
// For Auto-Labelling Data System
@@ -372,14 +374,12 @@ private async Task InitializeModel(string modelPath)
372374
if (_onnxModel?.OutputMetadata != null && _onnxModel.OutputMetadata.Count > 0)
373375
{
374376
Log(LogLevel.Info, "Starting AI Loop", false);
375-
_isAiLoopRunning = true;
376-
_aiLoopThread = new Thread(AiLoop)
377-
{
378-
IsBackground = true,
379-
Priority = ThreadPriority.AboveNormal
380-
};
381-
_aiLoopThread.Start();
382-
// Begin the loop
377+
_cts?.Cancel();
378+
379+
try { _aiLoopTask?.Wait(500); } catch { }
380+
_cts = new CancellationTokenSource();
381+
_aiLoopTask = Task.Run(() => AiLoop(_cts.Token), _cts.Token);
382+
Log(LogLevel.Info, "AI loop started");
383383
}
384384
else
385385
{
@@ -571,12 +571,12 @@ private static bool ShouldProcess() =>
571571
Dictionary.toggleState["Show Detected Player"] ||
572572
Dictionary.toggleState["Auto Trigger"];
573573

574-
private async void AiLoop()
574+
private async Task AiLoop(CancellationToken ct)
575575
{
576576
Stopwatch stopwatch = new();
577577
DetectedPlayerWindow? DetectedPlayerOverlay = Dictionary.DetectedPlayerOverlay;
578578

579-
while (_isAiLoopRunning)
579+
while (!ct.IsCancellationRequested && _isAiLoopRunning)
580580
{
581581
// Check for pending size changes at the start of each iteration
582582
lock (_sizeLock)
@@ -589,6 +589,7 @@ private async void AiLoop()
589589
}
590590

591591
stopwatch.Restart();
592+
592593
_captureManager.HandlePendingDisplayChanges();
593594

594595
using (Benchmark("AILoopIteration"))
@@ -632,13 +633,13 @@ private async void AiLoop()
632633
else
633634
{
634635
// Processing so we are at the ready but not holding right/click.
635-
await Task.Delay(1);
636+
await Task.Delay(1, ct);
636637
}
637638
}
638639
else
639640
{
640641
// No work to do—sleep briefly to free up CPU
641-
await Task.Delay(1);
642+
await Task.Delay(1, ct);
642643
}
643644
}
644645

@@ -693,7 +694,7 @@ private async Task AutoTrigger()
693694
if (!Dictionary.toggleState["Aim Assist"] || !Dictionary.toggleState["Show Detected Player"]) return;
694695

695696
}
696-
private void CheckSprayRelease()
697+
private void CheckSprayRelease()
697698
{
698699
if (!Dictionary.toggleState["Spray Mode"]) return;
699700

@@ -710,8 +711,6 @@ private void CheckSprayRelease()
710711
}
711712
}
712713

713-
714-
715714
private void CalculateCoordinates(DetectedPlayerWindow DetectedPlayerOverlay, Prediction closestPrediction, float scaleX, float scaleY)
716715
{
717716
AIConf = closestPrediction.Confidence;
@@ -889,9 +888,11 @@ private void HandlePredictions(KalmanPrediction kalmanPrediction, Prediction clo
889888
float[] inputArray;
890889
using (Benchmark("BitmapToFloatArray"))
891890
{
892-
if (_reusableInputArray == null || _reusableInputArray.Length != 3 * IMAGE_SIZE * IMAGE_SIZE)
891+
int requiredLength = 3 * IMAGE_SIZE * IMAGE_SIZE;
892+
893+
if (_reusableInputArray == null || _reusableInputArray.Length != requiredLength)
893894
{
894-
_reusableInputArray = new float[3 * IMAGE_SIZE * IMAGE_SIZE];
895+
_reusableInputArray = new float[requiredLength];
895896
}
896897
inputArray = _reusableInputArray;
897898

@@ -911,7 +912,11 @@ private void HandlePredictions(KalmanPrediction kalmanPrediction, Prediction clo
911912
inputArray.AsSpan().CopyTo(_reusableTensor.Buffer.Span);
912913
}
913914

914-
if (_onnxModel == null) return null;
915+
if (_onnxModel == null)
916+
{
917+
frame.Dispose();
918+
return null; // Model not loaded, exit early
919+
}
915920

916921
//IDisposableReadOnlyCollection<DisposableNamedOnnxValue> results;
917922
Tensor<float>? outputTensor = null;
@@ -921,7 +926,7 @@ private void HandlePredictions(KalmanPrediction kalmanPrediction, Prediction clo
921926
outputTensor = results[0].AsTensor<float>();
922927
}
923928

924-
if(outputTensor == null)
929+
if (outputTensor == null)
925930
{
926931
Log(LogLevel.Error, "Model inference returned null output tensor.", true, 2000);
927932
SaveFrame(frame);
@@ -941,7 +946,7 @@ private void HandlePredictions(KalmanPrediction kalmanPrediction, Prediction clo
941946
{
942947
(KDpoints, KDPredictions) = PrepareKDTreeData(outputTensor, detectionBox, fovMinX, fovMaxX, fovMinY, fovMaxY);
943948
}
944-
949+
945950
if (KDpoints.Count == 0 || KDPredictions.Count == 0)
946951
{
947952
SaveFrame(frame);
@@ -969,6 +974,7 @@ private void HandlePredictions(KalmanPrediction kalmanPrediction, Prediction clo
969974
{
970975
UpdateDetectionBox(finalTarget, detectionBox);
971976
SaveFrame(frame, finalTarget);
977+
frame.Dispose();
972978
return finalTarget;
973979
}
974980

@@ -977,14 +983,31 @@ private void HandlePredictions(KalmanPrediction kalmanPrediction, Prediction clo
977983
}
978984
private Prediction? HandleStickyAim(Prediction? bestCandidate, List<Prediction> KDPredictions)
979985
{
980-
bool stickyAimEnabled = Dictionary.toggleState["Sticky Aim"];
981-
if (!stickyAimEnabled)
986+
if (!Dictionary.toggleState["Sticky Aim"])
982987
{
983988
_currentTarget = bestCandidate; // update anyway
984989
return bestCandidate;
985990
}
986991

987-
float thresholdSqr = (float)Math.Pow(Dictionary.sliderSettings["Sticky Aim Threshold"], 2);
992+
float threshold = (float)Dictionary.sliderSettings["Sticky Aim Threshold"];
993+
float thresholdSqr = threshold * threshold;
994+
995+
if (bestCandidate == null || KDPredictions == null || KDPredictions.Count == 0)
996+
{
997+
if (_currentTarget != null)
998+
{
999+
if (++_consecutiveFramesWithoutTarget > MAX_FRAMES_WITHOUT_TARGET)
1000+
{
1001+
_currentTarget = null;
1002+
_consecutiveFramesWithoutTarget = 0;
1003+
}
1004+
// keep previous target while within grace period
1005+
return _currentTarget;
1006+
}
1007+
return null;
1008+
}
1009+
// reset consecutive frames since we have a target
1010+
_consecutiveFramesWithoutTarget = 0;
9881011

9891012
if (_currentTarget != null)
9901013
{
@@ -1011,9 +1034,10 @@ private void HandlePredictions(KalmanPrediction kalmanPrediction, Prediction clo
10111034
if (++_consecutiveFramesWithoutTarget > MAX_FRAMES_WITHOUT_TARGET)
10121035
{
10131036
_currentTarget = null;
1014-
} else
1037+
}
1038+
else
10151039
{
1016-
return null; // No match found, keep the current target
1040+
return _currentTarget;
10171041
}
10181042
}
10191043

@@ -1033,7 +1057,7 @@ private void UpdateDetectionBox(Prediction target, Rectangle detectionBox)
10331057
CenterYTranslated = target.CenterYTranslated;
10341058
}
10351059
private (List<double[]>, List<Prediction>) PrepareKDTreeData(
1036-
Tensor<float> outputTensor,
1060+
Tensor<float> outputTensor,
10371061
Rectangle detectionBox,
10381062
float fovMinX, float fovMaxX, float fovMinY, float fovMaxY)
10391063
{
@@ -1160,14 +1184,15 @@ public void Dispose()
11601184

11611185
// Stop the loop
11621186
_isAiLoopRunning = false;
1163-
if (_aiLoopThread != null && _aiLoopThread.IsAlive)
1187+
try
11641188
{
1165-
if (!_aiLoopThread.Join(TimeSpan.FromSeconds(1)))
1189+
_cts?.Cancel();
1190+
if (_aiLoopTask != null)
11661191
{
1167-
try { _aiLoopThread.Interrupt(); }
1168-
catch { }
1192+
_aiLoopTask.Wait(TimeSpan.FromSeconds(1));
11691193
}
11701194
}
1195+
catch { }
11711196

11721197
// Print final benchmarks
11731198
PrintBenchmarks();
@@ -1178,6 +1203,8 @@ public void Dispose()
11781203
// Clean up other resources
11791204
_reusableInputArray = null;
11801205
_reusableInputs = null;
1206+
_reusableTensor = null;
1207+
_cts?.Dispose();
11811208
_onnxModel?.Dispose();
11821209
_modeloptions?.Dispose();
11831210
}

Aimmy2/MainWindow.xaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs
398398
fileManager.InQuittingState = true;
399399
}
400400

401+
FileManager.AIManager?.Dispose();
401402
DisableAllFeatures();
402403
CloseWindows();
403404
CleanupDrivers();
@@ -410,7 +411,6 @@ private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs
410411
settingsMenu.Dispose();
411412

412413
SaveAllConfigurations();
413-
FileManager.AIManager?.Dispose();
414414

415415
// Clean up display manager
416416
DisplayManager.DisplayChanged -= OnDisplayChanged;

0 commit comments

Comments
 (0)