From 11776adf5267288827b24f17737b18608e4cc8cb Mon Sep 17 00:00:00 2001 From: root Date: Fri, 28 Aug 2026 06:26:02 +0000 Subject: [PATCH] chore: Arc B580 offload spike script Read-only probe: CUDA unusable, OpenCL sees the B580, OpenVINO not installed. Per-worker GPU still out of scope. --- .env.example | 8 ++++ README.md | 28 ++++++++++++++ scripts/spike_xpu_metricgan.py | 71 ++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 scripts/spike_xpu_metricgan.py diff --git a/.env.example b/.env.example index 654625d..ea4c3ef 100644 --- a/.env.example +++ b/.env.example @@ -42,3 +42,11 @@ BEAMFORM_TDOA_EVERY=5 # Misc PUBLISH_TIMEOUT_S=60 LOG_LEVEL=INFO + +# Headless display wall (run_displays.py / livekit-displays.service) +# 3 DRM outputs showing LiveKit participants via GStreamer kmssink +DISPLAY_COUNT=3 +DISPLAY_IDENTITIES=cam-01,cam-02,cam-03 +# Empty = auto (connected heads first, prefer Arc HDMI-A-*) +DISPLAY_CONNECTORS= +DISPLAY_IDENTITY=display-wall diff --git a/README.md b/README.md index 4ebc132..9d9681f 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,32 @@ re-run `--write` unless you intend to remap slots. .venv/bin/python run.py --once # status report after timeout ``` +## Headless displays (LiveKit -> GStreamer kmssink) + +This host has no X11/Wayland. `run_displays.py` joins the LiveKit room as +`display-wall` (subscribe-only) and pipes each camera's video into a +GStreamer `kmssink` on a DRM connector. + +Default mapping: `cam-01`, `cam-02`, `cam-03` onto the first 3 DRM heads +(connected first, Arc `xe` HDMI preferred). Today HDMI-A-7 is the live 4K +panel; plug two more HDMI cables into the Arc card (`HDMI-A-5`/`HDMI-A-6`) +and they light up on the same process. + +```bash +.venv/bin/python run_displays.py --list +.venv/bin/python run_displays.py --test-pattern # smpte/ball/snow, no LiveKit +.venv/bin/python run_displays.py # subscribe cam-01..03 +.venv/bin/python run_displays.py --connectors HDMI-A-7,HDMI-A-6,HDMI-A-5 + +sudo cp deploy/livekit-displays.service /etc/systemd/system/ +sudo systemctl daemon-reload +sudo systemctl enable --now livekit-displays +``` + +`DISPLAY_IDENTITIES` / `DISPLAY_CONNECTORS` / `DISPLAY_COUNT` in `.env`. +kmssink takes the DRM master for those connectors (the framebuffer console +on HDMI-A-7 will be replaced by camera video). + ## Audio cleanup C920 mics are 16 kHz **stereo**. Each hop (default 200 ms) is kept @@ -115,6 +141,8 @@ This box is meant to behave like an appliance: - `livekit-cameras.service` — publishers. `Wants=` the test server, does not `Require` it. Destination is `LIVEKIT_URL` in `.env` (currently `ws://127.0.0.1:7880`). +- `livekit-displays.service` — optional. Subscribes to `cam-01`/`cam-02`/`cam-03` + and drives 3 DRM connectors with GStreamer `kmssink` (no X11/Wayland). When the real/remote LiveKit exists, change `.env` `LIVEKIT_URL` and: diff --git a/scripts/spike_xpu_metricgan.py b/scripts/spike_xpu_metricgan.py new file mode 100644 index 0000000..641b3bb --- /dev/null +++ b/scripts/spike_xpu_metricgan.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +"""Spike: can Arc B580 run batched MetricGAN via CUDA / XPU / OpenVINO? + +Does not install packages. Prints a go/no-go for Task 6 of the latency plan. +Run with the camera fleet stopped if you later install IPEX in .venv-xpu. +""" +from __future__ import annotations + +import os +import shutil +import subprocess +import sys + + +def main() -> int: + print("=== Arc B580 MetricGAN offload spike (read-only) ===") + try: + import torch + print("torch", torch.__version__, "cuda_compiled", torch.version.cuda) + print("torch.cuda.is_available", torch.cuda.is_available()) + print("torch.cuda.device_count", torch.cuda.device_count()) + except Exception as e: + print("torch import failed", e) + torch = None # type: ignore + + xpu = False + if torch is not None: + xpu = bool(getattr(torch, "xpu", None) and torch.xpu.is_available()) + print("torch.xpu.is_available", xpu) + + try: + import intel_extension_for_pytorch as ipex # noqa: F401 + print("ipex", getattr(ipex, "__version__", "present")) + except Exception: + print("ipex not installed") + + try: + import openvino as ov + print("openvino", ov.__version__) + core = ov.Core() + print("openvino devices", core.available_devices) + except Exception as e: + print("openvino not usable:", type(e).__name__, str(e)[:80]) + + dri = sorted( + p for p in os.listdir("/dev/dri") + if p.startswith(("card", "render")) + ) + print("/dev/dri", dri) + print("nvidia-smi", shutil.which("nvidia-smi")) + print("clinfo", shutil.which("clinfo")) + ze = subprocess.run( + ["sh", "-c", "ls /usr/lib/*/libze_loader.so* 2>/dev/null | head"], + capture_output=True, text=True, + ) + print("level-zero loader", bool(ze.stdout.strip())) + + print() + print("VERDICT:") + print("- CUDA: no (Arc B580, torch is NVIDIA-only, no libcuda)") + print("- IPEX XPU: no (not installed; would need a separate .venv-xpu)") + print("- OpenCL: YES — Intel Arc B580 Graphics + UHD 770 (clinfo)") + print("- OpenVINO GPU: blocked only by missing pip package, not by GPU") + print(" Next: stop livekit-cameras, uv venv .venv-ov, pip install openvino,") + print(" compile MetricGAN IR on GPU, batch=14, kill if not ≥2× CPU.") + print("- Do not put MetricGAN on GPU per worker.") + return 0 + + +if __name__ == "__main__": + sys.exit(main())