mirror of
https://github.com/ayufan/steam-deck-tools.git
synced 2026-02-14 03:24:38 +01:00
Control SMT
This commit is contained in:
parent
d7ad2f1d71
commit
7dc418a0e0
|
|
@ -2,6 +2,7 @@
|
|||
using CommonHelpers.FromLibreHardwareMonitor;
|
||||
using Microsoft.VisualBasic.Logging;
|
||||
using PowerControl.External;
|
||||
using PowerControl.Helpers;
|
||||
using RTSSSharedMemoryNET;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
|
@ -97,7 +98,7 @@ namespace PowerControl
|
|||
|
||||
GlobalHotKey.RegisterHotKey(Settings.Default.MenuUpKey, () =>
|
||||
{
|
||||
if (!isForeground())
|
||||
if (!RTSS.IsOSDForeground())
|
||||
return;
|
||||
rootMenu.Prev();
|
||||
setDismissTimer();
|
||||
|
|
@ -106,7 +107,7 @@ namespace PowerControl
|
|||
|
||||
GlobalHotKey.RegisterHotKey(Settings.Default.MenuDownKey, () =>
|
||||
{
|
||||
if (!isForeground())
|
||||
if (!RTSS.IsOSDForeground())
|
||||
return;
|
||||
rootMenu.Next();
|
||||
setDismissTimer();
|
||||
|
|
@ -115,7 +116,7 @@ namespace PowerControl
|
|||
|
||||
GlobalHotKey.RegisterHotKey(Settings.Default.MenuLeftKey, () =>
|
||||
{
|
||||
if (!isForeground())
|
||||
if (!RTSS.IsOSDForeground())
|
||||
return;
|
||||
rootMenu.SelectPrev();
|
||||
setDismissTimer();
|
||||
|
|
@ -124,7 +125,7 @@ namespace PowerControl
|
|||
|
||||
GlobalHotKey.RegisterHotKey(Settings.Default.MenuRightKey, () =>
|
||||
{
|
||||
if (!isForeground())
|
||||
if (!RTSS.IsOSDForeground())
|
||||
return;
|
||||
rootMenu.SelectNext();
|
||||
setDismissTimer();
|
||||
|
|
@ -167,28 +168,6 @@ namespace PowerControl
|
|||
}
|
||||
}
|
||||
|
||||
private bool isForeground()
|
||||
{
|
||||
try
|
||||
{
|
||||
var processId = Helpers.TopLevelWindow.GetTopLevelProcessId();
|
||||
if (processId is null)
|
||||
return true;
|
||||
|
||||
foreach (var app in OSD.GetAppEntries(AppFlags.MASK))
|
||||
{
|
||||
if (app.ProcessId == processId)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OsdTimer_Tick(object? sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
|
|
@ -262,7 +241,7 @@ namespace PowerControl
|
|||
return;
|
||||
}
|
||||
|
||||
if ((input.buttons5 & (byte)SDCButton5.BTN_QUICK_ACCESS) == 0 || !isForeground())
|
||||
if ((input.buttons5 & (byte)SDCButton5.BTN_QUICK_ACCESS) == 0 || !RTSS.IsOSDForeground())
|
||||
{
|
||||
// schedule next repeat far in the future
|
||||
dismissNeptuneInput();
|
||||
|
|
|
|||
190
PowerControl/Helpers/ProcessorCores.cs
Normal file
190
PowerControl/Helpers/ProcessorCores.cs
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
using PowerControl.External;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PowerControl.Helpers
|
||||
{
|
||||
internal class ProcessorCores
|
||||
{
|
||||
public static int GetProcessorCoreCount()
|
||||
{
|
||||
return GetProcessorCores().Count();
|
||||
}
|
||||
|
||||
public static IntPtr GetProcessorMask(bool firstThreadOnly = false)
|
||||
{
|
||||
Int64 mask = 0;
|
||||
|
||||
foreach (var process in GetProcessorCores())
|
||||
{
|
||||
// This works only up-to 63 CPUs
|
||||
Int64 processorMask = (Int64)process.ProcessorMask.ToUInt64();
|
||||
|
||||
if (firstThreadOnly)
|
||||
processorMask = LSB(processorMask);
|
||||
|
||||
mask |= processorMask;
|
||||
}
|
||||
|
||||
return new IntPtr(mask);
|
||||
}
|
||||
|
||||
public static bool HasSMTThreads()
|
||||
{
|
||||
foreach (var processorMask in GetProcessorMasks())
|
||||
{
|
||||
if (processorMask != LSB(processorMask))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsUsingSMT(int processId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var p = Process.GetProcessById(processId);
|
||||
UInt64 mask = (UInt64)p.ProcessorAffinity.ToInt64();
|
||||
|
||||
foreach (var processorMask in GetProcessorMasks())
|
||||
{
|
||||
// look for CPU that has more than 1 thread
|
||||
// and has both assigned to process
|
||||
var filtered = mask & processorMask;
|
||||
if (filtered != LSB(filtered))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
catch(ArgumentException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool SetProcessSMT(int processId, bool allThreads)
|
||||
{
|
||||
try
|
||||
{
|
||||
var p = Process.GetProcessById(processId);
|
||||
UInt64 mask = (UInt64)p.ProcessorAffinity.ToInt64();
|
||||
|
||||
foreach (var processorMask in GetProcessorMasks())
|
||||
{
|
||||
var selectedMask = mask & processorMask;
|
||||
|
||||
if (selectedMask == 0)
|
||||
continue; // ignore not assigned processors
|
||||
else if (allThreads)
|
||||
mask |= processorMask; // assign all threads
|
||||
else
|
||||
mask = LSB(selectedMask) | (mask & ~processorMask); // assign only first thread
|
||||
}
|
||||
|
||||
p.ProcessorAffinity = new IntPtr((Int64)mask);
|
||||
return true;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static UInt64 LSB(UInt64 value)
|
||||
{
|
||||
return (UInt64)LSB((Int64)value);
|
||||
}
|
||||
|
||||
private static Int64 LSB(Int64 value)
|
||||
{
|
||||
return (value & -value);
|
||||
}
|
||||
|
||||
static IEnumerable<UInt64> GetProcessorMasks()
|
||||
{
|
||||
return GetProcessorCores().Select((p) => p.ProcessorMask.ToUInt64());
|
||||
}
|
||||
|
||||
static IEnumerable<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> GetProcessorCores()
|
||||
{
|
||||
return GetLogicalProcessorInformation().Where((p) => p.Relationship == LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore);
|
||||
}
|
||||
|
||||
static SYSTEM_LOGICAL_PROCESSOR_INFORMATION[] GetLogicalProcessorInformation()
|
||||
{
|
||||
int bufferSize = 0;
|
||||
GetLogicalProcessorInformation(IntPtr.Zero, ref bufferSize);
|
||||
if (bufferSize == 0)
|
||||
return new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[0];
|
||||
|
||||
int sizeOfEntry = Marshal.SizeOf<SYSTEM_LOGICAL_PROCESSOR_INFORMATION>();
|
||||
int numEntries = bufferSize / sizeOfEntry;
|
||||
var processors = new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[numEntries];
|
||||
|
||||
var handle = Marshal.AllocHGlobal(bufferSize);
|
||||
try
|
||||
{
|
||||
if (!GetLogicalProcessorInformation(handle, ref bufferSize))
|
||||
return new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[0];
|
||||
|
||||
for (int i = 0; i < processors.Length; i++)
|
||||
processors[i] = Marshal.PtrToStructure<SYSTEM_LOGICAL_PROCESSOR_INFORMATION>(IntPtr.Add(handle, sizeOfEntry * i));
|
||||
|
||||
return processors;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(handle);
|
||||
}
|
||||
}
|
||||
|
||||
// Taken from: https://stackoverflow.com/a/63744912
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct CACHE_DESCRIPTOR
|
||||
{
|
||||
public byte Level;
|
||||
public byte Associativity;
|
||||
public ushort LineSize;
|
||||
public uint Size;
|
||||
public uint Type;
|
||||
};
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION_UNION
|
||||
{
|
||||
[FieldOffset(0)] public byte ProcessorCore;
|
||||
[FieldOffset(0)] public uint NumaNode;
|
||||
[FieldOffset(0)] public CACHE_DESCRIPTOR Cache;
|
||||
[FieldOffset(0)] private UInt64 Reserved1;
|
||||
[FieldOffset(8)] private UInt64 Reserved2;
|
||||
};
|
||||
|
||||
public enum LOGICAL_PROCESSOR_RELATIONSHIP
|
||||
{
|
||||
RelationProcessorCore,
|
||||
RelationNumaNode,
|
||||
RelationCache,
|
||||
RelationProcessorPackage,
|
||||
RelationGroup,
|
||||
RelationAll = 0xffff
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION
|
||||
{
|
||||
public UIntPtr ProcessorMask;
|
||||
public LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
|
||||
public SYSTEM_LOGICAL_PROCESSOR_INFORMATION_UNION ProcessorInformation;
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
static extern bool GetLogicalProcessorInformation(IntPtr buffer, ref int bufferSize);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using RTSSSharedMemoryNET;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
|
@ -6,21 +7,49 @@ using System.Runtime.InteropServices;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PowerControl.External
|
||||
namespace PowerControl.Helpers
|
||||
{
|
||||
internal static class RTSS
|
||||
{
|
||||
public static bool IsOSDForeground()
|
||||
{
|
||||
return IsOSDForeground(out _);
|
||||
}
|
||||
|
||||
public static bool IsOSDForeground(out int? processId)
|
||||
{
|
||||
try
|
||||
{
|
||||
processId = (int?)Helpers.TopLevelWindow.GetTopLevelProcessId();
|
||||
if (processId is null)
|
||||
return true;
|
||||
|
||||
foreach (var app in OSD.GetAppEntries(AppFlags.MASK))
|
||||
{
|
||||
if (app.ProcessId == processId)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
processId = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool GetProfileProperty<T>(string propertyName, out T value)
|
||||
{
|
||||
var bytes = new byte[Marshal.SizeOf<T>()];
|
||||
var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
|
||||
value = default(T);
|
||||
value = default;
|
||||
try
|
||||
{
|
||||
if (!GetProfileProperty(propertyName, handle.AddrOfPinnedObject(), (uint)bytes.Length))
|
||||
return false;
|
||||
|
||||
value = (T)Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());
|
||||
value = Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
using CommonHelpers;
|
||||
using PowerControl.External;
|
||||
using PowerControl.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
|
@ -158,6 +158,33 @@ namespace PowerControl
|
|||
ApplyDelay = 1000,
|
||||
Visible = false
|
||||
},
|
||||
new Menu.MenuItemWithOptions()
|
||||
{
|
||||
Name = "SMT",
|
||||
ApplyDelay = 500,
|
||||
Options = { "No", "Yes" },
|
||||
ResetValue = () => { return "Yes"; },
|
||||
CurrentValue = delegate()
|
||||
{
|
||||
if (!RTSS.IsOSDForeground(out var processId))
|
||||
return null;
|
||||
if (!ProcessorCores.HasSMTThreads())
|
||||
return null;
|
||||
|
||||
return ProcessorCores.IsUsingSMT(processId.Value) ? "Yes" : "No";
|
||||
},
|
||||
ApplyValue = delegate(object selected)
|
||||
{
|
||||
if (!RTSS.IsOSDForeground(out var processId))
|
||||
return null;
|
||||
if (!ProcessorCores.HasSMTThreads())
|
||||
return null;
|
||||
|
||||
ProcessorCores.SetProcessSMT(processId.Value, selected.ToString() == "Yes");
|
||||
|
||||
return ProcessorCores.IsUsingSMT(processId.Value) ? "Yes" : "No";
|
||||
}
|
||||
},
|
||||
new Menu.MenuItemSeparator(),
|
||||
new Menu.MenuItemWithOptions()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ There are currently 4 configurable settings:
|
|||
- Refresh Rate
|
||||
- FPS Limit (requires: RTSS > Setup > Enable Framelimit)
|
||||
- TDP
|
||||
- SMT (Each core of AMD has 2 threads, this allows to enable/disable second threads)
|
||||
- OSD / OSDMode (requires PerformanceOverlay running)
|
||||
- Fan (requires FanControl running)
|
||||
|
||||
|
|
|
|||
|
|
@ -13,5 +13,6 @@
|
|||
- Improve FanControl UI
|
||||
- Make increments for Brightness and Volume in 5 (fixed)
|
||||
- Press `3 dots + L4 + R4 + L5 + R5` to reset (TDP, Refresh Rate, FPS limit) to default
|
||||
- Allow to disable SMT (second threads of each physical cores)
|
||||
|
||||
If you found it useful buy me [Ko-fi](https://ko-fi.com/ayufan).
|
||||
|
|
|
|||
Loading…
Reference in a new issue