mirror of
https://github.com/forkless/XPUSYS-Monitor-NG.git
synced 2026-08-16 00:46:37 +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
|
||||
#
|
||||
# Uses Windows built-in typeperf.exe (available since Vista) to query
|
||||
# the \GPU Engine(*)\Utilization Percentage performance counter.
|
||||
# ADDED for Windows-native AMD ROCm support (no rocm_smi_lib).
|
||||
#
|
||||
# typeperf avoids the quoting/escaping headaches of PowerShell -Command
|
||||
# and is available on every Windows system with WDDM drivers.
|
||||
# Two approaches were attempted before settling on typeperf:
|
||||
#
|
||||
# Output format (CSV):
|
||||
# "(PDH-CSV 4.0) (...)", "\\COMPUTER\GPU Engine(*)\Utilization Percentage"
|
||||
# "date time", "val1,val2,val3,..."
|
||||
# 1. PDH ctypes — _PdhQuery (above). Uses PdhAddEnglishCounterW with
|
||||
# wildcard path "\GPU Engine(*)\Utilization Percentage". The wildcard
|
||||
# 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
|
||||
# average them to get total GPU utilisation.
|
||||
# 2. typeperf — This class (below). Windows built-in CLI tool that
|
||||
# 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
|
||||
@@ -381,12 +397,26 @@ class _TypeperfGpuQuery:
|
||||
# ---------------------------------------------------------------------------
|
||||
# amdsmi-based GPU utilisation (official AMD SMI library)
|
||||
#
|
||||
# Uses the AMD SMI Python package which ships with ROCm. Talks directly
|
||||
# to the AMD driver — not through WDDM. Reports real GPU engine utilisation
|
||||
# (GFX, MM, MEM) as percentages 0–100%.
|
||||
# ADDED for Windows-native AMD ROCm support.
|
||||
#
|
||||
# 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
|
||||
# Requires: ROCm 6+ (user has ROCm 7.2)
|
||||
# Requires: ROCm 6+ (ROCm 7.2 on the tested configuration)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
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:
|
||||
return 0.0, 0.0, 0.0
|
||||
@@ -187,8 +199,20 @@ class AMDProvider(BaseGPUProvider):
|
||||
"""
|
||||
Return GPU utilisation %.
|
||||
|
||||
Tries amdsmi (official AMD SMI, bypasses WDDM).
|
||||
Falls back to typeperf (WDDM counters, best-effort).
|
||||
REPLACES: upstream rocm_smi.getGpuBusyVdev(0)
|
||||
|
||||
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:
|
||||
return self._as_gpu.read_gpu_utilization()
|
||||
@@ -200,7 +224,12 @@ class AMDProvider(BaseGPUProvider):
|
||||
"""
|
||||
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
|
||||
|
||||
@@ -208,7 +237,14 @@ class AMDProvider(BaseGPUProvider):
|
||||
"""
|
||||
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
|
||||
|
||||
@@ -216,7 +252,13 @@ class AMDProvider(BaseGPUProvider):
|
||||
"""
|
||||
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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user