DaVinci-Resolve-Intel-Arc-O.../install-davinci-resolve-intel-arc.sh

646 lines
28 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# ==============================================================================
# DaVinci Resolve Installer for Omarchy (Arch Linux + Hyprland + Intel Arc)
#
# Adapted from the NVIDIA-Open variant for systems with an Intel Arc GPU
# using the Xe kernel driver. Covers all current Arc-class hardware:
# - Alchemist Xe-HPG dGPU (Arc Axxx, e.g. A770) — xe driver
# - Battlemage Xe2 dGPU (Arc B580/B570, late 2024) — xe driver
# - Panther Lake Xe3-LPG iGPU (Arc B360/B370/B380/B390, — xe driver
# Core Ultra 300 series, launched Jan 2026)
# Older iGPUs (UHD/Iris on Gen 912.5) sit on the i915 driver and are
# also detected; they're left visible to OpenCL but not preferred for
# pinning when an xe device is available.
#
# IMPORTANT — SUPPORT CAVEAT
# Blackmagic does NOT officially support Intel GPUs on Linux. Resolve uses
# OpenCL (or CUDA on NVIDIA) for compute. With intel-compute-runtime
# installed, the Arc shows up as an OpenCL device and basic editing /
# playback / transcode often work, but specific effects, Neural Engine,
# Fairlight FX, and noise reduction may fall back to CPU or fail. Treat
# this as community/experimental — not a supported configuration.
#
# What this script does (same shape as the NVIDIA version):
# 1. Finds the Resolve ZIP in ~/Downloads/
# 2. Installs system + Intel GPU runtime dependencies
# 3. Extracts the ZIP → .run → squashfs-root (AppImage payload)
# 4. Replaces bundled glib/gio/gmodule with system versions (ABI-safe)
# 5. Keeps vendor libc++/libc++abi (removing these breaks Resolve)
# 6. Installs to /opt/resolve with RPATH patching for all ELF binaries
# 7. Ensures legacy libcrypt.so.1 is available (Arch dropped it)
# 8. Installs desktop entries, icons, and udev rules
# 9. Creates an XWayland wrapper script for Hyprland with Intel/OpenCL hints
#
# Prerequisites:
# - Omarchy (Arch Linux) with kernel 6.12+ (Xe driver). 6.19+ recommended
# for stable Battlemage support.
# - DaVinci Resolve Linux ZIP downloaded to ~/Downloads/
# - Internet connection
# ==============================================================================
set -euo pipefail
log(){ echo -e "$*"; }
warn(){ echo -e "⚠️ $*" >&2; }
err(){ echo -e "$*" >&2; exit 1; }
# ==================== Preflight: kernel + GPU ====================
KVER_FULL="$(uname -r)"
KMAJ="${KVER_FULL%%.*}"; KREST="${KVER_FULL#*.}"; KMIN="${KREST%%.*}"
if (( KMAJ < 6 )) || { (( KMAJ == 6 )) && (( KMIN < 12 )); }; then
warn "Kernel ${KVER_FULL} is older than 6.12 — Arc B580 (Battlemage) needs the Xe driver from 6.12+. Continue at your own risk."
else
log "Kernel ${KVER_FULL} OK for Xe driver"
fi
if lspci -nn 2>/dev/null | grep -qiE 'Intel.*(Arc|Battlemage|Alchemist|DG2)'; then
log "Intel Arc GPU detected"
else
warn "No Intel Arc GPU detected via lspci — script will continue but you may not have GPU acceleration"
fi
# ==================== Locate ZIP ====================
ZIP_DIR="${HOME}/Downloads"
shopt -s nullglob
ZIP_FILES=("${ZIP_DIR}"/DaVinci_Resolve*_Linux.zip)
shopt -u nullglob
if [[ ${#ZIP_FILES[@]} -eq 0 ]]; then
err "Put the official DaVinci Resolve Linux ZIP in ${ZIP_DIR}"
fi
# Pick the newest ZIP by mtime without parsing `ls` output.
RESOLVE_ZIP=""
RESOLVE_ZIP_MTIME=0
for _zf in "${ZIP_FILES[@]}"; do
_m=$(stat -c%Y "${_zf}" 2>/dev/null || echo 0)
if (( _m > RESOLVE_ZIP_MTIME )); then
RESOLVE_ZIP_MTIME=${_m}
RESOLVE_ZIP="${_zf}"
fi
done
[[ -n "${RESOLVE_ZIP}" ]] || err "Could not determine newest ZIP file"
log "Using installer ZIP: ${RESOLVE_ZIP}"
# ==================== Package Installation ====================
if [[ "${RESOLVE_FULL_UPGRADE:-0}" == "1" ]]; then
log "Updating system packages (RESOLVE_FULL_UPGRADE=1)..."
sudo pacman -Syu --noconfirm
else
log "Skipping full system upgrade (set RESOLVE_FULL_UPGRADE=1 to enable)"
sudo pacman -Sy --noconfirm
fi
# Build/extraction tools (same as NVIDIA variant)
log "Installing required tools..."
if ! sudo pacman -S --needed --noconfirm unzip patchelf libarchive xdg-user-dirs desktop-file-utils file gtk-update-icon-cache; then
warn "Some optional tools failed to install, continuing anyway..."
fi
# Runtime dependencies — Intel-specific stack added on top of the common set:
# libxcrypt-compat: Provides legacy libcrypt.so.1 (Arch moved to v2)
# ffmpeg4.4: Older FFmpeg version Resolve links against
# glu / gtk2 / fuse2: Same as NVIDIA variant (UI / AppImage compat)
# mesa: OpenGL stack (Iris driver for Intel)
# vulkan-intel: ANV — Vulkan ICD for Intel
# intel-media-driver: iHD VA-API driver (HW decode/encode H.264/HEVC/AV1)
# intel-compute-runtime: NEO — OpenCL (and Level Zero) ICD for Intel GPUs.
# This is what Resolve will actually use for compute.
# level-zero-loader: Loader for Intel oneAPI Level Zero (used by NEO)
# ocl-icd: Generic OpenCL ICD loader (libOpenCL.so.1)
# clinfo: Sanity tool — used at the end to confirm Resolve
# will see the Arc as an OpenCL device.
log "Installing Intel GPU + Resolve runtime dependencies..."
INTEL_RUNTIME_PKGS=(
libxcrypt-compat ffmpeg4.4 glu fuse2
mesa vulkan-intel intel-media-driver
intel-compute-runtime level-zero-loader ocl-icd clinfo
)
# Note: NOT installing gtk2 — verified via ldd that Resolve 21 does not link
# libgtk-x11-2.0. It links libgdk_pixbuf-2.0 (a different package, gdk-pixbuf2,
# already pulled in by virtually any desktop install). The gtk2 dep in older
# Resolve install scripts was a leftover from when Resolve had GTK file
# dialogs — modern Resolve is fully Qt.
if ! sudo pacman -S --needed --noconfirm "${INTEL_RUNTIME_PKGS[@]}"; then
err "pacman failed to install one or more runtime dependencies — re-run with the offending package name to see the error"
fi
# Compute-stack packages are non-negotiable: without them Resolve sees no
# OpenCL device and crashes with "Unsupported GPU Processing Mode" on first
# launch. Verify each is actually present before continuing.
for pkg in intel-compute-runtime level-zero-loader ocl-icd clinfo; do
pacman -Q "$pkg" >/dev/null 2>&1 || err "Required package not installed: ${pkg}"
done
log "Intel compute stack confirmed installed"
# Resolve's built-in extras downloader expects RHEL-style cert path
if [[ ! -e /etc/pki/tls ]]; then
sudo mkdir -p /etc/pki
sudo ln -sf /etc/ssl /etc/pki/tls
fi
# ==================== Extraction ====================
NEEDED_GB=10
FREE_KB=$(df --output=avail -k "${ZIP_DIR}" | tail -n1); FREE_GB=$((FREE_KB/1024/1024))
(( FREE_GB >= NEEDED_GB )) || err "Not enough free space in ${ZIP_DIR}: ${FREE_GB} GiB < ${NEEDED_GB} GiB"
WORKDIR="$(mktemp -d -p "${ZIP_DIR}" .resolve-extract-XXXXXXXX)"
cleanup() {
if [[ -n "${WORKDIR:-}" && -d "${WORKDIR}" ]]; then
log "Cleaning up temporary directory..."
rm -rf "${WORKDIR}" 2>/dev/null || true
fi
}
trap cleanup EXIT
log "Unpacking ZIP to ${WORKDIR}"
unzip -q "${RESOLVE_ZIP}" -d "${WORKDIR}"
RUN_FILE="$(find "${WORKDIR}" -maxdepth 2 -type f -name 'DaVinci_Resolve_*_Linux.run' | head -n1 || true)"
[[ -n "${RUN_FILE}" ]] || err "Could not find the .run installer in the ZIP"
chmod +x "${RUN_FILE}"
EX_DIR="$(dirname "${RUN_FILE}")"
log "Extracting AppImage payload…"
if ! ( cd "${EX_DIR}" && "./$(basename "${RUN_FILE}")" --appimage-extract >/dev/null ); then
err "Failed to extract AppImage payload"
fi
APPDIR="${EX_DIR}/squashfs-root"
[[ -d "${APPDIR}" ]] || err "Extraction failed (no squashfs-root)"
chmod -R u+rwX,go+rX,go-w "${APPDIR}" || warn "Could not normalize all permissions"
[[ -s "${APPDIR}/bin/resolve" ]] || err "resolve binary missing or zero-size"
# ==================== ABI-Safe Library Replacement ====================
# (Identical to NVIDIA variant — these are GPU-agnostic.)
pushd "${APPDIR}" >/dev/null
declare -A GLIB_LIBS=(
["/usr/lib/libglib-2.0.so.0"]="libs/libglib-2.0.so.0"
["/usr/lib/libgio-2.0.so.0"]="libs/libgio-2.0.so.0"
["/usr/lib/libgmodule-2.0.so.0"]="libs/libgmodule-2.0.so.0"
)
for syslib in "${!GLIB_LIBS[@]}"; do
target="${GLIB_LIBS[$syslib]}"
if [[ -e "${syslib}" ]]; then
rm -f "${target}" || true
ln -sf "${syslib}" "${target}" || warn "Failed to symlink ${syslib}"
else
warn "System library ${syslib} not found, keeping bundled version"
fi
done
if [[ -d "share/panels" ]]; then
pushd "share/panels" >/dev/null
tar -zxf dvpanel-framework-linux-x86_64.tgz 2>/dev/null || true
mkdir -p "${APPDIR}/libs"
find . -maxdepth 1 -type f -name '*.so' -exec mv -f {} "${APPDIR}/libs" \; 2>/dev/null || true
if [[ -d lib ]]; then
find lib -type f -name '*.so*' -exec mv -f {} "${APPDIR}/libs" \; 2>/dev/null || true
fi
popd >/dev/null
fi
rm -f "AppRun" "AppRun*" 2>/dev/null || true
rm -rf "installer" "installer*" 2>/dev/null || true
mkdir -p "bin"
ln -sf "../BlackmagicRAWPlayer/BlackmagicRawAPI" "bin/" 2>/dev/null || true
popd >/dev/null
# ==================== Install to /opt/resolve ====================
log "Installing Resolve to /opt/resolve…"
sudo rm -rf /opt/resolve
sudo mkdir -p /opt/resolve
if command -v rsync >/dev/null 2>&1; then
sudo rsync -a --delete "${APPDIR}/" /opt/resolve/
else
sudo cp -a "${APPDIR}/." /opt/resolve/
fi
sudo mkdir -p /opt/resolve/.license
log "Applying RPATH with patchelf (this may take a while for large libraries)…"
RPATH_DIRS=( "libs" "libs/plugins/sqldrivers" "libs/plugins/xcbglintegrations" "libs/plugins/imageformats"
"libs/plugins/platforms" "libs/Fusion" "plugins" "bin"
"BlackmagicRAWSpeedTest/BlackmagicRawAPI" "BlackmagicRAWSpeedTest/plugins/platforms"
"BlackmagicRAWSpeedTest/plugins/imageformats" "BlackmagicRAWSpeedTest/plugins/mediaservice"
"BlackmagicRAWSpeedTest/plugins/audio" "BlackmagicRAWSpeedTest/plugins/xcbglintegrations"
"BlackmagicRAWSpeedTest/plugins/bearer"
"BlackmagicRAWPlayer/BlackmagicRawAPI" "BlackmagicRAWPlayer/plugins/mediaservice"
"BlackmagicRAWPlayer/plugins/imageformats" "BlackmagicRAWPlayer/plugins/audio"
"BlackmagicRAWPlayer/plugins/platforms" "BlackmagicRAWPlayer/plugins/xcbglintegrations"
"BlackmagicRAWPlayer/plugins/bearer"
"Onboarding/plugins/xcbglintegrations" "Onboarding/plugins/qtwebengine"
"Onboarding/plugins/platforms" "Onboarding/plugins/imageformats"
"DaVinci Control Panels Setup/plugins/platforms"
"DaVinci Control Panels Setup/plugins/imageformats"
"DaVinci Control Panels Setup/plugins/bearer"
"DaVinci Control Panels Setup/AdminUtility/PlugIns/DaVinciKeyboards"
"DaVinci Control Panels Setup/AdminUtility/PlugIns/DaVinciPanels" )
RPATH_ABS=""; for p in "${RPATH_DIRS[@]}"; do RPATH_ABS+="/opt/resolve/${p}:"; done; RPATH_ABS+="\$ORIGIN"
if command -v patchelf >/dev/null 2>&1; then
PATCH_COUNT=0
PATCH_FAIL=0
PATCH_SKIP=0
while IFS= read -r -d '' f; do
FILE_INFO="$(file -b "$f" 2>/dev/null)"
if [[ "${FILE_INFO}" =~ ELF.*executable ]] || [[ "${FILE_INFO}" =~ ELF.*shared\ object ]]; then
CURRENT_RPATH="$(patchelf --print-rpath "$f" 2>/dev/null || true)"
if [[ "${CURRENT_RPATH}" == "${RPATH_ABS}" ]]; then
((PATCH_SKIP++)) || true
continue
fi
if sudo patchelf --set-rpath "${RPATH_ABS}" "$f" 2>/dev/null; then
((PATCH_COUNT++)) || true
else
((PATCH_FAIL++)) || true
FILE_SIZE=$(stat -c%s "$f" 2>/dev/null || echo 0)
if (( FILE_SIZE > 33554432 )); then
warn "Failed to patch large file: ${f##/opt/resolve/}"
fi
fi
fi
done < <(find /opt/resolve -type f -print0)
log "Patched RPATH: ${PATCH_COUNT} files (${PATCH_FAIL} failures, ${PATCH_SKIP} already correct)"
else
warn "patchelf not found, skipping RPATH patching"
fi
# Legacy libcrypt fix (same as NVIDIA variant)
sudo pacman -S --needed --noconfirm libxcrypt-compat || true
sudo ldconfig || true
if [[ -e /usr/lib/libcrypt.so.1 ]]; then
sudo ln -sf /usr/lib/libcrypt.so.1 /opt/resolve/libs/libcrypt.so.1
fi
# ==================== Desktop Integration ====================
log "Installing desktop entries and icons..."
declare -A DESKTOP_FILES=(
["/opt/resolve/share/DaVinciResolve.desktop"]="/usr/share/applications/DaVinciResolve.desktop"
["/opt/resolve/share/DaVinciControlPanelsSetup.desktop"]="/usr/share/applications/DaVinciControlPanelsSetup.desktop"
["/opt/resolve/share/blackmagicraw-player.desktop"]="/usr/share/applications/blackmagicraw-player.desktop"
["/opt/resolve/share/blackmagicraw-speedtest.desktop"]="/usr/share/applications/blackmagicraw-speedtest.desktop"
)
for src in "${!DESKTOP_FILES[@]}"; do
dest="${DESKTOP_FILES[$src]}"
if [[ -f "${src}" ]]; then
sudo install -D -m 0644 "${src}" "${dest}"
else
warn "Desktop file not found: ${src}"
fi
done
declare -A ICON_FILES=(
["/opt/resolve/graphics/DV_Resolve.png"]="/usr/share/icons/hicolor/128x128/apps/davinci-resolve.png"
["/opt/resolve/graphics/DV_Panels.png"]="/usr/share/icons/hicolor/128x128/apps/davinci-resolve-panels-setup.png"
["/opt/resolve/graphics/blackmagicraw-player_256x256_apps.png"]="/usr/share/icons/hicolor/256x256/apps/blackmagicraw-player.png"
["/opt/resolve/graphics/blackmagicraw-speedtest_256x256_apps.png"]="/usr/share/icons/hicolor/256x256/apps/blackmagicraw-speedtest.png"
)
for src in "${!ICON_FILES[@]}"; do
dest="${ICON_FILES[$src]}"
if [[ -f "${src}" ]]; then
sudo install -D -m 0644 "${src}" "${dest}"
else
warn "Icon file not found: ${src}"
fi
done
sudo update-desktop-database >/dev/null 2>&1 || true
sudo gtk-update-icon-cache -f /usr/share/icons/hicolor >/dev/null 2>&1 || true
for r in 99-BlackmagicDevices.rules 99-ResolveKeyboardHID.rules 99-DavinciPanel.rules; do
if [[ -f "/opt/resolve/share/etc/udev/rules.d/${r}" ]]; then
sudo install -D -m 0644 "/opt/resolve/share/etc/udev/rules.d/${r}" "/usr/lib/udev/rules.d/${r}"
fi
done
sudo udevadm control --reload-rules && sudo udevadm trigger || true
# ==================== XWayland Wrapper Script (Intel Arc) ====================
#
# Resolve still needs XWayland under Hyprland. Intel-specific bits:
# - OCL_ICD_VENDORS: Force the OpenCL ICD search dir.
# - NEOReadDebugKeys=1 +
# OverrideGpuAddressSpace=48:
# Workaround for OpenCL init failures on
# Battlemage. Applied ONLY if Battlemage is
# detected — Xe3 (Panther Lake) and Alchemist
# don't need it.
# - LIBVA_DRIVER_NAME=iHD: Pin Intel media driver for VA-API decode.
#
# GPU SELECTION (BDF-based, not index-based)
# The wrapper enumerates Intel GPUs at launch and picks one:
# - If a discrete Arc is present (PCI bus != 00, e.g. Battlemage B580 on
# 03:00.0), pick it.
# - Otherwise pick the only xe device available (covers Panther Lake
# laptops with just the Xe3 iGPU, etc.).
# The chosen device's PCI BDF is exported as ZE_AFFINITY_MASK in
# "DDDD:BB:DD.F" form (e.g. "0000:03:00.0"). NEO's numeric Level Zero index
# is NOT a reliable function of BDF order — on hybrid systems NEO often
# enumerates the discrete card at index 0 even though its BDF sorts later —
# so we use the BDF format which NEO matches against the actual device.
# Override with RESOLVE_GPU_BDF=0000:XX:YY.Z if needed; disable pinning
# entirely with RESOLVE_NO_PIN=1.
cat << 'EOF' | sudo tee /usr/local/bin/resolve-intel-arc >/dev/null
#!/usr/bin/env bash
set -euo pipefail
# Clear stale single-instance Qt lockfiles
if [[ -r /tmp ]]; then
for lockfile in /tmp/qtsingleapp-DaVinci*lockfile; do
[[ -f "$lockfile" ]] && rm -f "$lockfile" 2>/dev/null || true
done
fi
# Force XWayland under Hyprland/Wayland
export QT_QPA_PLATFORM=xcb
export QT_AUTO_SCREEN_SCALE_FACTOR=1
# Intel OpenCL / Level Zero (ICD search dir)
export OCL_ICD_VENDORS="${OCL_ICD_VENDORS:-/etc/OpenCL/vendors}"
# Intel VA-API (HW decode)
export LIBVA_DRIVER_NAME=iHD
# --- Detect Intel GPUs and pick the Arc to pin to ---
# NEO/Level Zero enumerates ALL Intel GPUs it supports (both i915 and xe
# backends), sorted by PCI BDF. The picker mirrors that ordering so the
# index we emit matches what ZE_AFFINITY_MASK expects.
#
# Selection priority:
# 1. xe-driven device on a non-SoC PCI bus (a discrete Battlemage like B580)
# 2. any xe-driven device (covers Panther Lake Xe3 iGPU)
# 3. first Intel device found (last-resort fallback)
arc_pick() {
local -a entries=()
local d vendor driver bdf
for d in /sys/class/drm/card[0-9]*; do
# Skip connector subentries like card0-DP-2
[[ "$d" =~ /card[0-9]+$ ]] || continue
[[ -e "$d/device/uevent" ]] || continue
vendor="$(cat "$d/device/vendor" 2>/dev/null || echo)"
[[ "$vendor" == "0x8086" ]] || continue
driver="$(awk -F= '/^DRIVER=/{print $2}' "$d/device/uevent")"
bdf="$(basename "$(readlink -f "$d/device")")"
entries+=("$bdf|$driver")
done
((${#entries[@]})) || return 1
# Sort by BDF (purely cosmetic — selection priorities below don't depend
# on order, and ZE_AFFINITY_MASK takes the BDF directly)
IFS=$'\n' read -rd '' -a entries < <(printf '%s\n' "${entries[@]}" | sort; printf '\0') || true
local picked_bdf="" picked_drv=""
local n=${#entries[@]} i e b drv
# Priority 1: discrete xe device (NOT on PCI bus 00 — those are SoC iGPUs)
for ((i=0; i<n; i++)); do
e="${entries[i]}"; b="${e%%|*}"; drv="${e##*|}"
if [[ "$drv" == "xe" && ! "$b" =~ ^0000:00: ]]; then
picked_bdf="$b"; picked_drv="$drv"; break
fi
done
# Priority 2: any xe device (covers Panther Lake Xe3-LPG iGPU)
if [[ -z "$picked_bdf" ]]; then
for ((i=0; i<n; i++)); do
e="${entries[i]}"; b="${e%%|*}"; drv="${e##*|}"
if [[ "$drv" == "xe" ]]; then
picked_bdf="$b"; picked_drv="$drv"; break
fi
done
fi
# Priority 3: first Intel device (last-resort fallback)
if [[ -z "$picked_bdf" ]]; then
picked_bdf="${entries[0]%%|*}"
picked_drv="${entries[0]##*|}"
fi
echo "$picked_bdf" "$picked_drv"
}
if [[ "${RESOLVE_NO_PIN:-0}" != "1" ]]; then
if [[ -n "${RESOLVE_GPU_BDF:-}" ]]; then
export ZE_AFFINITY_MASK="${RESOLVE_GPU_BDF}"
echo "Resolve: ZE_AFFINITY_MASK=${ZE_AFFINITY_MASK} (manual override via RESOLVE_GPU_BDF)" >&2
ARC_BDF="${RESOLVE_GPU_BDF}"
elif read -r ARC_BDF ARC_DRV < <(arc_pick); then
export ZE_AFFINITY_MASK="${ARC_BDF}"
echo "Resolve: pinning to Arc GPU at ${ARC_BDF} (driver=${ARC_DRV})" >&2
else
echo "Resolve: no Intel GPU found; not pinning ZE_AFFINITY_MASK" >&2
fi
fi
# OpenCL init workaround is specific to discrete Battlemage Xe2 silicon
# (Arc B5x0/B7x0 dGPU). Intel reuses the "Arc B-series" brand for the Xe3-LPG
# iGPUs in Panther Lake (B360/B370/B380/B390, launched Jan 2026), but those
# don't need — and don't want — this debug key. Gate on lspci match AND a
# non-SoC PCI bus, since iGPUs always live on 0000:00:02.0.
if lspci -nn 2>/dev/null | grep -qi 'Battlemage' \
&& [[ -n "${ARC_BDF:-}" && ! "${ARC_BDF}" =~ ^0000:00: ]]; then
export NEOReadDebugKeys=1
export OverrideGpuAddressSpace=48
fi
exec /opt/resolve/bin/resolve "$@"
EOF
sudo chmod +x /usr/local/bin/resolve-intel-arc
# Convenience symlink at /usr/bin/davinci-resolve
if [[ ! -e /usr/bin/davinci-resolve ]]; then
if [[ -x /usr/local/bin/resolve-intel-arc ]]; then
echo -e '#!/usr/bin/env bash\nexec /usr/local/bin/resolve-intel-arc "$@"' | sudo tee /usr/bin/davinci-resolve >/dev/null
else
echo -e '#!/usr/bin/env bash\nexec /opt/resolve/bin/resolve "$@"' | sudo tee /usr/bin/davinci-resolve >/dev/null
fi
sudo chmod +x /usr/bin/davinci-resolve
fi
# Point system .desktop entries at the wrapper
WRAPPER="/usr/local/bin/resolve-intel-arc"
if [[ -f /usr/share/applications/DaVinciResolve.desktop ]]; then
sudo sed -i "s|^Exec=.*|Exec=${WRAPPER} %U|" /usr/share/applications/DaVinciResolve.desktop
fi
if [[ -f /usr/share/applications/DaVinciResolveCaptureLogs.desktop ]]; then
sudo sed -i "s|^Exec=.*|Exec=${WRAPPER} %U|" /usr/share/applications/DaVinciResolveCaptureLogs.desktop
fi
sudo update-desktop-database >/dev/null 2>&1 || true
# User-level .desktop overrides system entry (survives reinstalls)
mkdir -p "${HOME}/.local/share/applications"
cat > "${HOME}/.local/share/applications/davinci-resolve-wrapper.desktop" << EOF
[Desktop Entry]
Type=Application
Name=DaVinci Resolve
Comment=DaVinci Resolve via XWayland wrapper (Intel Arc)
Exec=${WRAPPER} %U
TryExec=${WRAPPER}
Terminal=false
Icon=davinci-resolve
Categories=AudioVideo;Video;Audio;Graphics;
StartupWMClass=resolve
X-GNOME-UsesNotifications=true
EOF
update-desktop-database "${HOME}/.local/share/applications" >/dev/null 2>&1 || true
sudo gtk-update-icon-cache -f /usr/share/icons/hicolor >/dev/null 2>&1 || true
# ==================== Audio backend fix (DeckLink → ALSA) ====================
#
# Resolve ships with `Local.Audio.Type = DeckLink` as the default in its
# system-wide config template at /opt/resolve/share/default-config.dat.
# That's correct for users with a Blackmagic DeckLink capture/playback card,
# but on systems without one Resolve aborts on first launch. Patch both the
# template (so future first-launches are correct) and any existing user
# config (so the current install isn't broken).
log "Switching Resolve audio backend default from DeckLink to ALSA..."
TEMPLATE=/opt/resolve/share/default-config.dat
if [[ -f "${TEMPLATE}" ]] && grep -q '^Local\.Audio\.Type = DeckLink$' "${TEMPLATE}"; then
sudo sed -i 's|^Local\.Audio\.Type = DeckLink$|Local.Audio.Type = ALSA|' "${TEMPLATE}"
log " Patched system template ${TEMPLATE}"
fi
USER_CFG="${HOME}/.local/share/DaVinciResolve/configs/config.dat"
if [[ -f "${USER_CFG}" ]] && grep -q '^Local\.Audio\.Type = DeckLink$' "${USER_CFG}"; then
cp "${USER_CFG}" "${USER_CFG}.bak.$(date +%s)"
sed -i 's|^Local\.Audio\.Type = DeckLink$|Local.Audio.Type = ALSA|' "${USER_CFG}"
log " Patched existing user config ${USER_CFG} (backup .bak.<timestamp> created)"
fi
# ==================== snd-aloop (the actual render-blocker fix) ====================
#
# Resolve's audio engine opens raw ALSA hardware via snd_pcm_open("hw:%d", ...)
# — it never goes through ALSA's plugin layer (default/pulse/pipewire) and it
# enumerates EVERY card under /dev/snd/controlC[0-32] looking for a usable PCM.
# When every real ALSA card on the system is owned/contested by PipeWire's
# session manager, Resolve's enumeration loops forever, the render queue
# never spawns the encoder, and the user sees:
# - alsa device meters "flickering" in wireplumber (each enumeration cycle
# briefly opens controlC*; PipeWire reacts)
# - render that "won't start" with no error in ResolveDebug.txt
# Confirmed via strace: 14000+ SNDRV_CTL_IOCTL_PCM_INFO ENXIO ioctls and
# 47000+ /dev/snd/controlCN ENOENT opens during the failed render attempt,
# concentrated AFTER the user clicks Render — i.e. a tight retry loop.
#
# THE FIX: load the kernel's snd-aloop module. It exposes a virtual ALSA
# loopback card that PipeWire ignores (no ACP profile, not auto-acquired).
# Resolve's enumerator finds it, can fully own it, settles on it, and the
# render proceeds. Side effect: a "Loopback" device shows up in alsamixer
# and pavucontrol — harmless.
#
# Skipped if RESOLVE_NO_ALOOP=1 (e.g. user already has a dedicated audio
# interface that Resolve uses). Persistent across reboots via
# /etc/modules-load.d/.
if [[ "${RESOLVE_NO_ALOOP:-0}" == "1" ]]; then
log "Skipping snd-aloop setup (RESOLVE_NO_ALOOP=1)"
else
log "Setting up snd-aloop (virtual ALSA card so Resolve render can start)..."
if ! lsmod | grep -qE '^snd_aloop'; then
if sudo modprobe snd-aloop 2>/dev/null; then
log " snd-aloop loaded for the current session"
else
warn " modprobe snd-aloop failed — kernel may lack the module."
warn " On Arch this is part of linux/linux-zen/linux-lts; verify: modinfo snd-aloop"
fi
else
log " snd-aloop already loaded"
fi
ALOOP_CONF=/etc/modules-load.d/snd-aloop.conf
if [[ ! -f "${ALOOP_CONF}" ]] || ! grep -qx 'snd-aloop' "${ALOOP_CONF}" 2>/dev/null; then
echo 'snd-aloop' | sudo tee "${ALOOP_CONF}" >/dev/null
log " Wrote ${ALOOP_CONF} (autoloads at boot)"
else
log " ${ALOOP_CONF} already configured"
fi
# Bridge snd-aloop capture → default sink so monitor audio is audible.
# Without this, Resolve writes to the loopback and the audio goes nowhere
# (loopback is a black hole until something captures the other side).
# The bridge is a PipeWire loopback module loaded from user config; it
# tracks the default sink so headphone/HDMI switching keeps working.
ALOOP_BRIDGE_DIR="${HOME}/.config/pipewire/pipewire.conf.d"
ALOOP_BRIDGE_FILE="${ALOOP_BRIDGE_DIR}/50-resolve-aloop-bridge.conf"
mkdir -p "${ALOOP_BRIDGE_DIR}"
if [[ ! -f "${ALOOP_BRIDGE_FILE}" ]]; then
cat > "${ALOOP_BRIDGE_FILE}" <<'EOF'
# DaVinci Resolve aloop monitor bridge — managed by install-davinci-resolve-intel-arc.sh
# Bridges snd-aloop's capture side to the system default sink so Resolve's
# monitor audio is audible while editing. Without this, Resolve renders fine
# but you hear nothing during playback. Remove this file + restart PipeWire
# to disable.
context.modules = [
{ name = libpipewire-module-loopback
args = {
node.description = "DaVinci Resolve aloop monitor bridge"
capture.props = {
node.name = "resolve-aloop-capture"
target.object = "alsa_input.platform-snd_aloop.0.analog-stereo"
node.passive = true
}
playback.props = {
node.name = "resolve-aloop-playback"
media.class = "Stream/Output/Audio"
}
}
}
]
EOF
log " Wrote ${ALOOP_BRIDGE_FILE} (PipeWire loopback bridge)"
# Reload user PipeWire services so the new conf is picked up
if systemctl --user is-active --quiet pipewire 2>/dev/null; then
systemctl --user restart pipewire pipewire-pulse wireplumber 2>/dev/null || true
log " Reloaded user PipeWire services"
fi
else
log " ${ALOOP_BRIDGE_FILE} already in place"
fi
fi
# ==================== Post-install OpenCL sanity check ====================
echo
if command -v clinfo >/dev/null 2>&1; then
if clinfo -l 2>/dev/null | grep -qiE 'Arc|Intel.*Graphics|Battlemage|Alchemist'; then
log "OpenCL: Intel GPU is visible to clinfo — Resolve should see it for compute."
else
warn "OpenCL: no Intel GPU listed by clinfo. Resolve will likely fall back to CPU."
warn " Try: clinfo -l (and check that intel-compute-runtime is installed)"
fi
else
warn "clinfo not installed — skipped OpenCL visibility check"
fi
# ==================== Reset stale Resolve user configs ====================
#
# Resolve defaults to GPU Processing Mode = CUDA on Linux. If a previous
# launch couldn't find an OpenCL device (e.g. Intel compute runtime was
# missing), Resolve writes out a config snapshot and segfaults before the
# UI ever appears with "Unsupported GPU Processing Mode". Subsequent
# launches reuse that broken snapshot and crash the same way — even after
# the OpenCL stack is fixed. The fix is to delete configs/ and logs/ so
# Resolve goes through first-launch onboarding again with a working GPU
# stack present.
#
# We only wipe if we detect that prior-crash marker (or RESOLVE_RESET_CONFIG=1
# is set explicitly). Project databases under "Resolve Disk Database/" and
# "Resolve Project Library/" are NOT touched.
RESOLVE_USER_DIR="${HOME}/.local/share/DaVinciResolve"
PRIOR_CRASH=0
if [[ -f "${RESOLVE_USER_DIR}/logs/ResolveDebug.txt" ]] \
&& grep -q 'Unsupported GPU Processing Mode' "${RESOLVE_USER_DIR}/logs/ResolveDebug.txt" 2>/dev/null; then
PRIOR_CRASH=1
fi
if (( PRIOR_CRASH )) || [[ "${RESOLVE_RESET_CONFIG:-0}" == "1" ]]; then
if (( PRIOR_CRASH )); then
log "Detected prior crashed launch ('Unsupported GPU Processing Mode')."
else
log "RESOLVE_RESET_CONFIG=1 set — forcing config reset."
fi
log "Resetting Resolve configs and logs to first-launch state. Project databases preserved."
rm -rf "${RESOLVE_USER_DIR}/configs" "${RESOLVE_USER_DIR}/logs" 2>/dev/null || true
fi
echo
echo "✅ DaVinci Resolve installed to /opt/resolve"
echo " (vendor libc++ kept; libcrypt.so.1 ensured; Intel Arc OpenCL/VA-API enabled)"
echo " Launch from your app menu, or run: resolve-intel-arc"
echo " GPU pinning is automatic — discrete Arc preferred over iGPU."
echo " Override: RESOLVE_GPU_BDF=0000:XX:YY.Z resolve-intel-arc"
echo " Disable: RESOLVE_NO_PIN=1 resolve-intel-arc"
echo " Force config reset on next install: RESOLVE_RESET_CONFIG=1 ./install-davinci-resolve-intel-arc.sh"
echo " Skip snd-aloop module setup: RESOLVE_NO_ALOOP=1 ./install-davinci-resolve-intel-arc.sh"
echo " Logs: ~/.local/share/DaVinciResolve/logs/ResolveDebug.txt"
echo " Note: Intel Arc on Linux is unsupported by Blackmagic — expect quirks."
echo