fix: detect graphics capabilities and drop redundant title overrides

This commit is contained in:
Kurt Himebauch committed 2026-11-05 15:03:08 -05:00
1 parent 0bb8384c76
commit 64f114c96f
6 files changed
+55 -130

No files matched your search

@@ -30,8 +30,6 @@ object GameEnvironment {
fun defaults(preset: String, forceSsbs: Boolean = false): Map<String, String?> = linkedMapOf<String, String?>(
"MESA_SHADER_CACHE_DISABLE" to "false",
"DROIDDECK_ANTICHEAT_OFFLINE" to "1",
"VKD3D_FEATURE_LEVEL" to "12_2",
"VKD3D_SHADER_MODEL" to "6_9",
"DOTNET_GCRegionRange" to "800000000",
"DOTNET_EnableWriteXorExecute" to "0",
).apply {
@@ -4,12 +4,12 @@ import org.junit.Assert.*
import org.junit.Test
class GameEnvironmentTest {
@Test fun defaultsIncludeRequestedGraphicsLevelsAndKeepCaching() {
@Test fun defaultsDetectGraphicsCapabilitiesAndKeepCaching() {
val values = GameEnvironment.defaults("")
assertEquals("false", values["MESA_SHADER_CACHE_DISABLE"])
assertEquals("1", values["DROIDDECK_ANTICHEAT_OFFLINE"])
assertEquals("12_2", values["VKD3D_FEATURE_LEVEL"])
assertEquals("6_9", values["VKD3D_SHADER_MODEL"])
assertFalse(values.containsKey("VKD3D_FEATURE_LEVEL"))
assertFalse(values.containsKey("VKD3D_SHADER_MODEL"))
}
@Test fun forceSsbsIsADefaultAGameCanTurnOff() {
@@ -35,14 +35,14 @@ class GameEnvironmentTest {
}
@Test fun graphicsDefaultsRespectExistingEditsAndRemovals() {
val config = GameEnvironment.Config(shared = mapOf("VKD3D_FEATURE_LEVEL" to "12_0"))
val config = GameEnvironment.Config(shared = mapOf("VKD3D_FEATURE_LEVEL" to "12_0", "VKD3D_SHADER_MODEL" to "6_5"))
.withEntries("42", mapOf("VKD3D_SHADER_MODEL" to null))
val game = GameEnvironment.effective(config, "", "42")
assertEquals("12_0", game["VKD3D_FEATURE_LEVEL"])
assertTrue(game.containsKey("VKD3D_SHADER_MODEL"))
assertNull(game["VKD3D_SHADER_MODEL"])
val restored = GameEnvironment.effective(config.withEntries("42", emptyMap()), "", "42")
assertEquals("6_9", restored["VKD3D_SHADER_MODEL"])
assertEquals("6_5", restored["VKD3D_SHADER_MODEL"])
}
@Test fun dotnetRuntimeFitsAndroidAddressSpaceForEveryGame() {
@@ -164,13 +164,24 @@ class GameEnvironmentStoreTest {
assertEquals(config, GameEnvironmentStore.read(context))
val published = GameEnvironmentStore.decode(JSONObject(guest.readText()))
assertEquals("false", published.shared["MESA_SHADER_CACHE_DISABLE"])
assertEquals("12_2", published.shared["VKD3D_FEATURE_LEVEL"])
assertEquals("6_9", published.shared["VKD3D_SHADER_MODEL"])
assertFalse(published.shared.containsKey("VKD3D_FEATURE_LEVEL"))
assertFalse(published.shared.containsKey("VKD3D_SHADER_MODEL"))
assertEquals(config.games, published.games)
assertEquals(config.shared["LITERAL"], published.shared["LITERAL"])
assertEquals("", published.shared["EMPTY"])
}
@Test fun publishingPreservesExplicitSharedGraphicsCapabilities() {
val config = GameEnvironment.Config(shared = mapOf(
"VKD3D_FEATURE_LEVEL" to "12_1", "VKD3D_SHADER_MODEL" to "6_6",
))
GameEnvironmentStore.save(context, config)
val published = GameEnvironmentStore.decode(JSONObject(guest.readText()))
assertEquals(config, GameEnvironmentStore.read(context))
assertEquals("12_1", published.shared["VKD3D_FEATURE_LEVEL"])
assertEquals("6_6", published.shared["VKD3D_SHADER_MODEL"])
}
@Test fun changingFexPresetPublishesForNextGameLaunch() {
GameEnvironmentStore.save(context, GameEnvironment.Config())
SessionPrefs.setFexPreset(context, "EXTREME")
+11 -6
View File
@@ -191,9 +191,10 @@ per-game value in the environment editor still wins.
## Defaults and available suggestions
The initial configuration enables Mesa shader caching with
`MESA_SHADER_CACHE_DISABLE=false`, sets `VKD3D_FEATURE_LEVEL=12_2` and
`VKD3D_SHADER_MODEL=6_9`, and preserves the selected FEX preset. Existing edits
and explicit removals take precedence over these defaults. FEX's own default
`MESA_SHADER_CACHE_DISABLE=false` and preserves the selected FEX preset.
`VKD3D_FEATURE_LEVEL` and `VKD3D_SHADER_MODEL` are unset by default, so VKD3D
detects capabilities from the active Vulkan driver. Existing shared and per-game
overrides, including explicit removals, are preserved. FEX's own default
remains the default preset. Shader caching in VKD3D/DXVK, synchronization, ray
tracing and diagnostic logging otherwise retain the runtime's defaults.
@@ -209,8 +210,8 @@ values. Custom variables have editable names and literal values.
| Variable | Use |
| --- | --- |
| `VKD3D_FEATURE_LEVEL` | D3D12 capability override; defaults to `12_2`. |
| `VKD3D_SHADER_MODEL` | Shader-model override; defaults to `6_9`. |
| `VKD3D_FEATURE_LEVEL` | Optional D3D12 capability override; automatic detection by default. |
| `VKD3D_SHADER_MODEL` | Optional shader-model override; automatic detection by default. |
| `VKD3D_CONFIG` | Per-game workarounds such as `nodxr`; not a universal performance preset. |
| `MESA_SHADER_CACHE_MAX_SIZE` | Storage budget for Mesa's shader cache, for example `1G`. |
| `mesa_glthread` | OpenGL threading; test with the affected game. |
@@ -221,7 +222,11 @@ values. Custom variables have editable names and literal values.
| `PROTON_USE_XALIA` | Proton's gamepad-navigation helper toggle. |
Forcing `12_2` or `6_9` changes reported capabilities; it cannot implement a missing
Vulkan feature. Remove either entry to use automatic detection for that capability.
Vulkan feature. Remove or unset either entry to use automatic detection for that capability.
The former feature-level rules for Caravan SandWitch, RoboCop: Rogue City,
ReSkate and Cyberpunk 2077 compensated for the global `12_2` override and are
removed. Stellar Blade also uses automatic detection. The separate Deep Rock
Galactic GDK ray-tracing opt-out and Mali bridge capability cap remain.
Other picker suggestions are editable starting values and are enabled only when
added and saved. Older or
custom components can support a different set of options. Android Wine wrapper,
@@ -153,41 +153,6 @@ def little_nightmares_threads(game):
return {"PROTONFIXES_DISABLE": "1"}, [], "Little Nightmares: umu-protonfixes off (its -onethread puts the game and renderer on one thread)"
@fix
def caravan_sandwitch_feature_level(game):
if "caravansandwitch.exe" in game.files or game.stem == "caravansandwitch-win64-shipping":
return {"VKD3D_FEATURE_LEVEL": "12_1"}, [], "Caravan SandWitch: D3D12 feature level 12_1 (12_2 hands it mesh shader pipelines vkd3d-proton cannot build)"
@fix
def robocop_rogue_city_feature_level(game):
if "robocop.exe" in game.files or game.stem == "robocop-win64-shipping":
return {"VKD3D_FEATURE_LEVEL": "12_1"}, [], "RoboCop: Rogue City: D3D12 feature level 12_1"
@fix
def reskate_feature_level(game):
if "skate.exe" in game.files and "reskate.dll" in game.files:
return {"VKD3D_FEATURE_LEVEL": "12_1"}, [], "ReSkate (skate.): D3D12 feature level 12_1"
@fix
def stellar_blade_feature_level(game):
if game.stem == "sb-win64-shipping" or (
"sb.exe" in game.files
and os.path.isfile(os.path.join(os.path.dirname(game.exe), "SB", "Binaries", "Win64", "SB-Win64-Shipping.exe"))
):
return {"VKD3D_FEATURE_LEVEL": None}, [], "Stellar Blade: use the driver's D3D12 feature level (12_2 advertises unsupported ray tracing)"
@fix
def cyberpunk_feature_level(game):
if game.stem == "cyberpunk2077" or (
game.stem == "redprelauncher"
and os.path.isfile(os.path.join(os.path.dirname(game.exe), "bin", "x64", "Cyberpunk2077.exe"))
):
return {"VKD3D_FEATURE_LEVEL": "12_1"}, [], "Cyberpunk 2077: D3D12 feature level 12_1"
def skip_red_launcher(command):
if len(command) > 2 and os.path.basename(command[2]).lower() == "redprelauncher.exe":
game = os.path.join(os.path.dirname(command[2]), "bin", "x64", "Cyberpunk2077.exe")
+26 -80
View File
@@ -1170,87 +1170,33 @@ class GameEnvironmentTest(unittest.TestCase):
self.assertEqual(speeds([0, 1], tmp), {0: 2265600, 1: 3302400})
self.assertIsNone(speeds([0, 2], tmp))
def test_reskate_runs_at_feature_level_12_1(self):
def test_titles_with_former_feature_level_workarounds_keep_detected_capabilities(self):
with tempfile.TemporaryDirectory() as tmp:
reskate = Path(tmp) / "ReSkate"
reskate.mkdir()
for name in ("Skate.exe", "ReSkate.dll", "ReSkateLauncher.exe", "EAAntiCheat.GameServiceLauncher.exe"):
(reskate / name).touch()
for exe in ("ReSkateLauncher.exe", "Skate.exe"):
env, extra, notes = MODULE["engine_fixes"](str(reskate / exe), [])
self.assertEqual((env, extra), ({"VKD3D_FEATURE_LEVEL": "12_1"}, []), exe)
self.assertTrue(notes)
config = {"version": 1, "shared": {"VKD3D_FEATURE_LEVEL": "12_2"}}
self.assertEqual(MODULE["apply_config"]({}, config, "3354750", env)["VKD3D_FEATURE_LEVEL"], "12_1")
config["games"] = {"3354750": {"VKD3D_FEATURE_LEVEL": "12_2"}}
self.assertEqual(MODULE["apply_config"]({}, config, "3354750", env)["VKD3D_FEATURE_LEVEL"], "12_2")
skate = Path(tmp) / "Skate"
skate.mkdir()
for name in ("Skate.exe", "EAAntiCheat.GameServiceLauncher.exe"):
(skate / name).touch()
self.assertEqual(MODULE["engine_fixes"](str(skate / "EAAntiCheat.GameServiceLauncher.exe"), [])[:2], ({}, []))
def test_stellar_blade_uses_detected_feature_level_without_overriding_game_settings(self):
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp) / "StellarBlade"
shipping = root / "SB/Binaries/Win64/SB-Win64-Shipping.exe"
shipping.parent.mkdir(parents=True)
shipping.touch()
(root / "SB.exe").touch()
config = {"version": 1, "shared": {"VKD3D_FEATURE_LEVEL": "12_2"}}
for exe in (root / "SB.exe", root / ".droiddeck-launch.bat", shipping):
env, extra, notes = MODULE["engine_fixes"](str(exe), [])
self.assertEqual((env, extra), ({"VKD3D_FEATURE_LEVEL": None}, []), exe)
self.assertTrue(notes)
inherited = {"VKD3D_FEATURE_LEVEL": "12_2", "KEEP": "unchanged"}
effective = MODULE["apply_config"](inherited, config, "3489700", env)
self.assertNotIn("VKD3D_FEATURE_LEVEL", effective)
self.assertEqual(effective["KEEP"], "unchanged")
for value in ("12_1", "12_2", None):
explicit = dict(config, games={"3489700": {"VKD3D_FEATURE_LEVEL": value}})
actual = MODULE["apply_config"](inherited, explicit, "3489700", env)
self.assertEqual(actual.get("VKD3D_FEATURE_LEVEL"), value)
other = Path(tmp) / "Other"
other.mkdir()
(other / "SB.exe").touch()
self.assertEqual(MODULE["engine_fixes"](str(other / "SB.exe"), []), ({}, [], []))
def test_caravan_sandwitch_robocop_and_cyberpunk_run_at_feature_level_12_1(self):
with tempfile.TemporaryDirectory() as tmp:
caravan = Path(tmp) / "Caravan SandWitch"
shipping = caravan / "CaravanSandWitch/Binaries/Win64"
shipping.mkdir(parents=True)
for name in ("CaravanSandWitch.exe", ".droiddeck-launch.bat"):
(caravan / name).touch()
(shipping / "CaravanSandWitch-Win64-Shipping.exe").touch()
cyberpunk = Path(tmp) / "Cyberpunk 2077"
(cyberpunk / "bin/x64").mkdir(parents=True)
for name in ("REDprelauncher.exe", "bin/x64/Cyberpunk2077.exe"):
(cyberpunk / name).touch()
robocop = Path(tmp) / "RoboCop Rogue City"
(robocop / "RoboCop/Binaries/Win64").mkdir(parents=True)
for name in ("RoboCop.exe", "RoboCop/Binaries/Win64/RoboCop-Win64-Shipping.exe"):
(robocop / name).touch()
games = [caravan / "CaravanSandWitch.exe", caravan / ".droiddeck-launch.bat",
shipping / "CaravanSandWitch-Win64-Shipping.exe",
robocop / "RoboCop.exe", robocop / "RoboCop/Binaries/Win64/RoboCop-Win64-Shipping.exe",
cyberpunk / "REDprelauncher.exe", cyberpunk / "bin/x64/Cyberpunk2077.exe"]
for exe in games:
env, extra, notes = MODULE["engine_fixes"](str(exe), [])
self.assertEqual((env, extra), ({"VKD3D_FEATURE_LEVEL": "12_1"}, []), exe)
self.assertTrue(notes)
config = {"version": 1, "shared": {"VKD3D_FEATURE_LEVEL": "12_2"}}
self.assertEqual(MODULE["apply_config"]({}, config, "1582650", env)["VKD3D_FEATURE_LEVEL"], "12_1")
config["games"] = {"1582650": {"VKD3D_FEATURE_LEVEL": "12_2"}}
self.assertEqual(MODULE["apply_config"]({}, config, "1582650", env)["VKD3D_FEATURE_LEVEL"], "12_2")
other = Path(tmp) / "Other/Binaries/Win64"
other.mkdir(parents=True)
(other / "Other-Win64-Shipping.exe").touch()
self.assertEqual(MODULE["engine_fixes"](str(other / "Other-Win64-Shipping.exe"), []), ({}, [], []))
witcher = Path(tmp) / "The Witcher 3"
witcher.mkdir()
(witcher / "REDprelauncher.exe").touch()
self.assertNotIn("VKD3D_FEATURE_LEVEL", MODULE["engine_fixes"](str(witcher / "REDprelauncher.exe"), [])[0])
root = Path(tmp)
names = (
"StellarBlade/SB.exe", "StellarBlade/.droiddeck-launch.bat",
"StellarBlade/SB/Binaries/Win64/SB-Win64-Shipping.exe",
"Caravan/CaravanSandWitch.exe", "Caravan/.droiddeck-launch.bat",
"Caravan/CaravanSandWitch/Binaries/Win64/CaravanSandWitch-Win64-Shipping.exe",
"RoboCop/RoboCop.exe", "RoboCop/RoboCop/Binaries/Win64/RoboCop-Win64-Shipping.exe",
"ReSkate/Skate.exe", "ReSkate/ReSkate.dll", "ReSkate/ReSkateLauncher.exe",
"Cyberpunk/REDprelauncher.exe", "Cyberpunk/bin/x64/Cyberpunk2077.exe",
)
for name in names:
p = root / name
p.parent.mkdir(parents=True, exist_ok=True)
p.touch()
for name in names:
if name.endswith(".dll"):
continue
env, extra, notes = MODULE["engine_fixes"](str(root / name), [])
self.assertNotIn("VKD3D_FEATURE_LEVEL", env, name)
self.assertNotIn("VKD3D_SHADER_MODEL", env, name)
for values in ({}, {"VKD3D_FEATURE_LEVEL": "12_2", "VKD3D_SHADER_MODEL": "6_9"}):
config = {"version": 1, "shared": values}
self.assertEqual(MODULE["apply_config"]({}, config, "42", env), values, name)
config = {"version": 1, "shared": {}, "games": {"42": values}}
self.assertEqual(MODULE["apply_config"]({}, config, "42", env), values, name)
def test_unity_players_keep_tso_over_the_preset(self):
with tempfile.TemporaryDirectory() as tmp: