Files
deadinsideandClaude Opus 5.5 627ce1e0aa 0.2: Steam Deck fixes, cheaper live scaling, TAA flicker fix, mods, English launcher
Steam Deck and weak GPUs
- Outputs other than 1080p render at the preset size again (startup patch, as in 0.1);
  live resolution changes are now a setting (live_resolution=0|1|auto, off by default).
- Texture cache on integrated GPUs: thresholds follow the driver's current memory budget.
  The old budget (~1 GB on the Deck) kept the collector in its aggressive mode, evicting
  textures used 2-3 frames ago on every submission.
- Evicted GPU-written images are written back synchronously, while their pages are still
  protected (a deferred write could land over memory the game had reused).
- run.sh uses bash builtins only (the AppImage has no sed: the game did not start on NixOS).

Rendering
- Live scaling also reduces the game's half-resolution post-processing (960x540, including
  the bloom mip chain, one proxy per level).
- TAA: history of thin surfaces missed by a jitter phase is kept when nothing moves
  (railings, window bars: static-camera flicker p99.9 -45%).
- Scene texture LOD bias log2(render/output) in G-buffer passes while upscaling.
- Present on its own thread and at most one frame of GPU run-ahead (even frame pacing).
- Pipeline cache rebuilt instead of disabled when the shader profile changes.
- GPU idle only after the draw pipeline and deferred fences are done (work_retired).
- Recompiler fixes backported from shadPS4; integer interpolation fix for pre-Turing NVIDIA.
- Sharpness up to 2.0 (an extra RCAS pass); native-resolution TAA mode.

Launcher, mods, patches
- English launcher (follows the system language), loose-file mods with load order,
  third-party shadPS4 XML patches, 60/90 FPS patches with pointer slots fixed.
- AppImage: host NVIDIA Vulkan driver support, MangoHud, no FSR 4.1.1 models
  (BB_PACKAGE_FSR411=1 bundles local ones).

Tools: route recording/replay (F9), consecutive frame dumps, scene scaling diagnostics.
Details: docs/CHANGES_2026-10-02.md, docs/CHANGES_2026-10-03.md.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-03 08:17:26 +03:00

161 lines
7.8 KiB
Python

