mirror of
https://github.com/jkocon/g-helper.git
synced 2026-02-23 13:00:52 +01:00
Logic to find a stable minimum TDP for improved stability and less frame dips in low demanding scenes.
This commit is contained in:
@@ -7,8 +7,10 @@ namespace GHelper.AutoTDP
|
|||||||
internal class AutoTDPService : IDisposable
|
internal class AutoTDPService : IDisposable
|
||||||
{
|
{
|
||||||
|
|
||||||
|
private static readonly bool LOG_AUTO_TDP = false;
|
||||||
|
private static readonly int INTERVAL_MIN_CHECK = 30 * 1_000;
|
||||||
private static readonly int INTERVAL_APP_CHECK = 5_000;
|
private static readonly int INTERVAL_APP_CHECK = 5_000;
|
||||||
private static readonly int INTERVAL_FPS_CHECK = 2_500;
|
private static readonly int INTERVAL_FPS_CHECK = 500;
|
||||||
|
|
||||||
string GameProfileFile = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\GHelper\\AutoTDP.json";
|
string GameProfileFile = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\GHelper\\AutoTDP.json";
|
||||||
|
|
||||||
@@ -31,6 +33,10 @@ namespace GHelper.AutoTDP
|
|||||||
|
|
||||||
private List<double> FramerateLog = new List<double>();
|
private List<double> FramerateLog = new List<double>();
|
||||||
|
|
||||||
|
private double LowestTDP;
|
||||||
|
private double LowestStableTDP;
|
||||||
|
private long LowestStableStability = 0;
|
||||||
|
private int LowestStableChecks = 0;
|
||||||
private double CurrentTDP;
|
private double CurrentTDP;
|
||||||
private double LastAdjustment;
|
private double LastAdjustment;
|
||||||
private double LastAdjustmentTotal = 0;
|
private double LastAdjustmentTotal = 0;
|
||||||
@@ -176,13 +182,15 @@ namespace GHelper.AutoTDP
|
|||||||
|
|
||||||
if (runningGames.Count == 0)
|
if (runningGames.Count == 0)
|
||||||
{
|
{
|
||||||
Logger.WriteLine("[AutoTDPService] No games detected");
|
if (LOG_AUTO_TDP)
|
||||||
|
Logger.WriteLine("[AutoTDPService] No games detected");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (GameInstance gi in runningGames)
|
foreach (GameInstance gi in runningGames)
|
||||||
{
|
{
|
||||||
Logger.WriteLine("[AutoTDPService] Detected App: " + gi.ProcessName + " PID: " + gi.ProcessID);
|
if (LOG_AUTO_TDP)
|
||||||
|
Logger.WriteLine("[AutoTDPService] Detected App: " + gi.ProcessName + " PID: " + gi.ProcessID);
|
||||||
|
|
||||||
if (IsGameInList(gi.ProcessName))
|
if (IsGameInList(gi.ProcessName))
|
||||||
{
|
{
|
||||||
@@ -221,7 +229,8 @@ namespace GHelper.AutoTDP
|
|||||||
{
|
{
|
||||||
if (currentGame is not null)
|
if (currentGame is not null)
|
||||||
{
|
{
|
||||||
Logger.WriteLine("[AutoTDPService] Already handling a game");
|
if (LOG_AUTO_TDP)
|
||||||
|
Logger.WriteLine("[AutoTDPService] Already handling a game");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,10 +250,12 @@ namespace GHelper.AutoTDP
|
|||||||
GameFPSPrevious = double.NaN;
|
GameFPSPrevious = double.NaN;
|
||||||
GameFPS = 0;
|
GameFPS = 0;
|
||||||
LastAdjustmentsWithoutImprovement = 0;
|
LastAdjustmentsWithoutImprovement = 0;
|
||||||
LastAdjustment = 0.0;
|
LastAdjustment = 0.0;
|
||||||
FramerateLog = new List<double>();
|
FramerateLog = new List<double>();
|
||||||
FramerateTargetReachedCounter = 0;
|
FramerateTargetReachedCounter = 0;
|
||||||
FramerateDipCounter = 0;
|
FramerateDipCounter = 0;
|
||||||
|
LowestStableStability = 0;
|
||||||
|
LowestStableChecks = 0;
|
||||||
|
|
||||||
if (powerLimiter is not null)
|
if (powerLimiter is not null)
|
||||||
{
|
{
|
||||||
@@ -264,14 +275,17 @@ namespace GHelper.AutoTDP
|
|||||||
tdpThread = new Thread(() =>
|
tdpThread = new Thread(() =>
|
||||||
{
|
{
|
||||||
CurrentTDP = powerLimiter.GetCPUPowerLimit();
|
CurrentTDP = powerLimiter.GetCPUPowerLimit();
|
||||||
|
LowestStableTDP = profile.MaxTdp;
|
||||||
|
LowestTDP = profile.MaxTdp;
|
||||||
|
|
||||||
while (currentGame is not null && Running)
|
while (currentGame is not null && Running)
|
||||||
{
|
{
|
||||||
//prevent FPS from going to 0 which causes issues with the math
|
//prevent FPS from going to 0 which causes issues with the math
|
||||||
|
|
||||||
double fps = framerateSouce.GetFramerate(instance);
|
double fps = framerateSouce.GetFramerate(instance);
|
||||||
|
|
||||||
|
if (LOG_AUTO_TDP)
|
||||||
Logger.WriteLine("[AutoTDPService] (" + instance.ProcessName + ") Framerate " + GameFPS);
|
Logger.WriteLine("[AutoTDPService] (" + instance.ProcessName + ") Framerate " + GameFPS);
|
||||||
|
|
||||||
if (fps < 0.0d)
|
if (fps < 0.0d)
|
||||||
{
|
{
|
||||||
@@ -321,15 +335,22 @@ namespace GHelper.AutoTDP
|
|||||||
{
|
{
|
||||||
//short dip
|
//short dip
|
||||||
FramerateDipCounter++;
|
FramerateDipCounter++;
|
||||||
correction = targetFPS - currentFramerate;
|
FramerateUnstable();
|
||||||
|
|
||||||
|
correction = targetFPS + 0.15 - currentFramerate;
|
||||||
}
|
}
|
||||||
else if (FramerateDipCounter >= 4
|
else if (FramerateDipCounter >= 4
|
||||||
&& targetFPS - 0.5 <= FramerateLog.Average()
|
&& targetFPS - 0.5 <= FramerateLog.Average()
|
||||||
&& FramerateLog.Average() - 0.1 <= targetFPS)
|
&& FramerateLog.Average() - 0.1 <= targetFPS)
|
||||||
{
|
{
|
||||||
//long dip
|
//long dip
|
||||||
correction = targetFPS + 0.25 - currentFramerate;
|
correction = targetFPS + 0.35 - currentFramerate;
|
||||||
FramerateTargetReachedCounter = FPSDipHistorySize;
|
FramerateTargetReachedCounter = FPSDipHistorySize;
|
||||||
|
FramerateVeryUnstable();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FramerateStable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -338,16 +359,82 @@ namespace GHelper.AutoTDP
|
|||||||
correction = 0.0;
|
correction = 0.0;
|
||||||
FramerateTargetReachedCounter = 0;
|
FramerateTargetReachedCounter = 0;
|
||||||
FramerateDipCounter = 0;
|
FramerateDipCounter = 0;
|
||||||
|
FramerateStable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProcessStability();
|
||||||
return correction;
|
return correction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void FramerateStable()
|
||||||
|
{
|
||||||
|
LowestStableStability++;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FramerateUnstable()
|
||||||
|
{
|
||||||
|
LowestStableStability -= 3;
|
||||||
|
}
|
||||||
|
private void FramerateVeryUnstable()
|
||||||
|
{
|
||||||
|
LowestStableStability -= 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Stabilize()
|
||||||
|
{
|
||||||
|
return LowestStableChecks * INTERVAL_FPS_CHECK > INTERVAL_MIN_CHECK;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessStability()
|
||||||
|
{
|
||||||
|
if (!Stabilize()) LowestStableChecks++;
|
||||||
|
|
||||||
|
if (LowestStableStability < 0 && Stabilize())
|
||||||
|
{
|
||||||
|
//If unstable for too often increase lowest stable TDP
|
||||||
|
LowestStableTDP += 1;
|
||||||
|
LowestTDP += 1;
|
||||||
|
LowestStableStability = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CurrentTDP > LowestStableTDP - 0.1 && CurrentTDP < LowestStableTDP + 0.1 && Stabilize())
|
||||||
|
{
|
||||||
|
LowestStableStability++;
|
||||||
|
|
||||||
|
if (LowestStableStability > 120)
|
||||||
|
{
|
||||||
|
//if stable for long time try to reduce it a bit
|
||||||
|
LowestStableTDP -= 0.5;
|
||||||
|
LowestStableStability = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LowestTDP - 0.25 <= CurrentTDP && CurrentTDP >= LowestTDP + 0.25)
|
||||||
|
{
|
||||||
|
LowestStableStability++;
|
||||||
|
|
||||||
|
if (LowestStableStability > 10 && Stabilize())
|
||||||
|
{
|
||||||
|
LowestStableTDP = LowestTDP + (LowestTDP * 0.10); // Add 10% additional wattage to get a smoother framerate
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CurrentTDP < LowestTDP - 0.1 && LowestStableStability > 0)
|
||||||
|
{
|
||||||
|
LowestStableStability = 0;
|
||||||
|
LowestTDP = CurrentTDP;
|
||||||
|
}
|
||||||
|
|
||||||
|
LowestStableStability = Math.Min(LowestStableStability, 150);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private double TDPDamper(double currentFramerate)
|
private double TDPDamper(double currentFramerate)
|
||||||
{
|
{
|
||||||
if (double.IsNaN(GameFPSPrevious)) GameFPSPrevious = currentFramerate;
|
if (double.IsNaN(GameFPSPrevious)) GameFPSPrevious = currentFramerate;
|
||||||
double dF = -0.15d;
|
double dF = -0.12d;
|
||||||
|
|
||||||
// Calculation
|
// Calculation
|
||||||
double deltaError = currentFramerate - GameFPSPrevious;
|
double deltaError = currentFramerate - GameFPSPrevious;
|
||||||
@@ -374,11 +461,21 @@ namespace GHelper.AutoTDP
|
|||||||
delta = Math.Clamp(delta, -15, 15);
|
delta = Math.Clamp(delta, -15, 15);
|
||||||
|
|
||||||
|
|
||||||
double adjustment = (delta * CurrentTDP / GameFPS) * 0.9;
|
double adjustment = (delta * CurrentTDP / GameFPS) * 0.65;
|
||||||
//Dampen the changes to not change TDP too aggressively which would cause performance issues
|
//Dampen the changes to not change TDP too aggressively which would cause performance issues
|
||||||
adjustment += TDPDamper(GameFPS);
|
adjustment += TDPDamper(GameFPS);
|
||||||
|
|
||||||
adjustment = Math.Min(adjustment, (CurrentTDP / 5));
|
adjustment = Math.Min(adjustment, (CurrentTDP * 0.1));
|
||||||
|
|
||||||
|
if (GameFPSPrevious > profile.GetTDPFPS() && GameFPS < profile.GetTDPFPS())
|
||||||
|
{
|
||||||
|
if (LOG_AUTO_TDP)
|
||||||
|
Logger.WriteLine("[AutoTDPService] Single Dip, Ignore");
|
||||||
|
|
||||||
|
GameFPSPrevious = GameFPS;
|
||||||
|
//single dip. Ignore
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (LastAdjustment > 0 && GameFPS <= GameFPSPrevious && adjustment > 0)
|
if (LastAdjustment > 0 && GameFPS <= GameFPSPrevious && adjustment > 0)
|
||||||
{
|
{
|
||||||
@@ -389,7 +486,8 @@ namespace GHelper.AutoTDP
|
|||||||
if (LastAdjustmentsWithoutImprovement >= 3 && LastAdjustmentTotal > 3)
|
if (LastAdjustmentsWithoutImprovement >= 3 && LastAdjustmentTotal > 3)
|
||||||
{
|
{
|
||||||
//Do not adjust if increasing power does not improve framerate.
|
//Do not adjust if increasing power does not improve framerate.
|
||||||
Logger.WriteLine("[AutoTDPService] Not adjusting because no improvement from last increase");
|
if (LOG_AUTO_TDP)
|
||||||
|
Logger.WriteLine("[AutoTDPService] Not adjusting because no improvement from last increase");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,7 +504,14 @@ namespace GHelper.AutoTDP
|
|||||||
//Respect the limits that the user chose
|
//Respect the limits that the user chose
|
||||||
newPL = Math.Clamp(newPL, profile.MinTdp, profile.MaxTdp);
|
newPL = Math.Clamp(newPL, profile.MinTdp, profile.MaxTdp);
|
||||||
|
|
||||||
Logger.WriteLine("[AutoTDPService] Setting Power Limit from " + CurrentTDP + "W to " + newPL + "W, Delta:" + adjustment + ", FPS correction: " + fpsCorrection);
|
if (newPL < LowestStableTDP && LowestStableTDP < profile.MaxTdp - 1)
|
||||||
|
{
|
||||||
|
newPL = LowestStableTDP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LOG_AUTO_TDP)
|
||||||
|
Logger.WriteLine("[AutoTDPService] Power Limit from " + CurrentTDP + "W to " + newPL + "W, Delta:" + adjustment
|
||||||
|
+ " Lowest: " + LowestTDP + "W, Lowest Stable(" + LowestStableStability + "): " + LowestStableTDP + "W");
|
||||||
|
|
||||||
//We only limit to full watts, no fractions. In this case, we will cut off the fractional part
|
//We only limit to full watts, no fractions. In this case, we will cut off the fractional part
|
||||||
powerLimiter.SetCPUPowerLimit(newPL);
|
powerLimiter.SetCPUPowerLimit(newPL);
|
||||||
|
|||||||
Reference in New Issue
Block a user