DaVinci-Resolve-AMD-Omarchy/install-davinci-resolve.sh
28allday 05aaf04c27 v4.1 rewrite: pin ROCm 7.1.1, fix hybrid-GPU DRI_PRIME flip, glib-family swap
Total rewrite of the installer mirroring the proven NVIDIA-Open install path,
with two new root-cause fixes for AMD on hybrid systems.

Installer (v3.0 -> v4.1):
- Pin ROCm 7.1.1 from Arch Linux Archive as the OpenCL provider; current
  7.2.x is broken with DaVinci Resolve (clCreateContext fails / Color page
  hangs on every AMD card). Adds IgnorePkg to /etc/pacman.conf and aborts
  if pacman -U partial-downgrades.
- DRI_PRIME explicit PCI-tag pin in launcher (DRI_PRIME=pci-0000_BB_DD_F)
  instead of DRI_PRIME=1, which on Intel+AMD hybrids flips OpenGL to the
  iGPU and breaks CL/GL interop. switcherooctl branch removed (same bug).
- MESA_VK_DEVICE_SELECT pin added as defense-in-depth for Vulkan.
- Full glib-family symlink swap: all five of libglib-2.0, libgio-2.0,
  libgmodule-2.0, libgobject-2.0, libgthread-2.0 (was three; mismatch
  caused latent segfaults on signal emit / type registration).
- Manual install to /opt/resolve via ZIP -> .run -> appimage-extract ->
  rsync, with patchelf --set-rpath on every ELF (replaces AUR PKGBUILD
  approach).
- Auto-wipe stale ~/.local/share/DaVinciResolve/{configs,logs} on fresh
  installs and whenever known crash markers appear in ResolveDebug.txt.
- Generation-aware gfx target detection (gfx1030/1100/1101/1102/1200/1201)
  with auto HSA_OVERRIDE_GFX_VERSION for non-natively-supported cards.
- Hyprland windowrule v3 syntax; dropped stay_focused (trapped-cursor in
  modals); removed duplicate user-level .desktop entry; removed unused
  Studio USB-dongle udev rule.

Docs:
- Rewrite README to match v4.1 reality (manual /opt/resolve install,
  ROCm pinning rationale, hybrid-GPU PCI-tag note, refreshed GPU support
  tiers, corrected uninstall steps).
- Add NOTES.md with full root-cause analysis, currently-pinned package
  versions, recovery playbook, and quick sanity checks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 11:15:08 +01:00

2313 lines
89 KiB
Bash
Executable file

