Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion config/launcher_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"conflicts": ["real"],
"command": "gazebo:=true",
"runs_on_vnc": true,
"readiness_check": "pgrep -f '[g]azebo' >/dev/null 2>&1",
"readiness_check": "pgrep -f '[g]z sim' >/dev/null 2>&1",
"readiness_timeout": 120,
"default_on": false
},
Expand Down
156 changes: 156 additions & 0 deletions docker/gpu_detect.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
#!/usr/bin/env bash
# Detect host GPU capabilities and populate Docker flags for Lucy launches.
#
# Source from launch_lucy.sh or install.sh (do not execute directly):
# source "$SCRIPT_DIR/docker/gpu_detect.sh"
#
# Sets:
# LUCY_GPU_MODE — jetson | nvidia | dri | software
# GPU_DOCKER_ARGS — bash array appended to docker run
#
# Override detection for testing: LUCY_GPU_MODE=software|jetson|nvidia|dri

GPU_DOCKER_ARGS=()
LUCY_GPU_MODE=software

_lucy_is_jetson() {
if [[ -f /etc/nv_tegra_release ]]; then
return 0
fi
if [[ -r /proc/device-tree/model ]]; then
tr -d '\0' </proc/device-tree/model 2>/dev/null | grep -qi jetson
return $?
fi
return 1
}

_lucy_docker_has_nvidia_runtime() {
docker info 2>/dev/null | grep -qiE 'nvidia|Runtimes.*nvidia'
}

_lucy_append_dri_devices() {
local node
shopt -s nullglob
for node in /dev/dri/card* /dev/dri/renderD*; do
GPU_DOCKER_ARGS+=(--device "$node")
done
shopt -u nullglob
}

_lucy_append_render_groups() {
# Docker resolves group names against the *image* /etc/group, not the host.
# Pass numeric GIDs from the host so render/video access works in the container.
local group gid
for group in render video; do
gid="$(getent group "$group" 2>/dev/null | awk -F: '{print $3}')"
[[ -n "$gid" ]] || continue
GPU_DOCKER_ARGS+=(--group-add "$gid")
done
}

_lucy_apply_jetson_gpu() {
LUCY_GPU_MODE=jetson
if _lucy_docker_has_nvidia_runtime; then
GPU_DOCKER_ARGS+=(--runtime nvidia)
GPU_DOCKER_ARGS+=(-e "NVIDIA_VISIBLE_DEVICES=all")
GPU_DOCKER_ARGS+=(-e "NVIDIA_DRIVER_CAPABILITIES=graphics,utility,compute,video")
GPU_DOCKER_ARGS+=(-e "__GLX_VENDOR_LIBRARY_NAME=nvidia")
GPU_DOCKER_ARGS+=(-e "__EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/10_nvidia.json")
else
echo "GPU: jetson detected but Docker nvidia runtime missing; using software rendering." >&2
echo " Install nvidia-container-toolkit for hardware GL in the container." >&2
fi
if [[ -d /dev/dri ]]; then
_lucy_append_dri_devices
_lucy_append_render_groups
fi
}

lucy_apply_gpu_detect() {
GPU_DOCKER_ARGS=()
LUCY_GPU_MODE=software

case "$(echo "${LUCY_GPU_MODE_OVERRIDE:-}" | tr '[:upper:]' '[:lower:]')" in
jetson|nvidia|dri|software)
LUCY_GPU_MODE="${LUCY_GPU_MODE_OVERRIDE,,}"
case "$LUCY_GPU_MODE" in
jetson) _lucy_apply_jetson_gpu ;;
nvidia)
GPU_DOCKER_ARGS+=(--gpus all)
GPU_DOCKER_ARGS+=(-e "NVIDIA_VISIBLE_DEVICES=all")
GPU_DOCKER_ARGS+=(-e "NVIDIA_DRIVER_CAPABILITIES=graphics,utility,compute,video")
GPU_DOCKER_ARGS+=(-e "__GLX_VENDOR_LIBRARY_NAME=nvidia")
GPU_DOCKER_ARGS+=(-e "__EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/10_nvidia.json")
;;
dri)
_lucy_append_dri_devices
_lucy_append_render_groups
;;
esac
return 0
;;
esac

if _lucy_is_jetson; then
_lucy_apply_jetson_gpu
return 0
fi