from paths import ROOT
import importlib.util
import json
import os
from pathlib import Path
import subprocess
import sys
import tempfile
import unittest
spec = importlib.util.spec_from_file_location('bbmods', ROOT / 'scripts/mods.py')
mods = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mods)
class ModTests(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
self.addCleanup(self.temp.cleanup)
self.root = Path(self.temp.name)
self.game = self.root / 'CUSA03173'
self.assets = self.game / 'dvdroot_ps4' / 'chr'
self.assets.mkdir(parents=True)
(self.game / 'eboot.bin').write_bytes(b'original executable')
(self.assets / 'a.dcx').write_bytes(b'original')
(self.assets / 'b.dcx').write_bytes(b'untouched')
self.moddir = self.root / 'mods'
def mod(self, name, contents=b'mod', file='a.dcx'):
root = self.moddir / name
folder = root / 'dvdroot_ps4' / 'chr'
folder.mkdir(parents=True, exist_ok=True)
(folder / file).write_bytes(contents)
return root
def test_merge_replacement_new_file_and_directory_listing(self):
a = self.mod('A')
self.mod('A', b'new', 'new.dcx')
result = mods.build_overlay(self.game, self.root / 'out', [('A', a)])
folder = result / 'dvdroot_ps4' / 'chr'
self.assertEqual((folder / 'a.dcx').read_bytes(), b'mod')
self.assertEqual((folder / 'b.dcx').read_bytes(), b'untouched')
self.assertEqual((folder / 'new.dcx').read_bytes(), b'new')
self.assertEqual({p.name for p in folder.iterdir()}, {'a.dcx', 'b.dcx', 'new.dcx'})
self.assertEqual((self.assets / 'a.dcx').read_bytes(), b'original')
self.assertFalse((self.assets / 'new.dcx').exists())
self.assertEqual((result / 'eboot.bin').read_bytes(), b'original executable')
def test_last_mod_wins_and_reordering_changes_winner(self):
a, b = self.mod('A', b'A'), self.mod('B', b'B')
for layers, winner in [([('A', a), ('B', b)], b'B'), ([('B', b), ('A', a)], b'A')]:
result = mods.build_overlay(self.game, self.root / 'out', layers)
self.assertEqual((result / 'dvdroot_ps4/chr/a.dcx').read_bytes(), winner)
def test_selection_disable_and_new_mod(self):
for name in ['C', 'B', 'A']:
self.mod(name)
config = self.root / 'mods.json'
config.write_text(json.dumps({'order': ['B', 'A', 'deleted'], 'disabled': ['A']}))
self.assertEqual(mods.selected(self.moddir, config), ['B', 'C'])
def test_no_mod_returns_original_game(self):
self.assertEqual(mods.build_overlay(self.game, self.root / 'out', []), self.game)
self.assertFalse((self.root / 'out').exists())
def test_directory_conflict_does_not_modify_base(self):
a = self.mod('A')
(a / 'dvdroot_ps4/chr/a.dcx').unlink()
nested = a / 'dvdroot_ps4/chr/b.dcx'
nested.mkdir()
(nested / 'child').write_bytes(b'bad')
with self.assertRaises(ValueError):
mods.build_overlay(self.game, self.root / 'out', [('A', a)])
self.assertEqual((self.assets / 'b.dcx').read_bytes(), b'untouched')
self.assertEqual(list((self.root / 'out').iterdir()), [])
def test_mod_symlinks_rejected(self):
a = self.mod('A')
(a / 'dvdroot_ps4/escape').symlink_to(self.assets, target_is_directory=True)
with self.assertRaises(ValueError):
mods.build_overlay(self.game, self.root / 'out', [('A', a)])
def test_executable_replacement_rejected(self):
a = self.mod('A')
(a / 'eboot.bin').write_bytes(b'unsupported')
with self.assertRaises(ValueError):
mods.build_overlay(self.game, self.root / 'out', [('A', a)])
def test_shadps4_sibling_folder_and_global_disable(self):
legacy = Path(str(self.game) + '-mods')
folder = legacy / 'dvdroot_ps4/chr'
folder.mkdir(parents=True)
(folder / 'a.dcx').write_bytes(b'legacy')
for enabled, expected in [('1', b'legacy'), ('0', b'original')]:
result = subprocess.run([sys.executable, str(ROOT / 'scripts/mods.py'), str(self.game),
'--out', str(self.root / 'out'), '--mods-dir', str(self.moddir), '--enabled', enabled],
capture_output=True, text=True, check=True)
self.assertEqual((Path(result.stdout.strip()) / 'dvdroot_ps4/chr/a.dcx').read_bytes(), expected)
def test_app0_wrapped_mod_discovered(self):
self.moddir.mkdir()
(self.moddir / 'Wrapped/app0/dvdroot_ps4').mkdir(parents=True)
self.assertEqual(mods.discover(self.moddir), ['Wrapped'])
def test_other_case_replaces_the_game_file(self):
root = self.moddir / 'Upper'
(root / 'DVDROOT_PS4/Chr').mkdir(parents=True)
(root / 'DVDROOT_PS4/Chr/A.DCX').write_bytes(b'upper')
result = mods.build_overlay(self.game, self.root / 'out', [('Upper', root)])
folder = result / 'dvdroot_ps4/chr'
self.assertEqual((folder / 'a.dcx').read_bytes(), b'upper')
self.assertEqual({p.name for p in folder.iterdir()}, {'a.dcx', 'b.dcx'})
self.assertEqual({p.name for p in result.iterdir()}, {'dvdroot_ps4', 'eboot.bin'})
def test_wrapped_and_bare_layouts(self):
layouts = {'Title': 'CUSA03173/dvdroot_ps4/chr', 'Archive': 'Archive v1.2/dvdroot_ps4/chr',
'Bare': 'chr', 'Nested': 'Nested/app0/dvdroot_ps4/chr'}
for name, path in layouts.items():
folder = self.moddir / name / path
folder.mkdir(parents=True)
(folder / 'a.dcx').write_bytes(name.encode())
(self.moddir / 'Archive/readme.txt').write_text('notes')
(self.moddir / 'Junk/stuff').mkdir(parents=True)
self.assertEqual(mods.discover(self.moddir), ['Archive', 'Bare', 'Nested', 'Title'])
for name in layouts:
with self.subTest(name=name):
result = mods.build_overlay(self.game, self.root / 'out',
[(name, self.moddir / name)])
self.assertEqual((result / 'dvdroot_ps4/chr/a.dcx').read_bytes(), name.encode())
def test_invalid_profile_fails(self):
self.mod('A')
config = self.root / 'mods.json'
config.write_text('{"disabled":"A"}')
with self.assertRaises(ValueError):
mods.selected(self.moddir, config)
def test_run_uses_overlay_propagates_exit_and_cleans_view(self):
self.mod('A')
python = self.root / 'python'
python.write_text(f'#!{sys.executable}\nimport subprocess,sys\n'
'if sys.argv[1] == "scripts/mods.py" or sys.argv[1] == "-c":\n'
' sys.exit(subprocess.call([sys.executable,*sys.argv[1:]]))\n')
python.chmod(0o755)
probe = self.root / 'probe'
probe.write_text(f'#!{sys.executable}\nimport json,sys,os\nfrom pathlib import Path\n'
'game=Path(sys.argv[sys.argv.index("--app0")+1])\n'
'Path(os.environ["BB_DATA_DIR"],"mounted.json").write_text(json.dumps({\n'
'"path":str(game),"content":(game/"dvdroot_ps4/chr/a.dcx").read_text()}))\n'
'sys.exit(7)\n')
probe.chmod(0o755)
env = dict(os.environ, BB_PREBUILT='1', BB_PROBE=str(probe), PYTHON=str(python),
BB_DATA_DIR=str(self.root), BB_GAME_DIR=str(self.game),
BB_MODS_DIR=str(self.moddir), BB_MODS_ENABLED='1', BB_MODS_CONFIG=str(self.root/'mods.json'))
result = subprocess.run(['bash', 'run.sh'], cwd=ROOT, env=env, capture_output=True, timeout=30)
self.assertEqual(result.returncode, 7, result.stderr)
mounted = json.loads((self.root / 'mounted.json').read_text())
self.assertEqual(mounted['content'], 'mod')
self.assertFalse(Path(mounted['path']).exists())
self.assertEqual((self.assets/'a.dcx').read_bytes(), b'original')