From b0e412f370d1e6bb95cfc96bcb2bcd65178675b7 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Fri, 12 Jan 2024 18:16:06 +0100 Subject: [PATCH 01/76] Automatically restart audio capture when device changes. --- app/AnimeMatrix/AniMatrixControl.cs | 56 +++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/app/AnimeMatrix/AniMatrixControl.cs b/app/AnimeMatrix/AniMatrixControl.cs index 025f0f53a..d5d4b1b9f 100644 --- a/app/AnimeMatrix/AniMatrixControl.cs +++ b/app/AnimeMatrix/AniMatrixControl.cs @@ -10,7 +10,7 @@ namespace GHelper.AnimeMatrix { - public class AniMatrixControl + public class AniMatrixControl : NAudio.CoreAudioApi.Interfaces.IMMNotificationClient { SettingsForm settings; @@ -20,6 +20,8 @@ public class AniMatrixControl double[]? AudioValues; WasapiCapture? AudioDevice; + string? AudioDeviceId; + private MMDeviceEnumerator? AudioDeviceEnum; public bool IsValid => device != null; @@ -167,6 +169,9 @@ void StopMatrixAudio() Logger.WriteLine(ex.ToString()); } } + + AudioDeviceId = null; + AudioDeviceEnum?.Dispose(); } void SetMatrixAudio() @@ -179,10 +184,13 @@ void SetMatrixAudio() try { - using (var enumerator = new MMDeviceEnumerator()) - using (MMDevice device = enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Console)) + AudioDeviceEnum = new MMDeviceEnumerator(); + AudioDeviceEnum.RegisterEndpointNotificationCallback(this); + + using (MMDevice device = AudioDeviceEnum.GetDefaultAudioEndpoint(DataFlow.Render, Role.Console)) { AudioDevice = new WasapiLoopbackCapture(device); + AudioDeviceId = device.ID; WaveFormat fmt = AudioDevice.WaveFormat; AudioValues = new double[fmt.SampleRate / 1000]; @@ -397,6 +405,48 @@ protected void ProcessPicture(Image image) } + public void OnDeviceStateChanged(string deviceId, DeviceState newState) + { + + } + + public void OnDeviceAdded(string pwstrDeviceId) + { + + } + + public void OnDeviceRemoved(string deviceId) + { + + } + + public void OnDefaultDeviceChanged(DataFlow flow, Role role, string defaultDeviceId) + { + if (AudioDeviceId == defaultDeviceId) + { + //We already caputre this device. No need to re-initialize + return; + } + + int running = AppConfig.Get("matrix_running"); + if (flow != DataFlow.Render || role != Role.Console || running != 4) + { + return; + } + + //Restart audio if default audio changed + Logger.WriteLine("Matrix Audio: Default Output changed to " + defaultDeviceId); + + //Already set the device here. Otherwise this will be called multiple times in a short succession and causes a crash due to dispose during initalization. + AudioDeviceId = defaultDeviceId; + + //Delay is required or it will deadlock on dispose. + Task.Delay(50).ContinueWith(t => SetMatrixAudio()); + } + + public void OnPropertyValueChanged(string pwstrDeviceId, PropertyKey key) + { + } } } From b006a11dcf67e527a6966bb7d5b12dde3cb9a343 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sat, 13 Jan 2024 11:26:23 +0100 Subject: [PATCH 02/76] Wait for process to terminate instead of waiting a fixed amount of time. --- app/AutoUpdate/AutoUpdateControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/AutoUpdate/AutoUpdateControl.cs b/app/AutoUpdate/AutoUpdateControl.cs index 42093c87e..023bcd3a7 100644 --- a/app/AutoUpdate/AutoUpdateControl.cs +++ b/app/AutoUpdate/AutoUpdateControl.cs @@ -120,7 +120,7 @@ async void AutoUpdate(string requestUri) Logger.WriteLine(zipName); Logger.WriteLine(exeName); - string command = $"Start-Sleep -Seconds 1; $ErrorActionPreference = \"Stop\"; Expand-Archive \"{zipName}\" -DestinationPath . -Force; Remove-Item \"{zipName}\" -Force; \".\\{exeName}\"; "; + string command = $"$ErrorActionPreference = \"Stop\"; Wait-Process -Name \"GHelper\"; Expand-Archive \"{zipName}\" -DestinationPath . -Force; Remove-Item \"{zipName}\" -Force; \".\\{exeName}\"; "; Logger.WriteLine(command); try From acf52c2eddfaf2fe1d43d6f40151e363fe488903 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sat, 13 Jan 2024 11:27:04 +0100 Subject: [PATCH 03/76] Application.Exit might not exit if there is a child process running. Environment.Exit(0) do though. --- app/AutoUpdate/AutoUpdateControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/AutoUpdate/AutoUpdateControl.cs b/app/AutoUpdate/AutoUpdateControl.cs index 023bcd3a7..32757cfcd 100644 --- a/app/AutoUpdate/AutoUpdateControl.cs +++ b/app/AutoUpdate/AutoUpdateControl.cs @@ -139,7 +139,7 @@ async void AutoUpdate(string requestUri) Logger.WriteLine(ex.Message); } - Application.Exit(); + Environment.Exit(0); } } From 84a6fd4d5fcb40b90cc42d586adf91c2a45539f7 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Fri, 1 Mar 2024 07:47:00 +0100 Subject: [PATCH 04/76] Added AutoTDP feature with RTSS source and Intel + ASUS power limiters --- app/AutoTDP/AutoTDPGameProfileUI.Designer.cs | 337 ++++++++++++++ app/AutoTDP/AutoTDPGameProfileUI.cs | 90 ++++ app/AutoTDP/AutoTDPGameProfileUI.resx | 120 +++++ app/AutoTDP/AutoTDPService.cs | 435 ++++++++++++++++++ app/AutoTDP/AutoTDPUI.Designer.cs | 311 +++++++++++++ app/AutoTDP/AutoTDPUI.cs | 222 +++++++++ app/AutoTDP/AutoTDPUI.resx | 120 +++++ .../FramerateSource/IFramerateSource.cs | 17 + .../FramerateSource/RTSSFramerateSource.cs | 136 ++++++ app/AutoTDP/GameProfile.cs | 18 + .../PowerLimiter/ASUSACPIPowerLimiter.cs | 42 ++ app/AutoTDP/PowerLimiter/IPowerLimiter.cs | 12 + .../PowerLimiter/IntelMSRPowerLimiter.cs | 106 +++++ app/GHelper.csproj | 8 + app/GHelper.sln | 6 + app/Program.cs | 4 + app/RTSSSharedMemoryNET.dll | Bin 0 -> 75264 bytes app/Settings.Designer.cs | 47 ++ app/Settings.cs | 31 ++ 19 files changed, 2062 insertions(+) create mode 100644 app/AutoTDP/AutoTDPGameProfileUI.Designer.cs create mode 100644 app/AutoTDP/AutoTDPGameProfileUI.cs create mode 100644 app/AutoTDP/AutoTDPGameProfileUI.resx create mode 100644 app/AutoTDP/AutoTDPService.cs create mode 100644 app/AutoTDP/AutoTDPUI.Designer.cs create mode 100644 app/AutoTDP/AutoTDPUI.cs create mode 100644 app/AutoTDP/AutoTDPUI.resx create mode 100644 app/AutoTDP/FramerateSource/IFramerateSource.cs create mode 100644 app/AutoTDP/FramerateSource/RTSSFramerateSource.cs create mode 100644 app/AutoTDP/GameProfile.cs create mode 100644 app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs create mode 100644 app/AutoTDP/PowerLimiter/IPowerLimiter.cs create mode 100644 app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs create mode 100644 app/RTSSSharedMemoryNET.dll diff --git a/app/AutoTDP/AutoTDPGameProfileUI.Designer.cs b/app/AutoTDP/AutoTDPGameProfileUI.Designer.cs new file mode 100644 index 000000000..66b9c6544 --- /dev/null +++ b/app/AutoTDP/AutoTDPGameProfileUI.Designer.cs @@ -0,0 +1,337 @@ +namespace GHelper.AutoTDP +{ + partial class AutoTDPGameProfileUI + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + panelPerformanceHeader = new Panel(); + pictureKeyboard = new PictureBox(); + labelSettings = new Label(); + checkBoxEnabled = new CheckBox(); + panelLightingContent = new Panel(); + labelMinTDP = new Label(); + labelMaxTDP = new Label(); + sliderMaxTDP = new UI.Slider(); + numericUpDownFPS = new NumericUpDown(); + sliderMinTDP = new UI.Slider(); + label2 = new Label(); + label3 = new Label(); + label1 = new Label(); + textBoxTitle = new TextBox(); + textBoxProcessName = new TextBox(); + labelFPSSource = new Label(); + labelLimiter = new Label(); + buttonSave = new UI.RButton(); + buttonDelete = new UI.RButton(); + panelPerformanceHeader.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)pictureKeyboard).BeginInit(); + panelLightingContent.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)numericUpDownFPS).BeginInit(); + SuspendLayout(); + // + // panelPerformanceHeader + // + panelPerformanceHeader.BackColor = SystemColors.ControlLight; + panelPerformanceHeader.Controls.Add(pictureKeyboard); + panelPerformanceHeader.Controls.Add(labelSettings); + panelPerformanceHeader.Controls.Add(checkBoxEnabled); + panelPerformanceHeader.Dock = DockStyle.Top; + panelPerformanceHeader.Location = new Point(0, 0); + panelPerformanceHeader.Margin = new Padding(2); + panelPerformanceHeader.Name = "panelPerformanceHeader"; + panelPerformanceHeader.Size = new Size(386, 30); + panelPerformanceHeader.TabIndex = 53; + // + // pictureKeyboard + // + pictureKeyboard.BackgroundImage = Properties.Resources.icons8_automation_32; + pictureKeyboard.BackgroundImageLayout = ImageLayout.Zoom; + pictureKeyboard.Location = new Point(3, 8); + pictureKeyboard.Margin = new Padding(2); + pictureKeyboard.Name = "pictureKeyboard"; + pictureKeyboard.Size = new Size(16, 16); + pictureKeyboard.TabIndex = 35; + pictureKeyboard.TabStop = false; + // + // labelSettings + // + labelSettings.AutoSize = true; + labelSettings.Font = new Font("Segoe UI", 9F, FontStyle.Bold, GraphicsUnit.Point); + labelSettings.Location = new Point(22, 8); + labelSettings.Margin = new Padding(4, 0, 4, 0); + labelSettings.Name = "labelSettings"; + labelSettings.Size = new Size(89, 15); + labelSettings.TabIndex = 34; + labelSettings.Text = "Game Settings"; + // + // checkBoxEnabled + // + checkBoxEnabled.Location = new Point(241, 2); + checkBoxEnabled.Margin = new Padding(4, 0, 4, 0); + checkBoxEnabled.Name = "checkBoxEnabled"; + checkBoxEnabled.Size = new Size(136, 25); + checkBoxEnabled.TabIndex = 53; + checkBoxEnabled.Text = "Enabled"; + checkBoxEnabled.UseVisualStyleBackColor = true; + // + // panelLightingContent + // + panelLightingContent.AutoSize = true; + panelLightingContent.Controls.Add(labelMinTDP); + panelLightingContent.Controls.Add(labelMaxTDP); + panelLightingContent.Controls.Add(sliderMaxTDP); + panelLightingContent.Controls.Add(numericUpDownFPS); + panelLightingContent.Controls.Add(sliderMinTDP); + panelLightingContent.Controls.Add(label2); + panelLightingContent.Controls.Add(label3); + panelLightingContent.Controls.Add(label1); + panelLightingContent.Controls.Add(textBoxTitle); + panelLightingContent.Controls.Add(textBoxProcessName); + panelLightingContent.Controls.Add(labelFPSSource); + panelLightingContent.Controls.Add(labelLimiter); + panelLightingContent.Dock = DockStyle.Top; + panelLightingContent.Location = new Point(0, 30); + panelLightingContent.Margin = new Padding(2); + panelLightingContent.Name = "panelLightingContent"; + panelLightingContent.Padding = new Padding(0, 0, 0, 7); + panelLightingContent.Size = new Size(386, 146); + panelLightingContent.TabIndex = 57; + // + // labelMinTDP + // + labelMinTDP.Location = new Point(342, 91); + labelMinTDP.Name = "labelMinTDP"; + labelMinTDP.Size = new Size(39, 22); + labelMinTDP.TabIndex = 67; + labelMinTDP.Text = "30W"; + labelMinTDP.TextAlign = ContentAlignment.MiddleRight; + // + // labelMaxTDP + // + labelMaxTDP.Location = new Point(341, 117); + labelMaxTDP.Name = "labelMaxTDP"; + labelMaxTDP.Size = new Size(40, 22); + labelMaxTDP.TabIndex = 66; + labelMaxTDP.Text = "150W"; + labelMaxTDP.TextAlign = ContentAlignment.MiddleRight; + // + // sliderMaxTDP + // + sliderMaxTDP.AccessibleName = "DPI Slider"; + sliderMaxTDP.Location = new Point(140, 117); + sliderMaxTDP.Margin = new Padding(2); + sliderMaxTDP.Max = 200; + sliderMaxTDP.Min = 5; + sliderMaxTDP.Name = "sliderMaxTDP"; + sliderMaxTDP.Size = new Size(204, 20); + sliderMaxTDP.Step = 1; + sliderMaxTDP.TabIndex = 65; + sliderMaxTDP.TabStop = false; + sliderMaxTDP.Text = "sliderBattery"; + sliderMaxTDP.Value = 0; + // + // numericUpDownFPS + // + numericUpDownFPS.BorderStyle = BorderStyle.None; + numericUpDownFPS.Location = new Point(291, 66); + numericUpDownFPS.Margin = new Padding(2); + numericUpDownFPS.Maximum = new decimal(new int[] { 1000, 0, 0, 0 }); + numericUpDownFPS.Minimum = new decimal(new int[] { 20, 0, 0, 0 }); + numericUpDownFPS.Name = "numericUpDownFPS"; + numericUpDownFPS.Size = new Size(86, 19); + numericUpDownFPS.TabIndex = 64; + numericUpDownFPS.TextAlign = HorizontalAlignment.Center; + numericUpDownFPS.Value = new decimal(new int[] { 60, 0, 0, 0 }); + // + // sliderMinTDP + // + sliderMinTDP.AccessibleName = "DPI Slider"; + sliderMinTDP.Location = new Point(140, 93); + sliderMinTDP.Margin = new Padding(2); + sliderMinTDP.Max = 200; + sliderMinTDP.Min = 5; + sliderMinTDP.Name = "sliderMinTDP"; + sliderMinTDP.Size = new Size(204, 20); + sliderMinTDP.Step = 1; + sliderMinTDP.TabIndex = 63; + sliderMinTDP.TabStop = false; + sliderMinTDP.Text = "sliderBattery"; + sliderMinTDP.Value = 0; + // + // label2 + // + label2.Location = new Point(5, 117); + label2.Margin = new Padding(4, 0, 4, 0); + label2.Name = "label2"; + label2.Size = new Size(129, 22); + label2.TabIndex = 61; + label2.Text = "Max TDP:"; + label2.TextAlign = ContentAlignment.MiddleLeft; + // + // label3 + // + label3.Location = new Point(5, 91); + label3.Margin = new Padding(4, 0, 4, 0); + label3.Name = "label3"; + label3.Size = new Size(129, 22); + label3.TabIndex = 62; + label3.Text = "Min TDP:"; + label3.TextAlign = ContentAlignment.MiddleLeft; + // + // label1 + // + label1.Location = new Point(5, 63); + label1.Margin = new Padding(4, 0, 4, 0); + label1.Name = "label1"; + label1.Size = new Size(129, 22); + label1.TabIndex = 60; + label1.Text = "Target FPS:"; + label1.TextAlign = ContentAlignment.MiddleLeft; + // + // textBoxTitle + // + textBoxTitle.Location = new Point(140, 36); + textBoxTitle.Name = "textBoxTitle"; + textBoxTitle.Size = new Size(237, 23); + textBoxTitle.TabIndex = 59; + // + // textBoxProcessName + // + textBoxProcessName.Location = new Point(140, 6); + textBoxProcessName.Name = "textBoxProcessName"; + textBoxProcessName.ReadOnly = true; + textBoxProcessName.Size = new Size(237, 23); + textBoxProcessName.TabIndex = 58; + textBoxProcessName.WordWrap = false; + // + // labelFPSSource + // + labelFPSSource.Location = new Point(4, 37); + labelFPSSource.Margin = new Padding(4, 0, 4, 0); + labelFPSSource.Name = "labelFPSSource"; + labelFPSSource.Size = new Size(129, 22); + labelFPSSource.TabIndex = 57; + labelFPSSource.Text = "Name:"; + labelFPSSource.TextAlign = ContentAlignment.MiddleLeft; + // + // labelLimiter + // + labelLimiter.Location = new Point(4, 7); + labelLimiter.Margin = new Padding(4, 0, 4, 0); + labelLimiter.Name = "labelLimiter"; + labelLimiter.Size = new Size(129, 22); + labelLimiter.TabIndex = 47; + labelLimiter.Text = "Process"; + labelLimiter.TextAlign = ContentAlignment.MiddleLeft; + // + // buttonSave + // + buttonSave.AccessibleName = "Keyboard Color"; + buttonSave.Activated = false; + buttonSave.Anchor = AnchorStyles.Top | AnchorStyles.Right; + buttonSave.BackColor = SystemColors.ButtonHighlight; + buttonSave.BorderColor = Color.Transparent; + buttonSave.BorderRadius = 2; + buttonSave.FlatStyle = FlatStyle.Flat; + buttonSave.ForeColor = SystemColors.ControlText; + buttonSave.Location = new Point(279, 182); + buttonSave.Margin = new Padding(2, 4, 2, 4); + buttonSave.Name = "buttonSave"; + buttonSave.Secondary = false; + buttonSave.Size = new Size(103, 25); + buttonSave.TabIndex = 61; + buttonSave.Text = "Save"; + buttonSave.UseVisualStyleBackColor = false; + // + // buttonDelete + // + buttonDelete.AccessibleName = "Keyboard Color"; + buttonDelete.Activated = false; + buttonDelete.Anchor = AnchorStyles.Top | AnchorStyles.Right; + buttonDelete.BackColor = SystemColors.ButtonHighlight; + buttonDelete.BorderColor = Color.Transparent; + buttonDelete.BorderRadius = 2; + buttonDelete.FlatStyle = FlatStyle.Flat; + buttonDelete.ForeColor = SystemColors.ControlText; + buttonDelete.Location = new Point(4, 182); + buttonDelete.Margin = new Padding(2, 4, 2, 4); + buttonDelete.Name = "buttonDelete"; + buttonDelete.Secondary = false; + buttonDelete.Size = new Size(103, 25); + buttonDelete.TabIndex = 62; + buttonDelete.Text = "Delete"; + buttonDelete.UseVisualStyleBackColor = false; + // + // AutoTDPGameProfileUI + // + AutoScaleDimensions = new SizeF(7F, 15F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(386, 217); + Controls.Add(buttonDelete); + Controls.Add(buttonSave); + Controls.Add(panelLightingContent); + Controls.Add(panelPerformanceHeader); + FormBorderStyle = FormBorderStyle.FixedSingle; + MaximizeBox = false; + MinimizeBox = false; + Name = "AutoTDPGameProfileUI"; + ShowIcon = false; + ShowInTaskbar = false; + Text = "Game Profile"; + panelPerformanceHeader.ResumeLayout(false); + panelPerformanceHeader.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)pictureKeyboard).EndInit(); + panelLightingContent.ResumeLayout(false); + panelLightingContent.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)numericUpDownFPS).EndInit(); + ResumeLayout(false); + PerformLayout(); + } + + #endregion + + private Panel panelPerformanceHeader; + private PictureBox pictureKeyboard; + private Label labelSettings; + private CheckBox checkBoxEnabled; + private Panel panelLightingContent; + private Label labelFPSSource; + private Label labelLimiter; + private TextBox textBoxTitle; + private TextBox textBoxProcessName; + private UI.RButton buttonSave; + private Label label2; + private Label label3; + private Label label1; + private UI.Slider sliderMinTDP; + private UI.Slider sliderMaxTDP; + private NumericUpDown numericUpDownFPS; + private Label labelMinTDP; + private Label labelMaxTDP; + private UI.RButton buttonDelete; + } +} \ No newline at end of file diff --git a/app/AutoTDP/AutoTDPGameProfileUI.cs b/app/AutoTDP/AutoTDPGameProfileUI.cs new file mode 100644 index 000000000..95f966775 --- /dev/null +++ b/app/AutoTDP/AutoTDPGameProfileUI.cs @@ -0,0 +1,90 @@ +using GHelper.UI; + +namespace GHelper.AutoTDP +{ + public partial class AutoTDPGameProfileUI : RForm + { + public GameProfile GameProfile; + private AutoTDPUI AutoTDPUI; + + public AutoTDPGameProfileUI(GameProfile profile, AutoTDPUI parent) + { + AutoTDPUI = parent; + GameProfile = profile; + InitializeComponent(); + + sliderMinTDP.ValueChanged += SliderMinTDP_ValueChanged; + sliderMaxTDP.ValueChanged += SliderMaxTDP_ValueChanged; + buttonSave.Click += ButtonSave_Click; + buttonDelete.Click += ButtonDelete_Click; + + InitTheme(); + + + Shown += AutoTDPGameProfileUI_Shown; + + VisualizeGameProfile(); + } + + private void ButtonDelete_Click(object? sender, EventArgs e) + { + AutoTDPUI.DeleteGameProfile(GameProfile); + Close(); + } + + private void ButtonSave_Click(object? sender, EventArgs e) + { + GameProfile.Enabled = checkBoxEnabled.Checked; + GameProfile.GameTitle = textBoxTitle.Text; + GameProfile.TargetFPS = ((int)numericUpDownFPS.Value); + GameProfile.MinTdp = sliderMinTDP.Value; + GameProfile.MaxTdp = sliderMaxTDP.Value; + + AutoTDPUI.UpdateGameProfile(GameProfile); + + Close(); + } + + private void SliderMaxTDP_ValueChanged(object? sender, EventArgs e) + { + labelMaxTDP.Text = sliderMaxTDP.Value + "W"; + if (sliderMaxTDP.Value < sliderMinTDP.Value) + { + sliderMinTDP.Value = sliderMaxTDP.Value; + } + } + + private void SliderMinTDP_ValueChanged(object? sender, EventArgs e) + { + labelMinTDP.Text = sliderMinTDP.Value + "W"; + if (sliderMaxTDP.Value > sliderMinTDP.Value) + { + sliderMaxTDP.Value = sliderMinTDP.Value; + } + } + + private void AutoTDPGameProfileUI_Shown(object? sender, EventArgs e) + { + if (Height > Program.settingsForm.Height) + { + Top = Program.settingsForm.Top + Program.settingsForm.Height - Height; + } + else + { + Top = Program.settingsForm.Top + 60; + } + + Left = Program.settingsForm.Left - Width - ((AutoTDPUI.Width - Width) / 2); + } + + private void VisualizeGameProfile() + { + sliderMinTDP.Value = GameProfile.MinTdp; + sliderMaxTDP.Value = GameProfile.MaxTdp; + numericUpDownFPS.Value = GameProfile.TargetFPS; + textBoxProcessName.Text = GameProfile.ProcessName; + textBoxTitle.Text = GameProfile.GameTitle; + checkBoxEnabled.Checked = GameProfile.Enabled; + } + } +} diff --git a/app/AutoTDP/AutoTDPGameProfileUI.resx b/app/AutoTDP/AutoTDPGameProfileUI.resx new file mode 100644 index 000000000..af32865ec --- /dev/null +++ b/app/AutoTDP/AutoTDPGameProfileUI.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs new file mode 100644 index 000000000..dd9eb2be6 --- /dev/null +++ b/app/AutoTDP/AutoTDPService.cs @@ -0,0 +1,435 @@ +using System.Text.Json; +using GHelper.AutoTDP.FramerateSource; +using GHelper.AutoTDP.PowerLimiter; + +namespace GHelper.AutoTDP +{ + internal class AutoTDPService : IDisposable + { + + private static readonly int INTERVAL_APP_CHECK = 5_000; + private static readonly int INTERVAL_FPS_CHECK = 2_500; + + string GameProfileFile = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\GHelper\\AutoTDP.json"; + + IFramerateSource? framerateSouce; + IPowerLimiter? powerLimiter; + + public List GameProfiles = new List(); + + private bool Running = false; + private Thread? checkerThread; + private Thread? tdpThread; + + private double GameFPSPrevious = double.NaN; + private double GameFPS; + + private int FramerateTargetReachedCounter; + private int FramerateDipCounter; + + private static readonly int FPSDipHistorySize = 8; + + private List FramerateLog = new List(); + + private double CurrentTDP; + + private GameInstance? currentGame; + + public AutoTDPService() + { + LoadGameProfiles(); + + Start(); + } + + /// + /// Whether the system is enabled and currently running. + /// + /// + public bool IsRunning() + { + return Running; + } + + /// + /// Whether a supported game is actively monitored and TDP is adjusted + /// + /// + public bool IsActive() + { + return currentGame is not null; + } + + public void Start() + { + if (!IsEnabled() || IsRunning()) + { + return; + } + + Running = true; + + InitFramerateSource(); + + InitLimiter(); + + + checkerThread = new Thread(() => + { + while (Running) + { + CheckForGame(); + try + { + Thread.Sleep(INTERVAL_APP_CHECK); + } + catch (ThreadInterruptedException) + { + continue; + } + } + }); + checkerThread.Start(); + } + + public bool IsEnabled() + { + return AppConfig.Get("auto_tdp_enabled", 0) == 1; + } + + public void InitFramerateSource() + { + string? source = AppConfig.GetString("auto_tdp_fps_source"); + + if ((source is null || source.Equals("rtss")) && RTSSFramerateSource.IsAvailable()) + { + RTSSFramerateSource rtss = new RTSSFramerateSource(); + RTSSFramerateSource.Start(); + framerateSouce = rtss; + return; + } + } + + public void InitLimiter() + { + string? limiter = AppConfig.GetString("auto_tdp_limiter"); + + if (limiter is null || limiter.Equals("asus_acpi")) + { + powerLimiter = new ASUSACPIPowerLimiter(); + return; + } + + if (limiter is not null && limiter.Equals("intel_msr")) + { + powerLimiter = new IntelMSRPowerLimiter(); + return; + } + } + + public void SaveGameProfiles() + { + string json = JsonSerializer.Serialize(GameProfiles); + + File.WriteAllText(GameProfileFile, json); + } + + + public void LoadGameProfiles() + { + if (!File.Exists(GameProfileFile)) + { + return; + } + + string? json = File.ReadAllText(GameProfileFile); + + if (json == null) + { + return; + } + + try + { + GameProfiles = JsonSerializer.Deserialize>(json); + } + catch (Exception e) + { + Logger.WriteLine("[AutoTDPService] Deserialization failed. Creating empty list. Message: " + e.Message); + GameProfiles = new List(); + } + + } + + public void CheckForGame() + { + if (currentGame is not null) + { + //Already handling a running game. No need to check for other games + return; + } + List runningGames = framerateSouce.GetRunningGames(); + + if (runningGames.Count == 0) + { + Logger.WriteLine("[AutoTDPService] No games detected"); + return; + } + + foreach (GameInstance gi in runningGames) + { + Logger.WriteLine("[AutoTDPService] Detected App: " + gi.ProcessName + " PID: " + gi.ProcessID); + + if (IsGameInList(gi.ProcessName)) + { + Logger.WriteLine("[AutoTDPService] Detected Supported Game: " + gi.ProcessName + " PID: " + gi.ProcessID); + HandleGame(gi); + return; + } + } + } + + public GameProfile? ProfileForGame(String? processName) + { + if (processName is null) + { + return null; + } + + foreach (GameProfile gp in GameProfiles) + { + if (gp.ProcessName is not null && processName.EndsWith(gp.ProcessName)) + { + return gp; + } + } + + return null; + } + + public bool IsGameInList(String? processName) + { + return ProfileForGame(processName) is not null; + } + + + public void HandleGame(GameInstance instance) + { + if (currentGame is not null) + { + Logger.WriteLine("[AutoTDPService] Already handling a game"); + return; + } + + if (tdpThread is not null) + { + tdpThread.Join(); + tdpThread = null; + } + currentGame = instance; + + StartGameHandler(instance); + } + + public void Reset() + { + currentGame = null; + GameFPSPrevious = double.NaN; + GameFPS = 0; + + if (powerLimiter is not null) + { + powerLimiter.ResetPowerLimits(); + CurrentTDP = powerLimiter.GetCPUPowerLimit(); + } + } + + public void StartGameHandler(GameInstance instance) + { + GameProfile? profile = ProfileForGame(instance.ProcessName); + if (profile is null || powerLimiter is null || framerateSouce is null) + { + return; + } + + tdpThread = new Thread(() => + { + CurrentTDP = powerLimiter.GetCPUPowerLimit(); + while (currentGame is not null && Running) + { + GameFPS = framerateSouce.GetFramerate(profile.ProcessName); + + + Logger.WriteLine("[AutoTDPService] (" + instance.ProcessName + ") Framerate " + GameFPS); + + if (GameFPS < 0.0d) + { + //Game is not running anymore or RTSS lost its hook + Reset(); + return; + } + AdjustPowerLimit(profile); + + try + { + Thread.Sleep(INTERVAL_FPS_CHECK); + } + catch (ThreadInterruptedException) + { + continue; + } + + } + }); + tdpThread.Start(); + } + + private double FPSDipCorrection(double currentFramerate, double targetFPS) + { + double correction = 0.0d; + + + FramerateLog.Insert(0, currentFramerate); + + //Remove last entry when exceeding the desired size. + if (FramerateLog.Count > FPSDipHistorySize) + { + FramerateLog.RemoveAt(FramerateLog.Count - 1); + } + + if (targetFPS - 1 <= currentFramerate && currentFramerate <= targetFPS + 1) + { + FramerateTargetReachedCounter++; + + if (FramerateTargetReachedCounter >= 4 + && FramerateTargetReachedCounter < FPSDipHistorySize + && targetFPS - 0.75 <= FramerateLog.Take(4).Average() + && FramerateLog.Take(3).Average() <= targetFPS + 0.15) + { + //short dip + FramerateDipCounter++; + correction = targetFPS + 0.75 - currentFramerate; + } + else if (FramerateDipCounter >= 5 + && targetFPS - 0.75 <= FramerateLog.Average() + && FramerateLog.Average() <= targetFPS + 0.15) + { + //long dip + correction = targetFPS + 1.5 - currentFramerate; + FramerateTargetReachedCounter = FPSDipHistorySize; + } + } + else + { + //No dip, no correction + correction = 0.0; + FramerateTargetReachedCounter = 0; + FramerateDipCounter = 0; + } + + return correction; + } + + + private double TDPDamper(double currentFramerate) + { + if (double.IsNaN(GameFPSPrevious)) GameFPSPrevious = currentFramerate; + double dF = -0.1d; + + // Calculation + double deltaError = currentFramerate - GameFPSPrevious; + double dT = deltaError / (1010.0 / 1000.0); + double damping = CurrentTDP / currentFramerate * dF * dT; + + GameFPSPrevious = currentFramerate; + + return damping; + } + + public void AdjustPowerLimit(GameProfile profile) + { + + if (powerLimiter is null) + { + //Should not happen... but we also don't want it to crash + return; + } + + double newPL = CurrentTDP; + + + Logger.WriteLine("[AutoTDPService] Current: " + (int)GameFPS + "FPS"); + + + double delta = profile.TargetFPS - GameFPS - FPSDipCorrection(GameFPS, profile.TargetFPS); + delta = Math.Clamp(delta, -15, 15); + + + double adjustment = (delta * CurrentTDP / GameFPS) * 0.85; + //Dampen the changes to not change TDP too aggressively which would cause performance issues + adjustment += TDPDamper(GameFPS); + + + newPL += adjustment; + + //Respect the limits that the user chose + newPL = Math.Clamp(newPL, profile.MinTdp, profile.MaxTdp); + + Logger.WriteLine("[AutoTDPService] Setting Power Limit from " + CurrentTDP + "W to " + newPL + "W, Delta:" + adjustment); + + //We only limit to full watts, no fractions. In this case, we will cut off the fractional part + powerLimiter.SetCPUPowerLimit((int)newPL); + CurrentTDP = newPL; + } + + public void StopGameHandler() + { + if (tdpThread is not null) + { + currentGame = null; + tdpThread.Join(); + tdpThread = null; + } + + } + + public void Shutdown() + { + Running = false; + + if (checkerThread is not null) + { + checkerThread.Interrupt(); + checkerThread.Join(); + } + + + if (tdpThread is not null) + { + tdpThread.Interrupt(); + tdpThread.Join(); + } + + if (powerLimiter is not null) + { + powerLimiter.ResetPowerLimits(); + powerLimiter.Dispose(); + powerLimiter = null; + } + + if (framerateSouce is not null) + { + framerateSouce = null; + } + + //Kill RTSS instance if we started one + RTSSFramerateSource.Stop(); + + } + + public void Dispose() + { + Shutdown(); + } + } +} diff --git a/app/AutoTDP/AutoTDPUI.Designer.cs b/app/AutoTDP/AutoTDPUI.Designer.cs new file mode 100644 index 000000000..0b48a2b1b --- /dev/null +++ b/app/AutoTDP/AutoTDPUI.Designer.cs @@ -0,0 +1,311 @@ +using GHelper.UI; + +namespace GHelper.AutoTDP +{ + partial class AutoTDPUI + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + panelPerformanceHeader = new Panel(); + pictureKeyboard = new PictureBox(); + labelGlobalSettings = new Label(); + checkBoxEnabled = new CheckBox(); + panelLightingContent = new Panel(); + comboBoxFPSSource = new RComboBox(); + labelFPSSource = new Label(); + comboBoxLimiter = new RComboBox(); + labelLimiter = new Label(); + buttonAddGame = new RButton(); + panelGamesHeader = new Panel(); + pictureBox1 = new PictureBox(); + labelGames = new Label(); + tableLayoutGames = new TableLayoutPanel(); + buttonGameDummy = new RButton(); + panelPerformanceHeader.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)pictureKeyboard).BeginInit(); + panelLightingContent.SuspendLayout(); + panelGamesHeader.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)pictureBox1).BeginInit(); + tableLayoutGames.SuspendLayout(); + SuspendLayout(); + // + // panelPerformanceHeader + // + panelPerformanceHeader.BackColor = SystemColors.ControlLight; + panelPerformanceHeader.Controls.Add(pictureKeyboard); + panelPerformanceHeader.Controls.Add(labelGlobalSettings); + panelPerformanceHeader.Controls.Add(checkBoxEnabled); + panelPerformanceHeader.Dock = DockStyle.Top; + panelPerformanceHeader.Location = new Point(0, 0); + panelPerformanceHeader.Margin = new Padding(2); + panelPerformanceHeader.Name = "panelPerformanceHeader"; + panelPerformanceHeader.Size = new Size(446, 30); + panelPerformanceHeader.TabIndex = 52; + // + // pictureKeyboard + // + pictureKeyboard.BackgroundImage = Properties.Resources.icons8_automation_32; + pictureKeyboard.BackgroundImageLayout = ImageLayout.Zoom; + pictureKeyboard.Location = new Point(3, 8); + pictureKeyboard.Margin = new Padding(2); + pictureKeyboard.Name = "pictureKeyboard"; + pictureKeyboard.Size = new Size(16, 16); + pictureKeyboard.TabIndex = 35; + pictureKeyboard.TabStop = false; + // + // labelGlobalSettings + // + labelGlobalSettings.AutoSize = true; + labelGlobalSettings.Font = new Font("Segoe UI", 9F, FontStyle.Bold, GraphicsUnit.Point); + labelGlobalSettings.Location = new Point(22, 8); + labelGlobalSettings.Margin = new Padding(4, 0, 4, 0); + labelGlobalSettings.Name = "labelGlobalSettings"; + labelGlobalSettings.Size = new Size(100, 15); + labelGlobalSettings.TabIndex = 34; + labelGlobalSettings.Text = "General Settings"; + // + // checkBoxEnabled + // + checkBoxEnabled.Location = new Point(244, 4); + checkBoxEnabled.Margin = new Padding(4, 0, 4, 0); + checkBoxEnabled.Name = "checkBoxEnabled"; + checkBoxEnabled.Size = new Size(198, 25); + checkBoxEnabled.TabIndex = 53; + checkBoxEnabled.Text = "Enabled"; + checkBoxEnabled.UseVisualStyleBackColor = true; + // + // panelLightingContent + // + panelLightingContent.AutoSize = true; + panelLightingContent.Controls.Add(comboBoxFPSSource); + panelLightingContent.Controls.Add(labelFPSSource); + panelLightingContent.Controls.Add(comboBoxLimiter); + panelLightingContent.Controls.Add(labelLimiter); + panelLightingContent.Dock = DockStyle.Top; + panelLightingContent.Location = new Point(0, 30); + panelLightingContent.Margin = new Padding(2); + panelLightingContent.Name = "panelLightingContent"; + panelLightingContent.Padding = new Padding(0, 0, 0, 7); + panelLightingContent.Size = new Size(446, 67); + panelLightingContent.TabIndex = 56; + // + // comboBoxFPSSource + // + comboBoxFPSSource.BorderColor = Color.White; + comboBoxFPSSource.ButtonColor = Color.FromArgb(255, 255, 255); + comboBoxFPSSource.DropDownStyle = ComboBoxStyle.DropDownList; + comboBoxFPSSource.FlatStyle = FlatStyle.Flat; + comboBoxFPSSource.FormattingEnabled = true; + comboBoxFPSSource.Location = new Point(244, 37); + comboBoxFPSSource.Margin = new Padding(11, 0, 11, 0); + comboBoxFPSSource.Name = "comboBoxFPSSource"; + comboBoxFPSSource.Size = new Size(191, 23); + comboBoxFPSSource.TabIndex = 56; + // + // labelFPSSource + // + labelFPSSource.Location = new Point(4, 37); + labelFPSSource.Margin = new Padding(4, 0, 4, 0); + labelFPSSource.Name = "labelFPSSource"; + labelFPSSource.Size = new Size(211, 22); + labelFPSSource.TabIndex = 57; + labelFPSSource.Text = "FPS Source"; + // + // comboBoxLimiter + // + comboBoxLimiter.BorderColor = Color.White; + comboBoxLimiter.ButtonColor = Color.FromArgb(255, 255, 255); + comboBoxLimiter.DropDownStyle = ComboBoxStyle.DropDownList; + comboBoxLimiter.FlatStyle = FlatStyle.Flat; + comboBoxLimiter.FormattingEnabled = true; + comboBoxLimiter.Location = new Point(244, 7); + comboBoxLimiter.Margin = new Padding(11, 0, 11, 0); + comboBoxLimiter.Name = "comboBoxLimiter"; + comboBoxLimiter.Size = new Size(191, 23); + comboBoxLimiter.TabIndex = 46; + // + // labelLimiter + // + labelLimiter.Location = new Point(4, 7); + labelLimiter.Margin = new Padding(4, 0, 4, 0); + labelLimiter.Name = "labelLimiter"; + labelLimiter.Size = new Size(211, 22); + labelLimiter.TabIndex = 47; + labelLimiter.Text = "Power Limiter"; + // + // buttonAddGame + // + buttonAddGame.AccessibleName = "Keyboard Color"; + buttonAddGame.Activated = false; + buttonAddGame.Anchor = AnchorStyles.Top | AnchorStyles.Right; + buttonAddGame.BackColor = SystemColors.ButtonHighlight; + buttonAddGame.BorderColor = Color.Transparent; + buttonAddGame.BorderRadius = 2; + buttonAddGame.FlatStyle = FlatStyle.Flat; + buttonAddGame.ForeColor = SystemColors.ControlText; + buttonAddGame.Location = new Point(339, 3); + buttonAddGame.Margin = new Padding(2, 4, 2, 4); + buttonAddGame.Name = "buttonAddGame"; + buttonAddGame.Secondary = false; + buttonAddGame.Size = new Size(103, 25); + buttonAddGame.TabIndex = 60; + buttonAddGame.Text = "Add Game"; + buttonAddGame.UseVisualStyleBackColor = false; + // + // panelGamesHeader + // + panelGamesHeader.BackColor = SystemColors.ControlLight; + panelGamesHeader.Controls.Add(pictureBox1); + panelGamesHeader.Controls.Add(buttonAddGame); + panelGamesHeader.Controls.Add(labelGames); + panelGamesHeader.Dock = DockStyle.Top; + panelGamesHeader.Location = new Point(0, 97); + panelGamesHeader.Margin = new Padding(2); + panelGamesHeader.Name = "panelGamesHeader"; + panelGamesHeader.Size = new Size(446, 30); + panelGamesHeader.TabIndex = 61; + // + // pictureBox1 + // + pictureBox1.BackgroundImage = Properties.Resources.icons8_software_32_white; + pictureBox1.BackgroundImageLayout = ImageLayout.Zoom; + pictureBox1.Location = new Point(3, 8); + pictureBox1.Margin = new Padding(2); + pictureBox1.Name = "pictureBox1"; + pictureBox1.Size = new Size(16, 16); + pictureBox1.TabIndex = 35; + pictureBox1.TabStop = false; + // + // labelGames + // + labelGames.AutoSize = true; + labelGames.Font = new Font("Segoe UI", 9F, FontStyle.Bold, GraphicsUnit.Point); + labelGames.Location = new Point(22, 8); + labelGames.Margin = new Padding(4, 0, 4, 0); + labelGames.Name = "labelGames"; + labelGames.Size = new Size(45, 15); + labelGames.TabIndex = 34; + labelGames.Text = "Games"; + // + // tableLayoutGames + // + tableLayoutGames.AutoSize = true; + tableLayoutGames.AutoSizeMode = AutoSizeMode.GrowAndShrink; + tableLayoutGames.ColumnCount = 4; + tableLayoutGames.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 25F)); + tableLayoutGames.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 25F)); + tableLayoutGames.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 25F)); + tableLayoutGames.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 25F)); + tableLayoutGames.Controls.Add(buttonGameDummy, 0, 0); + tableLayoutGames.Dock = DockStyle.Top; + tableLayoutGames.Location = new Point(0, 127); + tableLayoutGames.Margin = new Padding(4, 2, 4, 2); + tableLayoutGames.Name = "tableLayoutGames"; + tableLayoutGames.RowCount = 7; + tableLayoutGames.RowStyles.Add(new RowStyle(SizeType.Absolute, 60F)); + tableLayoutGames.RowStyles.Add(new RowStyle(SizeType.Absolute, 60F)); + tableLayoutGames.RowStyles.Add(new RowStyle(SizeType.Absolute, 60F)); + tableLayoutGames.RowStyles.Add(new RowStyle(SizeType.Absolute, 60F)); + tableLayoutGames.RowStyles.Add(new RowStyle(SizeType.Absolute, 60F)); + tableLayoutGames.RowStyles.Add(new RowStyle(SizeType.Absolute, 60F)); + tableLayoutGames.RowStyles.Add(new RowStyle(SizeType.Absolute, 60F)); + tableLayoutGames.Size = new Size(446, 420); + tableLayoutGames.TabIndex = 62; + // + // buttonGameDummy + // + buttonGameDummy.AccessibleName = "DPI Setting 4"; + buttonGameDummy.Activated = false; + buttonGameDummy.AutoSize = true; + buttonGameDummy.AutoSizeMode = AutoSizeMode.GrowAndShrink; + buttonGameDummy.BackColor = SystemColors.ControlLightLight; + buttonGameDummy.BorderColor = Color.LightGreen; + buttonGameDummy.BorderRadius = 5; + buttonGameDummy.Dock = DockStyle.Fill; + buttonGameDummy.FlatAppearance.BorderSize = 0; + buttonGameDummy.FlatStyle = FlatStyle.Flat; + buttonGameDummy.ForeColor = SystemColors.ControlText; + buttonGameDummy.ImageAlign = ContentAlignment.BottomCenter; + buttonGameDummy.Location = new Point(2, 2); + buttonGameDummy.Margin = new Padding(2); + buttonGameDummy.Name = "buttonGameDummy"; + buttonGameDummy.Secondary = false; + buttonGameDummy.Size = new Size(107, 56); + buttonGameDummy.TabIndex = 7; + buttonGameDummy.Text = "Genshin Impact\r\n60FPS\r\n"; + buttonGameDummy.TextImageRelation = TextImageRelation.ImageAboveText; + buttonGameDummy.UseVisualStyleBackColor = false; + buttonGameDummy.Visible = false; + // + // AutoTDPUI + // + AutoScaleDimensions = new SizeF(7F, 15F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(446, 547); + Controls.Add(tableLayoutGames); + Controls.Add(panelGamesHeader); + Controls.Add(panelLightingContent); + Controls.Add(panelPerformanceHeader); + FormBorderStyle = FormBorderStyle.FixedSingle; + MaximizeBox = false; + MinimizeBox = false; + Name = "AutoTDPUI"; + ShowIcon = false; + Text = "Auto TDP Settings"; + panelPerformanceHeader.ResumeLayout(false); + panelPerformanceHeader.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)pictureKeyboard).EndInit(); + panelLightingContent.ResumeLayout(false); + panelGamesHeader.ResumeLayout(false); + panelGamesHeader.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)pictureBox1).EndInit(); + tableLayoutGames.ResumeLayout(false); + tableLayoutGames.PerformLayout(); + ResumeLayout(false); + PerformLayout(); + } + + #endregion + private Panel panelPerformanceHeader; + private PictureBox pictureKeyboard; + private Label labelGlobalSettings; + private Panel panelLightingContent; + private TableLayoutPanel tableLayoutGames; + private UI.RButton rButton1; + private CheckBox checkBoxEnabled; + private UI.RComboBox comboBoxLightingMode; + private Label labelLimiter; + private UI.RComboBox comboBoxFPSSource; + private Label labelFPSSource; + private RComboBox comboBoxLimiter; + private RButton buttonAddGame; + private Panel panelGamesHeader; + private PictureBox pictureBox1; + private Label labelGames; + private RButton buttonGameDummy; + } +} \ No newline at end of file diff --git a/app/AutoTDP/AutoTDPUI.cs b/app/AutoTDP/AutoTDPUI.cs new file mode 100644 index 000000000..525cb2990 --- /dev/null +++ b/app/AutoTDP/AutoTDPUI.cs @@ -0,0 +1,222 @@ +using GHelper.AutoTDP.FramerateSource; +using GHelper.UI; +using Ryzen; + +namespace GHelper.AutoTDP +{ + public partial class AutoTDPUI : RForm + { + private AutoTDPGameProfileUI? profileUI; + public AutoTDPUI() + { + InitializeComponent(); + + InitTheme(); + + checkBoxEnabled.CheckedChanged += CheckBoxEnabled_CheckedChanged; + buttonAddGame.Click += ButtonAddGame_Click; + + comboBoxLimiter.DropDownClosed += ComboBoxLimiter_DropDownClosed; + + comboBoxFPSSource.DropDownClosed += ComboBoxFPSSource_DropDownClosed; + + Shown += AutoTDPUI_Shown; + + VisualizeGeneralSettings(); + VizualizeGameList(); + } + + private void ComboBoxFPSSource_DropDownClosed(object? sender, EventArgs e) + { + if ((comboBoxFPSSource.SelectedItem as string).StartsWith("Riva")) + { + AppConfig.Set("auto_tdp_fps_source", "rtss"); + } + } + + private void ComboBoxLimiter_DropDownClosed(object? sender, EventArgs e) + { + if ((comboBoxLimiter.SelectedItem as string).StartsWith("Intel")) + { + AppConfig.Set("auto_tdp_limiter", "intel_msr"); + } + + + if ((comboBoxLimiter.SelectedItem as string).StartsWith("ASUS ACPI")) + { + AppConfig.Set("auto_tdp_limiter", "asus_acpi"); + } + } + + private void CheckBoxEnabled_CheckedChanged(object? sender, EventArgs e) + { + AppConfig.Set("auto_tdp_enabled", checkBoxEnabled.Checked ? 1 : 0); + + if (Program.autoTDPService.IsEnabled()) + { + Program.autoTDPService.Start(); + } + else + { + Program.autoTDPService.Shutdown(); + } + } + + private void ButtonAddGame_Click(object? sender, EventArgs e) + { + string? path = null; + Thread t = new Thread(() => + { + OpenFileDialog ofd = new OpenFileDialog(); + ofd.Filter = "Executables (*.exe)|*.exe"; + + if (ofd.ShowDialog() == DialogResult.OK) + { + path = ofd.FileName; + } + }); + + t.SetApartmentState(ApartmentState.STA); + t.Start(); + t.Join(); + + if (path is null) + { + //User did not select a file + return; + } + + GameProfile p = new GameProfile(); + p.ProcessName = Path.GetFileName(path); + p.GameTitle = Path.GetFileName(path); + p.Enabled = true; + p.TargetFPS = 60; + p.MaxTdp = 40; + p.MinTdp = 20; + + profileUI = new AutoTDPGameProfileUI(p, this); + profileUI.TopMost = true; + profileUI.FormClosed += ProfileUI_FormClosed; + profileUI.Show(); + } + + private void ProfileUI_FormClosed(object? sender, FormClosedEventArgs e) + { + profileUI = null; + } + + private void AutoTDPUI_Shown(object? sender, EventArgs e) + { + if (Height > Program.settingsForm.Height) + { + Top = Program.settingsForm.Top + Program.settingsForm.Height - Height; + } + else + { + Top = Program.settingsForm.Top; + } + + Left = Program.settingsForm.Left - Width - 5; + } + + private void VisualizeGeneralSettings() + { + checkBoxEnabled.Checked = AppConfig.Get("auto_tdp_enabled", 0) == 1; + + if (!RyzenControl.IsAMD()) + comboBoxLimiter.Items.Add("Intel MSR Power Limiter"); + + + comboBoxLimiter.Items.Add("ASUS ACPI Power Limiter"); + + string? limiter = AppConfig.GetString("auto_tdp_limiter"); + + if (comboBoxLimiter.Items.Count > 0 && limiter is null) + comboBoxLimiter.SelectedIndex = 0; + + if (!RyzenControl.IsAMD() && limiter is not null && limiter.Equals("intel_msr")) + { + comboBoxLimiter.SelectedIndex = 0; + } + + if (limiter is not null && limiter.Equals("asus_acpi")) + { + comboBoxLimiter.SelectedIndex = !RyzenControl.IsAMD() ? 1 : 0; + } + + + + if (RTSSFramerateSource.IsAvailable()) + comboBoxFPSSource.Items.Add("Riva Tuner Statistics Server"); + + + string? source = AppConfig.GetString("auto_tdp_fps_source", null); + + if (comboBoxFPSSource.Items.Count > 0 && source is null) + comboBoxFPSSource.SelectedIndex = 0; + + if (source is not null && source.Equals("rtss")) + { + comboBoxFPSSource.SelectedIndex = 0; + } + } + + + private void VizualizeGameList() + { + //Due to my lousy skills in UI design, the game table is limited to 7x4 games. + buttonAddGame.Enabled = Program.autoTDPService.GameProfiles.Count < 7 * 4; + + tableLayoutGames.Controls.Clear(); + + foreach (GameProfile gp in Program.autoTDPService.GameProfiles) + { + RButton bt = new RButton(); + bt.Text = gp.GameTitle + "\n" + gp.TargetFPS + " FPS"; + + bt.Dock = DockStyle.Fill; + bt.FlatStyle = FlatStyle.Flat; + bt.FlatAppearance.BorderColor = RForm.borderMain; + bt.UseVisualStyleBackColor = false; + bt.AutoSize = true; + bt.AutoSizeMode = AutoSizeMode.GrowAndShrink; + bt.BackColor = RForm.buttonMain; + bt.ForeColor = RForm.foreMain; + bt.Click += Bt_Click; + bt.Tag = gp; + + tableLayoutGames.Controls.Add(bt); + } + } + + private void Bt_Click(object? sender, EventArgs e) + { + GameProfile gp = (GameProfile)((RButton)sender).Tag; + profileUI = new AutoTDPGameProfileUI(gp, this); + profileUI.TopMost = true; + profileUI.FormClosed += ProfileUI_FormClosed; + profileUI.Show(); + } + + public void DeleteGameProfile(GameProfile gp) + { + if (Program.autoTDPService.IsGameInList(gp.ProcessName)) + { + Program.autoTDPService.GameProfiles.Remove(gp); + } + + Program.autoTDPService.SaveGameProfiles(); + VizualizeGameList(); + } + + public void UpdateGameProfile(GameProfile gp) + { + if (!Program.autoTDPService.IsGameInList(gp.ProcessName)) + { + Program.autoTDPService.GameProfiles.Add(gp); + } + Program.autoTDPService.SaveGameProfiles(); + VizualizeGameList(); + } + } +} diff --git a/app/AutoTDP/AutoTDPUI.resx b/app/AutoTDP/AutoTDPUI.resx new file mode 100644 index 000000000..af32865ec --- /dev/null +++ b/app/AutoTDP/AutoTDPUI.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/app/AutoTDP/FramerateSource/IFramerateSource.cs b/app/AutoTDP/FramerateSource/IFramerateSource.cs new file mode 100644 index 000000000..3bf7aa857 --- /dev/null +++ b/app/AutoTDP/FramerateSource/IFramerateSource.cs @@ -0,0 +1,17 @@ +namespace GHelper.AutoTDP.FramerateSource +{ + + internal class GameInstance + { + public string? ProcessName { get; set; } + + public int ProcessID { get; set; } + } + + internal interface IFramerateSource + { + public double GetFramerate(string processName); + + public List GetRunningGames(); + } +} diff --git a/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs b/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs new file mode 100644 index 000000000..e70ab6ac3 --- /dev/null +++ b/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using RTSSSharedMemoryNET; + +namespace GHelper.AutoTDP.FramerateSource +{ + internal class RTSSFramerateSource : IFramerateSource + { + private static Process? rtssInstance; + + private static OSD? osd; + + public static string RTSSPath { get; set; } + + + public static bool IsRunning => Process.GetProcessesByName("RTSS").Length != 0; + + + static RTSSFramerateSource() + { + RTSSPath = @"C:\Program Files (x86)\RivaTuner Statistics Server\RTSS.exe"; + } + + public static bool IsAvailable() + { + return File.Exists(RTSSPath); + + } + + public static void Start() + { + if ((rtssInstance == null || rtssInstance.HasExited) && !IsRunning && File.Exists(RTSSPath)) + { + try + { + rtssInstance = Process.Start(RTSSPath); + Thread.Sleep(2000); // If it works, don't touch it + } + catch (Exception exc) + { + Logger.WriteLine("Could not start RTSS Service" + exc.Message); + } + + RunOSD(); + } + else + { + RunOSD(); + } + } + + public List GetRunningGames() + { + if (!IsRunning) + { + return new List(); + } + + List giL = new List(); + + foreach (AppEntry appEntry in OSD.GetAppEntries()) + { + GameInstance i = new GameInstance(); + i.ProcessID = appEntry.ProcessId; + i.ProcessName = appEntry.Name; + + giL.Add(i); + } + + return giL; + } + + public double GetFramerate(string processName) + { + if (!IsRunning) + { + return -1.0d; + } + + try + { + var appE = OSD.GetAppEntries() + .Where(x => (x.Flags & AppFlags.MASK) != AppFlags.None).FirstOrDefault(a => a.Name.EndsWith(processName)); + if (appE is null) + return -1.0d; + + return (double)appE.StatFrameTimeBufFramerate / 10; + } + catch (InvalidDataException) + { + } + catch (FileNotFoundException) + { + } + + return -1.0d; + } + + public static void RunOSD() + { + if (osd == null) + { + try + { + osd = new OSD("GHELPER"); + } + catch (Exception exc) + { + Logger.WriteLine("Could not start OSD" + exc.Message); + } + } + } + + public static void Stop() + { + if (rtssInstance != null && !rtssInstance.HasExited) + { + try + { + rtssInstance.Kill(); + rtssInstance = null; + var proc = Process.GetProcessesByName("RTSSHooksLoader64"); + proc[0].Kill(); + } + catch (Exception) + { + // Ignored + } + } + } + } +} diff --git a/app/AutoTDP/GameProfile.cs b/app/AutoTDP/GameProfile.cs new file mode 100644 index 000000000..c01655923 --- /dev/null +++ b/app/AutoTDP/GameProfile.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GHelper.AutoTDP +{ + public class GameProfile + { + public string GameTitle { get; set; } + public string ProcessName { get; set; } + public int TargetFPS { get; set; } + public int MinTdp { get; set; } + public int MaxTdp { get; set; } + public bool Enabled { get; set; } + } +} diff --git a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs new file mode 100644 index 000000000..4c5ab738b --- /dev/null +++ b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GHelper.AutoTDP.PowerLimiter +{ + internal class ASUSACPIPowerLimiter : IPowerLimiter + { + + private int DefaultA0 = Program.acpi.DeviceGet(AsusACPI.PPT_APUA0); + private int DefaultA3 = Program.acpi.DeviceGet(AsusACPI.PPT_APUA0); + + public void SetCPUPowerLimit(int watts) + { + if (Program.acpi.DeviceGet(AsusACPI.PPT_APUA0) >= 0) + { + Program.acpi.DeviceSet(AsusACPI.PPT_APUA3, watts, "PowerLimit A3"); + Program.acpi.DeviceSet(AsusACPI.PPT_APUA0, watts, "PowerLimit A0"); + } + } + + + public int GetCPUPowerLimit() + { + return Program.acpi.DeviceGet(AsusACPI.PPT_APUA0); + } + + public void Dispose() + { + //Nothing to dispose here + } + + public void ResetPowerLimits() + { + //Load limits that were set before the limiter engaged + Program.acpi.DeviceSet(AsusACPI.PPT_APUA3, DefaultA0, "PowerLimit A3"); + Program.acpi.DeviceSet(AsusACPI.PPT_APUA0, DefaultA3, "PowerLimit A0"); + } + } +} diff --git a/app/AutoTDP/PowerLimiter/IPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IPowerLimiter.cs new file mode 100644 index 000000000..05e019962 --- /dev/null +++ b/app/AutoTDP/PowerLimiter/IPowerLimiter.cs @@ -0,0 +1,12 @@ +namespace GHelper.AutoTDP.PowerLimiter +{ + internal interface IPowerLimiter : IDisposable + { + public void SetCPUPowerLimit(int watts); + + public int GetCPUPowerLimit(); + + public void ResetPowerLimits(); + + } +} diff --git a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs new file mode 100644 index 000000000..5d93ae5b6 --- /dev/null +++ b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs @@ -0,0 +1,106 @@ +using HidSharp.Reports.Units; +using Ryzen; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GHelper.AutoTDP.PowerLimiter +{ + internal class IntelMSRPowerLimiter : IPowerLimiter + { + private Ols ols; + + private uint DefaultEax = 0; // Set on first reading + private uint DefaultEdx = 0; + + //Lower 14 bits are the power limits + private uint PL1_MASK = 0x3FFF; + private uint PL2_MASK = 0x3FFF; + + //The power unit factor (Default is 0.125 for most Intel CPUs). + private double PowerUnit = 0x0; + + public IntelMSRPowerLimiter() + { + ols = new Ols(); + ols.InitializeOls(); + ReadPowerUnit(); + } + + public void ReadPowerUnit() + { + uint eax = 0; + uint edx = 0; + + ols.Rdmsr(0x606, ref eax, ref edx); + + + uint pwr = eax & 0x03; + + PowerUnit = 1 / Math.Pow(2, pwr); + } + + public void SetCPUPowerLimit(int watts) + { + uint eax = 0; + uint edx = 0; + + + ols.Rdmsr(0x610, ref eax, ref edx); + + uint watsRapl = (uint)(watts / PowerUnit); + + //Set limits for both PL1 and PL2 + uint eaxFilterd = eax & ~PL1_MASK; + uint edxFilterd = edx & ~PL2_MASK; + + eaxFilterd |= watsRapl; + edxFilterd |= watsRapl; + + //Enable clamping + eaxFilterd |= 0x8000; + edxFilterd |= 0x8000; + + ols.Wrmsr(0x610, eaxFilterd, edxFilterd); + } + + + public int GetCPUPowerLimit() + { + uint eax = 0; + uint edx = 0; + + ols.Rdmsr(0x610, ref eax, ref edx); + + if (DefaultEax == 0) + { + //Store default settings to reset them on exit + DefaultEax = eax; + DefaultEdx = edx; + } + + uint pl1 = eax & PL1_MASK; + uint pl2 = edx & PL2_MASK; + + return (int)(pl1 * PowerUnit); + } + + + public void ResetPowerLimits() + { + if (DefaultEax == 0) + { + return; + } + ols.Wrmsr(0x610, DefaultEax, DefaultEdx); + } + + public void Dispose() + { + ols.DeinitializeOls(); + ols.Dispose(); + } + } +} diff --git a/app/GHelper.csproj b/app/GHelper.csproj index 56011b394..656a5a4df 100644 --- a/app/GHelper.csproj +++ b/app/GHelper.csproj @@ -88,6 +88,14 @@ + + + RTSSSharedMemoryNET.dll + PreserveNewest + true + + + True diff --git a/app/GHelper.sln b/app/GHelper.sln index 0b4abb975..64effb6ea 100644 --- a/app/GHelper.sln +++ b/app/GHelper.sln @@ -14,18 +14,24 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Debug|Any CPU.ActiveCfg = Debug|x64 {D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Debug|Any CPU.Build.0 = Debug|x64 {D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Debug|x64.ActiveCfg = Debug|Any CPU {D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Debug|x64.Build.0 = Debug|Any CPU + {D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Debug|x86.ActiveCfg = Debug|Any CPU + {D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Debug|x86.Build.0 = Debug|Any CPU {D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Release|Any CPU.ActiveCfg = Release|x64 {D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Release|Any CPU.Build.0 = Release|x64 {D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Release|x64.ActiveCfg = Release|x64 {D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Release|x64.Build.0 = Release|x64 + {D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Release|x86.ActiveCfg = Release|Any CPU + {D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/app/Program.cs b/app/Program.cs index 7cd18809d..2f07fda86 100644 --- a/app/Program.cs +++ b/app/Program.cs @@ -1,4 +1,5 @@ using GHelper.Ally; +using GHelper.AutoTDP; using GHelper.Battery; using GHelper.Display; using GHelper.Gpu; @@ -46,6 +47,8 @@ static class Program private static PowerLineStatus isPlugged = SystemInformation.PowerStatus.PowerLineStatus; + public static AutoTDPService autoTDPService = new AutoTDPService(); + // The main entry point for the application public static void Main(string[] args) { @@ -306,6 +309,7 @@ static void TrayIcon_MouseClick(object? sender, MouseEventArgs e) static void OnExit(object sender, EventArgs e) { + autoTDPService.Shutdown(); trayIcon.Visible = false; PeripheralsProvider.UnregisterForDeviceEvents(); clamshellControl.UnregisterDisplayEvents(); diff --git a/app/RTSSSharedMemoryNET.dll b/app/RTSSSharedMemoryNET.dll new file mode 100644 index 0000000000000000000000000000000000000000..37197eaaf571ad4342952711ecbe7d556c2c2f8c GIT binary patch literal 75264 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 literal 0 HcmV?d00001 diff --git a/app/Settings.Designer.cs b/app/Settings.Designer.cs index e08037a2f..6e2f9ed02 100644 --- a/app/Settings.Designer.cs +++ b/app/Settings.Designer.cs @@ -67,6 +67,8 @@ private void InitializeComponent() labelCPUFan = new Label(); panelGPU = new Panel(); labelTipGPU = new Label(); + tableAdditionalGPUFeature = new TableLayoutPanel(); + buttonAutoTDP = new RButton(); tableAMD = new TableLayoutPanel(); buttonOverlay = new RButton(); buttonFPS = new RButton(); @@ -143,6 +145,7 @@ private void InitializeComponent() panelCPUTitle.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)picturePerf).BeginInit(); panelGPU.SuspendLayout(); + tableAdditionalGPUFeature.SuspendLayout(); tableAMD.SuspendLayout(); tableGPU.SuspendLayout(); panelGPUTitle.SuspendLayout(); @@ -723,6 +726,7 @@ private void InitializeComponent() panelGPU.AutoSize = true; panelGPU.AutoSizeMode = AutoSizeMode.GrowAndShrink; panelGPU.Controls.Add(labelTipGPU); + panelGPU.Controls.Add(tableAdditionalGPUFeature); panelGPU.Controls.Add(tableAMD); panelGPU.Controls.Add(tableGPU); panelGPU.Controls.Add(panelGPUTitle); @@ -745,6 +749,46 @@ private void InitializeComponent() labelTipGPU.Size = new Size(787, 36); labelTipGPU.TabIndex = 20; // + // tableAdditionalGPUFeature + // + tableAdditionalGPUFeature.AutoSize = true; + tableAdditionalGPUFeature.AutoSizeMode = AutoSizeMode.GrowAndShrink; + tableAdditionalGPUFeature.ColumnCount = 3; + tableAdditionalGPUFeature.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33.3333321F)); + tableAdditionalGPUFeature.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33.3333321F)); + tableAdditionalGPUFeature.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33.3333321F)); + tableAdditionalGPUFeature.Controls.Add(buttonAutoTDP, 0, 0); + tableAdditionalGPUFeature.Dock = DockStyle.Top; + tableAdditionalGPUFeature.Location = new Point(10, 198); + tableAdditionalGPUFeature.Margin = new Padding(2, 1, 2, 1); + tableAdditionalGPUFeature.Name = "tableAdditionalGPUFeature"; + tableAdditionalGPUFeature.RowCount = 1; + tableAdditionalGPUFeature.RowStyles.Add(new RowStyle(SizeType.Absolute, 60F)); + tableAdditionalGPUFeature.Size = new Size(392, 60); + tableAdditionalGPUFeature.TabIndex = 25; + // + // buttonAutoTDP + // + buttonAutoTDP.Activated = false; + buttonAutoTDP.BackColor = SystemColors.ControlLightLight; + buttonAutoTDP.BorderColor = Color.Transparent; + buttonAutoTDP.BorderRadius = 5; + buttonAutoTDP.Dock = DockStyle.Fill; + buttonAutoTDP.FlatAppearance.BorderSize = 0; + buttonAutoTDP.FlatStyle = FlatStyle.Flat; + buttonAutoTDP.ForeColor = SystemColors.ControlText; + buttonAutoTDP.Image = Properties.Resources.icons8_processor_32; + buttonAutoTDP.ImageAlign = ContentAlignment.MiddleRight; + buttonAutoTDP.Location = new Point(1, 1); + buttonAutoTDP.Margin = new Padding(1, 1, 1, 1); + buttonAutoTDP.Name = "buttonAutoTDP"; + buttonAutoTDP.Secondary = false; + buttonAutoTDP.Size = new Size(128, 38); + buttonAutoTDP.TabIndex = 11; + buttonAutoTDP.Text = "Auto TDP"; + buttonAutoTDP.TextImageRelation = TextImageRelation.ImageBeforeText; + buttonAutoTDP.UseVisualStyleBackColor = false; + // // tableAMD // tableAMD.AutoSize = true; @@ -1790,6 +1834,7 @@ private void InitializeComponent() ((System.ComponentModel.ISupportInitialize)picturePerf).EndInit(); panelGPU.ResumeLayout(false); panelGPU.PerformLayout(); + tableAdditionalGPUFeature.ResumeLayout(false); tableAMD.ResumeLayout(false); tableGPU.ResumeLayout(false); panelGPUTitle.ResumeLayout(false); @@ -1931,5 +1976,7 @@ private void InitializeComponent() private Label labelGammaTitle; private CheckBox checkMatrixLid; private Panel panelMatrixAuto; + private TableLayoutPanel tableAdditionalGPUFeature; + private RButton buttonAutoTDP; } } diff --git a/app/Settings.cs b/app/Settings.cs index b5c34a811..3355764a2 100644 --- a/app/Settings.cs +++ b/app/Settings.cs @@ -1,5 +1,6 @@ using GHelper.Ally; using GHelper.AnimeMatrix; +using GHelper.AutoTDP; using GHelper.AutoUpdate; using GHelper.Battery; using GHelper.Display; @@ -28,6 +29,7 @@ public partial class SettingsForm : RForm AutoUpdateControl updateControl; AsusMouseSettings? mouseSettings; + AutoTDPUI? autoTdpUi; public AniMatrixControl matrixControl; @@ -244,6 +246,8 @@ public SettingsForm() buttonFPS.Click += ButtonFPS_Click; buttonOverlay.Click += ButtonOverlay_Click; + buttonAutoTDP.Click += ButtonAutoTDP_Click; + Text = "G-Helper " + (ProcessHelper.IsUserAdministrator() ? "—" : "-") + " " + AppConfig.GetModelShort(); TopMost = AppConfig.Is("topmost"); @@ -261,6 +265,32 @@ public SettingsForm() panelPerformance.Focus(); } + private void ButtonAutoTDP_Click(object? sender, EventArgs e) + { + autoTdpUi = new AutoTDPUI(); + autoTdpUi.TopMost = true; + autoTdpUi.FormClosed += AutoTdpUi_FormClosed; + autoTdpUi.Disposed += AutoTdpUi_Disposed; + if (!autoTdpUi.IsDisposed) + { + autoTdpUi.Show(); + } + else + { + autoTdpUi = null; + } + } + + private void AutoTdpUi_Disposed(object? sender, EventArgs e) + { + autoTdpUi = null; + } + + private void AutoTdpUi_FormClosed(object? sender, FormClosedEventArgs e) + { + autoTdpUi = null; + } + private void SliderGamma_ValueChanged(object? sender, EventArgs e) { screenControl.SetGamma(sliderGamma.Value); @@ -1081,6 +1111,7 @@ public void VisualiseScreen(bool screenEnabled, bool screenAuto, int frequency, private void ButtonQuit_Click(object? sender, EventArgs e) { + Program.autoTDPService.Shutdown(); matrixControl.Dispose(); Close(); Program.trayIcon.Visible = false; From 4af2bf63c7f5e6674b463e388c4efe250e10d284 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Fri, 1 Mar 2024 07:49:53 +0100 Subject: [PATCH 05/76] Remove unnecessary usings --- app/AutoTDP/FramerateSource/RTSSFramerateSource.cs | 7 +------ app/AutoTDP/GameProfile.cs | 7 +------ app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs | 2 -- app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs | 8 +------- 4 files changed, 3 insertions(+), 21 deletions(-) diff --git a/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs b/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs index e70ab6ac3..7802d47d2 100644 --- a/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs +++ b/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Diagnostics; using RTSSSharedMemoryNET; namespace GHelper.AutoTDP.FramerateSource diff --git a/app/AutoTDP/GameProfile.cs b/app/AutoTDP/GameProfile.cs index c01655923..bfdfbbf96 100644 --- a/app/AutoTDP/GameProfile.cs +++ b/app/AutoTDP/GameProfile.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - + namespace GHelper.AutoTDP { public class GameProfile diff --git a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs index 4c5ab738b..64510b1c2 100644 --- a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace GHelper.AutoTDP.PowerLimiter { diff --git a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs index 5d93ae5b6..f056bbe94 100644 --- a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs @@ -1,10 +1,4 @@ -using HidSharp.Reports.Units; -using Ryzen; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Ryzen; namespace GHelper.AutoTDP.PowerLimiter { From 82f90fcab35d9a93fc3a16a8871f5b1e215eeeb1 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Fri, 1 Mar 2024 07:50:13 +0100 Subject: [PATCH 06/76] Make sure the game list is initialzed --- app/AutoTDP/AutoTDPService.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index dd9eb2be6..9957b71d3 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -139,6 +139,7 @@ public void LoadGameProfiles() { if (!File.Exists(GameProfileFile)) { + if (GameProfiles is null) GameProfiles = new List(); return; } From 06205a1ad31c7ad8a4d5293aab70d95bfb8a1b47 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Fri, 1 Mar 2024 07:52:27 +0100 Subject: [PATCH 07/76] Make interface more flexible by passing the whole instance object to the framerate source --- app/AutoTDP/AutoTDPService.cs | 2 +- app/AutoTDP/FramerateSource/IFramerateSource.cs | 2 +- app/AutoTDP/FramerateSource/RTSSFramerateSource.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 9957b71d3..d153a8d80 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -258,7 +258,7 @@ public void StartGameHandler(GameInstance instance) CurrentTDP = powerLimiter.GetCPUPowerLimit(); while (currentGame is not null && Running) { - GameFPS = framerateSouce.GetFramerate(profile.ProcessName); + GameFPS = framerateSouce.GetFramerate(instance); Logger.WriteLine("[AutoTDPService] (" + instance.ProcessName + ") Framerate " + GameFPS); diff --git a/app/AutoTDP/FramerateSource/IFramerateSource.cs b/app/AutoTDP/FramerateSource/IFramerateSource.cs index 3bf7aa857..85e647255 100644 --- a/app/AutoTDP/FramerateSource/IFramerateSource.cs +++ b/app/AutoTDP/FramerateSource/IFramerateSource.cs @@ -10,7 +10,7 @@ internal class GameInstance internal interface IFramerateSource { - public double GetFramerate(string processName); + public double GetFramerate(GameInstance instance); public List GetRunningGames(); } diff --git a/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs b/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs index 7802d47d2..5a456c2f0 100644 --- a/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs +++ b/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs @@ -69,7 +69,7 @@ public List GetRunningGames() return giL; } - public double GetFramerate(string processName) + public double GetFramerate(GameInstance instance) { if (!IsRunning) { @@ -79,7 +79,7 @@ public double GetFramerate(string processName) try { var appE = OSD.GetAppEntries() - .Where(x => (x.Flags & AppFlags.MASK) != AppFlags.None).FirstOrDefault(a => a.Name.EndsWith(processName)); + .Where(x => (x.Flags & AppFlags.MASK) != AppFlags.None).FirstOrDefault(a => a.ProcessId == instance.ProcessID); if (appE is null) return -1.0d; From b9b98577d04364b6562888f16233ccff1aa5bba8 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Fri, 1 Mar 2024 15:33:28 +0100 Subject: [PATCH 08/76] ASUS power limiter fixes --- .../PowerLimiter/ASUSACPIPowerLimiter.cs | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs index 64510b1c2..589ba8d34 100644 --- a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs @@ -7,8 +7,19 @@ namespace GHelper.AutoTDP.PowerLimiter internal class ASUSACPIPowerLimiter : IPowerLimiter { - private int DefaultA0 = Program.acpi.DeviceGet(AsusACPI.PPT_APUA0); - private int DefaultA3 = Program.acpi.DeviceGet(AsusACPI.PPT_APUA0); + private int DefaultA0; + private int DefaultA3; + private int DefaultB0 = 0; + + public ASUSACPIPowerLimiter() + { + DefaultA0 = Program.acpi.DeviceGet(AsusACPI.PPT_APUA0); + DefaultA3 = Program.acpi.DeviceGet(AsusACPI.PPT_APUA3); + if (Program.acpi.IsAllAmdPPT()) // CPU limit all amd models + { + DefaultB0 = Program.acpi.DeviceGet(AsusACPI.PPT_CPUB0); + } + } public void SetCPUPowerLimit(int watts) { @@ -17,6 +28,11 @@ public void SetCPUPowerLimit(int watts) Program.acpi.DeviceSet(AsusACPI.PPT_APUA3, watts, "PowerLimit A3"); Program.acpi.DeviceSet(AsusACPI.PPT_APUA0, watts, "PowerLimit A0"); } + + if (Program.acpi.IsAllAmdPPT()) // CPU limit all amd models + { + Program.acpi.DeviceSet(AsusACPI.PPT_CPUB0, watts, "PowerLimit B0"); + } } @@ -33,8 +49,16 @@ public void Dispose() public void ResetPowerLimits() { //Load limits that were set before the limiter engaged - Program.acpi.DeviceSet(AsusACPI.PPT_APUA3, DefaultA0, "PowerLimit A3"); - Program.acpi.DeviceSet(AsusACPI.PPT_APUA0, DefaultA3, "PowerLimit A0"); + if (DefaultA3 > 0) + Program.acpi.DeviceSet(AsusACPI.PPT_APUA3, DefaultA3, "PowerLimit A0"); + + if (DefaultA0 > 0) + Program.acpi.DeviceSet(AsusACPI.PPT_APUA0, DefaultA0, "PowerLimit A3"); + + if (Program.acpi.IsAllAmdPPT() && DefaultB0 > 0) // CPU limit all amd models + { + Program.acpi.DeviceSet(AsusACPI.PPT_CPUB0, DefaultB0, "PowerLimit B0"); + } } } } From 5d231ea172528f61389fa76617c23c77ffdffb12 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Fri, 1 Mar 2024 18:54:40 +0100 Subject: [PATCH 09/76] Fix min slider affecting the max slider the wrong way --- app/AutoTDP/AutoTDPGameProfileUI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/AutoTDP/AutoTDPGameProfileUI.cs b/app/AutoTDP/AutoTDPGameProfileUI.cs index 95f966775..9f4369639 100644 --- a/app/AutoTDP/AutoTDPGameProfileUI.cs +++ b/app/AutoTDP/AutoTDPGameProfileUI.cs @@ -57,7 +57,7 @@ private void SliderMaxTDP_ValueChanged(object? sender, EventArgs e) private void SliderMinTDP_ValueChanged(object? sender, EventArgs e) { labelMinTDP.Text = sliderMinTDP.Value + "W"; - if (sliderMaxTDP.Value > sliderMinTDP.Value) + if (sliderMaxTDP.Value < sliderMinTDP.Value) { sliderMaxTDP.Value = sliderMinTDP.Value; } From 2aebf3044d33b83c50ba9fb80cd3f5a2fd56e7b8 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Fri, 1 Mar 2024 18:55:01 +0100 Subject: [PATCH 10/76] Fix for not detecting older games --- app/AutoTDP/FramerateSource/RTSSFramerateSource.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs b/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs index 5a456c2f0..c2e4b67b3 100644 --- a/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs +++ b/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs @@ -78,8 +78,7 @@ public double GetFramerate(GameInstance instance) try { - var appE = OSD.GetAppEntries() - .Where(x => (x.Flags & AppFlags.MASK) != AppFlags.None).FirstOrDefault(a => a.ProcessId == instance.ProcessID); + var appE = OSD.GetAppEntries().FirstOrDefault(a => a.ProcessId == instance.ProcessID); if (appE is null) return -1.0d; From 8cd256463a3664a18cc541234471eef09d49d087 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Fri, 1 Mar 2024 18:55:50 +0100 Subject: [PATCH 11/76] Use watts as double to have more fine grained control --- app/AutoTDP/AutoTDPService.cs | 2 +- app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs | 8 ++++---- app/AutoTDP/PowerLimiter/IPowerLimiter.cs | 2 +- app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index d153a8d80..761522e29 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -379,7 +379,7 @@ public void AdjustPowerLimit(GameProfile profile) Logger.WriteLine("[AutoTDPService] Setting Power Limit from " + CurrentTDP + "W to " + newPL + "W, Delta:" + adjustment); //We only limit to full watts, no fractions. In this case, we will cut off the fractional part - powerLimiter.SetCPUPowerLimit((int)newPL); + powerLimiter.SetCPUPowerLimit(newPL); CurrentTDP = newPL; } diff --git a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs index 589ba8d34..34d62e320 100644 --- a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs @@ -21,17 +21,17 @@ public ASUSACPIPowerLimiter() } } - public void SetCPUPowerLimit(int watts) + public void SetCPUPowerLimit(double watts) { if (Program.acpi.DeviceGet(AsusACPI.PPT_APUA0) >= 0) { - Program.acpi.DeviceSet(AsusACPI.PPT_APUA3, watts, "PowerLimit A3"); - Program.acpi.DeviceSet(AsusACPI.PPT_APUA0, watts, "PowerLimit A0"); + Program.acpi.DeviceSet(AsusACPI.PPT_APUA3, (int)watts, "PowerLimit A3"); + Program.acpi.DeviceSet(AsusACPI.PPT_APUA0, (int)watts, "PowerLimit A0"); } if (Program.acpi.IsAllAmdPPT()) // CPU limit all amd models { - Program.acpi.DeviceSet(AsusACPI.PPT_CPUB0, watts, "PowerLimit B0"); + Program.acpi.DeviceSet(AsusACPI.PPT_CPUB0, (int)watts, "PowerLimit B0"); } } diff --git a/app/AutoTDP/PowerLimiter/IPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IPowerLimiter.cs index 05e019962..b8045c78a 100644 --- a/app/AutoTDP/PowerLimiter/IPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/IPowerLimiter.cs @@ -2,7 +2,7 @@ { internal interface IPowerLimiter : IDisposable { - public void SetCPUPowerLimit(int watts); + public void SetCPUPowerLimit(double watts); public int GetCPUPowerLimit(); diff --git a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs index f056bbe94..e7bacc1d3 100644 --- a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs @@ -36,7 +36,7 @@ public void ReadPowerUnit() PowerUnit = 1 / Math.Pow(2, pwr); } - public void SetCPUPowerLimit(int watts) + public void SetCPUPowerLimit(double watts) { uint eax = 0; uint edx = 0; From f1198440d437ac9b7e447be2ba0e0797c15d3d60 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Fri, 1 Mar 2024 18:56:55 +0100 Subject: [PATCH 12/76] Better calibration for tdp adjustments to not cause extreme stutters --- app/AutoTDP/AutoTDPService.cs | 59 ++++++++++++++++++++++++----------- app/AutoTDP/GameProfile.cs | 5 +++ 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 761522e29..23bd86656 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -27,11 +27,14 @@ internal class AutoTDPService : IDisposable private int FramerateTargetReachedCounter; private int FramerateDipCounter; - private static readonly int FPSDipHistorySize = 8; + private static readonly int FPSDipHistorySize = 6; private List FramerateLog = new List(); private double CurrentTDP; + private double LastAdjustment; + private double LastAdjustmentTotal = 0; + private int LastAdjustmentsWithoutImprovement = 0; private GameInstance? currentGame; @@ -302,21 +305,21 @@ private double FPSDipCorrection(double currentFramerate, double targetFPS) { FramerateTargetReachedCounter++; - if (FramerateTargetReachedCounter >= 4 + if (FramerateTargetReachedCounter >= 3 && FramerateTargetReachedCounter < FPSDipHistorySize - && targetFPS - 0.75 <= FramerateLog.Take(4).Average() - && FramerateLog.Take(3).Average() <= targetFPS + 0.15) + && targetFPS - 0.5 <= FramerateLog.Take(3).Average() + && FramerateLog.Take(3).Average() - 0.05 <= targetFPS) { //short dip FramerateDipCounter++; - correction = targetFPS + 0.75 - currentFramerate; + correction = targetFPS - currentFramerate; } - else if (FramerateDipCounter >= 5 - && targetFPS - 0.75 <= FramerateLog.Average() - && FramerateLog.Average() <= targetFPS + 0.15) + else if (FramerateDipCounter >= 4 + && targetFPS - 0.5 <= FramerateLog.Average() + && FramerateLog.Average() - 0.1 <= targetFPS) { //long dip - correction = targetFPS + 1.5 - currentFramerate; + correction = targetFPS + 0.25 - currentFramerate; FramerateTargetReachedCounter = FPSDipHistorySize; } } @@ -335,11 +338,11 @@ private double FPSDipCorrection(double currentFramerate, double targetFPS) private double TDPDamper(double currentFramerate) { if (double.IsNaN(GameFPSPrevious)) GameFPSPrevious = currentFramerate; - double dF = -0.1d; + double dF = -0.15d; // Calculation double deltaError = currentFramerate - GameFPSPrevious; - double dT = deltaError / (1010.0 / 1000.0); + double dT = deltaError / (1020.0 / 1000.0); double damping = CurrentTDP / currentFramerate * dF * dT; GameFPSPrevious = currentFramerate; @@ -357,26 +360,44 @@ public void AdjustPowerLimit(GameProfile profile) } double newPL = CurrentTDP; - - - Logger.WriteLine("[AutoTDPService] Current: " + (int)GameFPS + "FPS"); - - - double delta = profile.TargetFPS - GameFPS - FPSDipCorrection(GameFPS, profile.TargetFPS); + double fpsCorrection = FPSDipCorrection(GameFPS, profile.GetTDPFPS()); + double delta = profile.GetTDPFPS() - GameFPS - fpsCorrection - 1; delta = Math.Clamp(delta, -15, 15); - double adjustment = (delta * CurrentTDP / GameFPS) * 0.85; + double adjustment = (delta * CurrentTDP / GameFPS) * 0.9; //Dampen the changes to not change TDP too aggressively which would cause performance issues adjustment += TDPDamper(GameFPS); + adjustment = Math.Min(adjustment, (CurrentTDP / 5)); + + if (LastAdjustment > 0 && GameFPS <= GameFPSPrevious && adjustment > 0) + { + LastAdjustmentsWithoutImprovement++; + LastAdjustmentTotal += adjustment; + + //Wait for 3 consecutive power increases and at least 3W increased TDP before judging that increasing power does nothing. + if (LastAdjustmentsWithoutImprovement >= 3 && LastAdjustmentTotal > 3) + { + //Do not adjust if increasing power does not improve framerate. + Logger.WriteLine("[AutoTDPService] Not adjusting because no improvement from last increase"); + return; + } + + } + else + { + LastAdjustmentsWithoutImprovement = 0; + LastAdjustmentTotal = 0; + } newPL += adjustment; + LastAdjustment = adjustment; //Respect the limits that the user chose newPL = Math.Clamp(newPL, profile.MinTdp, profile.MaxTdp); - Logger.WriteLine("[AutoTDPService] Setting Power Limit from " + CurrentTDP + "W to " + newPL + "W, Delta:" + adjustment); + Logger.WriteLine("[AutoTDPService] Setting Power Limit from " + CurrentTDP + "W to " + newPL + "W, Delta:" + adjustment + ", FPS correction: " + fpsCorrection); //We only limit to full watts, no fractions. In this case, we will cut off the fractional part powerLimiter.SetCPUPowerLimit(newPL); diff --git a/app/AutoTDP/GameProfile.cs b/app/AutoTDP/GameProfile.cs index bfdfbbf96..bc7033e39 100644 --- a/app/AutoTDP/GameProfile.cs +++ b/app/AutoTDP/GameProfile.cs @@ -9,5 +9,10 @@ public class GameProfile public int MinTdp { get; set; } public int MaxTdp { get; set; } public bool Enabled { get; set; } + + public int GetTDPFPS() + { + return TargetFPS - 1; + } } } From 0370aa8c9410bff0da2294ec6aedaf6716047853 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Fri, 1 Mar 2024 18:57:20 +0100 Subject: [PATCH 13/76] Fixed reset not working when a game was running --- app/AutoTDP/AutoTDPService.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 23bd86656..3b6865224 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -240,6 +240,11 @@ public void Reset() currentGame = null; GameFPSPrevious = double.NaN; GameFPS = 0; + LastAdjustmentsWithoutImprovement = 0; + LastAdjustment = 0.0; + FramerateLog = new List(); + FramerateTargetReachedCounter = 0; + FramerateDipCounter = 0; if (powerLimiter is not null) { @@ -444,6 +449,9 @@ public void Shutdown() framerateSouce = null; } + currentGame = null; + Reset(); + //Kill RTSS instance if we started one RTSSFramerateSource.Stop(); From 6850b644a0cd1fd4215e01e0fbfd9af4bcb1fca1 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Fri, 1 Mar 2024 20:14:53 +0100 Subject: [PATCH 14/76] Fix for detecting exit of game --- app/AutoTDP/AutoTDPService.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 3b6865224..e3408d65a 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -266,17 +266,21 @@ public void StartGameHandler(GameInstance instance) CurrentTDP = powerLimiter.GetCPUPowerLimit(); while (currentGame is not null && Running) { - GameFPS = framerateSouce.GetFramerate(instance); + //prevent FPS from going to 0 which causes issues with the math + + double fps = framerateSouce.GetFramerate(instance); Logger.WriteLine("[AutoTDPService] (" + instance.ProcessName + ") Framerate " + GameFPS); - if (GameFPS < 0.0d) + if (fps < 0.0d) { //Game is not running anymore or RTSS lost its hook Reset(); return; } + + GameFPS = Math.Max(5, fps); AdjustPowerLimit(profile); try From 7f087d36dcbf5d284bb0fd0df259f4f190fae2b6 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Fri, 1 Mar 2024 20:15:11 +0100 Subject: [PATCH 15/76] Ignore case when finding game profile by process name --- app/AutoTDP/AutoTDPService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index e3408d65a..af43915e8 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -202,7 +202,7 @@ public void CheckForGame() foreach (GameProfile gp in GameProfiles) { - if (gp.ProcessName is not null && processName.EndsWith(gp.ProcessName)) + if (gp.ProcessName is not null && processName.EndsWith(gp.ProcessName, StringComparison.CurrentCultureIgnoreCase)) { return gp; } From 5fee6e03d1b5ef023b7b41c3356fb5e4328ff417 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Fri, 1 Mar 2024 23:22:57 +0100 Subject: [PATCH 16/76] Logic to find a stable minimum TDP for improved stability and less frame dips in low demanding scenes. --- app/AutoTDP/AutoTDPService.cs | 133 ++++++++++++++++++++++++++++++---- 1 file changed, 119 insertions(+), 14 deletions(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index af43915e8..9e9387d27 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -7,8 +7,10 @@ namespace GHelper.AutoTDP 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_FPS_CHECK = 2_500; + private static readonly int INTERVAL_FPS_CHECK = 500; string GameProfileFile = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\GHelper\\AutoTDP.json"; @@ -31,6 +33,10 @@ internal class AutoTDPService : IDisposable private List FramerateLog = new List(); + private double LowestTDP; + private double LowestStableTDP; + private long LowestStableStability = 0; + private int LowestStableChecks = 0; private double CurrentTDP; private double LastAdjustment; private double LastAdjustmentTotal = 0; @@ -176,13 +182,15 @@ public void CheckForGame() if (runningGames.Count == 0) { - Logger.WriteLine("[AutoTDPService] No games detected"); + if (LOG_AUTO_TDP) + Logger.WriteLine("[AutoTDPService] No games detected"); return; } 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)) { @@ -221,7 +229,8 @@ public void HandleGame(GameInstance instance) { if (currentGame is not null) { - Logger.WriteLine("[AutoTDPService] Already handling a game"); + if (LOG_AUTO_TDP) + Logger.WriteLine("[AutoTDPService] Already handling a game"); return; } @@ -241,10 +250,12 @@ public void Reset() GameFPSPrevious = double.NaN; GameFPS = 0; LastAdjustmentsWithoutImprovement = 0; - LastAdjustment = 0.0; + LastAdjustment = 0.0; FramerateLog = new List(); FramerateTargetReachedCounter = 0; FramerateDipCounter = 0; + LowestStableStability = 0; + LowestStableChecks = 0; if (powerLimiter is not null) { @@ -264,14 +275,17 @@ public void StartGameHandler(GameInstance instance) tdpThread = new Thread(() => { CurrentTDP = powerLimiter.GetCPUPowerLimit(); + LowestStableTDP = profile.MaxTdp; + LowestTDP = profile.MaxTdp; + while (currentGame is not null && Running) { //prevent FPS from going to 0 which causes issues with the math double fps = framerateSouce.GetFramerate(instance); - - Logger.WriteLine("[AutoTDPService] (" + instance.ProcessName + ") Framerate " + GameFPS); + if (LOG_AUTO_TDP) + Logger.WriteLine("[AutoTDPService] (" + instance.ProcessName + ") Framerate " + GameFPS); if (fps < 0.0d) { @@ -321,15 +335,22 @@ private double FPSDipCorrection(double currentFramerate, double targetFPS) { //short dip FramerateDipCounter++; - correction = targetFPS - currentFramerate; + FramerateUnstable(); + + correction = targetFPS + 0.15 - currentFramerate; } else if (FramerateDipCounter >= 4 && targetFPS - 0.5 <= FramerateLog.Average() && FramerateLog.Average() - 0.1 <= targetFPS) { //long dip - correction = targetFPS + 0.25 - currentFramerate; + correction = targetFPS + 0.35 - currentFramerate; FramerateTargetReachedCounter = FPSDipHistorySize; + FramerateVeryUnstable(); + } + else + { + FramerateStable(); } } else @@ -338,16 +359,82 @@ private double FPSDipCorrection(double currentFramerate, double targetFPS) correction = 0.0; FramerateTargetReachedCounter = 0; FramerateDipCounter = 0; + FramerateStable(); } + ProcessStability(); 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) { if (double.IsNaN(GameFPSPrevious)) GameFPSPrevious = currentFramerate; - double dF = -0.15d; + double dF = -0.12d; // Calculation double deltaError = currentFramerate - GameFPSPrevious; @@ -374,11 +461,21 @@ public void AdjustPowerLimit(GameProfile profile) 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 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) { @@ -389,7 +486,8 @@ public void AdjustPowerLimit(GameProfile profile) if (LastAdjustmentsWithoutImprovement >= 3 && LastAdjustmentTotal > 3) { //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; } @@ -406,7 +504,14 @@ public void AdjustPowerLimit(GameProfile profile) //Respect the limits that the user chose 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 powerLimiter.SetCPUPowerLimit(newPL); From c31e52bc74971a08d5798e7d9dcefb7ae005fce6 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Fri, 1 Mar 2024 23:53:07 +0100 Subject: [PATCH 17/76] PID already does that. Therefore it is not necessary --- app/AutoTDP/AutoTDPService.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 9e9387d27..639aa81b5 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -471,8 +471,6 @@ public void AdjustPowerLimit(GameProfile profile) { if (LOG_AUTO_TDP) Logger.WriteLine("[AutoTDPService] Single Dip, Ignore"); - - GameFPSPrevious = GameFPS; //single dip. Ignore return; } From 2e9a34d8f55ca8e8a62eb2c5c3bd34cecdc18ae1 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sat, 2 Mar 2024 11:35:40 +0100 Subject: [PATCH 18/76] Adjusted some comments to reflect recent code changes --- app/AutoTDP/AutoTDPService.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 639aa81b5..0272b8d92 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -280,7 +280,6 @@ public void StartGameHandler(GameInstance instance) while (currentGame is not null && Running) { - //prevent FPS from going to 0 which causes issues with the math double fps = framerateSouce.GetFramerate(instance); @@ -294,6 +293,7 @@ public void StartGameHandler(GameInstance instance) return; } + //prevent FPS from going to 0 which causes issues with the math GameFPS = Math.Max(5, fps); AdjustPowerLimit(profile); @@ -326,6 +326,7 @@ private double FPSDipCorrection(double currentFramerate, double targetFPS) if (targetFPS - 1 <= currentFramerate && currentFramerate <= targetFPS + 1) { + //Framerate is inside ideal range FramerateTargetReachedCounter++; if (FramerateTargetReachedCounter >= 3 @@ -355,7 +356,7 @@ private double FPSDipCorrection(double currentFramerate, double targetFPS) } else { - //No dip, no correction + //Framerate not in target range correction = 0.0; FramerateTargetReachedCounter = 0; FramerateDipCounter = 0; @@ -436,7 +437,7 @@ private double TDPDamper(double currentFramerate) if (double.IsNaN(GameFPSPrevious)) GameFPSPrevious = currentFramerate; double dF = -0.12d; - // Calculation + // PID Compute double deltaError = currentFramerate - GameFPSPrevious; double dT = deltaError / (1020.0 / 1000.0); double damping = CurrentTDP / currentFramerate * dF * dT; @@ -511,7 +512,7 @@ public void AdjustPowerLimit(GameProfile profile) 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 + //Apply power limits powerLimiter.SetCPUPowerLimit(newPL); CurrentTDP = newPL; } From 082c7036f556c77fa70ee582f7e18b01e912bc31 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sat, 2 Mar 2024 13:16:08 +0100 Subject: [PATCH 19/76] Check whether autotdp is available at all. --- app/AutoTDP/AutoTDPService.cs | 26 +++++++++++++++++++++++++- app/Settings.cs | 2 ++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 0272b8d92..4dd76f036 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -1,6 +1,7 @@ using System.Text.Json; using GHelper.AutoTDP.FramerateSource; using GHelper.AutoTDP.PowerLimiter; +using Ryzen; namespace GHelper.AutoTDP { @@ -69,9 +70,32 @@ public bool IsActive() return currentGame is not null; } + public static bool IsAvailable() + { + + if (AppConfig.IsAlly()) + { + //Not yet supported + return false; + } + + int availableFS = 0; + int availablePL = 0; + + if (RTSSFramerateSource.IsAvailable()) availableFS++; + + //Intel MSR Limiter is available on Intel only + if (!RyzenControl.IsAMD()) availablePL++; + + //ASUS ACPI Power limiter is available + if (AppConfig.IsASUS()) availablePL++; + + return availablePL > 0 && availableFS > 0; + } + public void Start() { - if (!IsEnabled() || IsRunning()) + if (!IsEnabled() || IsRunning() || !IsAvailable()) { return; } diff --git a/app/Settings.cs b/app/Settings.cs index e93c66edb..add11280b 100644 --- a/app/Settings.cs +++ b/app/Settings.cs @@ -453,6 +453,8 @@ private void SettingsForm_VisibleChanged(object? sender, EventArgs e) Task.Run((Action)RefreshPeripheralsBattery); updateControl.CheckForUpdates(); + + tableAdditionalGPUFeature.Visible = AutoTDPService.IsAvailable(); } } From edf05935c0783c195d36c23c6a6356794203a35b Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sat, 2 Mar 2024 13:18:31 +0100 Subject: [PATCH 20/76] Use constants instead of hardcoding the MSR index. --- app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs index e7bacc1d3..03706ff5b 100644 --- a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs @@ -2,8 +2,12 @@ namespace GHelper.AutoTDP.PowerLimiter { + internal class IntelMSRPowerLimiter : IPowerLimiter { + public static readonly uint MSR_PKG_POWER_LIMIT = 0x610; + public static readonly uint MSR_RAPL_POWER_UNIT = 0x606; + private Ols ols; private uint DefaultEax = 0; // Set on first reading @@ -28,7 +32,7 @@ public void ReadPowerUnit() uint eax = 0; uint edx = 0; - ols.Rdmsr(0x606, ref eax, ref edx); + ols.Rdmsr(MSR_RAPL_POWER_UNIT, ref eax, ref edx); uint pwr = eax & 0x03; @@ -42,7 +46,7 @@ public void SetCPUPowerLimit(double watts) uint edx = 0; - ols.Rdmsr(0x610, ref eax, ref edx); + ols.Rdmsr(MSR_PKG_POWER_LIMIT, ref eax, ref edx); uint watsRapl = (uint)(watts / PowerUnit); @@ -66,7 +70,7 @@ public int GetCPUPowerLimit() uint eax = 0; uint edx = 0; - ols.Rdmsr(0x610, ref eax, ref edx); + ols.Rdmsr(MSR_PKG_POWER_LIMIT, ref eax, ref edx); if (DefaultEax == 0) { @@ -88,7 +92,7 @@ public void ResetPowerLimits() { return; } - ols.Wrmsr(0x610, DefaultEax, DefaultEdx); + ols.Wrmsr(MSR_PKG_POWER_LIMIT, DefaultEax, DefaultEdx); } public void Dispose() From 89de581e1f2d26c67e2453c3e9e390f3335e2370 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sat, 2 Mar 2024 13:22:55 +0100 Subject: [PATCH 21/76] Also set FPPT boost for non all-amd models --- app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs index 34d62e320..8e3397c7b 100644 --- a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs @@ -10,6 +10,7 @@ internal class ASUSACPIPowerLimiter : IPowerLimiter private int DefaultA0; private int DefaultA3; private int DefaultB0 = 0; + private int DefaultC1 = 0; public ASUSACPIPowerLimiter() { @@ -19,6 +20,10 @@ public ASUSACPIPowerLimiter() { DefaultB0 = Program.acpi.DeviceGet(AsusACPI.PPT_CPUB0); } + if (Program.acpi.DeviceGet(AsusACPI.PPT_APUC1) >= 0) // FPPT boost for non all-amd models + { + DefaultC1 = Program.acpi.DeviceGet(AsusACPI.PPT_APUC1); + } } public void SetCPUPowerLimit(double watts) @@ -33,6 +38,11 @@ public void SetCPUPowerLimit(double watts) { Program.acpi.DeviceSet(AsusACPI.PPT_CPUB0, (int)watts, "PowerLimit B0"); } + + if (Program.acpi.DeviceGet(AsusACPI.PPT_APUC1) >= 0) // FPPT boost for non all-amd models + { + Program.acpi.DeviceSet(AsusACPI.PPT_APUC1, (int)watts, "PowerLimit C1"); + } } @@ -59,6 +69,11 @@ public void ResetPowerLimits() { Program.acpi.DeviceSet(AsusACPI.PPT_CPUB0, DefaultB0, "PowerLimit B0"); } + + if (Program.acpi.DeviceGet(AsusACPI.PPT_APUC1) >= 0) // FPPT boost for non all-amd models + { + Program.acpi.DeviceSet(AsusACPI.PPT_APUC1, DefaultC1, "PowerLimit C1"); + } } } } From b104d852c7580f03a1a8f3d01e20a284b8480b4a Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sat, 2 Mar 2024 13:26:37 +0100 Subject: [PATCH 22/76] Explicitly save previous power limits --- app/AutoTDP/AutoTDPService.cs | 2 ++ app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs | 5 +++++ app/AutoTDP/PowerLimiter/IPowerLimiter.cs | 2 ++ app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs | 15 ++++++++------- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 4dd76f036..753c97a99 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -299,6 +299,8 @@ public void StartGameHandler(GameInstance instance) tdpThread = new Thread(() => { CurrentTDP = powerLimiter.GetCPUPowerLimit(); + powerLimiter.SavePowerLimits(); // save current power limits to restore them afterwards + LowestStableTDP = profile.MaxTdp; LowestTDP = profile.MaxTdp; diff --git a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs index 8e3397c7b..6697c0e19 100644 --- a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs @@ -13,6 +13,11 @@ internal class ASUSACPIPowerLimiter : IPowerLimiter private int DefaultC1 = 0; public ASUSACPIPowerLimiter() + { + + } + + public void SavePowerLimits() { DefaultA0 = Program.acpi.DeviceGet(AsusACPI.PPT_APUA0); DefaultA3 = Program.acpi.DeviceGet(AsusACPI.PPT_APUA3); diff --git a/app/AutoTDP/PowerLimiter/IPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IPowerLimiter.cs index b8045c78a..de799f6a0 100644 --- a/app/AutoTDP/PowerLimiter/IPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/IPowerLimiter.cs @@ -8,5 +8,7 @@ internal interface IPowerLimiter : IDisposable public void ResetPowerLimits(); + public void SavePowerLimits(); + } } diff --git a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs index 03706ff5b..5a3cf24e4 100644 --- a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs @@ -27,6 +27,14 @@ public IntelMSRPowerLimiter() ReadPowerUnit(); } + public void SavePowerLimits() + { + DefaultEax = 0; + DefaultEdx = 0; + + ols.Rdmsr(MSR_PKG_POWER_LIMIT, ref DefaultEax, ref DefaultEdx); + } + public void ReadPowerUnit() { uint eax = 0; @@ -72,13 +80,6 @@ public int GetCPUPowerLimit() ols.Rdmsr(MSR_PKG_POWER_LIMIT, ref eax, ref edx); - if (DefaultEax == 0) - { - //Store default settings to reset them on exit - DefaultEax = eax; - DefaultEdx = edx; - } - uint pl1 = eax & PL1_MASK; uint pl2 = edx & PL2_MASK; From f2ef77b866c2ddd8d7cc3a50d069eaae270ec6ea Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sat, 2 Mar 2024 13:29:56 +0100 Subject: [PATCH 23/76] Better logic to handle availability of tdp limiters --- app/AutoTDP/AutoTDPService.cs | 5 +++-- app/AutoTDP/AutoTDPUI.cs | 9 +++++---- app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs | 5 +++++ app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs | 5 +++++ 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 753c97a99..18c22b70e 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -82,13 +82,14 @@ public static bool IsAvailable() int availableFS = 0; int availablePL = 0; + //Requires RTSS to be installed if (RTSSFramerateSource.IsAvailable()) availableFS++; //Intel MSR Limiter is available on Intel only - if (!RyzenControl.IsAMD()) availablePL++; + if (IntelMSRPowerLimiter.IsAvailable()) availablePL++; //ASUS ACPI Power limiter is available - if (AppConfig.IsASUS()) availablePL++; + if (ASUSACPIPowerLimiter.IsAvailable()) availablePL++; return availablePL > 0 && availableFS > 0; } diff --git a/app/AutoTDP/AutoTDPUI.cs b/app/AutoTDP/AutoTDPUI.cs index 525cb2990..9d8096e4b 100644 --- a/app/AutoTDP/AutoTDPUI.cs +++ b/app/AutoTDP/AutoTDPUI.cs @@ -1,4 +1,5 @@ using GHelper.AutoTDP.FramerateSource; +using GHelper.AutoTDP.PowerLimiter; using GHelper.UI; using Ryzen; @@ -123,7 +124,7 @@ private void VisualizeGeneralSettings() { checkBoxEnabled.Checked = AppConfig.Get("auto_tdp_enabled", 0) == 1; - if (!RyzenControl.IsAMD()) + if (IntelMSRPowerLimiter.IsAvailable()) comboBoxLimiter.Items.Add("Intel MSR Power Limiter"); @@ -134,14 +135,14 @@ private void VisualizeGeneralSettings() if (comboBoxLimiter.Items.Count > 0 && limiter is null) comboBoxLimiter.SelectedIndex = 0; - if (!RyzenControl.IsAMD() && limiter is not null && limiter.Equals("intel_msr")) + if (IntelMSRPowerLimiter.IsAvailable() && limiter is not null && limiter.Equals("intel_msr")) { comboBoxLimiter.SelectedIndex = 0; } - if (limiter is not null && limiter.Equals("asus_acpi")) + if (limiter is not null && limiter.Equals("asus_acpi") && ASUSACPIPowerLimiter.IsAvailable()) { - comboBoxLimiter.SelectedIndex = !RyzenControl.IsAMD() ? 1 : 0; + comboBoxLimiter.SelectedIndex = IntelMSRPowerLimiter.IsAvailable() ? 1 : 0; } diff --git a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs index 6697c0e19..6cea04c51 100644 --- a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs @@ -17,6 +17,11 @@ public ASUSACPIPowerLimiter() } + public static bool IsAvailable() + { + return AppConfig.IsASUS(); + } + public void SavePowerLimits() { DefaultA0 = Program.acpi.DeviceGet(AsusACPI.PPT_APUA0); diff --git a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs index 5a3cf24e4..0a0a04aad 100644 --- a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs @@ -27,6 +27,11 @@ public IntelMSRPowerLimiter() ReadPowerUnit(); } + public static bool IsAvailable() + { + return !RyzenControl.IsAMD(); + } + public void SavePowerLimits() { DefaultEax = 0; From da47c094e4ae1a7ea95a2edfc994d1cc73482839 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sat, 2 Mar 2024 13:55:25 +0100 Subject: [PATCH 24/76] Streamlined options and availability checks for limiters and fps sources --- app/AutoTDP/AutoTDPService.cs | 27 ++++++++----- app/AutoTDP/AutoTDPUI.cs | 71 +++++++++++++++++------------------ 2 files changed, 53 insertions(+), 45 deletions(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 18c22b70e..4b8e66627 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -79,19 +79,28 @@ public static bool IsAvailable() return false; } - int availableFS = 0; - int availablePL = 0; + return AvailablePowerLimiters().Count > 0 && AvailableFramerateSources().Count > 0; + } + + public static List AvailableFramerateSources() + { + List l = new List(); + + if (RTSSFramerateSource.IsAvailable()) l.Add("rtss"); - //Requires RTSS to be installed - if (RTSSFramerateSource.IsAvailable()) availableFS++; + return l; + } + + + public static List AvailablePowerLimiters() + { + List l = new List(); - //Intel MSR Limiter is available on Intel only - if (IntelMSRPowerLimiter.IsAvailable()) availablePL++; + if (IntelMSRPowerLimiter.IsAvailable()) l.Add("intel_msr"); - //ASUS ACPI Power limiter is available - if (ASUSACPIPowerLimiter.IsAvailable()) availablePL++; + if (ASUSACPIPowerLimiter.IsAvailable()) l.Add("asus_acpi"); - return availablePL > 0 && availableFS > 0; + return l; } public void Start() diff --git a/app/AutoTDP/AutoTDPUI.cs b/app/AutoTDP/AutoTDPUI.cs index 9d8096e4b..82b80ed14 100644 --- a/app/AutoTDP/AutoTDPUI.cs +++ b/app/AutoTDP/AutoTDPUI.cs @@ -2,11 +2,16 @@ using GHelper.AutoTDP.PowerLimiter; using GHelper.UI; using Ryzen; +using System.Linq; namespace GHelper.AutoTDP { public partial class AutoTDPUI : RForm { + + private Dictionary ModeTexts = new Dictionary(); + + private AutoTDPGameProfileUI? profileUI; public AutoTDPUI() { @@ -14,6 +19,11 @@ public AutoTDPUI() InitTheme(); + + ModeTexts.Add("intel_msr", "Intel MSR Power Limiter"); + ModeTexts.Add("asus_acpi", "ASUS ACPI Power Limiter"); + ModeTexts.Add("rtss", "Riva Tuner Statistics Server"); + checkBoxEnabled.CheckedChanged += CheckBoxEnabled_CheckedChanged; buttonAddGame.Click += ButtonAddGame_Click; @@ -29,24 +39,13 @@ public AutoTDPUI() private void ComboBoxFPSSource_DropDownClosed(object? sender, EventArgs e) { - if ((comboBoxFPSSource.SelectedItem as string).StartsWith("Riva")) - { - AppConfig.Set("auto_tdp_fps_source", "rtss"); - } + AppConfig.Set("auto_tdp_fps_source", AutoTDPService.AvailableFramerateSources().ElementAt(comboBoxFPSSource.SelectedIndex)); + } private void ComboBoxLimiter_DropDownClosed(object? sender, EventArgs e) { - if ((comboBoxLimiter.SelectedItem as string).StartsWith("Intel")) - { - AppConfig.Set("auto_tdp_limiter", "intel_msr"); - } - - - if ((comboBoxLimiter.SelectedItem as string).StartsWith("ASUS ACPI")) - { - AppConfig.Set("auto_tdp_limiter", "asus_acpi"); - } + AppConfig.Set("auto_tdp_limiter", AutoTDPService.AvailablePowerLimiters().ElementAt(comboBoxLimiter.SelectedIndex)); } private void CheckBoxEnabled_CheckedChanged(object? sender, EventArgs e) @@ -124,42 +123,42 @@ private void VisualizeGeneralSettings() { checkBoxEnabled.Checked = AppConfig.Get("auto_tdp_enabled", 0) == 1; - if (IntelMSRPowerLimiter.IsAvailable()) - comboBoxLimiter.Items.Add("Intel MSR Power Limiter"); + comboBoxLimiter.Items.Clear(); + comboBoxFPSSource.Items.Clear(); - - comboBoxLimiter.Items.Add("ASUS ACPI Power Limiter"); - - string? limiter = AppConfig.GetString("auto_tdp_limiter"); - - if (comboBoxLimiter.Items.Count > 0 && limiter is null) - comboBoxLimiter.SelectedIndex = 0; - - if (IntelMSRPowerLimiter.IsAvailable() && limiter is not null && limiter.Equals("intel_msr")) + foreach (string s in AutoTDPService.AvailablePowerLimiters()) { - comboBoxLimiter.SelectedIndex = 0; + comboBoxLimiter.Items.Add(ModeTexts[s]); } - if (limiter is not null && limiter.Equals("asus_acpi") && ASUSACPIPowerLimiter.IsAvailable()) + foreach (string s in AutoTDPService.AvailableFramerateSources()) { - comboBoxLimiter.SelectedIndex = IntelMSRPowerLimiter.IsAvailable() ? 1 : 0; + comboBoxFPSSource.Items.Add(ModeTexts[s]); } - - if (RTSSFramerateSource.IsAvailable()) - comboBoxFPSSource.Items.Add("Riva Tuner Statistics Server"); - - + string? limiter = AppConfig.GetString("auto_tdp_limiter", null); string? source = AppConfig.GetString("auto_tdp_fps_source", null); - if (comboBoxFPSSource.Items.Count > 0 && source is null) - comboBoxFPSSource.SelectedIndex = 0; - if (source is not null && source.Equals("rtss")) + if (limiter is not null && AutoTDPService.AvailablePowerLimiters().Contains(limiter)) + { + comboBoxLimiter.SelectedIndex = AutoTDPService.AvailablePowerLimiters().IndexOf(limiter); + } + else + { + comboBoxLimiter.SelectedIndex = 0; + } + + if (source is not null && AutoTDPService.AvailableFramerateSources().Contains(source)) + { + comboBoxFPSSource.SelectedIndex = AutoTDPService.AvailableFramerateSources().IndexOf(source); + } + else { comboBoxFPSSource.SelectedIndex = 0; } + } From b765514477f6fc6aa79039e9a03f906a580b3a2d Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sat, 2 Mar 2024 13:56:21 +0100 Subject: [PATCH 25/76] Check limiters for their availability before using them --- app/AutoTDP/AutoTDPService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 4b8e66627..406ecd1af 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -157,13 +157,13 @@ public void InitLimiter() { string? limiter = AppConfig.GetString("auto_tdp_limiter"); - if (limiter is null || limiter.Equals("asus_acpi")) + if (limiter is null || limiter.Equals("asus_acpi") && ASUSACPIPowerLimiter.IsAvailable()) { powerLimiter = new ASUSACPIPowerLimiter(); return; } - if (limiter is not null && limiter.Equals("intel_msr")) + if (limiter is not null && limiter.Equals("intel_msr") && IntelMSRPowerLimiter.IsAvailable()) { powerLimiter = new IntelMSRPowerLimiter(); return; From b8c3c04e4be5a3fe57f996605ffa7536fcc306b4 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sat, 2 Mar 2024 16:41:05 +0100 Subject: [PATCH 26/76] Lower default min power to 15W and remove the .exe from the default game title. --- app/AutoTDP/AutoTDPUI.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/AutoTDP/AutoTDPUI.cs b/app/AutoTDP/AutoTDPUI.cs index 82b80ed14..8244a57b0 100644 --- a/app/AutoTDP/AutoTDPUI.cs +++ b/app/AutoTDP/AutoTDPUI.cs @@ -88,11 +88,11 @@ private void ButtonAddGame_Click(object? sender, EventArgs e) GameProfile p = new GameProfile(); p.ProcessName = Path.GetFileName(path); - p.GameTitle = Path.GetFileName(path); + p.GameTitle = Path.GetFileName(path).Replace(".exe", ""); p.Enabled = true; p.TargetFPS = 60; p.MaxTdp = 40; - p.MinTdp = 20; + p.MinTdp = 15; profileUI = new AutoTDPGameProfileUI(p, this); profileUI.TopMost = true; From 6523d6ac1284a046eed85f51d909c4193f2666d1 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sat, 2 Mar 2024 16:41:22 +0100 Subject: [PATCH 27/76] Added more logging. --- app/AutoTDP/AutoTDPService.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 406ecd1af..caa705abf 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -1,4 +1,5 @@ -using System.Text.Json; +using System.Collections.Generic; +using System.Text.Json; using GHelper.AutoTDP.FramerateSource; using GHelper.AutoTDP.PowerLimiter; using Ryzen; @@ -88,6 +89,7 @@ public static List AvailableFramerateSources() if (RTSSFramerateSource.IsAvailable()) l.Add("rtss"); + Logger.WriteLine("[AutoTDPService] Available Framerate Sources: " + string.Join(", ", l.ToArray())); return l; } @@ -100,6 +102,9 @@ public static List AvailablePowerLimiters() if (ASUSACPIPowerLimiter.IsAvailable()) l.Add("asus_acpi"); + + Logger.WriteLine("[AutoTDPService] Available Power Limiters: " + string.Join(", ", l.ToArray())); + return l; } @@ -107,6 +112,7 @@ public void Start() { if (!IsEnabled() || IsRunning() || !IsAvailable()) { + Logger.WriteLine("[AutoTDPService] Refusing startup. Stats: Enabled: " + IsEnabled() + ", Running: " + IsRunning() + " ,Available: " + IsAvailable()); return; } @@ -146,6 +152,7 @@ public void InitFramerateSource() if ((source is null || source.Equals("rtss")) && RTSSFramerateSource.IsAvailable()) { + Logger.WriteLine("[AutoTDPService] Initializing RTSSFramerateSource..."); RTSSFramerateSource rtss = new RTSSFramerateSource(); RTSSFramerateSource.Start(); framerateSouce = rtss; @@ -159,12 +166,14 @@ public void InitLimiter() if (limiter is null || limiter.Equals("asus_acpi") && ASUSACPIPowerLimiter.IsAvailable()) { + Logger.WriteLine("[AutoTDPService] Initializing ASUSACPIPowerLimiter..."); powerLimiter = new ASUSACPIPowerLimiter(); return; } if (limiter is not null && limiter.Equals("intel_msr") && IntelMSRPowerLimiter.IsAvailable()) { + Logger.WriteLine("[AutoTDPService] Initializing IntelMSRPowerLimiter..."); powerLimiter = new IntelMSRPowerLimiter(); return; } @@ -306,6 +315,8 @@ public void StartGameHandler(GameInstance instance) return; } + Logger.WriteLine("[AutoTDPService] Start handling game: " + instance.ProcessName + " PID: " + instance.ProcessID); + tdpThread = new Thread(() => { CurrentTDP = powerLimiter.GetCPUPowerLimit(); From cb685f915895a33a64a376824ee8b645ac97f53b Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sat, 2 Mar 2024 17:00:11 +0100 Subject: [PATCH 28/76] Fixes for stability checks --- app/AutoTDP/AutoTDPService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index caa705abf..234d67993 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -452,13 +452,13 @@ private void ProcessStability() if (LowestStableStability > 120) { - //if stable for long time try to reduce it a bit - LowestStableTDP -= 0.5; + //if stable for long time try to reduce it again + LowestStableTDP = LowestTDP + (LowestTDP * 0.10); ; LowestStableStability = 0; } } - if (LowestTDP - 0.25 <= CurrentTDP && CurrentTDP >= LowestTDP + 0.25) + if (LowestTDP - 0.25 <= CurrentTDP && CurrentTDP <= LowestTDP + 0.25) { LowestStableStability++; From 12d7afc53237d6b36de332ead6d2a2e5aa5b375a Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sat, 2 Mar 2024 17:21:02 +0100 Subject: [PATCH 29/76] Stuttering has bigger impact on stability. --- app/AutoTDP/AutoTDPService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 234d67993..6e59222f8 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -421,11 +421,11 @@ private void FramerateStable() private void FramerateUnstable() { - LowestStableStability -= 3; + LowestStableStability -= 15; } private void FramerateVeryUnstable() { - LowestStableStability -= 30; + LowestStableStability -= 60; } private bool Stabilize() @@ -453,7 +453,7 @@ private void ProcessStability() if (LowestStableStability > 120) { //if stable for long time try to reduce it again - LowestStableTDP = LowestTDP + (LowestTDP * 0.10); ; + LowestStableTDP = ProfileForGame(currentGame.ProcessName).MaxTdp; LowestStableStability = 0; } } From adcec744d444ddbb3f0f22c3778172460c7e792e Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sat, 2 Mar 2024 17:30:18 +0100 Subject: [PATCH 30/76] Do not use top most, but dialog for the game settings. --- app/AutoTDP/AutoTDPUI.cs | 6 ++---- app/Settings.cs | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/app/AutoTDP/AutoTDPUI.cs b/app/AutoTDP/AutoTDPUI.cs index 8244a57b0..35e0eee6f 100644 --- a/app/AutoTDP/AutoTDPUI.cs +++ b/app/AutoTDP/AutoTDPUI.cs @@ -95,9 +95,8 @@ private void ButtonAddGame_Click(object? sender, EventArgs e) p.MinTdp = 15; profileUI = new AutoTDPGameProfileUI(p, this); - profileUI.TopMost = true; profileUI.FormClosed += ProfileUI_FormClosed; - profileUI.Show(); + profileUI.ShowDialog(this); } private void ProfileUI_FormClosed(object? sender, FormClosedEventArgs e) @@ -193,9 +192,8 @@ private void Bt_Click(object? sender, EventArgs e) { GameProfile gp = (GameProfile)((RButton)sender).Tag; profileUI = new AutoTDPGameProfileUI(gp, this); - profileUI.TopMost = true; profileUI.FormClosed += ProfileUI_FormClosed; - profileUI.Show(); + profileUI.ShowDialog(this); } public void DeleteGameProfile(GameProfile gp) diff --git a/app/Settings.cs b/app/Settings.cs index add11280b..b5dcbcea5 100644 --- a/app/Settings.cs +++ b/app/Settings.cs @@ -280,7 +280,6 @@ public void InitBrightness() private void ButtonAutoTDP_Click(object? sender, EventArgs e) { autoTdpUi = new AutoTDPUI(); - autoTdpUi.TopMost = true; autoTdpUi.FormClosed += AutoTdpUi_FormClosed; autoTdpUi.Disposed += AutoTdpUi_Disposed; if (!autoTdpUi.IsDisposed) From 61803dbc39d074da6163b286c2ce95fd279aef7a Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sat, 2 Mar 2024 17:30:36 +0100 Subject: [PATCH 31/76] Renamed some controls --- app/AutoTDP/AutoTDPGameProfileUI.Designer.cs | 199 +++++++++---------- 1 file changed, 99 insertions(+), 100 deletions(-) diff --git a/app/AutoTDP/AutoTDPGameProfileUI.Designer.cs b/app/AutoTDP/AutoTDPGameProfileUI.Designer.cs index 66b9c6544..d93b047bb 100644 --- a/app/AutoTDP/AutoTDPGameProfileUI.Designer.cs +++ b/app/AutoTDP/AutoTDPGameProfileUI.Designer.cs @@ -32,15 +32,15 @@ private void InitializeComponent() pictureKeyboard = new PictureBox(); labelSettings = new Label(); checkBoxEnabled = new CheckBox(); - panelLightingContent = new Panel(); + panelGameSettings = new Panel(); labelMinTDP = new Label(); labelMaxTDP = new Label(); sliderMaxTDP = new UI.Slider(); numericUpDownFPS = new NumericUpDown(); sliderMinTDP = new UI.Slider(); - label2 = new Label(); - label3 = new Label(); - label1 = new Label(); + labelMaxTDPText = new Label(); + labeMinTDPText = new Label(); + labelTargetFPS = new Label(); textBoxTitle = new TextBox(); textBoxProcessName = new TextBox(); labelFPSSource = new Label(); @@ -49,7 +49,7 @@ private void InitializeComponent() buttonDelete = new UI.RButton(); panelPerformanceHeader.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)pictureKeyboard).BeginInit(); - panelLightingContent.SuspendLayout(); + panelGameSettings.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)numericUpDownFPS).BeginInit(); SuspendLayout(); // @@ -61,19 +61,17 @@ private void InitializeComponent() panelPerformanceHeader.Controls.Add(checkBoxEnabled); panelPerformanceHeader.Dock = DockStyle.Top; panelPerformanceHeader.Location = new Point(0, 0); - panelPerformanceHeader.Margin = new Padding(2); panelPerformanceHeader.Name = "panelPerformanceHeader"; - panelPerformanceHeader.Size = new Size(386, 30); + panelPerformanceHeader.Size = new Size(551, 50); panelPerformanceHeader.TabIndex = 53; // // pictureKeyboard // pictureKeyboard.BackgroundImage = Properties.Resources.icons8_automation_32; pictureKeyboard.BackgroundImageLayout = ImageLayout.Zoom; - pictureKeyboard.Location = new Point(3, 8); - pictureKeyboard.Margin = new Padding(2); + pictureKeyboard.Location = new Point(4, 13); pictureKeyboard.Name = "pictureKeyboard"; - pictureKeyboard.Size = new Size(16, 16); + pictureKeyboard.Size = new Size(23, 27); pictureKeyboard.TabIndex = 35; pictureKeyboard.TabStop = false; // @@ -81,60 +79,61 @@ private void InitializeComponent() // labelSettings.AutoSize = true; labelSettings.Font = new Font("Segoe UI", 9F, FontStyle.Bold, GraphicsUnit.Point); - labelSettings.Location = new Point(22, 8); - labelSettings.Margin = new Padding(4, 0, 4, 0); + labelSettings.Location = new Point(31, 13); + labelSettings.Margin = new Padding(6, 0, 6, 0); labelSettings.Name = "labelSettings"; - labelSettings.Size = new Size(89, 15); + labelSettings.Size = new Size(135, 25); labelSettings.TabIndex = 34; labelSettings.Text = "Game Settings"; // // checkBoxEnabled // - checkBoxEnabled.Location = new Point(241, 2); - checkBoxEnabled.Margin = new Padding(4, 0, 4, 0); + checkBoxEnabled.Location = new Point(344, 3); + checkBoxEnabled.Margin = new Padding(6, 0, 6, 0); checkBoxEnabled.Name = "checkBoxEnabled"; - checkBoxEnabled.Size = new Size(136, 25); + checkBoxEnabled.Size = new Size(194, 42); checkBoxEnabled.TabIndex = 53; checkBoxEnabled.Text = "Enabled"; checkBoxEnabled.UseVisualStyleBackColor = true; // - // panelLightingContent - // - panelLightingContent.AutoSize = true; - panelLightingContent.Controls.Add(labelMinTDP); - panelLightingContent.Controls.Add(labelMaxTDP); - panelLightingContent.Controls.Add(sliderMaxTDP); - panelLightingContent.Controls.Add(numericUpDownFPS); - panelLightingContent.Controls.Add(sliderMinTDP); - panelLightingContent.Controls.Add(label2); - panelLightingContent.Controls.Add(label3); - panelLightingContent.Controls.Add(label1); - panelLightingContent.Controls.Add(textBoxTitle); - panelLightingContent.Controls.Add(textBoxProcessName); - panelLightingContent.Controls.Add(labelFPSSource); - panelLightingContent.Controls.Add(labelLimiter); - panelLightingContent.Dock = DockStyle.Top; - panelLightingContent.Location = new Point(0, 30); - panelLightingContent.Margin = new Padding(2); - panelLightingContent.Name = "panelLightingContent"; - panelLightingContent.Padding = new Padding(0, 0, 0, 7); - panelLightingContent.Size = new Size(386, 146); - panelLightingContent.TabIndex = 57; + // panelGameSettings + // + panelGameSettings.AutoSize = true; + panelGameSettings.Controls.Add(labelMinTDP); + panelGameSettings.Controls.Add(labelMaxTDP); + panelGameSettings.Controls.Add(sliderMaxTDP); + panelGameSettings.Controls.Add(numericUpDownFPS); + panelGameSettings.Controls.Add(sliderMinTDP); + panelGameSettings.Controls.Add(labelMaxTDPText); + panelGameSettings.Controls.Add(labeMinTDPText); + panelGameSettings.Controls.Add(labelTargetFPS); + panelGameSettings.Controls.Add(textBoxTitle); + panelGameSettings.Controls.Add(textBoxProcessName); + panelGameSettings.Controls.Add(labelFPSSource); + panelGameSettings.Controls.Add(labelLimiter); + panelGameSettings.Dock = DockStyle.Top; + panelGameSettings.Location = new Point(0, 50); + panelGameSettings.Name = "panelGameSettings"; + panelGameSettings.Padding = new Padding(0, 0, 0, 12); + panelGameSettings.Size = new Size(551, 244); + panelGameSettings.TabIndex = 57; // // labelMinTDP // - labelMinTDP.Location = new Point(342, 91); + labelMinTDP.Location = new Point(489, 152); + labelMinTDP.Margin = new Padding(4, 0, 4, 0); labelMinTDP.Name = "labelMinTDP"; - labelMinTDP.Size = new Size(39, 22); + labelMinTDP.Size = new Size(56, 37); labelMinTDP.TabIndex = 67; labelMinTDP.Text = "30W"; labelMinTDP.TextAlign = ContentAlignment.MiddleRight; // // labelMaxTDP // - labelMaxTDP.Location = new Point(341, 117); + labelMaxTDP.Location = new Point(487, 195); + labelMaxTDP.Margin = new Padding(4, 0, 4, 0); labelMaxTDP.Name = "labelMaxTDP"; - labelMaxTDP.Size = new Size(40, 22); + labelMaxTDP.Size = new Size(57, 37); labelMaxTDP.TabIndex = 66; labelMaxTDP.Text = "150W"; labelMaxTDP.TextAlign = ContentAlignment.MiddleRight; @@ -142,12 +141,11 @@ private void InitializeComponent() // sliderMaxTDP // sliderMaxTDP.AccessibleName = "DPI Slider"; - sliderMaxTDP.Location = new Point(140, 117); - sliderMaxTDP.Margin = new Padding(2); + sliderMaxTDP.Location = new Point(200, 195); sliderMaxTDP.Max = 200; sliderMaxTDP.Min = 5; sliderMaxTDP.Name = "sliderMaxTDP"; - sliderMaxTDP.Size = new Size(204, 20); + sliderMaxTDP.Size = new Size(291, 33); sliderMaxTDP.Step = 1; sliderMaxTDP.TabIndex = 65; sliderMaxTDP.TabStop = false; @@ -157,12 +155,11 @@ private void InitializeComponent() // numericUpDownFPS // numericUpDownFPS.BorderStyle = BorderStyle.None; - numericUpDownFPS.Location = new Point(291, 66); - numericUpDownFPS.Margin = new Padding(2); + numericUpDownFPS.Location = new Point(416, 110); numericUpDownFPS.Maximum = new decimal(new int[] { 1000, 0, 0, 0 }); numericUpDownFPS.Minimum = new decimal(new int[] { 20, 0, 0, 0 }); numericUpDownFPS.Name = "numericUpDownFPS"; - numericUpDownFPS.Size = new Size(86, 19); + numericUpDownFPS.Size = new Size(123, 27); numericUpDownFPS.TabIndex = 64; numericUpDownFPS.TextAlign = HorizontalAlignment.Center; numericUpDownFPS.Value = new decimal(new int[] { 60, 0, 0, 0 }); @@ -170,80 +167,81 @@ private void InitializeComponent() // sliderMinTDP // sliderMinTDP.AccessibleName = "DPI Slider"; - sliderMinTDP.Location = new Point(140, 93); - sliderMinTDP.Margin = new Padding(2); + sliderMinTDP.Location = new Point(200, 155); sliderMinTDP.Max = 200; sliderMinTDP.Min = 5; sliderMinTDP.Name = "sliderMinTDP"; - sliderMinTDP.Size = new Size(204, 20); + sliderMinTDP.Size = new Size(291, 33); sliderMinTDP.Step = 1; sliderMinTDP.TabIndex = 63; sliderMinTDP.TabStop = false; sliderMinTDP.Text = "sliderBattery"; sliderMinTDP.Value = 0; // - // label2 + // labelMaxTDPText // - label2.Location = new Point(5, 117); - label2.Margin = new Padding(4, 0, 4, 0); - label2.Name = "label2"; - label2.Size = new Size(129, 22); - label2.TabIndex = 61; - label2.Text = "Max TDP:"; - label2.TextAlign = ContentAlignment.MiddleLeft; + labelMaxTDPText.Location = new Point(7, 195); + labelMaxTDPText.Margin = new Padding(6, 0, 6, 0); + labelMaxTDPText.Name = "labelMaxTDPText"; + labelMaxTDPText.Size = new Size(184, 37); + labelMaxTDPText.TabIndex = 61; + labelMaxTDPText.Text = "Max TDP:"; + labelMaxTDPText.TextAlign = ContentAlignment.MiddleLeft; // - // label3 + // labeMinTDPText // - label3.Location = new Point(5, 91); - label3.Margin = new Padding(4, 0, 4, 0); - label3.Name = "label3"; - label3.Size = new Size(129, 22); - label3.TabIndex = 62; - label3.Text = "Min TDP:"; - label3.TextAlign = ContentAlignment.MiddleLeft; + labeMinTDPText.Location = new Point(7, 152); + labeMinTDPText.Margin = new Padding(6, 0, 6, 0); + labeMinTDPText.Name = "labeMinTDPText"; + labeMinTDPText.Size = new Size(184, 37); + labeMinTDPText.TabIndex = 62; + labeMinTDPText.Text = "Min TDP:"; + labeMinTDPText.TextAlign = ContentAlignment.MiddleLeft; // - // label1 + // labelTargetFPS // - label1.Location = new Point(5, 63); - label1.Margin = new Padding(4, 0, 4, 0); - label1.Name = "label1"; - label1.Size = new Size(129, 22); - label1.TabIndex = 60; - label1.Text = "Target FPS:"; - label1.TextAlign = ContentAlignment.MiddleLeft; + labelTargetFPS.Location = new Point(7, 105); + labelTargetFPS.Margin = new Padding(6, 0, 6, 0); + labelTargetFPS.Name = "labelTargetFPS"; + labelTargetFPS.Size = new Size(184, 37); + labelTargetFPS.TabIndex = 60; + labelTargetFPS.Text = "Target FPS:"; + labelTargetFPS.TextAlign = ContentAlignment.MiddleLeft; // // textBoxTitle // - textBoxTitle.Location = new Point(140, 36); + textBoxTitle.Location = new Point(200, 60); + textBoxTitle.Margin = new Padding(4, 5, 4, 5); textBoxTitle.Name = "textBoxTitle"; - textBoxTitle.Size = new Size(237, 23); + textBoxTitle.Size = new Size(337, 31); textBoxTitle.TabIndex = 59; // // textBoxProcessName // - textBoxProcessName.Location = new Point(140, 6); + textBoxProcessName.Location = new Point(200, 10); + textBoxProcessName.Margin = new Padding(4, 5, 4, 5); textBoxProcessName.Name = "textBoxProcessName"; textBoxProcessName.ReadOnly = true; - textBoxProcessName.Size = new Size(237, 23); + textBoxProcessName.Size = new Size(337, 31); textBoxProcessName.TabIndex = 58; textBoxProcessName.WordWrap = false; // // labelFPSSource // - labelFPSSource.Location = new Point(4, 37); - labelFPSSource.Margin = new Padding(4, 0, 4, 0); + labelFPSSource.Location = new Point(6, 62); + labelFPSSource.Margin = new Padding(6, 0, 6, 0); labelFPSSource.Name = "labelFPSSource"; - labelFPSSource.Size = new Size(129, 22); + labelFPSSource.Size = new Size(184, 37); labelFPSSource.TabIndex = 57; labelFPSSource.Text = "Name:"; labelFPSSource.TextAlign = ContentAlignment.MiddleLeft; // // labelLimiter // - labelLimiter.Location = new Point(4, 7); - labelLimiter.Margin = new Padding(4, 0, 4, 0); + labelLimiter.Location = new Point(6, 12); + labelLimiter.Margin = new Padding(6, 0, 6, 0); labelLimiter.Name = "labelLimiter"; - labelLimiter.Size = new Size(129, 22); + labelLimiter.Size = new Size(184, 37); labelLimiter.TabIndex = 47; labelLimiter.Text = "Process"; labelLimiter.TextAlign = ContentAlignment.MiddleLeft; @@ -258,11 +256,11 @@ private void InitializeComponent() buttonSave.BorderRadius = 2; buttonSave.FlatStyle = FlatStyle.Flat; buttonSave.ForeColor = SystemColors.ControlText; - buttonSave.Location = new Point(279, 182); - buttonSave.Margin = new Padding(2, 4, 2, 4); + buttonSave.Location = new Point(399, 303); + buttonSave.Margin = new Padding(3, 7, 3, 7); buttonSave.Name = "buttonSave"; buttonSave.Secondary = false; - buttonSave.Size = new Size(103, 25); + buttonSave.Size = new Size(147, 42); buttonSave.TabIndex = 61; buttonSave.Text = "Save"; buttonSave.UseVisualStyleBackColor = false; @@ -277,25 +275,26 @@ private void InitializeComponent() buttonDelete.BorderRadius = 2; buttonDelete.FlatStyle = FlatStyle.Flat; buttonDelete.ForeColor = SystemColors.ControlText; - buttonDelete.Location = new Point(4, 182); - buttonDelete.Margin = new Padding(2, 4, 2, 4); + buttonDelete.Location = new Point(6, 303); + buttonDelete.Margin = new Padding(3, 7, 3, 7); buttonDelete.Name = "buttonDelete"; buttonDelete.Secondary = false; - buttonDelete.Size = new Size(103, 25); + buttonDelete.Size = new Size(147, 42); buttonDelete.TabIndex = 62; buttonDelete.Text = "Delete"; buttonDelete.UseVisualStyleBackColor = false; // // AutoTDPGameProfileUI // - AutoScaleDimensions = new SizeF(7F, 15F); + AutoScaleDimensions = new SizeF(10F, 25F); AutoScaleMode = AutoScaleMode.Font; - ClientSize = new Size(386, 217); + ClientSize = new Size(551, 362); Controls.Add(buttonDelete); Controls.Add(buttonSave); - Controls.Add(panelLightingContent); + Controls.Add(panelGameSettings); Controls.Add(panelPerformanceHeader); FormBorderStyle = FormBorderStyle.FixedSingle; + Margin = new Padding(4, 5, 4, 5); MaximizeBox = false; MinimizeBox = false; Name = "AutoTDPGameProfileUI"; @@ -305,8 +304,8 @@ private void InitializeComponent() panelPerformanceHeader.ResumeLayout(false); panelPerformanceHeader.PerformLayout(); ((System.ComponentModel.ISupportInitialize)pictureKeyboard).EndInit(); - panelLightingContent.ResumeLayout(false); - panelLightingContent.PerformLayout(); + panelGameSettings.ResumeLayout(false); + panelGameSettings.PerformLayout(); ((System.ComponentModel.ISupportInitialize)numericUpDownFPS).EndInit(); ResumeLayout(false); PerformLayout(); @@ -318,15 +317,15 @@ private void InitializeComponent() private PictureBox pictureKeyboard; private Label labelSettings; private CheckBox checkBoxEnabled; - private Panel panelLightingContent; + private Panel panelGameSettings; private Label labelFPSSource; private Label labelLimiter; private TextBox textBoxTitle; private TextBox textBoxProcessName; private UI.RButton buttonSave; - private Label label2; - private Label label3; - private Label label1; + private Label labelMaxTDPText; + private Label labeMinTDPText; + private Label labelTargetFPS; private UI.Slider sliderMinTDP; private UI.Slider sliderMaxTDP; private NumericUpDown numericUpDownFPS; From 65c5b645a8c24d410811c55ba7a4e4b2c34fb109 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sat, 2 Mar 2024 20:06:21 +0100 Subject: [PATCH 32/76] Small bugfix: Do not allow to open the autotdp window more than once. --- app/Settings.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/Settings.cs b/app/Settings.cs index b5dcbcea5..9a8e1a2a4 100644 --- a/app/Settings.cs +++ b/app/Settings.cs @@ -279,6 +279,11 @@ public void InitBrightness() private void ButtonAutoTDP_Click(object? sender, EventArgs e) { + if (autoTdpUi is not null) + { + return; + } + autoTdpUi = new AutoTDPUI(); autoTdpUi.FormClosed += AutoTdpUi_FormClosed; autoTdpUi.Disposed += AutoTdpUi_Disposed; From 02c2a7ff37a1b43e52078fe75343178798166d8d Mon Sep 17 00:00:00 2001 From: Serge <5920850+seerge@users.noreply.github.com> Date: Sun, 3 Mar 2024 00:41:31 +0100 Subject: [PATCH 33/76] Power limits in Asus way --- .../PowerLimiter/ASUSACPIPowerLimiter.cs | 58 +++++-------------- 1 file changed, 13 insertions(+), 45 deletions(-) diff --git a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs index 6cea04c51..fb351de14 100644 --- a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs @@ -1,20 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using GHelper.Mode; namespace GHelper.AutoTDP.PowerLimiter { internal class ASUSACPIPowerLimiter : IPowerLimiter { - private int DefaultA0; - private int DefaultA3; - private int DefaultB0 = 0; - private int DefaultC1 = 0; + private bool allAmd; + private bool fPPT; public ASUSACPIPowerLimiter() { - + allAmd = Program.acpi.IsAllAmdPPT(); + fPPT = Program.acpi.DeviceGet(AsusACPI.PPT_APUC1) >= 0; } public static bool IsAvailable() @@ -24,38 +21,23 @@ public static bool IsAvailable() public void SavePowerLimits() { - DefaultA0 = Program.acpi.DeviceGet(AsusACPI.PPT_APUA0); - DefaultA3 = Program.acpi.DeviceGet(AsusACPI.PPT_APUA3); - if (Program.acpi.IsAllAmdPPT()) // CPU limit all amd models - { - DefaultB0 = Program.acpi.DeviceGet(AsusACPI.PPT_CPUB0); - } - if (Program.acpi.DeviceGet(AsusACPI.PPT_APUC1) >= 0) // FPPT boost for non all-amd models - { - DefaultC1 = Program.acpi.DeviceGet(AsusACPI.PPT_APUC1); - } } public void SetCPUPowerLimit(double watts) { - if (Program.acpi.DeviceGet(AsusACPI.PPT_APUA0) >= 0) - { - Program.acpi.DeviceSet(AsusACPI.PPT_APUA3, (int)watts, "PowerLimit A3"); - Program.acpi.DeviceSet(AsusACPI.PPT_APUA0, (int)watts, "PowerLimit A0"); - } - if (Program.acpi.IsAllAmdPPT()) // CPU limit all amd models + if (allAmd) // CPU limit all amd models { Program.acpi.DeviceSet(AsusACPI.PPT_CPUB0, (int)watts, "PowerLimit B0"); - } - - if (Program.acpi.DeviceGet(AsusACPI.PPT_APUC1) >= 0) // FPPT boost for non all-amd models - { - Program.acpi.DeviceSet(AsusACPI.PPT_APUC1, (int)watts, "PowerLimit C1"); + } else { + Program.acpi.DeviceSet(AsusACPI.PPT_APUA3, (int)watts, "PowerLimit A3"); + Program.acpi.DeviceSet(AsusACPI.PPT_APUA0, (int)watts, "PowerLimit A0"); + if (fPPT) Program.acpi.DeviceSet(AsusACPI.PPT_APUC1, (int)watts, "PowerLimit C1"); } } + // You can't read PPTs on ASUS :) endpoints just return 0 if they are available public int GetCPUPowerLimit() { return Program.acpi.DeviceGet(AsusACPI.PPT_APUA0); @@ -66,24 +48,10 @@ public void Dispose() //Nothing to dispose here } + // Correct Asus way to reset everything, is just to set mode again public void ResetPowerLimits() { - //Load limits that were set before the limiter engaged - if (DefaultA3 > 0) - Program.acpi.DeviceSet(AsusACPI.PPT_APUA3, DefaultA3, "PowerLimit A0"); - - if (DefaultA0 > 0) - Program.acpi.DeviceSet(AsusACPI.PPT_APUA0, DefaultA0, "PowerLimit A3"); - - if (Program.acpi.IsAllAmdPPT() && DefaultB0 > 0) // CPU limit all amd models - { - Program.acpi.DeviceSet(AsusACPI.PPT_CPUB0, DefaultB0, "PowerLimit B0"); - } - - if (Program.acpi.DeviceGet(AsusACPI.PPT_APUC1) >= 0) // FPPT boost for non all-amd models - { - Program.acpi.DeviceSet(AsusACPI.PPT_APUC1, DefaultC1, "PowerLimit C1"); - } + Program.modeControl.SetPerformanceMode(Modes.GetCurrent()); } } } From df10af99fe972d538d29c2298239db96d5fa0382 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Tue, 5 Mar 2024 18:48:22 +0100 Subject: [PATCH 34/76] Start RTSS again if it got stopped --- app/AutoTDP/FramerateSource/RTSSFramerateSource.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs b/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs index c2e4b67b3..6fef21d4b 100644 --- a/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs +++ b/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs @@ -52,7 +52,7 @@ public List GetRunningGames() { if (!IsRunning) { - return new List(); + Start(); } List giL = new List(); From 531138ff6a751219d9f29d6dc739aef12c66d101 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Tue, 5 Mar 2024 18:48:50 +0100 Subject: [PATCH 35/76] Do not spam the ACPI driver with power limits if the limit hasn't changed. --- .../PowerLimiter/ASUSACPIPowerLimiter.cs | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs index fb351de14..57939d4a1 100644 --- a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs @@ -8,6 +8,8 @@ internal class ASUSACPIPowerLimiter : IPowerLimiter private bool allAmd; private bool fPPT; + private int lastPowerLimit = 0; + public ASUSACPIPowerLimiter() { allAmd = Program.acpi.IsAllAmdPPT(); @@ -26,14 +28,26 @@ public void SavePowerLimits() public void SetCPUPowerLimit(double watts) { + int pl = (int)Math.Ceiling(watts); + + if (lastPowerLimit == pl) + { + //Do not set the same limit twice to reduce load on the ACPI driver + return; + } + if (allAmd) // CPU limit all amd models { - Program.acpi.DeviceSet(AsusACPI.PPT_CPUB0, (int)watts, "PowerLimit B0"); - } else { - Program.acpi.DeviceSet(AsusACPI.PPT_APUA3, (int)watts, "PowerLimit A3"); - Program.acpi.DeviceSet(AsusACPI.PPT_APUA0, (int)watts, "PowerLimit A0"); - if (fPPT) Program.acpi.DeviceSet(AsusACPI.PPT_APUC1, (int)watts, "PowerLimit C1"); + Program.acpi.DeviceSet(AsusACPI.PPT_CPUB0, pl, "PowerLimit B0"); + } + else + { + Program.acpi.DeviceSet(AsusACPI.PPT_APUA3, pl, "PowerLimit A3"); + Program.acpi.DeviceSet(AsusACPI.PPT_APUA0, pl, "PowerLimit A0"); + if (fPPT) Program.acpi.DeviceSet(AsusACPI.PPT_APUC1, pl, "PowerLimit C1"); } + + lastPowerLimit = pl; } From bc5f5682ddafcd0746db5d170f49132b664296f9 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Tue, 5 Mar 2024 18:49:42 +0100 Subject: [PATCH 36/76] Separate logger for auto tdp. Increased sampling rate to have smoother framerates and faster power reduction. --- app/AutoTDP/AutoTDPLogger.cs | 39 ++++++++++++++++++++++++ app/AutoTDP/AutoTDPService.cs | 57 +++++++++++++++++++++++------------ 2 files changed, 76 insertions(+), 20 deletions(-) create mode 100644 app/AutoTDP/AutoTDPLogger.cs diff --git a/app/AutoTDP/AutoTDPLogger.cs b/app/AutoTDP/AutoTDPLogger.cs new file mode 100644 index 000000000..76660ed80 --- /dev/null +++ b/app/AutoTDP/AutoTDPLogger.cs @@ -0,0 +1,39 @@ +using System.Diagnostics; + +public static class AutoTDPLogger +{ + public static string appPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\GHelper"; + public static string logFile = appPath + "\\auto_tdp_log.txt"; + + public static void WriteLine(string logMessage) + { + Debug.WriteLine(logMessage); + if (!Directory.Exists(appPath)) Directory.CreateDirectory(appPath); + + try + { + using (StreamWriter w = File.AppendText(logFile)) + { + w.WriteLine($"{DateTime.Now}: {logMessage}"); + w.Close(); + } + } + catch { } + + if (new Random().Next(100) == 1) Cleanup(); + + + } + + public static void Cleanup() + { + try + { + var file = File.ReadAllLines(logFile); + int skip = Math.Max(0, file.Count() - 5000); + File.WriteAllLines(logFile, file.Skip(skip).ToArray()); + } + catch { } + } + +} diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 6e59222f8..35e1d21da 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -9,10 +9,13 @@ namespace GHelper.AutoTDP 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 bool LOG_AUTO_TDP = true; + private static readonly int INTERVAL_MIN_CHECK = 10 * 1_000; private static readonly int INTERVAL_APP_CHECK = 5_000; - private static readonly int INTERVAL_FPS_CHECK = 500; + private static readonly int INTERVAL_FPS_CHECK = 33; + + private static readonly int INTERVAL_LOG = 1_000; + private int LogCounter = 0; string GameProfileFile = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\GHelper\\AutoTDP.json"; @@ -226,14 +229,14 @@ public void CheckForGame() if (runningGames.Count == 0) { if (LOG_AUTO_TDP) - Logger.WriteLine("[AutoTDPService] No games detected"); + AutoTDPLogger.WriteLine("[AutoTDPService] No games detected"); return; } foreach (GameInstance gi in runningGames) { if (LOG_AUTO_TDP) - Logger.WriteLine("[AutoTDPService] Detected App: " + gi.ProcessName + " PID: " + gi.ProcessID); + AutoTDPLogger.WriteLine("[AutoTDPService] Detected App: " + gi.ProcessName + " PID: " + gi.ProcessID); if (IsGameInList(gi.ProcessName)) { @@ -273,7 +276,7 @@ public void HandleGame(GameInstance instance) if (currentGame is not null) { if (LOG_AUTO_TDP) - Logger.WriteLine("[AutoTDPService] Already handling a game"); + AutoTDPLogger.WriteLine("[AutoTDPService] Already handling a game"); return; } @@ -299,6 +302,7 @@ public void Reset() FramerateDipCounter = 0; LowestStableStability = 0; LowestStableChecks = 0; + LogCounter = 0; if (powerLimiter is not null) { @@ -330,12 +334,13 @@ public void StartGameHandler(GameInstance instance) double fps = framerateSouce.GetFramerate(instance); - if (LOG_AUTO_TDP) - Logger.WriteLine("[AutoTDPService] (" + instance.ProcessName + ") Framerate " + GameFPS); + if (LOG_AUTO_TDP && LogCounter * INTERVAL_FPS_CHECK > INTERVAL_LOG) + AutoTDPLogger.WriteLine("[AutoTDPService] (" + instance.ProcessName + ") Framerate " + GameFPS); if (fps < 0.0d) { //Game is not running anymore or RTSS lost its hook + Logger.WriteLine("[AutoTDPService] Game exited: " + instance.ProcessName + " PID: " + instance.ProcessID); Reset(); return; } @@ -385,14 +390,14 @@ private double FPSDipCorrection(double currentFramerate, double targetFPS) FramerateDipCounter++; FramerateUnstable(); - correction = targetFPS + 0.15 - currentFramerate; + correction = targetFPS + 0.25 - currentFramerate; } else if (FramerateDipCounter >= 4 && targetFPS - 0.5 <= FramerateLog.Average() && FramerateLog.Average() - 0.1 <= targetFPS) { //long dip - correction = targetFPS + 0.35 - currentFramerate; + correction = targetFPS + 0.45 - currentFramerate; FramerateTargetReachedCounter = FPSDipHistorySize; FramerateVeryUnstable(); } @@ -450,7 +455,8 @@ private void ProcessStability() { LowestStableStability++; - if (LowestStableStability > 120) + //Stable for at least 15s + if (LowestStableStability * INTERVAL_FPS_CHECK > (15 * 1_000)) { //if stable for long time try to reduce it again LowestStableTDP = ProfileForGame(currentGame.ProcessName).MaxTdp; @@ -462,7 +468,7 @@ private void ProcessStability() { LowestStableStability++; - if (LowestStableStability > 10 && Stabilize()) + if (LowestStableStability * INTERVAL_FPS_CHECK > (5 * 1_000) && Stabilize()) { LowestStableTDP = LowestTDP + (LowestTDP * 0.10); // Add 10% additional wattage to get a smoother framerate } @@ -509,7 +515,7 @@ public void AdjustPowerLimit(GameProfile profile) delta = Math.Clamp(delta, -15, 15); - double adjustment = (delta * CurrentTDP / GameFPS) * 0.65; + double adjustment = (delta * CurrentTDP / GameFPS) * 0.7; //Dampen the changes to not change TDP too aggressively which would cause performance issues adjustment += TDPDamper(GameFPS); @@ -518,22 +524,22 @@ public void AdjustPowerLimit(GameProfile profile) if (GameFPSPrevious > profile.GetTDPFPS() && GameFPS < profile.GetTDPFPS()) { if (LOG_AUTO_TDP) - Logger.WriteLine("[AutoTDPService] Single Dip, Ignore"); + AutoTDPLogger.WriteLine("[AutoTDPService] Single Dip, Ignore"); //single dip. Ignore return; } - if (LastAdjustment > 0 && GameFPS <= GameFPSPrevious && adjustment > 0) + if (LastAdjustment > 0 && GameFPS <= GameFPSPrevious + 0.1 && adjustment > 0) { LastAdjustmentsWithoutImprovement++; LastAdjustmentTotal += adjustment; - //Wait for 3 consecutive power increases and at least 3W increased TDP before judging that increasing power does nothing. - if (LastAdjustmentsWithoutImprovement >= 3 && LastAdjustmentTotal > 3) + //Wait for 10 consecutive power increases and at least 5W increased TDP before judging that increasing power does nothing. + if (LastAdjustmentsWithoutImprovement >= 10 && LastAdjustmentTotal > 5) { //Do not adjust if increasing power does not improve framerate. if (LOG_AUTO_TDP) - Logger.WriteLine("[AutoTDPService] Not adjusting because no improvement from last increase"); + AutoTDPLogger.WriteLine("[AutoTDPService] Not adjusting because no improvement from last increase"); return; } @@ -556,8 +562,19 @@ public void AdjustPowerLimit(GameProfile profile) } 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"); + { + if (LogCounter * INTERVAL_FPS_CHECK > INTERVAL_LOG) + { + LogCounter = 0; + AutoTDPLogger.WriteLine("[AutoTDPService] Power Limit from " + CurrentTDP + "W to " + newPL + "W, Delta:" + adjustment + + " Lowest: " + LowestTDP + "W, Lowest Stable(" + LowestStableStability + "): " + LowestStableTDP + "W"); + } + else + { + LogCounter++; + } + } + //Apply power limits powerLimiter.SetCPUPowerLimit(newPL); From 87eb98327eb118a603385f3c4f89801eb53152f2 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Tue, 5 Mar 2024 22:22:59 +0100 Subject: [PATCH 37/76] Do not initiate autotdp before ACPI was not initialized --- app/Program.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Program.cs b/app/Program.cs index 2f07fda86..f86594643 100644 --- a/app/Program.cs +++ b/app/Program.cs @@ -47,7 +47,7 @@ static class Program private static PowerLineStatus isPlugged = SystemInformation.PowerStatus.PowerLineStatus; - public static AutoTDPService autoTDPService = new AutoTDPService(); + public static AutoTDPService autoTDPService; // The main entry point for the application public static void Main(string[] args) @@ -73,6 +73,7 @@ public static void Main(string[] args) Logger.WriteLine("App launched: " + AppConfig.GetModel() + " :" + Assembly.GetExecutingAssembly().GetName().Version.ToString() + CultureInfo.CurrentUICulture + (ProcessHelper.IsUserAdministrator() ? "." : "")); acpi = new AsusACPI(); + autoTDPService = new AutoTDPService(); if (!acpi.IsConnected() && AppConfig.IsASUS()) { From e9f6e62418813baba91e8bfaea2799d252be1b36 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Tue, 5 Mar 2024 22:23:21 +0100 Subject: [PATCH 38/76] Additional logging to see whether power limits were read correctly --- app/AutoTDP/AutoTDPService.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 35e1d21da..d6fd88976 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -326,6 +326,8 @@ public void StartGameHandler(GameInstance instance) CurrentTDP = powerLimiter.GetCPUPowerLimit(); powerLimiter.SavePowerLimits(); // save current power limits to restore them afterwards + Logger.WriteLine("[AutoTDPService] Backing up Power limit: " + CurrentTDP + "W"); + LowestStableTDP = profile.MaxTdp; LowestTDP = profile.MaxTdp; From fa1010056ee87ec7c3f380e15eb357db9e0ab87e Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Tue, 5 Mar 2024 22:25:36 +0100 Subject: [PATCH 39/76] Additional error checking whether WinRing0 driver was loaded. Logging whether rdmsr and wrmsr actually worked. --- .../PowerLimiter/IntelMSRPowerLimiter.cs | 67 +++++++++++++++++-- 1 file changed, 61 insertions(+), 6 deletions(-) diff --git a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs index 0a0a04aad..f886d8fb3 100644 --- a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs @@ -25,11 +25,48 @@ public IntelMSRPowerLimiter() ols = new Ols(); ols.InitializeOls(); ReadPowerUnit(); + Logger.WriteLine("[AutoTDPService] Read MSR_RAPL_POWER_UNIT: " + PowerUnit); } public static bool IsAvailable() { - return !RyzenControl.IsAMD(); + if (RyzenControl.IsAMD()) + { + return false; + } + Ols o = new Ols(); + + o.InitializeOls(); + + uint err = o.GetDllStatus(); + o.DeinitializeOls(); + o.Dispose(); + + switch (err) + { + case (uint)Ols.OlsDllStatus.OLS_DLL_NO_ERROR: + return true; + case (uint)Ols.OlsDllStatus.OLS_DLL_DRIVER_NOT_LOADED: + Logger.WriteLine("[AutoTDPService] Intel MSR Error: OLS_DRIVER_NOT_LOADED"); + break; + case (uint)Ols.OlsDllStatus.OLS_DLL_UNSUPPORTED_PLATFORM: + Logger.WriteLine("[AutoTDPService] Intel MSR Error: OLS_DLL_UNSUPPORTED_PLATFORM"); + break; + case (uint)Ols.OlsDllStatus.OLS_DLL_DRIVER_NOT_FOUND: + Logger.WriteLine("[AutoTDPService] Intel MSR Error: OLS_DLL_DRIVER_NOT_FOUND"); + break; + case (uint)Ols.OlsDllStatus.OLS_DLL_DRIVER_UNLOADED: + Logger.WriteLine("[AutoTDPService] Intel MSR Error: OLS_DLL_DRIVER_UNLOADED"); + break; + case (uint)Ols.OlsDllStatus.OLS_DLL_DRIVER_NOT_LOADED_ON_NETWORK: + Logger.WriteLine("[AutoTDPService] Intel MSR Error: OLS_DLL_DRIVER_NOT_LOADED_ON_NETWORK"); + break; + case (uint)Ols.OlsDllStatus.OLS_DLL_UNKNOWN_ERROR: + Logger.WriteLine("[AutoTDPService] Intel MSR Error: OLS_DLL_UNKNOWN_ERROR"); + break; + } + + return false; } public void SavePowerLimits() @@ -37,7 +74,10 @@ public void SavePowerLimits() DefaultEax = 0; DefaultEdx = 0; - ols.Rdmsr(MSR_PKG_POWER_LIMIT, ref DefaultEax, ref DefaultEdx); + if (ols.Rdmsr(MSR_PKG_POWER_LIMIT, ref DefaultEax, ref DefaultEdx) == 0) + { + Logger.WriteLine("[AutoTDPService] Failed to Read MSR_PKG_POWER_LIMIT"); + } } public void ReadPowerUnit() @@ -45,7 +85,10 @@ public void ReadPowerUnit() uint eax = 0; uint edx = 0; - ols.Rdmsr(MSR_RAPL_POWER_UNIT, ref eax, ref edx); + if (ols.Rdmsr(MSR_RAPL_POWER_UNIT, ref eax, ref edx) == 0) + { + Logger.WriteLine("[AutoTDPService] Failed to Read MSR_RAPL_POWER_UNIT"); + } uint pwr = eax & 0x03; @@ -59,7 +102,10 @@ public void SetCPUPowerLimit(double watts) uint edx = 0; - ols.Rdmsr(MSR_PKG_POWER_LIMIT, ref eax, ref edx); + if (ols.Rdmsr(MSR_PKG_POWER_LIMIT, ref eax, ref edx) == 0) + { + Logger.WriteLine("[AutoTDPService] Failed to Read MSR_PKG_POWER_LIMIT"); + } uint watsRapl = (uint)(watts / PowerUnit); @@ -74,7 +120,10 @@ public void SetCPUPowerLimit(double watts) eaxFilterd |= 0x8000; edxFilterd |= 0x8000; - ols.Wrmsr(0x610, eaxFilterd, edxFilterd); + if (ols.Wrmsr(0x610, eaxFilterd, edxFilterd) == 0) + { + Logger.WriteLine("[AutoTDPService] Failed to Write MSR_PKG_POWER_LIMIT"); + } } @@ -83,11 +132,17 @@ public int GetCPUPowerLimit() uint eax = 0; uint edx = 0; - ols.Rdmsr(MSR_PKG_POWER_LIMIT, ref eax, ref edx); + if (ols.Rdmsr(MSR_PKG_POWER_LIMIT, ref eax, ref edx) == 0) + { + Logger.WriteLine("[AutoTDPService] Failed to Read MSR_PKG_POWER_LIMIT"); + } uint pl1 = eax & PL1_MASK; uint pl2 = edx & PL2_MASK; + + Logger.WriteLine("[AutoTDPService] Read Power Limit - PL1: " + pl1 + "W, PL2: " + pl2 + "W"); + return (int)(pl1 * PowerUnit); } From cf77977c9234c6e2cc38d7d1c125c5453830f3d7 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Wed, 6 Mar 2024 10:03:45 +0100 Subject: [PATCH 40/76] Read RTSS path from registry --- .../FramerateSource/RTSSFramerateSource.cs | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs b/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs index 6fef21d4b..8522d3e1d 100644 --- a/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs +++ b/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using Microsoft.Win32; using RTSSSharedMemoryNET; namespace GHelper.AutoTDP.FramerateSource @@ -15,15 +16,36 @@ internal class RTSSFramerateSource : IFramerateSource public static bool IsRunning => Process.GetProcessesByName("RTSS").Length != 0; + private static string GetInstallPath() + { + try + { + return (string)Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Unwinder\\RTSS", "InstallPath", null); + } + catch (Exception e) + { + return null; + } + } + + static RTSSFramerateSource() { - RTSSPath = @"C:\Program Files (x86)\RivaTuner Statistics Server\RTSS.exe"; + string path = GetInstallPath(); + + if (path is not null) + { + RTSSPath = path; + } + else + { + RTSSPath = @"C:\Program Files (x86)\RivaTuner Statistics Server\RTSS.exe"; + } } public static bool IsAvailable() { return File.Exists(RTSSPath); - } public static void Start() From 1fe8ebab160423f27a74a55a3ea69e7730c63ad2 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Wed, 6 Mar 2024 10:04:23 +0100 Subject: [PATCH 41/76] Have a variable minimum sample interval defined by the power limiter. --- app/AutoTDP/AutoTDPService.cs | 21 +++++++++++++------ .../PowerLimiter/ASUSACPIPowerLimiter.cs | 4 ++++ app/AutoTDP/PowerLimiter/IPowerLimiter.cs | 2 ++ .../PowerLimiter/IntelMSRPowerLimiter.cs | 5 +++++ 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index d6fd88976..2afc64d69 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -86,6 +86,15 @@ public static bool IsAvailable() return AvailablePowerLimiters().Count > 0 && AvailableFramerateSources().Count > 0; } + private int FPSCheckInterval() + { + if (powerLimiter is null) + { + return INTERVAL_FPS_CHECK; + } + return (int)Math.Max(INTERVAL_FPS_CHECK, powerLimiter.GetMinInterval()); + } + public static List AvailableFramerateSources() { List l = new List(); @@ -336,7 +345,7 @@ public void StartGameHandler(GameInstance instance) double fps = framerateSouce.GetFramerate(instance); - if (LOG_AUTO_TDP && LogCounter * INTERVAL_FPS_CHECK > INTERVAL_LOG) + if (LOG_AUTO_TDP && LogCounter * FPSCheckInterval() > INTERVAL_LOG) AutoTDPLogger.WriteLine("[AutoTDPService] (" + instance.ProcessName + ") Framerate " + GameFPS); if (fps < 0.0d) @@ -353,7 +362,7 @@ public void StartGameHandler(GameInstance instance) try { - Thread.Sleep(INTERVAL_FPS_CHECK); + Thread.Sleep(FPSCheckInterval()); } catch (ThreadInterruptedException) { @@ -437,7 +446,7 @@ private void FramerateVeryUnstable() private bool Stabilize() { - return LowestStableChecks * INTERVAL_FPS_CHECK > INTERVAL_MIN_CHECK; + return LowestStableChecks * FPSCheckInterval() > INTERVAL_MIN_CHECK; } private void ProcessStability() @@ -458,7 +467,7 @@ private void ProcessStability() LowestStableStability++; //Stable for at least 15s - if (LowestStableStability * INTERVAL_FPS_CHECK > (15 * 1_000)) + if (LowestStableStability * FPSCheckInterval() > (15 * 1_000)) { //if stable for long time try to reduce it again LowestStableTDP = ProfileForGame(currentGame.ProcessName).MaxTdp; @@ -470,7 +479,7 @@ private void ProcessStability() { LowestStableStability++; - if (LowestStableStability * INTERVAL_FPS_CHECK > (5 * 1_000) && Stabilize()) + if (LowestStableStability * FPSCheckInterval() > (5 * 1_000) && Stabilize()) { LowestStableTDP = LowestTDP + (LowestTDP * 0.10); // Add 10% additional wattage to get a smoother framerate } @@ -565,7 +574,7 @@ public void AdjustPowerLimit(GameProfile profile) if (LOG_AUTO_TDP) { - if (LogCounter * INTERVAL_FPS_CHECK > INTERVAL_LOG) + if (LogCounter * FPSCheckInterval() > INTERVAL_LOG) { LogCounter = 0; AutoTDPLogger.WriteLine("[AutoTDPService] Power Limit from " + CurrentTDP + "W to " + newPL + "W, Delta:" + adjustment diff --git a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs index 57939d4a1..8785e50c9 100644 --- a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs @@ -20,6 +20,10 @@ public static bool IsAvailable() { return AppConfig.IsASUS(); } + public int GetMinInterval() + { + return 250; + } public void SavePowerLimits() { diff --git a/app/AutoTDP/PowerLimiter/IPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IPowerLimiter.cs index de799f6a0..58ebe7f83 100644 --- a/app/AutoTDP/PowerLimiter/IPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/IPowerLimiter.cs @@ -10,5 +10,7 @@ internal interface IPowerLimiter : IDisposable public void SavePowerLimits(); + public int GetMinInterval(); + } } diff --git a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs index f886d8fb3..e2bf60a0e 100644 --- a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs @@ -28,6 +28,11 @@ public IntelMSRPowerLimiter() Logger.WriteLine("[AutoTDPService] Read MSR_RAPL_POWER_UNIT: " + PowerUnit); } + public int GetMinInterval() + { + return 33; + } + public static bool IsAvailable() { if (RyzenControl.IsAMD()) From 143137bb017fa816fe32e42b1150b9c9979cc44f Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Wed, 6 Mar 2024 12:04:13 +0100 Subject: [PATCH 42/76] Automatically re-apply power limiter when switching the drop down and the new value changed. --- app/AutoTDP/AutoTDPService.cs | 20 +++++++++++++++++++- app/AutoTDP/AutoTDPUI.cs | 10 +++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 2afc64d69..997407b09 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -120,6 +120,24 @@ public static List AvailablePowerLimiters() return l; } + public void SwapPowerLimiter() + { + IPowerLimiter? ipl = powerLimiter; + powerLimiter = null; + + if (ipl is not null) + { + ipl.ResetPowerLimits(); + ipl.Dispose(); + } + + + InitLimiter(); + + if (powerLimiter is not null) + powerLimiter.SavePowerLimits(); + } + public void Start() { if (!IsEnabled() || IsRunning() || !IsAvailable()) @@ -588,7 +606,7 @@ public void AdjustPowerLimit(GameProfile profile) //Apply power limits - powerLimiter.SetCPUPowerLimit(newPL); + powerLimiter?.SetCPUPowerLimit(newPL); CurrentTDP = newPL; } diff --git a/app/AutoTDP/AutoTDPUI.cs b/app/AutoTDP/AutoTDPUI.cs index 35e0eee6f..0874f15aa 100644 --- a/app/AutoTDP/AutoTDPUI.cs +++ b/app/AutoTDP/AutoTDPUI.cs @@ -45,7 +45,15 @@ private void ComboBoxFPSSource_DropDownClosed(object? sender, EventArgs e) private void ComboBoxLimiter_DropDownClosed(object? sender, EventArgs e) { - AppConfig.Set("auto_tdp_limiter", AutoTDPService.AvailablePowerLimiters().ElementAt(comboBoxLimiter.SelectedIndex)); + string? limiter = AppConfig.GetString("auto_tdp_limiter"); + string newLimiter = AutoTDPService.AvailablePowerLimiters().ElementAt(comboBoxLimiter.SelectedIndex); + + AppConfig.Set("auto_tdp_limiter", newLimiter); + + if (!newLimiter.Equals(limiter)) + { + Program.autoTDPService.SwapPowerLimiter(); + } } private void CheckBoxEnabled_CheckedChanged(object? sender, EventArgs e) From 5636a5b39fd9887bbcf44d76cf0daf1e25c6ff6b Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Wed, 6 Mar 2024 12:04:50 +0100 Subject: [PATCH 43/76] Do not deinitialize the driver when it is still in use --- .../PowerLimiter/IntelMSRPowerLimiter.cs | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs index e2bf60a0e..9ede44f4c 100644 --- a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs @@ -10,6 +10,8 @@ internal class IntelMSRPowerLimiter : IPowerLimiter private Ols ols; + private static bool DRIVER_LOADED = false; + private uint DefaultEax = 0; // Set on first reading private uint DefaultEdx = 0; @@ -39,6 +41,12 @@ public static bool IsAvailable() { return false; } + + if (DRIVER_LOADED) + { + return true; + } + Ols o = new Ols(); o.InitializeOls(); @@ -47,10 +55,22 @@ public static bool IsAvailable() o.DeinitializeOls(); o.Dispose(); + if (err == (uint)Ols.OlsDllStatus.OLS_DLL_NO_ERROR) + { + return true; + } + + LogOLSState(err); + + return false; + } + + private static void LogOLSState(uint err) + { switch (err) { case (uint)Ols.OlsDllStatus.OLS_DLL_NO_ERROR: - return true; + return; case (uint)Ols.OlsDllStatus.OLS_DLL_DRIVER_NOT_LOADED: Logger.WriteLine("[AutoTDPService] Intel MSR Error: OLS_DRIVER_NOT_LOADED"); break; @@ -70,8 +90,6 @@ public static bool IsAvailable() Logger.WriteLine("[AutoTDPService] Intel MSR Error: OLS_DLL_UNKNOWN_ERROR"); break; } - - return false; } public void SavePowerLimits() @@ -82,6 +100,7 @@ public void SavePowerLimits() if (ols.Rdmsr(MSR_PKG_POWER_LIMIT, ref DefaultEax, ref DefaultEdx) == 0) { Logger.WriteLine("[AutoTDPService] Failed to Read MSR_PKG_POWER_LIMIT"); + LogOLSState(ols.GetDllStatus()); } } @@ -93,6 +112,7 @@ public void ReadPowerUnit() if (ols.Rdmsr(MSR_RAPL_POWER_UNIT, ref eax, ref edx) == 0) { Logger.WriteLine("[AutoTDPService] Failed to Read MSR_RAPL_POWER_UNIT"); + LogOLSState(ols.GetDllStatus()); } @@ -110,6 +130,7 @@ public void SetCPUPowerLimit(double watts) if (ols.Rdmsr(MSR_PKG_POWER_LIMIT, ref eax, ref edx) == 0) { Logger.WriteLine("[AutoTDPService] Failed to Read MSR_PKG_POWER_LIMIT"); + LogOLSState(ols.GetDllStatus()); } uint watsRapl = (uint)(watts / PowerUnit); @@ -128,6 +149,7 @@ public void SetCPUPowerLimit(double watts) if (ols.Wrmsr(0x610, eaxFilterd, edxFilterd) == 0) { Logger.WriteLine("[AutoTDPService] Failed to Write MSR_PKG_POWER_LIMIT"); + LogOLSState(ols.GetDllStatus()); } } @@ -140,6 +162,7 @@ public int GetCPUPowerLimit() if (ols.Rdmsr(MSR_PKG_POWER_LIMIT, ref eax, ref edx) == 0) { Logger.WriteLine("[AutoTDPService] Failed to Read MSR_PKG_POWER_LIMIT"); + LogOLSState(ols.GetDllStatus()); } uint pl1 = eax & PL1_MASK; @@ -165,6 +188,7 @@ public void Dispose() { ols.DeinitializeOls(); ols.Dispose(); + DRIVER_LOADED = true; } } } From 8594e7827d8b8f70f5d45761f7bc4c5c05d73587 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Wed, 6 Mar 2024 12:05:09 +0100 Subject: [PATCH 44/76] Sample less often to reduce system calls --- app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs index 9ede44f4c..bdf5475a3 100644 --- a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs @@ -26,13 +26,14 @@ public IntelMSRPowerLimiter() { ols = new Ols(); ols.InitializeOls(); + DRIVER_LOADED = true; ReadPowerUnit(); Logger.WriteLine("[AutoTDPService] Read MSR_RAPL_POWER_UNIT: " + PowerUnit); } public int GetMinInterval() { - return 33; + return 250; } public static bool IsAvailable() From 63bf0bb1976f53528d68f6ee072f4d496b21490f Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Wed, 6 Mar 2024 13:20:31 +0100 Subject: [PATCH 45/76] Log power limits in Ws and not in RAPL units --- app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs index bdf5475a3..bbbf6fb23 100644 --- a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs @@ -170,7 +170,7 @@ public int GetCPUPowerLimit() uint pl2 = edx & PL2_MASK; - Logger.WriteLine("[AutoTDPService] Read Power Limit - PL1: " + pl1 + "W, PL2: " + pl2 + "W"); + Logger.WriteLine("[AutoTDPService] Read Power Limit - PL1: " + pl1 * PowerUnit + "W, PL2: " + pl2 * PowerUnit + "W"); return (int)(pl1 * PowerUnit); } From e9250d02f279f74c00c97af0aa309db27ebaa599 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Wed, 6 Mar 2024 17:49:12 +0100 Subject: [PATCH 46/76] Add preparation step. ASUS ACPI needs that to switch into manual mode on some laptops or power limiting will not work at all. --- app/AutoTDP/AutoTDPService.cs | 10 ++++++++-- app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs | 6 ++++++ app/AutoTDP/PowerLimiter/IPowerLimiter.cs | 2 ++ app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs | 5 +++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 997407b09..4ee46f514 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -134,8 +134,12 @@ public void SwapPowerLimiter() InitLimiter(); - if (powerLimiter is not null) + if (powerLimiter is not null && IsActive()) + { powerLimiter.SavePowerLimits(); + powerLimiter.Prepare(); + } + } public void Start() @@ -353,6 +357,8 @@ public void StartGameHandler(GameInstance instance) CurrentTDP = powerLimiter.GetCPUPowerLimit(); powerLimiter.SavePowerLimits(); // save current power limits to restore them afterwards + powerLimiter.Prepare(); + Logger.WriteLine("[AutoTDPService] Backing up Power limit: " + CurrentTDP + "W"); LowestStableTDP = profile.MaxTdp; @@ -624,6 +630,7 @@ public void StopGameHandler() public void Shutdown() { Running = false; + currentGame = null; if (checkerThread is not null) { @@ -650,7 +657,6 @@ public void Shutdown() framerateSouce = null; } - currentGame = null; Reset(); //Kill RTSS instance if we started one diff --git a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs index 8785e50c9..4bcb6cd5b 100644 --- a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs @@ -25,6 +25,12 @@ public int GetMinInterval() return 250; } + public void Prepare() + { + //Program.modeControl.AutoFans(AppConfig.IsManualModeRequired() || AppConfig.IsFanRequired()); + Program.modeControl.SetPerformanceMode(Modes.GetCurrent()); + } + public void SavePowerLimits() { } diff --git a/app/AutoTDP/PowerLimiter/IPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IPowerLimiter.cs index 58ebe7f83..919990f08 100644 --- a/app/AutoTDP/PowerLimiter/IPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/IPowerLimiter.cs @@ -12,5 +12,7 @@ internal interface IPowerLimiter : IDisposable public int GetMinInterval(); + public void Prepare(); + } } diff --git a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs index bbbf6fb23..16d90c7aa 100644 --- a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs @@ -36,6 +36,11 @@ public int GetMinInterval() return 250; } + public void Prepare() + { + + } + public static bool IsAvailable() { if (RyzenControl.IsAMD()) From 8d9c8c452b34873b81330dcecd98e17f571e270a Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Wed, 6 Mar 2024 17:49:30 +0100 Subject: [PATCH 47/76] Fixed a typo --- app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs index 16d90c7aa..354e39d2d 100644 --- a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs @@ -139,14 +139,15 @@ public void SetCPUPowerLimit(double watts) LogOLSState(ols.GetDllStatus()); } - uint watsRapl = (uint)(watts / PowerUnit); + //Power limit in RAPL units + uint wattsRapl = (uint)(watts / PowerUnit); //Set limits for both PL1 and PL2 uint eaxFilterd = eax & ~PL1_MASK; uint edxFilterd = edx & ~PL2_MASK; - eaxFilterd |= watsRapl; - edxFilterd |= watsRapl; + eaxFilterd |= wattsRapl; + edxFilterd |= wattsRapl; //Enable clamping eaxFilterd |= 0x8000; From 8c1152117c83a0d5909ba254e4af2d2cc15c693d Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Wed, 6 Mar 2024 17:50:14 +0100 Subject: [PATCH 48/76] Apparently we cannot read the power limits back from the API. Just return 200. We need a high value as starting point (which is usually the current limit). --- app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs index 4bcb6cd5b..cd4cb6ed0 100644 --- a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs @@ -64,7 +64,7 @@ public void SetCPUPowerLimit(double watts) // You can't read PPTs on ASUS :) endpoints just return 0 if they are available public int GetCPUPowerLimit() { - return Program.acpi.DeviceGet(AsusACPI.PPT_APUA0); + return 200; } public void Dispose() From fd4ba471b2d836dcb6543d5fd6749e1b1b19ff2c Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Wed, 6 Mar 2024 17:51:25 +0100 Subject: [PATCH 49/76] Add additional logging for initialization of limiter and fps source --- app/AutoTDP/AutoTDPService.cs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 4ee46f514..f7980d912 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -184,6 +184,15 @@ public void InitFramerateSource() { string? source = AppConfig.GetString("auto_tdp_fps_source"); + if (source is null) + { + Logger.WriteLine("[AutoTDPService] No source defined in settings. Using Default"); + } + else + { + Logger.WriteLine("[AutoTDPService] Framerate Source Setting: " + source); + } + if ((source is null || source.Equals("rtss")) && RTSSFramerateSource.IsAvailable()) { Logger.WriteLine("[AutoTDPService] Initializing RTSSFramerateSource..."); @@ -198,7 +207,16 @@ public void InitLimiter() { string? limiter = AppConfig.GetString("auto_tdp_limiter"); - if (limiter is null || limiter.Equals("asus_acpi") && ASUSACPIPowerLimiter.IsAvailable()) + if (limiter is null) + { + Logger.WriteLine("[AutoTDPService] No limiter defined in settings. Using Default"); + } + else + { + Logger.WriteLine("[AutoTDPService] Limiter Setting: " + limiter); + } + + if (limiter is null || (limiter.Equals("asus_acpi") && ASUSACPIPowerLimiter.IsAvailable())) { Logger.WriteLine("[AutoTDPService] Initializing ASUSACPIPowerLimiter..."); powerLimiter = new ASUSACPIPowerLimiter(); From 05591ba80ea9f9607653393ed4e437cc449b45c0 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Wed, 6 Mar 2024 17:52:17 +0100 Subject: [PATCH 50/76] Stabilize for a longer period of time to reduce stutter in constant low load scenes. --- app/AutoTDP/AutoTDPService.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index f7980d912..c331a290b 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -10,7 +10,7 @@ internal class AutoTDPService : IDisposable { private static readonly bool LOG_AUTO_TDP = true; - private static readonly int INTERVAL_MIN_CHECK = 10 * 1_000; + private static readonly int INTERVAL_MIN_CHECK = 15 * 1_000; private static readonly int INTERVAL_APP_CHECK = 5_000; private static readonly int INTERVAL_FPS_CHECK = 33; @@ -508,8 +508,8 @@ private void ProcessStability() { LowestStableStability++; - //Stable for at least 15s - if (LowestStableStability * FPSCheckInterval() > (15 * 1_000)) + //Stable for at least 120s + if (LowestStableStability * FPSCheckInterval() > (120 * 1_000)) { //if stable for long time try to reduce it again LowestStableTDP = ProfileForGame(currentGame.ProcessName).MaxTdp; @@ -534,7 +534,7 @@ private void ProcessStability() LowestTDP = CurrentTDP; } - LowestStableStability = Math.Min(LowestStableStability, 150); + LowestStableStability = Math.Min(LowestStableStability, (125 * 1_000) / FPSCheckInterval()); } From 3d3f67a635e0dd4c30f4973aeb08986920e07e3b Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Wed, 6 Mar 2024 17:52:53 +0100 Subject: [PATCH 51/76] Due to slower sample time, stutterings have bigger impact now on incease of TDP. --- app/AutoTDP/AutoTDPService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index c331a290b..da1fd8414 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -445,7 +445,7 @@ private double FPSDipCorrection(double currentFramerate, double targetFPS) correction = targetFPS + 0.25 - currentFramerate; } - else if (FramerateDipCounter >= 4 + else if (FramerateDipCounter >= 2 && targetFPS - 0.5 <= FramerateLog.Average() && FramerateLog.Average() - 0.1 <= targetFPS) { @@ -479,11 +479,11 @@ private void FramerateStable() private void FramerateUnstable() { - LowestStableStability -= 15; + LowestStableStability -= 30; } private void FramerateVeryUnstable() { - LowestStableStability -= 60; + LowestStableStability = -10; } private bool Stabilize() From 2fe9835cd03724100d33a0aa84660b0baae279fb Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Wed, 6 Mar 2024 17:53:36 +0100 Subject: [PATCH 52/76] This is required or the function would always return "false" when the laptop is currently not in a manual mode, which prevents the manual mode preparation to not engage. --- app/AppConfig.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/AppConfig.cs b/app/AppConfig.cs index a553c5438..9db04a742 100644 --- a/app/AppConfig.cs +++ b/app/AppConfig.cs @@ -1,4 +1,5 @@ -using GHelper.Mode; +using GHelper; +using GHelper.Mode; using System.Management; using System.Text.Json; @@ -482,7 +483,9 @@ public static bool NoAutoUltimate() public static bool IsManualModeRequired() { - if (!IsMode("auto_apply_power")) + if (!IsMode("auto_apply_power") + && (Program.autoTDPService is null + || (!Program.autoTDPService.IsActive() && AppConfig.GetString("auto_tdp_limiter").Equals("asus_acpi")))) return false; return From 1e5ab00ffae721f5a0ab165e54c1cc04d0268c54 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Wed, 6 Mar 2024 19:01:25 +0100 Subject: [PATCH 53/76] Fix for autotdp detection which could crash on startup --- app/AppConfig.cs | 14 +++++++++++--- app/AutoTDP/AutoTDPService.cs | 2 +- app/Program.cs | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/AppConfig.cs b/app/AppConfig.cs index 9db04a742..6bad388cb 100644 --- a/app/AppConfig.cs +++ b/app/AppConfig.cs @@ -483,9 +483,17 @@ public static bool NoAutoUltimate() public static bool IsManualModeRequired() { - if (!IsMode("auto_apply_power") - && (Program.autoTDPService is null - || (!Program.autoTDPService.IsActive() && AppConfig.GetString("auto_tdp_limiter").Equals("asus_acpi")))) + bool isAutoTDPActive = false; + + + if (Program.autoTDPService is not null) + { + //If using ASUS ACPI and AutoTDP is running right now, ignore current power mode as the auto tdp power settings are temporary + isAutoTDPActive = AppConfig.GetString("auto_tdp_limiter", "").Equals("asus_acpi") && Program.autoTDPService.IsActive(); + } + + + if (!IsMode("auto_apply_power") || isAutoTDPActive) return false; return diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index da1fd8414..63bec391d 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -47,7 +47,7 @@ internal class AutoTDPService : IDisposable private double LastAdjustmentTotal = 0; private int LastAdjustmentsWithoutImprovement = 0; - private GameInstance? currentGame; + private GameInstance? currentGame = null; public AutoTDPService() { diff --git a/app/Program.cs b/app/Program.cs index f86594643..09bdd0add 100644 --- a/app/Program.cs +++ b/app/Program.cs @@ -47,7 +47,7 @@ static class Program private static PowerLineStatus isPlugged = SystemInformation.PowerStatus.PowerLineStatus; - public static AutoTDPService autoTDPService; + public static AutoTDPService autoTDPService = null; // The main entry point for the application public static void Main(string[] args) From 207b44fed92ac6209ffc3e305c39ca91c47b98e7 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Wed, 6 Mar 2024 19:08:16 +0100 Subject: [PATCH 54/76] RTSS needs admin privileges for starting. But we can still attach to one if we're not admin and it is already running. --- app/AutoTDP/FramerateSource/RTSSFramerateSource.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs b/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs index 8522d3e1d..1dd1267fb 100644 --- a/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs +++ b/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using GHelper.Helpers; using Microsoft.Win32; using RTSSSharedMemoryNET; @@ -45,7 +46,12 @@ static RTSSFramerateSource() public static bool IsAvailable() { - return File.Exists(RTSSPath); + if (IsRunning) + { + return true; + } + + return File.Exists(RTSSPath) && ProcessHelper.IsUserAdministrator(); } public static void Start() From 4412286f99e1401507d08cc915c3200f5afc7e86 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Wed, 6 Mar 2024 19:08:37 +0100 Subject: [PATCH 55/76] Intel MSR is not available if not run as admin due to loading the ring0 driver. --- app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs index 354e39d2d..f5928c5a1 100644 --- a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs @@ -1,4 +1,5 @@ -using Ryzen; +using GHelper.Helpers; +using Ryzen; namespace GHelper.AutoTDP.PowerLimiter { @@ -43,6 +44,11 @@ public void Prepare() public static bool IsAvailable() { + if (!ProcessHelper.IsUserAdministrator()) + { + return false; + } + if (RyzenControl.IsAMD()) { return false; From 51d457cb368d4da36a5c7a715c73fc2d1009761e Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Thu, 7 Mar 2024 13:51:51 +0100 Subject: [PATCH 56/76] Added a slider for FPS additionaly to the stepper --- app/AutoTDP/AutoTDPGameProfileUI.Designer.cs | 131 +++++++++++-------- app/AutoTDP/AutoTDPGameProfileUI.cs | 13 ++ 2 files changed, 88 insertions(+), 56 deletions(-) diff --git a/app/AutoTDP/AutoTDPGameProfileUI.Designer.cs b/app/AutoTDP/AutoTDPGameProfileUI.Designer.cs index d93b047bb..58315d4c6 100644 --- a/app/AutoTDP/AutoTDPGameProfileUI.Designer.cs +++ b/app/AutoTDP/AutoTDPGameProfileUI.Designer.cs @@ -47,6 +47,7 @@ private void InitializeComponent() labelLimiter = new Label(); buttonSave = new UI.RButton(); buttonDelete = new UI.RButton(); + sliderFPS = new UI.Slider(); panelPerformanceHeader.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)pictureKeyboard).BeginInit(); panelGameSettings.SuspendLayout(); @@ -61,17 +62,19 @@ private void InitializeComponent() panelPerformanceHeader.Controls.Add(checkBoxEnabled); panelPerformanceHeader.Dock = DockStyle.Top; panelPerformanceHeader.Location = new Point(0, 0); + panelPerformanceHeader.Margin = new Padding(2, 2, 2, 2); panelPerformanceHeader.Name = "panelPerformanceHeader"; - panelPerformanceHeader.Size = new Size(551, 50); + panelPerformanceHeader.Size = new Size(386, 30); panelPerformanceHeader.TabIndex = 53; // // pictureKeyboard // pictureKeyboard.BackgroundImage = Properties.Resources.icons8_automation_32; pictureKeyboard.BackgroundImageLayout = ImageLayout.Zoom; - pictureKeyboard.Location = new Point(4, 13); + pictureKeyboard.Location = new Point(3, 8); + pictureKeyboard.Margin = new Padding(2, 2, 2, 2); pictureKeyboard.Name = "pictureKeyboard"; - pictureKeyboard.Size = new Size(23, 27); + pictureKeyboard.Size = new Size(16, 16); pictureKeyboard.TabIndex = 35; pictureKeyboard.TabStop = false; // @@ -79,19 +82,19 @@ private void InitializeComponent() // labelSettings.AutoSize = true; labelSettings.Font = new Font("Segoe UI", 9F, FontStyle.Bold, GraphicsUnit.Point); - labelSettings.Location = new Point(31, 13); - labelSettings.Margin = new Padding(6, 0, 6, 0); + labelSettings.Location = new Point(22, 8); + labelSettings.Margin = new Padding(4, 0, 4, 0); labelSettings.Name = "labelSettings"; - labelSettings.Size = new Size(135, 25); + labelSettings.Size = new Size(89, 15); labelSettings.TabIndex = 34; labelSettings.Text = "Game Settings"; // // checkBoxEnabled // - checkBoxEnabled.Location = new Point(344, 3); - checkBoxEnabled.Margin = new Padding(6, 0, 6, 0); + checkBoxEnabled.Location = new Point(241, 2); + checkBoxEnabled.Margin = new Padding(4, 0, 4, 0); checkBoxEnabled.Name = "checkBoxEnabled"; - checkBoxEnabled.Size = new Size(194, 42); + checkBoxEnabled.Size = new Size(136, 25); checkBoxEnabled.TabIndex = 53; checkBoxEnabled.Text = "Enabled"; checkBoxEnabled.UseVisualStyleBackColor = true; @@ -99,6 +102,7 @@ private void InitializeComponent() // panelGameSettings // panelGameSettings.AutoSize = true; + panelGameSettings.Controls.Add(sliderFPS); panelGameSettings.Controls.Add(labelMinTDP); panelGameSettings.Controls.Add(labelMaxTDP); panelGameSettings.Controls.Add(sliderMaxTDP); @@ -112,28 +116,27 @@ private void InitializeComponent() panelGameSettings.Controls.Add(labelFPSSource); panelGameSettings.Controls.Add(labelLimiter); panelGameSettings.Dock = DockStyle.Top; - panelGameSettings.Location = new Point(0, 50); + panelGameSettings.Location = new Point(0, 30); + panelGameSettings.Margin = new Padding(2, 2, 2, 2); panelGameSettings.Name = "panelGameSettings"; - panelGameSettings.Padding = new Padding(0, 0, 0, 12); - panelGameSettings.Size = new Size(551, 244); + panelGameSettings.Padding = new Padding(0, 0, 0, 7); + panelGameSettings.Size = new Size(386, 146); panelGameSettings.TabIndex = 57; // // labelMinTDP // - labelMinTDP.Location = new Point(489, 152); - labelMinTDP.Margin = new Padding(4, 0, 4, 0); + labelMinTDP.Location = new Point(342, 91); labelMinTDP.Name = "labelMinTDP"; - labelMinTDP.Size = new Size(56, 37); + labelMinTDP.Size = new Size(39, 22); labelMinTDP.TabIndex = 67; labelMinTDP.Text = "30W"; labelMinTDP.TextAlign = ContentAlignment.MiddleRight; // // labelMaxTDP // - labelMaxTDP.Location = new Point(487, 195); - labelMaxTDP.Margin = new Padding(4, 0, 4, 0); + labelMaxTDP.Location = new Point(341, 117); labelMaxTDP.Name = "labelMaxTDP"; - labelMaxTDP.Size = new Size(57, 37); + labelMaxTDP.Size = new Size(40, 22); labelMaxTDP.TabIndex = 66; labelMaxTDP.Text = "150W"; labelMaxTDP.TextAlign = ContentAlignment.MiddleRight; @@ -141,25 +144,27 @@ private void InitializeComponent() // sliderMaxTDP // sliderMaxTDP.AccessibleName = "DPI Slider"; - sliderMaxTDP.Location = new Point(200, 195); + sliderMaxTDP.Location = new Point(140, 117); + sliderMaxTDP.Margin = new Padding(2, 2, 2, 2); sliderMaxTDP.Max = 200; sliderMaxTDP.Min = 5; sliderMaxTDP.Name = "sliderMaxTDP"; - sliderMaxTDP.Size = new Size(291, 33); + sliderMaxTDP.Size = new Size(204, 20); sliderMaxTDP.Step = 1; sliderMaxTDP.TabIndex = 65; sliderMaxTDP.TabStop = false; - sliderMaxTDP.Text = "sliderBattery"; + sliderMaxTDP.Text = "Max TDP Slider"; sliderMaxTDP.Value = 0; // // numericUpDownFPS // numericUpDownFPS.BorderStyle = BorderStyle.None; - numericUpDownFPS.Location = new Point(416, 110); + numericUpDownFPS.Location = new Point(312, 66); + numericUpDownFPS.Margin = new Padding(2, 2, 2, 2); numericUpDownFPS.Maximum = new decimal(new int[] { 1000, 0, 0, 0 }); numericUpDownFPS.Minimum = new decimal(new int[] { 20, 0, 0, 0 }); numericUpDownFPS.Name = "numericUpDownFPS"; - numericUpDownFPS.Size = new Size(123, 27); + numericUpDownFPS.Size = new Size(65, 19); numericUpDownFPS.TabIndex = 64; numericUpDownFPS.TextAlign = HorizontalAlignment.Center; numericUpDownFPS.Value = new decimal(new int[] { 60, 0, 0, 0 }); @@ -167,81 +172,80 @@ private void InitializeComponent() // sliderMinTDP // sliderMinTDP.AccessibleName = "DPI Slider"; - sliderMinTDP.Location = new Point(200, 155); + sliderMinTDP.Location = new Point(140, 93); + sliderMinTDP.Margin = new Padding(2, 2, 2, 2); sliderMinTDP.Max = 200; sliderMinTDP.Min = 5; sliderMinTDP.Name = "sliderMinTDP"; - sliderMinTDP.Size = new Size(291, 33); + sliderMinTDP.Size = new Size(204, 20); sliderMinTDP.Step = 1; sliderMinTDP.TabIndex = 63; sliderMinTDP.TabStop = false; - sliderMinTDP.Text = "sliderBattery"; + sliderMinTDP.Text = "Min TDP Slider"; sliderMinTDP.Value = 0; // // labelMaxTDPText // - labelMaxTDPText.Location = new Point(7, 195); - labelMaxTDPText.Margin = new Padding(6, 0, 6, 0); + labelMaxTDPText.Location = new Point(5, 117); + labelMaxTDPText.Margin = new Padding(4, 0, 4, 0); labelMaxTDPText.Name = "labelMaxTDPText"; - labelMaxTDPText.Size = new Size(184, 37); + labelMaxTDPText.Size = new Size(129, 22); labelMaxTDPText.TabIndex = 61; labelMaxTDPText.Text = "Max TDP:"; labelMaxTDPText.TextAlign = ContentAlignment.MiddleLeft; // // labeMinTDPText // - labeMinTDPText.Location = new Point(7, 152); - labeMinTDPText.Margin = new Padding(6, 0, 6, 0); + labeMinTDPText.Location = new Point(5, 91); + labeMinTDPText.Margin = new Padding(4, 0, 4, 0); labeMinTDPText.Name = "labeMinTDPText"; - labeMinTDPText.Size = new Size(184, 37); + labeMinTDPText.Size = new Size(129, 22); labeMinTDPText.TabIndex = 62; labeMinTDPText.Text = "Min TDP:"; labeMinTDPText.TextAlign = ContentAlignment.MiddleLeft; // // labelTargetFPS // - labelTargetFPS.Location = new Point(7, 105); - labelTargetFPS.Margin = new Padding(6, 0, 6, 0); + labelTargetFPS.Location = new Point(5, 63); + labelTargetFPS.Margin = new Padding(4, 0, 4, 0); labelTargetFPS.Name = "labelTargetFPS"; - labelTargetFPS.Size = new Size(184, 37); + labelTargetFPS.Size = new Size(129, 22); labelTargetFPS.TabIndex = 60; labelTargetFPS.Text = "Target FPS:"; labelTargetFPS.TextAlign = ContentAlignment.MiddleLeft; // // textBoxTitle // - textBoxTitle.Location = new Point(200, 60); - textBoxTitle.Margin = new Padding(4, 5, 4, 5); + textBoxTitle.Location = new Point(140, 36); textBoxTitle.Name = "textBoxTitle"; - textBoxTitle.Size = new Size(337, 31); + textBoxTitle.Size = new Size(237, 23); textBoxTitle.TabIndex = 59; // // textBoxProcessName // - textBoxProcessName.Location = new Point(200, 10); - textBoxProcessName.Margin = new Padding(4, 5, 4, 5); + textBoxProcessName.Location = new Point(140, 6); textBoxProcessName.Name = "textBoxProcessName"; textBoxProcessName.ReadOnly = true; - textBoxProcessName.Size = new Size(337, 31); + textBoxProcessName.Size = new Size(237, 23); textBoxProcessName.TabIndex = 58; textBoxProcessName.WordWrap = false; // // labelFPSSource // - labelFPSSource.Location = new Point(6, 62); - labelFPSSource.Margin = new Padding(6, 0, 6, 0); + labelFPSSource.Location = new Point(4, 37); + labelFPSSource.Margin = new Padding(4, 0, 4, 0); labelFPSSource.Name = "labelFPSSource"; - labelFPSSource.Size = new Size(184, 37); + labelFPSSource.Size = new Size(129, 22); labelFPSSource.TabIndex = 57; labelFPSSource.Text = "Name:"; labelFPSSource.TextAlign = ContentAlignment.MiddleLeft; // // labelLimiter // - labelLimiter.Location = new Point(6, 12); - labelLimiter.Margin = new Padding(6, 0, 6, 0); + labelLimiter.Location = new Point(4, 7); + labelLimiter.Margin = new Padding(4, 0, 4, 0); labelLimiter.Name = "labelLimiter"; - labelLimiter.Size = new Size(184, 37); + labelLimiter.Size = new Size(129, 22); labelLimiter.TabIndex = 47; labelLimiter.Text = "Process"; labelLimiter.TextAlign = ContentAlignment.MiddleLeft; @@ -256,11 +260,11 @@ private void InitializeComponent() buttonSave.BorderRadius = 2; buttonSave.FlatStyle = FlatStyle.Flat; buttonSave.ForeColor = SystemColors.ControlText; - buttonSave.Location = new Point(399, 303); - buttonSave.Margin = new Padding(3, 7, 3, 7); + buttonSave.Location = new Point(279, 182); + buttonSave.Margin = new Padding(2, 4, 2, 4); buttonSave.Name = "buttonSave"; buttonSave.Secondary = false; - buttonSave.Size = new Size(147, 42); + buttonSave.Size = new Size(103, 25); buttonSave.TabIndex = 61; buttonSave.Text = "Save"; buttonSave.UseVisualStyleBackColor = false; @@ -275,26 +279,40 @@ private void InitializeComponent() buttonDelete.BorderRadius = 2; buttonDelete.FlatStyle = FlatStyle.Flat; buttonDelete.ForeColor = SystemColors.ControlText; - buttonDelete.Location = new Point(6, 303); - buttonDelete.Margin = new Padding(3, 7, 3, 7); + buttonDelete.Location = new Point(4, 182); + buttonDelete.Margin = new Padding(2, 4, 2, 4); buttonDelete.Name = "buttonDelete"; buttonDelete.Secondary = false; - buttonDelete.Size = new Size(147, 42); + buttonDelete.Size = new Size(103, 25); buttonDelete.TabIndex = 62; buttonDelete.Text = "Delete"; buttonDelete.UseVisualStyleBackColor = false; // + // sliderFPS + // + sliderFPS.AccessibleName = "DPI Slider"; + sliderFPS.Location = new Point(140, 66); + sliderFPS.Margin = new Padding(2); + sliderFPS.Max = 1000; + sliderFPS.Min = 20; + sliderFPS.Name = "sliderFPS"; + sliderFPS.Size = new Size(168, 20); + sliderFPS.Step = 1; + sliderFPS.TabIndex = 68; + sliderFPS.TabStop = false; + sliderFPS.Text = "FPS Slider"; + sliderFPS.Value = 60; + // // AutoTDPGameProfileUI // - AutoScaleDimensions = new SizeF(10F, 25F); + AutoScaleDimensions = new SizeF(7F, 15F); AutoScaleMode = AutoScaleMode.Font; - ClientSize = new Size(551, 362); + ClientSize = new Size(386, 217); Controls.Add(buttonDelete); Controls.Add(buttonSave); Controls.Add(panelGameSettings); Controls.Add(panelPerformanceHeader); FormBorderStyle = FormBorderStyle.FixedSingle; - Margin = new Padding(4, 5, 4, 5); MaximizeBox = false; MinimizeBox = false; Name = "AutoTDPGameProfileUI"; @@ -332,5 +350,6 @@ private void InitializeComponent() private Label labelMinTDP; private Label labelMaxTDP; private UI.RButton buttonDelete; + private UI.Slider sliderFPS; } } \ No newline at end of file diff --git a/app/AutoTDP/AutoTDPGameProfileUI.cs b/app/AutoTDP/AutoTDPGameProfileUI.cs index 9f4369639..cc46eb840 100644 --- a/app/AutoTDP/AutoTDPGameProfileUI.cs +++ b/app/AutoTDP/AutoTDPGameProfileUI.cs @@ -18,6 +18,9 @@ public AutoTDPGameProfileUI(GameProfile profile, AutoTDPUI parent) buttonSave.Click += ButtonSave_Click; buttonDelete.Click += ButtonDelete_Click; + sliderFPS.ValueChanged += SliderFPS_ValueChanged; + numericUpDownFPS.ValueChanged += NumericUpDownFPS_ValueChanged; + InitTheme(); @@ -26,6 +29,16 @@ public AutoTDPGameProfileUI(GameProfile profile, AutoTDPUI parent) VisualizeGameProfile(); } + private void NumericUpDownFPS_ValueChanged(object? sender, EventArgs e) + { + sliderFPS.Value = (int)numericUpDownFPS.Value; + } + + private void SliderFPS_ValueChanged(object? sender, EventArgs e) + { + numericUpDownFPS.Value = sliderFPS.Value; + } + private void ButtonDelete_Click(object? sender, EventArgs e) { AutoTDPUI.DeleteGameProfile(GameProfile); From 29813ef98fa63dc7d28580152f544a5c9365e509 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Thu, 7 Mar 2024 13:52:23 +0100 Subject: [PATCH 57/76] Respect the "TopMost" setting from GHelper for the AutoTDP windows. --- app/AutoTDP/AutoTDPUI.cs | 2 ++ app/Settings.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/app/AutoTDP/AutoTDPUI.cs b/app/AutoTDP/AutoTDPUI.cs index 0874f15aa..1c24b2789 100644 --- a/app/AutoTDP/AutoTDPUI.cs +++ b/app/AutoTDP/AutoTDPUI.cs @@ -104,6 +104,7 @@ private void ButtonAddGame_Click(object? sender, EventArgs e) profileUI = new AutoTDPGameProfileUI(p, this); profileUI.FormClosed += ProfileUI_FormClosed; + profileUI.TopMost = AppConfig.Is("topmost") || this.TopMost; profileUI.ShowDialog(this); } @@ -201,6 +202,7 @@ private void Bt_Click(object? sender, EventArgs e) GameProfile gp = (GameProfile)((RButton)sender).Tag; profileUI = new AutoTDPGameProfileUI(gp, this); profileUI.FormClosed += ProfileUI_FormClosed; + profileUI.TopMost = AppConfig.Is("topmost") || this.TopMost; profileUI.ShowDialog(this); } diff --git a/app/Settings.cs b/app/Settings.cs index 9a8e1a2a4..822d45fac 100644 --- a/app/Settings.cs +++ b/app/Settings.cs @@ -287,6 +287,8 @@ private void ButtonAutoTDP_Click(object? sender, EventArgs e) autoTdpUi = new AutoTDPUI(); autoTdpUi.FormClosed += AutoTdpUi_FormClosed; autoTdpUi.Disposed += AutoTdpUi_Disposed; + autoTdpUi.TopMost = AppConfig.Is("topmost"); + if (!autoTdpUi.IsDisposed) { autoTdpUi.Show(); From 09c0367ed99d831552d88115146cc0188ed89859 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Fri, 8 Mar 2024 12:10:34 +0100 Subject: [PATCH 58/76] Pretty printing for game profiles to be better readable by humans --- app/AutoTDP/AutoTDPService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 63bec391d..abdbcfc92 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -233,7 +233,7 @@ public void InitLimiter() public void SaveGameProfiles() { - string json = JsonSerializer.Serialize(GameProfiles); + string json = JsonSerializer.Serialize(GameProfiles, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(GameProfileFile, json); } From 937d3293a4fa17b59b39c3a5825e09b11df72b67 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sat, 9 Mar 2024 14:35:04 +0100 Subject: [PATCH 59/76] Extracted Intel CPU controls. This way it can be used for other means as well. --- .../PowerLimiter/IntelMSRPowerLimiter.cs | 145 +----- app/Intel/IntelCoreControl.cs | 457 ++++++++++++++++++ 2 files changed, 469 insertions(+), 133 deletions(-) create mode 100644 app/Intel/IntelCoreControl.cs diff --git a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs index f5928c5a1..48d4d40bc 100644 --- a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs @@ -1,4 +1,5 @@ using GHelper.Helpers; +using GHelper.Intel; using Ryzen; namespace GHelper.AutoTDP.PowerLimiter @@ -6,30 +7,13 @@ namespace GHelper.AutoTDP.PowerLimiter internal class IntelMSRPowerLimiter : IPowerLimiter { - public static readonly uint MSR_PKG_POWER_LIMIT = 0x610; - public static readonly uint MSR_RAPL_POWER_UNIT = 0x606; - private Ols ols; - - private static bool DRIVER_LOADED = false; - - private uint DefaultEax = 0; // Set on first reading - private uint DefaultEdx = 0; - - //Lower 14 bits are the power limits - private uint PL1_MASK = 0x3FFF; - private uint PL2_MASK = 0x3FFF; - - //The power unit factor (Default is 0.125 for most Intel CPUs). - private double PowerUnit = 0x0; + uint backupPl1 = 0; + uint backupPl2 = 0; public IntelMSRPowerLimiter() { - ols = new Ols(); - ols.InitializeOls(); - DRIVER_LOADED = true; - ReadPowerUnit(); - Logger.WriteLine("[AutoTDPService] Read MSR_RAPL_POWER_UNIT: " + PowerUnit); + IntelCoreControl.LogCurrentState(); } public int GetMinInterval() @@ -44,21 +28,11 @@ public void Prepare() public static bool IsAvailable() { - if (!ProcessHelper.IsUserAdministrator()) - { - return false; - } - - if (RyzenControl.IsAMD()) + if (!ProcessHelper.IsUserAdministrator() || !IntelCoreControl.IsIntel()) { return false; } - if (DRIVER_LOADED) - { - return true; - } - Ols o = new Ols(); o.InitializeOls(); @@ -72,136 +46,41 @@ public static bool IsAvailable() return true; } - LogOLSState(err); - return false; } - private static void LogOLSState(uint err) - { - switch (err) - { - case (uint)Ols.OlsDllStatus.OLS_DLL_NO_ERROR: - return; - case (uint)Ols.OlsDllStatus.OLS_DLL_DRIVER_NOT_LOADED: - Logger.WriteLine("[AutoTDPService] Intel MSR Error: OLS_DRIVER_NOT_LOADED"); - break; - case (uint)Ols.OlsDllStatus.OLS_DLL_UNSUPPORTED_PLATFORM: - Logger.WriteLine("[AutoTDPService] Intel MSR Error: OLS_DLL_UNSUPPORTED_PLATFORM"); - break; - case (uint)Ols.OlsDllStatus.OLS_DLL_DRIVER_NOT_FOUND: - Logger.WriteLine("[AutoTDPService] Intel MSR Error: OLS_DLL_DRIVER_NOT_FOUND"); - break; - case (uint)Ols.OlsDllStatus.OLS_DLL_DRIVER_UNLOADED: - Logger.WriteLine("[AutoTDPService] Intel MSR Error: OLS_DLL_DRIVER_UNLOADED"); - break; - case (uint)Ols.OlsDllStatus.OLS_DLL_DRIVER_NOT_LOADED_ON_NETWORK: - Logger.WriteLine("[AutoTDPService] Intel MSR Error: OLS_DLL_DRIVER_NOT_LOADED_ON_NETWORK"); - break; - case (uint)Ols.OlsDllStatus.OLS_DLL_UNKNOWN_ERROR: - Logger.WriteLine("[AutoTDPService] Intel MSR Error: OLS_DLL_UNKNOWN_ERROR"); - break; - } - } - public void SavePowerLimits() { - DefaultEax = 0; - DefaultEdx = 0; - - if (ols.Rdmsr(MSR_PKG_POWER_LIMIT, ref DefaultEax, ref DefaultEdx) == 0) - { - Logger.WriteLine("[AutoTDPService] Failed to Read MSR_PKG_POWER_LIMIT"); - LogOLSState(ols.GetDllStatus()); - } + backupPl1 = IntelCoreControl.GetPL1(); + backupPl2 = IntelCoreControl.GetPL2(); } - public void ReadPowerUnit() - { - uint eax = 0; - uint edx = 0; - - if (ols.Rdmsr(MSR_RAPL_POWER_UNIT, ref eax, ref edx) == 0) - { - Logger.WriteLine("[AutoTDPService] Failed to Read MSR_RAPL_POWER_UNIT"); - LogOLSState(ols.GetDllStatus()); - } - - - uint pwr = eax & 0x03; - - PowerUnit = 1 / Math.Pow(2, pwr); - } public void SetCPUPowerLimit(double watts) { - uint eax = 0; - uint edx = 0; - - - if (ols.Rdmsr(MSR_PKG_POWER_LIMIT, ref eax, ref edx) == 0) - { - Logger.WriteLine("[AutoTDPService] Failed to Read MSR_PKG_POWER_LIMIT"); - LogOLSState(ols.GetDllStatus()); - } - - //Power limit in RAPL units - uint wattsRapl = (uint)(watts / PowerUnit); - - //Set limits for both PL1 and PL2 - uint eaxFilterd = eax & ~PL1_MASK; - uint edxFilterd = edx & ~PL2_MASK; - - eaxFilterd |= wattsRapl; - edxFilterd |= wattsRapl; - - //Enable clamping - eaxFilterd |= 0x8000; - edxFilterd |= 0x8000; - - if (ols.Wrmsr(0x610, eaxFilterd, edxFilterd) == 0) - { - Logger.WriteLine("[AutoTDPService] Failed to Write MSR_PKG_POWER_LIMIT"); - LogOLSState(ols.GetDllStatus()); - } + IntelCoreControl.SetPowerLimits((uint)Math.Ceiling(watts), (uint)Math.Ceiling(watts), clampPl1: true, clampPl2: true); } public int GetCPUPowerLimit() { - uint eax = 0; - uint edx = 0; - - if (ols.Rdmsr(MSR_PKG_POWER_LIMIT, ref eax, ref edx) == 0) - { - Logger.WriteLine("[AutoTDPService] Failed to Read MSR_PKG_POWER_LIMIT"); - LogOLSState(ols.GetDllStatus()); - } - - uint pl1 = eax & PL1_MASK; - uint pl2 = edx & PL2_MASK; - - - Logger.WriteLine("[AutoTDPService] Read Power Limit - PL1: " + pl1 * PowerUnit + "W, PL2: " + pl2 * PowerUnit + "W"); + Logger.WriteLine("[AutoTDPService] Read Power Limit - PL1: " + IntelCoreControl.GetPL1() + "W, PL2: " + IntelCoreControl.GetPL2() + "W"); - return (int)(pl1 * PowerUnit); + return (int)IntelCoreControl.GetPL1(); } public void ResetPowerLimits() { - if (DefaultEax == 0) + if (backupPl1 == 0) { return; } - ols.Wrmsr(MSR_PKG_POWER_LIMIT, DefaultEax, DefaultEdx); + IntelCoreControl.SetPowerLimits(backupPl1, backupPl2); } public void Dispose() { - ols.DeinitializeOls(); - ols.Dispose(); - DRIVER_LOADED = true; } } } diff --git a/app/Intel/IntelCoreControl.cs b/app/Intel/IntelCoreControl.cs new file mode 100644 index 000000000..8ba966fce --- /dev/null +++ b/app/Intel/IntelCoreControl.cs @@ -0,0 +1,457 @@ +using GHelper.Helpers; +using NvAPIWrapper.Native.GPU; +using NvAPIWrapper.Native.GPU.Structures; +using Ryzen; +using System; + +namespace GHelper.Intel +{ + internal class IntelCoreControl + { + public static readonly uint MSR_PKG_POWER_LIMIT = 0x610; + public static readonly uint MSR_RAPL_POWER_UNIT = 0x606; + + public static readonly uint INTEL_PACKAGE_RAPL_LIMIT_0_0_0_MCHBAR_PCU = 0x59a0; + + + //Lower 14 bits are the power limits + private static readonly uint PL1_MASK = 0x3FFF; + private static readonly uint PL2_MASK = 0x3FFF; + + private static double POWER_UNIT = 0; + private static double TIME_UNIT = 0; + + public static bool IsIntel() + { + return !RyzenControl.IsAMD(); // There is no GHeloper supported ASUS laptop that has a CPU which is neither AMD nor Intel. Therefore, no need to re-invent the wheel. + } + + public static void Initialize() + { + if (!ProcessHelper.IsUserAdministrator() || RyzenControl.IsAMD()) + { + //Only for Admins and not for AMD CPUs. + return; + } + + if (POWER_UNIT > 0 && TIME_UNIT > 0) + { + return; + } + + Ols ols = new Ols(); + ols.InitializeOls(); + + POWER_UNIT = GetPowerRAPLUnit(ols); + TIME_UNIT = GetTimeUnit(ols); + + ols.DeinitializeOls(); + ols.Dispose(); + } + + private static void LogOLSState(uint err) + { + switch (err) + { + case (uint)Ols.OlsDllStatus.OLS_DLL_NO_ERROR: + return; + case (uint)Ols.OlsDllStatus.OLS_DLL_DRIVER_NOT_LOADED: + Logger.WriteLine("[IntelCoreControl] Intel MSR Error: OLS_DRIVER_NOT_LOADED"); + break; + case (uint)Ols.OlsDllStatus.OLS_DLL_UNSUPPORTED_PLATFORM: + Logger.WriteLine("[IntelCoreControl] Intel MSR Error: OLS_DLL_UNSUPPORTED_PLATFORM"); + break; + case (uint)Ols.OlsDllStatus.OLS_DLL_DRIVER_NOT_FOUND: + Logger.WriteLine("[IntelCoreControl] Intel MSR Error: OLS_DLL_DRIVER_NOT_FOUND"); + break; + case (uint)Ols.OlsDllStatus.OLS_DLL_DRIVER_UNLOADED: + Logger.WriteLine("[IntelCoreControl] Intel MSR Error: OLS_DLL_DRIVER_UNLOADED"); + break; + case (uint)Ols.OlsDllStatus.OLS_DLL_DRIVER_NOT_LOADED_ON_NETWORK: + Logger.WriteLine("[IntelCoreControl] Intel MSR Error: OLS_DLL_DRIVER_NOT_LOADED_ON_NETWORK"); + break; + case (uint)Ols.OlsDllStatus.OLS_DLL_UNKNOWN_ERROR: + Logger.WriteLine("[IntelCoreControl] Intel MSR Error: OLS_DLL_UNKNOWN_ERROR"); + break; + } + } + + /* + * Due to Winring0 shipped with GHelper not implementing readphysicalmemory, we cannot fix the MMIO. + * Therefore, Intel Dynamic Tuning can lower the power limits below what we configure. + * However, it cannot set the limits higher than we set them in the MSRs. + * + * This function is incomplete due to testing not possible + * + * See: https://github.com/horshack-dpreview/setPL/blob/master/setPL.sh for how to erase the MMIO to let MSR take full control over power limits. + public static unsafe void MMIOFix() + { + + Ols ols = new Ols(); + ols.InitializeOls(); + + uint mchbar = ols.ReadPciConfigDword(0x00, 0x48); + + bool enabled = (((mchbar & 0x1) != 0)); + + //Remove enabled bit to get phyiscal address + mchbar = (uint)((mchbar & ~1)); + + + uint raplLimitAddr = ((mchbar + INTEL_PACKAGE_RAPL_LIMIT_0_0_0_MCHBAR_PCU)); + + byte[] buffer = new byte[4]; + + fixed (byte* pBuf = buffer) + { + ols.ReadPhysicalMemory(raplLimitAddr + 0, pBuf, 4, 1); + } + + byte[] buffer2 = new byte[4]; + + fixed (byte* pBuf = buffer2) + { + ols.ReadPhysicalMemory(raplLimitAddr + 4, pBuf, 4, 1); + } + + + ols.DeinitializeOls(); + ols.Dispose(); + + } + */ + + public static void LogCurrentState() + { + uint pl1 = GetPL1(); + uint pl2 = GetPL2(); + + bool msrLocked = IsMSRLocked(); + bool pl1Clamped = IsPL1Clamped(); + bool pl2Clamped = IsPL2Clamped(); + + double TAU = GetTAU(); + + + Logger.WriteLine("[IntelCoreControl] MSR Power State: Pl1: " + pl1 + "W " + (pl1Clamped ? "(Clamped)" : "(Unclamped)") + ", PL2: " + pl2 + "W" + (pl2Clamped ? "(Clamped)" : "(Unclamped)") + ", TAU: " + TAU + "s. MSR State: " + (msrLocked ? "Locked" : "Unlocked")); + + } + + private static double GetPowerRAPLUnit(Ols ols) + { + uint eax = 0; + uint edx = 0; + + if (ols.Rdmsr(MSR_RAPL_POWER_UNIT, ref eax, ref edx) == 0) + { + Logger.WriteLine("[IntelCoreControl] Failed to Read MSR_RAPL_POWER_UNIT"); + LogOLSState(ols.GetDllStatus()); + return -1; + } + + //Bits 0:3 + uint pwr = eax & 0x3; + + return 1 / Math.Pow(2, pwr); + } + + private static double GetTimeUnit(Ols ols) + { + uint eax = 0; + uint edx = 0; + + if (ols.Rdmsr(MSR_RAPL_POWER_UNIT, ref eax, ref edx) == 0) + { + Logger.WriteLine("[IntelCoreControl] Failed to Read MSR_RAPL_POWER_UNIT"); + LogOLSState(ols.GetDllStatus()); + return -1; + } + + //Bits 16:19 + uint pwr = (eax & 0xF0000) >> 16; + + return 1 / Math.Pow(2, pwr); + } + + public static List AllowedTimeUnits() + { + Initialize(); + + if (TIME_UNIT <= 0) + { + //Timeunit was invalid + return new List(); + } + + List lst = new List(); + //y has 5 bits, z has 2 bits. + for (int y = 0; y <= 31; ++y) + { + for (int z = 0; z <= 3; ++z) + { + //Formula as specified by Intel + double val = Math.Pow(2, y) * (1.0 + (z / 4.0)) * TIME_UNIT; + lst.Add(val); + } + } + + return lst; + } + + // Forumla 2^Y * (1.0 + Z/4.0) * Time_Unit + // See intel specification + // Y has 5 bits, Z has 2 bits. + private static uint ComputeTimeWindowRAPL(double seconds) + { + int t = (int)(seconds / TIME_UNIT); + + double r = Math.Log2(t); + + int y = (int)r; + + if (y < 0 || y > 31) + { + //Value is out of range for bit range + } + + int exp = (int)Math.Pow(2, y); + + double fac = t / (double)exp; + + int z = (int)((fac - 1) * 4); + + if (z < 0 || z > 3) + { + //Value is out of range for bit range + } + + uint val = 0; + + //y are the first 5 bits, then z the next 2 bits for a total of 7 bits + val |= (uint)y; + val |= (uint)(z << 5); + + return val; + } + + public static bool ReadPowerMSR(ref uint eax, ref uint edx) + { + Ols ols = new Ols(); + ols.InitializeOls(); + + bool result = ols.Rdmsr(MSR_PKG_POWER_LIMIT, ref eax, ref edx) == 1; + + ols.DeinitializeOls(); + ols.Dispose(); + + return result; + } + + public static bool IsMSRLocked() + { + uint eax = 0; + uint edx = 0; + + if (!ReadPowerMSR(ref eax, ref edx)) + { + Logger.WriteLine("[IntelCoreControl] Failed to Read MSR_PKG_POWER_LIMIT"); + return true; + } + + //Bit 31 of edx marks whether the MSR is locked and read only or whether it is writeable + return (edx >> 31) != 0; + } + + public static bool IsPL1Clamped() + { + uint eax = 0; + uint edx = 0; + + if (!ReadPowerMSR(ref eax, ref edx)) + { + Logger.WriteLine("[IntelCoreControl] Failed to Read MSR_PKG_POWER_LIMIT"); + return true; + } + + //Bit 16 of eax + return (eax >> 16) != 0; + } + + public static bool IsPL2Clamped() + { + uint eax = 0; + uint edx = 0; + + if (!ReadPowerMSR(ref eax, ref edx)) + { + Logger.WriteLine("[IntelCoreControl] Failed to Read MSR_PKG_POWER_LIMIT"); + return true; + } + + //Bit 16 of edx + return (edx >> 16) != 0; + } + + public static uint GetPL2() + { + Initialize(); + + uint eax = 0; + uint edx = 0; + + if (!ReadPowerMSR(ref eax, ref edx)) + { + Logger.WriteLine("[IntelCoreControl] Failed to Read MSR_PKG_POWER_LIMIT"); + return 0; + } + + uint pl2 = edx & PL2_MASK; + + return (uint)(pl2 * POWER_UNIT); + } + + public static uint GetPL1() + { + Initialize(); + + uint eax = 0; + uint edx = 0; + + if (!ReadPowerMSR(ref eax, ref edx)) + { + Logger.WriteLine("[IntelCoreControl] Failed to Read MSR_PKG_POWER_LIMIT"); + return 0; + } + + uint pl1 = eax & PL1_MASK; + + return (uint)(pl1 * POWER_UNIT); + } + + public static double GetTAU() + { + Initialize(); + + uint eax = 0; + uint edx = 0; + + if (!ReadPowerMSR(ref eax, ref edx)) + { + Logger.WriteLine("[IntelCoreControl] Failed to Read MSR_PKG_POWER_LIMIT"); + return 0; + } + + uint y = (eax & 0x00000000003E0000) >> 17; + uint z = (eax & 0x0000000000C00000) >> 22; + + + double val = Math.Pow(2, y) * (1.0 + (z / 4.0)) * TIME_UNIT; + + return val; + } + + /// + /// Sets the power limts for the CPU via MSR + /// + /// Longterm power limit in watts + /// Shortterm power limit in watts + /// PL2 turbo time limit in seconds. See: AllowedTimeUnits() for possible TAU values + /// Allow going below OS-requested P/T state setting during time window of PL1 + /// Allow going below OS-requested P/T state setting during time window of PL2 + /// true if changes were applied successfully + public static bool SetPowerLimits(uint pl1, uint pl2, double tau = 0, bool clampPl1 = false, bool clampPl2 = false) + { + Initialize(); + + if (POWER_UNIT <= 0 || TIME_UNIT <= 0) + { + Logger.WriteLine("[IntelCoreControl] POWER_UNIT or TIME_UNIT could not be initialized."); + return false; + } + + bool status = false; + + Ols ols = new Ols(); + ols.InitializeOls(); + + + uint eax = 0; + uint edx = 0; + + + if (ols.Rdmsr(MSR_PKG_POWER_LIMIT, ref eax, ref edx) == 0) + { + Logger.WriteLine("[IntelCoreControl] Failed to Read MSR_PKG_POWER_LIMIT"); + LogOLSState(ols.GetDllStatus()); + ols.DeinitializeOls(); + ols.Dispose(); + return false; + } + + //Bit 31 of edx marks whether the MSR is locked and read only or whether it is writeable + bool msrLocked = (edx >> 31) != 0; + + if (msrLocked) + { + Logger.WriteLine("[IntelCoreControl] MSR_PKG_POWER_LIMIT is locked. Cannot change power limits"); + ols.DeinitializeOls(); + ols.Dispose(); + return false; + } + + + uint pl1Rapl = (uint)(pl1 / POWER_UNIT); + uint pl2Rapl = (uint)(pl2 / POWER_UNIT); + + //Set power limits + eax = (eax & ~PL1_MASK) | pl1Rapl; + edx = (edx & ~PL1_MASK) | pl2Rapl; + + + //Set clamp for PL1 + if (clampPl1) + eax |= 0x8000; + else + eax = eax & ~((uint)1 << 16); + + + //Set clamp for PL2 + if (clampPl2) + edx |= 0x8000; + else + edx = edx & ~((uint)1 << 16); + + + if (tau > 0) + { + //Set power limit time window for PL2 + uint tauBits = ComputeTimeWindowRAPL(tau); + + tauBits = tauBits << 17; + + //Clear old tau bits + eax = (uint)(eax & (0xFFFFFFFFFF01FFFF)); + + //Apply new tau bits + eax |= tauBits; + } + + + if (ols.Wrmsr(0x610, eax, edx) == 0) + { + Logger.WriteLine("[IntelCoreControl] Failed to Write MSR_PKG_POWER_LIMIT"); + LogOLSState(ols.GetDllStatus()); + } + else + { + status = true; + } + + ols.DeinitializeOls(); + ols.Dispose(); + + return status; + + } + } +} From 4d54b7e7480af9c09fb80b2ce4f680d8da0a3c38 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sat, 9 Mar 2024 22:37:04 +0100 Subject: [PATCH 60/76] Moved autoTDP to CPU section --- app/Settings.Designer.cs | 40 ++++++++++++++++++++-------------------- app/Settings.cs | 2 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/app/Settings.Designer.cs b/app/Settings.Designer.cs index 416b560fc..95f97f966 100644 --- a/app/Settings.Designer.cs +++ b/app/Settings.Designer.cs @@ -67,7 +67,7 @@ private void InitializeComponent() labelCPUFan = new Label(); panelGPU = new Panel(); labelTipGPU = new Label(); - tableAdditionalGPUFeature = new TableLayoutPanel(); + tableAdditionalCPUFeature = new TableLayoutPanel(); buttonAutoTDP = new RButton(); tableAMD = new TableLayoutPanel(); buttonOverlay = new RButton(); @@ -149,7 +149,7 @@ private void InitializeComponent() panelCPUTitle.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)picturePerf).BeginInit(); panelGPU.SuspendLayout(); - tableAdditionalGPUFeature.SuspendLayout(); + tableAdditionalCPUFeature.SuspendLayout(); tableAMD.SuspendLayout(); tableGPU.SuspendLayout(); panelGPUTitle.SuspendLayout(); @@ -554,6 +554,7 @@ private void InitializeComponent() panelPerformance.AccessibleRole = AccessibleRole.Grouping; panelPerformance.AutoSize = true; panelPerformance.AutoSizeMode = AutoSizeMode.GrowAndShrink; + panelPerformance.Controls.Add(tableAdditionalCPUFeature); panelPerformance.Controls.Add(tablePerf); panelPerformance.Controls.Add(panelCPUTitle); panelPerformance.Dock = DockStyle.Top; @@ -731,7 +732,6 @@ private void InitializeComponent() panelGPU.AutoSize = true; panelGPU.AutoSizeMode = AutoSizeMode.GrowAndShrink; panelGPU.Controls.Add(labelTipGPU); - panelGPU.Controls.Add(tableAdditionalGPUFeature); panelGPU.Controls.Add(tableAMD); panelGPU.Controls.Add(tableGPU); panelGPU.Controls.Add(panelGPUTitle); @@ -756,21 +756,21 @@ private void InitializeComponent() // // tableAdditionalGPUFeature // - tableAdditionalGPUFeature.AutoSize = true; - tableAdditionalGPUFeature.AutoSizeMode = AutoSizeMode.GrowAndShrink; - tableAdditionalGPUFeature.ColumnCount = 3; - tableAdditionalGPUFeature.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33.3333321F)); - tableAdditionalGPUFeature.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33.3333321F)); - tableAdditionalGPUFeature.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33.3333321F)); - tableAdditionalGPUFeature.Controls.Add(buttonAutoTDP, 0, 0); - tableAdditionalGPUFeature.Dock = DockStyle.Top; - tableAdditionalGPUFeature.Location = new Point(10, 198); - tableAdditionalGPUFeature.Margin = new Padding(2, 1, 2, 1); - tableAdditionalGPUFeature.Name = "tableAdditionalGPUFeature"; - tableAdditionalGPUFeature.RowCount = 1; - tableAdditionalGPUFeature.RowStyles.Add(new RowStyle(SizeType.Absolute, 60F)); - tableAdditionalGPUFeature.Size = new Size(392, 60); - tableAdditionalGPUFeature.TabIndex = 25; + tableAdditionalCPUFeature.AutoSize = true; + tableAdditionalCPUFeature.AutoSizeMode = AutoSizeMode.GrowAndShrink; + tableAdditionalCPUFeature.ColumnCount = 3; + tableAdditionalCPUFeature.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33.3333321F)); + tableAdditionalCPUFeature.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33.3333321F)); + tableAdditionalCPUFeature.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33.3333321F)); + tableAdditionalCPUFeature.Controls.Add(buttonAutoTDP, 0, 0); + tableAdditionalCPUFeature.Dock = DockStyle.Top; + tableAdditionalCPUFeature.Location = new Point(10, 198); + tableAdditionalCPUFeature.Margin = new Padding(2, 1, 2, 1); + tableAdditionalCPUFeature.Name = "tableAdditionalGPUFeature"; + tableAdditionalCPUFeature.RowCount = 1; + tableAdditionalCPUFeature.RowStyles.Add(new RowStyle(SizeType.Absolute, 60F)); + tableAdditionalCPUFeature.Size = new Size(392, 60); + tableAdditionalCPUFeature.TabIndex = 25; // // buttonAutoTDP // @@ -1910,7 +1910,7 @@ private void InitializeComponent() ((System.ComponentModel.ISupportInitialize)picturePerf).EndInit(); panelGPU.ResumeLayout(false); panelGPU.PerformLayout(); - tableAdditionalGPUFeature.ResumeLayout(false); + tableAdditionalCPUFeature.ResumeLayout(false); tableAMD.ResumeLayout(false); tableGPU.ResumeLayout(false); panelGPUTitle.ResumeLayout(false); @@ -2054,7 +2054,7 @@ private void InitializeComponent() private Label labelGammaTitle; private CheckBox checkMatrixLid; private Panel panelMatrixAuto; - private TableLayoutPanel tableAdditionalGPUFeature; + private TableLayoutPanel tableAdditionalCPUFeature; private RButton buttonAutoTDP; private TableLayoutPanel tableVisual; private RComboBox comboVisual; diff --git a/app/Settings.cs b/app/Settings.cs index ec069eaff..31f01dd95 100644 --- a/app/Settings.cs +++ b/app/Settings.cs @@ -532,7 +532,7 @@ private void SettingsForm_VisibleChanged(object? sender, EventArgs e) Task.Run((Action)RefreshPeripheralsBattery); updateControl.CheckForUpdates(); - tableAdditionalGPUFeature.Visible = AutoTDPService.IsAvailable(); + tableAdditionalCPUFeature.Visible = AutoTDPService.IsAvailable(); } } From 27188e3197400631df360d986ee06df620ef2ba0 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sun, 10 Mar 2024 14:36:01 +0100 Subject: [PATCH 61/76] Allow selecting .dat files as game. CNC Tiberium Wars and Kanes's Wrath for example do that stuff. --- app/AutoTDP/AutoTDPUI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/AutoTDP/AutoTDPUI.cs b/app/AutoTDP/AutoTDPUI.cs index 1c24b2789..a211f80f2 100644 --- a/app/AutoTDP/AutoTDPUI.cs +++ b/app/AutoTDP/AutoTDPUI.cs @@ -76,7 +76,7 @@ private void ButtonAddGame_Click(object? sender, EventArgs e) Thread t = new Thread(() => { OpenFileDialog ofd = new OpenFileDialog(); - ofd.Filter = "Executables (*.exe)|*.exe"; + ofd.Filter = "Executables (*.exe, *.dat)|*.exe; *.dat"; if (ofd.ShowDialog() == DialogResult.OK) { From d012fdac4d38b080714cec7ca228264fd5be261f Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sun, 10 Mar 2024 14:47:15 +0100 Subject: [PATCH 62/76] Auto-sort the game table by game title. --- app/AutoTDP/AutoTDPService.cs | 5 +++++ app/AutoTDP/AutoTDPUI.cs | 2 ++ app/AutoTDP/GameProfile.cs | 7 ++++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index abdbcfc92..3705c8d87 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -238,6 +238,11 @@ public void SaveGameProfiles() File.WriteAllText(GameProfileFile, json); } + public void SortGameProfiles() + { + GameProfiles.Sort(); + } + public void LoadGameProfiles() { diff --git a/app/AutoTDP/AutoTDPUI.cs b/app/AutoTDP/AutoTDPUI.cs index a211f80f2..249c0c3b1 100644 --- a/app/AutoTDP/AutoTDPUI.cs +++ b/app/AutoTDP/AutoTDPUI.cs @@ -213,6 +213,7 @@ public void DeleteGameProfile(GameProfile gp) Program.autoTDPService.GameProfiles.Remove(gp); } + Program.autoTDPService.SortGameProfiles(); Program.autoTDPService.SaveGameProfiles(); VizualizeGameList(); } @@ -223,6 +224,7 @@ public void UpdateGameProfile(GameProfile gp) { Program.autoTDPService.GameProfiles.Add(gp); } + Program.autoTDPService.SortGameProfiles(); Program.autoTDPService.SaveGameProfiles(); VizualizeGameList(); } diff --git a/app/AutoTDP/GameProfile.cs b/app/AutoTDP/GameProfile.cs index bc7033e39..68f171ad1 100644 --- a/app/AutoTDP/GameProfile.cs +++ b/app/AutoTDP/GameProfile.cs @@ -1,7 +1,7 @@  namespace GHelper.AutoTDP { - public class GameProfile + public class GameProfile : IComparable { public string GameTitle { get; set; } public string ProcessName { get; set; } @@ -10,6 +10,11 @@ public class GameProfile public int MaxTdp { get; set; } public bool Enabled { get; set; } + public int CompareTo(GameProfile? other) + { + return GameTitle.CompareTo(other?.GameTitle); + } + public int GetTDPFPS() { return TargetFPS - 1; From 71e072bee29c39b8e7f5c8b7ad2a633e095c62b5 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sun, 10 Mar 2024 14:47:48 +0100 Subject: [PATCH 63/76] Also replace .dat when adding a new game executable --- app/AutoTDP/AutoTDPUI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/AutoTDP/AutoTDPUI.cs b/app/AutoTDP/AutoTDPUI.cs index 249c0c3b1..201db8950 100644 --- a/app/AutoTDP/AutoTDPUI.cs +++ b/app/AutoTDP/AutoTDPUI.cs @@ -96,7 +96,7 @@ private void ButtonAddGame_Click(object? sender, EventArgs e) GameProfile p = new GameProfile(); p.ProcessName = Path.GetFileName(path); - p.GameTitle = Path.GetFileName(path).Replace(".exe", ""); + p.GameTitle = Path.GetFileName(path).Replace(".exe", "").Replace(".dat", ""); p.Enabled = true; p.TargetFPS = 60; p.MaxTdp = 40; From 1799dc233c4cc903d149df2b5a415d9ab8cde37e Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sun, 10 Mar 2024 23:10:22 +0100 Subject: [PATCH 64/76] Code cleanup --- app/AutoTDP/AutoTDPService.cs | 3 +-- app/AutoTDP/AutoTDPUI.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 3705c8d87..eb8a8aaf1 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.Text.Json; +using System.Text.Json; using GHelper.AutoTDP.FramerateSource; using GHelper.AutoTDP.PowerLimiter; using Ryzen; diff --git a/app/AutoTDP/AutoTDPUI.cs b/app/AutoTDP/AutoTDPUI.cs index 201db8950..64e4800ee 100644 --- a/app/AutoTDP/AutoTDPUI.cs +++ b/app/AutoTDP/AutoTDPUI.cs @@ -202,7 +202,7 @@ private void Bt_Click(object? sender, EventArgs e) GameProfile gp = (GameProfile)((RButton)sender).Tag; profileUI = new AutoTDPGameProfileUI(gp, this); profileUI.FormClosed += ProfileUI_FormClosed; - profileUI.TopMost = AppConfig.Is("topmost") || this.TopMost; + profileUI.TopMost = AppConfig.Is("topmost") || this.TopMost; profileUI.ShowDialog(this); } From 6a54bac349dda02bcb52bac79ffce9513395e262 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sun, 10 Mar 2024 23:11:08 +0100 Subject: [PATCH 65/76] Intel MSR is fast, we can change power at 30Hz if we want to without any performance issues. --- app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs index 48d4d40bc..3acf187e9 100644 --- a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs +++ b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs @@ -18,7 +18,7 @@ public IntelMSRPowerLimiter() public int GetMinInterval() { - return 250; + return 33; } public void Prepare() From 85f06170c25d3a65a3dc9ac96f4555aa8dea77cc Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sun, 10 Mar 2024 23:12:51 +0100 Subject: [PATCH 66/76] Slower intervals mean longer time for stutter recover, but also less stutter in general. 1000ms seems to be a good tradeoff. --- app/AutoTDP/AutoTDPService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index eb8a8aaf1..15b995e0f 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -11,7 +11,7 @@ internal class AutoTDPService : IDisposable private static readonly bool LOG_AUTO_TDP = true; private static readonly int INTERVAL_MIN_CHECK = 15 * 1_000; private static readonly int INTERVAL_APP_CHECK = 5_000; - private static readonly int INTERVAL_FPS_CHECK = 33; + private static readonly int INTERVAL_FPS_CHECK = 1_000; private static readonly int INTERVAL_LOG = 1_000; private int LogCounter = 0; From de41454a8381e2c6846517afac93b94651090098 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Mon, 11 Mar 2024 20:57:35 +0100 Subject: [PATCH 67/76] Respect the enable state of the game --- app/AutoTDP/AutoTDPService.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 15b995e0f..c0bec9bb8 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -311,6 +311,10 @@ public void CheckForGame() { if (gp.ProcessName is not null && processName.EndsWith(gp.ProcessName, StringComparison.CurrentCultureIgnoreCase)) { + if (!gp.Enabled) + { + return null; + } return gp; } } From 1b034315dc123b0e69bad1b3c6bbac971087a8ec Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Mon, 11 Mar 2024 20:59:45 +0100 Subject: [PATCH 68/76] Stop autotdp on next cycle if the game profile was disabled. --- app/AutoTDP/AutoTDPService.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index c0bec9bb8..2f8c70468 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -393,6 +393,14 @@ public void StartGameHandler(GameInstance instance) while (currentGame is not null && Running) { + if (!profile.Enabled) + { + //Game was disabled during session. Stop AutoTDP + Logger.WriteLine("[AutoTDPService] Game profile disabled: " + profile.GameTitle + ". Disengaging."); + Reset(); + return; + } + double fps = framerateSouce.GetFramerate(instance); if (LOG_AUTO_TDP && LogCounter * FPSCheckInterval() > INTERVAL_LOG) From 973bc2939a860950bc3bf68dab53a03834e56522 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Mon, 11 Mar 2024 22:44:31 +0100 Subject: [PATCH 69/76] Handle case when OSD is not ready in 5s, which would otherwise crash GHelper. --- .../FramerateSource/RTSSFramerateSource.cs | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs b/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs index 1dd1267fb..1e5f6cec6 100644 --- a/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs +++ b/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs @@ -84,14 +84,22 @@ public List GetRunningGames() } List giL = new List(); - - foreach (AppEntry appEntry in OSD.GetAppEntries()) + try { - GameInstance i = new GameInstance(); - i.ProcessID = appEntry.ProcessId; - i.ProcessName = appEntry.Name; + foreach (AppEntry appEntry in OSD.GetAppEntries()) + { + GameInstance i = new GameInstance(); + i.ProcessID = appEntry.ProcessId; + i.ProcessName = appEntry.Name; - giL.Add(i); + giL.Add(i); + } + } + catch (InvalidDataException) + { + } + catch (FileNotFoundException) + { } return giL; From 0de0963ce25aceeeac32a9ec8a06f80981f5d919 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Mon, 11 Mar 2024 22:45:18 +0100 Subject: [PATCH 70/76] 500ms seems to be the sweet spot for most games. Only ignore single dips for high sample rates or it will not react to longer dips. --- app/AutoTDP/AutoTDPService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 2f8c70468..88a1a6e95 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -11,7 +11,7 @@ internal class AutoTDPService : IDisposable private static readonly bool LOG_AUTO_TDP = true; private static readonly int INTERVAL_MIN_CHECK = 15 * 1_000; private static readonly int INTERVAL_APP_CHECK = 5_000; - private static readonly int INTERVAL_FPS_CHECK = 1_000; + private static readonly int INTERVAL_FPS_CHECK = 500; private static readonly int INTERVAL_LOG = 1_000; private int LogCounter = 0; @@ -590,7 +590,7 @@ public void AdjustPowerLimit(GameProfile profile) adjustment = Math.Min(adjustment, (CurrentTDP * 0.1)); - if (GameFPSPrevious > profile.GetTDPFPS() && GameFPS < profile.GetTDPFPS()) + if (GameFPSPrevious > profile.GetTDPFPS() && GameFPS < profile.GetTDPFPS() && FPSCheckInterval() <= 250) { if (LOG_AUTO_TDP) AutoTDPLogger.WriteLine("[AutoTDPService] Single Dip, Ignore"); From cdb80b2843f5be2a549a288a56b1e097076651d4 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sun, 24 Mar 2024 12:06:14 +0100 Subject: [PATCH 71/76] Fix UI bug that could cause profiles to duplicate if they're not enabled. --- app/AutoTDP/AutoTDPService.cs | 8 ++++---- app/AutoTDP/AutoTDPUI.cs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 88a1a6e95..70d4b97ee 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -300,7 +300,7 @@ public void CheckForGame() } } - public GameProfile? ProfileForGame(String? processName) + public GameProfile? ProfileForGame(String? processName, bool checkForEnabled = true) { if (processName is null) { @@ -311,7 +311,7 @@ public void CheckForGame() { if (gp.ProcessName is not null && processName.EndsWith(gp.ProcessName, StringComparison.CurrentCultureIgnoreCase)) { - if (!gp.Enabled) + if (!gp.Enabled && checkForEnabled) { return null; } @@ -322,9 +322,9 @@ public void CheckForGame() return null; } - public bool IsGameInList(String? processName) + public bool IsGameInList(String? processName, bool checkForEnabled = true) { - return ProfileForGame(processName) is not null; + return ProfileForGame(processName, checkForEnabled) is not null; } diff --git a/app/AutoTDP/AutoTDPUI.cs b/app/AutoTDP/AutoTDPUI.cs index 64e4800ee..e17dbe677 100644 --- a/app/AutoTDP/AutoTDPUI.cs +++ b/app/AutoTDP/AutoTDPUI.cs @@ -208,7 +208,7 @@ private void Bt_Click(object? sender, EventArgs e) public void DeleteGameProfile(GameProfile gp) { - if (Program.autoTDPService.IsGameInList(gp.ProcessName)) + if (Program.autoTDPService.IsGameInList(gp.ProcessName, false)) { Program.autoTDPService.GameProfiles.Remove(gp); } @@ -220,7 +220,7 @@ public void DeleteGameProfile(GameProfile gp) public void UpdateGameProfile(GameProfile gp) { - if (!Program.autoTDPService.IsGameInList(gp.ProcessName)) + if (!Program.autoTDPService.IsGameInList(gp.ProcessName, false)) { Program.autoTDPService.GameProfiles.Add(gp); } From 64217f1b652b0179107e59bba27561179f906f96 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Thu, 28 Mar 2024 14:36:46 +0100 Subject: [PATCH 72/76] Make Check Interval configurable per game. Default is 500ms --- app/AutoTDP/AutoTDPGameProfileUI.Designer.cs | 101 ++++++++++++++----- app/AutoTDP/AutoTDPGameProfileUI.cs | 21 ++++ app/AutoTDP/AutoTDPService.cs | 32 ++++-- app/AutoTDP/AutoTDPUI.cs | 1 + app/AutoTDP/GameProfile.cs | 1 + 5 files changed, 120 insertions(+), 36 deletions(-) diff --git a/app/AutoTDP/AutoTDPGameProfileUI.Designer.cs b/app/AutoTDP/AutoTDPGameProfileUI.Designer.cs index 58315d4c6..7688fd6ba 100644 --- a/app/AutoTDP/AutoTDPGameProfileUI.Designer.cs +++ b/app/AutoTDP/AutoTDPGameProfileUI.Designer.cs @@ -33,6 +33,10 @@ private void InitializeComponent() labelSettings = new Label(); checkBoxEnabled = new CheckBox(); panelGameSettings = new Panel(); + sliderCheckInterval = new UI.Slider(); + numericCheckInterval = new NumericUpDown(); + labelCheckInterval = new Label(); + sliderFPS = new UI.Slider(); labelMinTDP = new Label(); labelMaxTDP = new Label(); sliderMaxTDP = new UI.Slider(); @@ -47,10 +51,10 @@ private void InitializeComponent() labelLimiter = new Label(); buttonSave = new UI.RButton(); buttonDelete = new UI.RButton(); - sliderFPS = new UI.Slider(); panelPerformanceHeader.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)pictureKeyboard).BeginInit(); panelGameSettings.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)numericCheckInterval).BeginInit(); ((System.ComponentModel.ISupportInitialize)numericUpDownFPS).BeginInit(); SuspendLayout(); // @@ -62,7 +66,7 @@ private void InitializeComponent() panelPerformanceHeader.Controls.Add(checkBoxEnabled); panelPerformanceHeader.Dock = DockStyle.Top; panelPerformanceHeader.Location = new Point(0, 0); - panelPerformanceHeader.Margin = new Padding(2, 2, 2, 2); + panelPerformanceHeader.Margin = new Padding(2); panelPerformanceHeader.Name = "panelPerformanceHeader"; panelPerformanceHeader.Size = new Size(386, 30); panelPerformanceHeader.TabIndex = 53; @@ -72,7 +76,7 @@ private void InitializeComponent() pictureKeyboard.BackgroundImage = Properties.Resources.icons8_automation_32; pictureKeyboard.BackgroundImageLayout = ImageLayout.Zoom; pictureKeyboard.Location = new Point(3, 8); - pictureKeyboard.Margin = new Padding(2, 2, 2, 2); + pictureKeyboard.Margin = new Padding(2); pictureKeyboard.Name = "pictureKeyboard"; pictureKeyboard.Size = new Size(16, 16); pictureKeyboard.TabIndex = 35; @@ -102,6 +106,9 @@ private void InitializeComponent() // panelGameSettings // panelGameSettings.AutoSize = true; + panelGameSettings.Controls.Add(sliderCheckInterval); + panelGameSettings.Controls.Add(numericCheckInterval); + panelGameSettings.Controls.Add(labelCheckInterval); panelGameSettings.Controls.Add(sliderFPS); panelGameSettings.Controls.Add(labelMinTDP); panelGameSettings.Controls.Add(labelMaxTDP); @@ -117,12 +124,65 @@ private void InitializeComponent() panelGameSettings.Controls.Add(labelLimiter); panelGameSettings.Dock = DockStyle.Top; panelGameSettings.Location = new Point(0, 30); - panelGameSettings.Margin = new Padding(2, 2, 2, 2); + panelGameSettings.Margin = new Padding(2); panelGameSettings.Name = "panelGameSettings"; panelGameSettings.Padding = new Padding(0, 0, 0, 7); - panelGameSettings.Size = new Size(386, 146); + panelGameSettings.Size = new Size(386, 174); panelGameSettings.TabIndex = 57; // + // sliderCheckInterval + // + sliderCheckInterval.AccessibleName = "DPI Slider"; + sliderCheckInterval.Location = new Point(140, 145); + sliderCheckInterval.Margin = new Padding(2); + sliderCheckInterval.Max = 5000; + sliderCheckInterval.Min = 16; + sliderCheckInterval.Name = "sliderCheckInterval"; + sliderCheckInterval.Size = new Size(168, 20); + sliderCheckInterval.Step = 1; + sliderCheckInterval.TabIndex = 71; + sliderCheckInterval.TabStop = false; + sliderCheckInterval.Text = "FPS Slider"; + sliderCheckInterval.Value = 500; + // + // numericCheckInterval + // + numericCheckInterval.BorderStyle = BorderStyle.None; + numericCheckInterval.Location = new Point(312, 145); + numericCheckInterval.Margin = new Padding(2); + numericCheckInterval.Maximum = new decimal(new int[] { 5000, 0, 0, 0 }); + numericCheckInterval.Minimum = new decimal(new int[] { 16, 0, 0, 0 }); + numericCheckInterval.Name = "numericCheckInterval"; + numericCheckInterval.Size = new Size(65, 19); + numericCheckInterval.TabIndex = 70; + numericCheckInterval.TextAlign = HorizontalAlignment.Center; + numericCheckInterval.Value = new decimal(new int[] { 500, 0, 0, 0 }); + // + // labelCheckInterval + // + labelCheckInterval.Location = new Point(5, 142); + labelCheckInterval.Margin = new Padding(4, 0, 4, 0); + labelCheckInterval.Name = "labelCheckInterval"; + labelCheckInterval.Size = new Size(129, 22); + labelCheckInterval.TabIndex = 69; + labelCheckInterval.Text = "Check Interval (ms):"; + labelCheckInterval.TextAlign = ContentAlignment.MiddleLeft; + // + // sliderFPS + // + sliderFPS.AccessibleName = "DPI Slider"; + sliderFPS.Location = new Point(140, 66); + sliderFPS.Margin = new Padding(2); + sliderFPS.Max = 1000; + sliderFPS.Min = 20; + sliderFPS.Name = "sliderFPS"; + sliderFPS.Size = new Size(168, 20); + sliderFPS.Step = 1; + sliderFPS.TabIndex = 68; + sliderFPS.TabStop = false; + sliderFPS.Text = "FPS Slider"; + sliderFPS.Value = 60; + // // labelMinTDP // labelMinTDP.Location = new Point(342, 91); @@ -145,7 +205,7 @@ private void InitializeComponent() // sliderMaxTDP.AccessibleName = "DPI Slider"; sliderMaxTDP.Location = new Point(140, 117); - sliderMaxTDP.Margin = new Padding(2, 2, 2, 2); + sliderMaxTDP.Margin = new Padding(2); sliderMaxTDP.Max = 200; sliderMaxTDP.Min = 5; sliderMaxTDP.Name = "sliderMaxTDP"; @@ -160,7 +220,7 @@ private void InitializeComponent() // numericUpDownFPS.BorderStyle = BorderStyle.None; numericUpDownFPS.Location = new Point(312, 66); - numericUpDownFPS.Margin = new Padding(2, 2, 2, 2); + numericUpDownFPS.Margin = new Padding(2); numericUpDownFPS.Maximum = new decimal(new int[] { 1000, 0, 0, 0 }); numericUpDownFPS.Minimum = new decimal(new int[] { 20, 0, 0, 0 }); numericUpDownFPS.Name = "numericUpDownFPS"; @@ -173,7 +233,7 @@ private void InitializeComponent() // sliderMinTDP.AccessibleName = "DPI Slider"; sliderMinTDP.Location = new Point(140, 93); - sliderMinTDP.Margin = new Padding(2, 2, 2, 2); + sliderMinTDP.Margin = new Padding(2); sliderMinTDP.Max = 200; sliderMinTDP.Min = 5; sliderMinTDP.Name = "sliderMinTDP"; @@ -260,7 +320,7 @@ private void InitializeComponent() buttonSave.BorderRadius = 2; buttonSave.FlatStyle = FlatStyle.Flat; buttonSave.ForeColor = SystemColors.ControlText; - buttonSave.Location = new Point(279, 182); + buttonSave.Location = new Point(278, 225); buttonSave.Margin = new Padding(2, 4, 2, 4); buttonSave.Name = "buttonSave"; buttonSave.Secondary = false; @@ -279,7 +339,7 @@ private void InitializeComponent() buttonDelete.BorderRadius = 2; buttonDelete.FlatStyle = FlatStyle.Flat; buttonDelete.ForeColor = SystemColors.ControlText; - buttonDelete.Location = new Point(4, 182); + buttonDelete.Location = new Point(3, 225); buttonDelete.Margin = new Padding(2, 4, 2, 4); buttonDelete.Name = "buttonDelete"; buttonDelete.Secondary = false; @@ -288,26 +348,11 @@ private void InitializeComponent() buttonDelete.Text = "Delete"; buttonDelete.UseVisualStyleBackColor = false; // - // sliderFPS - // - sliderFPS.AccessibleName = "DPI Slider"; - sliderFPS.Location = new Point(140, 66); - sliderFPS.Margin = new Padding(2); - sliderFPS.Max = 1000; - sliderFPS.Min = 20; - sliderFPS.Name = "sliderFPS"; - sliderFPS.Size = new Size(168, 20); - sliderFPS.Step = 1; - sliderFPS.TabIndex = 68; - sliderFPS.TabStop = false; - sliderFPS.Text = "FPS Slider"; - sliderFPS.Value = 60; - // // AutoTDPGameProfileUI // AutoScaleDimensions = new SizeF(7F, 15F); AutoScaleMode = AutoScaleMode.Font; - ClientSize = new Size(386, 217); + ClientSize = new Size(386, 263); Controls.Add(buttonDelete); Controls.Add(buttonSave); Controls.Add(panelGameSettings); @@ -324,6 +369,7 @@ private void InitializeComponent() ((System.ComponentModel.ISupportInitialize)pictureKeyboard).EndInit(); panelGameSettings.ResumeLayout(false); panelGameSettings.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)numericCheckInterval).EndInit(); ((System.ComponentModel.ISupportInitialize)numericUpDownFPS).EndInit(); ResumeLayout(false); PerformLayout(); @@ -351,5 +397,8 @@ private void InitializeComponent() private Label labelMaxTDP; private UI.RButton buttonDelete; private UI.Slider sliderFPS; + private UI.Slider sliderCheckInterval; + private NumericUpDown numericCheckInterval; + private Label labelCheckInterval; } } \ No newline at end of file diff --git a/app/AutoTDP/AutoTDPGameProfileUI.cs b/app/AutoTDP/AutoTDPGameProfileUI.cs index cc46eb840..41db4f70d 100644 --- a/app/AutoTDP/AutoTDPGameProfileUI.cs +++ b/app/AutoTDP/AutoTDPGameProfileUI.cs @@ -21,6 +21,9 @@ public AutoTDPGameProfileUI(GameProfile profile, AutoTDPUI parent) sliderFPS.ValueChanged += SliderFPS_ValueChanged; numericUpDownFPS.ValueChanged += NumericUpDownFPS_ValueChanged; + sliderCheckInterval.ValueChanged += SliderCheckInterval_ValueChanged; + numericCheckInterval.ValueChanged += NumericCheckInterval_ValueChanged; + InitTheme(); @@ -29,6 +32,16 @@ public AutoTDPGameProfileUI(GameProfile profile, AutoTDPUI parent) VisualizeGameProfile(); } + private void NumericCheckInterval_ValueChanged(object? sender, EventArgs e) + { + sliderCheckInterval.Value = (int)numericCheckInterval.Value; + } + + private void SliderCheckInterval_ValueChanged(object? sender, EventArgs e) + { + numericCheckInterval.Value = sliderCheckInterval.Value; + } + private void NumericUpDownFPS_ValueChanged(object? sender, EventArgs e) { sliderFPS.Value = (int)numericUpDownFPS.Value; @@ -52,6 +65,7 @@ private void ButtonSave_Click(object? sender, EventArgs e) GameProfile.TargetFPS = ((int)numericUpDownFPS.Value); GameProfile.MinTdp = sliderMinTDP.Value; GameProfile.MaxTdp = sliderMaxTDP.Value; + GameProfile.Interval = ((int)numericCheckInterval.Value); AutoTDPUI.UpdateGameProfile(GameProfile); @@ -92,12 +106,19 @@ private void AutoTDPGameProfileUI_Shown(object? sender, EventArgs e) private void VisualizeGameProfile() { + if (GameProfile.Interval < sliderCheckInterval.Min || GameProfile.Interval > sliderCheckInterval.Max) + { + GameProfile.Interval = AutoTDPService.INTERVAL_FPS_CHECK; + } + sliderMinTDP.Value = GameProfile.MinTdp; sliderMaxTDP.Value = GameProfile.MaxTdp; numericUpDownFPS.Value = GameProfile.TargetFPS; textBoxProcessName.Text = GameProfile.ProcessName; textBoxTitle.Text = GameProfile.GameTitle; checkBoxEnabled.Checked = GameProfile.Enabled; + sliderCheckInterval.Value = GameProfile.Interval; + numericCheckInterval.Value = GameProfile.Interval; } } } diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 70d4b97ee..50e44f721 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -11,7 +11,7 @@ internal class AutoTDPService : IDisposable private static readonly bool LOG_AUTO_TDP = true; private static readonly int INTERVAL_MIN_CHECK = 15 * 1_000; private static readonly int INTERVAL_APP_CHECK = 5_000; - private static readonly int INTERVAL_FPS_CHECK = 500; + public static readonly int INTERVAL_FPS_CHECK = 500; private static readonly int INTERVAL_LOG = 1_000; private int LogCounter = 0; @@ -47,6 +47,7 @@ internal class AutoTDPService : IDisposable private int LastAdjustmentsWithoutImprovement = 0; private GameInstance? currentGame = null; + private GameProfile? currentGameProfile = null; public AutoTDPService() { @@ -91,7 +92,15 @@ private int FPSCheckInterval() { return INTERVAL_FPS_CHECK; } - return (int)Math.Max(INTERVAL_FPS_CHECK, powerLimiter.GetMinInterval()); + + int interval = INTERVAL_FPS_CHECK; + + if (currentGame is not null && currentGameProfile is not null && currentGameProfile.Interval > 0) + { + interval = currentGameProfile.Interval; + } + + return (int)Math.Max(interval, powerLimiter.GetMinInterval()); } public static List AvailableFramerateSources() @@ -350,6 +359,7 @@ public void HandleGame(GameInstance instance) public void Reset() { currentGame = null; + currentGameProfile = null; GameFPSPrevious = double.NaN; GameFPS = 0; LastAdjustmentsWithoutImprovement = 0; @@ -370,8 +380,8 @@ public void Reset() public void StartGameHandler(GameInstance instance) { - GameProfile? profile = ProfileForGame(instance.ProcessName); - if (profile is null || powerLimiter is null || framerateSouce is null) + currentGameProfile = ProfileForGame(instance.ProcessName); + if (currentGameProfile is null || powerLimiter is null || framerateSouce is null) { return; } @@ -387,16 +397,16 @@ public void StartGameHandler(GameInstance instance) Logger.WriteLine("[AutoTDPService] Backing up Power limit: " + CurrentTDP + "W"); - LowestStableTDP = profile.MaxTdp; - LowestTDP = profile.MaxTdp; + LowestStableTDP = currentGameProfile.MaxTdp; + LowestTDP = currentGameProfile.MaxTdp; while (currentGame is not null && Running) { - if (!profile.Enabled) + if (!currentGameProfile.Enabled) { //Game was disabled during session. Stop AutoTDP - Logger.WriteLine("[AutoTDPService] Game profile disabled: " + profile.GameTitle + ". Disengaging."); + Logger.WriteLine("[AutoTDPService] Game profile disabled: " + currentGameProfile.GameTitle + ". Disengaging."); Reset(); return; } @@ -416,7 +426,7 @@ public void StartGameHandler(GameInstance instance) //prevent FPS from going to 0 which causes issues with the math GameFPS = Math.Max(5, fps); - AdjustPowerLimit(profile); + AdjustPowerLimit(currentGameProfile); try { @@ -528,7 +538,7 @@ private void ProcessStability() if (LowestStableStability * FPSCheckInterval() > (120 * 1_000)) { //if stable for long time try to reduce it again - LowestStableTDP = ProfileForGame(currentGame.ProcessName).MaxTdp; + LowestStableTDP = currentGameProfile.MaxTdp; LowestStableStability = 0; } } @@ -655,6 +665,7 @@ public void StopGameHandler() if (tdpThread is not null) { currentGame = null; + currentGameProfile = null; tdpThread.Join(); tdpThread = null; } @@ -665,6 +676,7 @@ public void Shutdown() { Running = false; currentGame = null; + currentGameProfile = null; if (checkerThread is not null) { diff --git a/app/AutoTDP/AutoTDPUI.cs b/app/AutoTDP/AutoTDPUI.cs index e17dbe677..811abbae0 100644 --- a/app/AutoTDP/AutoTDPUI.cs +++ b/app/AutoTDP/AutoTDPUI.cs @@ -101,6 +101,7 @@ private void ButtonAddGame_Click(object? sender, EventArgs e) p.TargetFPS = 60; p.MaxTdp = 40; p.MinTdp = 15; + p.Interval = AutoTDPService.INTERVAL_FPS_CHECK; profileUI = new AutoTDPGameProfileUI(p, this); profileUI.FormClosed += ProfileUI_FormClosed; diff --git a/app/AutoTDP/GameProfile.cs b/app/AutoTDP/GameProfile.cs index 68f171ad1..b3ed552ad 100644 --- a/app/AutoTDP/GameProfile.cs +++ b/app/AutoTDP/GameProfile.cs @@ -9,6 +9,7 @@ public class GameProfile : IComparable public int MinTdp { get; set; } public int MaxTdp { get; set; } public bool Enabled { get; set; } + public int Interval { get; set; } public int CompareTo(GameProfile? other) { From f7151941f2db9c3ac9aaad3fb3320122e732b263 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Thu, 11 Apr 2024 13:29:13 +0200 Subject: [PATCH 73/76] Shortcut, when max = min TDP. In this case, there is no auto adjustment, but the value is considered a fixed power limiter for this game. --- app/AutoTDP/AutoTDPService.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs index 50e44f721..5050c0946 100644 --- a/app/AutoTDP/AutoTDPService.cs +++ b/app/AutoTDP/AutoTDPService.cs @@ -588,6 +588,21 @@ public void AdjustPowerLimit(GameProfile profile) return; } + //For the case that someone thinks they're super smart and sets the max lower than the min, we treat them as equal and consider the MAX to be the fixed TDP. + if (profile.MaxTdp <= profile.MinTdp) + { + if (CurrentTDP == profile.MaxTdp) + { + return; + } + powerLimiter?.SetCPUPowerLimit(profile.MaxTdp); + CurrentTDP = profile.MaxTdp; + if (LOG_AUTO_TDP) + AutoTDPLogger.WriteLine("[AutoTDPService] Power Limit fixed at " + profile.MaxTdp + "W"); + + return; + } + double newPL = CurrentTDP; double fpsCorrection = FPSDipCorrection(GameFPS, profile.GetTDPFPS()); double delta = profile.GetTDPFPS() - GameFPS - fpsCorrection - 1; From 6f74050dfc796e338ae50cb4bf6e99059ca65841 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sat, 27 Apr 2024 11:59:01 +0200 Subject: [PATCH 74/76] Rename button to not clash with Ally controls --- app/Settings.Designer.cs | 44 ++++++++++++++++++++-------------------- app/Settings.cs | 2 +- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/app/Settings.Designer.cs b/app/Settings.Designer.cs index a8442b290..cbae501cf 100644 --- a/app/Settings.Designer.cs +++ b/app/Settings.Designer.cs @@ -68,7 +68,7 @@ private void InitializeComponent() panelGPU = new Panel(); labelTipGPU = new Label(); tableAdditionalCPUFeature = new TableLayoutPanel(); - buttonAutoTDP = new RButton(); + buttonCPUAutoTDP = new RButton(); tableAMD = new TableLayoutPanel(); buttonOverlay = new RButton(); buttonFPS = new RButton(); @@ -769,7 +769,7 @@ private void InitializeComponent() tableAdditionalCPUFeature.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33.3333321F)); tableAdditionalCPUFeature.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33.3333321F)); tableAdditionalCPUFeature.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33.3333321F)); - tableAdditionalCPUFeature.Controls.Add(buttonAutoTDP, 0, 0); + tableAdditionalCPUFeature.Controls.Add(buttonCPUAutoTDP, 0, 0); tableAdditionalCPUFeature.Dock = DockStyle.Top; tableAdditionalCPUFeature.Location = new Point(10, 198); tableAdditionalCPUFeature.Margin = new Padding(2, 1, 2, 1); @@ -781,25 +781,25 @@ private void InitializeComponent() // // buttonAutoTDP // - buttonAutoTDP.Activated = false; - buttonAutoTDP.BackColor = SystemColors.ControlLightLight; - buttonAutoTDP.BorderColor = Color.Transparent; - buttonAutoTDP.BorderRadius = 5; - buttonAutoTDP.Dock = DockStyle.Fill; - buttonAutoTDP.FlatAppearance.BorderSize = 0; - buttonAutoTDP.FlatStyle = FlatStyle.Flat; - buttonAutoTDP.ForeColor = SystemColors.ControlText; - buttonAutoTDP.Image = Properties.Resources.icons8_processor_32; - buttonAutoTDP.ImageAlign = ContentAlignment.MiddleRight; - buttonAutoTDP.Location = new Point(1, 1); - buttonAutoTDP.Margin = new Padding(1, 1, 1, 1); - buttonAutoTDP.Name = "buttonAutoTDP"; - buttonAutoTDP.Secondary = false; - buttonAutoTDP.Size = new Size(128, 38); - buttonAutoTDP.TabIndex = 11; - buttonAutoTDP.Text = "Auto TDP"; - buttonAutoTDP.TextImageRelation = TextImageRelation.ImageBeforeText; - buttonAutoTDP.UseVisualStyleBackColor = false; + buttonCPUAutoTDP.Activated = false; + buttonCPUAutoTDP.BackColor = SystemColors.ControlLightLight; + buttonCPUAutoTDP.BorderColor = Color.Transparent; + buttonCPUAutoTDP.BorderRadius = 5; + buttonCPUAutoTDP.Dock = DockStyle.Fill; + buttonCPUAutoTDP.FlatAppearance.BorderSize = 0; + buttonCPUAutoTDP.FlatStyle = FlatStyle.Flat; + buttonCPUAutoTDP.ForeColor = SystemColors.ControlText; + buttonCPUAutoTDP.Image = Properties.Resources.icons8_processor_32; + buttonCPUAutoTDP.ImageAlign = ContentAlignment.MiddleRight; + buttonCPUAutoTDP.Location = new Point(1, 1); + buttonCPUAutoTDP.Margin = new Padding(1, 1, 1, 1); + buttonCPUAutoTDP.Name = "buttonAutoTDP"; + buttonCPUAutoTDP.Secondary = false; + buttonCPUAutoTDP.Size = new Size(128, 38); + buttonCPUAutoTDP.TabIndex = 11; + buttonCPUAutoTDP.Text = "Auto TDP"; + buttonCPUAutoTDP.TextImageRelation = TextImageRelation.ImageBeforeText; + buttonCPUAutoTDP.UseVisualStyleBackColor = false; // // tableAMD // @@ -2111,7 +2111,7 @@ private void InitializeComponent() private CheckBox checkMatrixLid; private Panel panelMatrixAuto; private TableLayoutPanel tableAdditionalCPUFeature; - private RButton buttonAutoTDP; + private RButton buttonCPUAutoTDP; private TableLayoutPanel tableVisual; private RComboBox comboVisual; private RComboBox comboGamut; diff --git a/app/Settings.cs b/app/Settings.cs index 3f30d78ce..8796203ac 100644 --- a/app/Settings.cs +++ b/app/Settings.cs @@ -248,7 +248,7 @@ public SettingsForm() buttonFPS.Click += ButtonFPS_Click; buttonOverlay.Click += ButtonOverlay_Click; - buttonAutoTDP.Click += ButtonAutoTDP_Click; + buttonCPUAutoTDP.Click += ButtonAutoTDP_Click; Text = "G-Helper " + (ProcessHelper.IsUserAdministrator() ? "—" : "-") + " " + AppConfig.GetModelShort(); TopMost = AppConfig.Is("topmost"); From 3f9ef96739cef6065e28e21a8502785b91c7f108 Mon Sep 17 00:00:00 2001 From: IceStormNG Date: Sat, 27 Apr 2024 12:01:43 +0200 Subject: [PATCH 75/76] Fixed name clash with ally tdp buttons --- app/Settings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Settings.cs b/app/Settings.cs index de6aeae30..f2371cf74 100644 --- a/app/Settings.cs +++ b/app/Settings.cs @@ -257,7 +257,7 @@ public SettingsForm() buttonAutoTDP.Click += ButtonAutoTDP_Click; buttonAutoTDP.BorderColor = colorTurbo; - buttonCPUAutoTDP.Click += ButtonAutoTDP_Click; + buttonCPUAutoTDP.Click += ButtonCPUAutoTDP_Click; Text = "G-Helper " + (ProcessHelper.IsUserAdministrator() ? "—" : "-") + " " + AppConfig.GetModelShort(); TopMost = AppConfig.Is("topmost"); @@ -435,7 +435,7 @@ public void VisualiseBrightness() }); } - private void ButtonAutoTDP_Click(object? sender, EventArgs e) + private void ButtonCPUAutoTDP_Click(object? sender, EventArgs e) { if (autoTdpUi is not null) { From 8961e8df85edc043a340adc3a2a4abc1b95508f8 Mon Sep 17 00:00:00 2001 From: Serge <5920850+seerge@users.noreply.github.com> Date: Tue, 16 Jul 2024 11:28:45 +0200 Subject: [PATCH 76/76] ROG Strix Evolve https://github.com/seerge/g-helper/issues/2842 --- app/Peripherals/PeripheralsProvider.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Peripherals/PeripheralsProvider.cs b/app/Peripherals/PeripheralsProvider.cs index 1e2c23dd1..a25ec5b16 100644 --- a/app/Peripherals/PeripheralsProvider.cs +++ b/app/Peripherals/PeripheralsProvider.cs @@ -219,6 +219,7 @@ public static void DetectAllAsusMice() DetectMouse(new PugioIIWired()); DetectMouse(new StrixImpactII()); DetectMouse(new StrixImpactIIElectroPunk()); + DetectMouse(new StrixEvolve()); DetectMouse(new Chakram()); DetectMouse(new ChakramWired()); DetectMouse(new ChakramCore());