From 92ca373e8e8e0a3f21ca4dde083a4eab3985c4e2 Mon Sep 17 00:00:00 2001 From: DasPauluteli <67437654+DasPauluteli@users.noreply.github.com> Date: Fri, 13 Mar 2026 21:22:46 +0100 Subject: [PATCH 1/3] Add APU unified memory (GTT) support for Strix Halo and similar APUs - Track GTT pool (system RAM mapped for GPU use via AMD TTM) alongside VRAM - Auto-detect APUs by device name or size heuristic (GTT >= 4x VRAM < 8GB) - Show purple "Unified RAM (GTT)" bar in UI when APU is detected - Relabel VRAM bar as "reserved pool" on APUs to avoid misleading reads - Include is_apu, gtt_used/total/percent, device_name in WebSocket payload - Update README with APU/unified memory section and Strix Halo notes Co-Authored-By: Claude Sonnet 4.6 --- README.md | 20 ++++++-- __init__.py | 74 ++++++++++++++++++++++++--- web/monitor.js | 133 +++++++++++++++++++++++++++++++------------------ 3 files changed, 169 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 10d0756..b47d947 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # AMD GPU Monitor for ComfyUI -A simple, lightweight AMD GPU monitoring tool for ComfyUI that displays real-time information about your AMD GPU directly in the UI. +A simple, lightweight AMD GPU monitoring tool for ComfyUI that displays real-time information about your AMD GPU directly in the UI. Supports both discrete AMD GPUs and APUs with unified memory (e.g. Strix Halo / gfx1151). ![AMD GPU Monitor Screenshot](https://github.com/iDAPPA/ComfyUI-AMDGPUMonitor/raw/main/screenshot.png) @@ -8,12 +8,14 @@ A simple, lightweight AMD GPU monitoring tool for ComfyUI that displays real-tim - Real-time GPU utilization monitoring (%) - VRAM usage tracking (both in MB/GB and percentage) +- **GTT / Unified RAM tracking for APUs** — dynamically allocated system RAM used as GPU memory (shown automatically on APUs like Strix Halo) - GPU temperature monitoring (°C) +- Automatic APU detection — relabels VRAM as "reserved pool" and shows the GTT pool as "Unified RAM" for APUs - Color-coded indicators (blue for low, orange for medium, red for high usage) - Draggable, collapsible, and closable UI - Position persistence between sessions -- Works with ROCm-enabled GPUs -- Specifically tested with AMD Radeon RX 7900 XTX +- Works with ROCm-enabled GPUs and APUs +- Tested with AMD Radeon RX 7900 XTX (discrete) and Strix Halo APU (gfx1151) ## Installation @@ -42,9 +44,19 @@ No setup is required. Once installed, the monitor will automatically appear in t - **Collapse/Expand**: Click the "−" button to collapse the monitor to just the title bar - **Close**: Click the "×" button to close the monitor (a "Show AMD GPU Monitor" button will appear to bring it back) +## APU / Unified Memory Support + +On AMD APUs with dynamically allocated unified memory (e.g. **Strix Halo / gfx1151**, Phoenix, Hawk Point, Rembrandt, etc.), the GPU does not have a fixed VRAM pool. Instead, it uses system RAM via the **GTT (Graphics Translation Table)** pool managed by the kernel's TTM subsystem. + +The monitor detects this automatically and: + +- Shows a **"Unified RAM (GTT)"** bar (purple) representing the system RAM currently mapped for GPU use +- Relabels the VRAM bar as **"VRAM (reserved pool)"** to clarify it is just a small pre-allocated chunk, not the full available memory +- APU detection uses device name matching (Strix, Phoenix, Hawk Point, etc.) and a size heuristic (GTT ≥ 4× VRAM and VRAM < 8 GB) + ## How It Works -This extension uses the `rocm-smi` command-line tool to collect GPU information and displays it in a floating UI element in the ComfyUI interface. It does not affect the performance of ComfyUI or your GPU. +This extension polls `rocm-smi` (or `amd-smi`) in a background thread to collect GPU information and pushes updates to the frontend via WebSocket. The floating UI is updated in real time and does not affect ComfyUI or GPU performance. ## Troubleshooting diff --git a/__init__.py b/__init__.py index 9ffd20c..7e04cdb 100644 --- a/__init__.py +++ b/__init__.py @@ -13,7 +13,12 @@ "vram_used": 0, "vram_total": 0, "vram_used_percent": 0, + "gtt_used": 0, + "gtt_total": 0, + "gtt_used_percent": 0, "gpu_temperature": 0, + "is_apu": False, + "device_name": "", "last_update": 0 } @@ -94,24 +99,70 @@ def get_gpu_info(rocm_smi_path): except: pass + # Get device name for APU detection + try: + info = run_rocm_smi_command(rocm_smi_path, '--showproductname', '--json') + if isinstance(info, dict) and 'card0' in info: + card_info = info['card0'] + for key in ('Card Series', 'Card Model', 'Card Vendor', 'Card SKU'): + if key in card_info: + gpu_stats["device_name"] = str(card_info[key]) + break + except: + pass + # Get VRAM information try: info = run_rocm_smi_command(rocm_smi_path, '--showmeminfo', 'vram', '--json') if isinstance(info, dict) and 'card0' in info: card_info = info['card0'] # Use first GPU - + # Parse the B (bytes) format ROCm 5.x/6.x uses if 'VRAM Total Memory (B)' in card_info and 'VRAM Total Used Memory (B)' in card_info: vram_total_bytes = int(card_info['VRAM Total Memory (B)']) vram_used_bytes = int(card_info['VRAM Total Used Memory (B)']) - + # Convert to MB for display vram_total = vram_total_bytes / (1024 * 1024) vram_used = vram_used_bytes / (1024 * 1024) - + gpu_stats["vram_total"] = int(vram_total) gpu_stats["vram_used"] = int(vram_used) - gpu_stats["vram_used_percent"] = int((vram_used / vram_total) * 100) + gpu_stats["vram_used_percent"] = int((vram_used / vram_total) * 100) if vram_total > 0 else 0 + except: + pass + + # Get GTT (unified system RAM) information — critical for APUs like Strix Halo + try: + info = run_rocm_smi_command(rocm_smi_path, '--showmeminfo', 'gtt', '--json') + if isinstance(info, dict) and 'card0' in info: + card_info = info['card0'] + if 'GTT Total Memory (B)' in card_info and 'GTT Total Used Memory (B)' in card_info: + gtt_total_bytes = int(card_info['GTT Total Memory (B)']) + gtt_used_bytes = int(card_info['GTT Total Used Memory (B)']) + + gtt_total = gtt_total_bytes / (1024 * 1024) + gtt_used = gtt_used_bytes / (1024 * 1024) + + gpu_stats["gtt_total"] = int(gtt_total) + gpu_stats["gtt_used"] = int(gtt_used) + gpu_stats["gtt_used_percent"] = int((gtt_used / gtt_total) * 100) if gtt_total > 0 else 0 + except: + pass + + # Detect APU: GTT pool significantly larger than VRAM pool indicates unified memory architecture. + # Also catches explicit device name patterns (gfx1151 = Strix Halo, etc.) + try: + vram_total = gpu_stats["vram_total"] + gtt_total = gpu_stats["gtt_total"] + device_name = gpu_stats["device_name"].lower() + apu_name_hints = ("radeon 890m", "radeon 780m", "radeon 760m", "radeon 740m", + "strix", "phoenix", "hawk point", "mendocino", "rembrandt", + "raphael", "dragon range", "barcelo", "cezanne") + name_match = any(hint in device_name for hint in apu_name_hints) + # Heuristic: if GTT is at least 4x VRAM and VRAM < 8 GB, likely APU + size_match = gtt_total > 0 and vram_total > 0 and (gtt_total >= vram_total * 4) and vram_total < 8192 + gpu_stats["is_apu"] = bool(name_match or size_match) except: pass @@ -147,7 +198,12 @@ def send_monitor_update(): 'gpu_temperature': gpu_stats['gpu_temperature'], 'vram_total': gpu_stats['vram_total'], 'vram_used': gpu_stats['vram_used'], - 'vram_used_percent': gpu_stats['vram_used_percent'] + 'vram_used_percent': gpu_stats['vram_used_percent'], + 'gtt_total': gpu_stats['gtt_total'], + 'gtt_used': gpu_stats['gtt_used'], + 'gtt_used_percent': gpu_stats['gtt_used_percent'], + 'is_apu': gpu_stats['is_apu'], + 'device_name': gpu_stats['device_name'], }] } @@ -233,7 +289,13 @@ def monitor_gpu(self, update_interval): monitor_update_interval = update_interval # Return current stats as a string for debugging - stats = f"GPU: {gpu_stats['gpu_utilization']}% | VRAM: {gpu_stats['vram_used']}MB/{gpu_stats['vram_total']}MB ({gpu_stats['vram_used_percent']}%) | Temp: {gpu_stats['gpu_temperature']}°C" + apu_tag = " [APU]" if gpu_stats['is_apu'] else "" + stats = ( + f"GPU{apu_tag}: {gpu_stats['gpu_utilization']}% | " + f"VRAM: {gpu_stats['vram_used']}MB/{gpu_stats['vram_total']}MB ({gpu_stats['vram_used_percent']}%) | " + f"GTT: {gpu_stats['gtt_used']}MB/{gpu_stats['gtt_total']}MB ({gpu_stats['gtt_used_percent']}%) | " + f"Temp: {gpu_stats['gpu_temperature']}°C" + ) return (stats,) # Register our node when this script is imported diff --git a/web/monitor.js b/web/monitor.js index afd0c5c..56214b2 100644 --- a/web/monitor.js +++ b/web/monitor.js @@ -137,9 +137,48 @@ const createMonitorElement = () => { vramSection.appendChild(vramBarContainer); content.appendChild(vramSection); + // GTT / Unified RAM section (shown for APUs with dynamic unified memory) + const gttSection = document.createElement("div"); + gttSection.style.marginBottom = "8px"; + gttSection.style.display = "none"; // hidden until we know it's an APU + + const gttLabel = document.createElement("div"); + gttLabel.className = "amd-gtt-label"; + gttLabel.textContent = "Unified RAM (GTT):"; + gttLabel.style.marginBottom = "2px"; + + const gttBarContainer = document.createElement("div"); + gttBarContainer.style.height = "15px"; + gttBarContainer.style.backgroundColor = "#333"; + gttBarContainer.style.borderRadius = "3px"; + gttBarContainer.style.position = "relative"; + + const gttBar = document.createElement("div"); + gttBar.className = "amd-gtt-bar"; + gttBar.style.height = "100%"; + gttBar.style.width = "0%"; + gttBar.style.backgroundColor = "#a78bfa"; // Purple to distinguish from VRAM + gttBar.style.borderRadius = "3px"; + gttBar.style.transition = "width 0.5s ease-out, background-color 0.3s"; + + const gttText = document.createElement("div"); + gttText.className = "amd-gtt-text"; + gttText.textContent = "0MB / 0MB (0%)"; + gttText.style.position = "absolute"; + gttText.style.top = "0"; + gttText.style.left = "5px"; + gttText.style.lineHeight = "15px"; + gttText.style.textShadow = "1px 1px 1px #000"; + + gttBarContainer.appendChild(gttBar); + gttBarContainer.appendChild(gttText); + gttSection.appendChild(gttLabel); + gttSection.appendChild(gttBarContainer); + content.appendChild(gttSection); + // Temperature section const tempSection = document.createElement("div"); - + const tempLabel = document.createElement("div"); tempLabel.textContent = "GPU Temperature:"; tempLabel.style.marginBottom = "2px"; @@ -304,80 +343,78 @@ const createMonitorElement = () => { // Initial visibility check updateShowButtonVisibility(); - return { container, gpuBar, gpuText, vramBar, vramText, tempBar, tempText }; + return { container, gpuBar, gpuText, vramBar, vramText, vramLabel, vramSection, gttBar, gttText, gttSection, gttLabel, tempBar, tempText }; +}; + +const formatMemText = (usedMB, totalMB, percent) => { + if (totalMB >= 1024) { + return `${(usedMB / 1024).toFixed(1)}GB / ${(totalMB / 1024).toFixed(1)}GB (${percent}%)`; + } + return `${usedMB}MB / ${totalMB}MB (${percent}%)`; +}; + +const barColor = (percent, low = '#47a0ff', mid = '#ffad33', high = '#ff4d4d', midThresh = 70, highThresh = 85) => { + if (percent > highThresh) return high; + if (percent > midThresh) return mid; + return low; }; // Update the monitor UI with new data const updateMonitorUI = (monitor, data) => { // Check if we have GPU data if (!data || !data.gpus || data.gpus.length === 0) return; - + const gpu = data.gpus[0]; // Use the first GPU - + const isAPU = !!gpu.is_apu; + // Update GPU utilization if (monitor.gpuBar && monitor.gpuText) { const utilization = gpu.gpu_utilization || 0; monitor.gpuBar.style.width = `${utilization}%`; monitor.gpuText.textContent = `${utilization}%`; - - // Change color based on utilization - if (utilization > 80) { - monitor.gpuBar.style.backgroundColor = '#ff4d4d'; // Red for high - } else if (utilization > 50) { - monitor.gpuBar.style.backgroundColor = '#ffad33'; // Orange for medium - } else { - monitor.gpuBar.style.backgroundColor = '#47a0ff'; // Blue for low - } + monitor.gpuBar.style.backgroundColor = barColor(utilization, '#47a0ff', '#ffad33', '#ff4d4d', 50, 80); } - - // Update VRAM usage + + // Update VRAM usage — on APUs this is a small pre-allocated pool; label accordingly if (monitor.vramBar && monitor.vramText) { const vramPercent = gpu.vram_used_percent || 0; const vramUsed = gpu.vram_used || 0; const vramTotal = gpu.vram_total || 1; - + monitor.vramBar.style.width = `${vramPercent}%`; - - // Format the text to show MB or GB - let vramUsedText = vramUsed; - let vramTotalText = vramTotal; - let unit = 'MB'; - - if (vramTotal >= 1024) { - vramUsedText = (vramUsed / 1024).toFixed(1); - vramTotalText = (vramTotal / 1024).toFixed(1); - unit = 'GB'; + monitor.vramText.textContent = formatMemText(vramUsed, vramTotal, vramPercent); + monitor.vramBar.style.backgroundColor = barColor(vramPercent); + + // On APUs, VRAM is just the small reserved pool — label it clearly + if (monitor.vramLabel) { + monitor.vramLabel.textContent = isAPU ? "VRAM (reserved pool):" : "VRAM Usage:"; } - - monitor.vramText.textContent = `${vramUsedText}${unit} / ${vramTotalText}${unit} (${vramPercent}%)`; - - // Change color based on VRAM usage - if (vramPercent > 85) { - monitor.vramBar.style.backgroundColor = '#ff4d4d'; // Red for high - } else if (vramPercent > 70) { - monitor.vramBar.style.backgroundColor = '#ffad33'; // Orange for medium - } else { - monitor.vramBar.style.backgroundColor = '#47a0ff'; // Blue for low + } + + // Show/update GTT (unified RAM) section for APUs + if (monitor.gttSection) { + const hasGTT = (gpu.gtt_total || 0) > 0; + monitor.gttSection.style.display = (isAPU && hasGTT) ? "block" : "none"; + + if (isAPU && hasGTT && monitor.gttBar && monitor.gttText) { + const gttPercent = gpu.gtt_used_percent || 0; + const gttUsed = gpu.gtt_used || 0; + const gttTotal = gpu.gtt_total || 1; + + monitor.gttBar.style.width = `${gttPercent}%`; + monitor.gttText.textContent = formatMemText(gttUsed, gttTotal, gttPercent); + // Purple baseline; warn orange/red as system RAM fills up + monitor.gttBar.style.backgroundColor = barColor(gttPercent, '#a78bfa', '#ffad33', '#ff4d4d'); } } - + // Update temperature if (monitor.tempBar && monitor.tempText) { const temp = gpu.gpu_temperature || 0; - - // Assume max reasonable temp is 100°C for the progress bar const tempPercent = Math.min(temp, 100); monitor.tempBar.style.width = `${tempPercent}%`; monitor.tempText.textContent = `${temp}°C`; - - // Change color based on temperature - if (temp > 80) { - monitor.tempBar.style.backgroundColor = '#ff4d4d'; // Red for high - } else if (temp > 60) { - monitor.tempBar.style.backgroundColor = '#ffad33'; // Orange for medium - } else { - monitor.tempBar.style.backgroundColor = '#47a0ff'; // Blue for low - } + monitor.tempBar.style.backgroundColor = barColor(temp, '#47a0ff', '#ffad33', '#ff4d4d', 60, 80); } }; From ac78a8c67991c8d33e9cd576ffdd4ebcfb05d94b Mon Sep 17 00:00:00 2001 From: DasPauluteli <67437654+DasPauluteli@users.noreply.github.com> Date: Sat, 14 Mar 2026 13:38:41 +0100 Subject: [PATCH 2/3] Add files via upload --- screenshot2.png | Bin 0 -> 17885 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 screenshot2.png diff --git a/screenshot2.png b/screenshot2.png new file mode 100644 index 0000000000000000000000000000000000000000..9a7ab9e5ba71a258e77fabbe2f611d57944333f2 GIT binary patch literal 17885 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 FV1mO)6|evR literal 0 HcmV?d00001 From eabc95bc10854b613e68d564008c3f37e7d5a920 Mon Sep 17 00:00:00 2001 From: DasPauluteli <67437654+DasPauluteli@users.noreply.github.com> Date: Sat, 14 Mar 2026 13:40:57 +0100 Subject: [PATCH 3/3] Add APU monitor screenshot to README Added a screenshot for AMD APU monitoring. Currently doesn't load due to missing image in iDAPPA's repo. Will properly load once merged. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b47d947..27bb239 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ A simple, lightweight AMD GPU monitoring tool for ComfyUI that displays real-time information about your AMD GPU directly in the UI. Supports both discrete AMD GPUs and APUs with unified memory (e.g. Strix Halo / gfx1151). ![AMD GPU Monitor Screenshot](https://github.com/iDAPPA/ComfyUI-AMDGPUMonitor/raw/main/screenshot.png) +![AMD APU Monitor Screenshot](https://github.com/iDAPPA/ComfyUI-AMDGPUMonitor/blob/main/screenshot2.png) ## Features