#!/bin/bash
# =============================================================================
# DaVinci Resolve Free - Arch Linux + AMD (RDNA2/3/4) + Hyprland Install Script
# =============================================================================
#
# Version 4.1 - Mirrors the proven NVIDIA-Open install path; only the GPU
# stack (ROCm vs CUDA) and HSA_OVERRIDE handling differ.
# 4.1: pinned ROCm 7.1.1 from Arch Linux Archive as default OpenCL provider,
# because Arch's current ROCm 7.2.x breaks DaVinci Resolve on AMD.
#
# Install method (shared with NVIDIA script):
# 1. Find the Resolve ZIP in ~/Downloads/
# 2. unzip ZIP -> .run AppImage -> --appimage-extract -> squashfs-root
# 3. Replace bundled glib/gio/gmodule with system symlinks (ABI-safe)
# KEEP vendored libc++/libc++abi (removing breaks Resolve)
# 4. rsync to /opt/resolve, patchelf --set-rpath every ELF
# 5. Install desktop entries, icons, udev rules system-wide
# 6. Apply audio fixes: DeckLink->ALSA, snd-aloop, PipeWire bridge
# 7. Apply Hyprland window rules and create XWayland launcher
#
# AMD-specific bits:
# - ROCm 7.2 (Arch extra) for OpenCL, with auto-detected HSA_OVERRIDE for
# RDNA2/3/4 cards that aren't natively in ROCm's supported list
# - Generation-aware gfx target detection (gfx1030/1100/1101/1102/1200/1201)
# - switcherooctl integration for hybrid Intel/AMD systems
# - BlackmagicRaw OpenCL decoder disable (prevents ROCm conflicts)
#
# Pacman does NOT track this install. To remove:
# sudo rm -rf /opt/resolve
# sudo rm -f /usr/share/applications/{DaVinciResolve,blackmagicraw-*}.desktop
# sudo rm -f /usr/lib/udev/rules.d/{99-BlackmagicDevices,99-ResolveKeyboardHID,99-DavinciPanel}.rules
#
# Based on techniques from:
# - The Omarchy NVIDIA-Open Resolve installer (this script is its mirror)
# - https://github.com/zelikos/davincibox
# - https://wiki.archlinux.org/title/DaVinci_Resolve
#
# Usage:
# chmod +x install-davinci-resolve.sh
# ./install-davinci-resolve.sh
#
# Env vars:
# RESOLVE_NO_ALOOP=1 Skip snd-aloop setup (you have a real audio interface)
#
# =============================================================================
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
MAGENTA='\033[0;35m'
BOLD='\033[1m'
NC='\033[0m' # No Color
# Logging functions
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
success() { echo -e "${GREEN}[OK]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
header() { echo -e "\n${CYAN}=== $1 ===${NC}\n"; }
scan_item() { echo -e " ${MAGENTA}${NC} $1: ${BOLD}$2${NC}"; }
# =============================================================================
# CONFIGURATION OPTIONS
# =============================================================================
# OpenCL Provider:
# "rocm-pinned-7.1.1" - DEFAULT. ROCm 7.1.1 stack from Arch Linux Archive,
# pinned in /etc/pacman.conf IgnorePkg. This is the
# ONLY currently-known-working configuration for
# DaVinci Resolve on AMD as of May 2026.
# Confirmed by CachyOS community + this script's
# own testing on RX 9060 XT (Navi 44, gfx1200).
# Refs:
# https://github.com/ROCm/ROCm/issues/5982
# https://discuss.cachyos.org/t/davinci-resolve-amd-rocm-fails-without-downgrade/28036
# "rocm-full" - Current Arch repos ROCm 7.2.x. KNOWN BROKEN with
# DaVinci Resolve - clCreateContext fails or hangs
# on Color page. Will work again when ROCm 7.3+ ships.
# "opencl-amd" - AUR opencl-amd 7.2.x. Same ROCm 7.2 ABI bug.
# KNOWN BROKEN with Resolve currently.
OPENCL_PROVIDER="rocm-pinned-7.1.1"
# =============================================================================
# SYSTEM SCAN VARIABLES (populated during scan)
# =============================================================================
# System info
SCAN_KERNEL_VERSION=""
SCAN_KERNEL_MAJOR=0
SCAN_KERNEL_MINOR=0
SCAN_ARCH=""
# GPU info - Single GPU (legacy/primary)
SCAN_GPU_VENDOR=""
SCAN_GPU_MODEL=""
SCAN_GPU_DRIVER=""
SCAN_GPU_RDNA_VERSION=""
SCAN_IS_RDNA4=false
# GPU info - Multi-GPU support
SCAN_GPU_COUNT=0
SCAN_GPUS=()
SCAN_GPU_VENDORS=()
SCAN_GPU_MODELS=()
SCAN_GPU_TYPES=()
SCAN_GPU_PCI_IDS=()
SCAN_GPU_RDNA_VERSIONS=()
SCAN_GPU_GFX_TARGETS=() # Per-GPU gfx target (e.g. gfx1030, gfx1102, gfx1200)
SCAN_GPU_HSA_OVERRIDES=() # Per-GPU recommended HSA_OVERRIDE_GFX_VERSION ("" if natively supported)
SCAN_GPU_DRIVERS_LOADED=()
SCAN_PRIMARY_GPU_INDEX=0
SCAN_HSA_OVERRIDE_VALUE="" # Set from primary GPU after scan
SCAN_GFX_TARGET="" # Set from primary GPU after scan
# Hybrid GPU flags
SCAN_HAS_IGPU=false
SCAN_HAS_DGPU=false
SCAN_HAS_AMD_DGPU=false
SCAN_HAS_NVIDIA_DGPU=false
SCAN_HAS_AMD_IGPU=false
SCAN_HAS_INTEL_IGPU=false
SCAN_IS_HYBRID=false
SCAN_PRIME_AVAILABLE=false
# Mesa/Graphics
SCAN_MESA_VERSION=""
SCAN_MESA_MAJOR=0
SCAN_VULKAN_AVAILABLE=false
SCAN_OPENGL_RENDERER=""
# OpenCL
SCAN_OPENCL_INSTALLED=false
SCAN_OPENCL_PROVIDER=""
SCAN_OPENCL_PLATFORMS=""
SCAN_AMD_OPENCL_WORKING=false
# Audio
SCAN_AUDIO_SERVER=""
SCAN_PIPEWIRE_INSTALLED=false
SCAN_PULSEAUDIO_INSTALLED=false
# Display
SCAN_DISPLAY_SERVER=""
SCAN_COMPOSITOR=""
SCAN_HYPRLAND_INSTALLED=false
SCAN_HYPRLAND_RUNNING=false
# DaVinci Resolve
SCAN_RESOLVE_INSTALLED=false
SCAN_RESOLVE_VERSION=""
SCAN_RESOLVE_PATH=""
# Package manager
SCAN_AUR_HELPER=""
# Disk space
SCAN_ROOT_FREE_GB=0
SCAN_HOME_FREE_GB=0
# Existing packages (to avoid reinstalling)
SCAN_INSTALLED_PACKAGES=""
# =============================================================================
# SYSTEM SCAN FUNCTIONS
# =============================================================================
scan_kernel() {
SCAN_KERNEL_VERSION=$(uname -r)
SCAN_KERNEL_MAJOR=$(echo "$SCAN_KERNEL_VERSION" | cut -d'.' -f1)
SCAN_KERNEL_MINOR=$(echo "$SCAN_KERNEL_VERSION" | cut -d'.' -f2)
SCAN_ARCH=$(uname -m)
}
scan_gpu() {
# Arrays for multi-GPU detection
SCAN_GPU_COUNT=0
SCAN_GPUS=() # Array of GPU info strings
SCAN_GPU_VENDORS=() # Array of vendors
SCAN_GPU_MODELS=() # Array of models
SCAN_GPU_TYPES=() # "integrated" or "discrete"
SCAN_GPU_PCI_IDS=() # PCI bus IDs
# Flags for GPU types present
SCAN_HAS_IGPU=false
SCAN_HAS_DGPU=false
SCAN_HAS_AMD_DGPU=false
SCAN_HAS_NVIDIA_DGPU=false
SCAN_HAS_AMD_IGPU=false
SCAN_HAS_INTEL_IGPU=false
SCAN_IS_HYBRID=false
# Primary GPU for DaVinci Resolve (discrete preferred)
SCAN_PRIMARY_GPU_INDEX=0
if ! command -v lspci &>/dev/null; then
return
fi
# Get all GPUs
while IFS= read -r gpu_line; do
[[ -z "$gpu_line" ]] && continue
local pci_id=$(echo "$gpu_line" | cut -d' ' -f1)
local gpu_info=$(echo "$gpu_line" | sed 's/^[^ ]* //' | sed 's/.*: //')
local vendor=""
local gpu_type="discrete" # Default assumption
local rdna_ver=""
# Detect vendor and type.
# Order matters: check Intel/NVIDIA first because "ATI" matches
# "Corpor[ATI]on" in Intel/NVIDIA lspci lines case-insensitively.
if echo "$gpu_line" | grep -qi "Intel"; then
vendor="Intel"
gpu_type="integrated"
SCAN_HAS_IGPU=true
SCAN_HAS_INTEL_IGPU=true
rdna_ver="N/A"
elif echo "$gpu_line" | grep -qi "NVIDIA"; then
vendor="NVIDIA"
gpu_type="discrete"
SCAN_HAS_DGPU=true
SCAN_HAS_NVIDIA_DGPU=true
rdna_ver="N/A"
elif echo "$gpu_line" | grep -qiE '\bAMD\b|\bATI\b|Radeon'; then
vendor="AMD"
# Check if integrated (APU) - common identifiers
if echo "$gpu_line" | grep -qiE "Radeon.*(Graphics|Vega|RX Vega|Renoir|Cezanne|Barcelo|Rembrandt|Phoenix|Hawk|Strix Point)|AMD.*Ryzen.*Radeon|Integrated"; then
gpu_type="integrated"
SCAN_HAS_IGPU=true
SCAN_HAS_AMD_IGPU=true
else
gpu_type="discrete"
SCAN_HAS_DGPU=true
SCAN_HAS_AMD_DGPU=true
fi
# Detect RDNA generation
if echo "$gpu_line" | grep -qiE "9070|9080|Navi 4|gfx1201"; then
rdna_ver="RDNA4"
SCAN_IS_RDNA4=true
elif echo "$gpu_line" | grep -qiE "7[0-9]{3}|Navi 3|gfx11"; then
rdna_ver="RDNA3"
elif echo "$gpu_line" | grep -qiE "6[0-9]{3}|Navi 2|gfx103"; then
rdna_ver="RDNA2"
elif echo "$gpu_line" | grep -qiE "5[0-9]{3}|Navi 1|gfx101"; then
rdna_ver="RDNA1"
elif echo "$gpu_line" | grep -qiE "Vega|gfx9"; then
rdna_ver="Vega"
elif echo "$gpu_line" | grep -qiE "Phoenix|Hawk Point|Strix"; then
rdna_ver="RDNA3 (iGPU)"
elif echo "$gpu_line" | grep -qiE "Rembrandt|Barcelo"; then
rdna_ver="RDNA2 (iGPU)"
else
rdna_ver="Pre-RDNA/GCN"
fi
# Detect gfx target and required HSA_OVERRIDE_GFX_VERSION.
# ROCm 7.2 officially supports: gfx1030, gfx1100, gfx1101, gfx1200, gfx1201.
# Other RDNA2/3 silicon needs HSA_OVERRIDE to spoof as the nearest supported target.
local gfx_target=""
local hsa_override=""
# RDNA4 - all natively supported
if echo "$gpu_line" | grep -qiE "Navi 48|9070|9080|gfx1201"; then
gfx_target="gfx1201"
elif echo "$gpu_line" | grep -qiE "Navi 44|RX 90[6-9][0-9]|gfx1200"; then
gfx_target="gfx1200"
# RDNA3
elif echo "$gpu_line" | grep -qiE "Navi 31|RX 79[0-9]{2}|gfx1100"; then
gfx_target="gfx1100"
elif echo "$gpu_line" | grep -qiE "Navi 32|RX 7[78][0-9]{2}|gfx1101"; then
gfx_target="gfx1101"
elif echo "$gpu_line" | grep -qiE "Navi 33|RX 76[0-9]{2}|gfx1102"; then
gfx_target="gfx1102"
hsa_override="11.0.0" # gfx1102 not natively supported, spoof as gfx1100
# RDNA2
elif echo "$gpu_line" | grep -qiE "Navi 21|RX 6[89][0-9]{2}|gfx1030"; then
gfx_target="gfx1030"
elif echo "$gpu_line" | grep -qiE "Navi 22|RX 67[0-9]{2}|gfx1031"; then
gfx_target="gfx1031"
hsa_override="10.3.0" # spoof as gfx1030
elif echo "$gpu_line" | grep -qiE "Navi 23|RX 66[0-9]{2}|gfx1032"; then
gfx_target="gfx1032"
hsa_override="10.3.0"
elif echo "$gpu_line" | grep -qiE "Navi 24|RX 6[45][0-9]{2}|gfx1034"; then
gfx_target="gfx1034"
hsa_override="10.3.0"
fi
fi
# Store GPU info
SCAN_GPUS+=("$gpu_info")
SCAN_GPU_VENDORS+=("$vendor")
SCAN_GPU_MODELS+=("$gpu_info")
SCAN_GPU_TYPES+=("$gpu_type")
SCAN_GPU_PCI_IDS+=("$pci_id")
# Store RDNA version, gfx target, and HSA override for AMD GPUs
if [[ "$vendor" == "AMD" ]]; then
SCAN_GPU_RDNA_VERSIONS+=("$rdna_ver")
SCAN_GPU_GFX_TARGETS+=("$gfx_target")
SCAN_GPU_HSA_OVERRIDES+=("$hsa_override")
else
SCAN_GPU_RDNA_VERSIONS+=("")
SCAN_GPU_GFX_TARGETS+=("")
SCAN_GPU_HSA_OVERRIDES+=("")
fi
SCAN_GPU_COUNT=$((SCAN_GPU_COUNT + 1))
done < <(lspci 2>/dev/null | grep -iE "VGA|3D|Display")
# Detect hybrid graphics
if [[ "$SCAN_HAS_IGPU" == true && "$SCAN_HAS_DGPU" == true ]]; then
SCAN_IS_HYBRID=true
fi
# Select primary GPU for DaVinci Resolve (prefer discrete AMD, then discrete NVIDIA, then any AMD)
for i in "${!SCAN_GPU_VENDORS[@]}"; do
if [[ "${SCAN_GPU_VENDORS[$i]}" == "AMD" && "${SCAN_GPU_TYPES[$i]}" == "discrete" ]]; then
SCAN_PRIMARY_GPU_INDEX=$i
break
fi
done
# If no discrete AMD, check for discrete NVIDIA
if [[ ${#SCAN_GPU_TYPES[@]} -gt 0 && "${SCAN_GPU_TYPES[$SCAN_PRIMARY_GPU_INDEX]}" != "discrete" ]]; then
for i in "${!SCAN_GPU_VENDORS[@]}"; do
if [[ "${SCAN_GPU_VENDORS[$i]}" == "NVIDIA" && "${SCAN_GPU_TYPES[$i]}" == "discrete" ]]; then
SCAN_PRIMARY_GPU_INDEX=$i
break
fi
done
fi
# Set legacy variables for compatibility (using primary GPU)
if [[ $SCAN_GPU_COUNT -gt 0 ]]; then
SCAN_GPU_VENDOR="${SCAN_GPU_VENDORS[$SCAN_PRIMARY_GPU_INDEX]}"
SCAN_GPU_MODEL="${SCAN_GPU_MODELS[$SCAN_PRIMARY_GPU_INDEX]}"
SCAN_GPU_RDNA_VERSION="${SCAN_GPU_RDNA_VERSIONS[$SCAN_PRIMARY_GPU_INDEX]}"
SCAN_GFX_TARGET="${SCAN_GPU_GFX_TARGETS[$SCAN_PRIMARY_GPU_INDEX]}"
SCAN_HSA_OVERRIDE_VALUE="${SCAN_GPU_HSA_OVERRIDES[$SCAN_PRIMARY_GPU_INDEX]}"
fi
# Check loaded kernel drivers
SCAN_GPU_DRIVERS_LOADED=()
if lsmod 2>/dev/null | grep -q "^amdgpu"; then
SCAN_GPU_DRIVERS_LOADED+=("amdgpu")
SCAN_GPU_DRIVER="amdgpu"
fi
if lsmod 2>/dev/null | grep -q "^nvidia"; then
SCAN_GPU_DRIVERS_LOADED+=("nvidia")
[[ -z "$SCAN_GPU_DRIVER" ]] && SCAN_GPU_DRIVER="nvidia"
fi
if lsmod 2>/dev/null | grep -q "^i915"; then
SCAN_GPU_DRIVERS_LOADED+=("i915")
[[ -z "$SCAN_GPU_DRIVER" ]] && SCAN_GPU_DRIVER="i915"
fi
if lsmod 2>/dev/null | grep -q "^radeon"; then
SCAN_GPU_DRIVERS_LOADED+=("radeon")
fi
# Detect DRI_PRIME / PRIME render offload status
SCAN_PRIME_AVAILABLE=false
if [[ "$SCAN_IS_HYBRID" == true ]]; then
# Check if DRI_PRIME would work
if [[ -d "/sys/class/drm" ]]; then
local render_nodes=$(ls /dev/dri/renderD* 2>/dev/null | wc -l)
if [[ $render_nodes -gt 1 ]]; then
SCAN_PRIME_AVAILABLE=true
fi
fi
fi
}
scan_mesa() {
if command -v glxinfo &>/dev/null; then
SCAN_MESA_VERSION=$(glxinfo 2>/dev/null | grep "OpenGL version" | grep -oP "Mesa \K[0-9]+\.[0-9]+(\.[0-9]+)?" || echo "")
SCAN_OPENGL_RENDERER=$(glxinfo 2>/dev/null | grep "OpenGL renderer" | sed 's/OpenGL renderer string: //' || echo "")
if [[ -n "$SCAN_MESA_VERSION" ]]; then
SCAN_MESA_MAJOR=$(echo "$SCAN_MESA_VERSION" | cut -d'.' -f1)
fi
fi
# Check Vulkan
if command -v vulkaninfo &>/dev/null && vulkaninfo --summary &>/dev/null; then
SCAN_VULKAN_AVAILABLE=true
fi
}
scan_opencl() {
# Check what OpenCL packages are installed
# Check for full ROCm stack first (preferred for RDNA4)
if pacman -Qq rocm-opencl-runtime &>/dev/null; then
SCAN_OPENCL_INSTALLED=true
# Check if full ROCm stack is installed
if pacman -Qq rocm-hip-runtime rocm-core &>/dev/null; then
SCAN_OPENCL_PROVIDER="rocm-full"
# Get ROCm version
local rocm_ver=$(pacman -Q rocm-core 2>/dev/null | awk '{print $2}' | cut -d'-' -f1)
[[ -n "$rocm_ver" ]] && SCAN_OPENCL_PROVIDER="rocm-full ($rocm_ver)"
else
SCAN_OPENCL_PROVIDER="rocm-opencl-runtime"
fi
elif pacman -Qq opencl-amd &>/dev/null; then
SCAN_OPENCL_INSTALLED=true
SCAN_OPENCL_PROVIDER="opencl-amd"
elif pacman -Qq mesa-opencl &>/dev/null || pacman -Qq opencl-mesa &>/dev/null; then
SCAN_OPENCL_INSTALLED=true
SCAN_OPENCL_PROVIDER="mesa (rusticl)"
elif pacman -Qq intel-compute-runtime &>/dev/null; then
SCAN_OPENCL_INSTALLED=true
SCAN_OPENCL_PROVIDER="intel-compute-runtime"
fi
# Check if OpenCL actually works with AMD
if command -v clinfo &>/dev/null; then
SCAN_OPENCL_PLATFORMS=$(clinfo --list 2>/dev/null | head -5 || echo "")
if clinfo --list 2>/dev/null | grep -qi "AMD\|gfx\|Radeon"; then
SCAN_AMD_OPENCL_WORKING=true
fi
fi
}
scan_audio() {
# Detect audio server
if command -v pactl &>/dev/null; then
local server_name=$(pactl info 2>/dev/null | grep "Server Name" || echo "")
if echo "$server_name" | grep -qi "PipeWire"; then
SCAN_AUDIO_SERVER="PipeWire"
SCAN_PIPEWIRE_INSTALLED=true
elif echo "$server_name" | grep -qi "PulseAudio"; then
SCAN_AUDIO_SERVER="PulseAudio"
SCAN_PULSEAUDIO_INSTALLED=true
fi
fi
# Double-check with package detection
if pacman -Qq pipewire &>/dev/null; then
SCAN_PIPEWIRE_INSTALLED=true
fi
if pacman -Qq pulseaudio &>/dev/null; then
SCAN_PULSEAUDIO_INSTALLED=true
fi
# If we couldn't detect from pactl, use package info
if [[ -z "$SCAN_AUDIO_SERVER" ]]; then
if [[ "$SCAN_PIPEWIRE_INSTALLED" == true ]]; then
SCAN_AUDIO_SERVER="PipeWire (detected from packages)"
elif [[ "$SCAN_PULSEAUDIO_INSTALLED" == true ]]; then
SCAN_AUDIO_SERVER="PulseAudio (detected from packages)"
else
SCAN_AUDIO_SERVER="Unknown/None"
fi
fi
}
scan_display() {
# Detect display server
if [[ -n "$WAYLAND_DISPLAY" ]]; then
SCAN_DISPLAY_SERVER="Wayland"
elif [[ "$XDG_SESSION_TYPE" == "wayland" ]]; then
# Wayland session but WAYLAND_DISPLAY not set (XWayland context)
SCAN_DISPLAY_SERVER="Wayland (XWayland available)"
elif [[ -n "$DISPLAY" ]]; then
SCAN_DISPLAY_SERVER="X11"
else
SCAN_DISPLAY_SERVER="None/TTY"
fi
# Detect compositor
if [[ -n "$HYPRLAND_INSTANCE_SIGNATURE" ]]; then
SCAN_COMPOSITOR="Hyprland"
SCAN_HYPRLAND_RUNNING=true
elif [[ -n "$SWAYSOCK" ]]; then
SCAN_COMPOSITOR="Sway"
elif [[ -n "$GNOME_DESKTOP_SESSION_ID" ]]; then
SCAN_COMPOSITOR="GNOME"
elif [[ -n "$KDE_FULL_SESSION" ]]; then
SCAN_COMPOSITOR="KDE Plasma"
elif pgrep -x "hyprland" &>/dev/null; then
SCAN_COMPOSITOR="Hyprland"
SCAN_HYPRLAND_RUNNING=true
else
SCAN_COMPOSITOR="Unknown"
fi
# Check if Hyprland is installed
if pacman -Qq hyprland &>/dev/null || command -v Hyprland &>/dev/null; then
SCAN_HYPRLAND_INSTALLED=true
fi
}
scan_resolve() {
# Check if DaVinci Resolve is installed
if [[ -d "/opt/resolve" ]] || [[ -f "/opt/resolve/bin/resolve" ]]; then
SCAN_RESOLVE_INSTALLED=true
SCAN_RESOLVE_PATH="/opt/resolve"
# Try to get version
if [[ -f "/opt/resolve/docs/License.txt" ]]; then
SCAN_RESOLVE_VERSION=$(grep -oP "DaVinci Resolve \K[0-9]+\.[0-9]+(\.[0-9]+)?" /opt/resolve/docs/License.txt 2>/dev/null | head -1 || echo "Unknown")
fi
fi
# Also check via pacman
if pacman -Qq davinci-resolve &>/dev/null || pacman -Qq davinci-resolve-studio &>/dev/null; then
SCAN_RESOLVE_INSTALLED=true
fi
}
scan_aur_helper() {
if command -v paru &>/dev/null; then
SCAN_AUR_HELPER="paru"
elif command -v yay &>/dev/null; then
SCAN_AUR_HELPER="yay"
elif command -v trizen &>/dev/null; then
SCAN_AUR_HELPER="trizen"
elif command -v pikaur &>/dev/null; then
SCAN_AUR_HELPER="pikaur"
else
SCAN_AUR_HELPER=""
fi
}
scan_disk_space() {
# Get free space in GB
SCAN_ROOT_FREE_GB=$(df -BG / 2>/dev/null | awk 'NR==2 {print $4}' | tr -d 'G' || echo "0")
SCAN_HOME_FREE_GB=$(df -BG "$HOME" 2>/dev/null | awk 'NR==2 {print $4}' | tr -d 'G' || echo "0")
}
scan_installed_packages() {
# Get list of installed packages for comparison
SCAN_INSTALLED_PACKAGES=$(pacman -Qq 2>/dev/null || echo "")
}
is_pkg_installed() {
echo "$SCAN_INSTALLED_PACKAGES" | grep -qx "$1"
}
# =============================================================================
# MAIN SYSTEM SCAN
# =============================================================================
run_system_scan() {
header "System Scan"
echo -e "${BOLD}Scanning your system to optimize installation...${NC}\n"
# Run all scans
echo -n " Scanning kernel... "
scan_kernel
echo -e "${GREEN}done${NC}"
echo -n " Scanning GPU... "
scan_gpu
echo -e "${GREEN}done${NC}"
echo -n " Scanning graphics stack... "
scan_mesa
echo -e "${GREEN}done${NC}"
echo -n " Scanning OpenCL... "
scan_opencl
echo -e "${GREEN}done${NC}"
echo -n " Scanning audio... "
scan_audio
echo -e "${GREEN}done${NC}"
echo -n " Scanning display... "
scan_display
echo -e "${GREEN}done${NC}"
echo -n " Scanning for DaVinci Resolve... "
scan_resolve
echo -e "${GREEN}done${NC}"
echo -n " Scanning AUR helpers... "
scan_aur_helper
echo -e "${GREEN}done${NC}"
echo -n " Scanning disk space... "
scan_disk_space
echo -e "${GREEN}done${NC}"
echo -n " Scanning installed packages... "
scan_installed_packages
echo -e "${GREEN}done${NC}"
echo ""
}
# =============================================================================
# DISPLAY SCAN RESULTS
# =============================================================================
display_scan_results() {
header "System Configuration Detected"
echo -e "${BOLD}System:${NC}"
scan_item "Kernel" "$SCAN_KERNEL_VERSION"
scan_item "Architecture" "$SCAN_ARCH"
echo ""
echo -e "${BOLD}GPU(s) Detected: ${SCAN_GPU_COUNT}${NC}"
if [[ $SCAN_GPU_COUNT -eq 0 ]]; then
scan_item "GPU" "None detected"
else
for i in "${!SCAN_GPUS[@]}"; do
local gpu_label="GPU $((i+1))"
local gpu_type_label="${SCAN_GPU_TYPES[$i]}"
local vendor="${SCAN_GPU_VENDORS[$i]}"
# Mark primary GPU
local primary_marker=""
if [[ $i -eq $SCAN_PRIMARY_GPU_INDEX ]]; then
primary_marker=" ${GREEN}← PRIMARY${NC}"
fi
echo ""
echo -e " ${BOLD}$gpu_label ($gpu_type_label):${NC}$primary_marker"
echo -e " Vendor: ${SCAN_GPU_VENDORS[$i]}"
echo -e " Model: ${SCAN_GPU_MODELS[$i]}"
if [[ "$vendor" == "AMD" && -n "${SCAN_GPU_RDNA_VERSIONS[$i]}" ]]; then
echo -e " Gen: ${SCAN_GPU_RDNA_VERSIONS[$i]}"
fi
if [[ "$vendor" == "AMD" && -n "${SCAN_GPU_GFX_TARGETS[$i]}" ]]; then
local override_note=""
if [[ -n "${SCAN_GPU_HSA_OVERRIDES[$i]}" ]]; then
override_note=" ${YELLOW}(needs HSA_OVERRIDE_GFX_VERSION=${SCAN_GPU_HSA_OVERRIDES[$i]})${NC}"
else
override_note=" ${GREEN}(natively supported)${NC}"
fi
echo -e " GFX: ${SCAN_GPU_GFX_TARGETS[$i]}$override_note"
fi
echo -e " PCI: ${SCAN_GPU_PCI_IDS[$i]}"
done
echo ""
if [[ "$SCAN_IS_HYBRID" == true ]]; then
echo -e " ${YELLOW}⚡ Hybrid Graphics Detected${NC}"
if [[ "$SCAN_PRIME_AVAILABLE" == true ]]; then
echo -e " DRI_PRIME offloading available"
fi
fi
if [[ ${#SCAN_GPU_DRIVERS_LOADED[@]} -gt 0 ]]; then
echo -e " Loaded drivers: ${SCAN_GPU_DRIVERS_LOADED[*]}"
fi
fi
echo ""
echo -e "${BOLD}Graphics Stack:${NC}"
scan_item "Mesa" "${SCAN_MESA_VERSION:-Not detected}"
scan_item "OpenGL Renderer" "${SCAN_OPENGL_RENDERER:-Not detected}"
scan_item "Vulkan" "$( [[ "$SCAN_VULKAN_AVAILABLE" == true ]] && echo "Available" || echo "Not available" )"
echo ""
echo -e "${BOLD}OpenCL:${NC}"
if [[ "$SCAN_OPENCL_INSTALLED" == true ]]; then
scan_item "Provider" "$SCAN_OPENCL_PROVIDER"
scan_item "AMD Working" "$( [[ "$SCAN_AMD_OPENCL_WORKING" == true ]] && echo "Yes ✓" || echo "No ✗" )"
else
scan_item "Status" "Not installed"
fi
echo ""
echo -e "${BOLD}Audio:${NC}"
scan_item "Server" "$SCAN_AUDIO_SERVER"
echo ""
echo -e "${BOLD}Display:${NC}"
scan_item "Server" "$SCAN_DISPLAY_SERVER"
scan_item "Compositor" "$SCAN_COMPOSITOR"
scan_item "Hyprland" "$( [[ "$SCAN_HYPRLAND_INSTALLED" == true ]] && echo "Installed" || echo "Not installed" )"
echo ""
echo -e "${BOLD}DaVinci Resolve:${NC}"
if [[ "$SCAN_RESOLVE_INSTALLED" == true ]]; then
scan_item "Status" "Installed"
scan_item "Version" "${SCAN_RESOLVE_VERSION:-Unknown}"
scan_item "Path" "$SCAN_RESOLVE_PATH"
else
scan_item "Status" "Not installed"
fi
echo ""
echo -e "${BOLD}Package Management:${NC}"
scan_item "AUR Helper" "${SCAN_AUR_HELPER:-None detected}"
echo ""
echo -e "${BOLD}Disk Space:${NC}"
scan_item "Root partition" "${SCAN_ROOT_FREE_GB}GB free"
scan_item "Home partition" "${SCAN_HOME_FREE_GB}GB free"
echo ""
}
# =============================================================================
# ANALYZE SCAN AND PROVIDE RECOMMENDATIONS
# =============================================================================
analyze_scan_results() {
header "Analysis & Recommendations"
local issues=0
local warnings=0
# Check kernel compatibility
if [[ $SCAN_KERNEL_MAJOR -lt 6 ]] || [[ $SCAN_KERNEL_MAJOR -eq 6 && $SCAN_KERNEL_MINOR -lt 12 ]]; then
if [[ "$SCAN_IS_RDNA4" == true ]]; then
echo -e "${RED}[ISSUE]${NC} Kernel $SCAN_KERNEL_VERSION is too old for RDNA 4. Need 6.12+."
issues=$((issues+1))
fi
fi
if [[ $SCAN_KERNEL_MAJOR -eq 6 && ($SCAN_KERNEL_MINOR -eq 14 || $SCAN_KERNEL_MINOR -eq 15) ]]; then
echo -e "${YELLOW}[WARNING]${NC} Kernel 6.14/6.15 has ROCm DKMS issues. Consider 6.12 or 6.13."
warnings=$((warnings+1))
fi
# Kernel 7.x is bleeding edge - ROCm DKMS may not yet support it
if [[ $SCAN_KERNEL_MAJOR -ge 7 ]]; then
echo -e "${YELLOW}[WARNING]${NC} Kernel $SCAN_KERNEL_VERSION is very new. ROCm DKMS may not support it yet."
warnings=$((warnings+1))
fi
# =========================================================================
# HYBRID GPU HANDLING
# =========================================================================
if [[ "$SCAN_IS_HYBRID" == true ]]; then
echo -e "${CYAN}[HYBRID GPU]${NC} Multiple GPUs detected - special configuration needed."
# AMD iGPU + AMD dGPU
if [[ "$SCAN_HAS_AMD_IGPU" == true && "$SCAN_HAS_AMD_DGPU" == true ]]; then
echo -e "${YELLOW}[NOTE]${NC} AMD iGPU + AMD dGPU detected."
echo " DaVinci Resolve should use the discrete GPU automatically."
echo " If it uses the wrong GPU, the launcher pins via PCI tag (DRI_PRIME=pci-DDDD_BB_DD_F)"
warnings=$((warnings+1))
fi
# Intel iGPU + AMD dGPU
if [[ "$SCAN_HAS_INTEL_IGPU" == true && "$SCAN_HAS_AMD_DGPU" == true ]]; then
echo -e "${YELLOW}[NOTE]${NC} Intel iGPU + AMD dGPU detected."
echo " You may need to launch with: DRI_PRIME=pci-DDDD_BB_DD_F davinci-resolve (use lspci to find the AMD bus)"
echo " The launcher script will be configured for this."
warnings=$((warnings+1))
fi
# Intel iGPU + NVIDIA dGPU
if [[ "$SCAN_HAS_INTEL_IGPU" == true && "$SCAN_HAS_NVIDIA_DGPU" == true ]]; then
echo -e "${YELLOW}[NOTE]${NC} Intel iGPU + NVIDIA dGPU (Optimus) detected."
echo " For NVIDIA GPU: use prime-run or __NV_PRIME_RENDER_OFFLOAD=1"
echo " Consider using NVIDIA GPU for better Resolve performance."
warnings=$((warnings+1))
fi
# AMD iGPU + NVIDIA dGPU
if [[ "$SCAN_HAS_AMD_IGPU" == true && "$SCAN_HAS_NVIDIA_DGPU" == true ]]; then
echo -e "${YELLOW}[NOTE]${NC} AMD iGPU + NVIDIA dGPU detected."
echo " For NVIDIA GPU: use prime-run or __NV_PRIME_RENDER_OFFLOAD=1"
warnings=$((warnings+1))
fi
# Check if PRIME is available
if [[ "$SCAN_PRIME_AVAILABLE" == true ]]; then
echo -e "${GREEN}[OK]${NC} PRIME GPU switching is available (multiple render nodes detected)."
else
echo -e "${YELLOW}[WARNING]${NC} PRIME switching may not be configured correctly."
warnings=$((warnings+1))
fi
echo ""
fi
# Check GPU vendor
if [[ "$SCAN_GPU_VENDOR" != "AMD" && "$SCAN_HAS_AMD_DGPU" != true ]]; then
if [[ "$SCAN_GPU_VENDOR" == "NVIDIA" ]]; then
echo -e "${YELLOW}[NOTE]${NC} NVIDIA GPU detected. This script is optimized for AMD."
echo " NVIDIA GPUs work well with Resolve but use different drivers (nvidia + CUDA)."
warnings=$((warnings+1))
elif [[ "$SCAN_GPU_VENDOR" == "Intel" ]]; then
echo -e "${YELLOW}[WARNING]${NC} Only Intel GPU detected. DaVinci Resolve has limited Intel support."
warnings=$((warnings+1))
else
echo -e "${YELLOW}[WARNING]${NC} GPU vendor not recognized. This script is optimized for AMD GPUs."
warnings=$((warnings+1))
fi
fi
# Check Mesa for RDNA 4
if [[ "$SCAN_IS_RDNA4" == true && $SCAN_MESA_MAJOR -lt 25 ]]; then
echo -e "${YELLOW}[WARNING]${NC} Mesa $SCAN_MESA_VERSION may not fully support RDNA 4. Consider updating."
warnings=$((warnings+1))
fi
# Check OpenCL
if [[ "$SCAN_OPENCL_INSTALLED" == true && "$SCAN_AMD_OPENCL_WORKING" == false ]]; then
echo -e "${YELLOW}[WARNING]${NC} OpenCL installed but AMD platform not detected. May need reconfiguration."
warnings=$((warnings+1))
fi
# RDNA4 info
if [[ "$SCAN_IS_RDNA4" == true ]]; then
echo -e "${CYAN}[RDNA4]${NC} Detected RDNA4 GPU (gfx1201)"
echo " Note: RDNA4 support is new - compatibility may vary"
echo " If Resolve fails, try switching OPENCL_PROVIDER in script"
fi
# Show selected OpenCL provider with explanation
echo -e "${CYAN}[CONFIG]${NC} OpenCL provider: $OPENCL_PROVIDER"
if [[ "$OPENCL_PROVIDER" == "opencl-amd" ]]; then
echo " Using AUR package (recommended by Arch Wiki for Resolve)"
else
echo " Using official Arch ROCm packages"
fi
# Check disk space (DaVinci Resolve needs ~3GB, opencl-amd ~500MB)
if [[ $SCAN_ROOT_FREE_GB -lt 5 ]]; then
echo -e "${RED}[ISSUE]${NC} Low disk space on root (${SCAN_ROOT_FREE_GB}GB). Need at least 5GB free."
issues=$((issues+1))
fi
# Check AUR helper
if [[ -z "$SCAN_AUR_HELPER" ]]; then
echo -e "${RED}[ISSUE]${NC} No AUR helper found. Install yay or paru first."
issues=$((issues+1))
fi
# Check display server
if [[ "$SCAN_DISPLAY_SERVER" == "None/TTY" ]]; then
echo -e "${YELLOW}[WARNING]${NC} No display server detected. Run this script from a graphical session."
warnings=$((warnings+1))
fi
# Check existing Resolve installation
if [[ "$SCAN_RESOLVE_INSTALLED" == true ]]; then
echo -e "${YELLOW}[NOTE]${NC} DaVinci Resolve already installed (v${SCAN_RESOLVE_VERSION}). Will update/reconfigure."
warnings=$((warnings+1))
fi
# Summary
echo ""
if [[ $issues -eq 0 && $warnings -eq 0 ]]; then
echo -e "${GREEN}✓ No issues detected. System looks ready for installation.${NC}"
elif [[ $issues -eq 0 ]]; then
echo -e "${YELLOW}$warnings warning(s) detected, but installation can proceed.${NC}"
else
echo -e "${RED}$issues issue(s) and $warnings warning(s) detected.${NC}"
echo -e "${RED} Please resolve issues before continuing.${NC}"
fi
echo ""
return $issues
}
# =============================================================================
# SMART PACKAGE SELECTION
# =============================================================================
build_package_list() {
header "Building Package List"
# Arrays for packages to install
PACMAN_PACKAGES=()
AUR_PACKAGES=()
SKIP_PACKAGES=()
# ---- Build/extraction tools (one-time, used by install step) ----
# Resolve ships as a ZIP containing a .run AppImage; we extract, patchelf,
# rsync to /opt/resolve, and update desktop/icon caches afterward.
local tools=(
unzip patchelf libarchive xdg-user-dirs desktop-file-utils
file gtk-update-icon-cache rsync
)
# ---- Resolve runtime deps NOT bundled in the AppImage ----
# Resolve bundles its own copies of Qt5, libpng12, libxcb-*, etc. and we
# patch RPATH so they're found in /opt/resolve/libs. The few libs below
# genuinely have to come from the host system.
local runtime=(
libxcrypt-compat # provides libcrypt.so.1 (Arch dropped it)
ffmpeg4.4 # Resolve links against this older FFmpeg ABI
glu # OpenGL Utility (3D rendering)
fuse2 # AppImage runtime compat
mesa # graphics stack (usually already present)
)
# ---- Display/Wayland compat for Hyprland ----
local display=(
xorg-xwayland # Resolve has no native Wayland support
xdg-desktop-portal
xdg-desktop-portal-gtk
)
if [[ "$SCAN_IS_HYBRID" == true ]]; then
display+=(switcheroo-control)
fi
# ---- AMD-specific OpenCL provider ----
# rocm-pinned-7.1.1 is the default and only currently-working path with
# DaVinci Resolve. The actual install of the 7.1.1 packages happens in
# install_rocm_pinned() (downloads from Arch Linux Archive).
local amd_opencl=()
case "$OPENCL_PROVIDER" in
rocm-pinned-7.1.1)
info "Using ROCm 7.1.1 pinned (working stack for DaVinci Resolve on AMD)"
# ocl-icd provides libOpenCL.so.1 loader. rocminfo for diagnostics.
# rocm-smi-lib for power/clock monitoring. rocm-* 7.1.1 are
# installed by install_rocm_pinned() below.
amd_opencl=(ocl-icd rocminfo rocm-smi-lib)
;;
rocm-full)
warn "rocm-full = ROCm 7.2.x - KNOWN BROKEN with DaVinci Resolve as of May 2026"
warn " See https://github.com/ROCm/ROCm/issues/5982"
warn " Switch OPENCL_PROVIDER to 'rocm-pinned-7.1.1' for the working stack."
amd_opencl=(
rocm-core rocm-opencl-runtime rocm-hip-runtime
hsa-rocr rocminfo rocm-smi-lib
)
;;
opencl-amd)
warn "opencl-amd = AUR ROCm 7.2.x - KNOWN BROKEN with DaVinci Resolve as of May 2026"
warn " Same ABI bug as rocm-full. Switch to 'rocm-pinned-7.1.1' for working stack."
amd_opencl=(ocl-icd)
AUR_PACKAGES+=(opencl-amd)
;;
*)
error "Unknown OPENCL_PROVIDER: '$OPENCL_PROVIDER'"
;;
esac
# ---- Audio (smart-pick based on detected server) ----
local audio=()
if [[ "$SCAN_AUDIO_SERVER" == *"PipeWire"* ]]; then
info "PipeWire detected - installing pipewire-pulse + pipewire-alsa"
audio=(pipewire-pulse pipewire-alsa)
else
info "PulseAudio detected - installing pulseaudio-alsa"
audio=(pulseaudio-alsa)
fi
# ---- Diagnostics ----
local diag=(clinfo mesa-utils expac python-distro)
# Combine and split into "need install" vs "skip"
for pkg in "${tools[@]}" "${runtime[@]}" "${display[@]}" \
"${amd_opencl[@]}" "${audio[@]}" "${diag[@]}"; do
if is_pkg_installed "$pkg"; then
SKIP_PACKAGES+=("$pkg")
else
PACMAN_PACKAGES+=("$pkg")
fi
done
# Display summary
echo -e "${BOLD}Packages to install from official repos:${NC}"
if [[ ${#PACMAN_PACKAGES[@]} -gt 0 ]]; then
echo " ${PACMAN_PACKAGES[*]}"
else
echo " (none needed)"
fi
echo ""
echo -e "${BOLD}Packages to install from AUR:${NC}"
if [[ ${#AUR_PACKAGES[@]} -gt 0 ]]; then
echo " ${AUR_PACKAGES[*]}"
else
echo " (none needed)"
fi
echo ""
echo -e "${BOLD}Packages already installed (skipping):${NC}"
if [[ ${#SKIP_PACKAGES[@]} -gt 0 ]]; then
echo " ${SKIP_PACKAGES[*]}"
else
echo " (none)"
fi
echo ""
}
# =============================================================================
# INSTALLATION FUNCTIONS
# =============================================================================
install_pacman_packages() {
if [[ ${#PACMAN_PACKAGES[@]} -eq 0 ]]; then
info "No official repo packages to install."
return 0
fi
header "Installing Official Repo Packages"
# Handle opencl-amd removal when using full ROCm stack
if [[ "$OPENCL_PROVIDER" == "rocm-full" ]] && is_pkg_installed "opencl-amd"; then
warn "Removing opencl-amd (AUR) to install official ROCm stack..."
sudo pacman -Rns opencl-amd --noconfirm 2>/dev/null || true
fi
# Handle OpenCL conflicts - rusticl/mesa-opencl breaks ROCm (confirmed by davincibox)
# Reference: https://github.com/zelikos/davincibox - "mesa-libOpenCL breaks rocm-opencl"
local -a opencl_conflicts=()
for pkg in opencl-rusticl-mesa opencl-clover-mesa mesa-opencl pocl; do
if is_pkg_installed "$pkg"; then
opencl_conflicts+=("$pkg")
fi
done
if [[ ${#opencl_conflicts[@]} -gt 0 ]]; then
warn "Removing conflicting OpenCL packages (breaks ROCm): ${opencl_conflicts[*]}"
sudo pacman -Rns "${opencl_conflicts[@]}" --noconfirm 2>/dev/null || true
fi
info "Installing ${#PACMAN_PACKAGES[@]} packages..."
sudo pacman -S --needed --noconfirm "${PACMAN_PACKAGES[@]}"
success "Official packages installed."
}
install_aur_packages() {
if [[ ${#AUR_PACKAGES[@]} -eq 0 ]]; then
info "No AUR packages to install."
return 0
fi
header "Installing AUR Packages"
# Handle OpenCL conflicts
if [[ " ${AUR_PACKAGES[*]} " =~ " opencl-amd " ]]; then
# Check for conflicting packages
local -a conflicts=()
for pkg in mesa-opencl pocl intel-compute-runtime rocm-opencl-runtime; do
if is_pkg_installed "$pkg"; then
conflicts+=("$pkg")
fi
done
if [[ ${#conflicts[@]} -gt 0 ]]; then
warn "Found conflicting OpenCL packages: ${conflicts[*]}"
read -p "Remove them to install opencl-amd? [Y/n] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
sudo pacman -Rns "${conflicts[@]}" --noconfirm 2>/dev/null || true
fi
fi
fi
info "Installing AUR packages..."
$SCAN_AUR_HELPER -S --needed --noconfirm "${AUR_PACKAGES[@]}"
success "AUR packages installed."
}
install_rocm_pinned() {
# Install ROCm 7.1.1 from the Arch Linux Archive (ALA) and pin it in
# /etc/pacman.conf IgnorePkg.
#
# Why: ROCm 7.2.x (current Arch extra) breaks DaVinci Resolve - the
# OpenCL clCreateContext call fails on AMD GPUs after the 7.2 update.
# See ROCm/ROCm#5982 (filed 2026-02-19, still open).
# Confirmed broken on 760M iGPU, 7800 XT, RX 9060 XT (this user).
# 7.1.1 is the last working release.
#
# Why not opencl-amd: it bundles its own 7.2.x ROCm internally and
# has the same ABI bug. It also conflicts with rocm-opencl-runtime
# so you can only have one or the other.
#
# When ROCm 7.3+ ships with the fix, lift the pin:
# sudo sed -i '/^IgnorePkg.*rocm-/d' /etc/pacman.conf
# sudo pacman -Syu
[[ "$OPENCL_PROVIDER" != "rocm-pinned-7.1.1" ]] && return 0
header "Installing ROCm 7.1.1 (pinned)"
# If the working stack is already present at the right version, skip.
local need_install=0
for pkg in rocm-core rocm-device-libs rocm-llvm rocm-opencl-runtime comgr; do
local ver
ver=$(pacman -Q "$pkg" 2>/dev/null | awk '{print $2}')
if [[ -z "$ver" || ! "$ver" =~ 7\.1\.1 ]]; then
need_install=1
break
fi
done
if [[ "$need_install" == 0 ]] && pacman -Q spirv-llvm-translator 2>/dev/null | grep -q "21.1.3"; then
info "ROCm 7.1.1 stack already present at correct versions - skipping ALA download."
else
# Remove conflicting package: opencl-amd bundles ROCm 7.2.1 and
# conflicts with rocm-opencl-runtime
if pacman -Q opencl-amd &>/dev/null; then
warn "Removing opencl-amd (AUR, conflicts with rocm-opencl-runtime)..."
sudo pacman -Rns opencl-amd --noconfirm 2>/dev/null || true
fi
if pacman -Q opencl-amd-debug &>/dev/null; then
sudo pacman -Rns opencl-amd-debug --noconfirm 2>/dev/null || true
fi
# Download the 6 packages from Arch Linux Archive
local ala="https://archive.archlinux.org/packages"
local pkgs=(
"${ala}/r/rocm-core/rocm-core-7.1.1-1-x86_64.pkg.tar.zst"
"${ala}/r/rocm-device-libs/rocm-device-libs-2:7.1.1-1-x86_64.pkg.tar.zst"
"${ala}/r/rocm-llvm/rocm-llvm-2:7.1.1-1-x86_64.pkg.tar.zst"
"${ala}/r/rocm-opencl-runtime/rocm-opencl-runtime-7.1.1-1-x86_64.pkg.tar.zst"
"${ala}/c/comgr/comgr-2:7.1.1-1-x86_64.pkg.tar.zst"
"${ala}/s/spirv-llvm-translator/spirv-llvm-translator-21.1.3-1-x86_64.pkg.tar.zst"
)
local tmpdir
tmpdir=$(mktemp -d -t rocm-pinned-XXXXXX)
local trap_old
# shellcheck disable=SC2064
trap "rm -rf '$tmpdir'" EXIT
info "Downloading ROCm 7.1.1 packages from Arch Linux Archive..."
local downloaded=()
for url in "${pkgs[@]}"; do
local fname
fname=$(basename "$url")
info " $fname"
if curl -fsSL --output "$tmpdir/$fname" "$url"; then
downloaded+=("$tmpdir/$fname")
else
error "Failed to download $url"
fi
done
# numactl + gflags are runtime deps usually already installed
sudo pacman -S --needed --noconfirm numactl gflags 2>&1 | tail -3 || true
info "Installing pinned ROCm 7.1.1 packages..."
sudo pacman -U --noconfirm "${downloaded[@]}" \
|| error "Failed to install pinned ROCm 7.1.1 packages from ALA - aborting before launcher/config steps run against a broken stack."
rm -rf "$tmpdir"
trap - EXIT
fi
# Pin in /etc/pacman.conf [options] IgnorePkg so future -Syu doesn't
# break Resolve again. Idempotent.
local pin_pkgs="rocm-core rocm-device-libs rocm-llvm rocm-opencl-runtime comgr spirv-llvm-translator"
if grep -q "^IgnorePkg.*rocm-core" /etc/pacman.conf; then
info "IgnorePkg already pins ROCm packages."
else
info "Adding IgnorePkg pin for ROCm 7.1.1 packages..."
# Insert immediately after [options] section header
sudo sed -i "/^\[options\]/a IgnorePkg = ${pin_pkgs}" /etc/pacman.conf
success "Pinned in /etc/pacman.conf [options]."
fi
success "ROCm 7.1.1 installed and pinned."
}
install_davinci_resolve() {
# NVIDIA-mirrored install path:
# 1. Find ZIP in ~/Downloads
# 2. Disk-space check (~10 GB free)
# 3. mktemp + trap to clean up on any exit
# 4. unzip ZIP -> find .run -> --appimage-extract -> squashfs-root
# 5. Replace bundled glib/gio/gmodule with system symlinks
# (KEEP vendored libc++/libc++abi - removing breaks Resolve)
# 6. Extract DaVinci panel-framework tarball if present
# 7. rsync squashfs-root to /opt/resolve
# 8. patchelf --set-rpath on every ELF in /opt/resolve
# 9. libcrypt.so.1 fallback symlink
# 10. Install desktop entries / icons / udev rules system-wide
#
# No AUR PKGBUILD, no qt5-webengine source build. Resolve's bundled Qt5
# is used via RPATH. Pacman does not track this install - to remove:
# `sudo rm -rf /opt/resolve` and the desktop/icon/udev files.
if [[ "$SCAN_RESOLVE_INSTALLED" == true ]]; then
info "DaVinci Resolve already installed at /opt/resolve."
info "Will apply workarounds and configuration updates."
return 0
fi
header "Installing DaVinci Resolve"
# ---- Locate the ZIP installer ----
local downloads_dir="$HOME/Downloads"
local resolve_zip=""
while IFS= read -r -d '' file; do
resolve_zip="$file"
done < <(find "$downloads_dir" -maxdepth 1 -name "DaVinci_Resolve_*_Linux.zip" -type f -print0 2>/dev/null | sort -zV)
if [[ -z "$resolve_zip" ]]; then
echo ""
warn "DaVinci Resolve ZIP not found in ~/Downloads"
echo " 1. Go to: https://www.blackmagicdesign.com/products/davinciresolve"
echo " 2. Click 'Download' -> 'DaVinci Resolve' (Free)"
echo " 3. Select 'Linux' and save the ZIP to ~/Downloads"
echo ""
read -p "Open download page? [Y/n] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
xdg-open "https://www.blackmagicdesign.com/products/davinciresolve" 2>/dev/null || true
fi
return 1
fi
success "Found installer: $resolve_zip"
local resolve_version=$(basename "$resolve_zip" | grep -oP 'Resolve_\K[0-9]+\.[0-9]+(\.[0-9]+)?' || echo "")
if [[ "$SCAN_IS_RDNA4" == true && -n "$resolve_version" ]]; then
local major=$(echo "$resolve_version" | cut -d'.' -f1)
if [[ "$major" -lt 20 ]]; then
warn "DaVinci Resolve $resolve_version may not work with RDNA 4 (need 20.x+)"
fi
fi
# ---- Disk space check (~10 GB needed for staged extraction) ----
local needed_gb=10
local free_kb=$(df --output=avail -k "$downloads_dir" | tail -n1)
local free_gb=$((free_kb / 1024 / 1024))
if (( free_gb < needed_gb )); then
error "Not enough free space in $downloads_dir: ${free_gb} GiB < ${needed_gb} GiB"
fi
# ---- Stage extraction in a temp dir under ~/Downloads ----
local workdir
workdir=$(mktemp -d -p "$downloads_dir" .resolve-extract-XXXXXXXX)
local original_dir="$PWD"
cleanup_resolve_build() {
cd "$original_dir" 2>/dev/null || cd ~
if [[ -n "${workdir:-}" && -d "$workdir" ]]; then
info "Cleaning up extraction directory..."
rm -rf "$workdir" 2>/dev/null || true
fi
}
trap cleanup_resolve_build EXIT
info "Unpacking ZIP to $workdir ..."
unzip -q "$resolve_zip" -d "$workdir"
local run_file
run_file=$(find "$workdir" -maxdepth 2 -type f -name 'DaVinci_Resolve_*_Linux.run' | head -n1 || true)
[[ -n "$run_file" ]] || error "Could not find the .run installer inside the ZIP"
chmod +x "$run_file"
local ex_dir=$(dirname "$run_file")
info "Extracting AppImage payload..."
if ! ( cd "$ex_dir" && "./$(basename "$run_file")" --appimage-extract >/dev/null ); then
error "Failed to extract AppImage payload from $run_file"
fi
local appdir="$ex_dir/squashfs-root"
[[ -d "$appdir" ]] || error "Extraction produced no squashfs-root directory"
[[ -s "$appdir/bin/resolve" ]] || error "resolve binary missing or zero-size after extraction"
chmod -R u+rwX,go+rX,go-w "$appdir" 2>/dev/null || warn "Could not normalize all permissions"
# ---- ABI-safe bundled-library replacement ----
# Replace bundled glib family with system versions (stable C ABI -
# safe to swap and avoids the well-known Resolve+Arch glib clash).
#
# All five glib-family libs must be swapped together: GObject's vtable
# layout has to match the GLib it was built against, and GThread shares
# internal state with GLib. Mixing a system libglib with bundled
# libgobject means GObject calls into GLib symbols that have moved
# between versions (Arch ships glib2 2.88+, bundled GObject is from
# 2.68) and segfaults on the first signal emit / type registration.
# Symptoms: random crashes in file pickers, GIO async ops, gstreamer
# media import.
#
# KEEP vendored libc++/libc++abi - replacing them causes immediate crashes
# because Resolve was compiled against specific libc++ ABI versions.
info "Replacing bundled glib family (glib/gio/gmodule/gobject/gthread) with system versions..."
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"
["/usr/lib/libgobject-2.0.so.0"]="libs/libgobject-2.0.so.0"
["/usr/lib/libgthread-2.0.so.0"]="libs/libgthread-2.0.so.0"
)
for syslib in "${!glib_libs[@]}"; do
local target="${glib_libs[$syslib]}"
if [[ -e "$syslib" ]]; then
rm -f "$target" || true
ln -sf "$syslib" "$target" || warn "Failed to symlink $syslib -> $target"
else
warn "System library $syslib not found, keeping bundled version"
fi
done
# Extract bundled DaVinci panel-framework (control surface support libs)
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
# AppImage launcher leftovers - we install to /opt/resolve, not run as AppImage
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 ----
info "Installing to /opt/resolve (rsync) ..."
sudo rm -rf /opt/resolve
sudo mkdir -p /opt/resolve
sudo rsync -a --delete "$appdir/" /opt/resolve/
sudo mkdir -p /opt/resolve/.license
# ---- RPATH patching ----
# Resolve's bundled binaries have RPATHs pointing to AppImage extraction
# paths that no longer exist. Patch every ELF (executable + shared object)
# to search /opt/resolve/libs/ and its plugin subdirectories.
info "Patching RPATH on every ELF in /opt/resolve (may take a minute)..."
local 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"
)
local rpath_abs=""
for p in "${rpath_dirs[@]}"; do rpath_abs+="/opt/resolve/${p}:"; done
rpath_abs+="\$ORIGIN"
local patch_count=0 patch_fail=0 patch_skip=0
while IFS= read -r -d '' f; do
local file_info
file_info=$(file -b "$f" 2>/dev/null)
if [[ "$file_info" =~ ELF.*executable ]] || [[ "$file_info" =~ ELF.*shared\ object ]]; then
local current_rpath
current_rpath=$(patchelf --print-rpath "$f" 2>/dev/null || true)
if [[ "$current_rpath" == "$rpath_abs" ]]; then
patch_skip=$((patch_skip + 1))
continue
fi
if sudo patchelf --set-rpath "$rpath_abs" "$f" 2>/dev/null; then
patch_count=$((patch_count + 1))
else
patch_fail=$((patch_fail + 1))
local file_size=$(stat -c%s "$f" 2>/dev/null || echo 0)
if (( file_size > 33554432 )); then
warn "Failed to patch large file (>32MB): ${f##/opt/resolve/}"
fi
fi
fi
done < <(find /opt/resolve -type f -print0)
success "Patched RPATH: $patch_count files ($patch_fail failures, $patch_skip already correct)"
# ---- libcrypt.so.1 fallback symlink ----
# libxcrypt-compat already provides /usr/lib/libcrypt.so.1; symlink it
# into /opt/resolve/libs as a belt-and-braces fallback.
sudo ldconfig 2>/dev/null || 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 entries (system-wide) ----
info "Installing desktop entries, icons, and udev rules..."
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
local dest="${desktop_files[$src]}"
if [[ -f "$src" ]]; then
sudo install -D -m 0644 "$src" "$dest"
# Critical: Resolve's bundled .desktop files have a literal
# "RESOLVE_INSTALL_LOCATION" placeholder that the official
# installer would substitute. Our manual copy doesn't, so the
# app menu launcher would do nothing. Substitute now.
sudo sed -i "s|RESOLVE_INSTALL_LOCATION|/opt/resolve|g" "$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
local 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
# ---- udev rules for Blackmagic capture cards / control surfaces ----
for r in 99-BlackmagicDevices.rules 99-ResolveKeyboardHID.rules 99-DavinciPanel.rules; do
local src="/opt/resolve/share/etc/udev/rules.d/$r"
if [[ -f "$src" ]]; then
sudo install -D -m 0644 "$src" "/usr/lib/udev/rules.d/$r"
fi
done
sudo udevadm control --reload-rules 2>/dev/null && sudo udevadm trigger 2>/dev/null || true
# Cleanup extraction tree
cleanup_resolve_build
trap - EXIT
# Refresh scan flag so downstream functions see the install
SCAN_RESOLVE_INSTALLED=true
SCAN_RESOLVE_PATH="/opt/resolve"
success "DaVinci Resolve installed to /opt/resolve."
}
disable_opencl_decoders() {
# Davincibox-style fix: disable BlackmagicRaw OpenCL decoders to prevent conflicts
# These are the exact paths from davincibox setup-davinci script
header "Disabling Problematic OpenCL Decoders"
local decoder_paths=(
# Exact paths from davincibox
"/opt/resolve/BlackmagicRAWPlayer/BlackmagicRawAPI/libDecoderOpenCL.so"
"/opt/resolve/BlackmagicRAWSpeedTest/BlackmagicRawAPI/libDecoderOpenCL.so"
# Additional possible locations
"/opt/resolve/libs/BlackmagicRawAPI/libDecoderOpenCL.so"
"/opt/resolve/BlackmagicRawAPI/libDecoderOpenCL.so"
)
local disabled=0
for decoder in "${decoder_paths[@]}"; do
if [[ -f "$decoder" && ! -f "${decoder}.bak" ]]; then
info "Disabling: $decoder"
sudo mv "$decoder" "${decoder}.bak"
disabled=$((disabled + 1))
elif [[ -f "${decoder}.bak" ]]; then
info "Already disabled: $decoder"
fi
done
if [[ $disabled -gt 0 ]]; then
success "Disabled $disabled OpenCL decoder(s) to prevent conflicts."
else
info "No OpenCL decoders needed disabling."
fi
}
setup_tls_symlink() {
# Resolve's built-in extras downloader expects RHEL/CentOS cert path
# /etc/pki/tls instead of Arch's /etc/ssl. Symlink so updates work.
header "Configuring TLS Cert Path for Resolve"
if [[ -e /etc/pki/tls ]]; then
info "/etc/pki/tls already exists - skipping."
return
fi
sudo mkdir -p /etc/pki
sudo ln -sf /etc/ssl /etc/pki/tls
success "Linked /etc/pki/tls -> /etc/ssl"
}
patch_audio_backend() {
# Resolve ships with `Local.Audio.Type = DeckLink` in its system-wide
# config template. On systems without a Blackmagic DeckLink card this
# makes Resolve fail on first launch. Switch the default to ALSA.
header "Patching Resolve Audio Backend (DeckLink -> ALSA)"
local 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"
success "Patched system template: $template"
elif [[ -f "$template" ]]; then
info "System template already patched or non-default."
else
warn "System template not found - skipping."
fi
local 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"
success "Patched user config (backup .bak.<timestamp> created)"
fi
}
setup_snd_aloop() {
# Resolve's audio engine opens raw ALSA hw: devices and never uses ALSA's
# plugin layer (default/pulse/pipewire). When PipeWire owns every real
# card, Resolve's enumerator loops looking for a free PCM and renders
# never start. Loading snd-aloop gives Resolve a virtual card it can fully
# own; PipeWire ignores it. Strace evidence: 14k+ SNDRV_CTL_IOCTL_PCM_INFO
# ENXIO / 47k+ /dev/snd/controlCN ENOENT during a stuck render.
#
# Skip with RESOLVE_NO_ALOOP=1 (e.g. user has a dedicated audio interface).
header "snd-aloop Render-Blocker Fix"
if [[ "${RESOLVE_NO_ALOOP:-0}" == "1" ]]; then
info "Skipping snd-aloop setup (RESOLVE_NO_ALOOP=1)."
return
fi
# 1. Load module now
if lsmod | grep -qE '^snd_aloop'; then
info "snd-aloop already loaded."
else
if sudo modprobe snd-aloop 2>/dev/null; then
success "snd-aloop loaded for the current session."
else
warn "modprobe snd-aloop failed. Verify with: modinfo snd-aloop"
warn "On Arch this is part of linux/linux-zen/linux-lts kernel packages."
fi
fi
# 2. Persist across reboots
local 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
success "Wrote $aloop_conf (autoloads at boot)."
else
info "$aloop_conf already configured."
fi
# 3. PipeWire loopback bridge - sends aloop capture to system default sink
# so Resolve monitor audio is audible while editing.
local bridge_dir="${HOME}/.config/pipewire/pipewire.conf.d"
local bridge_file="${bridge_dir}/50-resolve-aloop-bridge.conf"
mkdir -p "$bridge_dir"
if [[ ! -f "$bridge_file" ]]; then
cat > "$bridge_file" <<'EOF'
# DaVinci Resolve aloop monitor bridge - managed by install-davinci-resolve.sh
# Bridges snd-aloop's capture side to the system default sink so Resolve's
# monitor audio is audible while editing. Without this, renders complete 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
success "Wrote $bridge_file (PipeWire loopback bridge)."
else
info "$bridge_file already in place."
fi
# 4. Wireplumber rule - keep aloop OUT of default-sink rotation.
# Without this, wireplumber promotes aloop to default whenever Resolve
# makes it RUNNING, and the bridge feeds aloop back into itself.
local wp_dir="${HOME}/.config/wireplumber/wireplumber.conf.d"
local wp_file="${wp_dir}/51-resolve-aloop-no-default.conf"
mkdir -p "$wp_dir"
if [[ ! -f "$wp_file" ]]; then
cat > "$wp_file" <<'EOF'
# DaVinci Resolve aloop default-sink exclusion - managed by install-davinci-resolve.sh
# Without this, wireplumber promotes aloop to default whenever Resolve makes
# it RUNNING and the loopback bridge sends audio back into aloop. Setting
# both dont-fallback and disable-fallback covers minor key renames in the
# wireplumber 0.5.x series.
monitor.alsa.rules = [
{
matches = [
{ node.name = "alsa_output.platform-snd_aloop.0.analog-stereo" }
{ node.name = "alsa_input.platform-snd_aloop.0.analog-stereo" }
]
actions = {
update-props = {
priority.session = 0
priority.driver = 0
node.dont-fallback = true
node.disable-fallback = true
}
}
}
]
EOF
success "Wrote $wp_file (wireplumber default-sink exclusion)."
else
info "$wp_file already in place."
fi
# 5. Restart user services so the configs are picked up. wireplumber
# first, then pipewire/pipewire-pulse so the alsa rule re-applies
# when pipewire republishes aloop nodes.
if systemctl --user is-active --quiet pipewire 2>/dev/null; then
systemctl --user restart wireplumber pipewire pipewire-pulse 2>/dev/null || true
info "Reloaded user wireplumber + PipeWire services."
fi
}
configure_user_groups() {
# Davincibox requirement: user must be in 'render' and 'video' groups
# Without these, DaVinci Resolve may hang during rendering or fail to detect GPU
header "Configuring User Groups"
local groups_to_add=()
local current_groups=$(groups "$USER" 2>/dev/null)
# Check render group
if ! echo "$current_groups" | grep -qw "render"; then
if getent group render &>/dev/null; then
groups_to_add+=("render")
else
info "Group 'render' does not exist on this system (may not be needed)"
fi
else
info "User already in 'render' group"
fi
# Check video group
if ! echo "$current_groups" | grep -qw "video"; then
if getent group video &>/dev/null; then
groups_to_add+=("video")
fi
else
info "User already in 'video' group"
fi
# Add user to missing groups
if [[ ${#groups_to_add[@]} -gt 0 ]]; then
info "Adding user to groups: ${groups_to_add[*]}"
for grp in "${groups_to_add[@]}"; do
sudo usermod -a -G "$grp" "$USER"
done
warn "You will need to log out and back in for group changes to take effect!"
success "User added to required groups."
else
success "User already has required group memberships."
fi
}
reset_stale_configs() {
# Resolve defaults to GPU Processing Mode = CUDA on Linux. If a previous
# launch couldn't find an OpenCL device (e.g. ROCm wasn't installed yet,
# or an install was interrupted), Resolve writes a config snapshot and
# segfaults 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.
#
# Project databases under "Resolve Disk Database/" and "Resolve Project
# Library/" are NOT touched. Set RESOLVE_RESET_CONFIG=1 to force.
header "Checking for stale Resolve configs"
local resolve_user_dir="${HOME}/.local/share/DaVinciResolve"
local prior_crash=0
# Match any of the known crash signatures. ROCm 7.2 produces several:
# 'Unsupported GPU Processing Mode' is the original symptom (no OpenCL
# device visible at all). 'OpenCL Context Manager failed to create
# context' / 'Failed to create OpenCL context' fire when the device IS
# visible but clCreateContext fails - the actual recurrence the v4.1
# script missed on fresh installs. Once any of these poisoned configs
# land, every subsequent launch reuses them and crashes the same way.
local crash_markers='Unsupported GPU Processing Mode|OpenCL Context Manager failed to create context|Failed to create OpenCL context'
if [[ -f "${resolve_user_dir}/logs/ResolveDebug.txt" ]] && \
grep -qE "${crash_markers}" "${resolve_user_dir}/logs/ResolveDebug.txt" 2>/dev/null; then
prior_crash=1
local hit
hit=$(grep -oE "${crash_markers}" "${resolve_user_dir}/logs/ResolveDebug.txt" 2>/dev/null | head -1)
warn "Detected prior crash marker: '${hit}'"
fi
# On a fresh Resolve install (SCAN_RESOLVE_INSTALLED=false), any configs
# present can only have been written by a failed launch during this same
# install run - there is no real user state to preserve. Wipe them
# unconditionally so we never re-read a poisoned snapshot.
local fresh_install=0
if [[ "${SCAN_RESOLVE_INSTALLED}" != "true" ]]; then
fresh_install=1
fi
if (( prior_crash )) || (( fresh_install )) || [[ "${RESOLVE_RESET_CONFIG:-0}" == "1" ]]; then
if (( prior_crash )); then
info "Resetting stale configs to recover from prior failed launch."
elif (( fresh_install )); then
info "Fresh Resolve install - clearing any configs from in-install launches."
else
info "RESOLVE_RESET_CONFIG=1 set - forcing config reset."
fi
info "Project databases preserved (Resolve Disk Database, Resolve Project Library)."
rm -rf "${resolve_user_dir}/configs" "${resolve_user_dir}/logs" 2>/dev/null || true
success "Stale configs cleared. Next launch will run first-launch onboarding."
else
info "No stale config markers found."
fi
}
verify_opencl() {
# Sanity-check that an AMD OpenCL platform is visible after install.
# Resolve crashes on first launch with 'Unsupported GPU Processing Mode'
# if it can't see ANY OpenCL device, so this is the most useful single
# check we can do without launching Resolve.
header "OpenCL Sanity Check"
if ! command -v clinfo &>/dev/null; then
warn "clinfo not installed - cannot verify OpenCL visibility."
return
fi
local platforms
platforms=$(clinfo -l 2>/dev/null || true)
if echo "$platforms" | grep -qiE "AMD|gfx|Radeon"; then
success "AMD OpenCL platform visible to clinfo:"
echo "$platforms" | head -10
else
warn "No AMD OpenCL platform detected. Resolve will crash with 'Unsupported GPU Processing Mode'."
warn "Run: clinfo -l (and check rocminfo for ROCm device detection)"
warn "Verify ICD vendor file: ls /etc/OpenCL/vendors/ (should include amdocl64.icd)"
fi
# Pinned-stack health check
if [[ "$OPENCL_PROVIDER" == "rocm-pinned-7.1.1" ]]; then
local stack_ok=1
for pkg in rocm-core rocm-device-libs rocm-llvm rocm-opencl-runtime comgr; do
local ver
ver=$(pacman -Q "$pkg" 2>/dev/null | awk '{print $2}')
if [[ -z "$ver" ]]; then
warn "Missing pinned package: $pkg"
stack_ok=0
elif [[ ! "$ver" =~ 7\.1\.1 ]]; then
warn "$pkg is at $ver but expected 7.1.1 - pin may have been overridden!"
stack_ok=0
fi
done
local spirv_ver
spirv_ver=$(pacman -Q spirv-llvm-translator 2>/dev/null | awk '{print $2}')
if [[ -n "$spirv_ver" && ! "$spirv_ver" =~ 21\.1\.3 ]]; then
warn "spirv-llvm-translator is at $spirv_ver but expected 21.1.3"
stack_ok=0
fi
if [[ "$stack_ok" == 1 ]]; then
success "ROCm 7.1.1 pinned stack confirmed at correct versions."
else
warn "ROCm pinned stack drift detected - re-run install_rocm_pinned to repair."
fi
# IgnorePkg sanity
if grep -qE "^IgnorePkg.*rocm-core" /etc/pacman.conf; then
success "IgnorePkg pin is in place in /etc/pacman.conf [options]."
else
warn "IgnorePkg pin missing - next 'pacman -Syu' will break Resolve."
warn "Re-run install_rocm_pinned to fix."
fi
fi
}
cleanup_stale_extracts() {
# Previous failed runs may leave .resolve-extract-XXXXXX dirs (5-10 GB each)
# in ~/Downloads. Always clean these at the start.
local downloads_dir="$HOME/Downloads"
local stale=()
while IFS= read -r -d '' d; do
stale+=("$d")
done < <(find "$downloads_dir" -maxdepth 1 -type d -name '.resolve-extract-*' -print0 2>/dev/null)
if [[ ${#stale[@]} -gt 0 ]]; then
info "Cleaning ${#stale[@]} stale extraction dir(s) in ~/Downloads..."
rm -rf "${stale[@]}" 2>/dev/null || true
fi
}
create_launcher() {
header "Creating Launcher"
local launcher_dir="$HOME/.local/bin"
mkdir -p "$launcher_dir"
# GPU lock: whenever an AMD discrete GPU is present, pin OpenGL/Vulkan
# to that exact PCI bus address. This is unconditional - not gated on
# SCAN_IS_HYBRID, not gated on switcherooctl - because:
#
# - DRI_PRIME=1 is index-based and unsafe: it means "the OTHER card
# relative to Mesa's default", which on systems where the monitor
# is plugged into the AMD card is already AMD. =1 then FLIPS OpenGL
# to the iGPU, OpenCL stays on AMD via ROCR_VISIBLE_DEVICES, and
# CL/GL interop (clCreateContext with CL_GL_CONTEXT_KHR) fails -
# Resolve hangs on the Color page.
# - switcherooctl internally also uses DRI_PRIME=1, so it inherits
# the same bug. Skipping it avoids a needless layer.
# - On a single-GPU AMD box the pin is a no-op (correct card by
# default) but harmless, so we always emit it for safety.
#
# The explicit pci-DDDD_BB_DD_F tag pins by bus address and always
# lands on the real AMD card regardless of enumeration order, monitor
# routing, or compositor GBM device selection.
local gpu_launch_method=""
local prime_config=""
if [[ "$SCAN_HAS_AMD_DGPU" == true ]]; then
local amd_pci_tag=""
for i in "${!SCAN_GPU_VENDORS[@]}"; do
if [[ "${SCAN_GPU_VENDORS[$i]}" == "AMD" && "${SCAN_GPU_TYPES[$i]}" == "discrete" ]]; then
amd_pci_tag="pci-0000_${SCAN_GPU_PCI_IDS[$i]//[:.]/_}"
break
fi
done
if [[ -n "$amd_pci_tag" ]]; then
gpu_launch_method="dri_prime"
prime_config="
# Lock GPU to the discrete AMD card by PCI bus address. See create_launcher()
# in install-davinci-resolve.sh for why this is unconditional and why
# DRI_PRIME=1 / switcherooctl are deliberately not used.
export DRI_PRIME=${amd_pci_tag}
export MESA_VK_DEVICE_SELECT_FORCE_DEFAULT_DEVICE=1
export MESA_VK_DEVICE_SELECT=${amd_pci_tag}"
else
warn "AMD discrete GPU detected but PCI ID missing - falling back to no DRI_PRIME pin."
fi
elif [[ "$SCAN_IS_HYBRID" == true && "$SCAN_HAS_NVIDIA_DGPU" == true ]]; then
# Hybrid + NVIDIA discrete (no AMD discrete): use NVIDIA offload
gpu_launch_method="nvidia_prime"
prime_config='
# Hybrid GPU: Force discrete NVIDIA GPU
export __NV_PRIME_RENDER_OFFLOAD=1
export __GLX_VENDOR_LIBRARY_NAME=nvidia
export __VK_LAYER_NV_optimus=NVIDIA_only'
fi
# ROCm environment configuration.
# For rocm-pinned-7.1.1 (the working stack), ALWAYS export
# HSA_OVERRIDE_GFX_VERSION (matched to the detected gfx target) and
# ROCR_VISIBLE_DEVICES=0. These were empirically required to make
# Resolve's clCreateContext succeed on RDNA4 + ROCm 7.1.1 even though
# gfx1200 is "natively supported".
local rocm_config=""
if [[ "$OPENCL_PROVIDER" == "rocm-pinned-7.1.1" || "$OPENCL_PROVIDER" == "rocm-full" ]]; then
# Choose HSA value:
# - if scan detected a card needing spoof override, use that
# - else if we know the gfx target, use the matching native value
# - else leave unset (let ROCm autodetect)
local hsa_value="${SCAN_HSA_OVERRIDE_VALUE}"
if [[ -z "$hsa_value" && -n "$SCAN_GFX_TARGET" ]]; then
case "$SCAN_GFX_TARGET" in
gfx1201) hsa_value="12.0.1" ;;
gfx1200) hsa_value="12.0.0" ;;
gfx1101) hsa_value="11.0.1" ;;
gfx1100) hsa_value="11.0.0" ;;
gfx1030) hsa_value="10.3.0" ;;
esac
fi
local hsa_section=""
if [[ -n "$hsa_value" ]]; then
hsa_section="
# Detected ${SCAN_GFX_TARGET}. Setting HSA_OVERRIDE_GFX_VERSION explicitly
# (required for Resolve to succeed at clCreateContext on AMD even though
# this target is natively supported by ROCm).
export HSA_OVERRIDE_GFX_VERSION=${hsa_value}
# Pin Resolve to the discrete AMD GPU (defensive)
export ROCR_VISIBLE_DEVICES=0"
else
hsa_section='
# Could not auto-detect gfx target. Uncomment one of the lines below:
# export HSA_OVERRIDE_GFX_VERSION=12.0.1 # RX 9070 / 9070 XT (Navi 48, gfx1201)
# export HSA_OVERRIDE_GFX_VERSION=12.0.0 # RX 9060 / 9060 XT (Navi 44, gfx1200)
# export HSA_OVERRIDE_GFX_VERSION=11.0.1 # RX 7700/7800 (Navi 32, gfx1101)
# export HSA_OVERRIDE_GFX_VERSION=11.0.0 # RX 7900 (Navi 31, gfx1100) / RX 7600 (Navi 33, gfx1102)
# export HSA_OVERRIDE_GFX_VERSION=10.3.0 # RX 6800/6900 / RX 6600/6700 (RDNA2)
# export ROCR_VISIBLE_DEVICES=0'
fi
rocm_config="
# ROCm environment
export ROCM_PATH=/opt/rocm
export PATH=\"\$ROCM_PATH/bin:\$PATH\"
export LD_LIBRARY_PATH=\"\$ROCM_PATH/lib:\$ROCM_PATH/lib64:\$LD_LIBRARY_PATH\"
${hsa_section}
# ROCm OpenCL configuration
export OCL_ICD_VENDORS=/etc/OpenCL/vendors"
fi
# Create main launcher. AMD-discrete pin (prime_config) is unconditional
# when an AMD dGPU is present, so there is only one launcher template.
cat > "$launcher_dir/davinci-resolve" << EOF
#!/bin/bash
# DaVinci Resolve Launcher with workarounds
# Generated by install script on $(date)
# OpenCL Provider: $OPENCL_PROVIDER
# Fixes applied: patchelf (davincibox method)
# Clear stale single-instance Qt lockfiles (left behind by crashes/SIGKILL)
for lockfile in /tmp/qtsingleapp-DaVinci*lockfile; do
[[ -f "\$lockfile" ]] && rm -f "\$lockfile" 2>/dev/null || true
done
# Qt configuration for XWayland
export QT_QPA_PLATFORM=xcb
export QT_AUTO_SCREEN_SCALE_FACTOR=1
${prime_config}
${rocm_config}
# Increase file descriptor limit for large projects
ulimit -n 65535 2>/dev/null
exec /opt/resolve/bin/resolve "\$@"
EOF
chmod +x "$launcher_dir/davinci-resolve"
# Create Rusticl fallback launcher (davincibox -c flag equivalent)
# Use this if ROCm OpenCL doesn't work with your GPU
cat > "$launcher_dir/davinci-resolve-rusticl" << 'EOF'
#!/bin/bash
# DaVinci Resolve Launcher - Rusticl OpenCL Fallback
# Use this if ROCm doesn't work with your GPU (davincibox -c equivalent)
# Requires: opencl-rusticl-mesa package
export QT_QPA_PLATFORM=xcb
export QT_AUTO_SCREEN_SCALE_FACTOR=1
# Rusticl configuration (Mesa OpenCL implementation)
export RUSTICL_ENABLE=radeonsi,iris,nouveau
export OCL_ICD_VENDORS=rusticl.icd
ulimit -n 65535 2>/dev/null
exec /opt/resolve/bin/resolve "$@"
EOF
chmod +x "$launcher_dir/davinci-resolve-rusticl"
info "Created Rusticl fallback launcher: davinci-resolve-rusticl"
# Also create a version that forces integrated GPU (useful for testing/power saving)
if [[ "$SCAN_IS_HYBRID" == true ]]; then
cat > "$launcher_dir/davinci-resolve-igpu" << 'EOF'
#!/bin/bash
# DaVinci Resolve Launcher - Force Integrated GPU
# Use this for power saving or if discrete GPU causes issues
export QT_QPA_PLATFORM=xcb
export QT_AUTO_SCREEN_SCALE_FACTOR=1
export DRI_PRIME=0
ulimit -n 65535 2>/dev/null
exec /opt/resolve/bin/resolve "$@"
EOF
chmod +x "$launcher_dir/davinci-resolve-igpu"
info "Created alternate launcher: davinci-resolve-igpu (uses integrated GPU)"
fi
# Update PATH in shell configs
for rcfile in ~/.bashrc ~/.zshrc; do
if [[ -f "$rcfile" ]] && ! grep -q 'HOME/.local/bin' "$rcfile"; then
echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$rcfile"
fi
done
# Patch the Resolve-bundled system .desktop files to invoke our wrapper
# instead of the bare /opt/resolve/bin/resolve. The bare binary skips Qt
# lockfile cleanup, DRI_PRIME pinning, HSA_OVERRIDE, etc. This is the
# difference between "app-menu launch silently does nothing" and
# "Resolve actually starts".
#
# We deliberately DO NOT also create ~/.local/share/applications/davinci-resolve.desktop
# - the basenames differ from the system entry (davinci-resolve.desktop vs
# DaVinciResolve.desktop), so XDG would not shadow it and walker / app
# menus would show TWO "DaVinci Resolve" entries. One patched system
# entry is sufficient.
local wrapper="$HOME/.local/bin/davinci-resolve"
for sysdesktop in \
/usr/share/applications/DaVinciResolve.desktop \
/usr/share/applications/DaVinciResolveCaptureLogs.desktop; do
if [[ -f "$sysdesktop" ]]; then
sudo sed -i "s|^Exec=.*|Exec=$wrapper %U|" "$sysdesktop"
fi
done
# Remove any user-level davinci-resolve.desktop left over from older
# script runs (would duplicate the system entry in walker).
rm -f "$HOME/.local/share/applications/davinci-resolve.desktop" 2>/dev/null || true
sudo update-desktop-database >/dev/null 2>&1 || true
update-desktop-database "$HOME/.local/share/applications" >/dev/null 2>&1 || true
# System-wide convenience symlink so `davinci-resolve` works from any
# shell even when ~/.local/bin isn't on PATH (e.g. cron, ssh).
if [[ ! -e /usr/bin/davinci-resolve ]]; then
echo -e "#!/usr/bin/env bash\nexec $wrapper \"\$@\"" | \
sudo tee /usr/bin/davinci-resolve >/dev/null
sudo chmod +x /usr/bin/davinci-resolve
info "Created /usr/bin/davinci-resolve symlink"
fi
success "Launcher created."
echo ""
info "Available launchers:"
echo " davinci-resolve - Main launcher (default)"
echo " davinci-resolve-rusticl - Rusticl OpenCL fallback (if ROCm fails)"
if [[ "$SCAN_IS_HYBRID" == true ]]; then
echo " davinci-resolve-igpu - Force integrated GPU"
fi
echo ""
if [[ "$SCAN_HAS_AMD_DGPU" == true ]]; then
info "Launcher locked to AMD discrete GPU via PCI tag (DRI_PRIME=${amd_pci_tag:-<pci-tag>})"
elif [[ "$SCAN_IS_HYBRID" == true && "$SCAN_HAS_NVIDIA_DGPU" == true ]]; then
info "Hybrid GPU detected - launcher uses NVIDIA discrete via __NV_PRIME_RENDER_OFFLOAD"
fi
echo ""
info "If Resolve fails to use GPU, try: davinci-resolve-rusticl"
echo " (requires: sudo pacman -S opencl-rusticl-mesa)"
}
configure_hyprland() {
if [[ "$SCAN_HYPRLAND_INSTALLED" != true ]]; then
info "Hyprland not installed. Skipping configuration."
return
fi
header "Configuring Hyprland"
local hypr_conf="$HOME/.config/hypr/hyprland.conf"
if [[ ! -f "$hypr_conf" ]]; then
warn "Hyprland config not found at $hypr_conf"
echo "Add these rules manually (Hyprland 0.53+ syntax):"
echo ""
cat << 'EOF'
# Tag every floating Resolve window for easy targeting of the cosmetic
# tweaks below. Do NOT add `stay_focused on` here - it locks focus inside
# modal dialogs (Project Settings, Preferences, Render) so clicks on the
# main window do nothing, which feels like a trapped cursor.
windowrule = tag +drpopup, match:class ^(resolve)$, match:float 1
windowrule = border_size 0, match:tag drpopup
windowrule = no_shadow on, match:tag drpopup
windowrule = rounding 0, match:tag drpopup
windowrule = opacity 1, match:tag drpopup
EOF
return
fi
if grep -q "drpopup.*resolve\|tag +drpopup" "$hypr_conf" 2>/dev/null; then
info "Hyprland rules already configured."
return
fi
cp "$hypr_conf" "$hypr_conf.backup.$(date +%Y%m%d%H%M%S)"
# Hyprland 0.53+ syntax (windowrulev2 was removed). Verified against
# Omarchy's own default windowrules + Hyprland 0.54.3 binary symbols.
cat >> "$hypr_conf" << 'EOF'
# DaVinci Resolve - Fix floating dialogs (Hyprland 0.53+ syntax)
# Tag every floating Resolve window for easy targeting of the cosmetic
# tweaks below. Do NOT add `stay_focused on` here - it locks focus inside
# modal dialogs (Project Settings, Preferences, Render) so clicks on the
# main window do nothing, which feels like a trapped cursor.
windowrule = tag +drpopup, match:class ^(resolve)$, match:float 1
windowrule = border_size 0, match:tag drpopup
windowrule = no_shadow on, match:tag drpopup
windowrule = rounding 0, match:tag drpopup
windowrule = opacity 1, match:tag drpopup
EOF
success "Hyprland rules added."
info "Reload with: hyprctl reload && hyprctl configerrors"
}
create_conversion_script() {
header "Creating Media Conversion Helper"
cat > "$HOME/.local/bin/resolve-convert" << 'EOF'
#!/bin/bash
# Convert media to DNxHR for DaVinci Resolve Free (Linux)
convert_file() {
local input="$1"
local output="${2:-${input%.*}_dnxhr.mov}"
echo "Converting: $input -> $output"
ffmpeg -i "$input" -c:v dnxhd -profile:v dnxhr_hq -pix_fmt yuv422p -c:a pcm_s16le -y "$output"
echo "Done: $output"
}
[[ $# -eq 0 ]] && { echo "Usage: resolve-convert input.mp4 [output.mov]"; exit 1; }
if [[ $# -eq 2 && ! "$2" =~ \.(mp4|mkv|avi|webm)$ ]]; then
convert_file "$1" "$2"
else
for f in "$@"; do [[ -f "$f" ]] && convert_file "$f"; done
fi
EOF
chmod +x "$HOME/.local/bin/resolve-convert"
success "Created: resolve-convert"
}
install_diagnostics() {
header "Installing Diagnostics"
local checker_dir="$HOME/.local/share/davinci-resolve-checker"
$SCAN_AUR_HELPER -S --needed --noconfirm python-pylspci 2>/dev/null || true
if [[ -d "$checker_dir" ]]; then
cd "$checker_dir" && git pull --quiet
else
git clone --quiet https://github.com/Ashark/davinci-resolve-checker.git "$checker_dir"
fi
ln -sf "$checker_dir/davinci-resolve-checker.py" "$HOME/.local/bin/davinci-resolve-checker"
chmod +x "$checker_dir/davinci-resolve-checker.py"
success "Installed: davinci-resolve-checker"
}
print_summary() {
echo ""
echo "============================================================"
echo -e "${GREEN} Installation Complete!${NC}"
echo "============================================================"
echo ""
echo " Launch: davinci-resolve"
echo ""
echo " OpenCL Provider: $OPENCL_PROVIDER"
echo " Install method: NVIDIA-mirrored (manual extract + RPATH patch to /opt/resolve)"
echo " Fixes applied: glib symlink replace, RPATH patching, OpenCL decoder disable,"
echo " audio backend (DeckLink->ALSA), snd-aloop, TLS cert symlink,"
echo " Qt lockfile cleanup, libcrypt.so.1 fallback"
if [[ "$OPENCL_PROVIDER" == "rocm-pinned-7.1.1" ]]; then
echo " + ROCm 7.1.1 pinned from Arch Linux Archive"
echo " + IgnorePkg in /etc/pacman.conf [options]"
echo " + HSA_OVERRIDE_GFX_VERSION + ROCR_VISIBLE_DEVICES baked into launcher"
fi
echo ""
echo " Env vars (set before re-running this script):"
echo " RESOLVE_NO_ALOOP=1 Skip snd-aloop setup (you have a real audio interface)"
echo " RESOLVE_RESET_CONFIG=1 Force-wipe ~/.local/share/DaVinciResolve/{configs,logs}"
echo " (use after fixing OpenCL stack to clear crash snapshots)"
echo ""
if [[ "$OPENCL_PROVIDER" == "rocm-pinned-7.1.1" ]]; then
echo " IMPORTANT - ROCm 7.1.1 is PINNED:"
echo " ROCm 7.2.x breaks DaVinci Resolve (https://github.com/ROCm/ROCm/issues/5982)"
echo " These packages will NOT update on 'pacman -Syu':"
echo " rocm-core rocm-device-libs rocm-llvm rocm-opencl-runtime"
echo " comgr spirv-llvm-translator"
echo " When ROCm 7.3+ ships with the fix, lift the pin:"
echo " sudo sed -i '/^IgnorePkg.*rocm-/d' /etc/pacman.conf"
echo " sudo pacman -Syu"
echo ""
fi
echo " ROCm/diagnostic tools:"
echo " rocminfo # Show ROCm GPU info (Name: + Marketing Name)"
echo " rocm-smi # ROCm System Management Interface"
echo " clinfo -l # Check OpenCL platforms"
echo " davinci-resolve-checker # Diagnose Resolve issues"
echo ""
echo " Free version codec limitation (Linux):"
echo " No H.264/H.265/MP4 - convert with: resolve-convert video.mp4"
echo ""
echo " To upgrade Resolve: download new ZIP to ~/Downloads, re-run this script."
echo " To uninstall:"
echo " sudo rm -rf /opt/resolve"
echo " sudo rm -f /usr/share/applications/{DaVinciResolve,DaVinciControlPanelsSetup,blackmagicraw-*}.desktop"
echo " sudo rm -f /usr/lib/udev/rules.d/{99-BlackmagicDevices,99-ResolveKeyboardHID,99-DavinciPanel}.rules"
echo " sudo sed -i '/^IgnorePkg.*rocm-/d' /etc/pacman.conf # lift the pin"
echo ""
if [[ -n "$SCAN_GFX_TARGET" ]]; then
echo " GPU Detection:"
echo " Detected $SCAN_GFX_TARGET. Launcher pre-configured with:"
echo " HSA_OVERRIDE_GFX_VERSION (matched to your gfx target)"
echo " ROCR_VISIBLE_DEVICES=0"
echo " These were empirically required to make Resolve clCreateContext"
echo " succeed on AMD even though gfx1100+ targets are 'natively supported'."
echo ""
echo " Verify GPU detection: rocminfo | grep -E 'Name:|gfx'"
echo ""
fi
echo "============================================================"
}
# =============================================================================
# MAIN
# =============================================================================
main() {
echo ""
echo "============================================================"
echo " DaVinci Resolve Installer v4.1"
echo " Arch Linux + AMD (RDNA2/3/4) + Hyprland"
echo " NVIDIA-mirrored install path (extract + RPATH to /opt/resolve)"
echo " ROCm 7.1.1 pinned (working stack for Resolve on AMD, May 2026)"
echo "============================================================"
echo ""
# Run system scan
run_system_scan
display_scan_results
# Analyze and check for blockers
if ! analyze_scan_results; then
echo ""
read -p "Continue despite issues? [y/N] " -n 1 -r
echo
[[ ! $REPLY =~ ^[Yy]$ ]] && exit 1
fi
# Check AUR helper
if [[ -z "$SCAN_AUR_HELPER" ]]; then
error "No AUR helper found. Install yay or paru first."
fi
# Build package list
build_package_list
echo ""
read -p "Proceed with installation? [Y/n] " -n 1 -r
echo
[[ $REPLY =~ ^[Nn]$ ]] && exit 0
# Full system upgrade first - Arch is a rolling distro and partial upgrades
# (pacman -Sy <pkg> without -u) are unsupported and can break the system.
# This also ensures we get the latest ROCm.
header "System Update"
info "Arch is a rolling-release distro - a full system upgrade is recommended"
info "before installing new packages, to avoid partial-upgrade breakage and"
info "to pull the latest ROCm release."
echo ""
read -p "Run 'sudo pacman -Syu' now? [Y/n] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
sudo pacman -Syu --noconfirm
# Refresh the cached package list since the upgrade may have changed it
scan_installed_packages
success "System updated."
else
warn "Skipping system upgrade - proceed at your own risk."
warn "If pacman complains about stale databases, re-run after 'sudo pacman -Syu'."
fi
# Configure user groups for GPU access
configure_user_groups
# Clean up any leftover extraction dirs from prior failed runs
cleanup_stale_extracts
# Install
install_pacman_packages
install_aur_packages
install_rocm_pinned # downloads ROCm 7.1.1 from ALA + pins it
install_davinci_resolve
disable_opencl_decoders
setup_tls_symlink
patch_audio_backend
setup_snd_aloop
create_launcher
configure_hyprland
create_conversion_script
install_diagnostics
# Reset stale Resolve user configs if a prior crash marker exists
reset_stale_configs
# Final sanity check
verify_opencl
print_summary
}
main "$@"