From ba65485a53adf9219c0c340515ac0fcbe85cad66 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Mon, 4 May 2026 09:07:19 -0600 Subject: [PATCH] Adjust approach --- frigate/test/test_gpu_stats.py | 25 ++++++++++++++----------- frigate/util/services.py | 22 +++++++++++++--------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/frigate/test/test_gpu_stats.py b/frigate/test/test_gpu_stats.py index bfeebf83c..85b12138d 100644 --- a/frigate/test/test_gpu_stats.py +++ b/frigate/test/test_gpu_stats.py @@ -24,14 +24,15 @@ class TestGpuStats(unittest.TestCase): # 1 second of wall clock between snapshots monotonic.side_effect = [0.0, 1.0] - # Two i915 clients on the same iGPU. Engine values are cumulative ns; - # we'll arrange deltas of: - # client A (pid 100): render +200_000_000 ns (20%), video +500_000_000 ns (50%) - # client B (pid 200): compute +100_000_000 ns (10%) - # Combined engine totals → render 20%, compute 10%, video 50% - # → "compute" = render + compute = 30% - # → "dec" = video = 50% - # → "gpu" = max(30, 50, 0, 0) = 50% + # Two i915 clients on the same iGPU. Engine values are cumulative ns. + # Deltas over the 1s window: + # client A (pid 100): render +200_000_000 (20%), video +500_000_000 (50%), + # video-enhance +100_000_000 (10%) + # client B (pid 200): compute +100_000_000 (10%) + # Engine totals → render 20, video 50, video-enhance 10, compute 10 + # → compute = render + compute = 30 + # → dec = video + video-enhance = 60 + # → gpu = compute + dec = 90 snapshot_a = { ("0000:00:02.0", "1", "100"): { "driver": "i915", @@ -39,6 +40,7 @@ class TestGpuStats(unittest.TestCase): "engines": { "render": (1_000_000_000, 0), "video": (5_000_000_000, 0), + "video-enhance": (200_000_000, 0), "compute": (0, 0), }, }, @@ -58,6 +60,7 @@ class TestGpuStats(unittest.TestCase): "engines": { "render": (1_200_000_000, 0), "video": (5_500_000_000, 0), + "video-enhance": (300_000_000, 0), "compute": (0, 0), }, }, @@ -76,11 +79,11 @@ class TestGpuStats(unittest.TestCase): sleep.assert_called_once() assert intel_stats == { - "gpu": "50.0%", + "gpu": "90.0%", "mem": "-%", "compute": "30.0%", - "dec": "50.0%", - "clients": {"100": "70.0%", "200": "10.0%"}, + "dec": "60.0%", + "clients": {"100": "80.0%", "200": "10.0%"}, } @patch("frigate.util.services._read_intel_drm_fdinfo") diff --git a/frigate/util/services.py b/frigate/util/services.py index c57dd9580..657cf6d55 100644 --- a/frigate/util/services.py +++ b/frigate/util/services.py @@ -266,13 +266,17 @@ def get_amd_gpu_stats() -> Optional[dict[str, str]]: _INTEL_FDINFO_SAMPLE_SECONDS = 1.0 +# Engines we track. Render/3D and Compute are pooled into "compute"; Video and +# VideoEnhance into "dec" (VideoEnhance is the post-process engine that handles +# VAAPI scaling/deinterlace/CSC, e.g. ffmpeg `-vf scale_vaapi=...`). The Copy +# (DMA blitter) engine is intentionally ignored — it represents transparent +# memory transfers, not user-visible GPU work. # i915 fdinfo keys (cumulative ns) → logical engine name. _I915_ENGINE_KEYS = { "drm-engine-render": "render", "drm-engine-video": "video", "drm-engine-video-enhance": "video-enhance", "drm-engine-compute": "compute", - "drm-engine-copy": "copy", } # Xe fdinfo suffixes (cumulative cycles, paired with drm-total-cycles-*). _XE_ENGINE_KEYS = { @@ -280,7 +284,6 @@ _XE_ENGINE_KEYS = { "vcs": "video", "vecs": "video-enhance", "ccs": "compute", - "bcs": "copy", } @@ -396,8 +399,9 @@ def get_intel_gpu_stats(intel_gpu_device: Optional[str]) -> Optional[dict[str, A Each DRM client FD exposes monotonic per-engine busy counters via /proc//fdinfo/ (i915 since kernel 5.19, Xe since first release). We sample twice and divide busy-time deltas by wall-clock to derive - utilisation. Render/3D and the dedicated Compute engine are pooled into - "compute"; Video into "dec". + utilization. Render/3D and Compute are pooled into "compute"; Video and + VideoEnhance into "dec". Overall "gpu" is the sum of those pools (clamped + to 100%). """ target_pdev = _resolve_intel_gpu_pdev(intel_gpu_device) @@ -418,7 +422,6 @@ def get_intel_gpu_stats(intel_gpu_device: Optional[str]) -> Optional[dict[str, A "video": 0.0, "video-enhance": 0.0, "compute": 0.0, - "copy": 0.0, } pid_pct: dict[str, float] = {} @@ -429,6 +432,9 @@ def get_intel_gpu_stats(intel_gpu_device: Optional[str]) -> Optional[dict[str, A client_total = 0.0 for engine, (busy_b, total_b) in data_b["engines"].items(): + if engine not in engine_pct: + continue + busy_a, total_a = data_a["engines"].get(engine, (busy_b, total_b)) if data_b["driver"] == "i915": @@ -450,10 +456,8 @@ def get_intel_gpu_stats(intel_gpu_device: Optional[str]) -> Optional[dict[str, A engine_pct[engine] = min(100.0, engine_pct[engine]) compute_pct = min(100.0, engine_pct["render"] + engine_pct["compute"]) - dec_pct = engine_pct["video"] - overall_pct = max( - compute_pct, dec_pct, engine_pct["video-enhance"], engine_pct["copy"] - ) + dec_pct = min(100.0, engine_pct["video"] + engine_pct["video-enhance"]) + overall_pct = min(100.0, compute_pct + dec_pct) results: dict[str, Any] = { "gpu": f"{round(overall_pct, 2)}%",