mirror of
https://github.com/forkless/XPUSYS-Monitor-NG.git
synced 2026-08-16 08:46:38 +02:00
docs: add CHANGELOG.md + comprehensive inline code comments
This commit is contained in:
+148
@@ -0,0 +1,148 @@
|
|||||||
|
# Changelog — XPUSYS-Monitor-NG
|
||||||
|
|
||||||
|
All notable changes for the Windows-native AMD ROCm port are documented here.
|
||||||
|
|
||||||
|
This fork is based on [ComfyUI-XPUSYS-Monitor](https://github.com/allanmeng/ComfyUI-XPUSYS-Monitor)
|
||||||
|
v1.0.3 by allanmeng. The version below tracks deviations from that baseline.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v0.1.0 — 2026-06-08
|
||||||
|
|
||||||
|
### Intent
|
||||||
|
|
||||||
|
The upstream AMD provider (`providers/amd.py`) relied on `rocm_smi_lib` — a
|
||||||
|
Linux-only Python package that wraps `librocm_smi64.so`. On Windows with AMD
|
||||||
|
ROCm, `pip install rocm_smi_lib` fails because the native `.so` library does
|
||||||
|
not exist. This port replaces every `rocm_smi` call with a Windows-native
|
||||||
|
alternative.
|
||||||
|
|
||||||
|
### Detection: `providers/__init__.py`
|
||||||
|
|
||||||
|
**Problem:** The upstream detection function `_is_amd_rocme()` accessed
|
||||||
|
`torch.version.roc` directly. On Windows AMD ROCm builds (tested with PyTorch
|
||||||
|
2.9.1+rocm7.2.1), the `roc` attribute **does not exist** on the
|
||||||
|
`torch.version` module — raising `AttributeError`. The outer `try/except`
|
||||||
|
caught it and returned `False`, causing the auto-detector to load
|
||||||
|
`NvidiaProvider` instead of `AMDProvider`. The GPU name fallback code after
|
||||||
|
the `roc` check was unreachable.
|
||||||
|
|
||||||
|
**Fix:** Replaced bare attribute access with `getattr(torch.version, 'roc',
|
||||||
|
None)`. Added a secondary signal `getattr(torch.version, 'hip', None)` for
|
||||||
|
HIP-based detection. Added a tertiary fallback scanning the GPU device name
|
||||||
|
(via `torch.cuda.get_device_name(0)`) for the markers `"amd"`, `"radeon"`,
|
||||||
|
or `"advanced micro devices"`.
|
||||||
|
|
||||||
|
### VRAM: `providers/amd.py` → `_read_vram()`
|
||||||
|
|
||||||
|
**Problem:** The upstream AMD provider used `rocm_smi.getMemFreeVdev(0)`,
|
||||||
|
`rocm_smi.getMemSizeVdev(0)`, and `rocm_smi.getMemUsedVdev(0)` for
|
||||||
|
driver-level VRAM reads. Without `rocm_smi_lib`, the fallback returned only
|
||||||
|
total VRAM from `torch.cuda.get_device_properties(0).total_memory`, leaving
|
||||||
|
`free` and `driver_used` as `0.0` — making the PRED predictor and VRAM
|
||||||
|
capsule unusable.
|
||||||
|
|
||||||
|
**Fix:** Replaced all three `rocm_smi` VRAM calls with
|
||||||
|
`torch.cuda.mem_get_info(device_index)`, which returns `(free_bytes,
|
||||||
|
total_bytes)` from the AMD driver on ROCm 6+ for Windows. Moved the call
|
||||||
|
inside a `try/except` with a fallback to `get_device_properties().total_memory`
|
||||||
|
if `mem_get_info` is unavailable. Added `torch.cuda.synchronize()` before
|
||||||
|
reads to force CUDA/HIP context creation (some ROCm builds defer context
|
||||||
|
init until the first GPU operation, returning zeros otherwise).
|
||||||
|
|
||||||
|
### GPU Load: `providers/amd.py` → `_read_gpu_load()`
|
||||||
|
|
||||||
|
**Problem:** The upstream used `rocm_smi.getGpuBusyVdev(0)`. No standard
|
||||||
|
Python-accessible equivalent exists on Windows AMD.
|
||||||
|
|
||||||
|
**Solution attempted — PDH (ctypes):** Added `_PdhQuery` to
|
||||||
|
`providers/_utils.py` using `ctypes` wrappers around `pdh.dll` to query
|
||||||
|
`\GPU Engine(*)\Utilization Percentage`. The wildcard counter path does not
|
||||||
|
aggregate correctly with `PdhGetFormattedCounterValue` (returns only the
|
||||||
|
first matching instance). This approach was disabled for AMD in favour of
|
||||||
|
typeperf.
|
||||||
|
|
||||||
|
**Solution adopted — typeperf:** Added `_TypeperfGpuQuery` to
|
||||||
|
`providers/_utils.py`. Uses Windows built-in `typeperf.exe` (available since
|
||||||
|
Vista) with the same counter path `\GPU Engine(*)\Utilization Percentage`.
|
||||||
|
Output is CSV; we parse columns after the timestamp and take `max()` across
|
||||||
|
all engine instances. Averaging would dilute the signal (hundreds of engine
|
||||||
|
columns including idle video/copy/timer). `max()` correctly reflects the
|
||||||
|
busiest engine (typically 3D or Compute during a ComfyUI workflow). At idle
|
||||||
|
all engines report ~0%, so the capsule drops cleanly.
|
||||||
|
|
||||||
|
**Attempted — amdsmi:** Added `_AmdSmiGpuQuery` to `providers/_utils.py`
|
||||||
|
using the official AMD SMI Python library (`pip install amdsmi`). On Windows
|
||||||
|
the library searches for `libamd_smi.so` (a Linux shared object) at
|
||||||
|
`D:\opt\rocm\lib\`, which does not exist on the tested configuration. The
|
||||||
|
class logs a single info line and gracefully skips if `amdsmi` is not
|
||||||
|
installed or fails to load.
|
||||||
|
|
||||||
|
### GPU Frequency / Temperature / Power: `providers/amd.py`
|
||||||
|
|
||||||
|
**Problem:** The upstream used `rocm_smi.getSingleClockSpeed(0)`,
|
||||||
|
`rocm_smi.getTempVdev(0)`, `rocm_smi.getPowerVdev(0)`, and
|
||||||
|
`rocm_smi.getPowerCapVdev(0)` for clock speed, temperature, and power draw.
|
||||||
|
The AMD Windows WDDM driver on the RX 9070 XT does not register these
|
||||||
|
performance counters through any standard Python-accessible interface.
|
||||||
|
|
||||||
|
**Resolution:** All three return sentinel values matching the `GPUSnapshot`
|
||||||
|
contract defaults — `0.0` for frequency, `-1.0` for temperature,
|
||||||
|
`(-1.0, 0.0, False)` for power. The frontend displays these as unavailable
|
||||||
|
(`--` / greyed out), identical behaviour to when the Intel provider cannot
|
||||||
|
open Level Zero handles or the NVIDIA provider cannot reach pynvml.
|
||||||
|
|
||||||
|
### Shared Utilities: `providers/_utils.py` (new file)
|
||||||
|
|
||||||
|
**Problem:** The upstream AMD provider imported system-level CPU and RAM
|
||||||
|
utility functions (`_get_cpu_info`, `_read_cpu_ram_stats`,
|
||||||
|
`_read_commit_charge`) from `providers/intel.py`. This created a spurious
|
||||||
|
dependency on the Intel Level Zero provider code for non-Intel users.
|
||||||
|
|
||||||
|
**Fix:** Extracted these three functions plus `_is_admin()` into a new shared
|
||||||
|
module `providers/_utils.py`. Also relocated `_PdhQuery`, `_TypeperfGpuQuery`,
|
||||||
|
and `_AmdSmiGpuQuery` into the same module. Both `amd.py` and `nvidia.py`
|
||||||
|
now import from `_utils.py` instead of `intel.py`. The `intel.py` module is
|
||||||
|
no longer needed unless the Intel provider is loaded (auto-detection
|
||||||
|
fallback path).
|
||||||
|
|
||||||
|
### NVIDIA Provider: `providers/nvidia.py`
|
||||||
|
|
||||||
|
**Change:** Updated import path from `from .intel import ...` to
|
||||||
|
`from ._utils import ...`. No functional change — identical utility
|
||||||
|
functions.
|
||||||
|
|
||||||
|
### Frontend: `web/xpu_monitor.js`
|
||||||
|
|
||||||
|
**Problem:** The `__init__.py` declares `WEB_DIRECTORY = "./web"` which
|
||||||
|
tells ComfyUI to serve the JavaScript toolbar extension from a `web/`
|
||||||
|
subdirectory. This directory was not included in the initial workspace,
|
||||||
|
causing the toolbar capsules to not render.
|
||||||
|
|
||||||
|
**Fix:** Added `web/xpu_monitor.js` (56 KB, identical to upstream v1.0.3).
|
||||||
|
The file is the full JavaScript frontend that renders the seven-capsule
|
||||||
|
status bar, handles WebSocket updates from the backend, and provides the
|
||||||
|
VRAM predictor UI. No modifications were made.
|
||||||
|
|
||||||
|
### Dependencies: `requirements.txt`
|
||||||
|
|
||||||
|
**Change:** `rocm_smi_lib` commented out with an explanatory note. No
|
||||||
|
replacement dependency added — VRAM reads use `torch.cuda` (bundled with
|
||||||
|
the ROCm PyTorch installation), GPU load reads use `typeperf` (Windows
|
||||||
|
built-in), and CPU/RAM reads use `psutil` (already required by the upstream).
|
||||||
|
|
||||||
|
### Project Metadata: `pyproject.toml`
|
||||||
|
|
||||||
|
**Changes:**
|
||||||
|
- Repository URL updated to `https://github.com/forkless/XPUSYS-Monitor-NG`
|
||||||
|
- Display name set to `XPUSYS-Monitor-NG`
|
||||||
|
- Description updated to reflect the POC nature
|
||||||
|
- Publisher ID set to `forkless`
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- `README.md` — rewritten for the fork with POC context, status table,
|
||||||
|
relationship to upstream, tested hardware, support disclaimer, MIT license
|
||||||
|
- `AMD.md` — detailed technical summary of every change, intent, and end
|
||||||
|
result table
|
||||||
|
- `LICENSE.md` — MIT license (matches upstream)
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 forkless
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
+43
-13
@@ -277,18 +277,34 @@ class _PdhQuery:
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# typeperf-based GPU utilisation fallback
|
# typeperf-based GPU utilisation fallback
|
||||||
#
|
#
|
||||||
# Uses Windows built-in typeperf.exe (available since Vista) to query
|
# ADDED for Windows-native AMD ROCm support (no rocm_smi_lib).
|
||||||
# the \GPU Engine(*)\Utilization Percentage performance counter.
|
|
||||||
#
|
#
|
||||||
# typeperf avoids the quoting/escaping headaches of PowerShell -Command
|
# Two approaches were attempted before settling on typeperf:
|
||||||
# and is available on every Windows system with WDDM drivers.
|
|
||||||
#
|
#
|
||||||
# Output format (CSV):
|
# 1. PDH ctypes — _PdhQuery (above). Uses PdhAddEnglishCounterW with
|
||||||
# "(PDH-CSV 4.0) (...)", "\\COMPUTER\GPU Engine(*)\Utilization Percentage"
|
# wildcard path "\GPU Engine(*)\Utilization Percentage". The wildcard
|
||||||
# "date time", "val1,val2,val3,..."
|
# expands to hundreds of per-process engine instances, but
|
||||||
|
# PdhGetFormattedCounterValue on a wildcard handle returns only the
|
||||||
|
# first matching instance, not an aggregate. This approach is disabled
|
||||||
|
# for AMD in favour of typeperf.
|
||||||
#
|
#
|
||||||
# We parse the second line, split the comma-separated values, and
|
# 2. typeperf — This class (below). Windows built-in CLI tool that
|
||||||
# average them to get total GPU utilisation.
|
# accepts the same counter path and returns CSV with one column per
|
||||||
|
# engine instance. Every column after the timestamp is a separate
|
||||||
|
# engine value. We parse all columns and take max()—not average—because
|
||||||
|
# with hundreds of engines (video decode, copy, timer, security, etc.)
|
||||||
|
# all reporting 0 at idle, averaging dilutes the real signal from the
|
||||||
|
# few active 3D/Compute engines during a workflow.
|
||||||
|
#
|
||||||
|
# 3. amdsmi — _AmdSmiGpuQuery (further below). Official AMD SMI
|
||||||
|
# library. Gracefully skipped on Windows because the PyPI package
|
||||||
|
# searches for libamd_smi.so (Linux-only).
|
||||||
|
#
|
||||||
|
# Output format (typeperf CSV):
|
||||||
|
# Line 1: "(PDH-CSV 4.0)","\\PC\GPU Engine(pid_..._engtype_3D)\...", ...
|
||||||
|
# Line 2: "date time","0.000000","1.299634","0.000000", ...
|
||||||
|
#
|
||||||
|
# REPLACES: upstream rocm_smi.getGpuBusyVdev(0)
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
import csv as _csv
|
import csv as _csv
|
||||||
@@ -381,12 +397,26 @@ class _TypeperfGpuQuery:
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# amdsmi-based GPU utilisation (official AMD SMI library)
|
# amdsmi-based GPU utilisation (official AMD SMI library)
|
||||||
#
|
#
|
||||||
# Uses the AMD SMI Python package which ships with ROCm. Talks directly
|
# ADDED for Windows-native AMD ROCm support.
|
||||||
# to the AMD driver — not through WDDM. Reports real GPU engine utilisation
|
#
|
||||||
# (GFX, MM, MEM) as percentages 0–100%.
|
# The official AMD SMI Python package (pip install amdsmi) provides direct
|
||||||
|
# driver-level GPU metrics — engine utilisation (GFX, MM, MEM), temperature,
|
||||||
|
# power, clock speed — without going through WDDM performance counters.
|
||||||
|
#
|
||||||
|
# On Windows, the PyPI package's ctypes wrapper searches for the native
|
||||||
|
# library at a hardcoded Linux path (libamd_smi.so via ctypes.CDLL).
|
||||||
|
# Windows DLLs use different filenames and search paths, so the import
|
||||||
|
# fails with KeyError: 'libamd_smi.so' on a standard Windows ROCm install.
|
||||||
|
#
|
||||||
|
# This class uses try/except ImportError to gracefully skip when the
|
||||||
|
# package is not installed or the native library cannot be loaded. No
|
||||||
|
# crash, no stack trace — just a single info-line in the log.
|
||||||
|
#
|
||||||
|
# If AMD releases an official Windows-compatible amdsmi wheel in the
|
||||||
|
# future, this class will activate automatically without code changes.
|
||||||
#
|
#
|
||||||
# Install: pip install amdsmi
|
# Install: pip install amdsmi
|
||||||
# Requires: ROCm 6+ (user has ROCm 7.2)
|
# Requires: ROCm 6+ (ROCm 7.2 on the tested configuration)
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
class _AmdSmiGpuQuery:
|
class _AmdSmiGpuQuery:
|
||||||
|
|||||||
+48
-6
@@ -122,8 +122,20 @@ class AMDProvider(BaseGPUProvider):
|
|||||||
"""
|
"""
|
||||||
Return (free_gb, total_gb, driver_used_gb) via torch.cuda.mem_get_info.
|
Return (free_gb, total_gb, driver_used_gb) via torch.cuda.mem_get_info.
|
||||||
|
|
||||||
|
REPLACES: upstream rocm_smi.getMemFreeVdev(0),
|
||||||
|
rocm_smi.getMemSizeVdev(0),
|
||||||
|
rocm_smi.getMemUsedVdev(0)
|
||||||
|
|
||||||
|
torch.cuda.mem_get_info() returns (free_bytes, total_bytes) from
|
||||||
|
the AMD HIP driver on ROCm 6+ for Windows. This is the same function
|
||||||
|
used by NVIDIA CUDA — AMD ROCm's HIP runtime implements the same
|
||||||
|
CUDA API surface, so it works without any AMD-specific library.
|
||||||
|
|
||||||
Falls back to get_device_properties if mem_get_info is unavailable.
|
Falls back to get_device_properties if mem_get_info is unavailable.
|
||||||
Forces CUDA context init to ensure device queries succeed.
|
Forces CUDA context init (torch.cuda.synchronize) before reading;
|
||||||
|
some ROCm builds defer HIP context creation until the first GPU
|
||||||
|
operation, and mem_get_info() returns (0, 0) without an active
|
||||||
|
context.
|
||||||
"""
|
"""
|
||||||
if not self._torch_ok:
|
if not self._torch_ok:
|
||||||
return 0.0, 0.0, 0.0
|
return 0.0, 0.0, 0.0
|
||||||
@@ -187,8 +199,20 @@ class AMDProvider(BaseGPUProvider):
|
|||||||
"""
|
"""
|
||||||
Return GPU utilisation %.
|
Return GPU utilisation %.
|
||||||
|
|
||||||
Tries amdsmi (official AMD SMI, bypasses WDDM).
|
REPLACES: upstream rocm_smi.getGpuBusyVdev(0)
|
||||||
Falls back to typeperf (WDDM counters, best-effort).
|
|
||||||
|
Two-layer fallback chain:
|
||||||
|
1. amdsmi — official AMD SMI library (pip install amdsmi).
|
||||||
|
Bypasses WDDM, talks directly to the AMD driver.
|
||||||
|
Gracefully skipped on Windows because the PyPI
|
||||||
|
package searches for libamd_smi.so (Linux-only).
|
||||||
|
2. typeperf — Windows built-in (available since Vista).
|
||||||
|
Reads \\GPU Engine(*)\\Utilization Percentage
|
||||||
|
via WDDM performance counters. Returns CSV with
|
||||||
|
one column per engine instance (3D, Compute,
|
||||||
|
Copy, Video, Timer, etc.). We use max() across
|
||||||
|
all engines — averaging dilutes the signal
|
||||||
|
across hundreds of idle engine types.
|
||||||
"""
|
"""
|
||||||
if self._as_gpu_ok:
|
if self._as_gpu_ok:
|
||||||
return self._as_gpu.read_gpu_utilization()
|
return self._as_gpu.read_gpu_utilization()
|
||||||
@@ -200,7 +224,12 @@ class AMDProvider(BaseGPUProvider):
|
|||||||
"""
|
"""
|
||||||
GPU core frequency in MHz.
|
GPU core frequency in MHz.
|
||||||
|
|
||||||
Unavailable on Windows without vendor driver API.
|
REPLACES: upstream rocm_smi.getSingleClockSpeed(0)
|
||||||
|
|
||||||
|
The AMD WDDM driver on Windows does not expose GPU core clock
|
||||||
|
through any standard Python-accessible interface (no PDH counter,
|
||||||
|
no WMI class, no torch.cuda equivalent). Returns 0 (unavailable
|
||||||
|
sentinel matching the GPUSnapshot contract default).
|
||||||
"""
|
"""
|
||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
@@ -208,7 +237,14 @@ class AMDProvider(BaseGPUProvider):
|
|||||||
"""
|
"""
|
||||||
GPU core temperature in C.
|
GPU core temperature in C.
|
||||||
|
|
||||||
Unavailable on Windows without vendor driver API.
|
REPLACES: upstream rocm_smi.getTempVdev(0)
|
||||||
|
|
||||||
|
The AMD WDDM driver on the tested configuration (RX 9070 XT,
|
||||||
|
ROCm 7.2, Windows) does not register a GPU temperature performance
|
||||||
|
counter. Tested: typeperf -q "GPU Adapter" returned "object not
|
||||||
|
found". Returns -1 (unavailable sentinel). Some AMD cards on newer
|
||||||
|
driver versions or different Windows builds may expose this through
|
||||||
|
WMI or PDH — this is hardware/driver-dependent.
|
||||||
"""
|
"""
|
||||||
return -1.0
|
return -1.0
|
||||||
|
|
||||||
@@ -216,7 +252,13 @@ class AMDProvider(BaseGPUProvider):
|
|||||||
"""
|
"""
|
||||||
Return (power_w, tgp_w, power_available).
|
Return (power_w, tgp_w, power_available).
|
||||||
|
|
||||||
Unavailable on Windows without vendor driver API.
|
REPLACES: upstream rocm_smi.getPowerVdev(0) and
|
||||||
|
rocm_smi.getPowerCapVdev(0)
|
||||||
|
|
||||||
|
GPU power monitoring is not exposed through Windows standard APIs
|
||||||
|
on the tested AMD driver. Returns (-1.0, 0.0, False) — the
|
||||||
|
GPUSnapshot power_available=False tells the frontend to grey out
|
||||||
|
the PWR capsule.
|
||||||
"""
|
"""
|
||||||
return -1.0, 0.0, False
|
return -1.0, 0.0, False
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user