if command -v nvidia-smi >/dev/null 2>&1 && _lucy_docker_has_nvidia_runtime; then
if nvidia-smi >/dev/null 2>&1; then
LUCY_GPU_MODE=nvidia
GPU_DOCKER_ARGS+=(--gpus all)
GPU_DOCKER_ARGS+=(-e "NVIDIA_VISIBLE_DEVICES=all")
GPU_DOCKER_ARGS+=(-e "NVIDIA_DRIVER_CAPABILITIES=graphics,utility,compute,video")
GPU_DOCKER_ARGS+=(-e "__GLX_VENDOR_LIBRARY_NAME=nvidia")
GPU_DOCKER_ARGS+=(-e "__EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/10_nvidia.json")
return 0
fi
fi

if [[ -d /dev/dri ]] && compgen -G '/dev/dri/renderD*' >/dev/null 2>&1; then
LUCY_GPU_MODE=dri
_lucy_append_dri_devices
_lucy_append_render_groups
return 0
fi

LUCY_GPU_MODE=software
}

lucy_gpu_launch_message() {
case "$LUCY_GPU_MODE" in
jetson)
if _lucy_docker_has_nvidia_runtime; then
echo "GPU: jetson (nvidia container runtime + /dev/dri when available)"
else
echo "GPU: jetson (software fallback — nvidia runtime not configured)"
fi
;;
nvidia) echo "GPU: nvidia (hardware acceleration enabled)" ;;
dri) echo "GPU: dri (Mesa /dev/dri passthrough)" ;;
*) echo "GPU: software (VNC llvmpipe or headless rendering)" ;;
esac
}

# Pick a host DISPLAY when unset so Jetson can use native X11 + GPU instead of VNC/llvmpipe.
lucy_resolve_host_display() {
[[ -n "${DISPLAY:-}" ]] && return 0

local sock n
shopt -s nullglob
for sock in /tmp/.X11-unix/X[0-9]*; do
n="${sock##*/X}"
DISPLAY=":${n}"
export DISPLAY
shopt -u nullglob
echo "GUI: auto-selected DISPLAY=$DISPLAY (local X11 socket)" >&2
return 0
done
shopt -u nullglob
}

# When sourced, apply immediately unless caller sets LUCY_GPU_DETECT_DEFER=1.
if [[ "${BASH_SOURCE[0]}" != "${0}" ]] && [[ "${LUCY_GPU_DETECT_DEFER:-}" != 1 ]]; then
lucy_apply_gpu_detect
fi
11 changes: 11 additions & 0 deletions docker/gui_desktop.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ VNC_PASSWORD="${LUCY_GUI_VNC_PASSWORD:-lucy}"
VNC_PASSWD_FILE=/tmp/.lucy_vncpasswd
export DISPLAY=":${DISPLAY_NUM}"

# When the container was started with Jetson/NVIDIA GPU passthrough, prefer the
# NVIDIA GLX vendor so Xvfb-backed RViz/Gazebo use the Tegra GPU instead of llvmpipe.
case "${LUCY_GPU_MODE:-}" in
jetson|nvidia)
export __GLX_VENDOR_LIBRARY_NAME=nvidia
export __EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/10_nvidia.json
;;
esac
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/tmp/runtime-root}"
mkdir -p "$XDG_RUNTIME_DIR" 2>/dev/null || true

log() { echo "[gui_desktop] $*"; }
# Match by exact process name (comm), not full command line, so these checks can't
# accidentally match the surrounding shell whose argv may mention these binaries.
Expand Down
9 changes: 7 additions & 2 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ fi

# shellcheck disable=SC1091
source "$SCRIPT_DIR/docker/ensure_image.sh"
# shellcheck disable=SC1091
source "$SCRIPT_DIR/docker/gpu_detect.sh"

