diff --git a/tools/linuxfs/overlay/usr/local/bin/droiddeck-game-env b/tools/linuxfs/overlay/usr/local/bin/droiddeck-game-env index c9f37a361..49e1c4453 100755 --- a/tools/linuxfs/overlay/usr/local/bin/droiddeck-game-env +++ b/tools/linuxfs/overlay/usr/local/bin/droiddeck-game-env @@ -351,6 +351,20 @@ def game_cores(env): print("droiddeck-game-env: could not use game cores %r (%s)" % (text, error), file=sys.stderr) +def cpu_topology(env): + if env.get("WINE_CPU_TOPOLOGY") or not (env.get("DROIDDECK_GAME_CORES", "").strip() or env.get("BL_GAME_CPUS", "").strip()): + return + try: + cores = sorted(os.sched_getaffinity(0)) + except OSError: + return + online = online_cores() + if not cores or set(cores) >= online or cores[-1] >= len(online): + return + env["WINE_CPU_TOPOLOGY"] = "%d:%s" % (len(cores), ",".join(map(str, cores))) + print("droiddeck-game-env: Windows sees %d cores (%s)" % (len(cores), env["WINE_CPU_TOPOLOGY"]), file=sys.stderr) + + def apply_config(env, config, app_id, fixes=None): if config.get("version") != 1: raise ValueError("unsupported environment configuration") @@ -627,6 +641,7 @@ def main(): env = vram_cap(env) env = force_ssbs(command, env) game_cores(env) + cpu_topology(env) try: xbox = runpy.run_path(str(Path(__file__).with_name("droiddeck-xbox"))) except Exception as error: diff --git a/tools/tests/test_game_environment.py b/tools/tests/test_game_environment.py index 0bb794517..076564447 100644 --- a/tools/tests/test_game_environment.py +++ b/tools/tests/test_game_environment.py @@ -449,6 +449,23 @@ class GameEnvironmentTest(unittest.TestCase): self.assertEqual(calls, [{0, 1, 2}]) self.assertEqual(MODULE["parse_cores"]("0,2-7"), {0, 2, 3, 4, 5, 6, 7}) + def test_windows_games_see_only_the_cores_they_are_pinned_to(self): + os_mod = MODULE["os"] + online = {"online_cores": lambda: {0, 1, 2, 3, 4, 5, 6, 7}} + cases = [ + ({"BL_GAME_CPUS": "6,7"}, {6, 7}, "2:6,7"), + ({"DROIDDECK_GAME_CORES": "0-2"}, {0, 1, 2}, "3:0,1,2"), + ({"BL_GAME_CPUS": "0-7"}, set(range(8)), None), + ({}, {6, 7}, None), + ({"BL_GAME_CPUS": "6,7", "WINE_CPU_TOPOLOGY": "4:0,1,2,3"}, {6, 7}, "4:0,1,2,3"), + ({"BL_GAME_CPUS": "9"}, {9}, None), + ] + for env, affinity, expected in cases: + with mock.patch.dict(MODULE["cpu_topology"].__globals__, online), \ + mock.patch.object(os_mod, "sched_getaffinity", return_value=affinity, create=True): + MODULE["cpu_topology"](env) + self.assertEqual(env.get("WINE_CPU_TOPOLOGY"), expected, env) + def test_caravan_sandwitch_robocop_and_cyberpunk_run_at_feature_level_12_1(self): with tempfile.TemporaryDirectory() as tmp: caravan = Path(tmp) / "Caravan SandWitch"