diff --git a/src/System/HardwareMonitor.cs b/src/System/HardwareMonitor.cs index bf27da6..3d1ce17 100644 --- a/src/System/HardwareMonitor.cs +++ b/src/System/HardwareMonitor.cs @@ -5,6 +5,7 @@ using System.Reflection; // ★★★ 新增:用于反射关闭历史记录 using LibreHardwareMonitor.Hardware; using LiteMonitor.src.Core; +using LiteMonitor.src.SystemServices; // ★★★ [MTT] 摩尔线程支持 using System.Linq; using System.Threading; @@ -15,6 +16,7 @@ public sealed class HardwareMonitor : IDisposable #region Singleton & Events public static HardwareMonitor? Instance { get; private set; } public event Action? OnValuesUpdated; + public event Action? OnHardwareNamesCached; // ★★★ [新增] 硬件名称缓存完成事件 ★★★ #endregion #region Private Fields @@ -32,11 +34,15 @@ public sealed class HardwareMonitor : IDisposable // 性能计数器管理器 private readonly PerformanceCounterManager _perfCounterManager; + + // ★★★ [新增] 摩尔线程 GPU 监控器 ★★★ + private readonly MttGpuMonitor _mttGpuMonitor; private readonly Dictionary _lastValidMap = new(); // 状态标记 (防止并发重载) private volatile bool _isReloading = false; + private volatile bool _initialized = false; // ★★★ [新增] 标记硬件是否已初始化 ★★★ // 计时器相关 private long _tickCounter = 0; @@ -86,6 +92,9 @@ public HardwareMonitor(Settings cfg) // ★★★ [新增] 1. 初始化计数器管理器 (必须在 ValueProvider 之前) ★★★ _perfCounterManager = new PerformanceCounterManager(); + + // ★★★ [新增] 初始化摩尔线程 GPU 监控器 ★★★ + _mttGpuMonitor = new MttGpuMonitor(); // 2. 初始化服务 _sensorMap = new SensorMap(); @@ -106,6 +115,9 @@ public HardwareMonitor(Settings cfg) _lock, _lastValidMap ); + + // ★★★ [新增] 设置摩尔线程 GPU 监控器引用 ★★★ + _valueProvider.SetMttGpuMonitor(_mttGpuMonitor); // 3. 异步启动 (唯一优化:不卡UI) InitializeAsync(); @@ -117,6 +129,99 @@ public HardwareMonitor(Settings cfg) public float? Get(string key) => _valueProvider.GetValue(key); public string GetNetworkIP() => _networkManager.GetCurrentIP(); + + // ★★★ [新增] 获取摩尔线程 GPU 数据 ★★★ + public MttGpuData? GetMttGpuData() => _mttGpuMonitor?.Update(); + + // ★★★ [新增] 判断是否存在摩尔线程 GPU ★★★ + public bool HasMttGpu => _mttGpuMonitor?.HasMttGpu ?? false; + + // ★★★ [新增] 缓存 CPU/GPU 名称,避免重复获取 ★★★ + private string _cachedCpuName = ""; + private string _cachedGpuName = ""; + private bool _namesCached = false; + + /// + /// ★★★ [新增] 缓存硬件名称(在初始化完成后调用)★★★ + /// + private void CacheHardwareNames() + { + try + { + // CPU + var cpu = _computer.Hardware.FirstOrDefault(h => h.HardwareType == HardwareType.Cpu); + _cachedCpuName = cpu?.Name ?? ""; + + // GPU + if (_mttGpuMonitor?.HasMttGpu == true && !string.IsNullOrEmpty(_mttGpuMonitor.GpuName)) + { + _cachedGpuName = "Moore Threads " + _mttGpuMonitor.GpuName; // ★★★ 添加前缀 ★★★ + } + else + { + var gpu = _sensorMap.CachedGpu; + _cachedGpuName = gpu?.Name ?? ""; + } + + _namesCached = true; + + // ★★★ 触发事件,通知 UI 刷新 ★★★ + OnHardwareNamesCached?.Invoke(); + } + catch { } + } + + /// + /// ★★★ [新增] 获取 CPU 名称 ★★★ + /// + public string GetCpuName() + { + // 优先使用缓存 + if (_namesCached && !string.IsNullOrEmpty(_cachedCpuName)) + return _cachedCpuName; + + try + { + var cpu = _computer.Hardware.FirstOrDefault(h => h.HardwareType == HardwareType.Cpu); + if (cpu != null && !string.IsNullOrEmpty(cpu.Name)) + { + _cachedCpuName = cpu.Name; + return cpu.Name; + } + } + catch { } + return ""; + } + + /// + /// ★★★ [新增] 获取 GPU 名称 ★★★ + /// + public string GetGpuName() + { + // 优先使用缓存 + if (_namesCached && !string.IsNullOrEmpty(_cachedGpuName)) + return _cachedGpuName; + + try + { + // 优先:摩尔线程 GPU + if (_mttGpuMonitor?.HasMttGpu == true && !string.IsNullOrEmpty(_mttGpuMonitor.GpuName)) + { + _cachedGpuName = "Moore Threads " + _mttGpuMonitor.GpuName; // ★★★ 添加前缀 ★★★ + return _cachedGpuName; + } + + // 其次:LHM 检测到的 GPU + var gpu = _sensorMap.CachedGpu; + if (gpu != null && !string.IsNullOrEmpty(gpu.Name)) + { + _cachedGpuName = gpu.Name; + return gpu.Name; + } + } + catch { } + return ""; + } // ★★★ 新增:允许主程序手动触发驱动检查 (用于解决启动弹窗冲突) ★★★ public Task SmartCheckDriver() => _driverInstaller.SmartCheckDriver(); @@ -232,6 +337,12 @@ public void UpdateAll() _valueProvider.OnUpdateTickStarted(); + // ★★★ [新增] 更新摩尔线程 GPU 数据 ★★★ + if (_mttGpuMonitor?.HasMttGpu == true) + { + _mttGpuMonitor.Update(); + } + // 任务错峰执行 (调用 SystemOptimizer) SystemOptimizer.RunMaintenanceTasks(_secondsCounter); @@ -284,6 +395,7 @@ public void Dispose() _valueProvider.Dispose(); _perfCounterManager.Dispose(); // ★★★ [新增] 释放计数器资源 ★★★ _fpsCounter.Dispose(); // <--- 新增 + _mttGpuMonitor?.Dispose(); // ★★★ [新增] 释放摩尔线程 GPU 监控器 ★★★ _networkManager.ClearCache(); _diskManager.ClearCache(); // 漏掉的,补上 } @@ -300,6 +412,9 @@ private void InitializeAsync() // ★★★ [新增] 启动计数器预热 (不阻塞主 UI) ★★★ _perfCounterManager.InitializeAsync(); + // ★★★ [新增] 初始化摩尔线程 GPU 监控器 ★★★ + _mttGpuMonitor.Initialize(); + // 这句耗时 4-5 秒,但在执行过程中,硬件会陆续添加到 _computer.Hardware _computer.Open(); @@ -317,8 +432,13 @@ private void InitializeAsync() // 2. 数据有了,再建立映射 _sensorMap.Rebuild(_computer, _cfg); - // ★★★ [新增] 3. 静态化预热:将所有传感器对象存入 Provider 缓存 ★★★ + // 3. ★★★ [新增] 缓存硬件名称 ★★★ + CacheHardwareNames(); + + // 4. 静态化预热:将所有传感器对象存入 Provider 缓存 _valueProvider.PreCacheAllSensors(_sensorMap); + + _initialized = true; // ★★★ [新增] 标记初始化完成 ★★★ } // 优化 T1:启动后大扫除 diff --git a/src/System/HardwareServices/HardwareRules.cs b/src/System/HardwareServices/HardwareRules.cs index 97e2dfe..960c9c7 100644 --- a/src/System/HardwareServices/HardwareRules.cs +++ b/src/System/HardwareServices/HardwareRules.cs @@ -27,6 +27,9 @@ public static int GetHwPriority(IHardware hw) // 1. 特殊处理:Microsoft Basic Render Driver 永远垫底 if (name.Contains("Basic Render", StringComparison.OrdinalIgnoreCase)) return 100; + + // ★★★ [新增] 摩尔线程 GPU 优先级 (与 Nvidia/AMD 独显同级) ★★★ + if (IsMttGpu(hw, name)) return 0; // 2. Nvidia 显卡 (默认视为独显/最强) if (hw.HardwareType == HardwareType.GpuNvidia) return 0; @@ -62,6 +65,21 @@ public static int GetHwPriority(IHardware hw) // 其他 -> 优先级 4 return 4; } + + /// + /// ★★★ [新增] 判断是否为摩尔线程 GPU ★★★ + /// + public static bool IsMttGpu(IHardware hw, string? name = null) + { + name ??= hw.Name; + if (string.IsNullOrEmpty(name)) return false; + + // 摩尔线程 GPU 名称特征:MTT, Moore Threads, 摩尔线程 + return Has(name, "MTT") || + Has(name, "Moore Threads") || + Has(name, "摩尔线程") || + Has(name, "MT "); + } /// /// 判断是否为独显版 Arc (A/B/Pro 系列) @@ -83,7 +101,7 @@ public static bool IsDiscreteArc(string name) public static bool ShouldUseSharedMemory(IHardware hw) { // 只有 Intel 核显才优先使用共享内存 - // Intel 独显 (Arc A/B/Pro) 和其他厂商 (Nvidia/AMD) 都使用专用显存 (Dedicated) + // Intel 独显 (Arc A/B/Pro) 和其他厂商 (Nvidia/AMD/MTT) 都使用专用显存 (Dedicated) if (hw.HardwareType == HardwareType.GpuIntel) { // 如果是 Discrete Arc,则不使用 Shared (即使用 Dedicated) diff --git a/src/System/HardwareServices/HardwareValueProvider.cs b/src/System/HardwareServices/HardwareValueProvider.cs index 4dd9541..fb1f6f4 100644 --- a/src/System/HardwareServices/HardwareValueProvider.cs +++ b/src/System/HardwareServices/HardwareValueProvider.cs @@ -22,6 +22,9 @@ public class HardwareValueProvider : IDisposable // 子服务与处理器 private readonly PerformanceCounterManager _perfManager; private readonly ComponentProcessor _componentProcessor; + + // ★★★ [新增] 摩尔线程 GPU 监控器引用 ★★★ + private MttGpuMonitor? _mttGpuMonitor; // Tick 级智能缓存 (防止同帧重复计算) private readonly Dictionary _tickCache = new(); @@ -52,6 +55,14 @@ public HardwareValueProvider(Computer c, Settings s, SensorMap map, NetworkManag // 初始化子服务 _componentProcessor = new ComponentProcessor(c, s, map); } + + /// + /// ★★★ [新增] 设置摩尔线程 GPU 监控器引用 ★★★ + /// + public void SetMttGpuMonitor(MttGpuMonitor? monitor) + { + _mttGpuMonitor = monitor; + } // ★★★ [新增] 清空缓存并重新预热(当硬件重载或配置变更时调用) ★★★ public void PreCacheAllSensors(SensorMap map) @@ -230,7 +241,26 @@ public void OnUpdateTickStarted() // 定义临时结果变量 float? result = null; - + + // ★★★ [新增] 摩尔线程 GPU 优先逻辑 ★★★ + // 当存在摩尔线程 GPU 时,GPU 相关指标优先从 MTML 获取 + // 避免 LHM 返回其他 GPU(如 Intel iGPU)的数据导致不匹配 + if (_mttGpuMonitor?.HasMttGpu == true && key.StartsWith("GPU.")) + { + result = _mttGpuMonitor.GetValue(key); + if (result.HasValue) + { + // 记录最大值 + if (key == "GPU.Clock" && result.Value > 0 && result.Value < 6000f) + _cfg.UpdateMaxRecord(key, result.Value); + else if (key == "GPU.Power" && result.Value > 0 && result.Value < 1200f) + _cfg.UpdateMaxRecord(key, result.Value); + + _tickCache[key] = result.Value; + return result.Value; + } + } + // ★★★ [核心逻辑] 全局开关判断:只有当开关开启,且管理器已初始化成功时,才尝试走计数器 ★★★ // 这里的 UseWindowsPerformanceCounters 对应 Step 1 中 Settings 新增的属性 bool useCounter = _cfg.UseWinPerCounters && _perfManager.IsInitialized; @@ -459,6 +489,22 @@ public void OnUpdateTickStarted() result = last; } } + + // ★★★ [新增] 11. 摩尔线程 GPU 回退逻辑 ★★★ + // 对于 GPU key:开头已优先尝试 MTML,这里处理 MTML 无数据而 LHM 也无数据的情况 + // 对于非 GPU key:当 LHM 无数据时尝试 MTML(未来可能有其他硬件支持) + if (result == null && _mttGpuMonitor?.HasMttGpu == true && key.StartsWith("GPU.")) + { + result = _mttGpuMonitor.GetValue(key); + if (result.HasValue) + { + // 记录最大值 + if (key == "GPU.Clock" && result.Value > 0 && result.Value < 6000f) + _cfg.UpdateMaxRecord(key, result.Value); + else if (key == "GPU.Power" && result.Value > 0 && result.Value < 1200f) + _cfg.UpdateMaxRecord(key, result.Value); + } + } // ★★★ [新增 4] 写入缓存并返回 ★★★ if (result.HasValue) diff --git a/src/System/HardwareServices/MtmlNative.cs b/src/System/HardwareServices/MtmlNative.cs new file mode 100644 index 0000000..b437046 --- /dev/null +++ b/src/System/HardwareServices/MtmlNative.cs @@ -0,0 +1,120 @@ +using System; +using System.Runtime.InteropServices; + +namespace LiteMonitor.src.SystemServices +{ + /// + /// MTML (Moore Threads Management Library) P/Invoke 封装 + /// 用于获取摩尔线程显卡的监控数据 + /// + internal static class MtmlNative + { + private const string DLL_NAME = "mtml.dll"; + + #region Return Codes + public const int MTML_SUCCESS = 0; + public const int MTML_ERROR_DRIVER_NOT_LOADED = 1; + public const int MTML_ERROR_DRIVER_FAILURE = 2; + public const int MTML_ERROR_INVALID_ARGUMENT = 3; + public const int MTML_ERROR_NOT_SUPPORTED = 4; + public const int MTML_ERROR_NO_PERMISSION = 5; + public const int MTML_ERROR_INSUFFICIENT_SIZE = 6; + public const int MTML_ERROR_NOT_FOUND = 7; + #endregion + + #region Buffer Sizes + public const int MTML_DEVICE_NAME_BUFFER_SIZE = 32; + public const int MTML_DEVICE_UUID_BUFFER_SIZE = 48; + #endregion + + #region Opaque Handles + // 不透明句柄使用 IntPtr + // MtmlLibrary, MtmlSystem, MtmlDevice, MtmlGpu, MtmlMemory, MtmlVpu + #endregion + + #region Library Functions + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int mtmlLibraryInit(out IntPtr lib); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int mtmlLibraryShutDown(IntPtr lib); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int mtmlLibraryCountDevice(IntPtr lib, out uint count); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int mtmlLibraryInitDeviceByIndex(IntPtr lib, uint index, out IntPtr dev); + #endregion + + #region Device Functions + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int mtmlDeviceGetName(IntPtr dev, byte[] name, uint length); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int mtmlDeviceGetUUID(IntPtr dev, byte[] uuid, uint length); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int mtmlDeviceGetPowerUsage(IntPtr dev, out uint power); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int mtmlDeviceGetFanRpm(IntPtr dev, uint fanIndex, out uint fanRpm); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int mtmlDeviceCountFan(IntPtr dev, out uint count); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int mtmlDeviceInitGpu(IntPtr dev, out IntPtr gpu); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int mtmlDeviceInitMemory(IntPtr dev, out IntPtr mem); + #endregion + + #region GPU Functions + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int mtmlGpuGetUtilization(IntPtr gpu, out uint utilization); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int mtmlGpuGetTemperature(IntPtr gpu, out int temp); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int mtmlGpuGetClock(IntPtr gpu, out uint clockMhz); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int mtmlGpuGetMaxClock(IntPtr gpu, out uint clockMhz); + #endregion + + #region Memory Functions + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int mtmlMemoryGetTotal(IntPtr mem, out ulong total); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int mtmlMemoryGetUsed(IntPtr mem, out ulong used); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int mtmlMemoryGetUtilization(IntPtr mem, out uint utilization); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int mtmlMemoryGetClock(IntPtr mem, out uint clockMhz); + #endregion + + #region Helper Methods + /// + /// 检查 MTML 库是否可用 + /// + public static bool IsAvailable() + { + try + { + int result = mtmlLibraryInit(out IntPtr lib); + if (result == MTML_SUCCESS && lib != IntPtr.Zero) + { + mtmlLibraryShutDown(lib); + return true; + } + } + catch { } + return false; + } + #endregion + } +} diff --git a/src/System/HardwareServices/MttGpuMonitor.cs b/src/System/HardwareServices/MttGpuMonitor.cs new file mode 100644 index 0000000..c0a76f4 --- /dev/null +++ b/src/System/HardwareServices/MttGpuMonitor.cs @@ -0,0 +1,272 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using LiteMonitor.src.Core; + +namespace LiteMonitor.src.SystemServices +{ + /// + /// 摩尔线程 GPU 监控数据 + /// + public class MttGpuData + { + public string Name { get; set; } = ""; + public string UUID { get; set; } = ""; + public uint Index { get; set; } + + // GPU 核心数据 + public float? Load { get; set; } // 负载百分比 + public float? Temperature { get; set; } // 温度 (°C) + public float? Clock { get; set; } // 核心频率 (MHz) + public float? Power { get; set; } // 功耗 (W) + + // 显存数据 + public float? VramUsed { get; set; } // 已用显存 (MB) + public float? VramTotal { get; set; } // 总显存 (MB) + public float? VramLoad { get; set; } // 显存负载百分比 + public float? VramClock { get; set; } // 显存频率 (MHz) + + // 风扇 + public float? FanRpm { get; set; } // 风扇转速 (RPM) + } + + /// + /// 摩尔线程 GPU 监控器 + /// 通过 MTML 库获取摩尔线程显卡的实时数据 + /// + public class MttGpuMonitor : IDisposable + { + private IntPtr _library = IntPtr.Zero; + private IntPtr _device = IntPtr.Zero; + private IntPtr _gpu = IntPtr.Zero; + private IntPtr _memory = IntPtr.Zero; + + private readonly object _lock = new object(); + private bool _initialized = false; + private bool _disposed = false; + + // 缓存设备信息 + private MttGpuData? _cachedData; + private uint _deviceIndex; + + // 是否检测到摩尔线程 GPU + public bool HasMttGpu => _initialized && _device != IntPtr.Zero; + public string GpuName => _cachedData?.Name ?? ""; + + /// + /// 初始化 MTML 库并检测摩尔线程 GPU + /// + public bool Initialize() + { + lock (_lock) + { + if (_initialized) return HasMttGpu; + + try + { + // 1. 初始化 MTML 库 + int result = MtmlNative.mtmlLibraryInit(out _library); + if (result != MtmlNative.MTML_SUCCESS || _library == IntPtr.Zero) + { + Debug.WriteLine($"[MTT] mtmlLibraryInit failed: {result}"); + _initialized = true; + return false; + } + + // 2. 检查设备数量 + result = MtmlNative.mtmlLibraryCountDevice(_library, out uint count); + if (result != MtmlNative.MTML_SUCCESS || count == 0) + { + Debug.WriteLine($"[MTT] No MTT device found, count: {count}"); + _initialized = true; + return false; + } + + // 3. 初始化第一个设备 + result = MtmlNative.mtmlLibraryInitDeviceByIndex(_library, 0, out _device); + if (result != MtmlNative.MTML_SUCCESS || _device == IntPtr.Zero) + { + Debug.WriteLine($"[MTT] mtmlLibraryInitDeviceByIndex failed: {result}"); + _initialized = true; + return false; + } + + _deviceIndex = 0; + + // 4. 初始化 GPU 和 Memory 句柄 + MtmlNative.mtmlDeviceInitGpu(_device, out _gpu); + MtmlNative.mtmlDeviceInitMemory(_device, out _memory); + + // 5. 获取设备名称 + _cachedData = new MttGpuData { Index = 0 }; + + byte[] nameBuffer = new byte[MtmlNative.MTML_DEVICE_NAME_BUFFER_SIZE]; + if (MtmlNative.mtmlDeviceGetName(_device, nameBuffer, (uint)nameBuffer.Length) == MtmlNative.MTML_SUCCESS) + { + _cachedData.Name = System.Text.Encoding.ASCII.GetString(nameBuffer).TrimEnd('\0'); + } + + byte[] uuidBuffer = new byte[MtmlNative.MTML_DEVICE_UUID_BUFFER_SIZE]; + if (MtmlNative.mtmlDeviceGetUUID(_device, uuidBuffer, (uint)uuidBuffer.Length) == MtmlNative.MTML_SUCCESS) + { + _cachedData.UUID = System.Text.Encoding.ASCII.GetString(uuidBuffer).TrimEnd('\0'); + } + + // 获取总显存 (只需一次) + if (_memory != IntPtr.Zero) + { + if (MtmlNative.mtmlMemoryGetTotal(_memory, out ulong total) == MtmlNative.MTML_SUCCESS) + { + _cachedData.VramTotal = total / (1024f * 1024f); // 转换为 MB + Settings.DetectedGpuVramTotalGB = _cachedData.VramTotal.Value / 1024f; + } + } + + _initialized = true; + Debug.WriteLine($"[MTT] Initialized: {_cachedData.Name}"); + return true; + } + catch (Exception ex) + { + Debug.WriteLine($"[MTT] Initialize error: {ex.Message}"); + _initialized = true; + return false; + } + } + } + + /// + /// 更新 GPU 数据 + /// + public MttGpuData? Update() + { + if (!HasMttGpu || _cachedData == null) return null; + + lock (_lock) + { + try + { + var data = _cachedData; + + // GPU 核心数据 + if (_gpu != IntPtr.Zero) + { + // 负载 + if (MtmlNative.mtmlGpuGetUtilization(_gpu, out uint util) == MtmlNative.MTML_SUCCESS) + { + data.Load = Math.Clamp(util, 0f, 100f); + } + + // 温度 + if (MtmlNative.mtmlGpuGetTemperature(_gpu, out int temp) == MtmlNative.MTML_SUCCESS) + { + data.Temperature = temp; + } + + // 核心频率 + if (MtmlNative.mtmlGpuGetClock(_gpu, out uint clock) == MtmlNative.MTML_SUCCESS) + { + data.Clock = clock; + } + } + + // 功耗 + if (_device != IntPtr.Zero) + { + if (MtmlNative.mtmlDeviceGetPowerUsage(_device, out uint power) == MtmlNative.MTML_SUCCESS) + { + data.Power = power / 1000f; // mW -> W + } + } + + // 显存数据 + if (_memory != IntPtr.Zero) + { + // 已用显存 + if (MtmlNative.mtmlMemoryGetUsed(_memory, out ulong used) == MtmlNative.MTML_SUCCESS) + { + data.VramUsed = used / (1024f * 1024f); // 转换为 MB + + // 计算显存负载 + if (data.VramTotal.HasValue && data.VramTotal > 0) + { + data.VramLoad = (data.VramUsed.Value / data.VramTotal.Value) * 100f; + } + } + + // 显存频率 + if (MtmlNative.mtmlMemoryGetClock(_memory, out uint memClock) == MtmlNative.MTML_SUCCESS) + { + data.VramClock = memClock; + } + } + + // 风扇转速 + if (_device != IntPtr.Zero) + { + if (MtmlNative.mtmlDeviceCountFan(_device, out uint fanCount) == MtmlNative.MTML_SUCCESS && fanCount > 0) + { + if (MtmlNative.mtmlDeviceGetFanRpm(_device, 0, out uint rpm) == MtmlNative.MTML_SUCCESS) + { + data.FanRpm = rpm; + } + } + } + + return data; + } + catch (Exception ex) + { + Debug.WriteLine($"[MTT] Update error: {ex.Message}"); + return null; + } + } + } + + /// + /// 获取指定指标的值 + /// + public float? GetValue(string key) + { + var data = _cachedData; + if (data == null) return null; + + return key switch + { + "GPU.Load" => data.Load, + "GPU.Temp" => data.Temperature, + "GPU.Clock" => data.Clock, + "GPU.Power" => data.Power, + "GPU.VRAM.Used" => data.VramUsed, + "GPU.VRAM.Total" => data.VramTotal, + "GPU.VRAM.Load" => data.VramLoad, + "GPU.VRAM" => data.VramLoad, + "GPU.Fan" => data.FanRpm, + _ => null + }; + } + + public void Dispose() + { + if (_disposed) return; + _disposed = true; + + lock (_lock) + { + // MTML 的资源由库自动管理,不需要显式释放 GPU/Memory 句柄 + // 只需要关闭库 + if (_library != IntPtr.Zero) + { + try { MtmlNative.mtmlLibraryShutDown(_library); } catch { } + _library = IntPtr.Zero; + } + + _device = IntPtr.Zero; + _gpu = IntPtr.Zero; + _memory = IntPtr.Zero; + } + + GC.SuppressFinalize(this); + } + } +} diff --git a/src/UI/HardwareInfoForm.cs b/src/UI/HardwareInfoForm.cs index 920ab74..c189da5 100644 --- a/src/UI/HardwareInfoForm.cs +++ b/src/UI/HardwareInfoForm.cs @@ -179,7 +179,7 @@ private void RebuildTree(string filter) private void AddHardwareNode(TreeNodeCollection parentNodes, IHardware hw, string filter, bool isSearch, bool isFirstHardware) { - string typeStr = GetHardwareTypeString(hw.HardwareType); + string typeStr = GetHardwareTypeStringWithMtt(hw); // ★★★ [修改] 支持摩尔线程 GPU 检测 ★★★ // ★★★ 替换这里:使用强力白名单清洗 ★★★ string cleanName = SanitizeHardwareName(hw.Name); string label = $"{typeStr} {cleanName}"; @@ -302,6 +302,19 @@ private string GetHardwareTypeString(HardwareType type) default: return $"🟢 [{type}]"; } } + + /// + /// ★★★ [新增] 获取硬件类型字符串(支持摩尔线程 GPU 名称检测) ★★★ + /// + private string GetHardwareTypeStringWithMtt(IHardware hw) + { + // 优先检测摩尔线程 GPU + if (HardwareRules.IsMttGpu(hw)) + { + return T("🎮 [MTT GPU]", "🎮 [摩尔线程显卡]"); + } + return GetHardwareTypeString(hw.HardwareType); + } private string GetSensorTypeString(SensorType type) { switch (type) { diff --git a/src/UI/UIController.cs b/src/UI/UIController.cs index edc3258..db7dfc0 100644 --- a/src/UI/UIController.cs +++ b/src/UI/UIController.cs @@ -41,6 +41,18 @@ public UIController(Settings cfg, Form form) _cfg = cfg; _form = form; _mon = new HardwareMonitor(cfg); + + // ★★★ [新增] 监听硬件名称缓存完成事件,刷新布局 ★★★ + // 注意:事件从后台线程触发,需要用 BeginInvoke 封送到 UI 线程 + _mon.OnHardwareNamesCached += () => + { + _form.BeginInvoke(new Action(() => + { + BuildMetrics(); + _layoutDirty = true; + _form.Invalidate(); + })); + }; _layout = new UILayout(ThemeManager.Current); @@ -285,6 +297,28 @@ private void BuildMetrics() string gName = LanguageManager.T(UIUtils.Intern("Groups." + currentGroupKey)); if (_cfg.GroupAliases.ContainsKey(currentGroupKey)) gName = _cfg.GroupAliases[currentGroupKey]; + // ★★★ [新增] CPU/GPU 组使用实际硬件名称(保留 emoji 图标)★★★ + if (currentGroupKey == "CPU") + { + string cpuName = _mon.GetCpuName(); + if (!string.IsNullOrEmpty(cpuName)) + { + // 提取原有 emoji(第一个字符),添加硬件名称 + string emoji = gName.Length > 0 && char.IsSurrogate(gName[0]) ? gName.Substring(0, 2) : + (gName.Length > 0 ? gName[0].ToString() : ""); + gName = emoji + " " + cpuName; + } + } + else if (currentGroupKey == "GPU") + { + string gpuName = _mon.GetGpuName(); + if (!string.IsNullOrEmpty(gpuName)) + { + // GPU 使用和 CPU 一样的 💻 emoji + gName = "💻 " + gpuName; + } + } + gr.Label = gName; _groups.Add(gr); currentGroupList = new List(); @@ -333,6 +367,28 @@ private void BuildMetrics() string gName = LanguageManager.T(UIUtils.Intern("Groups." + currentGroupKey)); if (_cfg.GroupAliases.ContainsKey(currentGroupKey)) gName = _cfg.GroupAliases[currentGroupKey]; + // ★★★ [新增] CPU/GPU 组使用实际硬件名称(保留 emoji 图标)★★★ + if (currentGroupKey == "CPU") + { + string cpuName = _mon.GetCpuName(); + if (!string.IsNullOrEmpty(cpuName)) + { + // 提取原有 emoji(第一个字符),添加硬件名称 + string emoji = gName.Length > 0 && char.IsSurrogate(gName[0]) ? gName.Substring(0, 2) : + (gName.Length > 0 ? gName[0].ToString() : ""); + gName = emoji + " " + cpuName; + } + } + else if (currentGroupKey == "GPU") + { + string gpuName = _mon.GetGpuName(); + if (!string.IsNullOrEmpty(gpuName)) + { + // GPU 使用和 CPU 一样的 💻 emoji + gName = "💻 " + gpuName; + } + } + gr.Label = gName; _groups.Add(gr); }