From 52a144dfd84518c386999041bffd3ccf28af9b12 Mon Sep 17 00:00:00 2001 From: Pierre Delagrave Date: Sun, 22 Mar 2026 13:51:28 -0400 Subject: [PATCH] Upgrade to Wine 11.5 devel with EGL support and fix GPU rendering on NVIDIA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wine 11 switches from GLX to EGL as the default OpenGL backend, and requires several fixes to render correctly with an NVIDIA GPU under Docker. **Dockerfile** - Bump Wine to 11.5~trixie-1 devel (EGL default since 10.17, Wayland since 9.9) - Add libegl1:i386, libgl1:i386, libvulkan1:i386 for 32-bit EGL/GL/Vulkan parity - Register NVIDIA as a GLVND EGL vendor by baking 10_nvidia.json into the image so the EGL dispatcher loads libEGL_nvidia.so.0 when injected by the NVIDIA container runtime (harmless when NVIDIA is absent — GLVND skips missing libs) **entrypoint.sh** - Fix ownership check depth: maxdepth 0 → maxdepth 1 so root-owned .wine (created by mkdir -p before privilege drop) is detected and chowned - Fix gosu supplementary groups: gosu user:user skips initgroups() and drops all supplementary groups including the DRI render group; use gosu user instead - Register DRI render group by GID before privilege drop using getent group to avoid silent conflict with Mesa's pre-existing render:990 group **update_zwift.sh** - Skip WebView2 install: both bootstrapper and standalone require the MicrosoftEdgeUpdate COM service which Wine cannot activate; SilentLaunch bypasses the launcher UI so WebView2 is not needed at runtime **zwift.sh** - Add --ipc=host alongside the X11 socket mount so Mesa can attach to X11 shared memory (required for DRI2/DRI3 screen initialization) Co-Authored-By: Claude Sonnet 4.6 --- src/Dockerfile | 28 ++++++++++++++++++---------- src/entrypoint.sh | 22 ++++++++++++++++++++-- src/update_zwift.sh | 7 +++---- src/zwift.sh | 2 ++ 4 files changed, 43 insertions(+), 16 deletions(-) diff --git a/src/Dockerfile b/src/Dockerfile index b0ea886d..4cfdf0ff 100644 --- a/src/Dockerfile +++ b/src/Dockerfile @@ -21,23 +21,22 @@ RUN git clone https://github.com/quietvoid/runfromprocess-rs . \ FROM debian:${DEBIAN_VERSION}-slim AS wine-base -# As at May 2024 Wayland Native works wine 9.9 or later: -# WINE_BRANCH="devel" -# For Specific version fix add WINE_VERSION, -# make sure to add "=" to the start, comment out for latest -# WINE_VERSION="=9.9~bookworm-1" +# Wine 11: EGL is the default OpenGL backend (replaced GLX since 10.17). +# Wayland native support included since 9.9. Pinned to 11.5 devel. +# To update, change WINE_VERSION (prefix with "=", omit for latest devel) +# WINE_VERSION="=11.5~${DEBIAN_VERSION}-1" ARG DEBIAN_VERSION ARG WINE_BRANCH="devel" -ARG WINE_VERSION="=9.9~${DEBIAN_VERSION}-1" -ARG WINETRICKS_VERSION=20240105 +ARG WINE_VERSION="=11.5~${DEBIAN_VERSION}-1" +ARG WINETRICKS_VERSION=20260125 # Install prerequisites # - ca-certificates for wget and curl # - curl used in zwift authentication script # - gamemode for freedesktop screensaver inhibit # - gosu for invoking scripts in entrypoint -# - libegl1 and libgl1 for GL library -# - libvulkan1 for vulkan loader library +# - libegl1/libgl1 and i386 variants for GL/EGL library +# - libvulkan1 and i386 variant for vulkan loader library # - procps for pgrep # - sudo for normal user installation # - wget for downloading winehq key @@ -51,8 +50,11 @@ RUN dpkg --add-architecture i386 \ gamemode \ gosu \ libegl1 \ + libegl1:i386 \ libgl1 \ + libgl1:i386 \ libvulkan1 \ + libvulkan1:i386 \ procps \ sudo \ wget \ @@ -75,11 +77,17 @@ RUN wget -qO /etc/apt/trusted.gpg.d/winehq.asc https://dl.winehq.org/wine-builds && chmod +x /usr/local/bin/winetricks # Create passwordless user and make nvidia libraries discoverable +# Register NVIDIA as a GLVND EGL vendor so the dispatcher loads libEGL_nvidia.so.0 +# when injected by the NVIDIA container runtime. Harmless if NVIDIA is absent +# (GLVND silently skips vendors whose library is not found). RUN adduser --disabled-password --gecos '' user \ && adduser user sudo \ && echo '%SUDO ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers \ && echo "/usr/local/nvidia/lib" >> /etc/ld.so.conf.d/nvidia.conf \ - && echo "/usr/local/nvidia/lib64" >> /etc/ld.so.conf.d/nvidia.conf + && echo "/usr/local/nvidia/lib64" >> /etc/ld.so.conf.d/nvidia.conf \ + && mkdir -p /usr/share/glvnd/egl_vendor.d \ + && printf '{"file_format_version":"1.0.0","ICD":{"library_path":"libEGL_nvidia.so.0"}}\n' \ + > /usr/share/glvnd/egl_vendor.d/10_nvidia.json # Required for non-glvnd setups ENV LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu:/usr/lib/i386-linux-gnu:/usr/local/nvidia/lib:/usr/local/nvidia/lib64 diff --git a/src/entrypoint.sh b/src/entrypoint.sh index f278b5ab..477112da 100755 --- a/src/entrypoint.sh +++ b/src/entrypoint.sh @@ -142,7 +142,7 @@ if [[ ${CONTAINER_TOOL} == "docker" ]]; then # This avoids a costly recursive find on every normal startup local target="${1:?}" local result - [[ -d ${target} ]] && result="$(find "${target}" -maxdepth 0 \( ! -user user -o ! -group user \) -print 2> /dev/null)" && [[ -n ${result} ]] + [[ -d ${target} ]] && result="$(find "${target}" -maxdepth 1 \( ! -user user -o ! -group user \) -print 2> /dev/null)" && [[ -n ${result} ]] } update_ownership() { @@ -181,7 +181,25 @@ if [[ ${CONTAINER_TOOL} == "docker" ]]; then exit 1 fi - startup_cmd=(gosu user:user "${startup_cmd[@]}") + # Add DRI render group to user so gosu preserves GPU access. + # gosu sets supplementary groups from /etc/group, not from Docker's --group-add, + # so we must register the DRI device's GID in the container before dropping privileges. + # Note: do not use groupadd -f (silently no-ops if group name exists with different GID). + if [[ -d /dev/dri ]]; then + dri_gid="$(stat -c '%g' /dev/dri/renderD128 2>/dev/null || stat -c '%g' /dev/dri/card0 2>/dev/null || true)" + if [[ -n ${dri_gid} ]]; then + if ! getent group "${dri_gid}" > /dev/null 2>&1; then + groupadd -g "${dri_gid}" dri_render 2>/dev/null || true + fi + dri_group="$(getent group "${dri_gid}" | cut -d: -f1)" + if [[ -n ${dri_group} ]]; then + usermod -aG "${dri_group}" user 2>/dev/null || true + msgbox info "Added user to ${dri_group} group (gid=${dri_gid}) for DRI access" + fi + fi + fi + + startup_cmd=(gosu user "${startup_cmd[@]}") fi ######################################### diff --git a/src/update_zwift.sh b/src/update_zwift.sh index 4aceec61..bf5d2879 100755 --- a/src/update_zwift.sh +++ b/src/update_zwift.sh @@ -137,10 +137,9 @@ install_zwift() { msgbox info "Installing prerequisites using winetricks" winetricks -q dotnet20 dotnet48 d3dcompiler_47 || return 1 - # download and install webview 2 - msgbox info "Downloading and installing webview2" - wget -O webview2-setup.exe https://go.microsoft.com/fwlink/p/?LinkId=2124703 || return 1 - wine webview2-setup.exe /silent /install || return 1 + # WebView2 install skipped: both bootstrapper and standalone require the + # MicrosoftEdgeUpdate COM service which Wine cannot activate. SilentLaunch + # bypasses the launcher UI so WebView2 is not needed at runtime. # enable Wayland support, requires DISPLAY to be blank to use Wayland msgbox info "Enabling Wayland support" diff --git a/src/zwift.sh b/src/zwift.sh index 59376ddf..436bbe44 100755 --- a/src/zwift.sh +++ b/src/zwift.sh @@ -597,6 +597,8 @@ if [[ ${window_manager} == "XWayland" ]] || [[ ${window_manager} == "XOrg" ]]; t if [[ -d /tmp/.X11-unix ]]; then container_args+=(-v /tmp/.X11-unix:/tmp/.X11-unix) + # Share host IPC namespace so Mesa can attach to X11 shared memory (needed for DRI2/DRI3) + container_args+=(--ipc=host) else msgbox error "X11 socket does not exist at /tmp/.X11-unix" exit 1