ensure_docker_image() {
ensure_lucy_docker_image "$SCRIPT_DIR" "$IMAGE_NAME" "$DOCKERFILE_PATH"
Expand Down Expand Up @@ -103,7 +105,8 @@ remove_workspace_src_repo() {
local name="$1"
rm -rf "src/${name}" 2>/dev/null || true
docker_run_platform_flags "$SCRIPT_DIR"
docker run "${DOCKER_RUN_PLATFORM_ARGS[@]}" --rm \
docker run "${DOCKER_RUN_PLATFORM_ARGS[@]}" "${GPU_DOCKER_ARGS[@]}" --rm \
-e LUCY_GPU_MODE="$LUCY_GPU_MODE" \
-v "$SCRIPT_DIR:$WORKSPACE" \
"$IMAGE_NAME" -c "rm -rf ${WORKSPACE}/src/${name}"
}
Expand Down Expand Up @@ -150,6 +153,7 @@ docker_workspace_install() {
ensure_docker_image
docker_run_platform_flags "$SCRIPT_DIR"
docker_run_it_flags
lucy_gpu_launch_message
echo "Docker install: rosdep, colcon build, yarn install (lucy_control_panel) ..."
local inner_cmd
read -r -d '' inner_cmd <<'EOS' || true
Expand All @@ -163,7 +167,8 @@ source /opt/ros/humble/setup.bash \
( cd src/lucy_control_panel && yarn install ); \
fi
EOS
docker run "${DOCKER_RUN_PLATFORM_ARGS[@]}" "${DOCKER_RUN_IT[@]}" --rm \
docker run "${DOCKER_RUN_PLATFORM_ARGS[@]}" "${DOCKER_RUN_IT[@]}" "${GPU_DOCKER_ARGS[@]}" --rm \
-e LUCY_GPU_MODE="$LUCY_GPU_MODE" \
-v "$SCRIPT_DIR:$WORKSPACE" \
"$IMAGE_NAME" -c "$inner_cmd"
}
Expand Down
45 changes: 38 additions & 7 deletions launch_lucy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# VITE_PORT from src/lucy_control_panel/.env, else 5000). Vite proxies /rosbridge to the bridge.
#
# Docker platform follows the last ./install.sh run (.lucy-docker-platform; override with LUCY_DOCKER_PLATFORM).
# GPU mode is auto-detected via docker/gpu_detect.sh (jetson / nvidia / dri / software).

set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
Expand Down Expand Up @@ -61,6 +62,10 @@ WORKSPACE="/workspace"
# shellcheck disable=SC1091
source "$SCRIPT_DIR/docker/ensure_image.sh"

# shellcheck disable=SC1091
source "$SCRIPT_DIR/docker/gpu_detect.sh"
lucy_resolve_host_display

ensure_docker_image() {
ensure_lucy_docker_image "$SCRIPT_DIR" "$IMAGE_NAME" "$DOCKERFILE_PATH"
}
Expand All @@ -86,25 +91,41 @@ case "$(echo "${LUCY_FORCE_VNC:-}" | tr '[:upper:]' '[:lower:]')" in
1|true|yes) USE_VNC=1 ;;
0|false|no) USE_VNC=0 ;;
esac

# Prefer native X11 + GPU when hardware acceleration is available (Jetson / NVIDIA / DRI).
if [ "$USE_VNC" = 1 ] && [ "${LUCY_GPU_MODE:-software}" != "software" ] && [ -n "${DISPLAY:-}" ]; then
case "$(echo "${LUCY_FORCE_VNC:-}" | tr '[:upper:]' '[:lower:]')" in
1|true|yes) ;;
*) USE_VNC=0 ;;
esac
fi

