From 6f266256c675b804484e6cbb3a8586892682831d Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Wed, 13 May 2026 14:58:48 -0600 Subject: [PATCH] Improve GPU vendor handling --- frigate/stats/util.py | 17 +++++----- frigate/test/test_gpu_stats.py | 1 + frigate/util/services.py | 1 + web/src/types/stats.ts | 3 ++ web/src/views/system/GeneralMetrics.tsx | 45 ++++++++++++++----------- 5 files changed, 40 insertions(+), 27 deletions(-) diff --git a/frigate/stats/util.py b/frigate/stats/util.py index 079b2ea47a..a0141d1305 100644 --- a/frigate/stats/util.py +++ b/frigate/stats/util.py @@ -243,6 +243,7 @@ async def set_gpu_stats( if nvidia_usage: for i in range(len(nvidia_usage)): stats[nvidia_usage[i]["name"]] = { + "vendor": "nvidia", "gpu": str(round(float(nvidia_usage[i]["gpu"]), 2)) + "%", "mem": str(round(float(nvidia_usage[i]["mem"]), 2)) + "%", "enc": str(round(float(nvidia_usage[i]["enc"]), 2)) + "%", @@ -251,16 +252,16 @@ async def set_gpu_stats( } else: - stats["nvidia-gpu"] = {"gpu": "", "mem": ""} + stats["nvidia-gpu"] = {"vendor": "nvidia", "gpu": "", "mem": ""} hwaccel_errors.append(args) elif "nvmpi" in args or "jetson" in args: # nvidia Jetson jetson_usage = get_jetson_stats() if jetson_usage: - stats["jetson-gpu"] = jetson_usage + stats["jetson-gpu"] = {"vendor": "nvidia", **jetson_usage} else: - stats["jetson-gpu"] = {"gpu": "", "mem": ""} + stats["jetson-gpu"] = {"vendor": "nvidia", "gpu": "", "mem": ""} hwaccel_errors.append(args) elif "qsv" in args or ("vaapi" in args and not is_vaapi_amd_driver()): if not config.telemetry.stats.intel_gpu_stats: @@ -278,7 +279,7 @@ async def set_gpu_stats( name = entry.pop("name") stats[name] = entry else: - stats["intel-gpu"] = {"gpu": "", "mem": ""} + stats["intel-gpu"] = {"vendor": "intel", "gpu": "", "mem": ""} hwaccel_errors.append(args) elif "vaapi" in args: if not config.telemetry.stats.amd_gpu_stats: @@ -288,18 +289,18 @@ async def set_gpu_stats( amd_usage = get_amd_gpu_stats() if amd_usage: - stats["amd-vaapi"] = amd_usage + stats["amd-vaapi"] = {"vendor": "amd", **amd_usage} else: - stats["amd-vaapi"] = {"gpu": "", "mem": ""} + stats["amd-vaapi"] = {"vendor": "amd", "gpu": "", "mem": ""} hwaccel_errors.append(args) elif "preset-rk" in args: rga_usage = get_rockchip_gpu_stats() if rga_usage: - stats["rockchip"] = rga_usage + stats["rockchip"] = {"vendor": "rockchip", **rga_usage} elif "v4l2m2m" in args or "rpi" in args: # RPi v4l2m2m is currently not able to get usage stats - stats["rpi-v4l2m2m"] = {"gpu": "", "mem": ""} + stats["rpi-v4l2m2m"] = {"vendor": "rpi", "gpu": "", "mem": ""} if stats: all_stats["gpu_usages"] = stats diff --git a/frigate/test/test_gpu_stats.py b/frigate/test/test_gpu_stats.py index 72bf597f33..f6986912f7 100644 --- a/frigate/test/test_gpu_stats.py +++ b/frigate/test/test_gpu_stats.py @@ -83,6 +83,7 @@ class TestGpuStats(unittest.TestCase): assert intel_stats == { "0000:00:02.0": { "name": "Intel Graphics", + "vendor": "intel", "gpu": "90.0%", "mem": "-%", "compute": "30.0%", diff --git a/frigate/util/services.py b/frigate/util/services.py index 1f406e47b1..5ee15f8b48 100644 --- a/frigate/util/services.py +++ b/frigate/util/services.py @@ -479,6 +479,7 @@ def get_intel_gpu_stats( entry: dict[str, Any] = { "name": names.get(pdev) or f"Intel GPU {pdev}", + "vendor": "intel", "gpu": f"{round(overall_pct, 2)}%", "mem": "-%", "compute": f"{round(compute_pct, 2)}%", diff --git a/web/src/types/stats.ts b/web/src/types/stats.ts index 0bd4ebde30..0ebac9ebd3 100644 --- a/web/src/types/stats.ts +++ b/web/src/types/stats.ts @@ -62,7 +62,10 @@ export type ExtraProcessStats = { mem?: string; }; +export type GpuVendor = "intel" | "amd" | "nvidia" | "rockchip" | "rpi"; + export type GpuStats = { + vendor?: GpuVendor; gpu: string; mem: string; enc?: string; diff --git a/web/src/views/system/GeneralMetrics.tsx b/web/src/views/system/GeneralMetrics.tsx index 15e0407b5f..2c6c9df037 100644 --- a/web/src/views/system/GeneralMetrics.tsx +++ b/web/src/views/system/GeneralMetrics.tsx @@ -98,13 +98,11 @@ export default function GeneralMetrics({ let nvCount = 0; statsHistory.length > 0 && - Object.keys(statsHistory[0]?.gpu_usages ?? {}).forEach((key) => { - if (key == "amd-vaapi" || key == "intel-gpu") { - vaCount += 1; - } - - if (key.includes("NVIDIA")) { + Object.values(statsHistory[0]?.gpu_usages ?? {}).forEach((stats) => { + if (stats.vendor === "nvidia") { nvCount += 1; + } else if (stats.vendor === "intel" || stats.vendor === "amd") { + vaCount += 1; } }); @@ -288,11 +286,14 @@ export default function GeneralMetrics({ return []; } + // Intel doesn't expose VRAM usage, so hide the memory section + // entirely when every reporting GPU is Intel. + const firstUsages = statsHistory[0]?.gpu_usages ?? {}; + const firstEntries = Object.values(firstUsages); if ( - Object.keys(statsHistory?.at(0)?.gpu_usages ?? {}).length == 1 && - Object.keys(statsHistory?.at(0)?.gpu_usages ?? {})[0] === "intel-gpu" + firstEntries.length > 0 && + firstEntries.every((s) => s.vendor === "intel") ) { - // intel gpu stats do not support memory return undefined; } @@ -307,6 +308,10 @@ export default function GeneralMetrics({ } Object.entries(stats.gpu_usages || {}).forEach(([key, stats]) => { + if (stats.vendor === "intel") { + return; + } + if (!(key in series)) { series[key] = { name: key, data: [] }; } @@ -470,8 +475,9 @@ export default function GeneralMetrics({ return false; } - const gpuKeys = Object.keys(statsHistory[0]?.gpu_usages ?? {}); - const hasIntelGpu = gpuKeys.some((key) => key === "intel-gpu"); + const hasIntelGpu = Object.values(statsHistory[0]?.gpu_usages ?? {}).some( + (stats) => stats.vendor === "intel", + ); if (!hasIntelGpu) { return false; @@ -486,14 +492,15 @@ export default function GeneralMetrics({ continue; } - Object.entries(stats.gpu_usages || {}).forEach(([key, gpuStats]) => { - if (key === "intel-gpu") { - if (gpuStats.gpu) { - hasDataPoints = true; - const gpuValue = parseFloat(gpuStats.gpu.slice(0, -1)); - if (!isNaN(gpuValue) && gpuValue > 0) { - allZero = false; - } + Object.values(stats.gpu_usages || {}).forEach((gpuStats) => { + if (gpuStats.vendor !== "intel") { + return; + } + if (gpuStats.gpu) { + hasDataPoints = true; + const gpuValue = parseFloat(gpuStats.gpu.slice(0, -1)); + if (!isNaN(gpuValue) && gpuValue > 0) { + allZero = false; } } });