commit d80b376e99933aa17d0c6e33170ea479e81e13b7 Author: 28allday Date: Sat Mar 28 12:30:52 2026 +0000 Initial commit: DaVinci Resolve installer for Arch Linux + AMD RDNA 4 Co-Authored-By: Claude Opus 4.6 (1M context) diff --git a/install-davinci-resolve.sh b/install-davinci-resolve.sh new file mode 100755 index 0000000..1d2bdb6 --- /dev/null +++ b/install-davinci-resolve.sh @@ -0,0 +1,1870 @@ +#!/bin/bash + +# ============================================================================= +# DaVinci Resolve Free - Arch Linux + AMD RX 9000 + Hyprland Install Script +# ============================================================================= +# +# Version 3.0 - Davincibox-style fixes for better compatibility +# Validated against Arch Wiki, AUR, davincibox, and community docs (Dec 2025) +# +# Features: +# - Pre-installation system scan to detect configuration +# - Smart package selection based on existing setup +# - AMD RX 9000 series (RDNA 4 / gfx1201) GPU support via ROCm +# - Choice of OpenCL provider: rocm-full (recommended) or opencl-amd (AUR) +# - Patchelf fix for glib issues (cleaner than LD_PRELOAD) +# - Disables problematic BlackmagicRaw OpenCL decoders +# - Removes conflicting OpenCL packages (rusticl breaks ROCm) +# - Hyprland compatibility (XWayland required) +# - Hybrid GPU support (Intel/AMD iGPU + discrete GPU) +# +# Based on techniques from: +# - https://github.com/zelikos/davincibox +# - https://wiki.archlinux.org/title/DaVinci_Resolve +# +# Usage: +# chmod +x install-davinci-resolve.sh +# ./install-davinci-resolve.sh +# +# ============================================================================= + +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: "opencl-amd" (AUR) or "rocm-full" (official Arch ROCm stack) +# opencl-amd: Uses AUR package (7.1.1+), includes both ROCm and ORCA - RECOMMENDED +# rocm-full: Uses official Arch repos (6.4.4+), full ROCm stack with HIP +# +# NOTE: Arch Wiki recommends opencl-amd for DaVinci Resolve +# "DaVinci Resolve seems to need opencl-amd and doesn't work with rocm-opencl-runtime" +# Change to "rocm-full" if you need HIP support or experience issues +OPENCL_PROVIDER="opencl-amd" + +# ============================================================================= +# 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_DRIVERS_LOADED=() +SCAN_PRIMARY_GPU_INDEX=0 + +# 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 + if echo "$gpu_line" | grep -qi "AMD\|ATI\|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 + + 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 -qi "Intel"; then + vendor="Intel" + gpu_type="integrated" + SCAN_HAS_IGPU=true + SCAN_HAS_INTEL_IGPU=true + rdna_ver="N/A" + 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 for AMD GPUs + if [[ "$vendor" == "AMD" ]]; then + SCAN_GPU_RDNA_VERSIONS+=("$rdna_ver") + else + SCAN_GPU_RDNA_VERSIONS+=("") + 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]}" + 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 + + 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++)) + 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++)) + 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, launch with: DRI_PRIME=1 davinci-resolve" + ((warnings++)) + 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=1 davinci-resolve" + echo " The launcher script will be configured for this." + ((warnings++)) + 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++)) + 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++)) + 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++)) + 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++)) + elif [[ "$SCAN_GPU_VENDOR" == "Intel" ]]; then + echo -e "${YELLOW}[WARNING]${NC} Only Intel GPU detected. DaVinci Resolve has limited Intel support." + ((warnings++)) + else + echo -e "${YELLOW}[WARNING]${NC} GPU vendor not recognized. This script is optimized for AMD GPUs." + ((warnings++)) + 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++)) + 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++)) + 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++)) + 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++)) + 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++)) + 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++)) + 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=() + + # Core graphics packages + # Note: libva-mesa-driver and mesa-vdpau are now merged into mesa package + local core_graphics=( + "mesa" + "glu" + ) + + for pkg in "${core_graphics[@]}"; do + if is_pkg_installed "$pkg"; then + SKIP_PACKAGES+=("$pkg") + else + PACMAN_PACKAGES+=("$pkg") + fi + done + + # OpenCL/ROCm - based on OPENCL_PROVIDER configuration + if [[ "$OPENCL_PROVIDER" == "rocm-full" ]]; then + # Full ROCm stack from official Arch repos (recommended for RDNA4) + info "Using full ROCm stack (official Arch repos)" + + # Check if already installed and working + if [[ "$SCAN_AMD_OPENCL_WORKING" == true && "$SCAN_OPENCL_PROVIDER" == rocm-full* ]]; then + info "ROCm already configured correctly" + SKIP_PACKAGES+=("rocm-opencl-runtime" "rocm-hip-runtime" "rocm-core") + else + # Core ROCm packages from official repos + local rocm_packages=( + "rocm-core" + "rocm-opencl-runtime" + "rocm-hip-runtime" + "hsa-rocr" + "rocminfo" + "rocm-smi-lib" + ) + + for pkg in "${rocm_packages[@]}"; do + if is_pkg_installed "$pkg"; then + SKIP_PACKAGES+=("$pkg") + else + PACMAN_PACKAGES+=("$pkg") + fi + done + fi + + # Remove conflicting AUR package if present + if is_pkg_installed "opencl-amd"; then + warn "opencl-amd (AUR) will be replaced with rocm-opencl-runtime" + fi + else + # AUR opencl-amd (legacy option) + info "Using opencl-amd (AUR package)" + if [[ "$SCAN_AMD_OPENCL_WORKING" == true && "$SCAN_OPENCL_PROVIDER" == "opencl-amd" ]]; then + info "OpenCL already configured correctly with opencl-amd" + SKIP_PACKAGES+=("opencl-amd") + else + if ! is_pkg_installed "ocl-icd"; then + PACMAN_PACKAGES+=("ocl-icd") + else + SKIP_PACKAGES+=("ocl-icd") + fi + AUR_PACKAGES+=("opencl-amd") + fi + fi + + # System libraries (includes davincibox dependencies mapped to Arch packages) + # Note: gtk2 moved to AUR on Arch (late 2024) + local system_libs=( + "libxcrypt-compat" + "fuse2" + "fuse3" + "libc++" + "libc++abi" + "patchelf" + # Additional libraries from davincibox + "alsa-lib" + "apr" + "apr-util" + "libglvnd" + "libice" + "libsm" + "librsvg" + "libxcursor" + "libxfixes" + "libxi" + "libxinerama" + "libxkbcommon-x11" + "libxkbfile" + "libxrandr" + "libxtst" + "libxxf86vm" + "lshw" + "mtdev" + "nss" + "libpulse" + "python-gobject" + "gdk-pixbuf2" + ) + + for pkg in "${system_libs[@]}"; do + if is_pkg_installed "$pkg"; then + SKIP_PACKAGES+=("$pkg") + else + PACMAN_PACKAGES+=("$pkg") + fi + done + + # Qt packages + # Note: qt5-webengine and qt5-websockets moved to AUR on Arch (Qt5 deprecated in favor of Qt6) + local qt_packages=( + "qt5-x11extras" + "qt5-svg" + "qt5-quickcontrols2" + "qt5-multimedia" + ) + + # DaVinci Resolve runtime dependencies (from AUR PKGBUILD) + local resolve_deps=( + "python-numpy" + "onetbb" # Was 'tbb', renamed on Arch + "xmlsec" + "gst-plugins-bad-libs" + "luajit" + ) + + for pkg in "${qt_packages[@]}"; do + if is_pkg_installed "$pkg"; then + SKIP_PACKAGES+=("$pkg") + else + PACMAN_PACKAGES+=("$pkg") + fi + done + + for pkg in "${resolve_deps[@]}"; do + if is_pkg_installed "$pkg"; then + SKIP_PACKAGES+=("$pkg") + else + PACMAN_PACKAGES+=("$pkg") + fi + done + + # Display packages (includes xcb-util packages from davincibox) + local display_packages=( + "xorg-xwayland" + "xdg-desktop-portal" + "xdg-desktop-portal-gtk" + # XCB utilities from davincibox + "xcb-util" + "xcb-util-cursor" + "xcb-util-image" + "xcb-util-keysyms" + "xcb-util-renderutil" + "xcb-util-wm" + # GPU switching for hybrid systems + "switcheroo-control" + ) + + for pkg in "${display_packages[@]}"; do + if is_pkg_installed "$pkg"; then + SKIP_PACKAGES+=("$pkg") + else + PACMAN_PACKAGES+=("$pkg") + fi + done + + # Audio packages - smart selection based on current audio server + if [[ "$SCAN_AUDIO_SERVER" == *"PipeWire"* ]]; then + info "PipeWire detected - using pipewire-pulse for audio" + if ! is_pkg_installed "pipewire-pulse"; then + PACMAN_PACKAGES+=("pipewire-pulse") + else + SKIP_PACKAGES+=("pipewire-pulse") + fi + # Also need ALSA bridge for some Resolve functionality + if ! is_pkg_installed "pipewire-alsa"; then + PACMAN_PACKAGES+=("pipewire-alsa") + fi + else + info "PulseAudio/other detected - using pulseaudio-alsa" + if ! is_pkg_installed "pulseaudio-alsa"; then + PACMAN_PACKAGES+=("pulseaudio-alsa") + else + SKIP_PACKAGES+=("pulseaudio-alsa") + fi + fi + + # Diagnostic tools + local diag_tools=( + "clinfo" + "mesa-utils" + "expac" + "python-distro" + "unzip" + ) + + for pkg in "${diag_tools[@]}"; do + if is_pkg_installed "$pkg"; then + SKIP_PACKAGES+=("$pkg") + else + PACMAN_PACKAGES+=("$pkg") + fi + done + + # AUR packages (packages moved from official repos) + # gtk2 - moved to AUR late 2024 + if ! is_pkg_installed "gtk2"; then + AUR_PACKAGES+=("gtk2") + else + SKIP_PACKAGES+=("gtk2") + fi + + # qt5-webengine - moved to AUR (Qt5 deprecated) + # Use binary version to avoid 30+ min compile time + if ! is_pkg_installed "qt5-webengine" && ! is_pkg_installed "qt5-webengine-bin"; then + AUR_PACKAGES+=("qt5-webengine-bin") + else + SKIP_PACKAGES+=("qt5-webengine") + fi + + # qt5-websockets - moved to AUR (Qt5 deprecated) + if ! is_pkg_installed "qt5-websockets"; then + AUR_PACKAGES+=("qt5-websockets") + else + SKIP_PACKAGES+=("qt5-websockets") + fi + + if ! is_pkg_installed "libpng12"; then + AUR_PACKAGES+=("libpng12") + else + SKIP_PACKAGES+=("libpng12") + fi + + # ffmpeg4.4 is now in official repos (extra) + if ! is_pkg_installed "ffmpeg4.4"; then + PACMAN_PACKAGES+=("ffmpeg4.4") + else + SKIP_PACKAGES+=("ffmpeg4.4") + fi + + # DaVinci Resolve itself + if [[ "$SCAN_RESOLVE_INSTALLED" == false ]]; then + AUR_PACKAGES+=("davinci-resolve") + fi + + # 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 + + # Handle libpng12 GPG key + if [[ " ${AUR_PACKAGES[*]} " =~ " libpng12 " ]]; then + info "Importing GPG key for libpng12..." + gpg --recv-keys F54984BFA16C640F 2>/dev/null || true + fi + + # Install AUR packages (except davinci-resolve which needs special handling) + local aur_without_resolve=() + for pkg in "${AUR_PACKAGES[@]}"; do + if [[ "$pkg" != "davinci-resolve" ]]; then + aur_without_resolve+=("$pkg") + fi + done + + if [[ ${#aur_without_resolve[@]} -gt 0 ]]; then + info "Installing AUR dependencies..." + $SCAN_AUR_HELPER -S --needed --noconfirm "${aur_without_resolve[@]}" + fi + + success "AUR packages installed." +} + +install_davinci_resolve() { + if [[ "$SCAN_RESOLVE_INSTALLED" == true ]]; then + info "DaVinci Resolve already installed. Skipping installation." + info "Will apply workarounds and configuration updates." + return 0 + fi + + header "Installing DaVinci Resolve" + + # Find installer (handle filenames with spaces properly) + local downloads_dir="$HOME/Downloads" + local resolve_run="" resolve_zip="" + + while IFS= read -r -d '' file; do + resolve_run="$file" + done < <(find "$downloads_dir" -maxdepth 1 -name "DaVinci_Resolve_*_Linux.run" -type f -print0 2>/dev/null | sort -zV) + + 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) + + # PKGBUILD expects the ZIP file, not the extracted .run + # If we only have .run, we can't use the AUR PKGBUILD easily + if [[ -z "$resolve_zip" && -n "$resolve_run" ]]; then + warn "Only .run file found, but PKGBUILD expects .zip" + warn "Please download the ZIP file from Blackmagic website" + return 1 + fi + + if [[ -z "$resolve_zip" ]]; then + echo "" + warn "DaVinci Resolve ZIP not found in ~/Downloads" + echo "" + echo "Please download it:" + echo " 1. Go to: https://www.blackmagicdesign.com/products/davinciresolve" + echo " 2. Click 'Download' → 'DaVinci Resolve' (Free)" + echo " 3. Select 'Linux' and save 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" + + # Check version for RDNA 4 compatibility + 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 + + # Build via AUR + local build_dir + # Use home directory for build (tmpfs /tmp may be too small) + build_dir=$(mktemp -d -p "$HOME/.cache" davinci-build.XXXXXX) + local original_dir="$PWD" + info "Building in $build_dir..." + + # Cleanup function for build directory + cleanup_build() { + cd "$original_dir" 2>/dev/null || cd ~ + rm -rf "$build_dir" 2>/dev/null + } + trap cleanup_build EXIT + + cd "$build_dir" + git clone https://aur.archlinux.org/davinci-resolve.git + cd davinci-resolve + + # Copy the ZIP file (PKGBUILD expects ZIP, not .run) + # Rename to match expected filename: DaVinci_Resolve_${pkgver}_Linux.zip + local expected_zip="DaVinci_Resolve_${resolve_version}_Linux.zip" + cp "$resolve_zip" "$expected_zip" + + # Fix Arch package renames in PKGBUILD + # tbb → onetbb (Intel TBB renamed on Arch) + sed -i "s/'tbb'/'onetbb'/g" PKGBUILD + + # Update PKGBUILD version if needed + local version_updated=false + if [[ -n "$resolve_version" ]]; then + local pkgbuild_ver=$(grep "^pkgver=" PKGBUILD | cut -d'=' -f2) + if [[ "$resolve_version" != "$pkgbuild_ver" ]]; then + info "Updating PKGBUILD version to $resolve_version..." + sed -i "s/^pkgver=.*/pkgver=$resolve_version/" PKGBUILD + version_updated=true + # Use updpkgsums if available, otherwise we'll skip checksums during build + if command -v updpkgsums &>/dev/null; then + info "Updating checksums..." + updpkgsums + version_updated=false # checksums are now correct + else + info "Will skip checksum verification (version mismatch, updpkgsums not available)..." + fi + fi + fi + + info "Building package (this takes several minutes)..." + if [[ "$version_updated" == true ]]; then + makepkg -si --noconfirm --skipchecksums + else + makepkg -si --noconfirm + fi + + # Cleanup + cleanup_build + trap - EXIT + + success "DaVinci Resolve installed." +} + +apply_patchelf_fix() { + # Davincibox-style fix: use patchelf to add system library dependencies + # This is cleaner than LD_PRELOAD and survives across sessions + header "Applying Patchelf Fix (Davincibox Method)" + + local resolve_bin="/opt/resolve/bin" + + if [[ ! -d "$resolve_bin" ]]; then + warn "Resolve bin directory not found. Skipping patchelf fix." + return + fi + + if ! command -v patchelf &>/dev/null; then + warn "patchelf not installed. Falling back to library move method." + apply_glib_fix_fallback + return + fi + + # Check if already patched by looking for added dependencies + if patchelf --print-needed "$resolve_bin/resolve" 2>/dev/null | grep -q "libglib-2.0.so.0"; then + info "Patchelf fix already applied." + return + fi + + info "Patching DaVinci Resolve binaries with system library dependencies..." + + # Determine library path (Arch uses /usr/lib, Fedora uses /usr/lib64) + local lib_path="/usr/lib" + [[ -d "/usr/lib64" && -f "/usr/lib64/libglib-2.0.so.0" ]] && lib_path="/usr/lib64" + + # Libraries to add as dependencies (forces use of system libs over bundled) + # Using full paths as davincibox does + local libs_to_add=( + "${lib_path}/libglib-2.0.so.0" + "${lib_path}/libgio-2.0.so.0" + "${lib_path}/libgmodule-2.0.so.0" + "${lib_path}/libgdk_pixbuf-2.0.so.0" + ) + + # Verify libraries exist + for lib in "${libs_to_add[@]}"; do + if [[ ! -f "$lib" ]]; then + warn "Library not found: $lib" + warn "Falling back to library names only..." + libs_to_add=( + "libglib-2.0.so.0" + "libgio-2.0.so.0" + "libgmodule-2.0.so.0" + "libgdk_pixbuf-2.0.so.0" + ) + break + fi + done + + # Patch all executables in bin directory using find (like davincibox) + info "Running: find $resolve_bin -executable -type f -exec patchelf ..." + + local patch_count=0 + while IFS= read -r -d '' binary; do + # Skip if not an ELF binary + file "$binary" 2>/dev/null | grep -q "ELF" || continue + + local patch_args="" + for lib in "${libs_to_add[@]}"; do + patch_args+=" --add-needed $lib" + done + + if sudo patchelf $patch_args "$binary" 2>/dev/null; then + ((patch_count++)) + fi + done < <(find "$resolve_bin" -executable -type f -print0 2>/dev/null) + + local patched=$patch_count + local failed=0 + + if [[ $patched -gt 0 ]]; then + success "Patchelf fix applied to binaries." + else + warn "Patchelf fix may have failed. Applying fallback..." + apply_glib_fix_fallback + fi +} + +apply_glib_fix_fallback() { + # Fallback: move bundled glib libraries (original method) + local resolve_libs="/opt/resolve/libs" + + if [[ ! -d "$resolve_libs" ]]; then + warn "Resolve libs directory not found. Skipping." + return + fi + + # Check if already fixed + if [[ -d "$resolve_libs/disabled-libraries" ]] && \ + [[ ! -f "$resolve_libs/libglib-2.0.so.0" ]]; then + info "Glib fix (fallback) already applied." + return + fi + + info "Moving bundled glib libraries (fallback method)..." + sudo mkdir -p "$resolve_libs/disabled-libraries" + + # Use find to avoid glob pattern issues when no files match + while IFS= read -r -d '' lib; do + sudo mv "$lib" "$resolve_libs/disabled-libraries/" 2>/dev/null || true + done < <(find "$resolve_libs" -maxdepth 1 -type f \( -name 'libglib-2.0.so*' -o -name 'libgio-2.0.so*' -o -name 'libgmodule-2.0.so*' \) -print0 2>/dev/null) + + success "Glib fix (fallback) applied." +} + +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 +} + +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 +} + +setup_studio_usb_dongle() { + # For DaVinci Resolve Studio with USB license dongle + # Davincibox mentions vendor ID 096e for USB dongle access + header "Checking Studio USB Dongle Support" + + local udev_rule_file="/etc/udev/rules.d/75-davinci-resolve-dongle.rules" + local udev_rule='SUBSYSTEM=="usb", ATTR{idVendor}=="096e", MODE="0666"' + + # Check if Studio version + if [[ -f "/opt/resolve/bin/resolve" ]]; then + # Check if it's Studio by looking for specific files or just offer the option + if pacman -Qq davinci-resolve-studio &>/dev/null 2>&1 || \ + [[ -f "/opt/resolve/Developer/Scripting/README.txt" ]]; then + info "DaVinci Resolve Studio detected" + + if [[ ! -f "$udev_rule_file" ]]; then + read -p "Set up USB dongle udev rule for Studio license? [y/N] " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "$udev_rule" | sudo tee "$udev_rule_file" > /dev/null + sudo udevadm control --reload-rules + sudo udevadm trigger + success "USB dongle udev rule installed." + fi + else + info "USB dongle udev rule already exists." + fi + else + info "DaVinci Resolve Free detected - USB dongle rule not needed." + fi + else + info "DaVinci Resolve not yet installed - skipping USB dongle setup." + fi +} + +create_launcher() { + header "Creating Launcher" + + local launcher_dir="$HOME/.local/bin" + mkdir -p "$launcher_dir" + + # Check for switcherooctl (davincibox method for hybrid GPU) + local has_switcheroo=false + if command -v switcherooctl &>/dev/null && systemctl is-active --quiet switcheroo-control 2>/dev/null; then + has_switcheroo=true + fi + + # Determine GPU launch method for hybrid systems + local gpu_launch_method="" + local prime_config="" + if [[ "$SCAN_IS_HYBRID" == true ]]; then + if [[ "$has_switcheroo" == true ]]; then + # Davincibox method: use switcherooctl for GPU selection + gpu_launch_method="switcheroo" + info "Using switcherooctl for GPU selection (davincibox method)" + elif [[ "$SCAN_HAS_AMD_DGPU" == true ]]; then + # Fallback: AMD discrete GPU - use DRI_PRIME + gpu_launch_method="dri_prime" + prime_config=' +# Hybrid GPU: Force discrete AMD GPU +export DRI_PRIME=1' + elif [[ "$SCAN_HAS_NVIDIA_DGPU" == true ]]; then + # Fallback: NVIDIA discrete GPU - 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 + fi + + # ROCm environment configuration + local rocm_config="" + if [[ "$OPENCL_PROVIDER" == "rocm-full" ]]; then + rocm_config=' +# ROCm environment (full stack from official Arch repos) +export ROCM_PATH=/opt/rocm +export PATH="$ROCM_PATH/bin:$PATH" +export LD_LIBRARY_PATH="$ROCM_PATH/lib:$ROCM_PATH/lib64:$LD_LIBRARY_PATH" + +# Enable RDNA4 support (gfx1201) +export HSA_OVERRIDE_GFX_VERSION=11.0.1 + +# ROCm OpenCL configuration +export OCL_ICD_VENDORS=/etc/OpenCL/vendors' + fi + + # Create main launcher based on GPU method + if [[ "$gpu_launch_method" == "switcheroo" ]]; then + # Davincibox-style launcher using switcherooctl + cat > "$launcher_dir/davinci-resolve" << EOF +#!/bin/bash +# DaVinci Resolve Launcher with workarounds +# Generated by install script on $(date) +# OpenCL Provider: $OPENCL_PROVIDER +# GPU Selection: switcherooctl (davincibox method) +# Fixes applied: patchelf (davincibox method) + +# Qt configuration for XWayland +export QT_QPA_PLATFORM=xcb +export QT_AUTO_SCREEN_SCALE_FACTOR=1 +${rocm_config} + +# Increase file descriptor limit for large projects +ulimit -n 65535 2>/dev/null + +# Use switcherooctl for discrete GPU selection (davincibox method) +exec switcherooctl launch /opt/resolve/bin/resolve "\$@" +EOF + else + # Standard launcher with DRI_PRIME/NVIDIA offload + 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) + +# 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 + fi + + 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 + + # Create desktop entry + mkdir -p "$HOME/.local/share/applications" + + # Determine Exec line based on hybrid status + local exec_line="$HOME/.local/bin/davinci-resolve %U" + + cat > "$HOME/.local/share/applications/davinci-resolve.desktop" << EOF +[Desktop Entry] +Version=1.0 +Type=Application +Name=DaVinci Resolve +Comment=Professional video editing +Exec=$exec_line +Icon=DV_Resolve +Terminal=false +Categories=AudioVideo;Video; +StartupWMClass=resolve +EOF + + 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" + echo "" + info "Hybrid GPU detected - launcher configured to use discrete GPU" + if [[ "$gpu_launch_method" == "switcheroo" ]]; then + echo " Using: switcherooctl launch (davincibox method)" + elif [[ "$SCAN_HAS_AMD_DGPU" == true ]]; then + echo " Using: DRI_PRIME=1 (AMD discrete)" + elif [[ "$SCAN_HAS_NVIDIA_DGPU" == true ]]; then + echo " Using: __NV_PRIME_RENDER_OFFLOAD=1 (NVIDIA discrete)" + fi + 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:" + echo "" + cat << 'EOF' +windowrulev2 = tag +drpopup, class:^(resolve)$, floating:1 +windowrulev2 = stayfocused, tag:drpopup +windowrulev2 = noborder, tag:drpopup +windowrulev2 = opaque, 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)" + + cat >> "$hypr_conf" << 'EOF' + +# DaVinci Resolve - Fix floating dialogs +windowrulev2 = tag +drpopup, class:^(resolve)$, floating:1 +windowrulev2 = stayfocused, tag:drpopup +windowrulev2 = bordersize 0, tag:drpopup +windowrulev2 = noborder, tag:drpopup +windowrulev2 = noshadow, tag:drpopup +windowrulev2 = rounding 0, tag:drpopup +windowrulev2 = opaque, tag:drpopup +EOF + + success "Hyprland rules added." + info "Reload with: hyprctl reload" +} + +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 " Fixes applied: patchelf (davincibox method), OpenCL decoder disable" + echo "" + if [[ "$OPENCL_PROVIDER" == "rocm-full" ]]; then + echo " ROCm tools:" + echo " rocminfo # Show ROCm GPU info" + echo " rocm-smi # ROCm System Management Interface" + echo " clinfo --list # Check OpenCL platforms" + else + echo " Troubleshooting:" + echo " clinfo --list # Check OpenCL" + fi + 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 " After Resolve updates, re-run this script or manually:" + echo " # Re-apply patchelf fix to new binaries" + echo " for bin in /opt/resolve/bin/*; do" + echo " sudo patchelf --add-needed libglib-2.0.so.0 \"\$bin\" 2>/dev/null" + echo " done" + echo "" + if [[ "$SCAN_IS_RDNA4" == true ]]; then + echo " RDNA4 Note:" + echo " If Resolve doesn't detect GPU, try adjusting HSA_OVERRIDE_GFX_VERSION" + echo " in ~/.local/bin/davinci-resolve (currently set to 11.0.1)" + echo " Common values: 11.0.0 (gfx1100), 11.0.1 (gfx1201)" + echo "" + fi + echo "============================================================" +} + +# ============================================================================= +# MAIN +# ============================================================================= + +main() { + echo "" + echo "============================================================" + echo " DaVinci Resolve Installer v3.0" + echo " Arch Linux + AMD GPU (RDNA4) + Hyprland" + echo " With Davincibox-style fixes (patchelf, decoder disable)" + 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 + + # Configure user groups for GPU access + configure_user_groups + + # Install + install_pacman_packages + install_aur_packages + install_davinci_resolve + apply_patchelf_fix + disable_opencl_decoders + setup_studio_usb_dongle + create_launcher + configure_hyprland + create_conversion_script + install_diagnostics + + print_summary +} + +main "$@"