Files
runi-dist/docs/customize-profiles.md

8.4 KiB

Customize profiles

Community workarounds for unofficial/leaked upscaler builds (new FSR versions, DLSS/XeSS swaps, and similar) recur — a "customize profile" is a named, reusable recipe for one of these, so applying (and cleanly reverting) one doesn't mean hand-editing files each time.

Validated for real: FSR 4.1.1 INT8 on non-RDNA4 hardware, applied to a real game via OptiScaler, checksum-verified to reproduce a manually-confirmed working in-game result exactly. The equivalent non-OptiScaler ("native") path was also investigated for real, in depth, and does not work on this hardware/Proton build — see "Follow-up investigation" below for the full root cause (it's not just a per-game quirk: a specific vkd3d-proton compile-time flag is missing). OptiScaler is the correct tool for forcing an upscaler backend/version a game wouldn't otherwise offer, not merely a fallback.

Where profiles live

~/.config/runi/profiles/<name>.toml. runi customize apply <id> <name> looks there by name; you can also pass a direct file path instead of a name for a one-off profile you don't want to save centrally.

Format

# ~/.config/runi/profiles/fsr411-workaround.toml
[profile]
name = "fsr411-workaround"
description = "Community FSR 4.1.1 INT8 workaround for non-RDNA4 GPUs (via OptiScaler)"

[[files]]
src = "/home/user/Downloads/FidelityFX-Samples-v2.3.0-prebuilt/Samples/Upscalers/FidelityFX_FSR/dx12/x64/Release/amd_fidelityfx_upscaler_dx12.dll"
dest = "amd_fidelityfx_upscaler_dx12.dll"   # relative to the game's exe directory

[[ini_edits]]
file = "OptiScaler.ini"          # relative to the game dir; errors if missing
[ini_edits.set]
Fsr4Update = "true"

[env]
FSR4_UPGRADE = "1"                # only matters on a non-OptiScaler path; harmless otherwise
  • [[files]] — each entry copies src (a local path; leaked/unofficial builds have no stable official URL to automate) to dest, relative to the game's own exe directory. Goes through the same core/backup.py safety net every other game-file write in Runi uses — a colliding existing file is backed up first, never silently overwritten.
  • [[ini_edits]] — patches specific key=value lines in a target ini file (also relative to the game dir), leaving every other line/comment untouched. The target file must already exist (e.g. OptiScaler must already be installed) — this errors rather than silently skipping, since that usually means a prerequisite step was missed.
  • [env] — merged into the game's own [env] (overwrites, since applying a profile is a deliberate action). Only the keys a profile sets are removed again on customize remove — other env vars you've set yourself (e.g. WINEDLLOVERRIDES from an OptiScaler install) are left alone.

A profile that only needs the non-OptiScaler path (relying on a game's own native upscaler support rather than OptiScaler's proxy) simply omits [[ini_edits]] entirely — there's no conditional branching inside one profile; write two profiles if you want both paths available.

The non-OptiScaler (native) path is not universally reliable

Tried for real on Gothic 1 Remake, immediately after the OptiScaler path above was confirmed working: swapping amd_fidelityfx_upscaler_dx12.dll and setting FSR4_UPGRADE=1 with OptiScaler removed had no effect — the in-game menu still showed the game's original FSR 3.1.4, not 4.1.1. PROTON_LOG=1 (runi run <id> --debug --log) confirmed why: the game never loaded amd_fidelityfx_upscaler_dx12.dll as a module at all, even after several minutes of actual play — strongly suggesting this title's FidelityFX integration is compiled directly into the .exe rather than deployed as the separate, swappable DLL some other games/the reference FidelityFX SDK samples use.

OptiScaler works regardless of how a game is built because it intercepts at the DXGI/D3D12 layer itself (hijacking dxgi.dll) and loads its own upscaler module unconditionally — it doesn't depend on the target game choosing to dynamically load that DLL by name. The non-OptiScaler path only works for games that already do that dynamic loading themselves.

Practical takeaway: prefer the OptiScaler path by default. Before relying on a non-OptiScaler profile for a new game, verify the target DLL actually gets loaded — runi run <id> --debug --log, play for a minute, then check the Proton debug log (~/.local/share/runi/logs/<id>/steam-*.log) for the DLL's filename in a loaddll trace line. If it never appears, the swap won't do anything for that game, no matter what env vars are set.

Follow-up investigation: why the non-OptiScaler path can't work here at all

Pushed further using AMD's own official FidelityFX SDK sample app (FidelityFX_FSR.exe, which ships natively alongside the swappable DLL) as a clean, decoupled test case, fully rendering and running (its media assets had to be fetched separately via its bundled MediaDelivery.exe tool first — the "prebuilt" download didn't include them). Along the way this also surfaced and fixed a real, general bug: Runi wasn't setting the launched process's working directory to the game's own folder (see docs/architecture.md's cwd design point) — the sample couldn't even parse its own bundled config file until that was fixed. That fix is unrelated to FSR specifically and helps any game relying on relative-path resource loading.

With the sample actually running: amd_fidelityfx_upscaler_dx12.dll genuinely loaded, and the GPU was confirmed non-RDNA4 (AMD Strix Halo/RDNA 3.5) — exactly the target hardware for this workaround. But the sample's own "Override FSR Version" UI only offered FSR 2.3/3.1 — no FSR4 entry at all, regardless of the newer DLL being loaded. The downloaded amdxcffx64.dll was never loaded by anything either (checked the debug log directly) — it appears to target a real Windows AMD driver install (replacing a system32 file that only exists there), which doesn't map onto Wine at all.

The actual root cause: vkd3d-proton (the D3D12-to-Vulkan layer Proton uses) already has its own internal FSR4 support and vendor-hacks, but the INT8/ older-GPU emulation path that would make a non-RDNA4 GPU qualify is not enabled by default and requires building vkd3d-proton from source with specific compile flags — it's not a runtime env var or a droppable file, it's a compile-time choice baked into a specific Proton build. The FidelityFX SDK's own hardware capability query correctly (from its perspective) declined to offer FSR4 without that underlying capability.

Conclusion: OptiScaler isn't a workaround-of-last-resort here — it's the actually-correct tool for this job. It intercepts the game's upscaler calls itself and has its own explicit force-override setting (Fsr4Update=true), so it never depends on the game's own auto-detection, the FidelityFX SDK's own UI, or a specific vkd3d-proton build having a particular compile-time flag. Replicating what OptiScaler does without OptiScaler would mean rebuilding OptiScaler itself — not reasonable scope for Runi (same philosophy as wrapping umu-run instead of reimplementing its runtime). Use the OptiScaler path for any game where you need to force an upscaler backend/version the game or SDK wouldn't otherwise offer.

Commands

  • runi customize list [--json] — profiles found in ~/.config/runi/profiles/.
  • runi customize apply <id> <profile-name-or-path> [--json] — applies a profile. If a different profile is already applied to that game, it's fully reverted first (via its own tracked manifest), then the new one is applied — no simultaneous stacking.
  • runi customize remove <id> [--json] — reverts whatever the currently-applied profile changed: restores backed-up files exactly, removes files the profile added, and clears the env vars it set.

How it composes with OptiScaler

Nothing here is OptiScaler-specific — the mechanism is generic (file copies

  • ini edits + env vars against any game directory). It happens that the proven example edits OptiScaler.ini, since that's what the real workaround needed. A profile for the non-OptiScaler path (native FSR4 support some games already ship) would have no [[ini_edits]] at all, just the file copies and FSR4_UPGRADE=1.

See architecture.md (source repository) for the design rationale, and config-schema.md for the [customize] per-game config section this feature adds.