Miscellaneous Fixes (#23709)
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
CI / AMD64 Build (push) Waiting to run

This commit is contained in:
Nicolas Mowen
2026-07-13 19:55:00 -08:00
committed by GitHub
parent 6f24f5a595
commit 775ce22204
12 changed files with 368 additions and 66 deletions
+3
View File
@@ -400,6 +400,9 @@ def verify_objects_track(
)
camera_config.objects.track = valid_objects
for label in invalid_objects:
camera_config.objects.filters.pop(label, None)
def verify_lpr_and_face(
frigate_config: FrigateConfig, camera_config: CameraConfig
+37
View File
@@ -397,6 +397,43 @@ class TestConfig(unittest.TestCase):
assert "dog" in frigate_config.cameras["back"].objects.filters
assert frigate_config.cameras["back"].objects.filters["dog"].threshold == 0.7
def test_unsupported_tracked_object_pruned_from_track_and_filters(self):
# "unicorn" is not in the model labelmap, so it must be removed from the
# tracked objects AND from the object filters, otherwise a stale filter
# entry lingers in the parsed config.
config = {
"mqtt": {"host": "mqtt"},
"cameras": {
"back": {
"ffmpeg": {
"inputs": [
{"path": "rtsp://10.0.0.1:554/video", "roles": ["detect"]}
]
},
"detect": {
"height": 1080,
"width": 1920,
"fps": 5,
},
"objects": {
"track": ["person", "unicorn"],
"filters": {
"person": {"threshold": 0.7},
"unicorn": {"threshold": 0.7},
},
},
}
},
}
frigate_config = FrigateConfig(**config)
objects = frigate_config.cameras["back"].objects
assert "unicorn" not in objects.track
assert "unicorn" not in objects.filters
# supported entries are left untouched
assert "person" in objects.track
assert "person" in objects.filters
def test_global_object_mask(self):
config = {
"mqtt": {"host": "mqtt"},
+70 -12
View File
@@ -40,18 +40,18 @@ class TestGpuStats(unittest.TestCase):
"driver": "i915",
"pid": "100",
"engines": {
"render": (1_000_000_000, 0),
"video": (5_000_000_000, 0),
"video-enhance": (200_000_000, 0),
"compute": (0, 0),
"render": (1_000_000_000, 0, 1),
"video": (5_000_000_000, 0, 1),
"video-enhance": (200_000_000, 0, 1),
"compute": (0, 0, 1),
},
},
("0000:00:02.0", "2", "200"): {
"driver": "i915",
"pid": "200",
"engines": {
"render": (0, 0),
"compute": (2_000_000_000, 0),
"render": (0, 0, 1),
"compute": (2_000_000_000, 0, 1),
},
},
}
@@ -60,18 +60,18 @@ class TestGpuStats(unittest.TestCase):
"driver": "i915",
"pid": "100",
"engines": {
"render": (1_200_000_000, 0),
"video": (5_500_000_000, 0),
"video-enhance": (300_000_000, 0),
"compute": (0, 0),
"render": (1_200_000_000, 0, 1),
"video": (5_500_000_000, 0, 1),
"video-enhance": (300_000_000, 0, 1),
"compute": (0, 0, 1),
},
},
("0000:00:02.0", "2", "200"): {
"driver": "i915",
"pid": "200",
"engines": {
"render": (0, 0),
"compute": (2_100_000_000, 0),
"render": (0, 0, 1),
"compute": (2_100_000_000, 0, 1),
},
},
}
@@ -92,6 +92,64 @@ class TestGpuStats(unittest.TestCase):
},
}
@patch("frigate.stats.intel_gpu_info.intel_gpu_name_resolver.get_names")
@patch("frigate.util.services.time.sleep")
@patch("frigate.util.services.time.monotonic")
@patch("frigate.util.services._read_intel_drm_fdinfo")
def test_intel_gpu_stats_xe_capacity(
self, read_fdinfo, monotonic, sleep, get_names
):
# Xe engines report cumulative cycles paired with total cycles, plus a
# per-class capacity. drm-cycles-* is summed across every instance of a
# class, so on Battlemage (capacity 2 for vcs/vecs) busy/total must be
# divided by capacity to land in 0-100%.
monotonic.side_effect = [0.0, 1.0]
get_names.return_value = {"0000:03:00.0": "Intel Arc"}
# Deltas over the window (busy, total): render 200/1000 cap 1 = 20%,
# video 800/1000 cap 2 = 40%, video-enhance 400/1000 cap 2 = 20%,
# compute 100/1000 cap 1 = 10%. Without the capacity divisor video/
# video-enhance would read 80%/40% and dec would clamp at 100%.
snapshot_a = {
("0000:03:00.0", "1", "300"): {
"driver": "xe",
"pid": "300",
"engines": {
"render": (0, 0, 1),
"video": (0, 0, 2),
"video-enhance": (0, 0, 2),
"compute": (0, 0, 1),
},
},
}
snapshot_b = {
("0000:03:00.0", "1", "300"): {
"driver": "xe",
"pid": "300",
"engines": {
"render": (200, 1000, 1),
"video": (800, 1000, 2),
"video-enhance": (400, 1000, 2),
"compute": (100, 1000, 1),
},
},
}
read_fdinfo.side_effect = [snapshot_a, snapshot_b]
intel_stats = get_intel_gpu_stats(None)
assert intel_stats == {
"0000:03:00.0": {
"name": "Intel Arc",
"vendor": "intel",
"gpu": "90.0%",
"mem": "-%",
"compute": "30.0%",
"dec": "60.0%",
"clients": {"300": "90.0%"},
},
}
@patch("frigate.util.services._read_intel_drm_fdinfo")
def test_intel_gpu_stats_no_clients(self, read_fdinfo):
read_fdinfo.return_value = {}
+24 -5
View File
@@ -360,7 +360,7 @@ def _read_intel_drm_fdinfo(target_pdev: str | None) -> dict:
if key in snapshot:
continue
engines: dict[str, tuple[int, int]] = {}
engines: dict[str, tuple[int, int, int]] = {}
if driver == "i915":
for fkey, engine in _I915_ENGINE_KEYS.items():
@@ -368,19 +368,34 @@ def _read_intel_drm_fdinfo(target_pdev: str | None) -> dict:
if not raw:
continue
try:
engines[engine] = (int(raw.split()[0]), 0)
engines[engine] = (int(raw.split()[0]), 0, 1)
except (ValueError, IndexError):
continue
else:
for suffix, engine in _XE_ENGINE_KEYS.items():
busy_raw = fields.get(f"drm-cycles-{suffix}")
total_raw = fields.get(f"drm-total-cycles-{suffix}")
if not (busy_raw and total_raw):
continue
# drm-cycles-* is summed across every instance of the engine
# class while drm-total-cycles-* tracks a single instance, so
# busy/total scales up to the capacity (e.g. Battlemage
# reports 2 for vcs/vecs). Capture it to divide back out;
# absent means a single engine, so default to 1.
capacity_raw = fields.get(f"drm-engine-capacity-{suffix}")
try:
capacity = int(capacity_raw.split()[0]) if capacity_raw else 1
except (ValueError, IndexError):
capacity = 1
try:
engines[engine] = (
int(busy_raw.split()[0]),
int(total_raw.split()[0]),
max(1, capacity),
)
except (ValueError, IndexError):
continue
@@ -450,11 +465,13 @@ def get_intel_gpu_stats(
pid_pct = per_pdev_pid_pct.setdefault(pdev, {})
client_total = 0.0
for engine, (busy_b, total_b) in data_b["engines"].items():
for engine, (busy_b, total_b, capacity) 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))
busy_a, total_a, _ = data_a["engines"].get(
engine, (busy_b, total_b, capacity)
)
if data_b["driver"] == "i915":
delta = max(0, busy_b - busy_a)
@@ -464,7 +481,9 @@ def get_intel_gpu_stats(
delta_total = total_b - total_a
if delta_total <= 0:
continue
pct = min(100.0, delta_busy / delta_total * 100.0)
# Normalize by capacity so a class with N engine instances
# (busy summed across all N) reports 0-100%, not 0-N*100%.
pct = min(100.0, delta_busy / (delta_total * capacity) * 100.0)
engine_pct[engine] += pct
client_total += pct