summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rwxr-xr-xdefaults/assets/fgmod.sh104
-rw-r--r--main.py245
-rw-r--r--package.json17
-rw-r--r--src/utils/constants.ts15
5 files changed, 369 insertions, 14 deletions
diff --git a/README.md b/README.md
index 46ccc3d..8ba0918 100644
--- a/README.md
+++ b/README.md
@@ -80,7 +80,7 @@ Dx12Upscaler=fsr31 ~/fgmod/fgmod %command%
## Technical Details
### What's Included
-- **[OptiScaler 0.9.3](https://github.com/optiscaler/OptiScaler/releases/tag/v0.9.3)**: Official upstream OptiScaler bundle used by this plugin, with bundled FSR4 runtime variants for the archive-native RDNA4 path, the Steam Deck / RDNA2-3 optimized INT8 override, or the official 4.1.1 RDNA 3/4 override
+- **[OptiScaler 0.9.3](https://github.com/optiscaler/OptiScaler/releases/tag/v0.9.3)**: Official upstream OptiScaler bundle used by this plugin, with bundled FSR4 runtime variants for the archive-native RDNA4 path, the Steam Deck / RDNA2-3 optimized INT8 override, the official 4.1.1 RDNA 3/4 override, or the experimental Valve 4.1.1 RDNA2 compatibility path with a pre10 OptiScaler injector
- **Nukem9's DLSSG to FSR3 mod**: Allows use of DLSS inputs for FSR frame gen outputs, and xess or FSR upscaling outputs
- **FakeNVAPI**: NVIDIA API emulation for AMD/Intel GPUs, to make DLSS options selectable in game
- **Supporting Libraries**: All required DX12/Vulkan libraries (libxess.dll, amd_fidelityfx, etc.)
diff --git a/defaults/assets/fgmod.sh b/defaults/assets/fgmod.sh
index ba6d8ed..e0ee1d2 100755
--- a/defaults/assets/fgmod.sh
+++ b/defaults/assets/fgmod.sh
@@ -112,6 +112,7 @@ cleanup_files=(
"${proxy_backup_files[@]}"
"OptiScaler.dll"
"amdxcffx64.dll"
+ "amdxc64.dll"
"nvngx.dll"
"_nvngx.dll"
"nvngx-wrapper.dll"
@@ -171,6 +172,7 @@ PY
selected_fsr4_variant="$(resolve_fsr4_variant)"
variant_dir=""
variant_extra_files=()
+variant_ini_overrides=()
case "$selected_fsr4_variant" in
rdna4-native)
variant_dir="$fgmod_path/fsr4-rdna4"
@@ -181,6 +183,13 @@ case "$selected_fsr4_variant" in
fsr4_upscaler_src="$variant_dir/amd_fidelityfx_upscaler_dx12.dll"
variant_extra_files+=("amdxcffx64.dll")
;;
+ rdna2-valve-411-pre10)
+ variant_dir="$fgmod_path/fsr4-rdna2-valve-411-pre10"
+ fsr4_upscaler_src="$variant_dir/amd_fidelityfx_upscaler_dx12.dll"
+ variant_extra_files+=("amdxcffx64.dll" "amdxc64.dll")
+ variant_ini_overrides+=("FSR.Fsr4ForceModel=2")
+ variant_ini_overrides+=("Plugins.LoadCustomAmdxc64OnRdna2=true")
+ ;;
*)
selected_fsr4_variant="rdna23-int8"
variant_dir="$fgmod_path/fsr4-rdna2-3"
@@ -200,7 +209,8 @@ is_managed_support_file() {
"$fgmod_path/amd_fidelityfx_upscaler_dx12.dll" \
"$fgmod_path/fsr4-rdna2-3/amd_fidelityfx_upscaler_dx12.dll" \
"$fgmod_path/fsr4-rdna4/amd_fidelityfx_upscaler_dx12.dll" \
- "$fgmod_path/fsr4-rdna3-4-official-411/amd_fidelityfx_upscaler_dx12.dll"; do
+ "$fgmod_path/fsr4-rdna3-4-official-411/amd_fidelityfx_upscaler_dx12.dll" \
+ "$fgmod_path/fsr4-rdna2-valve-411-pre10/amd_fidelityfx_upscaler_dx12.dll"; do
[[ -f "$candidate" && -f "$existing_file" ]] && cmp -s "$existing_file" "$candidate" && return 0
done
return 1
@@ -208,7 +218,15 @@ is_managed_support_file() {
if [[ "$filename" == "amdxcffx64.dll" ]]; then
for candidate in \
"$fgmod_path/amdxcffx64.dll" \
- "$fgmod_path/fsr4-rdna3-4-official-411/amdxcffx64.dll"; do
+ "$fgmod_path/fsr4-rdna3-4-official-411/amdxcffx64.dll" \
+ "$fgmod_path/fsr4-rdna2-valve-411-pre10/amdxcffx64.dll"; do
+ [[ -f "$candidate" && -f "$existing_file" ]] && cmp -s "$existing_file" "$candidate" && return 0
+ done
+ return 1
+ fi
+ if [[ "$filename" == "amdxc64.dll" ]]; then
+ for candidate in \
+ "$fgmod_path/fsr4-rdna2-valve-411-pre10/amdxc64.dll"; do
[[ -f "$candidate" && -f "$existing_file" ]] && cmp -s "$existing_file" "$candidate" && return 0
done
return 1
@@ -240,7 +258,7 @@ done
unset cleanup_file
# === Optional: Backup Original DLLs ===
-original_dlls=("d3dcompiler_47.dll" "amd_fidelityfx_dx12.dll" "amd_fidelityfx_framegeneration_dx12.dll" "amd_fidelityfx_upscaler_dx12.dll" "amdxcffx64.dll" "amd_fidelityfx_vk.dll")
+original_dlls=("d3dcompiler_47.dll" "amd_fidelityfx_dx12.dll" "amd_fidelityfx_framegeneration_dx12.dll" "amd_fidelityfx_upscaler_dx12.dll" "amdxcffx64.dll" "amdxc64.dll" "amd_fidelityfx_vk.dll")
for dll in "${original_dlls[@]}"; do
existing_path="$exe_folder_path/$dll"
backup_path="$exe_folder_path/$dll.b"
@@ -261,7 +279,13 @@ rm -f "$exe_folder_path/nvapi64.dll" "$exe_folder_path/nvapi64.dll.b"
echo " Cleaned up nvapi64.dll and backup (legacy fakenvapi conflicts)"
# === Core Install ===
-if [[ -f "$fgmod_path/renames/$dll_name" ]]; then
+if [[ -n "$variant_dir" && -f "$variant_dir/renames/$dll_name" ]]; then
+ echo " Using variant pre-renamed $dll_name"
+ cp "$variant_dir/renames/$dll_name" "$exe_folder_path/$dll_name" || error_exit " Failed to copy variant $dll_name"
+elif [[ -n "$variant_dir" && -f "$variant_dir/OptiScaler.dll" ]]; then
+ echo " Using variant OptiScaler injector"
+ cp "$variant_dir/OptiScaler.dll" "$exe_folder_path/$dll_name" || error_exit " Failed to copy variant OptiScaler.dll as $dll_name"
+elif [[ -f "$fgmod_path/renames/$dll_name" ]]; then
echo " Using pre-renamed $dll_name"
cp "$fgmod_path/renames/$dll_name" "$exe_folder_path/$dll_name" || error_exit " Failed to copy $dll_name"
else
@@ -288,6 +312,78 @@ fi
# an external TTF that is not present. Only normalize the default auto value.
sed -i 's/^UseHQFont[[:space:]]*=[[:space:]]*auto$/UseHQFont=false/' "$exe_folder_path/OptiScaler.ini" || true
+if [[ ${#variant_ini_overrides[@]} -gt 0 && -n "$python_bin" ]]; then
+ "$python_bin" - "$exe_folder_path/OptiScaler.ini" "${variant_ini_overrides[@]}" <<'PY'
+import re
+import sys
+from pathlib import Path
+
+path = Path(sys.argv[1])
+overrides = sys.argv[2:]
+content = path.read_text(encoding='utf-8', errors='replace')
+newline = '\r\n' if '\r\n' in content else '\n'
+lines = content.splitlines(keepends=True)
+section_pattern = re.compile(r'^\s*\[(?P<section>[^\]]+)\]\s*$')
+
+def ensure_trailing_newline():
+ if lines and not lines[-1].endswith(('\n', '\r')):
+ lines[-1] += newline
+
+def upsert(section, key, value):
+ replacement = f'{key}={value}'
+ key_pattern = re.compile(rf'^(\s*{re.escape(key)}\s*)=.*$')
+ if section is None:
+ for idx, line in enumerate(lines):
+ if key_pattern.match(line):
+ line_ending = '\r\n' if line.endswith('\r\n') else ('\n' if line.endswith('\n') else newline)
+ lines[idx] = f'{replacement}{line_ending}'
+ return
+ ensure_trailing_newline()
+ lines.append(f'{replacement}{newline}')
+ return
+
+ in_section = False
+ insert_at = None
+ for idx, line in enumerate(lines):
+ match = section_pattern.match(line.strip())
+ if match:
+ if in_section:
+ insert_at = idx
+ break
+ if match.group('section') == section:
+ in_section = True
+ continue
+ if in_section and key_pattern.match(line):
+ line_ending = '\r\n' if line.endswith('\r\n') else ('\n' if line.endswith('\n') else newline)
+ lines[idx] = f'{replacement}{line_ending}'
+ return
+
+ if in_section:
+ if insert_at is None:
+ ensure_trailing_newline()
+ insert_at = len(lines)
+ lines.insert(insert_at, f'{replacement}{newline}')
+ return
+
+ ensure_trailing_newline()
+ if lines and lines[-1].strip():
+ lines.append(newline)
+ lines.append(f'[{section}]{newline}')
+ lines.append(f'{replacement}{newline}')
+
+for override in overrides:
+ key_part, value = override.split('=', 1)
+ if '.' in key_part:
+ section, key = key_part.split('.', 1)
+ else:
+ section, key = None, key_part
+ if key:
+ upsert(section.strip() if section else None, key.strip(), value.strip())
+
+path.write_text(''.join(lines), encoding='utf-8')
+PY
+fi
+
# === Migrate FGType → FGInput/FGOutput (pre-v0.9-final INIs) ===
# v0.9-final split the single FGType key into FGInput + FGOutput. Games that were
# patched with an older build will have FGType=<value> with no FGInput/FGOutput,
diff --git a/main.py b/main.py
index 70f1359..a4a6760 100644
--- a/main.py
+++ b/main.py
@@ -27,6 +27,24 @@ FSR4_OFFICIAL_411_ASSET = {
"version": "4.1.1-official",
}
+FSR4_VALVE_411_ASSET = {
+ "name": "amdxcffx64_valve_2.3.0.2913.dll",
+ "sha256": "4e7dc37aebea3a90e3d3cc43e24cb2b54176b2535315f20dbe63b3b7cfc56b1e",
+ "version": "4.1.1-valve-2.3.0.2913",
+}
+
+AMDXC64_RDNA2_ASSET = {
+ "name": "amdxc64.dll",
+ "sha256": "a0a0af61d475e30a70966b3459f3793df772faf8f26ae3261d10554ff592cbd5",
+ "version": "8.18.10.0474",
+}
+
+OPTISCALER_PRE10_ASSET = {
+ "name": "OptiScaler_0.10.0-pre1.20260622_135940.dll",
+ "sha256": "b374b19081cc066365d0c6da4808d768e16469b0cbdfc478b6e95999947d5364",
+ "version": "0.10.0-pre1.20260622_135940",
+}
+
OPTIPATCHER_ASSET = {
"name": "OptiPatcher_rolling.asi",
"sha256": "88b9e1be3559737cd205fdf5f2c8550cf1923fb1def4c603e5bf03c3e84131b1",
@@ -73,6 +91,39 @@ FSR4_VARIANTS = {
"source_version": FSR4_OFFICIAL_411_ASSET["version"],
}
],
+ "config_overrides": {},
+ },
+ "rdna2-valve-411-pre10": {
+ "label": "4.1.1 Valve RDNA2 compatibility",
+ "dir_name": "fsr4-rdna2-valve-411-pre10",
+ "sha256": "ec7ed3ca674e288240e6f04b986342aece47454c41d9b0959449e82e22bd7f6d",
+ "source_asset_name": OPTISCALER_ARCHIVE_ASSET["name"],
+ "source_version": OPTISCALER_ARCHIVE_ASSET["version"],
+ "uses_archive_native": True,
+ "injector": {
+ "name": "OptiScaler.dll",
+ "sha256": OPTISCALER_PRE10_ASSET["sha256"],
+ "source_asset_name": OPTISCALER_PRE10_ASSET["name"],
+ "source_version": OPTISCALER_PRE10_ASSET["version"],
+ },
+ "extra_files": [
+ {
+ "name": FSR4_DRIVER_OVERRIDE_FILENAME,
+ "sha256": FSR4_VALVE_411_ASSET["sha256"],
+ "source_asset_name": FSR4_VALVE_411_ASSET["name"],
+ "source_version": FSR4_VALVE_411_ASSET["version"],
+ },
+ {
+ "name": "amdxc64.dll",
+ "sha256": AMDXC64_RDNA2_ASSET["sha256"],
+ "source_asset_name": AMDXC64_RDNA2_ASSET["name"],
+ "source_version": AMDXC64_RDNA2_ASSET["version"],
+ }
+ ],
+ "config_overrides": {
+ "FSR.Fsr4ForceModel": "2",
+ "Plugins.LoadCustomAmdxc64OnRdna2": "true",
+ },
},
}
VARIANT_EXTRA_FILENAMES = sorted(
@@ -132,6 +183,7 @@ ORIGINAL_DLL_BACKUPS = [
"amd_fidelityfx_framegeneration_dx12.dll",
FSR4_UPSCALER_FILENAME,
FSR4_DRIVER_OVERRIDE_FILENAME,
+ "amdxc64.dll",
"amd_fidelityfx_vk.dll",
]
@@ -375,6 +427,105 @@ class Plugin:
def _fsr4_variant_extra_file_path(self, fgmod_path: Path, fsr4_variant: str | None, filename: str) -> Path:
return self._fsr4_variant_dir(fgmod_path, fsr4_variant) / filename
+ def _fsr4_variant_injector_info(self, fsr4_variant: str | None) -> dict | None:
+ variant = self._fsr4_variant_info(fsr4_variant)
+ injector = variant.get("injector")
+ return injector if isinstance(injector, dict) else None
+
+ def _fsr4_variant_injector_path(self, fgmod_path: Path, fsr4_variant: str | None) -> Path | None:
+ injector = self._fsr4_variant_injector_info(fsr4_variant)
+ if not injector:
+ return None
+ return self._fsr4_variant_dir(fgmod_path, fsr4_variant) / injector.get("name", "OptiScaler.dll")
+
+ def _fsr4_variant_renamed_proxy_path(self, fgmod_path: Path, fsr4_variant: str | None, dll_name: str) -> Path | None:
+ if not self._fsr4_variant_injector_info(fsr4_variant):
+ return None
+ return self._fsr4_variant_dir(fgmod_path, fsr4_variant) / "renames" / dll_name
+
+ def _fsr4_variant_config_overrides(self, fsr4_variant: str | None) -> dict:
+ variant = self._fsr4_variant_info(fsr4_variant)
+ overrides = variant.get("config_overrides") or {}
+ return dict(overrides) if isinstance(overrides, dict) else {}
+
+ def _split_ini_override_key(self, raw_key: str) -> tuple[str | None, str]:
+ key = str(raw_key).strip()
+ if "." in key:
+ section, section_key = key.split(".", 1)
+ section = section.strip()
+ section_key = section_key.strip()
+ if section and section_key:
+ return section, section_key
+ return None, key
+
+ def _apply_optiscaler_ini_overrides(self, ini_file: Path, overrides: dict) -> bool:
+ if not overrides:
+ return True
+ try:
+ content = ini_file.read_text(encoding="utf-8", errors="replace")
+ newline = "\r\n" if "\r\n" in content else "\n"
+ lines = content.splitlines(keepends=True)
+ section_pattern = re.compile(r"^\s*\[(?P<section>[^\]]+)\]\s*$")
+
+ def ensure_trailing_newline() -> None:
+ if lines and not lines[-1].endswith(("\n", "\r")):
+ lines[-1] += newline
+
+ def upsert(section: str | None, key: str, value: str) -> None:
+ replacement = f"{key}={value}"
+ key_pattern = re.compile(rf"^(\s*{re.escape(key)}\s*)=.*$")
+
+ if section is None:
+ for idx, line in enumerate(lines):
+ if key_pattern.match(line):
+ line_ending = "\r\n" if line.endswith("\r\n") else ("\n" if line.endswith("\n") else newline)
+ lines[idx] = f"{replacement}{line_ending}"
+ return
+ ensure_trailing_newline()
+ lines.append(f"{replacement}{newline}")
+ return
+
+ in_section = False
+ insert_at = None
+ for idx, line in enumerate(lines):
+ match = section_pattern.match(line.strip())
+ if match:
+ if in_section:
+ insert_at = idx
+ break
+ if match.group("section") == section:
+ in_section = True
+ continue
+ if in_section and key_pattern.match(line):
+ line_ending = "\r\n" if line.endswith("\r\n") else ("\n" if line.endswith("\n") else newline)
+ lines[idx] = f"{replacement}{line_ending}"
+ return
+
+ if in_section:
+ if insert_at is None:
+ ensure_trailing_newline()
+ insert_at = len(lines)
+ lines.insert(insert_at, f"{replacement}{newline}")
+ return
+
+ ensure_trailing_newline()
+ if lines and lines[-1].strip():
+ lines.append(newline)
+ lines.append(f"[{section}]{newline}")
+ lines.append(f"{replacement}{newline}")
+
+ for raw_key, raw_value in overrides.items():
+ section, key = self._split_ini_override_key(str(raw_key))
+ value = str(raw_value).strip()
+ if key:
+ upsert(section, key, value)
+
+ ini_file.write_text("".join(lines), encoding="utf-8")
+ return True
+ except Exception as exc:
+ decky.logger.error(f"Failed to apply OptiScaler.ini overrides to {ini_file}: {exc}")
+ return False
+
def _sync_variant_root_extra_files(self, fgmod_path: Path, fsr4_variant: str | None) -> None:
selected_extra_files = {extra_file["name"]: extra_file for extra_file in self._fsr4_variant_extra_files(fsr4_variant)}
for filename in VARIANT_EXTRA_FILENAMES:
@@ -580,11 +731,17 @@ class Plugin:
optiscaler_archive = bin_path / OPTISCALER_ARCHIVE_ASSET["name"]
fsr4_int8_src = bin_path / FSR4_INT8_ASSET["name"]
fsr4_official_411_src = bin_path / FSR4_OFFICIAL_411_ASSET["name"]
+ fsr4_valve_411_src = bin_path / FSR4_VALVE_411_ASSET["name"]
+ amdxc64_rdna2_src = bin_path / AMDXC64_RDNA2_ASSET["name"]
+ optiscaler_pre10_src = bin_path / OPTISCALER_PRE10_ASSET["name"]
optipatcher_src = bin_path / OPTIPATCHER_ASSET["name"]
for required_path, asset in [
(optiscaler_archive, OPTISCALER_ARCHIVE_ASSET),
(fsr4_int8_src, FSR4_INT8_ASSET),
(fsr4_official_411_src, FSR4_OFFICIAL_411_ASSET),
+ (fsr4_valve_411_src, FSR4_VALVE_411_ASSET),
+ (amdxc64_rdna2_src, AMDXC64_RDNA2_ASSET),
+ (optiscaler_pre10_src, OPTISCALER_PRE10_ASSET),
(optipatcher_src, OPTIPATCHER_ASSET),
]:
if not required_path.exists():
@@ -660,6 +817,54 @@ class Plugin:
"Prepared rdna34-official-411 driver override",
)
+ rdna2_valve_dir = extract_path / FSR4_VARIANTS["rdna2-valve-411-pre10"]["dir_name"]
+ rdna2_valve_dir.mkdir(parents=True, exist_ok=True)
+ rdna2_valve_upscaler = rdna2_valve_dir / FSR4_UPSCALER_FILENAME
+ shutil.copy2(native_upscaler_root, rdna2_valve_upscaler)
+ self._verify_bundled_asset(
+ rdna2_valve_upscaler,
+ FSR4_VARIANTS["rdna2-valve-411-pre10"]["sha256"],
+ "Prepared rdna2-valve-411-pre10 FSR4 upscaler",
+ )
+ self._verify_bundled_asset(
+ fsr4_valve_411_src,
+ FSR4_VALVE_411_ASSET["sha256"],
+ "Bundled rdna2-valve-411-pre10 driver override",
+ )
+ rdna2_valve_driver = rdna2_valve_dir / FSR4_DRIVER_OVERRIDE_FILENAME
+ shutil.copy2(fsr4_valve_411_src, rdna2_valve_driver)
+ self._verify_bundled_asset(
+ rdna2_valve_driver,
+ FSR4_VALVE_411_ASSET["sha256"],
+ "Prepared rdna2-valve-411-pre10 driver override",
+ )
+ self._verify_bundled_asset(
+ amdxc64_rdna2_src,
+ AMDXC64_RDNA2_ASSET["sha256"],
+ "Bundled rdna2-valve-411-pre10 amdxc64 override",
+ )
+ rdna2_valve_amdxc64 = rdna2_valve_dir / "amdxc64.dll"
+ shutil.copy2(amdxc64_rdna2_src, rdna2_valve_amdxc64)
+ self._verify_bundled_asset(
+ rdna2_valve_amdxc64,
+ AMDXC64_RDNA2_ASSET["sha256"],
+ "Prepared rdna2-valve-411-pre10 amdxc64 override",
+ )
+ self._verify_bundled_asset(
+ optiscaler_pre10_src,
+ OPTISCALER_PRE10_ASSET["sha256"],
+ "Bundled rdna2-valve-411-pre10 OptiScaler injector",
+ )
+ rdna2_valve_injector = rdna2_valve_dir / "OptiScaler.dll"
+ shutil.copy2(optiscaler_pre10_src, rdna2_valve_injector)
+ self._verify_bundled_asset(
+ rdna2_valve_injector,
+ OPTISCALER_PRE10_ASSET["sha256"],
+ "Prepared rdna2-valve-411-pre10 OptiScaler injector",
+ )
+ if not self._create_renamed_copies(rdna2_valve_injector, rdna2_valve_dir / "renames"):
+ return {"status": "error", "message": "Failed to prepare renamed pre10 OptiScaler proxies."}
+
rdna23_dir = extract_path / FSR4_VARIANTS["rdna23-int8"]["dir_name"]
rdna23_dir.mkdir(parents=True, exist_ok=True)
self._verify_bundled_asset(
@@ -704,6 +909,18 @@ class Plugin:
"source_asset_name": variant["source_asset_name"],
"source_version": variant["source_version"],
"uses_archive_native": bool(variant["uses_archive_native"]),
+ "injector": (
+ {
+ "name": variant["injector"]["name"],
+ "sha256": variant["injector"]["sha256"],
+ "source_asset_name": variant["injector"]["source_asset_name"],
+ "source_version": variant["injector"]["source_version"],
+ "path": str((Path(variant["dir_name"]) / variant["injector"]["name"]).as_posix()),
+ }
+ if isinstance(variant.get("injector"), dict)
+ else None
+ ),
+ "config_overrides": dict(variant.get("config_overrides") or {}),
"extra_files": [
{
"name": extra_file["name"],
@@ -864,11 +1081,17 @@ class Plugin:
if not plugins_dir.exists() or not (plugins_dir / "OptiPatcher.asi").exists():
return {"exists": False}
- for variant in FSR4_VARIANTS.values():
+ for variant_id, variant in FSR4_VARIANTS.items():
variant_dir = path / variant["dir_name"]
variant_path = variant_dir / FSR4_UPSCALER_FILENAME
if not variant_path.exists():
return {"exists": False}
+ injector = variant.get("injector")
+ if isinstance(injector, dict):
+ if not (variant_dir / injector.get("name", "OptiScaler.dll")).exists():
+ return {"exists": False}
+ if not (variant_dir / "renames" / "dxgi.dll").exists():
+ return {"exists": False}
for extra_file in variant.get("extra_files", []):
if not (variant_dir / extra_file["name"]).exists():
return {"exists": False}
@@ -917,8 +1140,14 @@ class Plugin:
}
preserve_ini = True
+ previous_marker_metadata = self._read_marker(directory / MARKER_FILENAME)
+ previous_variant = str(previous_marker_metadata.get("fsr4_variant") or "").strip()
selected_variant = self._selected_fsr4_variant(fgmod_path, fsr4_variant)
selected_variant_info = FSR4_VARIANTS[selected_variant]
+ selected_config_overrides = self._fsr4_variant_config_overrides(selected_variant)
+ if previous_variant == "rdna2-valve-411-pre10" and selected_variant != previous_variant:
+ selected_config_overrides.setdefault("FSR.Fsr4ForceModel", "auto")
+ selected_config_overrides.setdefault("Plugins.LoadCustomAmdxc64OnRdna2", "false")
selected_upscaler_src = self._fsr4_variant_path(fgmod_path, selected_variant)
if not selected_upscaler_src.exists():
selected_upscaler_src = fgmod_path / FSR4_UPSCALER_FILENAME
@@ -976,9 +1205,18 @@ class Plugin:
else "No original game DLLs required backup"
)
- renamed = fgmod_path / "renames" / dll_name
+ variant_renamed = self._fsr4_variant_renamed_proxy_path(fgmod_path, selected_variant, dll_name)
+ variant_injector = self._fsr4_variant_injector_path(fgmod_path, selected_variant)
+ root_renamed = fgmod_path / "renames" / dll_name
destination_dll = directory / dll_name
- source_for_copy = renamed if renamed.exists() else optiscaler_dll
+ if variant_renamed and variant_renamed.exists():
+ source_for_copy = variant_renamed
+ elif variant_injector and variant_injector.exists():
+ source_for_copy = variant_injector
+ elif root_renamed.exists():
+ source_for_copy = root_renamed
+ else:
+ source_for_copy = optiscaler_dll
shutil.copy2(source_for_copy, destination_dll)
decky.logger.info(f"Copied injector DLL from {source_for_copy} to {destination_dll}")
@@ -995,6 +1233,7 @@ class Plugin:
if target_ini.exists():
self._migrate_optiscaler_ini(target_ini)
self._disable_hq_font_auto(target_ini)
+ self._apply_optiscaler_ini_overrides(target_ini, selected_config_overrides)
plugins_src = fgmod_path / "plugins"
plugins_dest = directory / "plugins"
diff --git a/package.json b/package.json
index 1712039..663b43b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "decky-framegen",
- "version": "0.15.7",
+ "version": "0.16.3-pre",
"description": "This plugin installs and manages OptiScaler, a tool that enhances upscaling and enables frame generation in a range of DirectX 12 games.",
"type": "module",
"scripts": {
@@ -69,6 +69,21 @@
"name": "amdxcffx64.dll"
},
{
+ "sha256hash": "a0a0af61d475e30a70966b3459f3793df772faf8f26ae3261d10554ff592cbd5",
+ "url": "https://github.com/xXJSONDeruloXx/OptiScaler-Bleeding-Edge/releases/download/fsr-4-1-1-rdna2/amdxc64.dll",
+ "name": "amdxc64.dll"
+ },
+ {
+ "sha256hash": "4e7dc37aebea3a90e3d3cc43e24cb2b54176b2535315f20dbe63b3b7cfc56b1e",
+ "url": "https://github.com/xXJSONDeruloXx/OptiScaler-Bleeding-Edge/releases/download/fsr-4-1-1-rdna2/amdxcffx64.dll",
+ "name": "amdxcffx64_valve_2.3.0.2913.dll"
+ },
+ {
+ "sha256hash": "b374b19081cc066365d0c6da4808d768e16469b0cbdfc478b6e95999947d5364",
+ "url": "https://github.com/xXJSONDeruloXx/OptiScaler-Bleeding-Edge/releases/download/fsr-4-1-1-rdna2/OptiScaler_0.10.0-pre1.20260622_135940.dll",
+ "name": "OptiScaler_0.10.0-pre1.20260622_135940.dll"
+ },
+ {
"sha256hash": "88b9e1be3559737cd205fdf5f2c8550cf1923fb1def4c603e5bf03c3e84131b1",
"url": "https://github.com/xXJSONDeruloXx/OptiScaler-Bleeding-Edge/releases/download/bins-for-optipatcher-rolling/OptiPatcher_rolling.asi",
"name": "OptiPatcher_rolling.asi"
diff --git a/src/utils/constants.ts b/src/utils/constants.ts
index b873b61..13bf505 100644
--- a/src/utils/constants.ts
+++ b/src/utils/constants.ts
@@ -62,18 +62,23 @@ export const DEFAULT_PROXY_DLL: ProxyDllValue = "dxgi.dll";
export const FSR4_VARIANT_OPTIONS = [
{
value: "rdna23-int8",
- label: "Steam Deck / RDNA2-3 optimized",
+ label: "4.0.2c | RDNA2/3 Mod",
hint: "Uses the bundled FSR4 INT8 4.0.2c override. Recommended for Steam Deck and other non-RDNA4 systems.",
},
{
value: "rdna4-native",
- label: "Native bundle / RDNA4",
- hint: "Uses the amd_fidelityfx_upscaler_dx12.dll that ships inside the OptiScaler 0.9.3 bundle.",
+ label: "4.1.0 | RDNA4 Official",
+ hint: "Uses the AMD-supplied 4.1.0 amd_fidelityfx_upscaler_dx12.dll redistributed in the OptiScaler bundle.",
},
{
value: "rdna34-official-411",
- label: "4.1.1 official for RDNA 3/4",
- hint: "Uses the native 0.9.3 upscaler plus the official amdxcffx64.dll override for RDNA 3/4.",
+ label: "4.1.1 | RDNA3/4 Official",
+ hint: "Uses the 4.1.0 native upscaler plus the official 4.1.1 amdxcffx64.dll override for RDNA 3/4.",
+ },
+ {
+ value: "rdna2-valve-411-pre10",
+ label: "4.1.1 | RDNA2 Mod",
+ hint: "Uses the pre10 OptiScaler injector, 4.1.0 native upscaler, Valve 4.1.1 amdxcffx64.dll, old amdxc64.dll, and RDNA2-specific INI overrides.",
},
] as const;