I’m integrating a CSI-2 camera for an AGV vision stack on Jetson Orin NX 8GB and hit a repeatable “half-fps” behavior that appears coupled to the display stack / EGL path. I’d appreciate guidance on fixes or a supported low-memory display setup that preserves full frame rate without a heavy desktop.
Environment
-
Hardware: Jetson Orin NX Engineering Reference Developer Kit (Super) 8GB
-
JetPack / L4T: nvidia-jetpack 6.2.1+b38
-
Kernel:
Linux nvidia-sbc 5.15.148-rt-tegra #1 SMP PREEMPT_RT Mon Oct 14 17:05:40 PDT 2024 aarch64 aarch64 aarch64 GNU/Linux
-
(RT kernel / PREEMPT_RT)
-
OS: Ubuntu 22.04
-
Power/Frequency: NV Power Mode: MAXN_SUPER; jetson_clocks locked (GPU 1173 MHz, EMC 3199 MHz, etc.)
-
Camera: IMX219-83 Stereo Camera, Mode 4 = 1280×720 @ 60 fps (https://www.waveshare.com/wiki/IMX219-83_Stereo_Camera)
-
Camera stack: LibArgus / Isaac ROS Argus Camera (NITROS / GXF hawk extension; lower layer not open-sourced)
-
Consumer path: EGLStream + cuEGL (cuEGLStreamConsumerAcquireFrame)
Attachments available: measurefps_argus_cuda.cpp (minimal repro) and README.md (build/run steps and nsys capture notes).
Symptom
-
Goal: stable 1280×720 @ 60 fps in both no-X (multi-user) and with-X (graphical) runlevels.
-
Observed:
-
No X (multi-user.target): whether EGL_PLATFORM=surfaceless or DISPLAY=:0, I only get ~30 fps; AcquireFrame cadence ≈ 33 ms.
-
With X (graphical.target): consistently ~60 fps; AcquireFrame cadence ≈ 16.7 ms.
-
-
Analysis: nsys shows the slowdown is on the supply side (slower Acquire cadence), not GPU compute in my consumer.
On shutdown I sometimes see EGL teardown messages:
SCF: Error NotSupported: Unable to make context current
SCF: Error InvalidState: Error destorying EGL context
Repro (key steps)
Runlevel switch
# No X (save memory)
sudo systemctl isolate multi-user.target
# With X (graphical)
sudo systemctl isolate graphical.target
Lock power & clocks (done in both cases)
sudo nvpmodel -m 0
sudo jetson_clocks --show
sudo tegrastats
Run the minimal repro
# No X
EGL_PLATFORM=surfaceless ./build/measurefps --mode 4 --fps 60 --frames 120
# With X
DISPLAY=:0 ./build/measurefps --mode 4 --fps 60 --frames 120
-
No X: avg_fps ≈ 30.x
-
With X: avg_fps ≈ 60.x
What I tried (didn’t resolve “half-fps”)
-
MAXN_SUPER + jetson_clocks confirmed (GPU/EMC locked).
-
In multi-user: tested both EGL_PLATFORM=surfaceless and DISPLAY=:0 → still ~30 fps.
-
Restarted nvargus-daemon; in controlled demos I fixed frame duration / exposure and disabled anti-banding — the overall “no-X → half-fps” behavior remains.
-
In production I cannot easily replace the closed lower layer of Isaac ROS / GXF hawk.
Working hypothesis
Under identical hardware and locked clocks, “With X → 60 fps / No X → ~30 fps” points to a timing/clocking policy difference inside the EGL/cuEGL/Argus path when no display stack is present (possibly involving Host1x/VI/ISP or display-domain interaction). My consumer is not the bottleneck; Acquire cadence itself slows to ~33 ms.
Questions for NVIDIA / community
-
Is this a known issue or expected behavior for cuEGL/Argus when no X is running, especially on JetPack 6.2.1 with the PREEMPT_RT kernel?
-
What’s the recommended low-memory display stack on Jetson to preserve full frame rate without a full desktop?
-
Minimal Xorg (no desktop) using AllowEmptyInitialConfiguration / UseDisplayDevice None (headless X).
-
Wayland/Weston + Xwayland as a lightweight compositor/compat layer.
Are there official sample configs or systemd units for these?
-
-
For no-X deployments, are there supported driver/kernel/BPMP devfreq knobs (or env vars) to ensure multimedia/Host1x/VI/ISP domains don’t down-clock or change scheduling such that the stream drops to ~30 fps?
-
For Isaac ROS Argus Camera / GXF hawk: without modifying closed code, are there parameters / environment variables / graph options to keep camera streaming at 60 fps in headless mode (e.g., forcing a headless path, disabling any display-sync tendencies, enforcing frame-duration constraints)?
-
Any official logging toggles / diagnostic steps (Argus/driver-side) you recommend to pinpoint which domain/policy triggers the half-fps cadence when X is absent?
Attachments
measurefps.zip (10.6 KB)
- measurefps_argus_cuda.cpp — minimal repro (EGLDevice/surfaceless + cuEGL consumer + Argus stream), flags --mode / --fps / --frames; prints average fps and key messages.
Turning off X saves ~800 MB, but I still need consistent 60 fps from IMX219 mode 4 via cuEGL/Argus. Any guidance, known issues, or official recommendations to achieve this without a heavy desktop would be greatly appreciated. Thank you! 🙏