feat(quickemu): enable local SPICE over Unix sockets with VirGL
- Use Unix sockets for local --display spice to allow GL/VirGL acceleration - Use egl-headless backend to provide GL context for virtio-gpu-gl; do not enable SPICE gl=on (avoids blocking QEMU main loop) - Add -vga none for SPICE modes to prevent duplicate scanouts - Update start_viewer() to support spice+unix:// and spice://localhost:port connection modes for spicy and remote-viewer clients - Clean up Unix socket (.sock) files alongside existing .spice file cleanup Provides local GL-accelerated guest 3D via VirGL while keeping remote access over TCP unchanged. Signed-off-by: Martin Wimpress <martin@wimpress.org>
This commit is contained in:
parent
a89b6588ff
commit
ac4b75f46d
226
quickemu
226
quickemu
|
|
@ -166,11 +166,13 @@ function kill_vm() {
|
|||
echo " - ${VMNAME} is not running."
|
||||
rm -f "${VMDIR}/${VMNAME}.pid"
|
||||
rm -f "${VMDIR}/${VMNAME}.spice"
|
||||
rm -f "${VMDIR}/${VMNAME}.sock"
|
||||
elif [ -n "${VM_PID}" ]; then
|
||||
if kill -9 "${VM_PID}" > /dev/null 2>&1; then
|
||||
echo " - ${VMNAME} (${VM_PID}) killed."
|
||||
rm -f "${VMDIR}/${VMNAME}.pid"
|
||||
rm -f "${VMDIR}/${VMNAME}.spice"
|
||||
rm -f "${VMDIR}/${VMNAME}.sock"
|
||||
else
|
||||
echo " - ${VMNAME} (${VM_PID}) was not killed."
|
||||
fi
|
||||
|
|
@ -1324,7 +1326,20 @@ function configure_display() {
|
|||
gtk) DISPLAY_RENDER="${display},grab-on-hover=on,zoom-to-fit=off,gl=${gl}";;
|
||||
none) DISPLAY_RENDER="none"
|
||||
gl="off";; # No display backend means no GL context
|
||||
spice) DISPLAY_RENDER="none";;
|
||||
spice)
|
||||
# For local SPICE with GL, use egl-headless to provide the GL context
|
||||
# for VirGL. Don't use gl=on in SPICE itself as it blocks the main loop.
|
||||
if [ -z "${ACCESS}" ] || [ "${ACCESS}" == "local" ]; then
|
||||
if [ "${gl}" == "on" ]; then
|
||||
DISPLAY_RENDER="egl-headless,rendernode=/dev/dri/renderD128"
|
||||
else
|
||||
DISPLAY_RENDER="none"
|
||||
fi
|
||||
else
|
||||
# Remote access cannot use GL
|
||||
DISPLAY_RENDER="none"
|
||||
gl="off"
|
||||
fi;;
|
||||
sdl) DISPLAY_RENDER="${display},gl=${gl}";;
|
||||
spice-app) DISPLAY_RENDER="${display},gl=${gl}";;
|
||||
*) DISPLAY_RENDER="${display}";;
|
||||
|
|
@ -1349,8 +1364,15 @@ function configure_display() {
|
|||
echo -n " - Display: ${display^^}, ${DISPLAY_DEVICE}, GL (${gl}), VirGL (off)"
|
||||
fi
|
||||
|
||||
# Disable default VGA for SPICE modes to prevent duplicate scanouts
|
||||
# SPICE creates its own display output; the default VGA would create a second one
|
||||
case ${display} in
|
||||
none|spice|spice-app) VGA="-vga none";;
|
||||
*) VGA="";;
|
||||
esac
|
||||
|
||||
# Build the video configuration
|
||||
VIDEO="-device ${DISPLAY_DEVICE}"
|
||||
VIDEO="${VGA:+${VGA} }-device ${DISPLAY_DEVICE}"
|
||||
|
||||
# ARM64 needs ramfb for UEFI boot display before virtio-gpu driver loads
|
||||
if [ "${ARCH_VM}" == "aarch64" ]; then
|
||||
|
|
@ -1401,6 +1423,7 @@ function configure_audio() {
|
|||
function configure_ports() {
|
||||
echo -n "" > "${VMDIR}/${VMNAME}.ports"
|
||||
rm -f "${VMDIR}/${VMNAME}.spice"
|
||||
rm -f "${VMDIR}/${VMNAME}.sock"
|
||||
|
||||
if [ -z "${ssh_port}" ]; then
|
||||
# Find a free port to expose ssh to the guest
|
||||
|
|
@ -1429,40 +1452,43 @@ function configure_ports() {
|
|||
|
||||
if [ "${display}" == "none" ] || [ "${display}" == "spice" ] || [ "${display}" == "spice-app" ]; then
|
||||
SPICE="disable-ticketing=on"
|
||||
# gl=on can be use with 'spice' too, but only over local connections (not tcp ports)
|
||||
|
||||
if [ "${display}" == "spice-app" ]; then
|
||||
# spice-app uses QEMU's built-in viewer with GL support
|
||||
SPICE+=",gl=${gl}"
|
||||
fi
|
||||
|
||||
# TODO: Don't use ports so local-only connections can be used with gl=on
|
||||
if [ -z "${spice_port}" ]; then
|
||||
# Find a free port for spice
|
||||
spice_port=$(get_port 5930 9)
|
||||
fi
|
||||
|
||||
# ALLOW REMOTE ACCESS TO SPICE OVER LAN RATHER THAN JUST LOCALHOST
|
||||
if [ -z "${ACCESS}" ]; then
|
||||
SPICE_ADDR="127.0.0.1"
|
||||
else
|
||||
if [ "${ACCESS}" == "remote" ]; then
|
||||
SPICE_ADDR=""
|
||||
elif [ "${ACCESS}" == "local" ]; then
|
||||
SPICE_ADDR="127.0.0.1"
|
||||
echo " - SPICE: Enabled"
|
||||
elif [ "${display}" == "spice" ]; then
|
||||
# For spice display, use Unix socket for local or TCP for remote
|
||||
if [ -z "${ACCESS}" ] || [ "${ACCESS}" == "local" ]; then
|
||||
# Unix socket mode for local access
|
||||
# GL context is provided by egl-headless display, not SPICE
|
||||
SPICE+=",unix=on,addr=${VMDIR}/${VMNAME}.sock"
|
||||
echo "unix,${VMDIR}/${VMNAME}.sock" >> "${VMDIR}/${VMNAME}.ports"
|
||||
echo "${VMDIR}/${VMNAME}.sock" > "${VMDIR}/${VMNAME}.spice"
|
||||
echo -n " - SPICE: On host: spicy --uri=\"spice+unix://${VMDIR}/${VMNAME}.sock\" --title \"${VMNAME}\""
|
||||
if [ "${guest_os}" != "macos" ] && [ -n "${PUBLIC}" ]; then
|
||||
echo -n " --spice-shared-dir ${PUBLIC}"
|
||||
fi
|
||||
echo "${FULLSCREEN}"
|
||||
else
|
||||
SPICE_ADDR="${ACCESS}"
|
||||
fi
|
||||
fi
|
||||
# TCP mode for remote access (no GL support)
|
||||
if [ -z "${spice_port}" ]; then
|
||||
spice_port=$(get_port 5930 9)
|
||||
fi
|
||||
|
||||
if [ -z "${spice_port}" ]; then
|
||||
echo " - SPICE: All SPICE ports have been exhausted."
|
||||
if [ "${display}" == "none" ] || [ "${display}" == "spice" ] || [ "${display}" == "spice-app" ]; then
|
||||
echo " ERROR! Requested SPICE display, but no SPICE ports are free."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
if [ "${display}" == "spice-app" ]; then
|
||||
echo " - SPICE: Enabled"
|
||||
else
|
||||
if [ "${ACCESS}" == "remote" ]; then
|
||||
SPICE_ADDR=""
|
||||
else
|
||||
SPICE_ADDR="${ACCESS}"
|
||||
fi
|
||||
|
||||
if [ -z "${spice_port}" ]; then
|
||||
echo " - SPICE: All SPICE ports have been exhausted."
|
||||
echo " ERROR! Requested SPICE display, but no SPICE ports are free."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SPICE+=",port=${spice_port},addr=${SPICE_ADDR}"
|
||||
echo "spice,${spice_port}" >> "${VMDIR}/${VMNAME}.ports"
|
||||
echo "${spice_port}" > "${VMDIR}/${VMNAME}.spice"
|
||||
echo -n " - SPICE: On host: spicy --title \"${VMNAME}\" --port ${spice_port}"
|
||||
|
|
@ -1470,8 +1496,37 @@ function configure_ports() {
|
|||
echo -n " --spice-shared-dir ${PUBLIC}"
|
||||
fi
|
||||
echo "${FULLSCREEN}"
|
||||
SPICE="${SPICE},port=${spice_port},addr=${SPICE_ADDR}"
|
||||
fi
|
||||
elif [ "${display}" == "none" ]; then
|
||||
# display=none with SPICE for headless VMs - use TCP for remote access
|
||||
if [ -z "${spice_port}" ]; then
|
||||
spice_port=$(get_port 5930 9)
|
||||
fi
|
||||
|
||||
if [ -z "${ACCESS}" ]; then
|
||||
SPICE_ADDR="127.0.0.1"
|
||||
elif [ "${ACCESS}" == "remote" ]; then
|
||||
SPICE_ADDR=""
|
||||
elif [ "${ACCESS}" == "local" ]; then
|
||||
SPICE_ADDR="127.0.0.1"
|
||||
else
|
||||
SPICE_ADDR="${ACCESS}"
|
||||
fi
|
||||
|
||||
if [ -z "${spice_port}" ]; then
|
||||
echo " - SPICE: All SPICE ports have been exhausted."
|
||||
echo " ERROR! Requested SPICE display, but no SPICE ports are free."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SPICE+=",port=${spice_port},addr=${SPICE_ADDR}"
|
||||
echo "spice,${spice_port}" >> "${VMDIR}/${VMNAME}.ports"
|
||||
echo "${spice_port}" > "${VMDIR}/${VMNAME}.spice"
|
||||
echo -n " - SPICE: On host: spicy --title \"${VMNAME}\" --port ${spice_port}"
|
||||
if [ "${guest_os}" != "macos" ] && [ -n "${PUBLIC}" ]; then
|
||||
echo -n " --spice-shared-dir ${PUBLIC}"
|
||||
fi
|
||||
echo "${FULLSCREEN}"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
|
@ -2108,6 +2163,7 @@ function vm_boot() {
|
|||
echo " - Process: ERROR! Failed to start ${VM} as ${VMNAME}"
|
||||
rm -f "${VMDIR}/${VMNAME}.pid"
|
||||
rm -f "${VMDIR}/${VMNAME}.spice"
|
||||
rm -f "${VMDIR}/${VMNAME}.sock"
|
||||
echo && cat "${VMDIR}/${VMNAME}.log"
|
||||
exit 1
|
||||
fi
|
||||
|
|
@ -2115,38 +2171,85 @@ function vm_boot() {
|
|||
}
|
||||
|
||||
function start_viewer {
|
||||
errno=0
|
||||
if [ "${viewer}" != "none" ]; then
|
||||
# If output is 'none' then SPICE was requested.
|
||||
if [ "${display}" == "spice" ]; then
|
||||
if [ "${viewer}" == "remote-viewer" ]; then
|
||||
# show via viewer: remote-viewer
|
||||
if [ -n "${PUBLIC}" ]; then
|
||||
echo " - Viewer: ${viewer} --title \"${VMNAME}\" --spice-shared-dir \"${PUBLIC}\" ${FULLSCREEN} \"spice://localhost:${spice_port}\" >/dev/null 2>&1 &"
|
||||
${viewer} --title "${VMNAME}" --spice-shared-dir "${PUBLIC}" ${FULLSCREEN} "spice://localhost:${spice_port}" >/dev/null 2>&1 &
|
||||
errno=$?
|
||||
else
|
||||
echo " - Viewer: ${viewer} --title \"${VMNAME}\" ${FULLSCREEN} \"spice://localhost:${spice_port}\" >/dev/null 2>&1 &"
|
||||
${viewer} --title "${VMNAME}" ${FULLSCREEN} "spice://localhost:${spice_port}" >/dev/null 2>&1 &
|
||||
errno=$?
|
||||
fi
|
||||
elif [ "${viewer}" == "spicy" ]; then
|
||||
# show via viewer: spicy
|
||||
if [ -n "${PUBLIC}" ]; then
|
||||
echo " - Viewer: ${viewer} --title \"${VMNAME}\" --port \"${spice_port}\" --spice-shared-dir \"${PUBLIC}\" \"${FULLSCREEN}\" >/dev/null 2>&1 &"
|
||||
${viewer} --title "${VMNAME}" --port "${spice_port}" --spice-shared-dir "${PUBLIC}" "${FULLSCREEN}" >/dev/null 2>&1 &
|
||||
errno=$?
|
||||
else
|
||||
echo " - Viewer: ${viewer} --title \"${VMNAME}\" --port \"${spice_port}\" \"${FULLSCREEN}\" >/dev/null 2>&1 &"
|
||||
${viewer} --title "${VMNAME}" --port "${spice_port}" "${FULLSCREEN}" >/dev/null 2>&1 &
|
||||
errno=$?
|
||||
fi
|
||||
# Exit early if viewer is disabled or display is not SPICE
|
||||
if [ "${viewer}" == "none" ] || [ "${display}" != "spice" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
# Build viewer arguments based on connection mode (Unix socket or TCP)
|
||||
local viewer_args=()
|
||||
local viewer_uri=""
|
||||
local errno=0
|
||||
|
||||
# Determine connection mode from .spice file content
|
||||
# - Unix socket mode: file contains a path (with /)
|
||||
# - TCP mode: file contains just a port number
|
||||
local spice_info=""
|
||||
if [ -r "${VMDIR}/${VMNAME}.spice" ]; then
|
||||
spice_info=$(cat "${VMDIR}/${VMNAME}.spice")
|
||||
fi
|
||||
|
||||
if [[ "${spice_info}" == */* ]]; then
|
||||
# Unix socket mode (path contains /)
|
||||
local SPICE_SOCKET="${spice_info}"
|
||||
if [ "${viewer}" == "spicy" ]; then
|
||||
viewer_args+=("--uri=spice+unix://${SPICE_SOCKET}")
|
||||
else
|
||||
viewer_uri="spice+unix://${SPICE_SOCKET}"
|
||||
fi
|
||||
elif [ -n "${spice_info}" ]; then
|
||||
# TCP mode (port number)
|
||||
if [ "${viewer}" == "spicy" ]; then
|
||||
viewer_args+=("--port" "${spice_info}")
|
||||
else
|
||||
viewer_uri="spice://localhost:${spice_info}"
|
||||
fi
|
||||
else
|
||||
# Fallback: no .spice file, use ACCESS variable to determine mode
|
||||
if [ -z "${ACCESS}" ] || [ "${ACCESS}" == "local" ]; then
|
||||
# Unix socket mode
|
||||
local SPICE_SOCKET="${VMDIR}/${VMNAME}.sock"
|
||||
if [ "${viewer}" == "spicy" ]; then
|
||||
viewer_args+=("--uri=spice+unix://${SPICE_SOCKET}")
|
||||
else
|
||||
viewer_uri="spice+unix://${SPICE_SOCKET}"
|
||||
fi
|
||||
if [ ${errno} -ne 0 ]; then
|
||||
echo "WARNING! Could not start viewer (${viewer}) Err: ${errno}"
|
||||
else
|
||||
# TCP mode for remote access
|
||||
if [ "${viewer}" == "spicy" ]; then
|
||||
viewer_args+=("--port" "${spice_port}")
|
||||
else
|
||||
viewer_uri="spice://localhost:${spice_port}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Add common arguments
|
||||
viewer_args+=("--title" "${VMNAME}")
|
||||
|
||||
# Add shared directory if configured (not for macOS guests)
|
||||
if [ "${guest_os}" != "macos" ] && [ -n "${PUBLIC}" ]; then
|
||||
viewer_args+=("--spice-shared-dir" "${PUBLIC}")
|
||||
fi
|
||||
|
||||
# Add fullscreen if requested
|
||||
if [ -n "${FULLSCREEN}" ]; then
|
||||
viewer_args+=("${FULLSCREEN}")
|
||||
fi
|
||||
|
||||
# Add URI for remote-viewer (spicy uses --uri= in the args already)
|
||||
if [ "${viewer}" == "remote-viewer" ] && [ -n "${viewer_uri}" ]; then
|
||||
viewer_args+=("${viewer_uri}")
|
||||
fi
|
||||
|
||||
# Launch the viewer
|
||||
echo " - Viewer: ${viewer} ${viewer_args[*]} >/dev/null 2>&1 &"
|
||||
"${viewer}" "${viewer_args[@]}" >/dev/null 2>&1 &
|
||||
errno=$?
|
||||
|
||||
if [ ${errno} -ne 0 ]; then
|
||||
echo "WARNING! Could not start viewer (${viewer}) Err: ${errno}"
|
||||
fi
|
||||
}
|
||||
|
||||
function shortcut_create {
|
||||
|
|
@ -2726,6 +2829,7 @@ if [ -n "${VM}" ] && [ -e "${VM}" ]; then
|
|||
VM_PID=""
|
||||
rm -f "${VMDIR}/${VMNAME}.pid"
|
||||
rm -f "${VMDIR}/${VMNAME}.spice"
|
||||
rm -f "${VMDIR}/${VMNAME}.sock"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue