From 05aaf04c27fc64160c50ca924b45c42fe7b4aee1 Mon Sep 17 00:00:00 2001 From: 28allday Date: Sun, 3 May 2026 11:15:08 +0100 Subject: [PATCH] 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) --- NOTES.md | 312 ++++++ README.md | 229 ++--- install-davinci-resolve.sh | 1899 ++++++++++++++++++++++-------------- 3 files changed, 1577 insertions(+), 863 deletions(-) create mode 100644 NOTES.md diff --git a/NOTES.md b/NOTES.md new file mode 100644 index 0000000..314a126 --- /dev/null +++ b/NOTES.md @@ -0,0 +1,312 @@ +# DaVinci Resolve on Omarchy + RX 9060 XT — Working Configuration + +**Date solved:** 2026-05-02 (initial); 2026-05-03 (DRI_PRIME hybrid-GPU fix) +**System:** Omarchy (Arch Linux), Hyprland 0.54.3, kernel 7.0.3, RX 9060 XT (Navi 44, gfx1200) + Intel UHD 770 hybrid + +## TL;DR — what is currently working + +DaVinci Resolve 21.0 beta launches and stays open with full UI on Color page. The working stack is: + +- ROCm **7.1.1** (NOT current 7.2.x) pinned from Arch Linux Archive +- Resolve installed manually to `/opt/resolve` (no AUR PKGBUILD) +- `HSA_OVERRIDE_GFX_VERSION=12.0.0` and `ROCR_VISIBLE_DEVICES=0` in the launcher +- `DRI_PRIME=pci-0000_BB_DD_F` in the launcher — **explicit PCI tag, NOT `=1`** (see "Root cause" below) +- `MESA_VK_DEVICE_SELECT=pci-0000_BB_DD_F` + `MESA_VK_DEVICE_SELECT_FORCE_DEFAULT_DEVICE=1` (Vulkan pin, defense-in-depth) +- Hyprland windowrule v3 syntax (not deprecated `windowrulev2`) +- snd-aloop kernel module + PipeWire bridge for audio +- DeckLink → ALSA backend in Resolve config + +## Root cause + +There are TWO independent root causes that both have to be fixed for first launch to succeed: + +### 1. ROCm 7.2.x breaks Resolve on every AMD GPU + +OpenCL `clCreateContext` either fails outright or hangs on Color page. ROCm 7.1.1 is the last working release. + +- Upstream issue: https://github.com/ROCm/ROCm/issues/5982 (filed Feb 2026, still open) +- CachyOS thread with the recipe: https://discuss.cachyos.org/t/davinci-resolve-amd-rocm-fails-without-downgrade/28036 +- NixOS RDNA4-specific report: https://github.com/NixOS/nixpkgs/issues/481483 + +### 2. `DRI_PRIME=1` flips OpenGL to the iGPU on hybrid systems where Mesa already defaults to AMD + +On Intel iGPU + AMD dGPU systems where the monitor is plugged into the AMD card, Mesa's default OpenGL device is already the AMD card. `DRI_PRIME=1` means "give me the OTHER card relative to the default" — so it flips OpenGL to Intel. Resolve then has OpenGL on Intel and OpenCL on AMD, and CL/GL interop (`clCreateContext` with `CL_GL_CONTEXT_KHR`) fails. The log shows `OpenCL Context Manager failed to create context` and Resolve hangs on Color page. + +**Verify on any new box:** +```bash +glxinfo -B 2>/dev/null | grep -E 'OpenGL renderer|OpenGL vendor' # bare default +DRI_PRIME=1 glxinfo -B 2>/dev/null | grep -E 'OpenGL renderer|OpenGL vendor' # what =1 picks +DRI_PRIME=pci-0000_BB_DD_F glxinfo -B 2>/dev/null | grep -E 'OpenGL renderer|OpenGL vendor' # explicit +``` +If bare returns AMD and `=1` returns Intel, you have this bug. The explicit PCI tag is always correct. + +The installer detects the AMD discrete card's PCI ID from `lspci` and bakes the tag into the launcher unconditionally whenever an AMD dGPU is present (no longer gated on hybrid status or switcheroo). switcherooctl is **not** used — it internally sets `DRI_PRIME=1` and inherits the same flip bug. + +## The install script + +`/home/q/Downloads/install-davinci-resolve.sh` — version 4.1 with 2026-05-03 patches: + +- **DRI_PRIME explicit PCI-tag pin** (replaces buggy `=1`) + **MESA_VK_DEVICE_SELECT** Vulkan pin — locks OpenGL/Vulkan to the AMD discrete GPU by bus address, unconditional whenever an AMD dGPU is present (no longer gated on hybrid status, switcheroo branch removed) +- **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) +- **Broader crash-marker grep** in `reset_stale_configs()` — recognises `Unsupported GPU Processing Mode`, `OpenCL Context Manager failed to create context`, `Failed to create OpenCL context` +- **Fresh-install auto-wipe** of `~/.local/share/DaVinciResolve/{configs,logs}` whenever Resolve is being installed for the first time +- **ROCm-downgrade error check** — `pacman -U` now aborts the install if the 7.1.1 packages fail to apply, instead of silently continuing on a broken stack +- **Removed USB-dongle udev rule** and the prompt for it (Studio license dongle not in scope) +- **Removed duplicate user-level `.desktop` entry** — system-level entry is patched to use the wrapper, user entry caused walker to show two "DaVinci Resolve" entries +- **Dropped `stay_focused on` from the Resolve Hyprland rules** — it locked focus inside modal dialogs, felt like a trapped cursor + +Runs the full install end-to-end: + +1. System scan (kernel, GPU detection with gfx target + PCI bus address, audio, display, etc.) +2. Pacman package install (minimal — Resolve bundles its own Qt5/libpng/etc.) +3. **`install_rocm_pinned()`** — downloads ROCm 7.1.1 from Arch Linux Archive, installs via `pacman -U` (now `|| error` on failure so partial-downgrade can't go unnoticed), adds `IgnorePkg` to `/etc/pacman.conf [options]` +4. Manual Resolve extraction (ZIP → .run → squashfs → /opt/resolve) with RPATH patching of every ELF. Bundled glib family — **all five** of `libglib-2.0`, `libgio-2.0`, `libgmodule-2.0`, `libgobject-2.0`, `libgthread-2.0` — replaced with system symlinks. Earlier installs only swapped three; mixing system glib with bundled libgobject (4-year version skew) risks segfaults on signal emit / type registration. Bundled `libc++`/`libc++abi` are deliberately KEPT (Resolve was compiled against specific ABI versions; replacing them crashes immediately). +5. Audio fixes: DeckLink→ALSA, snd-aloop, PipeWire bridge, wireplumber default-sink exclusion +6. Hyprland windowrule (v3 syntax) +7. Desktop entries with `RESOLVE_INSTALL_LOCATION` placeholder substituted, system .desktop Exec lines pointed at the wrapper +8. Launcher in `~/.local/bin/davinci-resolve` with HSA_OVERRIDE + ROCR_VISIBLE_DEVICES + **`DRI_PRIME=pci-` (explicit tag, derived from the AMD discrete card's PCI ID)** + MESA_VK_DEVICE_SELECT baked in. Single launcher template — switcheroo branch removed. +9. **`reset_stale_configs()`** — clears `~/.local/share/DaVinciResolve/{configs,logs}` if any of these crash markers appear in `ResolveDebug.txt`: `Unsupported GPU Processing Mode`, `OpenCL Context Manager failed to create context`, `Failed to create OpenCL context`. Also fires unconditionally on fresh Resolve installs (`SCAN_RESOLVE_INSTALLED != true`) so any in-install failed-launch poison is wiped before the user ever opens the app. +10. Final `clinfo -l` sanity check + ROCm pin verification + +Re-run any time: +```bash +~/Downloads/install-davinci-resolve.sh +``` + +## Key files / locations + +| File | Purpose | +|------|---------| +| `~/Downloads/install-davinci-resolve.sh` | The full installer (v4.1) | +| `~/Downloads/fix-rocm-for-resolve.sh` | Standalone downgrade-only script (no install needed) | +| `~/.local/bin/davinci-resolve` | Working launcher: HSA_OVERRIDE + ROCR_VISIBLE_DEVICES + DRI_PRIME PCI tag + MESA_VK pin | +| `~/.local/bin/davinci-resolve-rusticl` | Rusticl OpenCL fallback (not currently working on RDNA4) | +| `~/.local/bin/davinci-resolve-igpu` | Force iGPU (Intel) for testing | +| `/usr/share/applications/DaVinciResolve.desktop` | The single Resolve menu entry; Exec rewritten to wrapper. No user-level davinci-resolve.desktop is created (would duplicate this in walker). | +| `/usr/bin/davinci-resolve` | Convenience symlink to wrapper | +| `/etc/pacman.conf` | Has `IgnorePkg = rocm-core rocm-device-libs rocm-llvm rocm-opencl-runtime comgr spirv-llvm-translator` in `[options]` | +| `~/.config/pipewire/pipewire.conf.d/50-resolve-aloop-bridge.conf` | PipeWire loopback bridge for audio | +| `~/.config/wireplumber/wireplumber.conf.d/51-resolve-aloop-no-default.conf` | Keep aloop off default-sink rotation | +| `/etc/modules-load.d/snd-aloop.conf` | Auto-load snd-aloop at boot | +| `/etc/pki/tls -> /etc/ssl` | Symlink so Resolve's extras downloader finds Arch certs | + +## Currently pinned packages (do NOT update) + +``` +rocm-core 7.1.1-1 +rocm-device-libs 2:7.1.1-1 +rocm-llvm 2:7.1.1-1 +rocm-opencl-runtime 7.1.1-1 +comgr 2:7.1.1-1 +spirv-llvm-translator 21.1.3-1 +``` + +## Critical "do not" + +- **Do not** install `opencl-amd` from AUR — conflicts with `rocm-opencl-runtime`, also has the broken 7.2 ABI. +- **Do not** run `pacman -Syu` and accept rocm-* updates until ROCm 7.3+ ships with the Resolve fix. +- **Do not** set `DRI_PRIME=1` on hybrid AMD systems — it's the OPPOSITE of what you want when Mesa already defaults to AMD (see Root cause #2). Always use the explicit PCI tag form `DRI_PRIME=pci-0000_BB_DD_F`. +- **Do not** wrap the launcher in `switcherooctl launch` — it inherits the same `DRI_PRIME=1` bug. +- **Do not** use `windowrulev2 = ...` in Hyprland config — deprecated in 0.53+. Use `windowrule = ACTION, match:KEY VALUE` form. +- **Do not** add `stay_focused on` to the Resolve `drpopup` tag — it locks focus inside modal dialogs (Project Settings, Preferences, Render) and feels like a trapped cursor. Cosmetic-only rules (border_size, no_shadow, rounding, opacity) are safe. +- **Do not** edit `~/.local/share/omarchy/` — it's git-managed by Omarchy and gets blown away on `omarchy update`. + +## When ROCm 7.3+ ships + +To lift the pin and try the official stack again: +```bash +sudo sed -i '/^IgnorePkg.*rocm-/d' /etc/pacman.conf +sudo pacman -Syu +~/.local/bin/davinci-resolve # test +``` + +If 7.3 also breaks Resolve, restore the pin via: +```bash +~/Downloads/fix-rocm-for-resolve.sh +``` + +## Recovery procedures + +### Resolve hangs / crashes + +The installer now wipes stale configs automatically on fresh installs and any time it sees a known crash marker, so this should rarely be needed manually. If you do need it: + +```bash +# 1. Wipe stale config (preserves project DBs) +mv ~/.local/share/DaVinciResolve/configs ~/.local/share/DaVinciResolve/configs.bak.$(date +%s) +mv ~/.local/share/DaVinciResolve/logs ~/.local/share/DaVinciResolve/logs.bak.$(date +%s) + +# 2. Verify the OpenCL stack is intact +clinfo -l # should show AMD gfx1200 +pacman -Q rocm-core rocm-opencl-runtime # should show 7.1.1 +grep IgnorePkg /etc/pacman.conf # should pin the rocm packages + +# 3. Verify the launcher pins to AMD (NOT DRI_PRIME=1) +grep -E '^export (DRI_PRIME|HSA_OVERRIDE|ROCR_VISIBLE)' ~/.local/bin/davinci-resolve +# DRI_PRIME must be a pci-... tag, never =1 + +# 4. Re-launch +~/.local/bin/davinci-resolve +``` + +Crash markers the installer recognises (any of these in `~/.local/share/DaVinciResolve/logs/ResolveDebug.txt` triggers the auto-wipe on next install run): +- `Unsupported GPU Processing Mode` — original ROCm 7.2 symptom (no OpenCL device visible) +- `OpenCL Context Manager failed to create context` — DRI_PRIME=1 flip / CL-GL interop failure +- `Failed to create OpenCL context` — DVIP precursor of the above + +### Hyprland config errors after edit + +```bash +hyprctl reload +hyprctl configerrors # should be empty +``` +If errors mention `windowrulev2`, replace with `windowrule = ACTION, match:KEY VALUE`. Examples in `~/.local/share/omarchy/default/hypr/`. + +### Desktop entry broken (app menu does nothing) + +Check for the literal placeholder: +```bash +grep RESOLVE_INSTALL_LOCATION /usr/share/applications/DaVinciResolve.desktop +``` +If found, fix: +```bash +sudo sed -i \ + -e "s|^Exec=.*|Exec=$HOME/.local/bin/davinci-resolve %U|" \ + -e "s|RESOLVE_INSTALL_LOCATION|/opt/resolve|g" \ + /usr/share/applications/DaVinciResolve.desktop +sudo update-desktop-database +``` + +### Audio renders won't start + +snd-aloop must be loaded: +```bash +lsmod | grep snd_aloop # should show "snd_aloop" +sudo modprobe snd-aloop # if missing +``` + +PipeWire bridge must be active: +```bash +ls ~/.config/pipewire/pipewire.conf.d/50-resolve-aloop-bridge.conf +ls ~/.config/wireplumber/wireplumber.conf.d/51-resolve-aloop-no-default.conf +systemctl --user restart wireplumber pipewire pipewire-pulse +``` + +## Card compatibility — confidence tiers + +The script auto-detects gfx targets and applies the right HSA override for every card listed below. Confidence levels reflect how well-tested each tier is, not whether the script logic handles them. + +### ✅ Confirmed working (tested directly) + +| Card | gfx target | Auto HSA value | +|------|-----------|----------------| +| **RX 9060 / 9060 XT** (Navi 44) | gfx1200 | `12.0.0` | + +This is the only card where I've personally seen Resolve launch all the way to the Color page with this exact stack. + +### 🟢 Should work — natively supported by ROCm 7.1.1 + +| Card | gfx target | Auto HSA value | +|------|-----------|----------------| +| **RX 9070 / 9070 XT** (Navi 48) | gfx1201 | `12.0.1` | +| **RX 7900 / 7900 XT / 7900 XTX** (Navi 31) | gfx1100 | `11.0.0` | +| **RX 7700 XT / 7800 XT** (Navi 32) | gfx1101 | `11.0.1` | +| **RX 6800 / 6800 XT / 6900 XT / 6950 XT** (Navi 21) | gfx1030 | `10.3.0` | + +The 7900 XTX has the most community success reports. CachyOS thread has a confirmed working 7800 XT with the same ROCm 7.1.1 pinned stack. + +### 🟡 Should work via HSA spoof — not natively supported, spoofs to nearest target + +| Card | Real gfx target | Spoofs as | Auto HSA value | +|------|-----------------|-----------|----------------| +| **RX 7600 / 7600 XT** (Navi 33) | gfx1102 | gfx1100 | `11.0.0` | +| **RX 6700 / 6750 XT** (Navi 22) | gfx1031 | gfx1030 | `10.3.0` | +| **RX 6600 / 6650 XT** (Navi 23) | gfx1032 | gfx1030 | `10.3.0` | +| **RX 6500 XT / 6400** (Navi 24) | gfx1034 | gfx1030 | `10.3.0` | + +**Caveat**: RX 6700 XT has been a problem child historically (see [Arch BBS#284316](https://bbs.archlinux.org/viewtopic.php?id=284316) — one user gave up and switched to NVIDIA). Spoofing trades correctness for not-quite-optimal codegen; some effects may fall back to CPU. + +### 🟢 iGPUs + +| Chip | gfx target | Notes | +|------|-----------|-------| +| Ryzen 7 8000 / 9000 (Phoenix/Hawk Point/Strix) | RDNA3.5 (gfx1150-ish) | CachyOS thread had a 760M (gfx1103) hit the same ROCm 7.2 bug; same downgrade fixes it. | +| Strix Halo | gfx1151 | Confirmed working via Distrobox per Framework Community Forum. | + +### 🔴 Will NOT work — ROCm 7.x dropped support before this script's pinned baseline + +- **RX 5000 series** (RDNA1, gfx101x) +- **Vega** (gfx9) +- **Polaris** (RX 580 etc.) — only Mesa rusticl path, this script isn't tuned for that +- **Pre-Polaris GCN** + +### What "should work" actually means + +For tiers 🟢 and 🟡: the **install path is fully baked in** — same package list, RPATH, audio fixes, and launcher env vars (HSA value auto-matched). What might differ between cards: + +- **First-launch latency** — Color page can take 60s+ while shaders compile +- **Performance ceiling** — spoofed cards (gfx1102, gfx103x) trade correctness for slightly suboptimal codegen +- **Specific features** — Neural Engine, Fairlight noise reduction may fall back to CPU on some cards + +### Sharing the script with someone on a different AMD card + +What they need: +1. Run `~/Downloads/install-davinci-resolve.sh` — auto-detects everything +2. If launch fails, `verify_opencl()` at the end of the script tells them what's wrong +3. This notes file has all the diagnostic commands + +The one thing they'd need that you don't: the **DaVinci Resolve ZIP in `~/Downloads`** (Blackmagic gates it behind a registration form, no direct link). + +## Quick reference: HSA override values + +For copy/paste if hand-editing the launcher: + +| Card family | gfx target | HSA_OVERRIDE_GFX_VERSION | +|-------------|-----------|--------------------------| +| RX 9070 / 9070 XT (Navi 48) | gfx1201 | 12.0.1 | +| RX 9060 / 9060 XT (Navi 44) | gfx1200 | 12.0.0 | +| RX 7700 / 7800 (Navi 32) | gfx1101 | 11.0.1 | +| RX 7900 (Navi 31) | gfx1100 | 11.0.0 | +| RX 7600 (Navi 33) | gfx1102 | 11.0.0 (spoof) | +| RX 6800 / 6900 (Navi 21) | gfx1030 | 10.3.0 | +| RX 6600 / 6700 (Navi 22-24) | gfx1031/1032/1034 | 10.3.0 (spoof) | + +## Sources / further reading + +- [ROCm/ROCm#5982 — 7.2 OpenCL Breaks Davinci Resolve](https://github.com/ROCm/ROCm/issues/5982) +- [CachyOS — Davinci Resolve + AMD ROCM fails without downgrade](https://discuss.cachyos.org/t/davinci-resolve-amd-rocm-fails-without-downgrade/28036) +- [CachyOS — Davinci Resolve + AMD GPU FAQ](https://discuss.cachyos.org/t/davinci-resolve-amd-gpu/28038) +- [NixOS#481483 — Resolve 20.3.1 OpenCL unusable on RDNA 4](https://github.com/NixOS/nixpkgs/issues/481483) +- [DaVinci Resolve — ArchWiki](https://wiki.archlinux.org/title/DaVinci_Resolve) +- [Sheridan Computers — libc++ rollback fix (Oct 2025)](https://sheridancomputers.com/blog/view/videos/fix-davinci-resolve-crashing-on-arch-linux-after-system-update-october-2025) +- [Arch Linux Archive (ALA) — historic packages](https://archive.archlinux.org/packages/) + +## Things still left as future work + +- **Resolve 20.3.x stable** — currently on 21.0 beta. Stable might handle the GPU stack differently. +- **Distrobox + Debian 13** path — community-confirmed working on Strix Halo (gfx1151). Would isolate Resolve from the host's library churn entirely. Useful as a fallback. +- **libc++ ABI break** — Resolve's bundled libc++ vs Arch's system libc++ has bitten the community before (Oct 2025). When Arch jumps libc++ major versions, Resolve may segfault on launch. Sheridan Computers documented the rollback approach. + +## Quick sanity checks (paste into terminal anytime) + +```bash +# Is Resolve install intact? +test -x /opt/resolve/bin/resolve && echo "Resolve binary OK" || echo "MISSING" + +# Is the OpenCL stack the working version? +pacman -Q rocm-core rocm-opencl-runtime comgr spirv-llvm-translator | grep -E "7\.1\.1|21\.1\.3" | wc -l +# expect output: 4 + +# Is the IgnorePkg pin in place? +grep -q "^IgnorePkg.*rocm-core" /etc/pacman.conf && echo "PINNED" || echo "NOT PINNED" + +# Does clinfo see the GPU? +clinfo -l | grep -i gfx1200 + +# Does the launcher have the env vars? +grep -E "HSA_OVERRIDE|ROCR_VISIBLE|DRI_PRIME|MESA_VK" ~/.local/bin/davinci-resolve +# DRI_PRIME line MUST be the explicit PCI tag form (pci-0000_BB_DD_F), +# NEVER `=1`. If you see `=1`, fix it before launching: +# amd_bus=$(lspci -nn | grep -iE 'AMD.*Radeon|Navi' | head -1 | cut -d' ' -f1) +# sed -i "s|^export DRI_PRIME=.*|export DRI_PRIME=pci-0000_${amd_bus//[:.]/_}|" ~/.local/bin/davinci-resolve +``` diff --git a/README.md b/README.md index 9ca6238..507f83a 100644 --- a/README.md +++ b/README.md @@ -8,195 +8,154 @@

-Install [DaVinci Resolve](https://www.blackmagicdesign.com/products/davinciresolve) on [Omarchy](https://omarchy.com) (Arch Linux + Hyprland) with AMD GPU support, including the new RX 9000 series (RDNA 4). +Install [DaVinci Resolve](https://www.blackmagicdesign.com/products/davinciresolve) on [Omarchy](https://omarchy.com) (Arch Linux + Hyprland) on AMD GPUs — RDNA 2, RDNA 3, and the new RDNA 4 cards (RX 9060 / 9070 / 9080). -Features a pre-installation system scanner that detects your GPU, drivers, OpenCL stack, display server, and audio — then builds a tailored package list for your exact setup. +The script does a **manual install to `/opt/resolve`** (no AUR PKGBUILD), pins **ROCm 7.1.1** from the Arch Linux Archive (current 7.2.x is broken with Resolve), patches every ELF's RPATH, fixes audio (DeckLink → ALSA + snd-aloop + PipeWire bridge), and writes a launcher with the right `HSA_OVERRIDE` / `DRI_PRIME` / `MESA_VK_DEVICE_SELECT` env vars baked in. + +For the full design rationale, root-cause analysis, and recovery procedures see **[NOTES.md](NOTES.md)**. ## Requirements - **OS**: [Omarchy](https://omarchy.com) (Arch Linux) or any Arch-based distro -- **GPU**: AMD (RDNA 1 through RDNA 4, including RX 9070/9080) -- **AUR Helper**: yay or paru -- **Compositor**: Hyprland (optional, but script adds window rules if detected) +- **GPU**: AMD RDNA 2 / 3 / 4 (see [GPU support](#gpu-support) below) +- **Compositor**: Hyprland (optional — script adds window rules if detected) +- **DaVinci Resolve ZIP** in `~/Downloads/` — Blackmagic gates this behind a registration form, no direct link ## Quick Start ```bash +# 1. Download DaVinci Resolve from blackmagicdesign.com into ~/Downloads/ +# 2. Clone and run: git clone https://git.no-signal.uk/nosignal/DaVinci-Resolve-AMD-Omarchy.git cd DaVinci-Resolve-AMD-Omarchy chmod +x install-davinci-resolve.sh ./install-davinci-resolve.sh ``` -The script will scan your system, show what it found, and ask before making any changes. +The script scans your system, shows what it found, asks before making changes, and is idempotent — safe to re-run any time. -## What It Does +## Why ROCm 7.1.1 (and not the current 7.2.x) -### 1. System Scan +ROCm 7.2.x's OpenCL stack breaks DaVinci Resolve on **every** AMD GPU — `clCreateContext` either fails outright or hangs on the Color page. ROCm 7.1.1 is the last known-good release. -Before installing anything, the script scans your system to detect: +The script downloads 7.1.1 from the [Arch Linux Archive](https://archive.archlinux.org/packages/), installs via `pacman -U`, and adds an `IgnorePkg` line to `/etc/pacman.conf` so `pacman -Syu` won't quietly upgrade you back into the broken stack. -| Component | What It Checks | -|-----------|---------------| -| **Kernel** | Version, RDNA 4 compatibility (needs 6.12+) | -| **GPU(s)** | Vendor, model, RDNA generation, discrete vs integrated | -| **Hybrid GPU** | Intel/AMD iGPU + discrete GPU detection, PRIME offload support | -| **Mesa** | Version, OpenGL renderer, Vulkan availability | -| **OpenCL** | Installed provider, whether AMD platform is actually working | -| **Audio** | PipeWire or PulseAudio detection | -| **Display** | Wayland/X11, compositor (Hyprland, Sway, GNOME, KDE) | -| **Resolve** | Existing installation detection | -| **Disk space** | Free space on root and home partitions | +References: +- [ROCm/ROCm#5982](https://github.com/ROCm/ROCm/issues/5982) — upstream issue, still open +- [CachyOS thread](https://discuss.cachyos.org/t/davinci-resolve-amd-rocm-fails-without-downgrade/28036) — community recipe +- [NixOS#481483](https://github.com/NixOS/nixpkgs/issues/481483) — RDNA4-specific report -### 2. Smart Package Selection +When ROCm 7.3+ ships and is verified working, the pin can be lifted — see NOTES.md. -Based on the scan, it builds a package list that: -- Skips packages already installed -- Selects the right OpenCL provider for your GPU -- Removes conflicting packages (e.g. rusticl breaks ROCm) -- Adds hybrid GPU support if needed +## What it does -### 3. OpenCL Provider +1. **System scan** — kernel, GPU(s) with gfx target + PCI bus address, audio stack, display server, existing Resolve install +2. **ROCm 7.1.1 pinned install** — downloads from Arch Linux Archive, `pacman -U`, adds `IgnorePkg` to `/etc/pacman.conf`. Aborts on partial-downgrade failure (won't silently leave you on a broken stack) +3. **Manual Resolve install** — `unzip` → `.run` AppImage → `--appimage-extract` → `rsync` to `/opt/resolve` → `patchelf --set-rpath` every ELF +4. **glib-family symlink swap** — replaces all five bundled glib libs (`libglib-2.0`, `libgio-2.0`, `libgmodule-2.0`, `libgobject-2.0`, `libgthread-2.0`) with system symlinks. Bundled `libc++` / `libc++abi` are deliberately **kept** (Resolve was compiled against specific ABI versions; replacing them crashes on launch) +5. **Audio fixes** — DeckLink → ALSA backend, snd-aloop kernel module, PipeWire loopback bridge, wireplumber rule keeping aloop off the default-sink rotation +6. **Hyprland window rules** — v3 syntax (not deprecated `windowrulev2`), cosmetic-only on the modal dialogs to avoid the trapped-cursor problem +7. **Launcher** at `~/.local/bin/davinci-resolve` with `HSA_OVERRIDE_GFX_VERSION`, `ROCR_VISIBLE_DEVICES`, `DRI_PRIME=pci-` (explicit tag, not `=1` — see below), and `MESA_VK_DEVICE_SELECT` baked in +8. **Stale-config wipe** — clears `~/.local/share/DaVinciResolve/{configs,logs}` if it sees a known crash marker, and unconditionally on fresh installs -Two options (configurable in the script): +## Hybrid GPU note (Intel iGPU + AMD dGPU) -| Provider | Source | Best For | -|----------|--------|----------| -| `opencl-amd` (default) | AUR | Recommended by Arch Wiki for Resolve | -| `rocm-full` | Official repos | Full ROCm/HIP stack, better RDNA 4 support | +On hybrid laptops where the monitor is wired to the AMD discrete card, Mesa already defaults OpenGL to the AMD card. Setting `DRI_PRIME=1` in this scenario actually flips OpenGL to the **Intel** iGPU — Resolve then has OpenGL on Intel and OpenCL on AMD, CL/GL interop fails, and Resolve hangs on the Color page. -### 4. Installs DaVinci Resolve +The launcher uses the **explicit PCI tag form** (`DRI_PRIME=pci-0000_BB_DD_F`) derived from the AMD card's `lspci` ID. `switcherooctl` is **not** used — it internally sets `DRI_PRIME=1` and inherits the same flip bug. -Installs via the AUR package (`davinci-resolve` or `davinci-resolve-studio`). +## GPU support -### 5. Applies Compatibility Fixes +The script auto-detects the gfx target and applies the right HSA override unattended. -| Fix | Method | Why | -|-----|--------|-----| -| **GLib conflict** | patchelf (davincibox-style) | Cleaner than LD_PRELOAD, survives updates | -| **OpenCL decoders** | Disable BlackmagicRaw OpenCL | Prevents crashes with AMD OpenCL | -| **Rusticl removal** | Removes conflicting packages | Mesa's rusticl breaks ROCm OpenCL | -| **XWayland wrapper** | Custom launcher script | Resolve doesn't support native Wayland | -| **RDNA 4 env vars** | HSA_OVERRIDE_GFX_VERSION | Tells ROCm how to talk to gfx1201 GPUs | +### Confirmed working (tested directly) -### 6. Creates Launcher +| Card | gfx target | HSA value | +|------|-----------|-----------| +| **RX 9060 / 9060 XT** (Navi 44) | gfx1200 | `12.0.0` | -A wrapper script at `~/.local/bin/davinci-resolve` that: -- Forces XWayland mode (`QT_QPA_PLATFORM=xcb`) -- Sets ROCm/OpenCL environment variables -- Handles DRI_PRIME for hybrid GPU setups -- Sets RDNA 4 GFX version override if needed +### Should work — natively supported by ROCm 7.1.1 -### 7. Configures Hyprland +| Card | gfx target | HSA value | +|------|-----------|-----------| +| **RX 9070 / 9070 XT** (Navi 48) | gfx1201 | `12.0.1` | +| **RX 7900 / 7900 XT / 7900 XTX** (Navi 31) | gfx1100 | `11.0.0` | +| **RX 7700 XT / 7800 XT** (Navi 32) | gfx1101 | `11.0.1` | +| **RX 6800 / 6800 XT / 6900 XT / 6950 XT** (Navi 21) | gfx1030 | `10.3.0` | -Adds window rules to `hyprland.conf` for Resolve's floating dialogs (file pickers, render settings, etc.) so they behave correctly under Hyprland. +### Should work via HSA spoof — not natively supported, spoofed to nearest target -### 8. Bonus Tools +| Card | Real gfx | Spoofs as | HSA value | +|------|----------|-----------|-----------| +| **RX 7600 / 7600 XT** (Navi 33) | gfx1102 | gfx1100 | `11.0.0` | +| **RX 6700 / 6750 XT** (Navi 22) | gfx1031 | gfx1030 | `10.3.0` | +| **RX 6600 / 6650 XT** (Navi 23) | gfx1032 | gfx1030 | `10.3.0` | +| **RX 6500 XT / 6400** (Navi 24) | gfx1034 | gfx1030 | `10.3.0` | -- **resolve-convert** — Converts MP4/MKV to DNxHR format (DaVinci Resolve Free on Linux doesn't support H.264/H.265) -- **davinci-resolve-checker** — Community diagnostic tool for troubleshooting Resolve issues +### Not supported — ROCm 7.x dropped these before our pinned baseline -## Supported AMD GPUs +- **RX 5000 series** (RDNA 1) +- **Vega** +- **Polaris and earlier** (rusticl-only path, this script isn't tuned for it) -| Generation | GPUs | Notes | -|-----------|------|-------| -| **RDNA 4** | RX 9070, 9080 | Needs kernel 6.12+, Mesa 25+ | -| **RDNA 3** | RX 7600–7900 | Full support | -| **RDNA 2** | RX 6400–6950 | Full support | -| **RDNA 1** | RX 5500–5700 | Full support | -| **Vega** | Vega 56/64 | Supported | -| **GCN** | Older cards | May work, not tested | +## Free-version codec limitation -## Hybrid GPU Support - -The script detects and handles these configurations: - -- **Intel iGPU + AMD dGPU** — Configures DRI_PRIME for discrete GPU -- **AMD iGPU + AMD dGPU** — Auto-selects discrete GPU -- **Intel iGPU + NVIDIA dGPU** — Advises on PRIME render offload -- **AMD iGPU + NVIDIA dGPU** — Advises on PRIME render offload - -## Configuration - -### Changing OpenCL Provider - -Edit the script and change `OPENCL_PROVIDER`: +DaVinci Resolve Free on Linux can't decode H.264/H.265. Convert media first: ```bash -# In install-davinci-resolve.sh: -OPENCL_PROVIDER="opencl-amd" # AUR package (default) -OPENCL_PROVIDER="rocm-full" # Official ROCm stack +resolve-convert video.mp4 # writes video_dnxhr.mov (DNxHR) ``` -### Free Version Codec Limitation +## Environment variables -DaVinci Resolve Free on Linux cannot decode H.264/H.265. Convert your media first: +| Var | Effect | +|-----|--------| +| `RESOLVE_NO_ALOOP=1` | Skip the snd-aloop / PipeWire bridge audio setup (use this if you have a real audio interface) | + +## Uninstall + +The script does **not** track itself with pacman. To remove cleanly: ```bash -resolve-convert video.mp4 -# Creates video_dnxhr.mov (DNxHR format that Resolve Free supports) +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 +rm -f ~/.local/bin/davinci-resolve ~/.local/bin/davinci-resolve-rusticl ~/.local/bin/davinci-resolve-igpu +rm -f ~/.local/bin/resolve-convert ~/.local/bin/davinci-resolve-checker + +# Optional — also remove user data (DELETES ALL PROJECTS) +rm -rf ~/.local/share/DaVinciResolve +``` + +To lift the ROCm pin: + +```bash +sudo sed -i '/^IgnorePkg.*rocm-/d' /etc/pacman.conf +sudo pacman -Syu ``` ## Troubleshooting -### GPU not detected in Resolve +If first launch fails or Resolve hangs on the Color page, **[NOTES.md](NOTES.md)** has the full recovery playbook — quick sanity checks at the bottom let you verify the OpenCL stack, launcher env vars, and PipeWire bridge in a few lines of paste. + +Common quick checks: ```bash -# Check OpenCL -clinfo --list - -# Check ROCm -rocminfo - -# Run diagnostics -davinci-resolve-checker -``` - -### RDNA 4 specific issues - -If Resolve doesn't detect your RX 9070/9080, try adjusting the GFX version in the launcher: - -```bash -# Edit ~/.local/bin/davinci-resolve -# Change HSA_OVERRIDE_GFX_VERSION value: -# 11.0.1 (default for gfx1201) -# 11.0.0 (fallback to gfx1100) -``` - -### Resolve crashes on launch - -- Check patchelf fix is applied: `patchelf --print-needed /opt/resolve/bin/resolve | grep glib` -- Re-run the script to reapply fixes -- Check logs: `~/.local/share/DaVinciResolve/logs/` - -### Kernel 6.14/6.15 ROCm issues - -Kernels 6.14 and 6.15 have known DKMS issues with ROCm. Consider using kernel 6.12 or 6.13. - -## Uninstalling - -```bash -# Remove Resolve (AUR package) -yay -Rns davinci-resolve - -# Remove launcher and tools -rm -f ~/.local/bin/davinci-resolve -rm -f ~/.local/bin/resolve-convert -rm -f ~/.local/bin/davinci-resolve-checker -rm -rf ~/.local/share/davinci-resolve-checker - -# Remove user data (WARNING: deletes all projects) -rm -rf ~/.local/share/DaVinciResolve +clinfo -l # should show your AMD gfx target +pacman -Q rocm-core rocm-opencl-runtime # should show 7.1.1 +grep IgnorePkg /etc/pacman.conf # should pin the rocm packages +grep -E 'DRI_PRIME|HSA_OVERRIDE' ~/.local/bin/davinci-resolve # DRI_PRIME must be pci-... form, NEVER =1 ``` ## Credits -- [Omarchy](https://omarchy.com) - The Arch Linux distribution this was built for -- [davincibox](https://github.com/zelikos/davincibox) - Patchelf fix technique -- [Arch Wiki - DaVinci Resolve](https://wiki.archlinux.org/title/DaVinci_Resolve) - Community documentation -- [Blackmagic Design](https://www.blackmagicdesign.com/) - DaVinci Resolve -- [davinci-resolve-checker](https://github.com/Ashark/davinci-resolve-checker) - Diagnostic tool +- [Omarchy](https://omarchy.com) — the Arch + Hyprland distribution this targets +- [davincibox](https://github.com/zelikos/davincibox) — patchelf RPATH technique +- [Arch Wiki — DaVinci Resolve](https://wiki.archlinux.org/title/DaVinci_Resolve) — community documentation +- [Sheridan Computers](https://sheridancomputers.com/blog/view/videos/fix-davinci-resolve-crashing-on-arch-linux-after-system-update-october-2025) — libc++ ABI rollback writeup +- [Blackmagic Design](https://www.blackmagicdesign.com/) — DaVinci Resolve +- [davinci-resolve-checker](https://github.com/Ashark/davinci-resolve-checker) — diagnostic tool ## License -This project is provided as-is for the Omarchy community. +Provided as-is for the Omarchy community. diff --git a/install-davinci-resolve.sh b/install-davinci-resolve.sh index 1d2bdb6..ea6a2f9 100755 --- a/install-davinci-resolve.sh +++ b/install-davinci-resolve.sh @@ -1,24 +1,38 @@ #!/bin/bash # ============================================================================= -# DaVinci Resolve Free - Arch Linux + AMD RX 9000 + Hyprland Install Script +# DaVinci Resolve Free - Arch Linux + AMD (RDNA2/3/4) + Hyprland Install Script # ============================================================================= # -# Version 3.0 - Davincibox-style fixes for better compatibility -# Validated against Arch Wiki, AUR, davincibox, and community docs (Dec 2025) +# 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. # -# 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) +# 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 # @@ -26,6 +40,9 @@ # 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 @@ -52,14 +69,22 @@ 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" +# 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) @@ -86,8 +111,12 @@ 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 @@ -156,7 +185,7 @@ scan_gpu() { 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 @@ -165,28 +194,44 @@ scan_gpu() { 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 + + # 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" @@ -197,7 +242,7 @@ scan_gpu() { 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" @@ -217,45 +262,70 @@ scan_gpu() { 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" + + # 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 for AMD GPUs + + # 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 @@ -263,7 +333,7 @@ scan_gpu() { 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 @@ -273,14 +343,16 @@ scan_gpu() { 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 @@ -298,7 +370,7 @@ scan_gpu() { 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 @@ -316,12 +388,12 @@ 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 @@ -374,7 +446,7 @@ scan_audio() { SCAN_PULSEAUDIO_INSTALLED=true fi fi - + # Double-check with package detection if pacman -Qq pipewire &>/dev/null; then SCAN_PIPEWIRE_INSTALLED=true @@ -382,7 +454,7 @@ scan_audio() { 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 @@ -407,7 +479,7 @@ scan_display() { else SCAN_DISPLAY_SERVER="None/TTY" fi - + # Detect compositor if [[ -n "$HYPRLAND_INSTANCE_SIGNATURE" ]]; then SCAN_COMPOSITOR="Hyprland" @@ -424,7 +496,7 @@ scan_display() { 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 @@ -436,13 +508,13 @@ scan_resolve() { 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 @@ -484,50 +556,50 @@ is_pkg_installed() { 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 "" } @@ -537,14 +609,14 @@ run_system_scan() { 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 @@ -552,25 +624,35 @@ display_scan_results() { 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}" @@ -578,18 +660,18 @@ display_scan_results() { 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 @@ -598,17 +680,17 @@ display_scan_results() { 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 @@ -618,16 +700,16 @@ display_scan_results() { 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 "" } @@ -637,97 +719,103 @@ display_scan_results() { 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=$((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=$((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, launch with: DRI_PRIME=1 davinci-resolve" - ((warnings++)) + 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=1 davinci-resolve" + 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=$((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=$((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=$((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=$((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=$((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=$((warnings+1)) else echo -e "${YELLOW}[WARNING]${NC} GPU vendor not recognized. This script is optimized for AMD GPUs." - ((warnings++)) + 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=$((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=$((warnings+1)) fi # RDNA4 info @@ -744,31 +832,31 @@ analyze_scan_results() { 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=$((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=$((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=$((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=$((warnings+1)) fi - + # Summary echo "" if [[ $issues -eq 0 && $warnings -eq 0 ]]; then @@ -779,7 +867,7 @@ analyze_scan_results() { 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 } @@ -790,255 +878,98 @@ analyze_scan_results() { 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" + # ---- 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 - 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 + # ---- Audio (smart-pick based on detected server) ---- + local audio=() 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 + info "PipeWire detected - installing pipewire-pulse + pipewire-alsa" + audio=(pipewire-pulse pipewire-alsa) 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 + info "PulseAudio detected - installing pulseaudio-alsa" + audio=(pulseaudio-alsa) fi - - # Diagnostic tools - local diag_tools=( - "clinfo" - "mesa-utils" - "expac" - "python-distro" - "unzip" - ) - - for pkg in "${diag_tools[@]}"; do + + # ---- 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 - - # 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 @@ -1046,7 +977,7 @@ build_package_list() { else echo " (none needed)" fi - + echo "" echo -e "${BOLD}Packages to install from AUR:${NC}" if [[ ${#AUR_PACKAGES[@]} -gt 0 ]]; then @@ -1054,7 +985,7 @@ build_package_list() { else echo " (none needed)" fi - + echo "" echo -e "${BOLD}Packages already installed (skipping):${NC}" if [[ ${#SKIP_PACKAGES[@]} -gt 0 ]]; then @@ -1062,7 +993,7 @@ build_package_list() { else echo " (none)" fi - + echo "" } @@ -1108,9 +1039,9 @@ install_aur_packages() { 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 @@ -1130,66 +1061,153 @@ install_aur_packages() { 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 - + + 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. Skipping installation." + info "DaVinci Resolve already installed at /opt/resolve." info "Will apply workarounds and configuration updates." return 0 fi - + header "Installing DaVinci Resolve" - - # Find installer (handle filenames with spaces properly) + + # ---- Locate the ZIP installer ---- 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) - + 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) - - # 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 " 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 @@ -1198,10 +1216,8 @@ install_davinci_resolve() { 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) @@ -1209,175 +1225,233 @@ install_davinci_resolve() { 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) + + # ---- 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" - info "Building in $build_dir..." - - # Cleanup function for build directory - cleanup_build() { + cleanup_resolve_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 + 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 - info "Building package (this takes several minutes)..." - if [[ "$version_updated" == true ]]; then - makepkg -si --noconfirm --skipchecksums - else - makepkg -si --noconfirm - 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" - # Cleanup - cleanup_build - trap - EXIT - - success "DaVinci Resolve installed." -} + chmod -R u+rwX,go+rX,go-w "$appdir" 2>/dev/null || warn "Could not normalize all permissions" -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" + # ---- 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" ) - - # 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 + 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 - # 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++)) + # 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 - 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 + popd >/dev/null 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 + # 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 - info "Moving bundled glib libraries (fallback method)..." - sudo mkdir -p "$resolve_libs/disabled-libraries" + # ---- 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 - # 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) + 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 - success "Glib fix (fallback) applied." + 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() { @@ -1412,6 +1486,160 @@ disable_opencl_decoders() { 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. 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 @@ -1453,38 +1681,135 @@ configure_user_groups() { 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" +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 udev_rule_file="/etc/udev/rules.d/75-davinci-resolve-dongle.rules" - local udev_rule='SUBSYSTEM=="usb", ATTR{idVendor}=="096e", MODE="0666"' + local resolve_user_dir="${HOME}/.local/share/DaVinciResolve" + local prior_crash=0 - # 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" + # 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 - 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 + # 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 "DaVinci Resolve Free detected - USB dongle rule not needed." + 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 "DaVinci Resolve not yet installed - skipping USB dongle setup." + 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 } @@ -1494,84 +1819,124 @@ create_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 + # 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_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 + 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=' -# 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=' + 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 fi - # ROCm environment configuration + # 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-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" + 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 -# Enable RDNA4 support (gfx1201) -export HSA_OVERRIDE_GFX_VERSION=11.0.1 + 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' +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 + # 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 @@ -1583,7 +1948,6 @@ ulimit -n 65535 2>/dev/null exec /opt/resolve/bin/resolve "\$@" EOF - fi chmod +x "$launcher_dir/davinci-resolve" @@ -1625,32 +1989,49 @@ 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 + + # 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." @@ -1661,15 +2042,12 @@ EOF 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 "" + if [[ "$SCAN_HAS_AMD_DGPU" == true ]]; then + info "Launcher locked to AMD discrete GPU via PCI tag (DRI_PRIME=${amd_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 "" @@ -1682,50 +2060,59 @@ configure_hyprland() { 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 "Add these rules manually (Hyprland 0.53+ syntax):" echo "" cat << 'EOF' -windowrulev2 = tag +drpopup, class:^(resolve)$, floating:1 -windowrulev2 = stayfocused, tag:drpopup -windowrulev2 = noborder, tag:drpopup -windowrulev2 = opaque, tag:drpopup +# 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 -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 +# 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" + 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) @@ -1753,20 +2140,20 @@ EOF 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" } @@ -1779,33 +2166,57 @@ print_summary() { 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" + 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 " 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 " 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 [[ "$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)" + 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 "============================================================" @@ -1818,9 +2229,10 @@ print_summary() { 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 " 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 "" @@ -1849,21 +2261,52 @@ main() { echo [[ $REPLY =~ ^[Nn]$ ]] && exit 0 + # Full system upgrade first - Arch is a rolling distro and partial upgrades + # (pacman -Sy 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 - apply_patchelf_fix disable_opencl_decoders - setup_studio_usb_dongle + 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 }