if [ "${1:-}" = "--headless" ]; then
shift
echo "Headless: no X11 (Gazebo runs headless; RViz is disabled by the launch)."
elif [ "$USE_VNC" = 1 ]; then
# VNC desktop: native X11/GLX is unavailable or unreliable for GL apps (RViz,
# Gazebo). Offer an opt-in virtual desktop via noVNC/VNC from the launcher. The
# display is not started automatically — enable it in the launcher.
# VNC desktop: software GL fallback when no hardware GPU path is available.
# With jetson/nvidia/dri, prefer native X11 above; if you still land here (no DISPLAY),
# RViz/Gazebo render via Xvfb + llvmpipe on CPU — see message below.
GUI_VNC_PORT="$(resolve_host_port VNC "${LUCY_GUI_VNC_PORT:-5901}")"
GUI_NOVNC_PORT="$(resolve_host_port noVNC "${LUCY_GUI_NOVNC_PORT:-6080}")"
GUI_VNC_PASSWORD="${LUCY_GUI_VNC_PASSWORD:-lucy}"
X11_ARGS=(
-e LUCY_GUI_VNC_AVAILABLE=1
-e LUCY_GUI_VNC_PASSWORD="$GUI_VNC_PASSWORD"
-e LUCY_ORIGINAL_DISPLAY="${DISPLAY:-}"
-e LIBGL_ALWAYS_SOFTWARE=1
-e GALLIUM_DRIVER=llvmpipe
-e LUCY_GUI_NOVNC_PUBLISHED_PORT="$GUI_NOVNC_PORT"
-e LUCY_GUI_VNC_PUBLISHED_PORT="$GUI_VNC_PORT"
-e XDG_RUNTIME_DIR=/tmp/runtime-root
)
if [ "${LUCY_GPU_MODE:-software}" = "software" ]; then
X11_ARGS+=(
-e LIBGL_ALWAYS_SOFTWARE=1
-e GALLIUM_DRIVER=llvmpipe
)
else
echo "GUI: noVNC virtual desktop (Jetson/NVIDIA GL when __GLX_VENDOR_LIBRARY_NAME=nvidia is set)" >&2
fi
GUI_PORT_ARGS=(
-p "${GUI_VNC_PORT}:5901"
-p "${GUI_NOVNC_PORT}:6080"
Expand All @@ -113,7 +134,7 @@ elif [ "$USE_VNC" = 1 ]; then
echo " Browser (noVNC): http://localhost:${GUI_NOVNC_PORT}/vnc.html (no password)"
echo " VNC Viewer: localhost:${GUI_VNC_PORT} (password: ${GUI_VNC_PASSWORD})"
else
# AMD64: native X11 forwarding, no VNC.
# Native X11 forwarding (GPU path on Jetson / NVIDIA / DRI when DISPLAY is set).
GUI_DISPLAY="${DOCKER_GUI_DISPLAY:-$DISPLAY}"
if [ -n "$GUI_DISPLAY" ]; then
if command -v xhost &>/dev/null; then
Expand All @@ -124,7 +145,11 @@ else
echo "GUI: DISPLAY=:0 (host network)."
else
X11_ARGS=(-e DISPLAY="$GUI_DISPLAY" -v /tmp/.X11-unix:/tmp/.X11-unix:rw)
echo "GUI: DISPLAY=$GUI_DISPLAY"
if [ "${LUCY_GPU_MODE:-software}" != "software" ]; then
echo "GUI: DISPLAY=$GUI_DISPLAY (hardware GPU — nvidia runtime / DRI)"
else
echo "GUI: DISPLAY=$GUI_DISPLAY"
fi
fi
else
echo "GUI: DISPLAY not set; Gazebo will run headless (RViz disabled)."
Expand All @@ -135,6 +160,8 @@ ensure_docker_image
docker_run_platform_flags "$SCRIPT_DIR"
docker_run_it_flags

lucy_gpu_launch_message

# ----------------------------------------------------------------------------
# Port mapping (control panel + rosbridge)
# ----------------------------------------------------------------------------
Expand Down Expand Up @@ -251,8 +278,10 @@ if [ $# -eq 0 ]; then
--name lucy_dev \
"${DOCKER_PORT_ARGS[@]}" \
"${GUI_PORT_ARGS[@]}" \
"${GPU_DOCKER_ARGS[@]}" \
-v "$SCRIPT_DIR:$WORKSPACE" \
"${X11_ARGS[@]}" \
-e LUCY_GPU_MODE="$LUCY_GPU_MODE" \
-e LUCY_LCP_PUBLISHED_HOST_PORT="$PORT_CONTROL_PANEL" \
-e LUCY_LCP_CONTAINER_PORT="$PORT_CONTROL_PANEL_CONTAINER" \
-e LUCY_LCP_SCHEME="$LCP_SCHEME" \
Expand All @@ -261,7 +290,9 @@ else
docker run "${DOCKER_RUN_PLATFORM_ARGS[@]}" "${DOCKER_RUN_IT[@]}" --rm \
--name lucy_dev \
"${DOCKER_PORT_ARGS[@]}" \
"${GPU_DOCKER_ARGS[@]}" \
-v "$SCRIPT_DIR:$WORKSPACE" \
"${X11_ARGS[@]}" \
-e LUCY_GPU_MODE="$LUCY_GPU_MODE" \
"$IMAGE_NAME" -c "${SETUP} && ${SOURCE_WORKSPACE} && $*"
fi
Loading
Loading